Compare commits
165 Commits
e218fc826c
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b46c0874f2 | |||
| 1d246353a8 | |||
| 9833ce23ce | |||
| 45a6416d27 | |||
| 152caa25e9 | |||
| 67da186672 | |||
| 56f9544cb0 | |||
| 7e8f4859cd | |||
| e3503b6a77 | |||
| 9c5df0abdb | |||
| 190b9a3af5 | |||
| 2c5a0b6aef | |||
| d333d8a45a | |||
| 0227b7b982 | |||
| 71c5e88181 | |||
| cd7d20ccd4 | |||
| 75d07b5ac9 | |||
| 52b96d01de | |||
| a0f77ad121 | |||
| 16aab2279c | |||
| 15dec6c3fc | |||
| 33c3935164 | |||
| 6bcd1a5c58 | |||
| de4201d7d3 | |||
| e3e0de66e9 | |||
| 866fea7946 | |||
| 85c7415b8a | |||
| ed1b302274 | |||
| 26cc71ce60 | |||
| 285c007dff | |||
| 03aa4efcf0 | |||
| 4f90ed688f | |||
| 548dba6476 | |||
| 4a808dd9c4 | |||
| e71602e90c | |||
| bd96c4122c | |||
| 661710f349 | |||
| eae94cf1f3 | |||
| eeb9f6c686 | |||
| 37174788bf | |||
| 2db4810dfc | |||
| 8b6ef392f5 | |||
| eef557675b | |||
| 1a399400ff | |||
| 29314a7ec4 | |||
| 70fbac1b49 | |||
| 1c3d9906e4 | |||
| c841be1b31 | |||
| 94583bd30f | |||
| 17dff31dec | |||
| b14a0b3d58 | |||
| 03963fd896 | |||
| 663ed13522 | |||
| 9c122c8328 | |||
| 4d243c16e6 | |||
| 994b41aa37 | |||
| b79895e6fb | |||
| fb224a116c | |||
| e0eabfb350 | |||
| 3274dede7a | |||
| 754d609399 | |||
| e4948ad4c8 | |||
| be851ee311 | |||
| cb9ecb2de6 | |||
| b939a25456 | |||
| 8909629d8f | |||
| bc754e66fd | |||
| 7f35c3a585 | |||
| 2cc07ee2e5 | |||
| 69db07fe35 | |||
| acc19f4a44 | |||
| 2bbe67b4c5 | |||
| 7bc6eefad2 | |||
| 09505f5793 | |||
| cd2bd5bf19 | |||
| 20f9fa1e85 | |||
| b8954b300d | |||
| 00ae9d3067 | |||
| 3c80617ffb | |||
| 268f34198b | |||
| ae1b1f35e1 | |||
| f86fcc05f5 | |||
| 9404596012 | |||
| 10024a66c3 | |||
| b7a7b1e84d | |||
| 30d9d721fb | |||
| c9edd62a0b | |||
| ddc9332122 | |||
| e82cb0e68b | |||
| b1c838a637 | |||
| d13d5a06cc | |||
| 3bf0f506eb | |||
| c88354347b | |||
| 33c62993d5 | |||
| 499d787aa5 | |||
| b0b7d12d5a | |||
| 1ba563aa70 | |||
| 63ded10341 | |||
| 32204c9305 | |||
| 6799cb1715 | |||
| f33487f511 | |||
| 8367ced926 | |||
| 8b05f0f05c | |||
| d19131fa75 | |||
| 95c1c31e1f | |||
| 0dcd7f460b | |||
| b68a750e4f | |||
| 5d46504c1d | |||
| 4391bbe61d | |||
| 960cd78b8a | |||
| 01de542d15 | |||
| 9502d789b9 | |||
| 83d1517afc | |||
| 1da666b090 | |||
| b3d3f6db95 | |||
| fd3bee394c | |||
| 82a0444013 | |||
| 1139e54086 | |||
| d315488d83 | |||
| 87c3defc76 | |||
| 1460ffb676 | |||
| 4267f82db9 | |||
| c9e578a33e | |||
| 256a31b38c | |||
| 001237ea35 | |||
| dcd1a9855e | |||
| 1564f8648d | |||
| 93fd95a6b3 | |||
| a63fe0fb5c | |||
| 5a17f969b8 | |||
| ff5cadc9f2 | |||
| c04c5d1902 | |||
| 3bf34ea840 | |||
| 433b149c85 | |||
| 942cda486c | |||
| a2973be208 | |||
| 990d80e16d | |||
| b5425d8b20 | |||
| 2e50bcaa58 | |||
| f47363c24a | |||
| a2952a32a4 | |||
| 91e9c714df | |||
| 1580ab2c18 | |||
| f17a81d363 | |||
| 080cbbdd85 | |||
| 4a2ff8aa22 | |||
| a0b9b55130 | |||
| 7259cbf67a | |||
| ea5499123e | |||
| 3e25097470 | |||
| 7e6ad0d346 | |||
| 0ed27f477f | |||
| bf997d85ac | |||
| 89bbd5ecd7 | |||
| 7d6450d3f7 | |||
| 9d000ae22a | |||
| af70e53f61 | |||
| cb340ca1e4 | |||
| 50bffdf38a | |||
| 544bb30277 | |||
| 1a162bf320 | |||
| 6cb3ae10ee | |||
| 0240d80da5 | |||
| 07cc6d47b1 | |||
| 5537d20152 |
+27
-21
@@ -2,7 +2,12 @@
|
|||||||
|
|
||||||
> **The Event Horizon of Software Quality.**
|
> **The Event Horizon of Software Quality.**
|
||||||
> _Adapted for Google Antigravity IDE from [github/spec-kit](https://github.com/github/spec-kit)._
|
> _Adapted for Google Antigravity IDE from [github/spec-kit](https://github.com/github/spec-kit)._
|
||||||
> _Version: 1.8.6 — LCBP3-DMS Edition (v1.8.6 Production Ready)_
|
>
|
||||||
|
> # Speckit Agent Infrastructure (v1.9.0)
|
||||||
|
>
|
||||||
|
> - Version: 1.9.0
|
||||||
|
> - Last Updated: 2026-05-22
|
||||||
|
> - Core Principle: **Sync with AGENTS.md v1.9.6**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -78,36 +83,37 @@ The toolkit is organized into modular components that provide both the logic (Sc
|
|||||||
│ ├── speckit-tester/ # Test Runner & Coverage
|
│ ├── speckit-tester/ # Test Runner & Coverage
|
||||||
│ └── speckit-validate/ # Implementation Validator
|
│ └── speckit-validate/ # Implementation Validator
|
||||||
│
|
│
|
||||||
|
├── workflows/ # / Slash Commands (Orchestration)
|
||||||
|
│ ├── 00-speckit.all.md # Full Pipeline (10 steps: Specify → Validate)
|
||||||
|
│ ├── 01–11-speckit-*.md # Individual phase workflows
|
||||||
|
│ ├── speckit-prepare.md # Prep Pipeline (5 steps: Specify → Analyze)
|
||||||
|
│ ├── schema-change.md # DB Schema Change (ADR-009)
|
||||||
|
│ ├── create-backend-module.md # NestJS Module Scaffolding
|
||||||
|
│ ├── create-frontend-page.md # Next.js Page Scaffolding
|
||||||
|
│ ├── deploy.md # Deployment via Gitea CI/CD
|
||||||
|
│ ├── review.md # Code Review Workflow
|
||||||
|
│ └── util-speckit-*.md # Utilities (checklist, diff, migrate, etc.)
|
||||||
|
│
|
||||||
├── rules/ # Project Context & Validation Rules
|
├── rules/ # Project Context & Validation Rules
|
||||||
│ ├── 00-project-context.md # Role, Persona, Rule Tiers
|
│ ├── 00-project-context.md # Role, Persona, Rule Tiers (v1.9.6)
|
||||||
│ ├── 01-adr-019-uuid.md # UUID Strategy (Critical)
|
│ ├── 01-adr-019-uuid.md # UUID Strategy (Critical)
|
||||||
│ ├── 02-security.md # Security Requirements
|
│ ├── 02-security.md # Security Requirements (ADR-023/023A)
|
||||||
│ ├── 03-typescript.md # TypeScript Standards
|
│ ├── 03-typescript.md # TypeScript Standards
|
||||||
│ ├── 04-domain-terminology.md # DMS Glossary Compliance
|
│ ├── 04-domain-terminology.md # DMS Glossary Compliance
|
||||||
│ ├── 05-forbidden-actions.md # Critical Prohibited Patterns
|
│ ├── 05-forbidden-actions.md # Critical Prohibited Patterns
|
||||||
│ ├── 06-backend-patterns.md # NestJS Architecture Rules
|
│ ├── 06-backend-patterns.md # NestJS Architecture Rules
|
||||||
│ ├── 07-frontend-patterns.md # Next.js App Router Rules
|
│ ├── 07-frontend-patterns.md # Next.js App Router Rules
|
||||||
│ ├── 08-development-flow.md # Development Workflow
|
│ ├── 08-development-flow.md # Development Workflow (Tier 3 SPECIALIZED WORK)
|
||||||
│ ├── 09-commit-checklist.md # Pre-commit Validation
|
│ ├── 09-commit-checklist.md # Pre-commit Validation
|
||||||
│ ├── 10-error-handling.md # ADR-007 Compliance
|
│ ├── 10-error-handling.md # ADR-007 Compliance
|
||||||
│ └── 11-ai-integration.md # ADR-018/020 AI Boundaries
|
│ └── 11-ai-integration.md # ADR-023/023A AI Boundaries
|
||||||
│
|
│
|
||||||
└── scripts/
|
└── scripts/
|
||||||
├── bash/ # Bash Core (Kinetic logic)
|
├── bash/ # Bash Core (Kinetic logic)
|
||||||
├── powershell/ # PowerShell Equivalents (Windows-native)
|
├── powershell/ # PowerShell Equivalents (Windows-native)
|
||||||
├── fix_links.py # Spec link fixer
|
├── fix_links.py # Spec link fixer
|
||||||
├── verify_links.py # Spec link verifier
|
├── verify_links.py # Spec link verifier
|
||||||
└── start-mcp.js # MCP server launcher
|
└── start-mcp.js # MCP server launcher
|
||||||
|
|
||||||
.windsurf/workflows/ # / Slash Commands (Orchestration)
|
|
||||||
├── 00-speckit.all.md # Full Pipeline (10 steps: Specify → Validate)
|
|
||||||
├── 01–11-speckit-*.md # Individual phase workflows
|
|
||||||
├── speckit-prepare.md # Prep Pipeline (5 steps: Specify → Analyze)
|
|
||||||
├── schema-change.md # DB Schema Change (ADR-009)
|
|
||||||
├── create-backend-module.md # NestJS Module Scaffolding
|
|
||||||
├── create-frontend-page.md # Next.js Page Scaffolding
|
|
||||||
├── deploy.md # Deployment via Gitea CI/CD
|
|
||||||
├── review.md # Code Review Workflow
|
|
||||||
└── util-speckit-*.md # Utilities (checklist, diff, migrate, etc.)
|
└── util-speckit-*.md # Utilities (checklist, diff, migrate, etc.)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -259,9 +265,9 @@ If you change your mind mid-project:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🏗️ LCBP3-DMS Project Notes (v1.8.6)
|
## 🏗️ LCBP3-DMS Project Notes (v1.9.6)
|
||||||
|
|
||||||
### 📊 Current Status: Production Ready (2026-04-14)
|
### 📊 Current Status: Production Ready (2026-05-22)
|
||||||
|
|
||||||
| Area | Status |
|
| Area | Status |
|
||||||
| ------------- | ------------------------------- |
|
| ------------- | ------------------------------- |
|
||||||
@@ -277,7 +283,7 @@ If you change your mind mid-project:
|
|||||||
|
|
||||||
| เอกสาร | Path | ใช้เมื่อ |
|
| เอกสาร | Path | ใช้เมื่อ |
|
||||||
| --------------- | ---------------------------------------------------------------- | ------------------- |
|
| --------------- | ---------------------------------------------------------------- | ------------------- |
|
||||||
| Schema Tables | `specs/03-Data-and-Storage/lcbp3-v1.8.0-schema-02-tables.sql` | ก่อนเขียน Query |
|
| Schema Tables | `specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql` | ก่อนเขียน Query |
|
||||||
| Data Dictionary | `specs/03-Data-and-Storage/03-01-data-dictionary.md` | ตรวจ Business Rules |
|
| Data Dictionary | `specs/03-Data-and-Storage/03-01-data-dictionary.md` | ตรวจ Business Rules |
|
||||||
| Edge Cases | `specs/01-Requirements/01-06-edge-cases-and-rules.md` | 37 Rules |
|
| Edge Cases | `specs/01-Requirements/01-06-edge-cases-and-rules.md` | 37 Rules |
|
||||||
| Migration Scope | `specs/03-Data-and-Storage/03-06-migration-business-scope.md` | Migration Bot |
|
| Migration Scope | `specs/03-Data-and-Storage/03-06-migration-business-scope.md` | Migration Bot |
|
||||||
@@ -300,7 +306,7 @@ If you change your mind mid-project:
|
|||||||
- ❌ DO NOT bypass Release Gates before deploying — `04-08-release-management-policy.md`
|
- ❌ DO NOT bypass Release Gates before deploying — `04-08-release-management-policy.md`
|
||||||
- ❌ DO NOT start Migration without Gate #1 approval — `03-06-migration-business-scope.md`
|
- ❌ DO NOT start Migration without Gate #1 approval — `03-06-migration-business-scope.md`
|
||||||
- ❌ DO NOT use TypeORM Migrations — modify schema SQL directly (ADR-009)
|
- ❌ DO NOT use TypeORM Migrations — modify schema SQL directly (ADR-009)
|
||||||
- ❌ DO NOT give Ollama direct DB access — all writes via DMS API (ADR-018)
|
- ❌ DO NOT give Ollama direct DB access — all writes via DMS API (ADR-023/023A)
|
||||||
- ❌ DO NOT use `any` TypeScript type anywhere
|
- ❌ DO NOT use `any` TypeScript type anywhere
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -319,7 +325,7 @@ If you change your mind mid-project:
|
|||||||
# Run version validation
|
# Run version validation
|
||||||
./scripts/bash/validate-versions.sh
|
./scripts/bash/validate-versions.sh
|
||||||
|
|
||||||
# Fix by updating all files to v1.8.6
|
# Fix by updating all files to v1.9.6
|
||||||
# Then re-run validation to confirm
|
# Then re-run validation to confirm
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+1
@@ -2,6 +2,7 @@
|
|||||||
name: setup-matt-pocock-skills
|
name: setup-matt-pocock-skills
|
||||||
description: Sets up an `## Agent skills` block in AGENTS.md/CLAUDE.md and `docs/agents/` so the engineering skills know this repo's issue tracker (GitHub or local markdown), triage label vocabulary, and domain doc layout. Run before first use of `to-issues`, `to-prd`, `triage`, `diagnose`, `tdd`, `improve-codebase-architecture`, or `zoom-out` — or if those skills appear to be missing context about the issue tracker, triage labels, or domain docs.
|
description: Sets up an `## Agent skills` block in AGENTS.md/CLAUDE.md and `docs/agents/` so the engineering skills know this repo's issue tracker (GitHub or local markdown), triage label vocabulary, and domain doc layout. Run before first use of `to-issues`, `to-prd`, `triage`, `diagnose`, `tdd`, `improve-codebase-architecture`, or `zoom-out` — or if those skills appear to be missing context about the issue tracker, triage labels, or domain docs.
|
||||||
disable-model-invocation: true
|
disable-model-invocation: true
|
||||||
|
version: 1.9.0
|
||||||
---
|
---
|
||||||
|
|
||||||
# Setup Matt Pocock's Skills
|
# Setup Matt Pocock's Skills
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
# NAP-DMS Project Context & Rules
|
||||||
|
|
||||||
|
- For: Devin Cascade (and compatible: Codex CLI, opencode, Amp, Antigravity, AGENTS.md tools)
|
||||||
|
- Version: 1.9.10 | Last synced from repo: 2026-06-06
|
||||||
|
- Repo: [https://git.np-dms.work/np-dms/lcbp3](https://git.np-dms.work/np-dms/lcbp3)
|
||||||
|
- Skill pack: `.agents/skills/` (v1.9.0, 21 skills) — see [`skills/README.md`](./.agents/skills/README.md) + [`skills/_LCBP3-CONTEXT.md`](./.agents/skills/_LCBP3-CONTEXT.md)
|
||||||
|
|
||||||
|
## 🧠 Role & Persona
|
||||||
|
|
||||||
|
Act as **Senior Full Stack Developer** specialized in NestJS, Next.js, TypeScript, DMS. Focus: Data Integrity, Security, Maintainability, Performance.
|
||||||
|
|
||||||
|
You are a **Document Intelligence Engine** — not a general chatbot. Every response must be **precise**, **spec-compliant**, and **production-ready**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 Thought & Planning Protocol (Powered by Everything-Claude-Code)
|
||||||
|
|
||||||
|
Before writing any code or taking any action in Tier 1 and Tier 2, the AI must demonstrate the following thinking process:
|
||||||
|
|
||||||
|
### 1. Analysis Phase (Explore & Analyze)
|
||||||
|
|
||||||
|
Problem Understanding: Restate what the user wants in clear, unambiguous terms.
|
||||||
|
Context Search: Identify the relevant Spec files or ADRs from the "Key Spec Files" table that must be read before starting.
|
||||||
|
Constraints Identification: Identify key constraints (e.g. Security rules, UUID patterns, or Domain terminology).
|
||||||
|
|
||||||
|
### 2. Planning Phase (Plan)
|
||||||
|
|
||||||
|
Alternative Exploration: Present at least 2 solution approaches (where possible) with pros/cons analysis.
|
||||||
|
Step-by-Step Roadmap: Write a file-by-file plan of changes before executing.
|
||||||
|
Verification Plan: Specify how to verify the work is complete (e.g. "which unit tests to write" or "which file to check the schema in").
|
||||||
|
|
||||||
|
### 3. Execution & Refinement (Execute & Refine)
|
||||||
|
|
||||||
|
Follow the plan step by step, and pause to ask if any uncertainty arises.
|
||||||
|
If significant logic changes are made, summarize what was done for the user after completion.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ DMS Workflow Engine Protocol
|
||||||
|
|
||||||
|
กฎนี้ใช้คุม Logic การไหลของเอกสาร (RFA, Transmittal, Correspondence) เพื่อป้องกัน Race Condition และรักษาความถูกต้องของสถานะ:
|
||||||
|
|
||||||
|
- **State Management:** ตรวจสอบสถานะปัจจุบันจาก DB ก่อนเสมอ เพื่อป้องกันการอนุมัติซ้ำซ้อน (ดู `05-06-code-snippets.md` `[workflow-transition]`)
|
||||||
|
- **Concurrency Control:** การจอนเลขที่เอกสารต้องใช้ **Redis Redlock** หรือ **TypeORM `@VersionColumn`** เท่านั้น (ADR-002)
|
||||||
|
- **Background Jobs:** งานนานหรือการแจ้งเตือนต้องส่งไปทำที่ **BullMQ** ห้ามเขียนแบบ Inline (ADR-008)
|
||||||
|
- **Term Consistency:** ห้ามใช้ "Approval Flow" ให้ใช้ **"Workflow Engine"** และห้ามใช้ "Letter" ให้ใช้ **"Correspondence"** (หมายเหตุ: "จดหมาย" ในคอมเมนต์ภาษาไทย = Correspondence ที่ครอบคลุมทุกประเภท)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛡️ Security & Integrity Audit Protocol
|
||||||
|
|
||||||
|
กฎนี้ให้ AI เป็น Gatekeeper ก่อน Commit โดยเน้น **Tier 1 — CRITICAL**:
|
||||||
|
|
||||||
|
- **UUID Validation:** ตรวจสอบว่าเป็น **UUIDv7** และห้ามใช้ `parseInt()` บน UUID (ADR-019)
|
||||||
|
- **RBAC Check:** API ใหม่ต้องมี **CASL Guard** และตรวจสอบ 4-Level RBAC Matrix (ADR-016)
|
||||||
|
- **Data Isolation:** AI ต้องรันผ่าน **Ollama บน Admin Desktop** เท่านั้น ห้ามเข้าถึง DB/storage โดยตรง (ADR-023)
|
||||||
|
- **Input Sanitization:** ไฟล์อัปโหลดต้องผ่าน **Two-Phase** (Temp → Commit) และสแกนด้วย **ClamAV** (ADR-016)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧭 Rule Enforcement Tiers
|
||||||
|
|
||||||
|
### 🔴 Tier 1 — CRITICAL (CI BLOCKER)
|
||||||
|
|
||||||
|
Build fails หากละเมิด:
|
||||||
|
|
||||||
|
- Security (Auth, RBAC, Validation)
|
||||||
|
- UUID Strategy (ADR-019) — no `parseInt` / `Number` / `+` on UUID
|
||||||
|
- Database correctness — verify schema before writing queries
|
||||||
|
- File upload security (ClamAV + whitelist)
|
||||||
|
- AI validation boundary (ADR-023)
|
||||||
|
- Error handling strategy (ADR-007)
|
||||||
|
- Forbidden patterns: `any`, `console.log`, UUID misuse, `id ?? ''` fallback
|
||||||
|
|
||||||
|
### 🟡 Tier 2 — IMPORTANT (CODE REVIEW)
|
||||||
|
|
||||||
|
Must fix ก่อน merge:
|
||||||
|
|
||||||
|
- Architecture patterns (thin controller, business logic in service)
|
||||||
|
- Test coverage (80%+ business logic, 70%+ backend overall)
|
||||||
|
- Cache invalidation
|
||||||
|
- Naming conventions
|
||||||
|
- **TypeScript Standards:** Missing JSDoc, explicit types, or file headers
|
||||||
|
|
||||||
|
### 🟢 Tier 3 — SPECIALIZED WORK
|
||||||
|
|
||||||
|
Requires domain-specific knowledge:
|
||||||
|
|
||||||
|
- **ADR-021 Integration:** Workflow Engine & Context implementation
|
||||||
|
- **AI Infrastructure:** ADR-023/023A boundary enforcement and pipeline usage
|
||||||
|
- **AI Runtime Layer:** ADR-024 Intent Classification, ADR-025 Tool Layer, ADR-026 Chat UI, ADR-027 Admin Console
|
||||||
|
- **Migration Pipeline:** ADR-028 Staging Queue & post-migration cleanup
|
||||||
|
- **Complex Business Logic:** Multi-step workflows with state management
|
||||||
|
- **Performance Optimization:** Database queries, caching strategies, bulk operations
|
||||||
|
|
||||||
|
### 🔵 Tier 4 — GUIDELINES
|
||||||
|
|
||||||
|
Best practice — follow when possible:
|
||||||
|
|
||||||
|
- Code style / formatting (Prettier handles)
|
||||||
|
- Comment completeness
|
||||||
|
- Minor optimizations
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📐 TypeScript Rules & Coding Standards
|
||||||
|
|
||||||
|
### 📝 Core Standards
|
||||||
|
|
||||||
|
- **Strict Mode** — all strict checks enforced.
|
||||||
|
- **ZERO `any` types** — use proper types or `unknown` + narrowing.
|
||||||
|
- **ZERO `console.log`** — use NestJS `Logger` (backend) or remove (frontend).
|
||||||
|
- **English for Code** — use English for all code identifiers, variables, and logic.
|
||||||
|
- **Thai for Comments** — use Thai for comments, documentation, and JSDoc.
|
||||||
|
- **Explicit Typing** — explicitly define types for all variables, parameters, and return values.
|
||||||
|
- **JSDoc** — use JSDoc for all public classes and methods.
|
||||||
|
|
||||||
|
### 🏗️ File & Function Structure
|
||||||
|
|
||||||
|
- **File Headers** — every file MUST start with `// File: path/filename` on the first line.
|
||||||
|
- **Change Log** — include `// Change Log` at the top of the file to track modifications.
|
||||||
|
- **Single Export** — export **only one main symbol** per file.
|
||||||
|
- **Function Style** — avoid unnecessary blank lines inside functions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚫 Forbidden Actions
|
||||||
|
|
||||||
|
| ❌ Forbidden | ✅ Correct Approach | ⚠️ Why |
|
||||||
|
| ----------------------------------------------- | ------------------------------------------------------- | ---------------------------------------------------- |
|
||||||
|
| SQL Triggers for business logic | NestJS Service methods | Untestable; bypasses audit log |
|
||||||
|
| `.env` files in production | `docker-compose.yml` environment section | Secrets exposed in version control |
|
||||||
|
| TypeORM migration files | Edit schema SQL directly (ADR-009) | Migration drift risk; schema managed via SQL delta |
|
||||||
|
| Inventing table/column names | Verify against `schema-02-tables.sql` | Schema mismatch causes silent runtime errors |
|
||||||
|
| `any` TypeScript type | Proper types / generics | Defeats strict mode; hides runtime type errors |
|
||||||
|
| `console.log` in committed code | NestJS Logger (backend) / remove (frontend) | Log flooding in production; risk of data leakage |
|
||||||
|
| `req: any` in controllers | `RequestWithUser` typed interface | Type safety lost; auth context unreachable |
|
||||||
|
| `parseInt()` on UUID values | Use UUID string directly (ADR-019) | `"0195…"` parsed to integer `19` — silently wrong |
|
||||||
|
| Exposing INT PK in API responses | UUIDv7 `publicId` (ADR-019) | Leaks row count; enables DB enumeration attacks |
|
||||||
|
| AI accessing DB/storage directly | AI → DMS API → DB (ADR-023/023A) | Bypasses RBAC, audit trail, and validation layer |
|
||||||
|
| Direct file operations bypassing StorageService | `StorageService` for all file moves | Orphaned files; broken ClamAV scan; no audit trail |
|
||||||
|
| Inline email/notification sending | BullMQ queue job (ADR-008) | Blocks request thread; no retry on transient failure |
|
||||||
|
| Deploying without Release Gates | Complete `04-08-release-management-policy.md` | Unverified deploy risks data loss in production |
|
||||||
|
| AI direct cloud API calls | On-premises Ollama only (ADR-023/023A) | Data privacy violation; no audit control |
|
||||||
|
| AI outputs without human validation | Human-in-the-loop validation required (ADR-023/023A) | Unvalidated AI metadata corrupts document records |
|
||||||
|
| n8n calling Ollama/Qdrant directly | n8n → DMS API → BullMQ → Ollama (ADR-023A) | Bypasses audit log, RBAC, and error handling layer |
|
||||||
|
| Qdrant query without `projectPublicId` filter | `QdrantService.search(projectPublicId, ...)` (ADR-023A) | Cross-project data leak via vector search |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚧 Out of Scope — Never Do Without Explicit Approval
|
||||||
|
|
||||||
|
| ❌ Never Do Autonomously | ⚠️ Why Approval Is Required |
|
||||||
|
| --------------------------------------------------------------- | ---------------------------------------------------------------- |
|
||||||
|
| `DROP` or `RENAME` a column / table | Irreversible data loss — requires DBA + PM sign-off |
|
||||||
|
| Push directly to `main` / `master` branch | Bypasses CI, code review, and release gates |
|
||||||
|
| Generate or insert seed data into production database | May corrupt live data or violate business state invariants |
|
||||||
|
| Delete files from permanent storage | Files may be referenced in active documents or audit trails |
|
||||||
|
| Modify RBAC permission matrix without security team approval | Defines access control for all users — security boundary change |
|
||||||
|
| Upgrade major library versions (NestJS, Next.js, TypeORM, etc.) | Breaking changes require full regression test cycle |
|
||||||
|
| Disable or modify authentication / authorization guards | Creates unguarded endpoints — immediate security risk |
|
||||||
|
| Change Redis lock TTL or disable Redlock | Risk of document number race condition (ADR-002) |
|
||||||
|
| Create or supersede an ADR unilaterally | Architecture decisions require team consensus and review process |
|
||||||
|
| Add new columns to production tables without schema review | Must update Data Dictionary + downstream queries simultaneously |
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
|
||||||
|
# ADR-019 UUID Strategy
|
||||||
|
|
||||||
|
## CRITICAL RULES
|
||||||
|
|
||||||
|
- **NEVER** use `parseInt()` on UUID values
|
||||||
|
- **NEVER** use `Number()` on UUID values
|
||||||
|
- **NEVER** use `+` operator on UUID values
|
||||||
|
- **ALWAYS** use `publicId` (string UUID) for API responses
|
||||||
|
- **NEVER** expose internal INT `id` in API responses (use `@Exclude()`)
|
||||||
|
|
||||||
|
## Identifier Types
|
||||||
|
|
||||||
|
| Context | Type | Notes |
|
||||||
|
| ---------------- | ------------------------- | ------------------------------------------- |
|
||||||
|
| Internal / DB FK | `INT AUTO_INCREMENT` | Never exposed in API |
|
||||||
|
| Public API / URL | `UUIDv7` (MariaDB native) | Stored as BINARY(16), no transformer needed |
|
||||||
|
| Entity Property | `publicId: string` | Exposed directly in API (no transformation) |
|
||||||
|
| API Response | `publicId: string` (UUID) | INT `id` has `@Exclude()` — never appears |
|
||||||
|
|
||||||
|
## Backend Pattern (NestJS/TypeORM)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Entity
|
||||||
|
@Entity()
|
||||||
|
class Project extends UuidBaseEntity {
|
||||||
|
@Column({ type: 'uuid' })
|
||||||
|
publicId: string; // UUID string, no transformation needed
|
||||||
|
|
||||||
|
@PrimaryKey()
|
||||||
|
@Exclude()
|
||||||
|
id: number; // Internal INT, never exposed
|
||||||
|
}
|
||||||
|
|
||||||
|
// API Response → { id: "019505a1-7c3e-7000-8000-abc123def456" }
|
||||||
|
// Uses publicId directly, no @Expose({ name: 'id' }) needed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Frontend Pattern (Next.js)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ CORRECT — Use publicId only
|
||||||
|
type ProjectOption = {
|
||||||
|
publicId?: string; // No uuid, no id fallback
|
||||||
|
projectName?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ❌ WRONG — Multiple identifiers cause confusion
|
||||||
|
type ProjectOption = {
|
||||||
|
publicId?: string;
|
||||||
|
uuid?: string; // Don't do this
|
||||||
|
id?: number; // Don't do this
|
||||||
|
};
|
||||||
|
|
||||||
|
// ❌ NEVER use parseInt on UUID
|
||||||
|
parseInt(projectId); // "0195..." → 19 (WRONG!)
|
||||||
|
|
||||||
|
// ❌ NEVER use id ?? '' fallback
|
||||||
|
const value = c.publicId ?? c.id ?? ''; // Wrong!
|
||||||
|
|
||||||
|
// ✅ CORRECT — Use publicId only
|
||||||
|
const value = c.publicId; // "019505a1-7c3e-7000-8000-abc123def456"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related Documents
|
||||||
|
|
||||||
|
- `specs/06-Decision-Records/ADR-019-hybrid-identifier-strategy.md`
|
||||||
|
- `specs/05-Engineering-Guidelines/05-07-hybrid-uuid-implementation-plan.md`
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Security Rules (Non-Negotiable)
|
||||||
|
|
||||||
|
## Mandatory Security Requirements
|
||||||
|
|
||||||
|
1. **Idempotency:** All critical `POST`/`PUT`/`PATCH` MUST validate `Idempotency-Key` header
|
||||||
|
2. **Two-Phase File Upload:** Upload → Temp → Commit → Permanent
|
||||||
|
3. **Race Conditions:** Redis Redlock + TypeORM `@VersionColumn` for Document Numbering
|
||||||
|
4. **Validation:** Zod (frontend) + class-validator (backend DTO)
|
||||||
|
5. **Password:** bcrypt 12 salt rounds, min 8 chars, rotate every 90 days
|
||||||
|
6. **Rate Limiting:** `ThrottlerGuard` on all auth endpoints
|
||||||
|
7. **File Upload:** Whitelist PDF/DWG/DOCX/XLSX/ZIP, max 50MB, ClamAV scan
|
||||||
|
8. **AI Isolation (ADR-023/023A):** Ollama on Admin Desktop ONLY — NO direct DB/storage access; 2-model stack `gemma4:e4b Q8_0` + `nomic-embed-text`; all inference via BullMQ (`ai-realtime` / `ai-batch`)
|
||||||
|
9. **Error Handling (ADR-007):** Use layered error classification with user-friendly messages
|
||||||
|
10. **AI Integration (ADR-023/023A):** RFA-First approach; n8n orchestrates Migration Phase only via DMS API — never calls Ollama directly; `QdrantService.search()` requires `projectPublicId` as mandatory param
|
||||||
|
11. **AI Audit Trail:** Log all AI interactions and human validations
|
||||||
|
12. **Rate Limiting:** Apply to AI endpoints to prevent abuse
|
||||||
|
|
||||||
|
## Full Documentation
|
||||||
|
|
||||||
|
`specs/06-Decision-Records/ADR-016-security-authentication.md`
|
||||||
|
|
||||||
|
## Security Checklist (Before Every Commit)
|
||||||
|
|
||||||
|
- [ ] Input validation implemented (Zod/class-validator)
|
||||||
|
- [ ] RBAC/CASL permissions checked
|
||||||
|
- [ ] No SQL injection vulnerabilities
|
||||||
|
- [ ] File upload validation (whitelist + ClamAV)
|
||||||
|
- [ ] Rate limiting applied to auth endpoints
|
||||||
|
- [ ] AI boundary enforcement (ADR-023/023A) - no direct DB/storage access
|
||||||
|
- [ ] AI audit logging implemented for AI interactions
|
||||||
|
- [ ] AI outputs validated before use (human-in-the-loop)
|
||||||
|
- [ ] Error handling follows ADR-007 layered classification
|
||||||
|
- [ ] Cache invalidation when data modified
|
||||||
|
- [ ] OWASP Top 10 review passed
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# TypeScript Rules
|
||||||
|
|
||||||
|
## Core Standards
|
||||||
|
|
||||||
|
- **Strict Mode** — all strict checks enforced.
|
||||||
|
- **ZERO `any` types** — use proper types or `unknown` + narrowing.
|
||||||
|
- **ZERO `console.log`** — use NestJS `Logger` (backend) or remove before commit (frontend).
|
||||||
|
- **English for Code** — use English for all code identifiers, variables, and logic.
|
||||||
|
- **Thai for Comments** — use Thai for comments, documentation, and JSDoc.
|
||||||
|
- **Explicit Typing** — explicitly define types for all variables, parameters, and return values.
|
||||||
|
- **JSDoc** — use JSDoc for all public classes and methods (in **Thai**).
|
||||||
|
|
||||||
|
## File & Function Structure
|
||||||
|
|
||||||
|
- **File Headers** — every file MUST start with `// File: path/filename` on the first line.
|
||||||
|
- Use **absolute path** from project root (e.g., `// File: backend/src/modules/correspondence/correspondence.service.ts`)
|
||||||
|
- Do NOT use relative path (e.g., `// File: src/example.service.ts`)
|
||||||
|
- **Change Log** — include `// Change Log` at the top of the file.
|
||||||
|
- **Single Export** — export **only one main symbol** per file.
|
||||||
|
- **Function Style** — avoid unnecessary blank lines inside functions.
|
||||||
|
|
||||||
|
## i18n Guidelines
|
||||||
|
|
||||||
|
- **No Hardcoded Text:** Use i18n keys for all user-facing text
|
||||||
|
- **Reference:** `specs/05-Engineering-Guidelines/05-08-i18n-guidelines.md`
|
||||||
|
- **Pattern:** Use `t('key.path')` from i18n hook instead of hardcoded strings
|
||||||
|
|
||||||
|
## Patterns
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ CORRECT
|
||||||
|
// File: src/example.service.ts
|
||||||
|
// Change Log:
|
||||||
|
// - 2026-05-13: Initial creation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* บริการตัวอย่างสำหรับ LCBP3
|
||||||
|
*/
|
||||||
|
export class ExampleService {
|
||||||
|
public doSomething(data: string): boolean {
|
||||||
|
return data.length > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Domain Terminology
|
||||||
|
|
||||||
|
## DMS Glossary
|
||||||
|
|
||||||
|
| ✅ Use | ❌ Don't Use | คำอธิบายเพิ่มเติม |
|
||||||
|
| ------------------ | ------------------------------------- | ------------------------------------------------ |
|
||||||
|
| Correspondence | Letter, Communication, Document | ครอบคลุมทุกประเภท: Letter, RFA, Memo, ฯลฯ |
|
||||||
|
| RFA | Approval Request, Submit for Approval | เอกสารขออนุมัติ (ชนิดหนึ่งของ Correspondence) |
|
||||||
|
| Transmittal | Delivery Note, Cover Letter | เอกสารนำส่ง (ชนิดหนึ่งของ Correspondence) |
|
||||||
|
| Circulation | Distribution, Routing | ใบเวียนเอกสารภายใน (ชนิดหนึ่งของ Correspondence) |
|
||||||
|
| Shop Drawing | Construction Drawing | แบบก่อสร้าง |
|
||||||
|
| Contract Drawing | Design Drawing, Blueprint | แบบคู่สัญญา |
|
||||||
|
| Workflow Engine | Approval Flow, Process Engine | เครื่องมือจัดการลำดับงาน |
|
||||||
|
| Document Numbering | Document ID, Auto Number | ระบบจัดการเลขที่เอกสาร |
|
||||||
|
| RBAC | Permission System (generic) | การควบคุมสิทธิ์ตามบทบาท |
|
||||||
|
|
||||||
|
## Full Glossary
|
||||||
|
|
||||||
|
`specs/00-overview/00-02-glossary.md`
|
||||||
|
|
||||||
|
## Key Spec Files Priority
|
||||||
|
|
||||||
|
Spec priority: **`06-Decision-Records`** > **`05-Engineering-Guidelines`** > others
|
||||||
|
|
||||||
|
| Document | Path | Use When |
|
||||||
|
| ------------------------------ | --------------------------------------------------------------------------- | --------------------------------- |
|
||||||
|
| **Glossary** | `specs/00-overview/00-02-glossary.md` | Verify domain terminology |
|
||||||
|
| **Schema Tables** | `specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql` | Before writing any query |
|
||||||
|
| **Data Dictionary** | `specs/03-Data-and-Storage/03-01-data-dictionary.md` | Field meanings + business rules |
|
||||||
|
| **Edge Cases** | `specs/01-Requirements/01-06-edge-cases-and-rules.md` | Prevent bugs in flows |
|
||||||
|
| **ADR-019 UUID** | `specs/06-Decision-Records/ADR-019-hybrid-identifier-strategy.md` | UUID-related work |
|
||||||
|
| **ADR-023 AI** | `specs/06-Decision-Records/ADR-023-unified-ai-architecture.md` | AI integration work |
|
||||||
|
| **ADR-023A AI Model** | `specs/06-Decision-Records/ADR-023A-unified-ai-architecture.md` | 2-model stack, BullMQ 2-queue |
|
||||||
|
| **ADR-024 Intent Class.** | `specs/06-Decision-Records/ADR-024-intent-classification-strategy.md` | Pattern→LLM Fallback; Redis cache |
|
||||||
|
| **ADR-025 AI Tool Layer** | `specs/06-Decision-Records/ADR-025-ai-tool-layer-architecture.md` | Tool Registry; CASL-guarded |
|
||||||
|
| **ADR-026 Chat UI** | `specs/06-Decision-Records/ADR-026-document-chat-ui-pattern.md` | Side-panel; streaming SSE |
|
||||||
|
| **ADR-027 AI Admin Console** | `specs/06-Decision-Records/ADR-027-ai-admin-console-and-dynamic-control.md` | Dynamic control; admin-only |
|
||||||
|
| **ADR-028 Migration Refactor** | `specs/06-Decision-Records/ADR-028-migration-architecture-refactor.md` | Staging Queue; cleanup |
|
||||||
|
| **Backend Guidelines** | `specs/05-Engineering-Guidelines/05-02-backend-guidelines.md` | NestJS patterns |
|
||||||
|
| **Frontend Guidelines** | `specs/05-Engineering-Guidelines/05-03-frontend-guidelines.md` | Next.js patterns |
|
||||||
|
| **Testing Strategy** | `specs/05-Engineering-Guidelines/05-04-testing-strategy.md` | Coverage goals |
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Forbidden Actions
|
||||||
|
|
||||||
|
## ❌ Never Do This
|
||||||
|
|
||||||
|
| ❌ Forbidden | ✅ Correct Approach | ⚠️ Why |
|
||||||
|
| ----------------------------------------------- | ------------------------------------------------------- | ---------------------------------------------------- |
|
||||||
|
| SQL Triggers for business logic | NestJS Service methods | Untestable; bypasses audit log |
|
||||||
|
| `.env` files in production | `docker-compose.yml` environment section | Secrets exposed in version control |
|
||||||
|
| TypeORM migration files | Edit schema SQL directly (ADR-009) | Migration drift risk; schema managed via SQL delta |
|
||||||
|
| Inventing table/column names | Verify against `schema-02-tables.sql` | Schema mismatch causes silent runtime errors |
|
||||||
|
| `any` TypeScript type | Proper types / generics | Defeats strict mode; hides runtime type errors |
|
||||||
|
| `console.log` in committed code | NestJS Logger (backend) / remove (frontend) | Log flooding in production; risk of data leakage |
|
||||||
|
| `req: any` in controllers | `RequestWithUser` typed interface | Type safety lost; auth context unreachable |
|
||||||
|
| `parseInt()` on UUID values | Use UUID string directly (ADR-019) | `"0195…"` parsed to integer `19` — silently wrong |
|
||||||
|
| Exposing INT PK in API responses | UUIDv7 `publicId` (ADR-019) | Leaks row count; enables DB enumeration attacks |
|
||||||
|
| AI accessing DB/storage directly | AI → DMS API → DB (ADR-023/023A) | Bypasses RBAC, audit trail, and validation layer |
|
||||||
|
| Direct file operations bypassing StorageService | `StorageService` for all file moves | Orphaned files; broken ClamAV scan; no audit trail |
|
||||||
|
| Inline email/notification sending | BullMQ queue job (ADR-008) | Blocks request thread; no retry on transient failure |
|
||||||
|
| Deploying without Release Gates | Complete `04-08-release-management-policy.md` | Unverified deploy risks data loss in production |
|
||||||
|
| AI direct cloud API calls | On-premises Ollama only (ADR-023/023A) | Data privacy violation; no audit control |
|
||||||
|
| AI outputs without human validation | Human-in-the-loop validation required (ADR-023/023A) | Unvalidated AI metadata corrupts document records |
|
||||||
|
| n8n calling Ollama/Qdrant directly | n8n → DMS API → BullMQ → Ollama (ADR-023A) | Bypasses audit log, RBAC, and error handling layer |
|
||||||
|
| Qdrant query without `projectPublicId` filter | `QdrantService.search(projectPublicId, ...)` (ADR-023A) | Cross-project data leak via vector search |
|
||||||
|
|
||||||
|
## Schema Changes (ADR-009)
|
||||||
|
|
||||||
|
- **NO TypeORM migrations** — edit SQL schema directly
|
||||||
|
- Always check `specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql` before writing queries
|
||||||
|
- Update Data Dictionary when changing fields
|
||||||
|
|
||||||
|
## UUID Handling
|
||||||
|
|
||||||
|
See `01-adr-019-uuid.md` for complete UUID rules.
|
||||||
|
|
||||||
|
Quick reminder:
|
||||||
|
|
||||||
|
- ❌ `parseInt(uuid)` → NEVER
|
||||||
|
- ❌ `Number(uuid)` → NEVER
|
||||||
|
- ✅ Use UUID string directly
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# Backend Patterns (NestJS)
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
- **Thin Controller** — business logic in Service layer
|
||||||
|
- **DTO Validation** — class-validator + class-transformer
|
||||||
|
- **RBAC** — CASL for authorization
|
||||||
|
- **Error Handling** — Logger + HttpException
|
||||||
|
|
||||||
|
## UUID Resolution Pattern
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Controller - accept UUID in DTO
|
||||||
|
@Post()
|
||||||
|
async create(@Body() dto: CreateCorrespondenceDto) {
|
||||||
|
// Resolve UUID to internal ID
|
||||||
|
const contract = await this.contractService.findOneByUuid(dto.contractUuid);
|
||||||
|
const contractId = contract.id; // Internal INT for DB queries
|
||||||
|
|
||||||
|
return this.service.create(dto, contractId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service - use internal ID for DB operations
|
||||||
|
async create(dto: CreateCorrespondenceDto, contractId: number) {
|
||||||
|
// Use contractId (INT) for database queries
|
||||||
|
const correspondence = this.repo.create({
|
||||||
|
contractId, // FK is INT
|
||||||
|
// ... other fields
|
||||||
|
});
|
||||||
|
return this.repo.save(correspondence);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Response Pattern
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Entity
|
||||||
|
@Entity()
|
||||||
|
class Contract extends UuidBaseEntity {
|
||||||
|
@Column({ type: 'uuid' })
|
||||||
|
publicId: string;
|
||||||
|
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
@Exclude()
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response automatically includes publicId as 'id'
|
||||||
|
// { id: "019505a1-7c3e-7000-8000-abc123def456", ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Full Guidelines
|
||||||
|
|
||||||
|
`specs/05-Engineering-Guidelines/05-02-backend-guidelines.md`
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Frontend Patterns (Next.js)
|
||||||
|
|
||||||
|
## Form Handling
|
||||||
|
|
||||||
|
- **RHF** (React Hook Form) for form management
|
||||||
|
- **Zod** for validation schema
|
||||||
|
- **TanStack Query** for server state
|
||||||
|
|
||||||
|
## UUID Handling
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ CORRECT - Use publicId only
|
||||||
|
interface ProjectOption {
|
||||||
|
publicId?: string;
|
||||||
|
projectName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select options
|
||||||
|
const options = contracts.map((c) => ({
|
||||||
|
label: `${c.contractName} (${c.contractCode})`,
|
||||||
|
value: c.publicId!, // Use publicId, no fallback to id
|
||||||
|
}));
|
||||||
|
|
||||||
|
// ❌ WRONG - Never use these patterns
|
||||||
|
const value = c.publicId ?? c.id ?? ''; // Wrong!
|
||||||
|
const id = parseInt(projectId); // Wrong - parseInt on UUID!
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Client Pattern
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Use publicId directly in API calls
|
||||||
|
const contract = await contractService.getById(publicId);
|
||||||
|
|
||||||
|
// Form submission with UUID
|
||||||
|
const onSubmit = async (data: FormData) => {
|
||||||
|
await correspondenceService.create({
|
||||||
|
contractUuid: selectedContract.publicId!, // UUID string
|
||||||
|
// ... other fields
|
||||||
|
});
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Full Guidelines
|
||||||
|
|
||||||
|
`specs/05-Engineering-Guidelines/05-03-frontend-guidelines.md`
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
# Development Flow
|
||||||
|
|
||||||
|
## 🔴 Critical Work — DB / API / Security / Workflow Engine
|
||||||
|
|
||||||
|
**MUST complete all steps:**
|
||||||
|
|
||||||
|
1. **Glossary check** — verify domain terms in `00-02-glossary.md`
|
||||||
|
2. **Read the spec** — select from Key Spec Files table
|
||||||
|
3. **Check schema** — verify table/column in `lcbp3-v1.9.0-schema-02-tables.sql`
|
||||||
|
4. **Check data dictionary** — confirm field meanings + business rules
|
||||||
|
5. **Scan edge cases** — `01-06-edge-cases-and-rules.md`
|
||||||
|
6. **Check ADRs** — verify decisions align (ADR-009, ADR-019, ADR-023)
|
||||||
|
7. **Write code** — TypeScript strict, no `any`, no `console.log`
|
||||||
|
|
||||||
|
## 🟡 Normal Work — UI / Feature / Integration
|
||||||
|
|
||||||
|
- Follow existing patterns in codebase.
|
||||||
|
- Check spec for relevant module only.
|
||||||
|
- **Hybrid Specs Organization:**
|
||||||
|
- Place new Infrastructure tasks in `specs/100-Infrastructures/`
|
||||||
|
- Place new Feature/Workflow tasks in `specs/200-fullstacks/`
|
||||||
|
- Place Documentation/Research in `specs/300-others/`
|
||||||
|
- Ensure no forbidden patterns (`any`, `console.log`, UUID misuse) are introduced.
|
||||||
|
|
||||||
|
## 🟢 Quick Fix — Bug Fix / Typo / Style
|
||||||
|
|
||||||
|
- Fix directly
|
||||||
|
- Add minimal test if logic changed
|
||||||
|
- Check forbidden patterns before commit
|
||||||
|
|
||||||
|
### 🟢 Specialized Work — ADR-021, AI Runtime Layer, Complex Logic
|
||||||
|
|
||||||
|
**MUST complete:**
|
||||||
|
|
||||||
|
1. **Domain Knowledge Check** - Read relevant ADRs (ADR-021, ADR-023/023A, ADR-024~028)
|
||||||
|
2. **Pattern Verification** - Check existing implementations in codebase
|
||||||
|
3. **Specialized Requirements** - Follow domain-specific patterns
|
||||||
|
4. **Complex Logic Testing** - Multi-scenario test coverage
|
||||||
|
5. **Performance Validation** - Load testing if applicable
|
||||||
|
|
||||||
|
**For ADR-021 Integration:**
|
||||||
|
|
||||||
|
- Read ADR-021 - Integrated workflow & step attachments
|
||||||
|
- Check ADR-001 - Unified workflow engine patterns
|
||||||
|
- Verify WorkflowEngineService - Polymorphic instance handling
|
||||||
|
- Add workflow fields - Expose workflowInstanceId, workflowState, availableActions
|
||||||
|
- Include IntegratedBanner - Frontend workflow lifecycle display
|
||||||
|
- Test workflow transitions - State changes and action validation
|
||||||
|
|
||||||
|
**For AI Infrastructure (ADR-023/023A):**
|
||||||
|
|
||||||
|
- Verify AI boundary enforcement - No direct DB/storage access
|
||||||
|
- Check BullMQ 2-queue setup - ai-realtime + ai-batch
|
||||||
|
- Validate Qdrant multi-tenancy - projectPublicId filter required
|
||||||
|
- Test human-in-the-loop validation workflows
|
||||||
|
- Audit AI interaction logging to ai_audit_logs
|
||||||
|
|
||||||
|
**For AI Runtime Layer (ADR-024/025/026/027):**
|
||||||
|
|
||||||
|
- ADR-024: Pattern Layer first (ai_intent_patterns DB + Redis cache 5 min) → LLM Fallback (gemma4:e4b, semaphore max=3)
|
||||||
|
- ADR-025: Tool Registry dispatch — AI Gateway → Tool → Business Service; ToolResult DTO must use publicId only
|
||||||
|
- ADR-026: useAiChat() hook + side-panel UI; streaming response via SSE; TanStack Query cache
|
||||||
|
- ADR-027: Admin Console — dynamic model/prompt/intent control; CASL-guarded admin-only endpoints
|
||||||
|
|
||||||
|
**For Migration Pipeline (ADR-028):**
|
||||||
|
|
||||||
|
- Use Staging Queue pattern — never write directly to production tables
|
||||||
|
- Post-migration cleanup process required after each batch
|
||||||
|
- Migration Validation Gates must pass before promoting to production
|
||||||
|
|
||||||
|
**Expected output:**
|
||||||
|
|
||||||
|
- Backend services expose specialized context fields
|
||||||
|
- Frontend components use domain-specific patterns
|
||||||
|
- Complex state management with proper validation
|
||||||
|
- Performance metrics within acceptable thresholds
|
||||||
|
- Comprehensive test coverage for edge cases
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Context-Aware Triggers
|
||||||
|
|
||||||
|
| Request | Files to Check | Expected Response |
|
||||||
|
| --------------------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
||||||
|
| "สร้าง API ใหม่" | `05-02-backend-guidelines.md`, `lcbp3-v1.9.0-schema-02-tables.sql` | NestJS Controller + Service + DTO + CASL Guard |
|
||||||
|
| "แก้ฟอร์ม frontend" | `05-03-frontend-guidelines.md`, `01-06-edge-cases-and-rules.md` | RHF+Zod + TanStack Query + Thai comments |
|
||||||
|
| "เพิ่ม field ใหม่" | `ADR-009`, `03-01-data-dictionary.md`, `lcbp3-v1.9.0-schema-02-tables.sql` | Edit SQL directly + update Data Dictionary + Entity |
|
||||||
|
| "ตรวจสอบ UUID" | `ADR-019`, `05-07-hybrid-uuid-implementation-plan.md` | UUIDv7 MariaDB native UUID + TransformInterceptor |
|
||||||
|
| "สร้าง migration" | `ADR-009`, `03-06-migration-business-scope.md` | Edit SQL schema directly + n8n workflow |
|
||||||
|
| "ตรวจสอบ permission" | `lcbp3-v1.9.0-seed-permissions.sql`, `ADR-016` | CASL 4-Level RBAC matrix |
|
||||||
|
| "deploy production" | `04-08-release-management-policy.md`, `ADR-015` | Release Gates + Blue-Green strategy |
|
||||||
|
| "เพิ่ม test" | `05-04-testing-strategy.md` | Coverage goals + test patterns |
|
||||||
|
| "AI integration" | `ADR-023`, `ADR-023A`, `ADR-024`, `ADR-025` | AI boundary + 2-model stack + BullMQ queue policy + Intent/Tool Layer |
|
||||||
|
| "Error handling" | `ADR-007` | Layered error classification + recovery |
|
||||||
|
| "File upload" | `ADR-016`, `05-02-backend-guidelines.md`, `03-Data-and-Storage/03-03-file-storage.md` | Two-phase upload → temp → commit; ClamAV + whitelist |
|
||||||
|
| "Notifications / Queue" | `ADR-008`, `05-02-backend-guidelines.md` | BullMQ job — never inline; check retry + dead-letter |
|
||||||
|
| "Add i18n / translate" | `05-08-i18n-guidelines.md` | i18n keys only — no hardcoded text |
|
||||||
|
| "Workflow / DSL" | `ADR-001`, `01-03-modules/01-03-06-unified-workflow.md` | DSL state machine + WorkflowEngineService |
|
||||||
|
| "Document numbering" | `ADR-002`, `01-02-business-rules/01-02-02-doc-numbering-rules.md` | Redis Redlock + DB optimistic lock (double-lock) |
|
||||||
|
| "ตรวจสอบ Workflow" | `01-06-edge-cases-and-rules.md`, `05-02-backend-guidelines.md`, `ADR-001`, `ADR-002` | เช็คการเปลี่ยน State, คิว BullMQ และการล็อกเลขที่เอกสาร |
|
||||||
|
| "Transmittal submit" | `ADR-021`, `specs/200-fullstacks/201-transmittals-circulation/` | submit() with EC-RFA-004 validation |
|
||||||
|
| "Circulation reassign" | `ADR-021`, `specs/200-fullstacks/201-transmittals-circulation/` | reassignRouting() with EC-CIRC-001 |
|
||||||
|
| "สร้าง workflow ใหม่" | `ADR-001`, `ADR-021`, `specs/200-fullstacks/203-unified-workflow-engine/` | DSL workflow definition + WorkflowEngineService setup |
|
||||||
|
| "ตรวจสอบ AI boundary" | `ADR-023`, `ADR-023A` | Verify Ollama isolation + BullMQ queues + Qdrant projectPublicId filter |
|
||||||
|
| "Intent classification" | `ADR-024`, `specs/200-fullstacks/224-intent-classification/` | Pattern Layer → LLM Fallback; ai_intent_patterns; Redis cache 5 min |
|
||||||
|
| "AI Tool Layer" | `ADR-025`, `specs/200-fullstacks/225-ai-tool-layer-architecture/` | Tool Registry; CASL-guarded dispatch; ToolResult publicId only |
|
||||||
|
| "Document Chat UI" | `ADR-026`, `specs/200-fullstacks/226-document-chat-ui-pattern/` | Side-panel; useAiChat() hook; streaming SSE; TanStack Query cache |
|
||||||
|
| "AI Admin Console" | `ADR-027`, `specs/200-fullstacks/227-ai-admin-console/` | Dynamic model/prompt/intent control; admin-only CASL endpoints |
|
||||||
|
| "Migration refactor" | `ADR-028`, `specs/200-fullstacks/228-migration-arch-refactor/` | Staging Queue; post-migration cleanup; validation gates |
|
||||||
|
| "จัดการ document numbering" | `ADR-002`, `specs/03-Data-and-Storage/03-04-document-numbering.md` | Redis Redlock + template system + preview/override workflows |
|
||||||
|
| "Audit ความปลอดภัย" | `ADR-016`, `ADR-019`, `ADR-023`, `ADR-023A` | ตรวจสอบ UUID pattern, CASL Guard, AI Boundary และ Qdrant multi-tenancy |
|
||||||
|
| "แก้ bug / bugfix" | `.agents/workflows/bugfix.md`, `error-catalog.md` | ใช้ bugfix workflow สำหรับเคสที่สาเหตุชัดเจน |
|
||||||
|
| "ตรวจแอปจริง" | `.windsurf/workflows/check-real-app.md` | ตรวจ endpoint/UI/console หลัง build pass — No Fake Evidence |
|
||||||
|
| "งานค้าง / resume" | `.windsurf/workflows/resume-pending-work.md` | อ่าน checkpoint เดิม → ตรวจ build → วางแผนต่อโดยไม่ทำงานซ้ำ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔌 MCP MariaDB Tools
|
||||||
|
|
||||||
|
MCP MariaDB server ให้เครื่องมือสำหรับตรวจสอบและจัดการ database โดยตรง ใช้สำหรับ:
|
||||||
|
|
||||||
|
- ตรวจสอบ schema กับ spec file `specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql`
|
||||||
|
- Debug ปัญหา database โดยไม่ต้องเข้า MySQL client
|
||||||
|
- ตรวจสอบ data ใน production/staging
|
||||||
|
- Validate การเปลี่ยนแปลง schema ก่อน deploy
|
||||||
|
|
||||||
|
### Available Tools
|
||||||
|
|
||||||
|
| Tool | หน้าที่ | ตัวอย่างการใช้งาน |
|
||||||
|
|------|----------|------------------|
|
||||||
|
| `mcp1_mysql_test_connection` | ทดสอบ connection กับ database | ตรวจสอบว่า MCP server เชื่อมต่อได้ |
|
||||||
|
| `mcp1_mysql_show_databases` | แสดง databases ทั้งหมด | ดูว่ามี database อะไรบ้าง |
|
||||||
|
| `mcp1_mysql_show_tables` | แสดง tables ทั้งหมดใน database | ดูรายชื่อ tables ใน `lcbp3` |
|
||||||
|
| `mcp1_mysql_describe_table` | ดู structure/columns ของ table | ตรวจสอบ columns, types, keys ของ `correspondences` |
|
||||||
|
| `mcp1_mysql_query` | รัน SELECT query | ดู data ใน table หรือ join query |
|
||||||
|
| `mcp1_mysql_insert` | INSERT data | เพิ่ม seed data หรือ test data |
|
||||||
|
| `mcp1_mysql_update` | UPDATE data | แก้ไข data ใน table |
|
||||||
|
| `mcp1_mysql_delete` | DELETE data | ลบ data ใน table |
|
||||||
|
|
||||||
|
### การใช้งานร่วมกับ Development Flow
|
||||||
|
|
||||||
|
**เมื่อเขียน query ใหม่:**
|
||||||
|
1. ใช้ `mcp1_mysql_describe_table` เพื่อตรวจสอบ columns และ types
|
||||||
|
2. เปรียบเทียบกับ `specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql`
|
||||||
|
3. ใช้ `mcp1_mysql_query` เพื่อทดสอบ query ก่อน implement
|
||||||
|
|
||||||
|
**เมื่อเปลี่ยน schema (ADR-009):**
|
||||||
|
1. ใช้ `mcp1_mysql_describe_table` เพื่อดู structure ปัจจุบัน
|
||||||
|
2. สร้าง SQL delta ใน `specs/03-Data-and-Storage/deltas/`
|
||||||
|
3. ใช้ `mcp1_mysql_query` เพื่อตรวจสอบผลลัพธ์หลัง apply delta
|
||||||
|
|
||||||
|
**เมื่อ debug ปัญหา database:**
|
||||||
|
1. ใช้ `mcp1_mysql_query` เพื่อดู data จริง
|
||||||
|
2. เปรียบเทียบกับ spec และ data dictionary
|
||||||
|
3. ตรวจสอบ foreign keys และ constraints
|
||||||
|
|
||||||
|
### ข้อควรระวัง
|
||||||
|
|
||||||
|
- **❌ ห้ามใช้ MCP MariaDB สำหรับ DDL operations** (CREATE/ALTER/DROP) โดยตรง — ต้องใช้ SQL delta ตาม ADR-009
|
||||||
|
- **✅ ใช้สำหรับ DQL/DML operations** (SELECT/INSERT/UPDATE/DELETE) เพื่อ debug และ test เท่านั้น
|
||||||
|
- **⚠️ ระวัง DELETE operations** — อาจทำให้เสีย data ใน production
|
||||||
|
- **✅ ตรวจสอบ schema กับ spec file เสมอ** ก่อนเขียน query
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧠 MCP Memory Tools
|
||||||
|
|
||||||
|
MCP Memory server ให้เครื่องมือสำหรับจัดการ Knowledge Graph และ Long-term Memory ใช้สำหรับ:
|
||||||
|
|
||||||
|
- จัดเก็บความรู้และ context ของโปรเจกต์ในรูปแบบ Graph (Entities + Relations + Observations)
|
||||||
|
- ค้นหาและดึงข้อมูล context จาก memory ที่บันทึกไว้ใน session ก่อนหน้า
|
||||||
|
- สร้าง/แก้ไข/ลบ entities, relations, และ observations ใน knowledge graph
|
||||||
|
|
||||||
|
### Available Tools
|
||||||
|
|
||||||
|
| Tool | หน้าที่ | ตัวอย่างการใช้งาน |
|
||||||
|
|------|----------|------------------|
|
||||||
|
| `mcp3_create_entities` | สร้าง entities ใหม่หลายตัวพร้อม observations | สร้าง entity ใหม่เช่น Project, User, Task |
|
||||||
|
| `mcp3_create_relations` | สร้าง relations ระหว่าง entities | สร้าง relation: Project → has → User |
|
||||||
|
| `mcp3_add_observations` | เพิ่ม observations ให้ entity ที่มีอยู่แล้ว | เพิ่ม context เพิ่มเติมให้ entity |
|
||||||
|
| `mcp3_delete_entities` | ลบ entities และ relations ที่เกี่ยวข้อง | ลบ entity ที่ไม่ใช้แล้ว |
|
||||||
|
| `mcp3_delete_relations` | ลบ relations ระหว่าง entities | ลบ relation ที่ผิดหรือไม่ใช้แล้ว |
|
||||||
|
| `mcp3_delete_observations` | ลบ observations จาก entity | ลบ context ที่ผิดหรือล้าสุด |
|
||||||
|
| `mcp3_open_nodes` | ดึงข้อมูล entities ตามชื่อ | ดึง entity ที่ระบุชื่อ |
|
||||||
|
| `mcp3_read_graph` | อ่าน knowledge graph ทั้งหมด | ดูทั้ง graph structure |
|
||||||
|
| `mcp3_search_nodes` | ค้นหา entities ตาม query | ค้นหา entity จากชื่อ, type, หรือ observation |
|
||||||
|
|
||||||
|
### การใช้งานร่วมกับ Development Flow
|
||||||
|
|
||||||
|
**เมื่อบันทึก context ใหม่:**
|
||||||
|
1. ใช้ `mcp3_create_entities` เพื่อสร้าง entities ใหม่ (ถ้ายังไม่มี)
|
||||||
|
2. ใช้ `mcp3_create_relations` เพื่อเชื่อมโยง entities
|
||||||
|
3. ใช้ `mcp3_add_observations` เพื่อเพิ่ม context/observations
|
||||||
|
|
||||||
|
**เมื่อค้นหา context:**
|
||||||
|
1. ใช้ `mcp3_search_nodes` เพื่อค้นหา entities ที่เกี่ยวข้อง
|
||||||
|
2. ใช้ `mcp3_open_nodes` เพื่อดึงข้อมูล entities ที่ต้องการ
|
||||||
|
3. ใช้ `mcp3_read_graph` เพื่อดู relations ระหว่าง entities
|
||||||
|
|
||||||
|
**เมื่อแก้ไข context:**
|
||||||
|
1. ใช้ `mcp3_add_observations` เพื่อเพิ่ม observations ใหม่
|
||||||
|
2. ใช้ `mcp3_delete_observations` เพื่อลบ observations ที่ผิด
|
||||||
|
3. ใช้ `mcp3_create_relations` หรือ `mcp3_delete_relations` เพื่อปรับ relations
|
||||||
|
|
||||||
|
### ข้อควรระวัง
|
||||||
|
|
||||||
|
- **✅ ใช้สำหรับบันทึก context ที่ต้องใช้ร่วมกันหลาย session** — เช่น การตัดสินใจสำคัญ, architecture decisions, rollout history
|
||||||
|
- **⚠️ ระวังการลบ entities** — อาจทำให้เสีย context ที่ยังใช้งานอยู่
|
||||||
|
- **✅ ตรวจสอบว่า entity มีอยู่แล้วก่อนสร้าง** — ใช้ `mcp3_search_nodes` หรือ `mcp3_open_nodes` ก่อน
|
||||||
|
- **✅ ใช้ชื่อ entity ที่ชัดเจนและไม่ซ้ำกัน** — เพื่อป้องกันความสับสน
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Commit Checklist
|
||||||
|
|
||||||
|
## Pre-Commit Verification
|
||||||
|
|
||||||
|
- [ ] UUID pattern verified (no parseInt on UUID)
|
||||||
|
- [ ] No `any` types in TypeScript
|
||||||
|
- [ ] No `console.log` in committed code
|
||||||
|
- [ ] Comments in Thai
|
||||||
|
- [ ] Code identifiers in English
|
||||||
|
- [ ] Schema changes via SQL directly (not migration)
|
||||||
|
- [ ] Test coverage meets targets (Backend 70%+, Business Logic 80%+)
|
||||||
|
- [ ] Relevant ADRs checked (ADR-009, ADR-018, ADR-019)
|
||||||
|
- [ ] Glossary terms used correctly
|
||||||
|
- [ ] Error handling complete (Logger + HttpException)
|
||||||
|
- [ ] i18n keys used instead of hardcode text
|
||||||
|
- [ ] Cache invalidation when data modified
|
||||||
|
- [ ] Security checklist passed (OWASP Top 10)
|
||||||
|
|
||||||
|
## Commit Message Format
|
||||||
|
|
||||||
|
```
|
||||||
|
type(scope): description
|
||||||
|
|
||||||
|
[optional body]
|
||||||
|
```
|
||||||
|
|
||||||
|
Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- `feat(correspondence): add originator organization validation`
|
||||||
|
- `fix(uuid): correct parseInt usage to string comparison`
|
||||||
|
- `spec(agents): bump to v1.8.5 - refactor structure`
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# ADR-007 Error Handling Strategy
|
||||||
|
|
||||||
|
## CRITICAL RULES
|
||||||
|
|
||||||
|
- **ALWAYS** use layered error classification (Validation, Business, System)
|
||||||
|
- **NEVER** expose technical details to end users
|
||||||
|
- **ALWAYS** provide user-friendly error messages with recovery guidance
|
||||||
|
- **ALWAYS** log technical details for debugging
|
||||||
|
- **NEVER** use generic error messages without context
|
||||||
|
|
||||||
|
## Error Classification
|
||||||
|
|
||||||
|
| Error Type | Description | User Message | Technical Log |
|
||||||
|
| -------------- | ------------------------- | ------------------------ | -------------------------- |
|
||||||
|
| **Validation** | Input validation failures | Clear field-level errors | Full validation details |
|
||||||
|
| **Business** | Business rule violations | Actionable guidance | Business context + user ID |
|
||||||
|
| **System** | Infrastructure failures | Generic "try again" | Full stack trace + metrics |
|
||||||
|
|
||||||
|
## Backend Pattern (NestJS)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Custom Exception Hierarchy
|
||||||
|
export class BusinessException extends HttpException {
|
||||||
|
constructor(message: string, userMessage: string, recoveryAction?: string, errorCode?: string) {
|
||||||
|
super({ message, userMessage, recoveryAction, errorCode }, 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global Exception Filter
|
||||||
|
@Catch()
|
||||||
|
export class GlobalExceptionFilter implements ExceptionFilter {
|
||||||
|
catch(exception: unknown, host: ArgumentsHost) {
|
||||||
|
// Classify error and provide appropriate response
|
||||||
|
// Log technical details
|
||||||
|
// Return user-friendly message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Frontend Pattern (Next.js)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Error Display Component
|
||||||
|
const ErrorDisplay = ({ error, onRetry }) => {
|
||||||
|
const userMessage = error.userMessage || 'เกิดข้อผิดพลาด';
|
||||||
|
const recoveryAction = error.recoveryAction;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>{userMessage}</p>
|
||||||
|
{recoveryAction && <p>{recoveryAction}</p>}
|
||||||
|
{onRetry && <button onClick={onRetry}>ลองใหม่</button>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Required Implementation
|
||||||
|
|
||||||
|
- [ ] Global Exception Filter with layered classification
|
||||||
|
- [ ] Custom exception hierarchy (Validation, Business, System)
|
||||||
|
- [ ] Standardized error response DTOs
|
||||||
|
- [ ] Frontend error display components
|
||||||
|
- [ ] Error recovery mechanisms where applicable
|
||||||
|
|
||||||
|
## Related Documents
|
||||||
|
|
||||||
|
- `specs/06-Decision-Records/ADR-007-error-handling-strategy.md`
|
||||||
|
- `specs/06-Decision-Records/ADR-010-logging-monitoring-strategy.md`
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
# ADR-023/023A AI Integration Architecture
|
||||||
|
|
||||||
|
## CRITICAL RULES
|
||||||
|
|
||||||
|
- **ALWAYS** follow ADR-023 AI boundary policy (isolation on Admin Desktop)
|
||||||
|
- **ALWAYS** use ADR-034 model stack (typhoon2.5-np-dms:latest + typhoon-np-dms-ocr:latest + nomic-embed-text)
|
||||||
|
- **ALWAYS** use BullMQ 2-queue (ai-realtime + ai-batch) for GPU overload prevention
|
||||||
|
- **NEVER** allow AI direct database/storage access
|
||||||
|
- **ALWAYS** implement human-in-the-loop validation
|
||||||
|
- **NEVER** send sensitive data to cloud AI services
|
||||||
|
- **ALWAYS** enforce Qdrant projectPublicId filter (compile-time enforcement)
|
||||||
|
- **NEVER** allow n8n to call Ollama/Qdrant directly (must go through DMS API → BullMQ)
|
||||||
|
|
||||||
|
## AI Integration Patterns
|
||||||
|
|
||||||
|
### Architecture Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
Frontend → AI Gateway API → BullMQ → Admin Desktop (Ollama) → Backend Validation
|
||||||
|
n8n (Migration) → DMS API → BullMQ → Admin Desktop (Ollama) → Backend Validation
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Components
|
||||||
|
|
||||||
|
| Component | Location | Purpose |
|
||||||
|
| ----------------- | ------------------------- | ------------------------------------------------------------------------ |
|
||||||
|
| **AI Gateway** | Backend (NestJS) | API endpoints, validation, audit logging |
|
||||||
|
| **BullMQ Queues** | Backend (NestJS) | ai-realtime (RAG/Suggest), ai-batch (OCR/Extract/Embed) |
|
||||||
|
| **Ollama Engine** | Admin Desktop (Desk-5439) | typhoon2.5-np-dms:latest (Main LLM) + typhoon-np-dms-ocr:latest (OCR, keep_alive:0) + nomic-embed-text (Embedding) |
|
||||||
|
| **OCR Engine** | Admin Desktop (Desk-5439) | Tesseract OCR + Typhoon OCR (via Ollama) + PyThaiNLP (Thai/English text extraction) |
|
||||||
|
| **Orchestrator** | QNAP NAS (n8n) | Migration Phase orchestrator only (calls DMS API, never Ollama directly) |
|
||||||
|
|
||||||
|
## Backend Implementation (NestJS)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// AI Module with boundary enforcement
|
||||||
|
@Module({
|
||||||
|
controllers: [AiController],
|
||||||
|
providers: [AiService, AiGateway, QdrantService],
|
||||||
|
exports: [AiService],
|
||||||
|
})
|
||||||
|
export class AiModule {
|
||||||
|
constructor() {
|
||||||
|
// Enforce ADR-023 boundaries
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QdrantService with compile-time projectPublicId enforcement
|
||||||
|
@Injectable()
|
||||||
|
export class QdrantService {
|
||||||
|
async search(
|
||||||
|
projectPublicId: string, // required — compile-time enforcement
|
||||||
|
vector: number[],
|
||||||
|
topK: number = 5,
|
||||||
|
): Promise<QdrantSearchResult[]> {
|
||||||
|
return this.client.search('documents', {
|
||||||
|
vector,
|
||||||
|
limit: topK,
|
||||||
|
filter: {
|
||||||
|
must: [{ key: 'project_public_id', match: { value: projectPublicId } }],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async upsert(
|
||||||
|
projectPublicId: string, // required
|
||||||
|
chunks: DocumentChunk[],
|
||||||
|
): Promise<void> { ... }
|
||||||
|
|
||||||
|
// ❌ NEVER expose rawSearch() or method without projectPublicId filter
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI Service with validation
|
||||||
|
@Injectable()
|
||||||
|
export class AiService {
|
||||||
|
async extractMetadata(documentId: string): Promise<AIMetadata> {
|
||||||
|
// 1. Validate permissions
|
||||||
|
// 2. Queue job to BullMQ (ai-batch or ai-realtime)
|
||||||
|
// 3. Worker sends to Admin Desktop AI (typhoon2.5-np-dms:latest)
|
||||||
|
// 4. Validate AI response
|
||||||
|
// 5. Log audit trail to ai_audit_logs
|
||||||
|
// 6. Return validated results
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Frontend Pattern (Next.js)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Document Review Form (reusable component)
|
||||||
|
const DocumentReviewForm = ({ document, aiSuggestions }) => {
|
||||||
|
return (
|
||||||
|
<form>
|
||||||
|
<Field label="Document Type" suggestions={aiSuggestions.documentType} />
|
||||||
|
<Field label="Project Code" suggestions={aiSuggestions.projectCode} />
|
||||||
|
<Field label="Discipline" suggestions={aiSuggestions.discipline} />
|
||||||
|
|
||||||
|
<ConfidenceScore score={aiSuggestions.confidence} />
|
||||||
|
<HumanValidationActions />
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Requirements
|
||||||
|
|
||||||
|
- **AI Isolation:** All AI processing on Admin Desktop only (Desk-5439)
|
||||||
|
- **Data Privacy:** No cloud AI services, on-premises only
|
||||||
|
- **Audit Trail:** Log all AI interactions and human validations to ai_audit_logs
|
||||||
|
- **Rate Limiting:** Prevent AI abuse and resource exhaustion
|
||||||
|
- **Validation:** All AI outputs must be validated before use
|
||||||
|
- **Multi-tenant Isolation:** Qdrant queries MUST include projectPublicId filter (compile-time enforcement)
|
||||||
|
- **n8n Boundary:** n8n MUST call DMS API → BullMQ, NEVER Ollama/Qdrant directly
|
||||||
|
- **GPU Overload Prevention:** BullMQ 2-queue (ai-realtime + ai-batch) with concurrency=1
|
||||||
|
|
||||||
|
## ADR-034 Model Stack (supersedes ADR-023A §2.1)
|
||||||
|
|
||||||
|
- **3-Model Config:** typhoon2.5-np-dms:latest (Main) + typhoon-np-dms-ocr:latest (OCR, keep_alive:0) + nomic-embed-text (Embedding)
|
||||||
|
- **PDF 3-Page Limit:** Classification/Tagging uses first 3 pages only (NOT RAG embedding)
|
||||||
|
- **RAG Embedding:** Full document chunked at 512 tokens/64 tokens overlap
|
||||||
|
- **OCR Auto-Detect:** PyMuPDF chars > 100 → Fast path, else Tesseract OCR (with Typhoon OCR option)
|
||||||
|
- **Embed Auto-Trigger:** AUTO after commit (parallel), gap covered by DB search
|
||||||
|
- **Threshold Recalibration:** After 100-500 docs, based on ai_audit_logs analysis
|
||||||
|
|
||||||
|
## Required Implementation
|
||||||
|
|
||||||
|
- [ ] AiModule with ADR-023 boundary enforcement
|
||||||
|
- [ ] AI Gateway API endpoints with validation
|
||||||
|
- [ ] BullMQ 2-queue setup (ai-realtime + ai-batch)
|
||||||
|
- [ ] QdrantService with projectPublicId enforcement
|
||||||
|
- [ ] DocumentReviewForm reusable component
|
||||||
|
- [ ] Admin Desktop Ollama (typhoon2.5-np-dms:latest + typhoon-np-dms-ocr:latest + nomic-embed-text) setup
|
||||||
|
- [ ] n8n workflow orchestration (Migration Phase only)
|
||||||
|
- [ ] AI audit logging and monitoring (ai_audit_logs)
|
||||||
|
- [ ] Human-in-the-loop validation workflows
|
||||||
|
|
||||||
|
## Related Documents
|
||||||
|
|
||||||
|
- `specs/06-Decision-Records/ADR-023-unified-ai-architecture.md` (Base architecture)
|
||||||
|
- `specs/06-Decision-Records/ADR-023A-unified-ai-architecture.md` (Model revision - current)
|
||||||
|
- `specs/06-Decision-Records/ADR-024-intent-classification-strategy.md` (Pattern→LLM Fallback)
|
||||||
|
- `specs/06-Decision-Records/ADR-025-ai-tool-layer-architecture.md` (Tool Registry)
|
||||||
|
- `specs/06-Decision-Records/ADR-026-document-chat-ui-pattern.md` (Chat UI)
|
||||||
|
- `specs/06-Decision-Records/ADR-027-ai-admin-console-and-dynamic-control.md` (Admin Console)
|
||||||
|
- `specs/06-Decision-Records/ADR-028-migration-architecture-refactor.md` (Migration Pipeline)
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
# LCBP3 Agent Rules
|
||||||
|
|
||||||
|
Critical rules and guidelines for AI agents working on LCBP3-DMS.
|
||||||
|
|
||||||
|
## Version
|
||||||
|
|
||||||
|
- **Current:** v1.9.6
|
||||||
|
- **Last Updated:** 2026-05-22
|
||||||
|
- **Synced with:** `AGENTS.md` (v1.9.6)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This directory contains rule files that define:
|
||||||
|
|
||||||
|
- Project context and role expectations
|
||||||
|
- Critical Tier 1 rules (CI blockers)
|
||||||
|
- Coding standards and patterns
|
||||||
|
- Domain terminology and glossary
|
||||||
|
- Development workflows
|
||||||
|
- Security requirements
|
||||||
|
- AI integration architecture (ADR-023/023A)
|
||||||
|
|
||||||
|
## Rule Enforcement Tiers
|
||||||
|
|
||||||
|
### 🔴 Tier 1 — CRITICAL (CI BLOCKER)
|
||||||
|
|
||||||
|
Build fails immediately if violated:
|
||||||
|
|
||||||
|
- Security (Auth, RBAC, Validation)
|
||||||
|
- UUID Strategy (ADR-019) — no `parseInt` / `Number` / `+` on UUID
|
||||||
|
- Database correctness — verify schema before writing queries
|
||||||
|
- File upload security (ClamAV + whitelist)
|
||||||
|
- AI validation boundary (ADR-023)
|
||||||
|
- Error handling strategy (ADR-007)
|
||||||
|
- Forbidden patterns: `any`, `console.log`, UUID misuse, `id ?? ''` fallback
|
||||||
|
|
||||||
|
### 🟡 Tier 2 — IMPORTANT (CODE REVIEW)
|
||||||
|
|
||||||
|
Must fix before merge:
|
||||||
|
|
||||||
|
- Architecture patterns (thin controller, business logic in service)
|
||||||
|
- Test coverage (80%+ business logic, 70%+ backend overall)
|
||||||
|
- Cache invalidation
|
||||||
|
- Naming conventions
|
||||||
|
- TypeScript Standards: Missing JSDoc, explicit types, or file headers
|
||||||
|
|
||||||
|
### 🟢 Tier 3 — SPECIALIZED WORK
|
||||||
|
|
||||||
|
Requires domain-specific knowledge:
|
||||||
|
|
||||||
|
- **ADR-021 Integration:** Workflow Engine & Context implementation
|
||||||
|
- **AI Infrastructure:** ADR-023/023A boundary enforcement and pipeline usage
|
||||||
|
- **AI Runtime Layer:** ADR-024 Intent Classification, ADR-025 Tool Layer, ADR-026 Chat UI, ADR-027 Admin Console
|
||||||
|
- **Migration Pipeline:** ADR-028 Staging Queue & post-migration cleanup
|
||||||
|
- **Complex Business Logic:** Multi-step workflows with state management
|
||||||
|
- **Performance Optimization:** Database queries, caching strategies, bulk operations
|
||||||
|
|
||||||
|
### 🔵 Tier 4 — GUIDELINES
|
||||||
|
|
||||||
|
Best practice — follow when possible:
|
||||||
|
|
||||||
|
- Code style / formatting (Prettier handles)
|
||||||
|
- Comment completeness
|
||||||
|
- Minor optimizations
|
||||||
|
|
||||||
|
## Rule Files
|
||||||
|
|
||||||
|
### Core Rules (Tier 1 - CRITICAL)
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
| ----------------------- | ------------------------------------------------------------------------------- |
|
||||||
|
| `00-project-context.md` | Project context, role & persona, tier classification, specs folder organization |
|
||||||
|
| `01-adr-019-uuid.md` | UUID handling strategy — no parseInt, use publicId only |
|
||||||
|
| `02-security.md` | Security requirements, checklist, ADR-023/023A AI boundaries |
|
||||||
|
|
||||||
|
### Coding Standards
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
| ------------------------- | ------------------------------------------------------- |
|
||||||
|
| `03-typescript.md` | TypeScript rules, file headers, i18n guidelines |
|
||||||
|
| `06-backend-patterns.md` | NestJS patterns, UUID resolution, API response patterns |
|
||||||
|
| `07-frontend-patterns.md` | Next.js patterns, RHF+Zod+TanStack Query, UUID handling |
|
||||||
|
|
||||||
|
### Domain & Workflow
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
| -------------------------- | ------------------------------------------------------------- |
|
||||||
|
| `04-domain-terminology.md` | DMS glossary, key spec files priority table |
|
||||||
|
| `08-development-flow.md` | Development workflow by work type (Critical/Normal/Quick Fix) |
|
||||||
|
|
||||||
|
### Compliance & Architecture
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
| ------------------------- | -------------------------------------------------------------- |
|
||||||
|
| `05-forbidden-actions.md` | Actions that must never be done, schema changes, UUID handling |
|
||||||
|
| `09-commit-checklist.md` | Pre-commit verification, commit message format |
|
||||||
|
| `10-error-handling.md` | ADR-007 error handling strategy, layered classification |
|
||||||
|
| `11-ai-integration.md` | ADR-023/023A AI architecture, 2-model stack, BullMQ 2-queue |
|
||||||
|
|
||||||
|
## Key Spec Files Priority
|
||||||
|
|
||||||
|
Spec priority: **`06-Decision-Records`** > **`05-Engineering-Guidelines`** > others
|
||||||
|
|
||||||
|
| Document | Path | Use When |
|
||||||
|
| ------------------------------ | --------------------------------------------------------------------------- | --------------------------------- |
|
||||||
|
| **Glossary** | `specs/00-overview/00-02-glossary.md` | Verify domain terminology |
|
||||||
|
| **Schema Tables** | `specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql` | Before writing any query |
|
||||||
|
| **Data Dictionary** | `specs/03-Data-and-Storage/03-01-data-dictionary.md` | Field meanings + business rules |
|
||||||
|
| **Edge Cases** | `specs/01-Requirements/01-06-edge-cases-and-rules.md` | Prevent bugs in flows |
|
||||||
|
| **ADR-019 UUID** | `specs/06-Decision-Records/ADR-019-hybrid-identifier-strategy.md` | UUID-related work |
|
||||||
|
| **ADR-023 AI** | `specs/06-Decision-Records/ADR-023-unified-ai-architecture.md` | AI integration work |
|
||||||
|
| **ADR-023A AI Model** | `specs/06-Decision-Records/ADR-023A-unified-ai-architecture.md` | 2-model stack, BullMQ 2-queue |
|
||||||
|
| **ADR-024 Intent Class.** | `specs/06-Decision-Records/ADR-024-intent-classification-strategy.md` | Pattern→LLM Fallback; Redis cache |
|
||||||
|
| **ADR-025 AI Tool Layer** | `specs/06-Decision-Records/ADR-025-ai-tool-layer-architecture.md` | Tool Registry; CASL-guarded |
|
||||||
|
| **ADR-026 Chat UI** | `specs/06-Decision-Records/ADR-026-document-chat-ui-pattern.md` | Side-panel; streaming SSE |
|
||||||
|
| **ADR-027 AI Admin Console** | `specs/06-Decision-Records/ADR-027-ai-admin-console-and-dynamic-control.md` | Dynamic control; admin-only |
|
||||||
|
| **ADR-028 Migration Refactor** | `specs/06-Decision-Records/ADR-028-migration-architecture-refactor.md` | Staging Queue; cleanup |
|
||||||
|
| **Backend Guidelines** | `specs/05-Engineering-Guidelines/05-02-backend-guidelines.md` | NestJS patterns |
|
||||||
|
| **Frontend Guidelines** | `specs/05-Engineering-Guidelines/05-03-frontend-guidelines.md` | Next.js patterns |
|
||||||
|
| **Testing Strategy** | `specs/05-Engineering-Guidelines/05-04-testing-strategy.md` | Coverage goals |
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
When updating rules:
|
||||||
|
|
||||||
|
1. **Check AGENTS.md version** — Ensure rule files are synced
|
||||||
|
2. **Update version numbers** — Bump version in `00-project-context.md` only (03-typescript.md no longer has version)
|
||||||
|
3. **Review ADR references** — Ensure all ADR references are current (ADR-023, ADR-023A, ADR-024~028)
|
||||||
|
4. **Add new forbidden actions** — When new patterns are identified as violations
|
||||||
|
5. **Update key spec files table** — When new ADRs or guidelines are added
|
||||||
|
6. **Update Tier 3 SPECIALIZED WORK** — When new domain-specific workflows are added
|
||||||
|
|
||||||
|
## Related Documents
|
||||||
|
|
||||||
|
- `AGENTS.md` — Master agent configuration and context
|
||||||
|
- `specs/06-Decision-Records/` — All Architecture Decision Records
|
||||||
|
- `specs/05-Engineering-Guidelines/` — Backend, frontend, and testing guidelines
|
||||||
@@ -42,7 +42,7 @@ init_agent_registry() {
|
|||||||
[qwen]="Qwen Code"
|
[qwen]="Qwen Code"
|
||||||
[opencode]="opencode"
|
[opencode]="opencode"
|
||||||
[codex]="Codex CLI"
|
[codex]="Codex CLI"
|
||||||
[windsurf]="Windsurf"
|
[devin]="Devin"
|
||||||
[kilocode]="Kilo Code"
|
[kilocode]="Kilo Code"
|
||||||
[auggie]="Auggie CLI"
|
[auggie]="Auggie CLI"
|
||||||
[roo]="Roo Code"
|
[roo]="Roo Code"
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ SKILLS_DIR="$AGENTS_DIR/skills"
|
|||||||
|
|
||||||
echo "=== Skills Health Audit ==="
|
echo "=== Skills Health Audit ==="
|
||||||
echo "Base directory: $BASE_DIR"
|
echo "Base directory: $BASE_DIR"
|
||||||
|
echo "Agents directory: $AGENTS_DIR"
|
||||||
|
echo "Specs directory: $BASE_DIR/specs"
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# Function to check if skill has required files
|
# Function to check if skill has required files
|
||||||
@@ -178,8 +180,57 @@ fi
|
|||||||
|
|
||||||
echo
|
echo
|
||||||
|
|
||||||
|
# Specs Integrity Audit (New for v1.9.0)
|
||||||
|
echo "=== Specs Integrity Audit ==="
|
||||||
|
echo "---------------------------"
|
||||||
|
|
||||||
|
SPECS_DIR="$BASE_DIR/specs"
|
||||||
|
CATEGORIES=("100-Infrastructures" "200-fullstacks" "300-others")
|
||||||
|
SPEC_ISSUES=0
|
||||||
|
|
||||||
|
if [[ -d "$SPECS_DIR" ]]; then
|
||||||
|
# 1. Check for core categories
|
||||||
|
for cat in "${CATEGORIES[@]}"; do
|
||||||
|
if [[ -d "$SPECS_DIR/$cat" ]]; then
|
||||||
|
echo -e "${GREEN} FOUND${NC}: category $cat"
|
||||||
|
|
||||||
|
# Check features within category
|
||||||
|
features=$(find "$SPECS_DIR/$cat" -maxdepth 1 -type d -not -path "$SPECS_DIR/$cat" | wc -l)
|
||||||
|
echo " -> Contains $features feature(s)"
|
||||||
|
|
||||||
|
# Check each feature for spec.md
|
||||||
|
while IFS= read -r f_dir; do
|
||||||
|
if [[ -n "$f_dir" ]]; then
|
||||||
|
f_name=$(basename "$f_dir")
|
||||||
|
if [[ ! -f "$f_dir/spec.md" ]]; then
|
||||||
|
echo -e " ${RED} MISSING${NC}: $f_name/spec.md"
|
||||||
|
((SPEC_ISSUES++))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done < <(find "$SPECS_DIR/$cat" -maxdepth 1 -type d -not -path "$SPECS_DIR/$cat")
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW} MISSING${NC}: category $cat"
|
||||||
|
((SPEC_ISSUES++))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 2. Check for misplaced files in specs root
|
||||||
|
misplaced=$(find "$SPECS_DIR" -maxdepth 1 -type f -name "*.md" | wc -l)
|
||||||
|
if [[ $misplaced -gt 1 ]]; then # Ignore README.md
|
||||||
|
echo -e "${YELLOW} WARNING${NC}: Found $misplaced misplaced files in specs root (should be in 100/200/300)"
|
||||||
|
((SPEC_ISSUES++))
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${RED}ERROR: Specs directory not found${NC}"
|
||||||
|
((SPEC_ISSUES++))
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
|
||||||
# Overall health
|
# Overall health
|
||||||
if [[ $TOTAL_ISSUES -eq 0 ]]; then
|
TOTAL_HEALTH_ISSUES=$((TOTAL_ISSUES + SPEC_ISSUES))
|
||||||
|
|
||||||
|
if [[ $TOTAL_HEALTH_ISSUES -eq 0 ]]; then
|
||||||
echo -e "${GREEN}=== SUCCESS: All skills healthy ===${NC}"
|
echo -e "${GREEN}=== SUCCESS: All skills healthy ===${NC}"
|
||||||
echo "Total skills: ${#SKILL_DIRS[@]}"
|
echo "Total skills: ${#SKILL_DIRS[@]}"
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -99,14 +99,13 @@ find_feature_dir_by_prefix() {
|
|||||||
|
|
||||||
local prefix="${BASH_REMATCH[1]}"
|
local prefix="${BASH_REMATCH[1]}"
|
||||||
|
|
||||||
# Search for directories in specs/ that start with this prefix
|
# Search for directories in specs/ that start with this prefix (supporting subdirectories)
|
||||||
local matches=()
|
local matches=()
|
||||||
if [[ -d "$specs_dir" ]]; then
|
if [[ -d "$specs_dir" ]]; then
|
||||||
for dir in "$specs_dir"/"$prefix"-*; do
|
# ค้นหาโฟลเดอร์ที่ตรงกับ prefix ในระบบย่อย
|
||||||
if [[ -d "$dir" ]]; then
|
while IFS= read -r -d '' dir; do
|
||||||
matches+=("$(basename "$dir")")
|
matches+=("$dir")
|
||||||
fi
|
done < <(find "$specs_dir" -maxdepth 3 -type d -name "${prefix}-*" -print0 2>/dev/null)
|
||||||
done
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Handle results
|
# Handle results
|
||||||
@@ -115,12 +114,12 @@ find_feature_dir_by_prefix() {
|
|||||||
echo "$specs_dir/$branch_name"
|
echo "$specs_dir/$branch_name"
|
||||||
elif [[ ${#matches[@]} -eq 1 ]]; then
|
elif [[ ${#matches[@]} -eq 1 ]]; then
|
||||||
# Exactly one match - perfect!
|
# Exactly one match - perfect!
|
||||||
echo "$specs_dir/${matches[0]}"
|
echo "${matches[0]}"
|
||||||
else
|
else
|
||||||
# Multiple matches - this shouldn't happen with proper naming convention
|
# Multiple matches - this shouldn't happen with proper naming convention
|
||||||
echo "ERROR: Multiple spec directories found with prefix '$prefix': ${matches[*]}" >&2
|
echo "ERROR: Multiple spec directories found with prefix '$prefix': ${matches[*]}" >&2
|
||||||
echo "Please ensure only one spec directory exists per numeric prefix." >&2
|
echo "Please ensure only one spec directory exists per numeric prefix." >&2
|
||||||
echo "$specs_dir/$branch_name" # Return something to avoid breaking the script
|
echo "${matches[0]}" # Return first match to avoid breaking the script
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,12 +30,12 @@
|
|||||||
#
|
#
|
||||||
# 5. Multi-Agent Support
|
# 5. Multi-Agent Support
|
||||||
# - Handles agent-specific file paths and naming conventions
|
# - Handles agent-specific file paths and naming conventions
|
||||||
# - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Windsurf, Kilo Code, Auggie CLI, Roo Code, CodeBuddy CLI, Qoder CLI, Amp, SHAI, or Amazon Q Developer CLI
|
# - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Devin, Kilo Code, Auggie CLI, Roo Code, CodeBuddy CLI, Qoder CLI, Amp, SHAI, or Amazon Q Developer CLI
|
||||||
# - Can update single agents or all existing agent files
|
# - Can update single agents or all existing agent files
|
||||||
# - Creates default Claude file if no agent files exist
|
# - Creates default Claude file if no agent files exist
|
||||||
#
|
#
|
||||||
# Usage: ./update-agent-context.sh [agent_type]
|
# Usage: ./update-agent-context.sh [agent_type]
|
||||||
# Agent types: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|shai|q|bob|qoder
|
# Agent types: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|devin|kilocode|auggie|shai|q|bob|qoder
|
||||||
# Leave empty to update all existing agent files
|
# Leave empty to update all existing agent files
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
@@ -609,8 +609,8 @@ update_specific_agent() {
|
|||||||
codex)
|
codex)
|
||||||
update_agent_file "$AGENTS_FILE" "Codex CLI"
|
update_agent_file "$AGENTS_FILE" "Codex CLI"
|
||||||
;;
|
;;
|
||||||
windsurf)
|
devin)
|
||||||
update_agent_file "$WINDSURF_FILE" "Windsurf"
|
update_agent_file "$DEVIN_FILE" "Devin"
|
||||||
;;
|
;;
|
||||||
kilocode)
|
kilocode)
|
||||||
update_agent_file "$KILOCODE_FILE" "Kilo Code"
|
update_agent_file "$KILOCODE_FILE" "Kilo Code"
|
||||||
@@ -681,8 +681,8 @@ update_all_existing_agents() {
|
|||||||
found_agent=true
|
found_agent=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -f "$WINDSURF_FILE" ]]; then
|
if [[ -f "$DEVIN_FILE" ]]; then
|
||||||
update_agent_file "$WINDSURF_FILE" "Windsurf"
|
update_agent_file "$DEVIN_FILE" "Devin"
|
||||||
found_agent=true
|
found_agent=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
+19
-17
@@ -1,8 +1,8 @@
|
|||||||
# `.agents/skills/` — LCBP3 Agent Skill Pack
|
# `.agents/skills/` — LCBP3 Agent Skill Pack
|
||||||
|
|
||||||
**Version:** 1.8.9 | **Last Updated:** 2026-04-22 | **Total Skills:** 20
|
**Version:** 1.9.0 | **Last Updated:** 2026-06-07 | **Total Skills:** 24
|
||||||
|
|
||||||
Agent skills for AI-assisted development in **Windsurf IDE** (and compatible agents: Codex CLI, opencode, Amp, Antigravity, AGENTS.md-aware tools).
|
Agent skills for AI-assisted development in **Devin IDE** (and compatible agents: Codex CLI, opencode, Amp, Antigravity, AGENTS.md-aware tools).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -14,25 +14,27 @@ Agent skills for AI-assisted development in **Windsurf IDE** (and compatible age
|
|||||||
├── skills.md # Overview + dependency matrix + health monitoring
|
├── skills.md # Overview + dependency matrix + health monitoring
|
||||||
├── _LCBP3-CONTEXT.md # Shared LCBP3 context injected into every speckit-* skill
|
├── _LCBP3-CONTEXT.md # Shared LCBP3 context injected into every speckit-* skill
|
||||||
├── README.md # (this file)
|
├── README.md # (this file)
|
||||||
|
├── save-memory/ # Session log & project memory update
|
||||||
├── nestjs-best-practices/ # Backend rules (40 rules across 10 categories)
|
├── nestjs-best-practices/ # Backend rules (40 rules across 10 categories)
|
||||||
├── next-best-practices/ # Frontend rules (Next.js 15+)
|
├── next-best-practices/ # Frontend rules (Next.js 15+)
|
||||||
|
├── e2e-testing/ # Playwright E2E testing patterns (POM, flaky tests, CI/CD)
|
||||||
|
├── verification-loop/ # Comprehensive verification (build, typecheck, lint, test, security)
|
||||||
|
├── security-review/ # OWASP Top 10 + ADR compliance checklist
|
||||||
└── speckit-*/ # 18 workflow skills (spec → plan → tasks → implement → …)
|
└── speckit-*/ # 18 workflow skills (spec → plan → tasks → implement → …)
|
||||||
```
|
```
|
||||||
|
|
||||||
Each skill directory contains:
|
Each skill directory contains:
|
||||||
|
|
||||||
- `SKILL.md` — frontmatter (`name`, `description`, `version: 1.8.9`, `scope`, `depends-on`, `handoffs`) + instructions
|
- `SKILL.md` — frontmatter (`name`, `description`, `version: 1.9.0`, `scope`, `depends-on`, `handoffs`) + instructions
|
||||||
- `templates/` _(optional)_ — artifact templates (spec/plan/tasks/checklist)
|
- `templates/` _(optional)_ — artifact templates (spec/plan/tasks/checklist)
|
||||||
- `rules/` _(nestjs only)_ — individual rule files grouped by prefix (`arch-`, `security-`, `db-`, etc.)
|
- `rules/` _(nestjs only)_ — individual rule files grouped by prefix (`arch-`, `security-`, `db-`, etc.)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 How Windsurf Invokes These Skills
|
## 🚀 How Devin Invokes These Skills
|
||||||
|
|
||||||
Windsurf exposes two entry points:
|
1. **Skill tool** — Devin discovers skills by scanning `.agents/skills/*/SKILL.md` frontmatter. Skills marked `user-invocable: false` are used silently by Cascade.
|
||||||
|
2. **Slash commands** — `.devin/workflows/*.md` wraps each skill as a slash command (e.g. `/04-speckit.plan`). The workflow file is short; the heavy lifting is delegated to the skill via `skill` tool.
|
||||||
1. **Skill tool** — Windsurf discovers skills by scanning `.agents/skills/*/SKILL.md` frontmatter. Skills marked `user-invocable: false` are used silently by Cascade.
|
|
||||||
2. **Slash commands** — `.windsurf/workflows/*.md` wraps each skill as a slash command (e.g. `/04-speckit.plan`). The workflow file is short; the heavy lifting is delegated to the skill via `skill` tool.
|
|
||||||
|
|
||||||
Both paths end up executing the same `SKILL.md` instructions.
|
Both paths end up executing the same `SKILL.md` instructions.
|
||||||
|
|
||||||
@@ -62,14 +64,14 @@ Use `/00-speckit.all` to run specify → clarify → plan → tasks → analyze
|
|||||||
|
|
||||||
From repo root:
|
From repo root:
|
||||||
|
|
||||||
| Script | Purpose |
|
| Script | Purpose |
|
||||||
| --- | --- |
|
| ------------------------------------------------------ | ---------------------------------------------------------- |
|
||||||
| `./.agents/scripts/bash/check-prerequisites.sh --json` | Emit `FEATURE_DIR` + `AVAILABLE_DOCS` for a feature branch |
|
| `./.agents/scripts/bash/check-prerequisites.sh --json` | Emit `FEATURE_DIR` + `AVAILABLE_DOCS` for a feature branch |
|
||||||
| `./.agents/scripts/bash/setup-plan.sh --json` | Emit `FEATURE_SPEC`, `IMPL_PLAN`, `SPECS_DIR`, `BRANCH` |
|
| `./.agents/scripts/bash/setup-plan.sh --json` | Emit `FEATURE_SPEC`, `IMPL_PLAN`, `SPECS_DIR`, `BRANCH` |
|
||||||
| `./.agents/scripts/bash/update-agent-context.sh windsurf` | Append tech entries to `AGENTS.md` |
|
| `./.agents/scripts/bash/update-agent-context.sh devin` | Append tech entries to `AGENTS.md` |
|
||||||
| `./.agents/scripts/bash/audit-skills.sh` | Validate all `SKILL.md` frontmatter + presence |
|
| `./.agents/scripts/bash/audit-skills.sh` | Validate all `SKILL.md` frontmatter + presence |
|
||||||
| `./.agents/scripts/bash/validate-versions.sh` | Version consistency check |
|
| `./.agents/scripts/bash/validate-versions.sh` | Version consistency check |
|
||||||
| `./.agents/scripts/bash/sync-workflows.sh` | Verify every skill has a `.windsurf/workflows/*.md` wrapper |
|
| `./.agents/scripts/bash/sync-workflows.sh` | Verify every skill has a `.devin/workflows/*.md` wrapper |
|
||||||
|
|
||||||
All scripts mirror to `.agents/scripts/powershell/*.ps1` for Windows.
|
All scripts mirror to `.agents/scripts/powershell/*.ps1` for Windows.
|
||||||
|
|
||||||
@@ -92,9 +94,9 @@ See [`_LCBP3-CONTEXT.md`](./_LCBP3-CONTEXT.md) for the complete list.
|
|||||||
|
|
||||||
To add a new skill:
|
To add a new skill:
|
||||||
|
|
||||||
1. Create `NAME/SKILL.md` with frontmatter: `name`, `description`, `version: 1.8.9`, `scope`, `depends-on`.
|
1. Create `NAME/SKILL.md` with frontmatter: `name`, `description`, `version: 1.9.0`, `scope`, `depends-on`.
|
||||||
2. Append an LCBP3 context reference pointing to `_LCBP3-CONTEXT.md`.
|
2. Append an LCBP3 context reference pointing to `_LCBP3-CONTEXT.md`.
|
||||||
3. Wrap with `.windsurf/workflows/NAME.md` so it becomes a slash command.
|
3. Wrap with `.devin/workflows/NAME.md` so it becomes a slash command.
|
||||||
4. Update [`skills.md`](./skills.md) dependency matrix.
|
4. Update [`skills.md`](./skills.md) dependency matrix.
|
||||||
5. Run `./.agents/scripts/bash/audit-skills.sh` → must pass.
|
5. Run `./.agents/scripts/bash/audit-skills.sh` → must pass.
|
||||||
|
|
||||||
|
|||||||
+1
-33
@@ -1,33 +1 @@
|
|||||||
# Speckit Skills Version
|
version: 1.9.0
|
||||||
|
|
||||||
version: 1.8.9
|
|
||||||
release_date: 2026-04-22
|
|
||||||
|
|
||||||
## Changelog
|
|
||||||
|
|
||||||
### 1.8.9 (2026-04-22)
|
|
||||||
- Full LCBP3-native rebuild of `.agents/skills/`
|
|
||||||
- Fixed ADR-019 drift (removed `@Expose({ name: 'id' })` and `id ?? ''` fallback patterns)
|
|
||||||
- Replaced all dead references (`GEMINI.md` → `AGENTS.md`, v1.7.0 → v1.8.0 schema, `.specify/memory/` → `AGENTS.md`)
|
|
||||||
- Added real helper scripts under `.agents/scripts/bash/` and `.agents/scripts/powershell/`
|
|
||||||
- Added ADR-007/008/020/021 coverage
|
|
||||||
- New rules: workflow-engine, file-two-phase-upload, ai-boundary, i18n, file-upload, workflow-banner
|
|
||||||
- Standardized frontmatter across all 20 skills (`version: 1.8.9`)
|
|
||||||
|
|
||||||
### 1.8.6 (2026-04-14)
|
|
||||||
- Version alignment with LCBP3-DMS v1.8.6
|
|
||||||
- Complete skill implementations for all 20 skills
|
|
||||||
- Enhanced security and audit capabilities
|
|
||||||
- Production-ready deployment status
|
|
||||||
|
|
||||||
### 1.1.0 (2026-01-24)
|
|
||||||
- New QA skills: tester, reviewer, checker
|
|
||||||
- tester: Execute tests, measure coverage, report results
|
|
||||||
- reviewer: Code review with severity levels and suggestions
|
|
||||||
- checker: Static analysis aggregation (lint, types, security)
|
|
||||||
|
|
||||||
### 1.0.0 (2026-01-24)
|
|
||||||
- Initial versioned release
|
|
||||||
- Core skills: specify, clarify, plan, tasks, implement, analyze, checklist, constitution, quizme, taskstoissues
|
|
||||||
- New skills: diff, validate, migrate, status
|
|
||||||
- All workflows enhanced with error handling and relative paths
|
|
||||||
|
|||||||
@@ -5,14 +5,14 @@
|
|||||||
|
|
||||||
**Project:** NAP-DMS (LCBP3) — Laem Chabang Port Phase 3 Document Management System
|
**Project:** NAP-DMS (LCBP3) — Laem Chabang Port Phase 3 Document Management System
|
||||||
**Stack:** NestJS 11 + Next.js 16 + TypeScript + MariaDB 11.8 + Redis + BullMQ + Elasticsearch + Ollama (on-prem AI)
|
**Stack:** NestJS 11 + Next.js 16 + TypeScript + MariaDB 11.8 + Redis + BullMQ + Elasticsearch + Ollama (on-prem AI)
|
||||||
**Version:** 1.8.9 (2026-04-18)
|
**Version:** 1.9.7 (2026-05-25)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📌 Canonical Rule Sources (read in this order)
|
## 📌 Canonical Rule Sources (read in this order)
|
||||||
|
|
||||||
1. **`AGENTS.md`** (repo root) — primary rule file for AI agents; supersedes legacy `GEMINI.md`.
|
1. **`AGENTS.md`** (repo root) — primary rule file for AI agents; supersedes legacy `GEMINI.md`.
|
||||||
2. **`specs/06-Decision-Records/`** — architectural decisions (22 ADRs); ADR priority > Engineering Guidelines.
|
2. **`specs/06-Decision-Records/`** — architectural decisions (29 ADRs); ADR priority > Engineering Guidelines.
|
||||||
3. **`specs/05-Engineering-Guidelines/`** — backend/frontend/testing/i18n/git patterns.
|
3. **`specs/05-Engineering-Guidelines/`** — backend/frontend/testing/i18n/git patterns.
|
||||||
4. **`specs/00-Overview/00-02-glossary.md`** — domain terminology (Correspondence / RFA / Transmittal / Circulation).
|
4. **`specs/00-Overview/00-02-glossary.md`** — domain terminology (Correspondence / RFA / Transmittal / Circulation).
|
||||||
5. **`specs/00-Overview/00-03-product-vision.md`** — project constitution (Vision, Strategic Pillars, Guardrails).
|
5. **`specs/00-Overview/00-03-product-vision.md`** — project constitution (Vision, Strategic Pillars, Guardrails).
|
||||||
@@ -24,11 +24,12 @@
|
|||||||
## 🔴 Tier 1 Non-Negotiables
|
## 🔴 Tier 1 Non-Negotiables
|
||||||
|
|
||||||
- **ADR-019 UUID:** `publicId: string` exposed directly — **no** `@Expose({ name: 'id' })` rename; **no** `parseInt`/`Number`/`+` on UUID; **no** `id ?? ''` fallback in frontend.
|
- **ADR-019 UUID:** `publicId: string` exposed directly — **no** `@Expose({ name: 'id' })` rename; **no** `parseInt`/`Number`/`+` on UUID; **no** `id ?? ''` fallback in frontend.
|
||||||
- **ADR-009:** No TypeORM migrations — edit `specs/03-Data-and-Storage/lcbp3-v1.8.0-schema-02-tables.sql` or add a `deltas/*.sql` file.
|
- **ADR-009:** No TypeORM migrations — edit `specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql` or add a `deltas/*.sql` file.
|
||||||
- **ADR-016 Security:** JWT + CASL 4-Level RBAC; `@UseGuards(JwtAuthGuard, CaslAbilityGuard)` on every mutation controller; `ThrottlerGuard` on auth; bcrypt 12 rounds; `Idempotency-Key` required on POST/PUT/PATCH.
|
- **ADR-016 Security:** JWT + CASL 4-Level RBAC; `@UseGuards(JwtAuthGuard, CaslAbilityGuard)` on every mutation controller; `ThrottlerGuard` on auth; bcrypt 12 rounds; `Idempotency-Key` required on POST/PUT/PATCH.
|
||||||
- **ADR-002 Document Numbering:** Redis Redlock + TypeORM `@VersionColumn` (double-lock). Never use application-side counter alone.
|
- **ADR-002 Document Numbering:** Redis Redlock + TypeORM `@VersionColumn` (double-lock). Never use application-side counter alone.
|
||||||
- **ADR-008 Notifications:** BullMQ queue — never inline email/notification in a request thread.
|
- **ADR-008 Notifications:** BullMQ queue — never inline email/notification in a request thread.
|
||||||
- **ADR-018 AI Boundary:** Ollama on Admin Desktop only; AI → DMS API → DB (never direct DB/storage). Human-in-the-loop validation required.
|
- **ADR-023/023A AI Boundary:** Ollama on Admin Desktop only; AI → DMS API → DB (never direct DB/storage). 2-model stack: `gemma4:e4b Q8_0` + `nomic-embed-text`. BullMQ `ai-realtime` / `ai-batch` queues. Human-in-the-loop validation required. (ADR-018 superseded by ADR-023)
|
||||||
|
- **ADR-029 Dynamic Prompt Management:** Prompt templates in DB (`ai_prompts`), never hardcoded in processor; Redis cache `ai:prompt:active:{type}` TTL 60s; `activate()` runs in DB transaction + Redis DEL after commit; `system.manage_all` guard on all mutations.
|
||||||
- **ADR-007 Error Handling:** Layered (Validation / Business / System); `BusinessException` hierarchy; user-friendly `userMessage` + `recoveryAction`; technical stack only in logs.
|
- **ADR-007 Error Handling:** Layered (Validation / Business / System); `BusinessException` hierarchy; user-friendly `userMessage` + `recoveryAction`; technical stack only in logs.
|
||||||
- **TypeScript Strict:** Zero `any`, zero `console.log` (use NestJS `Logger`).
|
- **TypeScript Strict:** Zero `any`, zero `console.log` (use NestJS `Logger`).
|
||||||
- **i18n:** No hardcoded Thai/English strings in components — use i18n keys (see `05-08-i18n-guidelines.md`).
|
- **i18n:** No hardcoded Thai/English strings in components — use i18n keys (see `05-08-i18n-guidelines.md`).
|
||||||
@@ -38,30 +39,36 @@
|
|||||||
|
|
||||||
## 🏷️ Domain Glossary (reject generic terms)
|
## 🏷️ Domain Glossary (reject generic terms)
|
||||||
|
|
||||||
| ✅ Use | ❌ Don't Use |
|
| ✅ Use | ❌ Don't Use |
|
||||||
| --- | --- |
|
| ------------------ | ------------------------------------- |
|
||||||
| Correspondence | Letter, Communication, Document |
|
| Correspondence | Letter, Communication, Document |
|
||||||
| RFA | Approval Request, Submit for Approval |
|
| RFA | Approval Request, Submit for Approval |
|
||||||
| Transmittal | Delivery Note, Cover Letter |
|
| Transmittal | Delivery Note, Cover Letter |
|
||||||
| Circulation | Distribution, Routing |
|
| Circulation | Distribution, Routing |
|
||||||
| Shop Drawing | Construction Drawing |
|
| Shop Drawing | Construction Drawing |
|
||||||
| Contract Drawing | Design Drawing, Blueprint |
|
| Contract Drawing | Design Drawing, Blueprint |
|
||||||
| Workflow Engine | Approval Flow, Process Engine |
|
| Workflow Engine | Approval Flow, Process Engine |
|
||||||
| Document Numbering | Document ID, Auto Number |
|
| Document Numbering | Document ID, Auto Number |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📁 Key Files for Generating / Validating Artifacts
|
## 📁 Key Files for Generating / Validating Artifacts
|
||||||
|
|
||||||
| When you need... | Read |
|
| When you need... | Read |
|
||||||
| --- | --- |
|
| -------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||||
| A new feature spec | `.agents/skills/speckit-specify/templates/spec-template.md` + `specs/01-Requirements/01-06-edge-cases-and-rules.md` |
|
| A new feature spec | `.agents/skills/speckit-specify/templates/spec-template.md` + `specs/01-Requirements/01-06-edge-cases-and-rules.md` |
|
||||||
| A plan | `.agents/skills/speckit-plan/templates/plan-template.md` + relevant ADRs |
|
| A plan | `.agents/skills/speckit-plan/templates/plan-template.md` + relevant ADRs |
|
||||||
| Task breakdown | `.agents/skills/speckit-tasks/templates/tasks-template.md` + existing patterns in `specs/08-Tasks/` |
|
| Task breakdown | `.agents/skills/speckit-tasks/templates/tasks-template.md` + existing patterns in `specs/08-Tasks/` |
|
||||||
| Acceptance criteria / UAT | `specs/01-Requirements/01-05-acceptance-criteria.md` |
|
| Acceptance criteria / UAT | `specs/01-Requirements/01-05-acceptance-criteria.md` |
|
||||||
| Schema / table definition | `specs/03-Data-and-Storage/lcbp3-v1.8.0-schema-02-tables.sql` + `03-01-data-dictionary.md` |
|
| Schema / table definition | `specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql` + `03-01-data-dictionary.md` |
|
||||||
| RBAC / permissions | `specs/03-Data-and-Storage/lcbp3-v1.8.0-seed-permissions.sql` + `01-02-01-rbac-matrix.md` |
|
| RBAC / permissions | `specs/03-Data-and-Storage/lcbp3-v1.8.0-seed-permissions.sql` + `01-02-01-rbac-matrix.md` |
|
||||||
| Release / hotfix | `specs/04-Infrastructure-OPS/04-08-release-management-policy.md` |
|
| Release / hotfix | `specs/04-Infrastructure-OPS/04-08-release-management-policy.md` |
|
||||||
|
| ADR-024 Intent Class. | `specs/06-Decision-Records/ADR-024-intent-classification-strategy.md` |
|
||||||
|
| ADR-025 AI Tool Layer | `specs/06-Decision-Records/ADR-025-ai-tool-layer-architecture.md` |
|
||||||
|
| ADR-026 Chat UI | `specs/06-Decision-Records/ADR-026-document-chat-ui-pattern.md` |
|
||||||
|
| ADR-027 AI Admin Console | `specs/06-Decision-Records/ADR-027-ai-admin-console-and-dynamic-control.md` |
|
||||||
|
| ADR-028 Migration Refactor | `specs/06-Decision-Records/ADR-028-migration-architecture-refactor.md` |
|
||||||
|
| ADR-029 Dynamic Prompts | `specs/06-Decision-Records/ADR-029-dynamic-prompt-management.md` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -83,7 +90,7 @@
|
|||||||
- [ ] Business comments in Thai, code identifiers in English
|
- [ ] Business comments in Thai, code identifiers in English
|
||||||
- [ ] Schema changes via SQL directly (not migration)
|
- [ ] Schema changes via SQL directly (not migration)
|
||||||
- [ ] Test coverage meets targets (Backend 70%+, Business Logic 80%+)
|
- [ ] Test coverage meets targets (Backend 70%+, Business Logic 80%+)
|
||||||
- [ ] Relevant ADRs referenced (007/008/009/016/018/019/020/021)
|
- [ ] Relevant ADRs referenced (007/008/009/016/019/021/023/023A/024-029 for AI work)
|
||||||
- [ ] Domain glossary terms used correctly
|
- [ ] Domain glossary terms used correctly
|
||||||
- [ ] Error handling: `Logger` + `HttpException` / `BusinessException`
|
- [ ] Error handling: `Logger` + `HttpException` / `BusinessException`
|
||||||
- [ ] i18n keys used (no hardcode text)
|
- [ ] i18n keys used (no hardcode text)
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
name: bugfix
|
||||||
|
description: Quick bugfix workflow with minimal impact. Focused on surgical fixes without unrelated refactoring.
|
||||||
|
version: 1.9.0
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bugfix
|
||||||
|
|
||||||
|
ใช้สำหรับแก้ไข Bug ที่ระบุสาเหตุได้ชัดเจน โดยเน้นที่การแก้ไขที่ตรงจุด (Surgical Fix) และมีผลกระทบน้อยที่สุด (Minimal Impact)
|
||||||
|
|
||||||
|
## Phase 1 — Analysis (การวิเคราะห์)
|
||||||
|
|
||||||
|
1. **Read Logs & Context**: อ่าน Error Logs หรือรายละเอียดที่ User แจ้งมาให้ครบถ้วน
|
||||||
|
2. **Identify Root Cause**: ค้นหาสาเหตุที่แท้จริง (Root Cause) ไม่ใช่แค่การแก้ที่ปลายเหตุ
|
||||||
|
3. **Check Error Catalog**: ตรวจสอบ `docs/error-catalog.md` เพื่อดูว่ามี Error Code หรือ Pattern ที่เกี่ยวข้องหรือไม่
|
||||||
|
4. **Locate Code**: ระบุไฟล์และบรรทัดที่เกิดปัญหาให้ชัดเจน
|
||||||
|
|
||||||
|
## Phase 2 — Planning (การวางแผน)
|
||||||
|
|
||||||
|
1. **Create Fix Plan**: ร่างแผนการแก้ไขที่เน้น **Minimal Change**
|
||||||
|
- ห้าม Refactor โค้ดที่ไม่เกี่ยวข้อง
|
||||||
|
- หลีกเลี่ยงการเปลี่ยนแปลงที่จะส่งผลกระทบต่อส่วนอื่น (No Side Effects)
|
||||||
|
2. **Verify Standards**: ตรวจสอบว่าแผนการแก้ไขไม่ขัดกับ Tier 1 (Security, UUID, DB) และ Tier 2 (Architecture) ใน `AGENTS.md`
|
||||||
|
3. **Save to fix.md**: (Optional) บันทึกรายละเอียดการแก้ไขลงในไฟล์ `fix.md` เพื่อใช้ตรวจสอบก่อนลงมือจริง
|
||||||
|
|
||||||
|
## Phase 3 — Execution (การดำเนินการ)
|
||||||
|
|
||||||
|
1. **Apply Fix**: ลงมือแก้ไขโค้ดตามแผน
|
||||||
|
2. **Verify Fix**:
|
||||||
|
- จำลองสถานการณ์เพื่อยืนยันว่า Bug หายไปจริง
|
||||||
|
- ตรวจสอบว่าไม่มี Forbidden Patterns (`any`, `console.log`, UUID misuse)
|
||||||
|
3. **Regression Check**: ตรวจสอบส่วนที่เกี่ยวข้องว่ายังทำงานได้ปกติ
|
||||||
|
|
||||||
|
## Phase 4 — Finalization (การสรุปผล)
|
||||||
|
|
||||||
|
1. **Report**: สรุปผลการแก้ไขให้ User ทราบ
|
||||||
|
- Root cause (สาเหตุ)
|
||||||
|
- Fix detail (รายละเอียดการแก้)
|
||||||
|
- Affected files (ไฟล์ที่เกี่ยวข้อง)
|
||||||
|
2. **Cleanup**: ลบไฟล์ `fix.md` หรือ Debug logs ที่สร้างขึ้น
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: diagnose
|
name: diagnose
|
||||||
description: Disciplined diagnosis loop for hard bugs and performance regressions. Reproduce → minimise → hypothesise → instrument → fix → regression-test. Use when user says "diagnose this" / "debug this", reports a bug, says something is broken/throwing/failing, or describes a performance regression.
|
description: Disciplined diagnosis loop for hard bugs and performance regressions. Reproduce → minimise → hypothesise → instrument → fix → regression-test. Use when user says "diagnose this" / "debug this", reports a bug, says something is broken/throwing/failing, or describes a performance regression.
|
||||||
|
version: 1.9.0
|
||||||
---
|
---
|
||||||
|
|
||||||
# Diagnose
|
# Diagnose
|
||||||
|
|||||||
@@ -0,0 +1,354 @@
|
|||||||
|
---
|
||||||
|
name: e2e-testing
|
||||||
|
description: Playwright E2E testing patterns, Page Object Model, configuration, CI/CD integration, artifact management, and flaky test strategies for LCBP3-DMS.
|
||||||
|
version: 1.9.0
|
||||||
|
scope: testing
|
||||||
|
depends-on: []
|
||||||
|
handoffs-to: [speckit-tester]
|
||||||
|
user-invocable: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# E2E Testing Skill
|
||||||
|
|
||||||
|
Playwright E2E testing patterns adapted for LCBP3-DMS (NestJS + Next.js + MariaDB stack).
|
||||||
|
|
||||||
|
## LCBP3 Context
|
||||||
|
|
||||||
|
See [`_LCBP3-CONTEXT.md`](../_LCBP3-CONTEXT.md) for project-specific testing requirements:
|
||||||
|
- Backend: Jest (Unit + Integration + E2E)
|
||||||
|
- Frontend: Vitest (Unit) + Playwright (E2E)
|
||||||
|
- E2E test location: `frontend/e2e/workflow-adr021.spec.ts`
|
||||||
|
- Coverage goals: Backend 70%+, Business Logic 80%+
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
Invoke this skill when:
|
||||||
|
- Creating new E2E tests for frontend features
|
||||||
|
- Debugging flaky Playwright tests
|
||||||
|
- Setting up CI/CD integration for E2E tests
|
||||||
|
- Optimizing test performance and reliability
|
||||||
|
- Implementing Page Object Model (POM) patterns
|
||||||
|
|
||||||
|
## Test File Organization
|
||||||
|
|
||||||
|
```
|
||||||
|
frontend/
|
||||||
|
├── e2e/
|
||||||
|
│ ├── auth/
|
||||||
|
│ │ ├── login.spec.ts
|
||||||
|
│ │ └── logout.spec.ts
|
||||||
|
│ ├── correspondence/
|
||||||
|
│ │ ├── create.spec.ts
|
||||||
|
│ │ └── workflow.spec.ts
|
||||||
|
│ ├── transmittals/
|
||||||
|
│ │ ├── create.spec.ts
|
||||||
|
│ │ └── submit.spec.ts
|
||||||
|
│ ├── circulation/
|
||||||
|
│ │ ├── routing.spec.ts
|
||||||
|
│ │ └── approval.spec.ts
|
||||||
|
│ └── workflow-adr021.spec.ts # Existing ADR-021 integration test
|
||||||
|
├── playwright.config.ts
|
||||||
|
└── tests/
|
||||||
|
└── fixtures/
|
||||||
|
├── auth.ts
|
||||||
|
└── data.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
## Page Object Model (POM)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// frontend/e2e/pages/CorrespondencePage.ts
|
||||||
|
import { Page, Locator } from '@playwright/test'
|
||||||
|
|
||||||
|
export class CorrespondencePage {
|
||||||
|
readonly page: Page
|
||||||
|
readonly createButton: Locator
|
||||||
|
readonly subjectInput: Locator
|
||||||
|
readonly recipientSelect: Locator
|
||||||
|
readonly submitButton: Locator
|
||||||
|
readonly successMessage: Locator
|
||||||
|
|
||||||
|
constructor(page: Page) {
|
||||||
|
this.page = page
|
||||||
|
this.createButton = page.getByTestId('create-correspondence')
|
||||||
|
this.subjectInput = page.getByTestId('subject-input')
|
||||||
|
this.recipientSelect = page.getByTestId('recipient-select')
|
||||||
|
this.submitButton = page.getByTestId('submit-button')
|
||||||
|
this.successMessage = page.getByTestId('success-message')
|
||||||
|
}
|
||||||
|
|
||||||
|
async goto() {
|
||||||
|
await this.page.goto('/admin/doc-control/correspondences')
|
||||||
|
await this.page.waitForLoadState('networkidle')
|
||||||
|
}
|
||||||
|
|
||||||
|
async createCorrespondence(data: {
|
||||||
|
subject: string
|
||||||
|
recipientId: string
|
||||||
|
}) {
|
||||||
|
await this.createButton.click()
|
||||||
|
await this.subjectInput.fill(data.subject)
|
||||||
|
await this.recipientSelect.selectOption(data.recipientId)
|
||||||
|
await this.submitButton.click()
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifySuccess() {
|
||||||
|
await expect(this.successMessage).toBeVisible()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Structure
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// frontend/e2e/correspondence/create.spec.ts
|
||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
import { CorrespondencePage } from '../pages/CorrespondencePage'
|
||||||
|
|
||||||
|
test.describe('Correspondence Creation', () => {
|
||||||
|
let correspondencePage: CorrespondencePage
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
correspondencePage = new CorrespondencePage(page)
|
||||||
|
await correspondencePage.goto()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should create correspondence successfully', async ({ page }) => {
|
||||||
|
await correspondencePage.createCorrespondence({
|
||||||
|
subject: 'Test Correspondence',
|
||||||
|
recipientId: 'test-recipient-id'
|
||||||
|
})
|
||||||
|
|
||||||
|
await correspondencePage.verifySuccess()
|
||||||
|
await page.screenshot({ path: 'artifacts/correspondence-created.png' })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should validate required fields', async ({ page }) => {
|
||||||
|
await correspondencePage.createButton.click()
|
||||||
|
await correspondencePage.submitButton.click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('subject-error')).toBeVisible()
|
||||||
|
await expect(page.getByTestId('recipient-error')).toBeVisible()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Playwright Configuration
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// frontend/playwright.config.ts
|
||||||
|
import { defineConfig, devices } from '@playwright/test'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: './e2e',
|
||||||
|
fullyParallel: true,
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
workers: process.env.CI ? 1 : undefined,
|
||||||
|
reporter: [
|
||||||
|
['html', { outputFolder: 'playwright-report' }],
|
||||||
|
['junit', { outputFile: 'playwright-results.xml' }],
|
||||||
|
['json', { outputFile: 'playwright-results.json' }]
|
||||||
|
],
|
||||||
|
use: {
|
||||||
|
baseURL: process.env.BASE_URL || 'http://localhost:3000',
|
||||||
|
trace: 'on-first-retry',
|
||||||
|
screenshot: 'only-on-failure',
|
||||||
|
video: 'retain-on-failure',
|
||||||
|
actionTimeout: 10000,
|
||||||
|
navigationTimeout: 30000,
|
||||||
|
},
|
||||||
|
projects: [
|
||||||
|
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
||||||
|
],
|
||||||
|
webServer: {
|
||||||
|
command: 'pnpm dev',
|
||||||
|
url: 'http://localhost:3000',
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
timeout: 120000,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Flaky Test Patterns
|
||||||
|
|
||||||
|
### Quarantine
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
test('flaky: complex workflow', async ({ page }) => {
|
||||||
|
test.fixme(true, 'Flaky - Issue #123')
|
||||||
|
// test code...
|
||||||
|
})
|
||||||
|
|
||||||
|
test('conditional skip', async ({ page }) => {
|
||||||
|
test.skip(process.env.CI, 'Flaky in CI - Issue #123')
|
||||||
|
// test code...
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Identify Flakiness
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
npx playwright test e2e/correspondence/create.spec.ts --repeat-each=10
|
||||||
|
npx playwright test e2e/correspondence/create.spec.ts --retries=3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Causes & Fixes
|
||||||
|
|
||||||
|
**Race conditions:**
|
||||||
|
```typescript
|
||||||
|
// Bad: assumes element is ready
|
||||||
|
await page.click('[data-testid="submit-button"]')
|
||||||
|
|
||||||
|
// Good: auto-wait locator
|
||||||
|
await page.locator('[data-testid="submit-button"]').click()
|
||||||
|
```
|
||||||
|
|
||||||
|
**Network timing:**
|
||||||
|
```typescript
|
||||||
|
// Bad: arbitrary timeout
|
||||||
|
await page.waitForTimeout(5000)
|
||||||
|
|
||||||
|
// Good: wait for specific condition
|
||||||
|
await page.waitForResponse(resp =>
|
||||||
|
resp.url().includes('/api/correspondences') && resp.status() === 201
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Animation timing:**
|
||||||
|
```typescript
|
||||||
|
// Bad: click during animation
|
||||||
|
await page.click('[data-testid="menu-item"]')
|
||||||
|
|
||||||
|
// Good: wait for stability
|
||||||
|
await page.locator('[data-testid="menu-item"]').waitFor({ state: 'visible' })
|
||||||
|
await page.waitForLoadState('networkidle')
|
||||||
|
await page.locator('[data-testid="menu-item"]').click()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Artifact Management
|
||||||
|
|
||||||
|
### Screenshots
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
await page.screenshot({ path: 'artifacts/after-login.png' })
|
||||||
|
await page.screenshot({ path: 'artifacts/full-page.png', fullPage: true })
|
||||||
|
await page.locator('[data-testid="workflow-banner"]').screenshot({
|
||||||
|
path: 'artifacts/workflow-banner.png'
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Traces
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// In playwright.config.ts
|
||||||
|
use: {
|
||||||
|
trace: 'on-first-retry'
|
||||||
|
}
|
||||||
|
|
||||||
|
// View trace
|
||||||
|
npx playwright show-trace trace.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
### Video
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// In playwright.config.ts
|
||||||
|
use: {
|
||||||
|
video: 'retain-on-failure',
|
||||||
|
videosPath: 'artifacts/videos/'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI/CD Integration
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# .github/workflows/e2e.yml
|
||||||
|
name: E2E Tests
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
- run: pnpm install
|
||||||
|
- run: cd frontend && npx playwright install --with-deps
|
||||||
|
- run: cd frontend && npx playwright test
|
||||||
|
env:
|
||||||
|
BASE_URL: ${{ vars.STAGING_URL }}
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: playwright-report
|
||||||
|
path: frontend/playwright-report/
|
||||||
|
retention-days: 30
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Report Template
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# E2E Test Report
|
||||||
|
|
||||||
|
**Date:** YYYY-MM-DD HH:MM
|
||||||
|
**Duration:** Xm Ys
|
||||||
|
**Status:** PASSING / FAILING
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
- Total: X | Passed: Y (Z%) | Failed: A | Flaky: B | Skipped: C
|
||||||
|
|
||||||
|
## Failed Tests
|
||||||
|
|
||||||
|
### correspondence-create
|
||||||
|
**File:** `frontend/e2e/correspondence/create.spec.ts:45`
|
||||||
|
**Error:** Expected element to be visible
|
||||||
|
**Screenshot:** artifacts/failed.png
|
||||||
|
**Recommended Fix:** Add waitForLoadState after form submission
|
||||||
|
|
||||||
|
## Artifacts
|
||||||
|
- HTML Report: frontend/playwright-report/index.html
|
||||||
|
- Screenshots: frontend/artifacts/*.png
|
||||||
|
- Videos: frontend/artifacts/videos/*.webm
|
||||||
|
- Traces: frontend/artifacts/*.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
## Critical Flow Testing
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// frontend/e2e/workflow/adr021.spec.ts
|
||||||
|
test('workflow: correspondence → rfa → approval', async ({ page }) => {
|
||||||
|
// Create correspondence
|
||||||
|
await createCorrespondence(page)
|
||||||
|
await expect(page.getByTestId('correspondence-created')).toBeVisible()
|
||||||
|
|
||||||
|
// Submit for RFA
|
||||||
|
await page.getByTestId('submit-rfa').click()
|
||||||
|
await expect(page.getByTestId('rfa-submitted')).toBeVisible()
|
||||||
|
|
||||||
|
// Approve RFA
|
||||||
|
await page.goto('/admin/doc-control/rfa/123')
|
||||||
|
await page.getByTestId('approve-button').click()
|
||||||
|
await expect(page.getByTestId('approval-success')).toBeVisible()
|
||||||
|
|
||||||
|
// Verify workflow state
|
||||||
|
await expect(page.getByTestId('workflow-state')).toContainText('APPROVED')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## LCBP3-Specific Considerations
|
||||||
|
|
||||||
|
- **UUID Handling:** Use `publicId` (string UUID) in E2E tests, never `parseInt()` (ADR-019)
|
||||||
|
- **Authentication:** Mock auth tokens for E2E tests to avoid real auth flows
|
||||||
|
- **Workflow States:** Test ADR-021 workflow transitions (DRAFT → PENDING → APPROVED)
|
||||||
|
- **i18n:** Test with Thai language to verify i18n key resolution
|
||||||
|
- **RBAC:** Test different user roles (admin, user, reviewer) for permission checks
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- LCBP3 Testing Strategy: `specs/05-Engineering-Guidelines/05-04-testing-strategy.md`
|
||||||
|
- ADR-021 Workflow Context: `specs/06-Decision-Records/ADR-021-workflow-context.md`
|
||||||
|
- Existing E2E test: `frontend/e2e/workflow-adr021.spec.ts`
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: grill-with-docs
|
name: grill-with-docs
|
||||||
description: Grilling session that challenges your plan against the existing domain model, sharpens terminology, and updates documentation (CONTEXT.md, ADRs) inline as decisions crystallise. Use when user wants to stress-test a plan against their project's language and documented decisions.
|
description: Grilling session that challenges your plan against the existing domain model, sharpens terminology, and updates documentation (CONTEXT.md, ADRs) inline as decisions crystallise. Use when user wants to stress-test a plan against their project's language and documented decisions.
|
||||||
|
version: 1.9.0
|
||||||
---
|
---
|
||||||
|
|
||||||
<what-to-do>
|
<what-to-do>
|
||||||
|
|||||||
@@ -6454,7 +6454,7 @@ CREATE TABLE ai_audit_log (
|
|||||||
user_id INT NOT NULL,
|
user_id INT NOT NULL,
|
||||||
action VARCHAR(64) NOT NULL, -- 'ai.extract_metadata', 'ai.classify', etc.
|
action VARCHAR(64) NOT NULL, -- 'ai.extract_metadata', 'ai.classify', etc.
|
||||||
file_id INT,
|
file_id INT,
|
||||||
model VARCHAR(64), -- 'gemma-4:7b', 'paddleocr-v3'
|
model VARCHAR(64), -- 'gemma-4:7b', 'typhoon-np-dms-ocr', 'tesseract-ocr'
|
||||||
confidence DECIMAL(4,3),
|
confidence DECIMAL(4,3),
|
||||||
input_hash CHAR(64), -- SHA-256 of input for replay detection
|
input_hash CHAR(64), -- SHA-256 of input for replay detection
|
||||||
output_summary JSON,
|
output_summary JSON,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: nestjs-best-practices
|
name: nestjs-best-practices
|
||||||
description: NestJS best practices and architecture patterns for building production-ready LCBP3-DMS backend code. Enforces ADR-009 (no TypeORM migrations), ADR-019 (hybrid UUID), ADR-016 (security), ADR-007 (error handling), ADR-008 (BullMQ), ADR-001/002 (workflow + numbering), ADR-018/020 (AI boundary), and ADR-021 (workflow context).
|
description: NestJS best practices and architecture patterns for building production-ready LCBP3-DMS backend code. Enforces ADR-009 (no TypeORM migrations), ADR-019 (hybrid UUID), ADR-016 (security), ADR-007 (error handling), ADR-008 (BullMQ), ADR-001/002 (workflow + numbering), ADR-018/020 (AI boundary), and ADR-021 (workflow context).
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
scope: backend
|
scope: backend
|
||||||
user-invocable: false
|
user-invocable: false
|
||||||
license: MIT
|
license: MIT
|
||||||
@@ -126,7 +126,7 @@ These rules override general NestJS best practices for the NAP-DMS project:
|
|||||||
### ADR-009: No TypeORM Migrations
|
### ADR-009: No TypeORM Migrations
|
||||||
|
|
||||||
- **ห้ามสร้างไฟล์ migration ของ TypeORM**
|
- **ห้ามสร้างไฟล์ migration ของ TypeORM**
|
||||||
- แก้ไข schema โดยตรงที่: `specs/03-Data-and-Storage/lcbp3-v1.8.0-schema-02-tables.sql`
|
- แก้ไข schema โดยตรงที่: `specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql`
|
||||||
- ใช้ n8n workflow สำหรับ data migration ถ้าจำเป็น
|
- ใช้ n8n workflow สำหรับ data migration ถ้าจำเป็น
|
||||||
|
|
||||||
### ADR-019: Hybrid Identifier Strategy (CRITICAL — March 2026 Pattern)
|
### ADR-019: Hybrid Identifier Strategy (CRITICAL — March 2026 Pattern)
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ CREATE TABLE ai_audit_log (
|
|||||||
user_id INT NOT NULL,
|
user_id INT NOT NULL,
|
||||||
action VARCHAR(64) NOT NULL, -- 'ai.extract_metadata', 'ai.classify', etc.
|
action VARCHAR(64) NOT NULL, -- 'ai.extract_metadata', 'ai.classify', etc.
|
||||||
file_id INT,
|
file_id INT,
|
||||||
model VARCHAR(64), -- 'gemma-4:7b', 'paddleocr-v3'
|
model VARCHAR(64), -- 'gemma-4:7b', 'typhoon-np-dms-ocr', 'tesseract-ocr'
|
||||||
confidence DECIMAL(4,3),
|
confidence DECIMAL(4,3),
|
||||||
input_hash CHAR(64), -- SHA-256 of input for replay detection
|
input_hash CHAR(64), -- SHA-256 of input for replay detection
|
||||||
output_summary JSON,
|
output_summary JSON,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: next-best-practices
|
name: next-best-practices
|
||||||
description: Next.js best practices for LCBP3-DMS frontend. Enforces ADR-019 (publicId only, no parseInt/id fallback), TanStack Query + RHF + Zod, shadcn/ui, i18n, ADR-007 error UX, ADR-021 IntegratedBanner/WorkflowLifecycle, two-phase file upload.
|
description: Next.js best practices for LCBP3-DMS frontend. Enforces ADR-019 (publicId only, no parseInt/id fallback), TanStack Query + RHF + Zod, shadcn/ui, i18n, ADR-007 error UX, ADR-021 IntegratedBanner/WorkflowLifecycle, two-phase file upload.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
scope: frontend
|
scope: frontend
|
||||||
user-invocable: false
|
user-invocable: false
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -0,0 +1,198 @@
|
|||||||
|
---
|
||||||
|
name: save-memory
|
||||||
|
description: บันทึก session log และอัปเดต project memory ตามโครงสร้างใหม่
|
||||||
|
version: 1.9.0
|
||||||
|
scope: project-management
|
||||||
|
depends-on: []
|
||||||
|
user-invocable: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# บันทึก Memory (Save Memory)
|
||||||
|
|
||||||
|
Skill นี้ใช้สำหรับบันทึก session log และอัปเดต project memory ตามโครงสร้างใหม่ที่ reorganization แล้ว
|
||||||
|
|
||||||
|
## โครงสร้าง Memory ใหม่
|
||||||
|
|
||||||
|
```
|
||||||
|
memory/
|
||||||
|
├── README.md (index + overview)
|
||||||
|
├── mcp-tools.md (MCP MariaDB + Memory Tools)
|
||||||
|
└── project-memory-override.md (OS rules, Current Decisions, Environment, Next Session Focus)
|
||||||
|
|
||||||
|
specs/88-logs/
|
||||||
|
├── rollouts.md (Recent rollouts table)
|
||||||
|
└── session-YYYY-MM-DD-[topic].md (Session logs)
|
||||||
|
```
|
||||||
|
|
||||||
|
## ขั้นตอนการบันทึก Memory
|
||||||
|
|
||||||
|
### 1. สร้าง Session Log (ถ้ามีงาน session ใหม่)
|
||||||
|
|
||||||
|
เมื่อทำงาน session ใหม่ให้:
|
||||||
|
|
||||||
|
1. **สร้างไฟล์ session log ใหม่** ใน `specs/88-logs/`
|
||||||
|
- ชื่อไฟล์: `session-YYYY-MM-DD-[topic].md`
|
||||||
|
- ตัวอย่าง: `session-2026-06-07-memory-reorganization.md`
|
||||||
|
|
||||||
|
2. **บันทึกเนื้อหาใน session log**:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Session [N] — YYYY-MM-DD ([Topic])
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
[สรุปสิ่งที่ทำใน session นี้]
|
||||||
|
|
||||||
|
## ปัญหาที่พบ (Root Cause)
|
||||||
|
|
||||||
|
[อธิบายปัญหาและสาเหตุ]
|
||||||
|
|
||||||
|
## การแก้ไข (Fix)
|
||||||
|
|
||||||
|
| ไฟล์ | การเปลี่ยนแปลง |
|
||||||
|
| -------------- | ---------------------- |
|
||||||
|
| [path/to/file] | [อธิบายการเปลี่ยนแปลง] |
|
||||||
|
|
||||||
|
## กฎที่ Lock แล้ว
|
||||||
|
|
||||||
|
[บันทึก pattern หรือ decision ที่ตกลง]
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
[วิธีตรวจสอบว่างานสำเร็จ]
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **อัปเดต `specs/88-logs/rollouts.md`**
|
||||||
|
- เพิ่ม entry ใหม่ในตาราง Recent Rollouts
|
||||||
|
- รูปแบบ: `| วันที่ | Version | รายการ | สถานะ |`
|
||||||
|
|
||||||
|
### 2. อัปเดต Project Memory (ถ้ามี decision ใหม่)
|
||||||
|
|
||||||
|
เมื่อมีการตัดสินใจสำคัญใหม่ให้:
|
||||||
|
|
||||||
|
1. **เปิดไฟล์ `memory/project-memory-override.md`**
|
||||||
|
|
||||||
|
2. **อัปเดตตาราง "Current Decisions (Locked)"**
|
||||||
|
- เพิ่ม entry ใหม่ถ้ามี decision ใหม่
|
||||||
|
- รูปแบบ: `| ID | Decision | ADR |`
|
||||||
|
|
||||||
|
3. **อัปเดต "Next Session Focus"**
|
||||||
|
- เพิ่มงานใหม่ถ้ามี
|
||||||
|
- ทำเครื่องหมาย `[ ]` สำหรับงานที่ยังไม่เสร็จ
|
||||||
|
- ทำเครื่องหมาย `[X]` สำหรับงานที่เสร็จแล้ว
|
||||||
|
|
||||||
|
4. **อัปเดต "Environment & Services"** (ถ้ามีการเปลี่ยนแปลง)
|
||||||
|
- อัปเดต URL, port, หรือ notes ถ้ามีการเปลี่ยน infrastructure
|
||||||
|
|
||||||
|
### 3. อัปเดต MCP Tools (ถ้ามี tools ใหม่)
|
||||||
|
|
||||||
|
เมื่อมี MCP tools ใหม่ให้:
|
||||||
|
|
||||||
|
1. **เปิดไฟล์ `memory/mcp-tools.md`**
|
||||||
|
|
||||||
|
2. **เพิ่ม tool ใหม่ในตาราง "Available Tools"**
|
||||||
|
- รูปแบบ: `| Tool | Purpose | Example Usage |`
|
||||||
|
|
||||||
|
3. **เพิ่ม usage example และ warnings** ถ้าจำเป็น
|
||||||
|
|
||||||
|
### 4. อัปเดต Root Documentation (ถ้ามีการเปลี่ยนแปลง)
|
||||||
|
|
||||||
|
เมื่อมีการเปลี่ยนแปลงที่ส่งผลต่อเอกสารระดับ root ให้:
|
||||||
|
|
||||||
|
1. **ARCHITECTURE.md** — อัปเดตเมื่อ:
|
||||||
|
- เปลี่ยน architecture หลัก
|
||||||
|
- เพิ่ม/ลบ component สำคัญ
|
||||||
|
- เปลี่ยน data flow หรือ integration pattern
|
||||||
|
|
||||||
|
2. **CHANGELOG.md** — อัปเดตเมื่อ:
|
||||||
|
- Deploy version ใหม่
|
||||||
|
- เพิ่ม feature หรือ breaking change สำคัญ
|
||||||
|
- รูปแบบ: `## [version] (YYYY-MM-DD)` → `### feat(scope): description`
|
||||||
|
|
||||||
|
3. **CONTEXT.md** — อัปเดตเมื่อ:
|
||||||
|
- เปลี่ยน domain terminology หลัก
|
||||||
|
- เพิ่ม concept ใหม่ที่ใช้ทั่ว project
|
||||||
|
- อัปเดต glossary หรือ business rules
|
||||||
|
|
||||||
|
4. **CONTRIBUTING.md** — อัปเดตเมื่อ:
|
||||||
|
- เปลี่ยน workflow การทำงาน
|
||||||
|
- เพิ่ม/เปลี่ยน coding standards
|
||||||
|
- อัปเดต CI/CD process
|
||||||
|
|
||||||
|
5. **README.md** — อัปเดตเมื่อ:
|
||||||
|
- เปลี่ยน project structure
|
||||||
|
- เพิ่ม/เปลี่ยน installation steps
|
||||||
|
- อัปเดต feature overview หรือ tech stack
|
||||||
|
|
||||||
|
## Template สำหรับ Session Log
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Session [N] — YYYY-MM-DD ([Topic])
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
[สรุปสิ่งที่ทำใน session นี้ใน 1-2 ประโยค]
|
||||||
|
|
||||||
|
## ปัญหาที่พบ (Root Cause)
|
||||||
|
|
||||||
|
[อธิบายปัญหาและสาเหตุหลัก]
|
||||||
|
|
||||||
|
## การแก้ไข (Fix)
|
||||||
|
|
||||||
|
| ไฟล์ | การเปลี่ยนแปลง |
|
||||||
|
| -------------- | ---------------------- |
|
||||||
|
| `path/to/file` | [อธิบายการเปลี่ยนแปลง] |
|
||||||
|
|
||||||
|
## กฎที่ Lock แล้ว
|
||||||
|
|
||||||
|
[บันทึก pattern หรือ decision ที่ตกลงและไม่ควรเปลี่ยน]
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- [ ] [check 1]
|
||||||
|
- [ ] [check 2]
|
||||||
|
```
|
||||||
|
|
||||||
|
## ข้อควรระวัง
|
||||||
|
|
||||||
|
- **ห้าม** บันทึก rules ที่ซ้ำกับ specs/ (ADRs, glossary, guidelines)
|
||||||
|
- **ห้าม** บันทึก commands ที่ซ้ำกับ specs/05-Engineering-Guidelines/
|
||||||
|
- **ห้าม** บันทึก environment ที่ซ้ำกับ specs/04-Infrastructure-OPS/
|
||||||
|
- **ใช้** `specs/88-logs/` สำหรับ session history และ rollouts
|
||||||
|
- **ใช้** `memory/project-memory-override.md` สำหรับ OS rules, decisions, environment ที่ไม่มีใน specs
|
||||||
|
- **ใช้** `memory/mcp-tools.md` สำหรับ MCP tools documentation
|
||||||
|
- **อัปเดต Root Documentation** (ARCHITECTURE.md, CHANGELOG.md, CONTEXT.md, CONTRIBUTING.md, README.md) เฉพาะเมื่อมีการเปลี่ยนแปลงที่ส่งผลต่อ project architecture, version, terminology, workflow หรือ structure
|
||||||
|
|
||||||
|
## ตัวอย่างการใช้งาน
|
||||||
|
|
||||||
|
### กรณีที่ 1: ทำงาน session ใหม่
|
||||||
|
|
||||||
|
```
|
||||||
|
1. สร้างไฟล์ specs/88-logs/session-2026-06-07-bug-fix.md
|
||||||
|
2. บันทึกปัญหา, การแก้ไข, verification
|
||||||
|
3. อัปเดต specs/88-logs/rollouts.md
|
||||||
|
```
|
||||||
|
|
||||||
|
### กรณีที่ 2: มี decision ใหม่
|
||||||
|
|
||||||
|
```
|
||||||
|
1. เปิด memory/project-memory-override.md
|
||||||
|
2. เพิ่ม entry ใหม่ในตาราง Current Decisions
|
||||||
|
3. อัปเดต Next Session Focus
|
||||||
|
```
|
||||||
|
|
||||||
|
### กรณีที่ 3: เปลี่ยน infrastructure
|
||||||
|
|
||||||
|
```
|
||||||
|
1. เปิด memory/project-memory-override.md
|
||||||
|
2. อัปเดตตาราง Environment & Services
|
||||||
|
3. อัปเดต Key Environment Variables ถ้าจำเป็น
|
||||||
|
```
|
||||||
|
|
||||||
|
### กรณีที่ 4: อัปเดต Root Documentation
|
||||||
|
|
||||||
|
```
|
||||||
|
1. ตรวจสอบว่ามีการเปลี่ยนแปลงที่ส่งผลต่อ ARCHITECTURE.md, CHANGELOG.md, CONTEXT.md, CONTRIBUTING.md, หรือ README.md
|
||||||
|
2. อัปเดตไฟล์ที่เกี่ยวข้องตามรูปแบบที่กำหนด
|
||||||
|
3. ตรวจสอบว่าการเปลี่ยนแปลงสอดคล้องกับ specs/ และ ADRs
|
||||||
|
```
|
||||||
@@ -0,0 +1,517 @@
|
|||||||
|
---
|
||||||
|
name: security-review
|
||||||
|
description: Comprehensive security review for LCBP3-DMS with OWASP Top 10 checklist, ADR compliance, and automated security testing patterns.
|
||||||
|
version: 1.9.0
|
||||||
|
scope: security
|
||||||
|
depends-on: []
|
||||||
|
handoffs-to: [speckit-reviewer, speckit-security-audit]
|
||||||
|
user-invocable: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Security Review Skill
|
||||||
|
|
||||||
|
Comprehensive security review for LCBP3-DMS ensuring all code follows security best practices and identifies potential vulnerabilities.
|
||||||
|
|
||||||
|
## LCBP3 Context
|
||||||
|
|
||||||
|
See [`_LCBP3-CONTEXT.md`](../_LCBP3-CONTEXT.md) for project-specific security requirements:
|
||||||
|
- **ADR-016**: Security & Authentication (JWT, CASL, RBAC, file upload)
|
||||||
|
- **ADR-018**: AI Boundary (Ollama on Admin Desktop only, no direct DB/storage access)
|
||||||
|
- **ADR-019**: UUID Strategy (no parseInt/Number/+ on UUID)
|
||||||
|
- **ADR-023**: Unified AI Architecture (AI via DMS API only)
|
||||||
|
- **ADR-007**: Error Handling (layered error classification)
|
||||||
|
|
||||||
|
## When to Activate
|
||||||
|
|
||||||
|
Invoke this skill:
|
||||||
|
- Implementing authentication or authorization
|
||||||
|
- Handling user input or file uploads
|
||||||
|
- Creating new API endpoints
|
||||||
|
- Working with secrets or credentials
|
||||||
|
- Integrating AI features (Ollama/Qdrant)
|
||||||
|
- Storing or transmitting sensitive data
|
||||||
|
- Integrating third-party APIs
|
||||||
|
|
||||||
|
## Security Checklist
|
||||||
|
|
||||||
|
### 1. Secrets Management
|
||||||
|
|
||||||
|
#### FAIL: NEVER Do This
|
||||||
|
```typescript
|
||||||
|
const apiKey = "sk-proj-xxxxx" // Hardcoded secret
|
||||||
|
const dbPassword = "password123" // In source code
|
||||||
|
```
|
||||||
|
|
||||||
|
#### PASS: ALWAYS Do This
|
||||||
|
```typescript
|
||||||
|
const apiKey = process.env.OPENAI_API_KEY
|
||||||
|
const dbUrl = process.env.DATABASE_URL
|
||||||
|
|
||||||
|
// Verify secrets exist
|
||||||
|
if (!apiKey) {
|
||||||
|
throw new Error('OPENAI_API_KEY not configured')
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Verification Steps
|
||||||
|
- [ ] No hardcoded API keys, tokens, or passwords
|
||||||
|
- [ ] All secrets in environment variables
|
||||||
|
- [ ] `.env.local` in .gitignore
|
||||||
|
- [ ] No secrets in git history
|
||||||
|
- [ ] Production secrets in QNAP docker-compose environment section (not .env files)
|
||||||
|
|
||||||
|
### 2. Input Validation
|
||||||
|
|
||||||
|
#### Always Validate User Input
|
||||||
|
```typescript
|
||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
// Define validation schema
|
||||||
|
const CreateCorrespondenceSchema = z.object({
|
||||||
|
subject: z.string().min(1).max(500),
|
||||||
|
recipientId: z.string().uuid(),
|
||||||
|
typeCode: z.string().min(1).max(50)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Validate before processing
|
||||||
|
export async function createCorrespondence(input: unknown) {
|
||||||
|
try {
|
||||||
|
const validated = CreateCorrespondenceSchema.parse(input)
|
||||||
|
return await correspondenceService.create(validated)
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof z.ZodError) {
|
||||||
|
throw new BadRequestException(error.errors)
|
||||||
|
}
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### File Upload Validation (ADR-016)
|
||||||
|
```typescript
|
||||||
|
function validateFileUpload(file: Express.Multer.File) {
|
||||||
|
// Size check (50MB max per ADR-016)
|
||||||
|
const maxSize = 50 * 1024 * 1024
|
||||||
|
if (file.size > maxSize) {
|
||||||
|
throw new BadRequestException('File too large (max 50MB)')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type check (whitelist: PDF, DWG, DOCX, XLSX, ZIP)
|
||||||
|
const allowedTypes = [
|
||||||
|
'application/pdf',
|
||||||
|
'application/vnd.dwg',
|
||||||
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||||
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||||
|
'application/zip'
|
||||||
|
]
|
||||||
|
if (!allowedTypes.includes(file.mimetype)) {
|
||||||
|
throw new BadRequestException('Invalid file type')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extension check
|
||||||
|
const allowedExtensions = ['.pdf', '.dwg', '.docx', '.xlsx', '.zip']
|
||||||
|
const extension = path.extname(file.originalname).toLowerCase()
|
||||||
|
if (!allowedExtensions.includes(extension)) {
|
||||||
|
throw new BadRequestException('Invalid file extension')
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Verification Steps
|
||||||
|
- [ ] All user inputs validated with Zod (frontend) + class-validator (backend)
|
||||||
|
- [ ] File uploads restricted (50MB max, whitelist types)
|
||||||
|
- [ ] No direct use of user input in queries
|
||||||
|
- [ ] Whitelist validation (not blacklist)
|
||||||
|
- [ ] Error messages don't leak sensitive info
|
||||||
|
|
||||||
|
### 3. SQL Injection Prevention
|
||||||
|
|
||||||
|
#### FAIL: NEVER Concatenate SQL
|
||||||
|
```typescript
|
||||||
|
// DANGEROUS - SQL Injection vulnerability
|
||||||
|
const query = `SELECT * FROM correspondences WHERE uuid = '${correspondenceUuid}'`
|
||||||
|
await this.connection.query(query)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### PASS: ALWAYS Use TypeORM Parameterized Queries
|
||||||
|
```typescript
|
||||||
|
// Safe - TypeORM parameterized query
|
||||||
|
const correspondence = await this.correspondenceRepository.findOne({
|
||||||
|
where: { publicId: correspondenceUuid }
|
||||||
|
})
|
||||||
|
|
||||||
|
// Or with QueryBuilder
|
||||||
|
const result = await this.correspondenceRepository
|
||||||
|
.createQueryBuilder('c')
|
||||||
|
.where('c.publicId = :uuid', { uuid: correspondenceUuid })
|
||||||
|
.getOne()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Verification Steps
|
||||||
|
- [ ] All database queries use TypeORM parameterized queries
|
||||||
|
- [ ] No string concatenation in SQL
|
||||||
|
- [ ] TypeORM query builder used correctly
|
||||||
|
- [ ] Schema verified before writing queries (ADR-009)
|
||||||
|
|
||||||
|
### 4. Authentication & Authorization (ADR-016)
|
||||||
|
|
||||||
|
#### JWT Token Handling
|
||||||
|
```typescript
|
||||||
|
// FAIL: WRONG: localStorage (vulnerable to XSS)
|
||||||
|
localStorage.setItem('token', token)
|
||||||
|
|
||||||
|
// PASS: CORRECT: httpOnly cookies
|
||||||
|
response.setHeader('Set-Cookie',
|
||||||
|
`token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Authorization Checks (CASL)
|
||||||
|
```typescript
|
||||||
|
// Controller with CASL guard
|
||||||
|
@Post()
|
||||||
|
@UseGuards(JwtAuthGuard, RolesGuard, AbilitiesGuard)
|
||||||
|
@CheckAbilities({ action: 'create', subject: 'Correspondence' })
|
||||||
|
async create(@Body() dto: CreateCorrespondenceDto, @Request() req) {
|
||||||
|
// Service logic
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### RBAC Matrix (ADR-016)
|
||||||
|
- [ ] 4-Level RBAC matrix implemented (Admin, Manager, User, Viewer)
|
||||||
|
- [ ] CASL AbilityFactory configured with correct permissions
|
||||||
|
- [ ] JwtAuthGuard on all protected routes
|
||||||
|
- [ ] RolesGuard for role-based access
|
||||||
|
- [ ] AuditLogInterceptor on all mutation endpoints
|
||||||
|
|
||||||
|
#### Verification Steps
|
||||||
|
- [ ] Tokens stored in httpOnly cookies (not localStorage)
|
||||||
|
- [ ] Authorization checks before sensitive operations
|
||||||
|
- [ ] CASL abilities configured correctly
|
||||||
|
- [ ] Role-based access control implemented
|
||||||
|
- [ ] Session management secure
|
||||||
|
|
||||||
|
### 5. XSS Prevention
|
||||||
|
|
||||||
|
#### Sanitize HTML
|
||||||
|
```typescript
|
||||||
|
import DOMPurify from 'isomorphic-dompurify'
|
||||||
|
|
||||||
|
// ALWAYS sanitize user-provided HTML
|
||||||
|
function renderUserContent(html: string) {
|
||||||
|
const clean = DOMPurify.sanitize(html, {
|
||||||
|
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p'],
|
||||||
|
ALLOWED_ATTR: []
|
||||||
|
})
|
||||||
|
return <div dangerouslySetInnerHTML={{ __html: clean }} />
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Content Security Policy (Next.js)
|
||||||
|
```typescript
|
||||||
|
// next.config.js
|
||||||
|
const securityHeaders = [
|
||||||
|
{
|
||||||
|
key: 'Content-Security-Policy',
|
||||||
|
value: `
|
||||||
|
default-src 'self';
|
||||||
|
script-src 'self' 'unsafe-eval' 'unsafe-inline';
|
||||||
|
style-src 'self' 'unsafe-inline';
|
||||||
|
img-src 'self' data: https:;
|
||||||
|
font-src 'self';
|
||||||
|
connect-src 'self' http://localhost:3001 https://192.168.10.8;
|
||||||
|
`.replace(/\s{2,}/g, ' ').trim()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Verification Steps
|
||||||
|
- [ ] User-provided HTML sanitized
|
||||||
|
- [ ] CSP headers configured
|
||||||
|
- [ ] No unvalidated dynamic content rendering
|
||||||
|
- [ ] React's built-in XSS protection used
|
||||||
|
|
||||||
|
### 6. CSRF Protection
|
||||||
|
|
||||||
|
#### CSRF Tokens
|
||||||
|
```typescript
|
||||||
|
import { csrf } from '@/lib/csrf'
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const token = request.headers.get('X-CSRF-Token')
|
||||||
|
|
||||||
|
if (!csrf.verify(token)) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Invalid CSRF token' },
|
||||||
|
{ status: 403 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process request
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### SameSite Cookies
|
||||||
|
```typescript
|
||||||
|
response.setHeader('Set-Cookie',
|
||||||
|
`session=${sessionId}; HttpOnly; Secure; SameSite=Strict`
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Verification Steps
|
||||||
|
- [ ] CSRF tokens on state-changing operations
|
||||||
|
- [ ] SameSite=Strict on all cookies
|
||||||
|
- [ ] Double-submit cookie pattern implemented
|
||||||
|
|
||||||
|
### 7. Rate Limiting (ADR-016)
|
||||||
|
|
||||||
|
#### API Rate Limiting
|
||||||
|
```typescript
|
||||||
|
import { ThrottlerGuard } from '@nestjs/throttler'
|
||||||
|
|
||||||
|
// Apply to auth endpoints
|
||||||
|
@UseGuards(ThrottlerGuard)
|
||||||
|
@Throttle({ default: { limit: 10, ttl: 60000 } })
|
||||||
|
async login(@Body() dto: LoginDto) {
|
||||||
|
// Login logic
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Expensive Operations
|
||||||
|
```typescript
|
||||||
|
// Aggressive rate limiting for AI endpoints
|
||||||
|
@Throttle({ default: { limit: 5, ttl: 60000 } })
|
||||||
|
async extractMetadata(@Body() dto: ExtractMetadataDto) {
|
||||||
|
// AI extraction logic
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Verification Steps
|
||||||
|
- [ ] Rate limiting on all auth endpoints (ADR-016)
|
||||||
|
- [ ] Rate limiting on AI endpoints (ADR-018/023)
|
||||||
|
- [ ] IP-based rate limiting
|
||||||
|
- [ ] User-based rate limiting (authenticated)
|
||||||
|
|
||||||
|
### 8. Sensitive Data Exposure
|
||||||
|
|
||||||
|
#### Logging
|
||||||
|
```typescript
|
||||||
|
// FAIL: WRONG: Logging sensitive data
|
||||||
|
this.logger.log('User login:', { email, password })
|
||||||
|
this.logger.log('Payment:', { cardNumber, cvv })
|
||||||
|
|
||||||
|
// PASS: CORRECT: Redact sensitive data
|
||||||
|
this.logger.log('User login:', { email, userId })
|
||||||
|
this.logger.log('Payment:', { last4: card.last4, userId })
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Error Messages (ADR-007)
|
||||||
|
```typescript
|
||||||
|
// FAIL: WRONG: Exposing internal details
|
||||||
|
catch (error) {
|
||||||
|
return { error: error.message, stack: error.stack }
|
||||||
|
}
|
||||||
|
|
||||||
|
// PASS: CORRECT: Generic error messages
|
||||||
|
catch (error) {
|
||||||
|
this.logger.error('Internal error:', error)
|
||||||
|
throw new BadRequestException('An error occurred. Please try again.')
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Verification Steps
|
||||||
|
- [ ] No passwords, tokens, or secrets in logs
|
||||||
|
- [ ] Error messages generic for users
|
||||||
|
- [ ] Detailed errors only in server logs
|
||||||
|
- [ ] No stack traces exposed to users
|
||||||
|
|
||||||
|
### 9. AI Boundary Enforcement (ADR-018/023)
|
||||||
|
|
||||||
|
#### FAIL: NEVER Do This
|
||||||
|
```typescript
|
||||||
|
// Direct AI access - FORBIDDEN
|
||||||
|
import ollama from 'ollama'
|
||||||
|
const response = await ollama.chat({ model: 'gemma4', messages })
|
||||||
|
|
||||||
|
// Direct Qdrant access - FORBIDDEN
|
||||||
|
import { QdrantClient } from '@qdrant/js-client-rest'
|
||||||
|
const client = new QdrantClient({ url: 'http://localhost:6333' })
|
||||||
|
```
|
||||||
|
|
||||||
|
#### PASS: ALWAYS Do This
|
||||||
|
```typescript
|
||||||
|
// AI via DMS API only
|
||||||
|
const response = await fetch('http://localhost:3001/api/ai/extract-metadata', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ documentId })
|
||||||
|
})
|
||||||
|
|
||||||
|
// Qdrant via DMS API only
|
||||||
|
const response = await fetch('http://localhost:3001/api/ai/search', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ query, projectPublicId })
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Verification Steps
|
||||||
|
- [ ] AI processing on Admin Desktop only (Desk-5439)
|
||||||
|
- [ ] No direct Ollama calls from backend/frontend
|
||||||
|
- [ ] No direct Qdrant calls from backend/frontend
|
||||||
|
- [ ] All AI interactions via DMS API endpoints
|
||||||
|
- [ ] AI audit logging implemented (ADR-020)
|
||||||
|
- [ ] Human-in-the-loop validation for AI outputs
|
||||||
|
|
||||||
|
### 10. UUID Handling (ADR-019)
|
||||||
|
|
||||||
|
#### FAIL: NEVER Do This
|
||||||
|
```typescript
|
||||||
|
// parseInt on UUID - FORBIDDEN
|
||||||
|
const projectId = parseInt(projectUuid) // "0195..." → 19 (WRONG!)
|
||||||
|
|
||||||
|
// Number on UUID - FORBIDDEN
|
||||||
|
const projectId = Number(projectUuid)
|
||||||
|
|
||||||
|
// + operator on UUID - FORBIDDEN
|
||||||
|
const projectId = +projectUuid
|
||||||
|
|
||||||
|
// id ?? '' fallback - FORBIDDEN
|
||||||
|
const value = c.publicId ?? c.id ?? ''
|
||||||
|
```
|
||||||
|
|
||||||
|
#### PASS: ALWAYS Do This
|
||||||
|
```typescript
|
||||||
|
// Use UUID string directly
|
||||||
|
const projectId = projectUuid // "019505a1-7c3e-7000-8000-abc123def456"
|
||||||
|
|
||||||
|
// Backend: findOneByUuid returns entity with publicId
|
||||||
|
const project = await this.projectService.findOneByUuid(projectUuid)
|
||||||
|
const projectId = project.id // Internal INT for DB operations
|
||||||
|
|
||||||
|
// Frontend: use publicId only
|
||||||
|
interface ProjectOption {
|
||||||
|
publicId?: string; // No uuid fallback
|
||||||
|
projectName?: string;
|
||||||
|
}
|
||||||
|
const value = c.publicId // "019505a1-7c3e-7000-8000-abc123def456"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Verification Steps
|
||||||
|
- [ ] No `parseInt()` on UUID values
|
||||||
|
- [ ] No `Number()` on UUID values
|
||||||
|
- [ ] No `+` operator on UUID values
|
||||||
|
- [ ] No `id ?? ''` fallback patterns
|
||||||
|
- [ ] Use `publicId` (string UUID) in API responses
|
||||||
|
- [ ] Internal INT `id` marked with `@Exclude()` in entities
|
||||||
|
|
||||||
|
### 11. Dependency Security
|
||||||
|
|
||||||
|
#### Regular Updates
|
||||||
|
```bash
|
||||||
|
# Check for vulnerabilities
|
||||||
|
pnpm audit
|
||||||
|
|
||||||
|
# Fix automatically fixable issues
|
||||||
|
pnpm audit fix
|
||||||
|
|
||||||
|
# Update dependencies
|
||||||
|
pnpm update
|
||||||
|
|
||||||
|
# Check for outdated packages
|
||||||
|
pnpm outdated
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Lock Files
|
||||||
|
```bash
|
||||||
|
# ALWAYS commit lock files
|
||||||
|
git add pnpm-lock.yaml
|
||||||
|
|
||||||
|
# Use in CI/CD for reproducible builds
|
||||||
|
pnpm install --frozen-lockfile
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Verification Steps
|
||||||
|
- [ ] Dependencies up to date
|
||||||
|
- [ ] No known vulnerabilities (pnpm audit clean)
|
||||||
|
- [ ] Lock files committed
|
||||||
|
- [ ] Regular security updates
|
||||||
|
|
||||||
|
## Security Testing
|
||||||
|
|
||||||
|
### Automated Security Tests
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Test authentication
|
||||||
|
test('requires authentication', async () => {
|
||||||
|
const response = await fetch('/api/correspondences')
|
||||||
|
expect(response.status).toBe(401)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test authorization
|
||||||
|
test('requires admin role', async () => {
|
||||||
|
const response = await fetch('/api/admin/users', {
|
||||||
|
headers: { Authorization: `Bearer ${userToken}` }
|
||||||
|
})
|
||||||
|
expect(response.status).toBe(403)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test input validation
|
||||||
|
test('rejects invalid input', async () => {
|
||||||
|
const response = await fetch('/api/correspondences', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ subject: '', recipientId: 'invalid' })
|
||||||
|
})
|
||||||
|
expect(response.status).toBe(400)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test rate limiting
|
||||||
|
test('enforces rate limits', async () => {
|
||||||
|
const requests = Array(11).fill(null).map(() =>
|
||||||
|
fetch('/api/auth/login', { method: 'POST' })
|
||||||
|
)
|
||||||
|
|
||||||
|
const responses = await Promise.all(requests)
|
||||||
|
const tooManyRequests = responses.filter(r => r.status === 429)
|
||||||
|
|
||||||
|
expect(tooManyRequests.length).toBeGreaterThan(0)
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pre-Deployment Security Checklist
|
||||||
|
|
||||||
|
Before ANY production deployment:
|
||||||
|
|
||||||
|
- [ ] **Secrets**: No hardcoded secrets, all in env vars
|
||||||
|
- [ ] **Input Validation**: All user inputs validated (Zod + class-validator)
|
||||||
|
- [ ] **SQL Injection**: All queries parameterized (TypeORM)
|
||||||
|
- [ ] **XSS**: User content sanitized
|
||||||
|
- [ ] **CSRF**: Protection enabled
|
||||||
|
- [ ] **Authentication**: Proper token handling (httpOnly cookies)
|
||||||
|
- [ ] **Authorization**: RBAC + CASL checks in place
|
||||||
|
- [ ] **Rate Limiting**: Enabled on auth and AI endpoints
|
||||||
|
- [ ] **HTTPS**: Enforced in production
|
||||||
|
- [ ] **Security Headers**: CSP, X-Frame-Options configured
|
||||||
|
- [ ] **Error Handling**: No sensitive data in errors (ADR-007)
|
||||||
|
- [ ] **Logging**: No sensitive data logged
|
||||||
|
- [ ] **Dependencies**: Up to date, no vulnerabilities
|
||||||
|
- [ ] **UUID Handling**: No parseInt/Number/+ on UUID (ADR-019)
|
||||||
|
- [ ] **AI Boundary**: AI via DMS API only (ADR-018/023)
|
||||||
|
- [ ] **File Uploads**: Validated (50MB max, whitelist types)
|
||||||
|
- [ ] **AI Audit**: All AI interactions logged (ADR-020)
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
|
||||||
|
- [NestJS Security](https://docs.nestjs.com/security)
|
||||||
|
- [Next.js Security](https://nextjs.org/docs/security)
|
||||||
|
- [ADR-016 Security Authentication](../../specs/06-Decision-Records/ADR-016-security-authentication.md)
|
||||||
|
- [ADR-018 AI Boundary](../../specs/06-Decision-Records/ADR-018-ai-boundary.md)
|
||||||
|
- [ADR-019 UUID Strategy](../../specs/06-Decision-Records/ADR-019-hybrid-identifier-strategy.md)
|
||||||
|
- [ADR-023 AI Architecture](../../specs/06-Decision-Records/ADR-023-unified-ai-architecture.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Remember**: Security is not optional. One vulnerability can compromise the entire platform. When in doubt, err on the side of caution.
|
||||||
+31
-27
@@ -1,8 +1,8 @@
|
|||||||
# 🧠 NAP-DMS Agent Skills (v1.8.9)
|
# 🧠 NAP-DMS Agent Skills (v1.9.0)
|
||||||
|
|
||||||
ไฟล์นี้กำหนดทักษะและความสามารถเฉพาะทางของ Document Intelligence Engine สำหรับโครงการ LCBP3 v1.8.9 เพื่อรักษามาตรฐานสูงสุดด้าน Security และ Data Integrity
|
ไฟล์นี้กำหนดทักษะและความสามารถเฉพาะทางของ Document Intelligence Engine สำหรับโครงการ LCBP3 v1.9.0 เพื่อรักษามาตรฐานสูงสุดด้าน Security และ Data Integrity
|
||||||
|
|
||||||
**Status**: Production Ready | **Last Updated**: 2026-04-22 | **Total Skills**: 20
|
**Status**: Production Ready | **Last Updated**: 2026-06-07 | **Total Skills**: 24
|
||||||
|
|
||||||
> 📌 Shared context for all speckit-\* skills: see [`_LCBP3-CONTEXT.md`](./_LCBP3-CONTEXT.md).
|
> 📌 Shared context for all speckit-\* skills: see [`_LCBP3-CONTEXT.md`](./_LCBP3-CONTEXT.md).
|
||||||
|
|
||||||
@@ -57,28 +57,32 @@
|
|||||||
|
|
||||||
## 🔄 Skill Dependency Matrix
|
## 🔄 Skill Dependency Matrix
|
||||||
|
|
||||||
| Skill | Dependencies | Handoffs To | Notes |
|
| Skill | Dependencies | Handoffs To | Notes |
|
||||||
| -------------------------- | -------------------- | -------------------------------- | ----------------------------- |
|
| -------------------------- | -------------------- | ---------------------------------------- | ----------------------------- |
|
||||||
| **speckit-constitution** | None | speckit-specify | Project governance foundation |
|
| **speckit-constitution** | None | speckit-specify | Project governance foundation |
|
||||||
| **speckit-specify** | speckit-constitution | speckit-clarify | Feature specification |
|
| **speckit-specify** | speckit-constitution | speckit-clarify | Feature specification |
|
||||||
| **speckit-clarify** | speckit-specify | speckit-plan | Resolve ambiguities |
|
| **speckit-clarify** | speckit-specify | speckit-plan | Resolve ambiguities |
|
||||||
| **speckit-plan** | speckit-clarify | speckit-tasks, speckit-checklist | Technical design |
|
| **speckit-plan** | speckit-clarify | speckit-tasks, speckit-checklist | Technical design |
|
||||||
| **speckit-tasks** | speckit-plan | speckit-implement | Task breakdown |
|
| **speckit-tasks** | speckit-plan | speckit-implement | Task breakdown |
|
||||||
| **speckit-implement** | speckit-tasks | speckit-checker | Code implementation |
|
| **speckit-implement** | speckit-tasks | speckit-checker | Code implementation |
|
||||||
| **speckit-checker** | speckit-implement | speckit-tester | Static analysis |
|
| **speckit-checker** | speckit-implement | speckit-tester | Static analysis |
|
||||||
| **speckit-tester** | speckit-checker | speckit-reviewer | Test execution |
|
| **speckit-tester** | speckit-checker | speckit-reviewer | Test execution |
|
||||||
| **speckit-reviewer** | speckit-tester | speckit-validate | Code review |
|
| **speckit-reviewer** | speckit-tester | speckit-validate | Code review |
|
||||||
| **speckit-validate** | speckit-reviewer | None | Requirements validation |
|
| **speckit-validate** | speckit-reviewer | None | Requirements validation |
|
||||||
| **speckit-analyze** | speckit-tasks | None | Cross-artifact consistency |
|
| **speckit-analyze** | speckit-tasks | None | Cross-artifact consistency |
|
||||||
| **speckit-migrate** | None | speckit-plan | Legacy code import |
|
| **speckit-migrate** | None | speckit-plan | Legacy code import |
|
||||||
| **speckit-quizme** | speckit-specify | speckit-plan | Logic validation |
|
| **speckit-quizme** | speckit-specify | speckit-plan | Logic validation |
|
||||||
| **speckit-diff** | None | speckit-plan | Version comparison |
|
| **speckit-diff** | None | speckit-plan | Version comparison |
|
||||||
| **speckit-status** | None | None | Progress tracking |
|
| **speckit-status** | None | None | Progress tracking |
|
||||||
| **speckit-taskstoissues** | speckit-tasks | None | Issue sync |
|
| **speckit-taskstoissues** | speckit-tasks | None | Issue sync |
|
||||||
| **speckit-checklist** | speckit-plan | None | Requirements validation |
|
| **speckit-checklist** | speckit-plan | None | Requirements validation |
|
||||||
| **nestjs-best-practices** | None | speckit-implement | Backend patterns |
|
| **save-memory** | None | None | Session log & memory update |
|
||||||
| **next-best-practices** | None | speckit-implement | Frontend patterns |
|
| **nestjs-best-practices** | None | speckit-implement | Backend patterns |
|
||||||
| **speckit-security-audit** | None | speckit-reviewer | Security validation |
|
| **next-best-practices** | None | speckit-implement | Frontend patterns |
|
||||||
|
| **speckit-security-audit** | None | speckit-reviewer | Security validation |
|
||||||
|
| **e2e-testing** | None | speckit-tester | Playwright E2E patterns |
|
||||||
|
| **verification-loop** | None | speckit-checker, speckit-tester | Comprehensive verification |
|
||||||
|
| **security-review** | None | speckit-reviewer, speckit-security-audit | OWASP Top 10 + ADR compliance |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -96,8 +100,8 @@
|
|||||||
|
|
||||||
### Health Metrics
|
### Health Metrics
|
||||||
|
|
||||||
- **Total Skills**: 20 implemented
|
- **Total Skills**: 24 implemented
|
||||||
- **Version Alignment**: v1.8.9 across all skills
|
- **Version Alignment**: v1.9.0 across all skills
|
||||||
- **Template Coverage**: 100% for skills requiring templates
|
- **Template Coverage**: 100% for skills requiring templates
|
||||||
- **Documentation**: Complete front matter + shared `_LCBP3-CONTEXT.md` appendix
|
- **Documentation**: Complete front matter + shared `_LCBP3-CONTEXT.md` appendix
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-analyze
|
name: speckit-analyze
|
||||||
description: Perform a non-destructive cross-artifact consistency and quality analysis across spec.md, plan.md, and tasks.md after task generation.
|
description: Perform a non-destructive cross-artifact consistency and quality analysis across spec.md, plan.md, and tasks.md after task generation.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
depends-on:
|
depends-on:
|
||||||
- speckit-tasks
|
- speckit-tasks
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-checker
|
name: speckit-checker
|
||||||
description: Run static analysis tools and aggregate results.
|
description: Run static analysis tools and aggregate results.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
depends-on: []
|
depends-on: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-checklist
|
name: speckit-checklist
|
||||||
description: Generate a custom checklist for the current feature based on user requirements.
|
description: Generate a custom checklist for the current feature based on user requirements.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
---
|
---
|
||||||
|
|
||||||
## Checklist Purpose: "Unit Tests for English"
|
## Checklist Purpose: "Unit Tests for English"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-clarify
|
name: speckit-clarify
|
||||||
description: Identify underspecified areas in the current feature spec by asking up to 5 highly targeted clarification questions and encoding answers back into the spec.
|
description: Identify underspecified areas in the current feature spec by asking up to 5 highly targeted clarification questions and encoding answers back into the spec.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
depends-on:
|
depends-on:
|
||||||
- speckit-specify
|
- speckit-specify
|
||||||
handoffs:
|
handoffs:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-constitution
|
name: speckit-constitution
|
||||||
description: Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync.
|
description: Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
handoffs:
|
handoffs:
|
||||||
- label: Build Specification
|
- label: Build Specification
|
||||||
agent: speckit-specify
|
agent: speckit-specify
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-diff
|
name: speckit-diff
|
||||||
description: Compare two versions of a spec or plan to highlight changes.
|
description: Compare two versions of a spec or plan to highlight changes.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
depends-on: []
|
depends-on: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-implement
|
name: speckit-implement
|
||||||
description: Execute the implementation plan by processing and executing all tasks defined in tasks.md (with Ironclad Anti-Regression Protocols)
|
description: Execute the implementation plan by processing and executing all tasks defined in tasks.md (with Ironclad Anti-Regression Protocols)
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
depends-on:
|
depends-on:
|
||||||
- speckit-tasks
|
- speckit-tasks
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-migrate
|
name: speckit-migrate
|
||||||
description: Migrate existing projects into the speckit structure by generating spec.md, plan.md, and tasks.md from existing code.
|
description: Migrate existing projects into the speckit structure by generating spec.md, plan.md, and tasks.md from existing code.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
depends-on: []
|
depends-on: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-plan
|
name: speckit-plan
|
||||||
description: Execute the implementation planning workflow using the plan template to generate design artifacts.
|
description: Execute the implementation planning workflow using the plan template to generate design artifacts.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
depends-on:
|
depends-on:
|
||||||
- speckit-specify
|
- speckit-specify
|
||||||
handoffs:
|
handoffs:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-quizme
|
name: speckit-quizme
|
||||||
description: Challenge the specification with Socratic questioning to identify logical gaps, unhandled edge cases, and robustness issues.
|
description: Challenge the specification with Socratic questioning to identify logical gaps, unhandled edge cases, and robustness issues.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
handoffs:
|
handoffs:
|
||||||
- label: Clarify Spec Requirements
|
- label: Clarify Spec Requirements
|
||||||
agent: speckit-clarify
|
agent: speckit-clarify
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-reviewer
|
name: speckit-reviewer
|
||||||
description: Perform code review with actionable feedback and suggestions.
|
description: Perform code review with actionable feedback and suggestions.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
depends-on: []
|
depends-on: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-security-audit
|
name: speckit-security-audit
|
||||||
description: Perform a security-focused audit of the codebase against OWASP Top 10, CASL authorization, and LCBP3-DMS security requirements.
|
description: Perform a security-focused audit of the codebase against OWASP Top 10, CASL authorization, and LCBP3-DMS security requirements.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
depends-on:
|
depends-on:
|
||||||
- speckit-checker
|
- speckit-checker
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-specify
|
name: speckit-specify
|
||||||
description: Create or update the feature specification from a natural language feature description.
|
description: Create or update the feature specification from a natural language feature description.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
handoffs:
|
handoffs:
|
||||||
- label: Build Technical Plan
|
- label: Build Technical Plan
|
||||||
agent: speckit-plan
|
agent: speckit-plan
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-status
|
name: speckit-status
|
||||||
description: Display a dashboard showing feature status, completion percentage, and blockers.
|
description: Display a dashboard showing feature status, completion percentage, and blockers.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
depends-on: []
|
depends-on: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-tasks
|
name: speckit-tasks
|
||||||
description: Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts.
|
description: Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
depends-on:
|
depends-on:
|
||||||
- speckit-plan
|
- speckit-plan
|
||||||
handoffs:
|
handoffs:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-taskstoissues
|
name: speckit-taskstoissues
|
||||||
description: Convert existing tasks into actionable, dependency-ordered issues for the feature based on available design artifacts.
|
description: Convert existing tasks into actionable, dependency-ordered issues for the feature based on available design artifacts.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
depends-on:
|
depends-on:
|
||||||
- speckit-tasks
|
- speckit-tasks
|
||||||
tools: ['github/github-mcp-server/issue_write']
|
tools: ['github/github-mcp-server/issue_write']
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-tester
|
name: speckit-tester
|
||||||
description: Execute tests, measure coverage, and report results.
|
description: Execute tests, measure coverage, and report results.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
depends-on: []
|
depends-on: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: speckit-validate
|
name: speckit-validate
|
||||||
description: Validate that implementation matches specification requirements.
|
description: Validate that implementation matches specification requirements.
|
||||||
version: 1.8.9
|
version: 1.9.0
|
||||||
depends-on:
|
depends-on:
|
||||||
- speckit-implement
|
- speckit-implement
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -0,0 +1,224 @@
|
|||||||
|
---
|
||||||
|
name: verification-loop
|
||||||
|
description: A comprehensive verification system for LCBP3-DMS development sessions with build, type check, lint, test, security scan, and diff review phases.
|
||||||
|
version: 1.9.0
|
||||||
|
scope: verification
|
||||||
|
depends-on: []
|
||||||
|
handoffs-to: [speckit-checker, speckit-tester]
|
||||||
|
user-invocable: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Verification Loop Skill
|
||||||
|
|
||||||
|
A comprehensive verification system for LCBP3-DMS development sessions.
|
||||||
|
|
||||||
|
## LCBP3 Context
|
||||||
|
|
||||||
|
See [`_LCBP3-CONTEXT.md`](../_LCBP3-CONTEXT.md) for project-specific verification requirements:
|
||||||
|
- Backend: NestJS with TypeScript strict mode
|
||||||
|
- Frontend: Next.js with TypeScript strict mode
|
||||||
|
- Package manager: pnpm
|
||||||
|
- Coverage goals: Backend 70%+, Business Logic 80%+
|
||||||
|
- Security: ADR-016, ADR-018, ADR-019, ADR-023 compliance
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
Invoke this skill:
|
||||||
|
- After completing a feature or significant code change
|
||||||
|
- Before creating a PR
|
||||||
|
- When you want to ensure quality gates pass
|
||||||
|
- After refactoring
|
||||||
|
- Before deploying to staging/production
|
||||||
|
|
||||||
|
## Verification Phases
|
||||||
|
|
||||||
|
### Phase 1: Build Verification
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backend build
|
||||||
|
cd backend
|
||||||
|
pnpm build 2>&1 | tail -20
|
||||||
|
|
||||||
|
# Frontend build
|
||||||
|
cd frontend
|
||||||
|
pnpm build 2>&1 | tail -20
|
||||||
|
```
|
||||||
|
|
||||||
|
If build fails, STOP and fix before continuing.
|
||||||
|
|
||||||
|
### Phase 2: Type Check
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backend TypeScript
|
||||||
|
cd backend
|
||||||
|
pnpm typecheck 2>&1 | head -30
|
||||||
|
|
||||||
|
# Frontend TypeScript
|
||||||
|
cd frontend
|
||||||
|
pnpm typecheck 2>&1 | head -30
|
||||||
|
```
|
||||||
|
|
||||||
|
Report all type errors. Fix critical ones before continuing.
|
||||||
|
|
||||||
|
### Phase 3: Lint Check
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backend lint
|
||||||
|
cd backend
|
||||||
|
pnpm lint 2>&1 | head -30
|
||||||
|
|
||||||
|
# Frontend lint
|
||||||
|
cd frontend
|
||||||
|
pnpm lint 2>&1 | head -30
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Test Suite
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backend tests with coverage
|
||||||
|
cd backend
|
||||||
|
pnpm test -- --coverage 2>&1 | tail -50
|
||||||
|
|
||||||
|
# Frontend unit tests
|
||||||
|
cd frontend
|
||||||
|
pnpm test 2>&1 | tail -50
|
||||||
|
|
||||||
|
# Frontend E2E tests (if applicable)
|
||||||
|
cd frontend
|
||||||
|
npx playwright test 2>&1 | tail -50
|
||||||
|
```
|
||||||
|
|
||||||
|
Report:
|
||||||
|
- Total tests: X
|
||||||
|
- Passed: X
|
||||||
|
- Failed: X
|
||||||
|
- Coverage: X%
|
||||||
|
|
||||||
|
### Phase 5: Security Scan
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check for hardcoded secrets
|
||||||
|
grep -rn "sk-" --include="*.ts" --include="*.tsx" . 2>/dev/null | head -10
|
||||||
|
grep -rn "api_key" --include="*.ts" --include="*.tsx" . 2>/dev/null | head -10
|
||||||
|
grep -rn "password" --include="*.ts" --include="*.tsx" . 2>/dev/null | head -10
|
||||||
|
|
||||||
|
# Check for console.log (forbidden in committed code)
|
||||||
|
grep -rn "console.log" --include="*.ts" --include="*.tsx" backend/src/ frontend/src/ 2>/dev/null | head -10
|
||||||
|
|
||||||
|
# Check for any types (forbidden)
|
||||||
|
grep -rn ": any" --include="*.ts" --include="*.tsx" backend/src/ frontend/src/ 2>/dev/null | head -10
|
||||||
|
|
||||||
|
# Check for parseInt on UUID (ADR-019 violation)
|
||||||
|
grep -rn "parseInt(" --include="*.ts" --include="*.tsx" backend/src/ frontend/src/ 2>/dev/null | head -10
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 6: ADR Compliance Check
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check for id ?? '' fallback (ADR-019 violation)
|
||||||
|
grep -rn "id ?? ''" --include="*.ts" --include="*.tsx" frontend/src/ 2>/dev/null | head -10
|
||||||
|
|
||||||
|
# Check for Number() on UUID (ADR-019 violation)
|
||||||
|
grep -rn "Number(" --include="*.ts" --include="*.tsx" frontend/src/ 2>/dev/null | head -10
|
||||||
|
|
||||||
|
# Check for + operator on UUID (ADR-019 violation)
|
||||||
|
grep -rn "+ publicId\|+ id" --include="*.ts" --include="*.tsx" frontend/src/ 2>/dev/null | head -10
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 7: Diff Review
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Show what changed
|
||||||
|
git diff --stat
|
||||||
|
git diff HEAD~1 --name-only
|
||||||
|
|
||||||
|
# Show detailed changes
|
||||||
|
git diff
|
||||||
|
```
|
||||||
|
|
||||||
|
Review each changed file for:
|
||||||
|
- Unintended changes
|
||||||
|
- Missing error handling (ADR-007)
|
||||||
|
- Potential edge cases
|
||||||
|
- UUID handling (ADR-019)
|
||||||
|
- Security vulnerabilities (ADR-016)
|
||||||
|
- AI boundary violations (ADR-018/023)
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
|
||||||
|
After running all phases, produce a verification report:
|
||||||
|
|
||||||
|
```
|
||||||
|
VERIFICATION REPORT
|
||||||
|
==================
|
||||||
|
|
||||||
|
Build: [PASS/FAIL]
|
||||||
|
Types: [PASS/FAIL] (X errors)
|
||||||
|
Lint: [PASS/FAIL] (X warnings)
|
||||||
|
Tests: [PASS/FAIL] (X/Y passed, Z% coverage)
|
||||||
|
Security: [PASS/FAIL] (X issues)
|
||||||
|
ADR: [PASS/FAIL] (X violations)
|
||||||
|
Diff: [X files changed]
|
||||||
|
|
||||||
|
Overall: [READY/NOT READY] for PR
|
||||||
|
|
||||||
|
Issues to Fix:
|
||||||
|
1. ...
|
||||||
|
2. ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Continuous Mode
|
||||||
|
|
||||||
|
For long sessions, run verification every 15 minutes or after major changes:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Set a mental checkpoint:
|
||||||
|
- After completing each function
|
||||||
|
- After finishing a component
|
||||||
|
- Before moving to next task
|
||||||
|
|
||||||
|
Run: /verify
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration with LCBP3 Skills
|
||||||
|
|
||||||
|
This skill complements:
|
||||||
|
- **speckit-checker**: Runs static analysis (lint, typecheck)
|
||||||
|
- **speckit-tester**: Runs tests with coverage verification
|
||||||
|
- **speckit-security-audit**: Performs security review against OWASP Top 10
|
||||||
|
|
||||||
|
This skill provides a unified verification loop that combines all checks into a single report.
|
||||||
|
|
||||||
|
## LCBP3-Specific Checks
|
||||||
|
|
||||||
|
### Tier 1 — CRITICAL (CI BLOCKER)
|
||||||
|
|
||||||
|
- [ ] **Security**: Auth, RBAC, Validation implemented
|
||||||
|
- [ ] **UUID Strategy (ADR-019)**: No `parseInt` / `Number` / `+` on UUID
|
||||||
|
- [ ] **Database correctness**: Schema verified before writing queries
|
||||||
|
- [ ] **File upload security**: ClamAV + whitelist implemented
|
||||||
|
- [ ] **AI validation boundary (ADR-018/023)**: AI via DMS API only
|
||||||
|
- [ ] **Error handling (ADR-007)**: Layered error classification
|
||||||
|
- [ ] **Forbidden patterns**: Zero `any`, zero `console.log`, UUID misuse
|
||||||
|
|
||||||
|
### Tier 2 — IMPORTANT (CODE REVIEW)
|
||||||
|
|
||||||
|
- [ ] **Architecture patterns**: Thin controller, business logic in service
|
||||||
|
- [ ] **Test coverage**: 80%+ business logic, 70%+ backend overall
|
||||||
|
- [ ] **Cache invalidation**: Implemented when data modified
|
||||||
|
- [ ] **Naming conventions**: Follow domain terminology
|
||||||
|
|
||||||
|
### Tier 3 — GUIDELINES
|
||||||
|
|
||||||
|
- [ ] **Code style**: Prettier formatting
|
||||||
|
- [ ] **Comment completeness**: Thai comments, JSDoc on public methods
|
||||||
|
- [ ] **Minor optimizations**: Performance improvements where applicable
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- LCBP3 AGENTS.md: `AGENTS.md` (repo root)
|
||||||
|
- ADR-007 Error Handling: `specs/06-Decision-Records/ADR-007-error-handling-strategy.md`
|
||||||
|
- ADR-016 Security: `specs/06-Decision-Records/ADR-016-security-authentication.md`
|
||||||
|
- ADR-019 UUID: `specs/06-Decision-Records/ADR-019-hybrid-identifier-strategy.md`
|
||||||
|
- ADR-018 AI Boundary: `specs/06-Decision-Records/ADR-018-ai-boundary.md`
|
||||||
|
- ADR-023 AI Architecture: `specs/06-Decision-Records/ADR-023-unified-ai-architecture.md`
|
||||||
@@ -15,220 +15,249 @@ const WORKFLOWS_DIR = path.join(BASE_DIR, '.windsurf', 'workflows');
|
|||||||
|
|
||||||
// Test utilities
|
// Test utilities
|
||||||
class SkillTestSuite {
|
class SkillTestSuite {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.results = {
|
this.results = {
|
||||||
passed: 0,
|
passed: 0,
|
||||||
failed: 0,
|
failed: 0,
|
||||||
errors: []
|
errors: [],
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
log(message, type = 'info') {
|
||||||
|
const colors = {
|
||||||
|
info: '\x1b[36m', // Cyan
|
||||||
|
pass: '\x1b[32m', // Green
|
||||||
|
fail: '\x1b[31m', // Red
|
||||||
|
warn: '\x1b[33m', // Yellow
|
||||||
|
reset: '\x1b[0m',
|
||||||
|
};
|
||||||
|
|
||||||
|
const color = colors[type] || colors.info;
|
||||||
|
console.log(`${color}${message}${colors.reset}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(condition, message) {
|
||||||
|
if (condition) {
|
||||||
|
this.log(` PASS: ${message}`, 'pass');
|
||||||
|
this.results.passed++;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
this.log(` FAIL: ${message}`, 'fail');
|
||||||
|
this.results.failed++;
|
||||||
|
this.results.errors.push(message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testDirectoryExists(dirPath, description) {
|
||||||
|
const exists = fs.existsSync(dirPath);
|
||||||
|
this.assert(exists, `${description} exists at ${dirPath}`);
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
testFileExists(filePath, description) {
|
||||||
|
const exists = fs.existsSync(filePath);
|
||||||
|
this.assert(exists, `${description} exists at ${filePath}`);
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
testFileContent(filePath, pattern, description) {
|
||||||
|
if (!fs.existsSync(filePath)) {
|
||||||
|
this.assert(false, `${description} - file not found: ${filePath}`);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
log(message, type = 'info') {
|
try {
|
||||||
const colors = {
|
const content = fs.readFileSync(filePath, 'utf8');
|
||||||
info: '\x1b[36m', // Cyan
|
const matches = content.match(pattern);
|
||||||
pass: '\x1b[32m', // Green
|
this.assert(matches !== null, `${description} - pattern found in ${filePath}`);
|
||||||
fail: '\x1b[31m', // Red
|
return matches !== null;
|
||||||
warn: '\x1b[33m', // Yellow
|
} catch (error) {
|
||||||
reset: '\x1b[0m'
|
this.assert(false, `${description} - error reading file: ${error.message}`);
|
||||||
};
|
return false;
|
||||||
|
|
||||||
const color = colors[type] || colors.info;
|
|
||||||
console.log(`${color}${message}${colors.reset}`);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
assert(condition, message) {
|
runScript(scriptPath, description) {
|
||||||
if (condition) {
|
try {
|
||||||
this.log(` PASS: ${message}`, 'pass');
|
const output = execSync(scriptPath, { encoding: 'utf8', cwd: BASE_DIR });
|
||||||
this.results.passed++;
|
this.log(` SCRIPT: ${description} executed successfully`, 'pass');
|
||||||
return true;
|
return { success: true, output };
|
||||||
} else {
|
} catch (error) {
|
||||||
this.log(` FAIL: ${message}`, 'fail');
|
this.log(` SCRIPT: ${description} failed - ${error.message}`, 'fail');
|
||||||
this.results.failed++;
|
this.results.failed++;
|
||||||
this.results.errors.push(message);
|
this.results.errors.push(`${description}: ${error.message}`);
|
||||||
return false;
|
return { success: false, error: error.message };
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testDirectoryExists(dirPath, description) {
|
|
||||||
const exists = fs.existsSync(dirPath);
|
|
||||||
this.assert(exists, `${description} exists at ${dirPath}`);
|
|
||||||
return exists;
|
|
||||||
}
|
|
||||||
|
|
||||||
testFileExists(filePath, description) {
|
|
||||||
const exists = fs.existsSync(filePath);
|
|
||||||
this.assert(exists, `${description} exists at ${filePath}`);
|
|
||||||
return exists;
|
|
||||||
}
|
|
||||||
|
|
||||||
testFileContent(filePath, pattern, description) {
|
|
||||||
if (!fs.existsSync(filePath)) {
|
|
||||||
this.assert(false, `${description} - file not found: ${filePath}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const content = fs.readFileSync(filePath, 'utf8');
|
|
||||||
const matches = content.match(pattern);
|
|
||||||
this.assert(matches !== null, `${description} - pattern found in ${filePath}`);
|
|
||||||
return matches !== null;
|
|
||||||
} catch (error) {
|
|
||||||
this.assert(false, `${description} - error reading file: ${error.message}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
runScript(scriptPath, description) {
|
|
||||||
try {
|
|
||||||
const output = execSync(scriptPath, { encoding: 'utf8', cwd: BASE_DIR });
|
|
||||||
this.log(` SCRIPT: ${description} executed successfully`, 'pass');
|
|
||||||
return { success: true, output };
|
|
||||||
} catch (error) {
|
|
||||||
this.log(` SCRIPT: ${description} failed - ${error.message}`, 'fail');
|
|
||||||
this.results.failed++;
|
|
||||||
this.results.errors.push(`${description}: ${error.message}`);
|
|
||||||
return { success: false, error: error.message };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test suite implementation
|
// Test suite implementation
|
||||||
const testSuite = new SkillTestSuite();
|
const testSuite = new SkillTestSuite();
|
||||||
|
|
||||||
function runAllTests() {
|
function runAllTests() {
|
||||||
testSuite.log('=== .agents Integration Test Suite ===', 'info');
|
testSuite.log('=== .agents Integration Test Suite ===', 'info');
|
||||||
testSuite.log(`Base directory: ${BASE_DIR}`, 'info');
|
testSuite.log(`Base directory: ${BASE_DIR}`, 'info');
|
||||||
testSuite.log(`Started: ${new Date().toISOString()}`, 'info');
|
testSuite.log(`Started: ${new Date().toISOString()}`, 'info');
|
||||||
testSuite.log('');
|
testSuite.log('');
|
||||||
|
|
||||||
// Test 1: Directory Structure
|
// Test 1: Directory Structure
|
||||||
testSuite.log('Test 1: Directory Structure', 'info');
|
testSuite.log('Test 1: Directory Structure', 'info');
|
||||||
testSuite.testDirectoryExists(AGENTS_DIR, '.agents directory');
|
testSuite.testDirectoryExists(AGENTS_DIR, '.agents directory');
|
||||||
testSuite.testDirectoryExists(SKILLS_DIR, 'skills directory');
|
testSuite.testDirectoryExists(SKILLS_DIR, 'skills directory');
|
||||||
testSuite.testDirectoryExists(WORKFLOWS_DIR, 'workflows directory');
|
testSuite.testDirectoryExists(WORKFLOWS_DIR, 'workflows directory');
|
||||||
testSuite.testDirectoryExists(path.join(AGENTS_DIR, 'scripts'), 'scripts directory');
|
testSuite.testDirectoryExists(path.join(AGENTS_DIR, 'scripts'), 'scripts directory');
|
||||||
testSuite.testDirectoryExists(path.join(AGENTS_DIR, 'rules'), 'rules directory');
|
testSuite.testDirectoryExists(path.join(AGENTS_DIR, 'rules'), 'rules directory');
|
||||||
testSuite.log('');
|
testSuite.log('');
|
||||||
|
|
||||||
// Test 2: Core Files
|
// Test 2: Core Files
|
||||||
testSuite.log('Test 2: Core Files', 'info');
|
testSuite.log('Test 2: Core Files', 'info');
|
||||||
testSuite.testFileExists(path.join(AGENTS_DIR, 'README.md'), 'README.md');
|
testSuite.testFileExists(path.join(AGENTS_DIR, 'README.md'), 'README.md');
|
||||||
testSuite.testFileExists(path.join(SKILLS_DIR, 'VERSION'), 'skills VERSION file');
|
testSuite.testFileExists(path.join(SKILLS_DIR, 'VERSION'), 'skills VERSION file');
|
||||||
testSuite.testFileExists(path.join(SKILLS_DIR, 'skills.md'), 'skills.md documentation');
|
testSuite.testFileExists(path.join(SKILLS_DIR, 'skills.md'), 'skills.md documentation');
|
||||||
testSuite.log('');
|
testSuite.log('');
|
||||||
|
|
||||||
// Test 3: Script Files
|
// Test 3: Script Files
|
||||||
testSuite.log('Test 3: Validation Scripts', 'info');
|
testSuite.log('Test 3: Validation Scripts', 'info');
|
||||||
testSuite.testFileExists(path.join(AGENTS_DIR, 'scripts', 'bash', 'validate-versions.sh'), 'bash validate-versions.sh');
|
testSuite.testFileExists(
|
||||||
testSuite.testFileExists(path.join(AGENTS_DIR, 'scripts', 'bash', 'audit-skills.sh'), 'bash audit-skills.sh');
|
path.join(AGENTS_DIR, 'scripts', 'bash', 'validate-versions.sh'),
|
||||||
testSuite.testFileExists(path.join(AGENTS_DIR, 'scripts', 'bash', 'sync-workflows.sh'), 'bash sync-workflows.sh');
|
'bash validate-versions.sh'
|
||||||
testSuite.testFileExists(path.join(AGENTS_DIR, 'scripts', 'powershell', 'validate-versions.ps1'), 'powershell validate-versions.ps1');
|
);
|
||||||
testSuite.testFileExists(path.join(AGENTS_DIR, 'scripts', 'powershell', 'audit-skills.ps1'), 'powershell audit-skills.ps1');
|
testSuite.testFileExists(path.join(AGENTS_DIR, 'scripts', 'bash', 'audit-skills.sh'), 'bash audit-skills.sh');
|
||||||
testSuite.log('');
|
testSuite.testFileExists(path.join(AGENTS_DIR, 'scripts', 'bash', 'sync-workflows.sh'), 'bash sync-workflows.sh');
|
||||||
|
testSuite.testFileExists(
|
||||||
|
path.join(AGENTS_DIR, 'scripts', 'powershell', 'validate-versions.ps1'),
|
||||||
|
'powershell validate-versions.ps1'
|
||||||
|
);
|
||||||
|
testSuite.testFileExists(
|
||||||
|
path.join(AGENTS_DIR, 'scripts', 'powershell', 'audit-skills.ps1'),
|
||||||
|
'powershell audit-skills.ps1'
|
||||||
|
);
|
||||||
|
testSuite.log('');
|
||||||
|
|
||||||
// Test 4: Version Consistency
|
// Test 4: Version Consistency
|
||||||
testSuite.log('Test 4: Version Consistency', 'info');
|
testSuite.log('Test 4: Version Consistency', 'info');
|
||||||
testSuite.testFileContent(path.join(AGENTS_DIR, 'README.md'), /v1\.8\.6/, 'README.md version');
|
testSuite.testFileContent(path.join(AGENTS_DIR, 'README.md'), /v1\.8\.6/, 'README.md version');
|
||||||
testSuite.testFileContent(path.join(SKILLS_DIR, 'VERSION'), /version: 1\.8\.6/, 'skills VERSION file');
|
testSuite.testFileContent(path.join(SKILLS_DIR, 'VERSION'), /version: 1\.8\.6/, 'skills VERSION file');
|
||||||
testSuite.testFileContent(path.join(SKILLS_DIR, 'skills.md'), /v1\.8\.6/, 'skills.md version');
|
testSuite.testFileContent(path.join(SKILLS_DIR, 'skills.md'), /v1\.8\.6/, 'skills.md version');
|
||||||
testSuite.testFileContent(path.join(AGENTS_DIR, 'rules', '00-project-context.md'), /v1\.8\.6/, 'project context version');
|
testSuite.testFileContent(
|
||||||
testSuite.log('');
|
path.join(AGENTS_DIR, 'rules', '00-project-context.md'),
|
||||||
|
/v1\.8\.6/,
|
||||||
|
'project context version'
|
||||||
|
);
|
||||||
|
testSuite.log('');
|
||||||
|
|
||||||
// Test 5: Skills Structure
|
// Test 5: Skills Structure
|
||||||
testSuite.log('Test 5: Skills Structure', 'info');
|
testSuite.log('Test 5: Skills Structure', 'info');
|
||||||
const skillDirs = fs.readdirSync(SKILLS_DIR).filter(item => {
|
const skillDirs = fs.readdirSync(SKILLS_DIR).filter((item) => {
|
||||||
const itemPath = path.join(SKILLS_DIR, item);
|
const itemPath = path.join(SKILLS_DIR, item);
|
||||||
return fs.statSync(itemPath).isDirectory() && item.startsWith('speckit-') || item === 'nestjs-best-practices' || item === 'next-best-practices';
|
return (
|
||||||
});
|
(fs.statSync(itemPath).isDirectory() && item.startsWith('speckit-')) ||
|
||||||
|
item === 'nestjs-best-practices' ||
|
||||||
|
item === 'next-best-practices'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
testSuite.assert(skillDirs.length >= 20, `Found at least 20 skill directories (found ${skillDirs.length})`);
|
testSuite.assert(skillDirs.length >= 20, `Found at least 20 skill directories (found ${skillDirs.length})`);
|
||||||
|
|
||||||
// Test a few key skills
|
// Test a few key skills
|
||||||
const keySkills = ['speckit-plan', 'speckit-implement', 'speckit-specify', 'speckit-validate'];
|
const keySkills = ['speckit-plan', 'speckit-implement', 'speckit-specify', 'speckit-validate'];
|
||||||
keySkills.forEach(skill => {
|
keySkills.forEach((skill) => {
|
||||||
const skillPath = path.join(SKILLS_DIR, skill);
|
const skillPath = path.join(SKILLS_DIR, skill);
|
||||||
const skillMdPath = path.join(skillPath, 'SKILL.md');
|
const skillMdPath = path.join(skillPath, 'SKILL.md');
|
||||||
testSuite.testDirectoryExists(skillPath, `${skill} directory`);
|
testSuite.testDirectoryExists(skillPath, `${skill} directory`);
|
||||||
testSuite.testFileExists(skillMdPath, `${skill} SKILL.md`);
|
testSuite.testFileExists(skillMdPath, `${skill} SKILL.md`);
|
||||||
|
|
||||||
if (fs.existsSync(skillMdPath)) {
|
if (fs.existsSync(skillMdPath)) {
|
||||||
testSuite.testFileContent(skillMdPath, /^name:/, `${skill} has name field`);
|
testSuite.testFileContent(skillMdPath, /^name:/, `${skill} has name field`);
|
||||||
testSuite.testFileContent(skillMdPath, /^description:/, `${skill} has description field`);
|
testSuite.testFileContent(skillMdPath, /^description:/, `${skill} has description field`);
|
||||||
testSuite.testFileContent(skillMdPath, /^version:/, `${skill} has version field`);
|
testSuite.testFileContent(skillMdPath, /^version:/, `${skill} has version field`);
|
||||||
testSuite.testFileContent(skillMdPath, /^## Role$/, `${skill} has Role section`);
|
testSuite.testFileContent(skillMdPath, /^## Role$/, `${skill} has Role section`);
|
||||||
testSuite.testFileContent(skillMdPath, /^## Task$/, `${skill} has Task section`);
|
testSuite.testFileContent(skillMdPath, /^## Task$/, `${skill} has Task section`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
testSuite.log('');
|
testSuite.log('');
|
||||||
|
|
||||||
// Test 6: Workflows Structure
|
// Test 6: Workflows Structure
|
||||||
testSuite.log('Test 6: Workflows Structure', 'info');
|
testSuite.log('Test 6: Workflows Structure', 'info');
|
||||||
const workflowFiles = fs.readdirSync(WORKFLOWS_DIR).filter(item => item.endsWith('.md'));
|
const workflowFiles = fs.readdirSync(WORKFLOWS_DIR).filter((item) => item.endsWith('.md'));
|
||||||
testSuite.assert(workflowFiles.length >= 20, `Found at least 20 workflow files (found ${workflowFiles.length})`);
|
testSuite.assert(workflowFiles.length >= 20, `Found at least 20 workflow files (found ${workflowFiles.length})`);
|
||||||
|
|
||||||
// Test key workflows
|
// Test key workflows
|
||||||
const keyWorkflows = ['00-speckit.all.md', '02-speckit.specify.md', '04-speckit.plan.md', '07-speckit.implement.md'];
|
const keyWorkflows = ['00-speckit.all.md', '02-speckit.specify.md', '04-speckit.plan.md', '07-speckit.implement.md'];
|
||||||
keyWorkflows.forEach(workflow => {
|
keyWorkflows.forEach((workflow) => {
|
||||||
const workflowPath = path.join(WORKFLOWS_DIR, workflow);
|
const workflowPath = path.join(WORKFLOWS_DIR, workflow);
|
||||||
testSuite.testFileExists(workflowPath, `${workflow} file`);
|
testSuite.testFileExists(workflowPath, `${workflow} file`);
|
||||||
});
|
});
|
||||||
testSuite.log('');
|
testSuite.log('');
|
||||||
|
|
||||||
// Test 7: Rules Structure
|
// Test 7: Rules Structure
|
||||||
testSuite.log('Test 7: Rules Structure', 'info');
|
testSuite.log('Test 7: Rules Structure', 'info');
|
||||||
const rulesDir = path.join(AGENTS_DIR, 'rules');
|
const rulesDir = path.join(AGENTS_DIR, 'rules');
|
||||||
const ruleFiles = fs.readdirSync(rulesDir).filter(item => item.endsWith('.md'));
|
const ruleFiles = fs.readdirSync(rulesDir).filter((item) => item.endsWith('.md'));
|
||||||
testSuite.assert(ruleFiles.length >= 10, `Found at least 10 rule files (found ${ruleFiles.length})`);
|
testSuite.assert(ruleFiles.length >= 10, `Found at least 10 rule files (found ${ruleFiles.length})`);
|
||||||
|
|
||||||
// Test key rules
|
// Test key rules
|
||||||
const keyRules = ['00-project-context.md', '01-adr-019-uuid.md', '02-security.md'];
|
const keyRules = ['00-project-context.md', '01-adr-019-uuid.md', '02-security.md'];
|
||||||
keyRules.forEach(rule => {
|
keyRules.forEach((rule) => {
|
||||||
const rulePath = path.join(rulesDir, rule);
|
const rulePath = path.join(rulesDir, rule);
|
||||||
testSuite.testFileExists(rulePath, `${rule} file`);
|
testSuite.testFileExists(rulePath, `${rule} file`);
|
||||||
});
|
});
|
||||||
testSuite.log('');
|
testSuite.log('');
|
||||||
|
|
||||||
// Test 8: Script Execution (if on Unix-like system)
|
// Test 8: Script Execution (if on Unix-like system)
|
||||||
if (process.platform !== 'win32') {
|
if (process.platform !== 'win32') {
|
||||||
testSuite.log('Test 8: Script Execution', 'info');
|
testSuite.log('Test 8: Script Execution', 'info');
|
||||||
|
|
||||||
// Test version validation script
|
// Test version validation script
|
||||||
const versionScript = path.join(AGENTS_DIR, 'scripts', 'bash', 'validate-versions.sh');
|
const versionScript = path.join(AGENTS_DIR, 'scripts', 'bash', 'validate-versions.sh');
|
||||||
if (fs.existsSync(versionScript)) {
|
if (fs.existsSync(versionScript)) {
|
||||||
try {
|
try {
|
||||||
// Make executable
|
// Make executable
|
||||||
fs.chmodSync(versionScript, '755');
|
fs.chmodSync(versionScript, '755');
|
||||||
testSuite.runScript(versionScript, 'Version validation script');
|
testSuite.runScript(versionScript, 'Version validation script');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
testSuite.log(` SKIP: Cannot execute version script - ${error.message}`, 'warn');
|
testSuite.log(` SKIP: Cannot execute version script - ${error.message}`, 'warn');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
testSuite.log('');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test 9: Documentation Quality
|
|
||||||
testSuite.log('Test 9: Documentation Quality', 'info');
|
|
||||||
testSuite.testFileContent(path.join(AGENTS_DIR, 'README.md'), /## Troubleshooting/, 'README.md has troubleshooting section');
|
|
||||||
testSuite.testFileContent(path.join(SKILLS_DIR, 'skills.md'), /## Skill Dependency Matrix/, 'skills.md has dependency matrix');
|
|
||||||
testSuite.testFileContent(path.join(AGENTS_DIR, 'README.md'), /## Architecture/, 'README.md has architecture section');
|
|
||||||
testSuite.log('');
|
testSuite.log('');
|
||||||
|
}
|
||||||
|
|
||||||
// Results Summary
|
// Test 9: Documentation Quality
|
||||||
testSuite.log('=== Test Results Summary ===', 'info');
|
testSuite.log('Test 9: Documentation Quality', 'info');
|
||||||
testSuite.log(`Passed: ${testSuite.results.passed}`, 'pass');
|
testSuite.testFileContent(
|
||||||
testSuite.log(`Failed: ${testSuite.results.failed}`, testSuite.results.failed > 0 ? 'fail' : 'pass');
|
path.join(AGENTS_DIR, 'README.md'),
|
||||||
|
/## Troubleshooting/,
|
||||||
|
'README.md has troubleshooting section'
|
||||||
|
);
|
||||||
|
testSuite.testFileContent(
|
||||||
|
path.join(SKILLS_DIR, 'skills.md'),
|
||||||
|
/## Skill Dependency Matrix/,
|
||||||
|
'skills.md has dependency matrix'
|
||||||
|
);
|
||||||
|
testSuite.testFileContent(
|
||||||
|
path.join(AGENTS_DIR, 'README.md'),
|
||||||
|
/## Architecture/,
|
||||||
|
'README.md has architecture section'
|
||||||
|
);
|
||||||
|
testSuite.log('');
|
||||||
|
|
||||||
if (testSuite.results.errors.length > 0) {
|
// Results Summary
|
||||||
testSuite.log('Errors:', 'fail');
|
testSuite.log('=== Test Results Summary ===', 'info');
|
||||||
testSuite.results.errors.forEach(error => {
|
testSuite.log(`Passed: ${testSuite.results.passed}`, 'pass');
|
||||||
testSuite.log(` - ${error}`, 'fail');
|
testSuite.log(`Failed: ${testSuite.results.failed}`, testSuite.results.failed > 0 ? 'fail' : 'pass');
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
testSuite.log(`Completed: ${new Date().toISOString()}`, 'info');
|
if (testSuite.results.errors.length > 0) {
|
||||||
|
testSuite.log('Errors:', 'fail');
|
||||||
|
testSuite.results.errors.forEach((error) => {
|
||||||
|
testSuite.log(` - ${error}`, 'fail');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return testSuite.results.failed === 0;
|
testSuite.log(`Completed: ${new Date().toISOString()}`, 'info');
|
||||||
|
|
||||||
|
return testSuite.results.failed === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export for use in other modules
|
// Export for use in other modules
|
||||||
@@ -236,6 +265,6 @@ module.exports = { SkillTestSuite, runAllTests };
|
|||||||
|
|
||||||
// Run tests if called directly
|
// Run tests if called directly
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
const success = runAllTests();
|
const success = runAllTests();
|
||||||
process.exit(success ? 0 : 1);
|
process.exit(success ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,216 +13,218 @@ const AGENTS_DIR = path.join(BASE_DIR, '.agents');
|
|||||||
|
|
||||||
// Test utilities
|
// Test utilities
|
||||||
class WorkflowTestSuite {
|
class WorkflowTestSuite {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.results = {
|
this.results = {
|
||||||
passed: 0,
|
passed: 0,
|
||||||
failed: 0,
|
failed: 0,
|
||||||
errors: []
|
errors: [],
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
log(message, type = 'info') {
|
||||||
|
const colors = {
|
||||||
|
info: '\x1b[36m', // Cyan
|
||||||
|
pass: '\x1b[32m', // Green
|
||||||
|
fail: '\x1b[31m', // Red
|
||||||
|
warn: '\x1b[33m', // Yellow
|
||||||
|
reset: '\x1b[0m',
|
||||||
|
};
|
||||||
|
|
||||||
|
const color = colors[type] || colors.info;
|
||||||
|
console.log(`${color}${message}${colors.reset}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(condition, message) {
|
||||||
|
if (condition) {
|
||||||
|
this.log(` PASS: ${message}`, 'pass');
|
||||||
|
this.results.passed++;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
this.log(` FAIL: ${message}`, 'fail');
|
||||||
|
this.results.failed++;
|
||||||
|
this.results.errors.push(message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testWorkflowFile(filePath, expectedName) {
|
||||||
|
if (!fs.existsSync(filePath)) {
|
||||||
|
this.assert(false, `Workflow file exists: ${expectedName}`);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
log(message, type = 'info') {
|
try {
|
||||||
const colors = {
|
const content = fs.readFileSync(filePath, 'utf8');
|
||||||
info: '\x1b[36m', // Cyan
|
|
||||||
pass: '\x1b[32m', // Green
|
|
||||||
fail: '\x1b[31m', // Red
|
|
||||||
warn: '\x1b[33m', // Yellow
|
|
||||||
reset: '\x1b[0m'
|
|
||||||
};
|
|
||||||
|
|
||||||
const color = colors[type] || colors.info;
|
// Basic structure checks
|
||||||
console.log(`${color}${message}${colors.reset}`);
|
this.assert(content.length > 0, `${expectedName} has content`);
|
||||||
|
this.assert(content.includes('#'), `${expectedName} has markdown headers`);
|
||||||
|
|
||||||
|
// Check for workflow-specific patterns
|
||||||
|
if (expectedName.includes('speckit-')) {
|
||||||
|
this.assert(content.includes('speckit-'), `${expectedName} contains speckit reference`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for proper markdown formatting
|
||||||
|
const lines = content.split('\n');
|
||||||
|
const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
|
||||||
|
this.assert(nonEmptyLines.length >= 5, `${expectedName} has sufficient content`);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
this.assert(false, `${expectedName} - error reading file: ${error.message}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validateWorkflowDependency(workflowName, workflowContent) {
|
||||||
|
// Check if workflow references existing skills
|
||||||
|
const skillReferences = workflowContent.match(/@speckit-\w+/g) || [];
|
||||||
|
const skillsDir = path.join(AGENTS_DIR, 'skills');
|
||||||
|
|
||||||
|
for (const skillRef of skillReferences) {
|
||||||
|
const skillName = skillRef.replace('@', '');
|
||||||
|
const skillPath = path.join(skillsDir, skillName);
|
||||||
|
|
||||||
|
if (!fs.existsSync(skillPath)) {
|
||||||
|
this.assert(false, `${workflowName} references non-existent skill: ${skillRef}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(condition, message) {
|
return true;
|
||||||
if (condition) {
|
}
|
||||||
this.log(` PASS: ${message}`, 'pass');
|
|
||||||
this.results.passed++;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
this.log(` FAIL: ${message}`, 'fail');
|
|
||||||
this.results.failed++;
|
|
||||||
this.results.errors.push(message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testWorkflowFile(filePath, expectedName) {
|
|
||||||
if (!fs.existsSync(filePath)) {
|
|
||||||
this.assert(false, `Workflow file exists: ${expectedName}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const content = fs.readFileSync(filePath, 'utf8');
|
|
||||||
|
|
||||||
// Basic structure checks
|
|
||||||
this.assert(content.length > 0, `${expectedName} has content`);
|
|
||||||
this.assert(content.includes('#'), `${expectedName} has markdown headers`);
|
|
||||||
|
|
||||||
// Check for workflow-specific patterns
|
|
||||||
if (expectedName.includes('speckit-')) {
|
|
||||||
this.assert(content.includes('speckit-'), `${expectedName} contains speckit reference`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for proper markdown formatting
|
|
||||||
const lines = content.split('\n');
|
|
||||||
const nonEmptyLines = lines.filter(line => line.trim().length > 0);
|
|
||||||
this.assert(nonEmptyLines.length >= 5, `${expectedName} has sufficient content`);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
this.assert(false, `${expectedName} - error reading file: ${error.message}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
validateWorkflowDependency(workflowName, workflowContent) {
|
|
||||||
// Check if workflow references existing skills
|
|
||||||
const skillReferences = workflowContent.match(/@speckit-\w+/g) || [];
|
|
||||||
const skillsDir = path.join(AGENTS_DIR, 'skills');
|
|
||||||
|
|
||||||
for (const skillRef of skillReferences) {
|
|
||||||
const skillName = skillRef.replace('@', '');
|
|
||||||
const skillPath = path.join(skillsDir, skillName);
|
|
||||||
|
|
||||||
if (!fs.existsSync(skillPath)) {
|
|
||||||
this.assert(false, `${workflowName} references non-existent skill: ${skillRef}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expected workflows mapping
|
// Expected workflows mapping
|
||||||
const expectedWorkflows = {
|
const expectedWorkflows = {
|
||||||
'00-speckit.all.md': 'Full pipeline workflow',
|
'00-speckit.all.md': 'Full pipeline workflow',
|
||||||
'01-speckit.constitution.md': 'Constitution workflow',
|
'01-speckit.constitution.md': 'Constitution workflow',
|
||||||
'02-speckit.specify.md': 'Specification workflow',
|
'02-speckit.specify.md': 'Specification workflow',
|
||||||
'03-speckit.clarify.md': 'Clarification workflow',
|
'03-speckit.clarify.md': 'Clarification workflow',
|
||||||
'04-speckit.plan.md': 'Planning workflow',
|
'04-speckit.plan.md': 'Planning workflow',
|
||||||
'05-speckit.tasks.md': 'Task breakdown workflow',
|
'05-speckit.tasks.md': 'Task breakdown workflow',
|
||||||
'06-speckit.analyze.md': 'Analysis workflow',
|
'06-speckit.analyze.md': 'Analysis workflow',
|
||||||
'07-speckit.implement.md': 'Implementation workflow',
|
'07-speckit.implement.md': 'Implementation workflow',
|
||||||
'08-speckit.checker.md': 'Static analysis workflow',
|
'08-speckit.checker.md': 'Static analysis workflow',
|
||||||
'09-speckit.tester.md': 'Testing workflow',
|
'09-speckit.tester.md': 'Testing workflow',
|
||||||
'10-speckit.reviewer.md': 'Code review workflow',
|
'10-speckit.reviewer.md': 'Code review workflow',
|
||||||
'11-speckit.validate.md': 'Validation workflow',
|
'11-speckit.validate.md': 'Validation workflow',
|
||||||
'speckit.prepare.md': 'Preparation workflow',
|
'speckit.prepare.md': 'Preparation workflow',
|
||||||
'schema-change.md': 'Schema change workflow',
|
'schema-change.md': 'Schema change workflow',
|
||||||
'create-backend-module.md': 'Backend module creation',
|
'create-backend-module.md': 'Backend module creation',
|
||||||
'create-frontend-page.md': 'Frontend page creation',
|
'create-frontend-page.md': 'Frontend page creation',
|
||||||
'deploy.md': 'Deployment workflow',
|
'deploy.md': 'Deployment workflow',
|
||||||
'review.md': 'Code review workflow',
|
'review.md': 'Code review workflow',
|
||||||
'util-speckit.checklist.md': 'Checklist utility',
|
'util-speckit.checklist.md': 'Checklist utility',
|
||||||
'util-speckit.diff.md': 'Diff utility',
|
'util-speckit.diff.md': 'Diff utility',
|
||||||
'util-speckit.migrate.md': 'Migration utility',
|
'util-speckit.migrate.md': 'Migration utility',
|
||||||
'util-speckit.quizme.md': 'Quiz utility',
|
'util-speckit.quizme.md': 'Quiz utility',
|
||||||
'util-speckit.status.md': 'Status utility',
|
'util-speckit.status.md': 'Status utility',
|
||||||
'util-speckit.taskstoissues.md': 'Task to issues utility'
|
'util-speckit.taskstoissues.md': 'Task to issues utility',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test suite implementation
|
// Test suite implementation
|
||||||
const workflowTestSuite = new WorkflowTestSuite();
|
const workflowTestSuite = new WorkflowTestSuite();
|
||||||
|
|
||||||
function runWorkflowTests() {
|
function runWorkflowTests() {
|
||||||
workflowTestSuite.log('=== Workflow Validation Test Suite ===', 'info');
|
workflowTestSuite.log('=== Workflow Validation Test Suite ===', 'info');
|
||||||
workflowTestSuite.log(`Workflows directory: ${WORKFLOWS_DIR}`, 'info');
|
workflowTestSuite.log(`Workflows directory: ${WORKFLOWS_DIR}`, 'info');
|
||||||
workflowTestSuite.log(`Started: ${new Date().toISOString()}`, 'info');
|
workflowTestSuite.log(`Started: ${new Date().toISOString()}`, 'info');
|
||||||
workflowTestSuite.log('');
|
workflowTestSuite.log('');
|
||||||
|
|
||||||
// Test 1: Workflows directory exists
|
// Test 1: Workflows directory exists
|
||||||
workflowTestSuite.log('Test 1: Directory Structure', 'info');
|
workflowTestSuite.log('Test 1: Directory Structure', 'info');
|
||||||
workflowTestSuite.assert(fs.existsSync(WORKFLOWS_DIR), 'Workflows directory exists');
|
workflowTestSuite.assert(fs.existsSync(WORKFLOWS_DIR), 'Workflows directory exists');
|
||||||
workflowTestSuite.log('');
|
workflowTestSuite.log('');
|
||||||
|
|
||||||
// Test 2: Expected workflow files exist
|
// Test 2: Expected workflow files exist
|
||||||
workflowTestSuite.log('Test 2: Expected Workflow Files', 'info');
|
workflowTestSuite.log('Test 2: Expected Workflow Files', 'info');
|
||||||
let foundWorkflows = 0;
|
let foundWorkflows = 0;
|
||||||
|
|
||||||
for (const [filename, description] of Object.entries(expectedWorkflows)) {
|
for (const [filename, description] of Object.entries(expectedWorkflows)) {
|
||||||
const filePath = path.join(WORKFLOWS_DIR, filename);
|
const filePath = path.join(WORKFLOWS_DIR, filename);
|
||||||
workflowTestSuite.testWorkflowFile(filePath, description);
|
workflowTestSuite.testWorkflowFile(filePath, description);
|
||||||
if (fs.existsSync(filePath)) {
|
if (fs.existsSync(filePath)) {
|
||||||
foundWorkflows++;
|
foundWorkflows++;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
workflowTestSuite.assert(foundWorkflows >= 20, `Found at least 20 workflows (found ${foundWorkflows})`);
|
workflowTestSuite.assert(foundWorkflows >= 20, `Found at least 20 workflows (found ${foundWorkflows})`);
|
||||||
workflowTestSuite.log('');
|
workflowTestSuite.log('');
|
||||||
|
|
||||||
// Test 3: Workflow content validation
|
// Test 3: Workflow content validation
|
||||||
workflowTestSuite.log('Test 3: Content Validation', 'info');
|
workflowTestSuite.log('Test 3: Content Validation', 'info');
|
||||||
|
|
||||||
for (const [filename, description] of Object.entries(expectedWorkflows)) {
|
for (const [filename, description] of Object.entries(expectedWorkflows)) {
|
||||||
const filePath = path.join(WORKFLOWS_DIR, filename);
|
const filePath = path.join(WORKFLOWS_DIR, filename);
|
||||||
|
|
||||||
if (fs.existsSync(filePath)) {
|
if (fs.existsSync(filePath)) {
|
||||||
try {
|
try {
|
||||||
const content = fs.readFileSync(filePath, 'utf8');
|
const content = fs.readFileSync(filePath, 'utf8');
|
||||||
|
|
||||||
// Check for proper workflow structure
|
// Check for proper workflow structure
|
||||||
workflowTestSuite.assert(content.includes('#'), `${filename} has markdown headers`);
|
workflowTestSuite.assert(content.includes('#'), `${filename} has markdown headers`);
|
||||||
workflowTestSuite.assert(content.length > 100, `${filename} has substantial content`);
|
workflowTestSuite.assert(content.length > 100, `${filename} has substantial content`);
|
||||||
|
|
||||||
// Validate skill dependencies
|
// Validate skill dependencies
|
||||||
workflowTestSuite.validateWorkflowDependency(filename, content);
|
workflowTestSuite.validateWorkflowDependency(filename, content);
|
||||||
|
} catch (error) {
|
||||||
} catch (error) {
|
workflowTestSuite.assert(false, `${filename} - content validation error: ${error.message}`);
|
||||||
workflowTestSuite.assert(false, `${filename} - content validation error: ${error.message}`);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
workflowTestSuite.log('');
|
}
|
||||||
|
workflowTestSuite.log('');
|
||||||
|
|
||||||
// Test 4: Workflow naming consistency
|
// Test 4: Workflow naming consistency
|
||||||
workflowTestSuite.log('Test 4: Naming Consistency', 'info');
|
workflowTestSuite.log('Test 4: Naming Consistency', 'info');
|
||||||
const actualFiles = fs.readdirSync(WORKFLOWS_DIR).filter(file => file.endsWith('.md'));
|
const actualFiles = fs.readdirSync(WORKFLOWS_DIR).filter((file) => file.endsWith('.md'));
|
||||||
|
|
||||||
for (const actualFile of actualFiles) {
|
for (const actualFile of actualFiles) {
|
||||||
if (!expectedWorkflows[actualFile]) {
|
if (!expectedWorkflows[actualFile]) {
|
||||||
workflowTestSuite.log(` UNEXPECTED: ${actualFile} not in expected list`, 'warn');
|
workflowTestSuite.log(` UNEXPECTED: ${actualFile} not in expected list`, 'warn');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const expectedFile of Object.keys(expectedWorkflows)) {
|
for (const expectedFile of Object.keys(expectedWorkflows)) {
|
||||||
if (!actualFiles.includes(expectedFile)) {
|
if (!actualFiles.includes(expectedFile)) {
|
||||||
workflowTestSuite.assert(false, `Missing expected workflow: ${expectedFile}`);
|
workflowTestSuite.assert(false, `Missing expected workflow: ${expectedFile}`);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
workflowTestSuite.log('');
|
}
|
||||||
|
workflowTestSuite.log('');
|
||||||
|
|
||||||
// Test 5: Cross-reference validation
|
// Test 5: Cross-reference validation
|
||||||
workflowTestSuite.log('Test 5: Cross-Reference Validation', 'info');
|
workflowTestSuite.log('Test 5: Cross-Reference Validation', 'info');
|
||||||
|
|
||||||
// Check if README.md references workflows correctly
|
// Check if README.md references workflows correctly
|
||||||
const readmePath = path.join(AGENTS_DIR, 'README.md');
|
const readmePath = path.join(AGENTS_DIR, 'README.md');
|
||||||
if (fs.existsSync(readmePath)) {
|
if (fs.existsSync(readmePath)) {
|
||||||
const readmeContent = fs.readFileSync(readmePath, 'utf8');
|
const readmeContent = fs.readFileSync(readmePath, 'utf8');
|
||||||
workflowTestSuite.assert(
|
workflowTestSuite.assert(
|
||||||
readmeContent.includes('.windsurf/workflows'),
|
readmeContent.includes('.windsurf/workflows'),
|
||||||
'README.md references correct workflows path'
|
'README.md references correct workflows path'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
workflowTestSuite.log('');
|
workflowTestSuite.log('');
|
||||||
|
|
||||||
// Results Summary
|
// Results Summary
|
||||||
workflowTestSuite.log('=== Workflow Test Results Summary ===', 'info');
|
workflowTestSuite.log('=== Workflow Test Results Summary ===', 'info');
|
||||||
workflowTestSuite.log(`Passed: ${workflowTestSuite.results.passed}`, 'pass');
|
workflowTestSuite.log(`Passed: ${workflowTestSuite.results.passed}`, 'pass');
|
||||||
workflowTestSuite.log(`Failed: ${workflowTestSuite.results.failed}`, workflowTestSuite.results.failed > 0 ? 'fail' : 'pass');
|
workflowTestSuite.log(
|
||||||
|
`Failed: ${workflowTestSuite.results.failed}`,
|
||||||
|
workflowTestSuite.results.failed > 0 ? 'fail' : 'pass'
|
||||||
|
);
|
||||||
|
|
||||||
if (workflowTestSuite.results.errors.length > 0) {
|
if (workflowTestSuite.results.errors.length > 0) {
|
||||||
workflowTestSuite.log('Errors:', 'fail');
|
workflowTestSuite.log('Errors:', 'fail');
|
||||||
workflowTestSuite.results.errors.forEach(error => {
|
workflowTestSuite.results.errors.forEach((error) => {
|
||||||
workflowTestSuite.log(` - ${error}`, 'fail');
|
workflowTestSuite.log(` - ${error}`, 'fail');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
workflowTestSuite.log(`Completed: ${new Date().toISOString()}`, 'info');
|
workflowTestSuite.log(`Completed: ${new Date().toISOString()}`, 'info');
|
||||||
|
|
||||||
return workflowTestSuite.results.failed === 0;
|
return workflowTestSuite.results.failed === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export for use in other modules
|
// Export for use in other modules
|
||||||
@@ -230,6 +232,6 @@ module.exports = { WorkflowTestSuite, runWorkflowTests };
|
|||||||
|
|
||||||
// Run tests if called directly
|
// Run tests if called directly
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
const success = runWorkflowTests();
|
const success = runWorkflowTests();
|
||||||
process.exit(success ? 0 : 1);
|
process.exit(success ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,27 +10,27 @@ This meta-workflow orchestrates the **complete development lifecycle**, from spe
|
|||||||
## Preparation Phase (Steps 1-5)
|
## Preparation Phase (Steps 1-5)
|
||||||
|
|
||||||
1. **Specify** (`/speckit.specify`):
|
1. **Specify** (`/speckit.specify`):
|
||||||
- Use the `view_file` tool to read: `.windsurf/skills/speckit-specify/SKILL.md`
|
- Use the `view_file` tool to read: `.agents/skills/speckit-specify/SKILL.md`
|
||||||
- Execute with user's feature description
|
- Execute with user's feature description
|
||||||
- Creates: `spec.md`
|
- Creates: `spec.md`
|
||||||
|
|
||||||
2. **Clarify** (`/speckit.clarify`):
|
2. **Clarify** (`/speckit.clarify`):
|
||||||
- Use the `view_file` tool to read: `.windsurf/skills/speckit-clarify/SKILL.md`
|
- Use the `view_file` tool to read: `.agents/skills/speckit-clarify/SKILL.md`
|
||||||
- Execute to resolve ambiguities
|
- Execute to resolve ambiguities
|
||||||
- Updates: `spec.md`
|
- Updates: `spec.md`
|
||||||
|
|
||||||
3. **Plan** (`/speckit.plan`):
|
3. **Plan** (`/speckit.plan`):
|
||||||
- Use the `view_file` tool to read: `.windsurf/skills/speckit-plan/SKILL.md`
|
- Use the `view_file` tool to read: `.agents/skills/speckit-plan/SKILL.md`
|
||||||
- Execute to create technical design
|
- Execute to create technical design
|
||||||
- Creates: `plan.md`
|
- Creates: `plan.md`
|
||||||
|
|
||||||
4. **Tasks** (`/speckit.tasks`):
|
4. **Tasks** (`/speckit.tasks`):
|
||||||
- Use the `view_file` tool to read: `.windsurf/skills/speckit-tasks/SKILL.md`
|
- Use the `view_file` tool to read: `.agents/skills/speckit-tasks/SKILL.md`
|
||||||
- Execute to generate task breakdown
|
- Execute to generate task breakdown
|
||||||
- Creates: `tasks.md`
|
- Creates: `tasks.md`
|
||||||
|
|
||||||
5. **Analyze** (`/speckit.analyze`):
|
5. **Analyze** (`/speckit.analyze`):
|
||||||
- Use the `view_file` tool to read: `.windsurf/skills/speckit-analyze/SKILL.md`
|
- Use the `view_file` tool to read: `.agents/skills/speckit-analyze/SKILL.md`
|
||||||
- Execute to validate consistency across spec, plan, and tasks
|
- Execute to validate consistency across spec, plan, and tasks
|
||||||
- Output: Analysis report
|
- Output: Analysis report
|
||||||
- **Gate**: If critical issues found, stop and fix before proceeding
|
- **Gate**: If critical issues found, stop and fix before proceeding
|
||||||
@@ -38,29 +38,29 @@ This meta-workflow orchestrates the **complete development lifecycle**, from spe
|
|||||||
## Implementation Phase (Steps 6-7)
|
## Implementation Phase (Steps 6-7)
|
||||||
|
|
||||||
6. **Implement** (`/speckit.implement`):
|
6. **Implement** (`/speckit.implement`):
|
||||||
- Use the `view_file` tool to read: `.windsurf/skills/speckit-implement/SKILL.md`
|
- Use the `view_file` tool to read: `.agents/skills/speckit-implement/SKILL.md`
|
||||||
- Execute all tasks from `tasks.md` with anti-regression protocols
|
- Execute all tasks from `tasks.md` with anti-regression protocols
|
||||||
- Output: Working implementation
|
- Output: Working implementation
|
||||||
|
|
||||||
7. **Check** (`/speckit.checker`):
|
7. **Check** (`/speckit.checker`):
|
||||||
- Use the `view_file` tool to read: `.windsurf/skills/speckit-checker/SKILL.md`
|
- Use the `view_file` tool to read: `.agents/skills/speckit-checker/SKILL.md`
|
||||||
- Run static analysis (linters, type checkers, security scanners)
|
- Run static analysis (linters, type checkers, security scanners)
|
||||||
- Output: Checker report
|
- Output: Checker report
|
||||||
|
|
||||||
## Verification Phase (Steps 8-10)
|
## Verification Phase (Steps 8-10)
|
||||||
|
|
||||||
8. **Test** (`/speckit.tester`):
|
8. **Test** (`/speckit.tester`):
|
||||||
- Use the `view_file` tool to read: `.windsurf/skills/speckit-tester/SKILL.md`
|
- Use the `view_file` tool to read: `.agents/skills/speckit-tester/SKILL.md`
|
||||||
- Run tests with coverage
|
- Run tests with coverage
|
||||||
- Output: Test + coverage report
|
- Output: Test + coverage report
|
||||||
|
|
||||||
9. **Review** (`/speckit.reviewer`):
|
9. **Review** (`/speckit.reviewer`):
|
||||||
- Use the `view_file` tool to read: `.windsurf/skills/speckit-reviewer/SKILL.md`
|
- Use the `view_file` tool to read: `.agents/skills/speckit-reviewer/SKILL.md`
|
||||||
- Perform code review
|
- Perform code review
|
||||||
- Output: Review report with findings
|
- Output: Review report with findings
|
||||||
|
|
||||||
10. **Validate** (`/speckit.validate`):
|
10. **Validate** (`/speckit.validate`):
|
||||||
- Use the `view_file` tool to read: `.windsurf/skills/speckit-validate/SKILL.md`
|
- Use the `view_file` tool to read: `.agents/skills/speckit-validate/SKILL.md`
|
||||||
- Verify implementation matches spec requirements
|
- Verify implementation matches spec requirements
|
||||||
- Output: Validation report (pass/fail)
|
- Output: Validation report (pass/fail)
|
||||||
|
|
||||||
@@ -9,20 +9,20 @@ This workflow orchestrates the sequential execution of the Speckit preparation p
|
|||||||
|
|
||||||
1. **Step 1: Specify (Skill 02)**
|
1. **Step 1: Specify (Skill 02)**
|
||||||
- Goal: Create or update the `spec.md` based on user input.
|
- Goal: Create or update the `spec.md` based on user input.
|
||||||
- Action: Read and execute `.windsurf/skills/speckit-specify/SKILL.md`.
|
- Action: Read and execute `.agents/skills/speckit-specify/SKILL.md`.
|
||||||
|
|
||||||
2. **Step 2: Clarify (Skill 03)**
|
2. **Step 2: Clarify (Skill 03)**
|
||||||
- Goal: Refine the `spec.md` by identifying and resolving ambiguities.
|
- Goal: Refine the `spec.md` by identifying and resolving ambiguities.
|
||||||
- Action: Read and execute `.windsurf/skills/speckit-clarify/SKILL.md`.
|
- Action: Read and execute `.agents/skills/speckit-clarify/SKILL.md`.
|
||||||
|
|
||||||
3. **Step 3: Plan (Skill 04)**
|
3. **Step 3: Plan (Skill 04)**
|
||||||
- Goal: Generate `plan.md` from the finalized spec.
|
- Goal: Generate `plan.md` from the finalized spec.
|
||||||
- Action: Read and execute `.windsurf/skills/speckit-plan/SKILL.md`.
|
- Action: Read and execute `.agents/skills/speckit-plan/SKILL.md`.
|
||||||
|
|
||||||
4. **Step 4: Tasks (Skill 05)**
|
4. **Step 4: Tasks (Skill 05)**
|
||||||
- Goal: Generate actionable `tasks.md` from the plan.
|
- Goal: Generate actionable `tasks.md` from the plan.
|
||||||
- Action: Read and execute `.windsurf/skills/speckit-tasks/SKILL.md`.
|
- Action: Read and execute `.agents/skills/speckit-tasks/SKILL.md`.
|
||||||
|
|
||||||
5. **Step 5: Analyze (Skill 06)**
|
5. **Step 5: Analyze (Skill 06)**
|
||||||
- Goal: Validate consistency across all design artifacts (spec, plan, tasks).
|
- Goal: Validate consistency across all design artifacts (spec, plan, tasks).
|
||||||
- Action: Read and execute `.windsurf/skills/speckit-analyze/SKILL.md`.
|
- Action: Read and execute `.agents/skills/speckit-analyze/SKILL.md`.
|
||||||
+1
-1
@@ -9,7 +9,7 @@ description: Create or update the project constitution from interactive or provi
|
|||||||
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-constitution/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-constitution/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
+1
-1
@@ -10,7 +10,7 @@ description: Create or update the feature specification from a natural language
|
|||||||
- This is typically the starting point of a new feature.
|
- This is typically the starting point of a new feature.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-specify/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-specify/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
+1
-1
@@ -9,7 +9,7 @@ description: Identify underspecified areas in the current feature spec by asking
|
|||||||
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-clarify/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-clarify/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
@@ -9,7 +9,7 @@ description: Execute the implementation planning workflow using the plan templat
|
|||||||
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-plan/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-plan/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
@@ -9,7 +9,7 @@ description: Generate an actionable, dependency-ordered tasks.md for the feature
|
|||||||
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-tasks/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-tasks/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
+1
-1
@@ -11,7 +11,7 @@ description: Perform a non-destructive cross-artifact consistency and quality an
|
|||||||
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-analyze/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-analyze/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
+1
-1
@@ -9,7 +9,7 @@ description: Execute the implementation plan by processing and executing all tas
|
|||||||
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-implement/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-implement/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
+1
-1
@@ -11,7 +11,7 @@ description: Run static analysis tools and aggregate results.
|
|||||||
- The user may specify paths to check or run on entire project.
|
- The user may specify paths to check or run on entire project.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-checker/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-checker/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
@@ -11,7 +11,7 @@ description: Execute tests, measure coverage, and report results.
|
|||||||
- The user may specify test paths, options, or just run all tests.
|
- The user may specify test paths, options, or just run all tests.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-tester/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-tester/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
+1
-1
@@ -9,7 +9,7 @@ description: Perform code review with actionable feedback and suggestions.
|
|||||||
- The user may specify files to review, "staged" for git staged changes, or "branch" for branch diff.
|
- The user may specify files to review, "staged" for git staged changes, or "branch" for branch diff.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-reviewer/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-reviewer/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
+1
-1
@@ -9,7 +9,7 @@ description: Validate that implementation matches specification requirements.
|
|||||||
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-validate/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-validate/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
+1
-1
@@ -9,7 +9,7 @@ description: Perform a security-focused audit of the codebase against OWASP Top
|
|||||||
- The user may pass a scope hint: `backend`, `frontend`, `both`, or specific module paths (defaults to `both`).
|
- The user may pass a scope hint: `backend`, `frontend`, `both`, or specific module paths (defaults to `both`).
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-security-audit/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-security-audit/SKILL.md`
|
||||||
- Also load `.agents/skills/_LCBP3-CONTEXT.md` for project-specific rules.
|
- Also load `.agents/skills/_LCBP3-CONTEXT.md` for project-specific rules.
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
auto_execution_mode: 0
|
||||||
|
description: Quick bugfix workflow with minimal impact. Focused on surgical fixes without unrelated refactoring.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Workflow: bugfix
|
||||||
|
|
||||||
|
1. **Context Analysis**:
|
||||||
|
- The user has reported a bug. Treat the bug description or logs as the primary input.
|
||||||
|
|
||||||
|
2. **Load Skill**:
|
||||||
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/bugfix/SKILL.md`
|
||||||
|
|
||||||
|
3. **Execute**:
|
||||||
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
|
- Phases: Analysis → Planning → Execution → Finalization
|
||||||
|
|
||||||
|
4. **Safety Check**:
|
||||||
|
- Always ensure Tier 1 rules (Security, UUID v7, DB Schema) are NOT violated.
|
||||||
|
- Refer to `AGENTS.md` for the full list of forbidden patterns.
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
---
|
||||||
|
auto_execution_mode: 0
|
||||||
|
description: Manual real-app verification — ตรวจแอปจริงหลัง build pass เพื่อยืนยันว่าทำงานถูกต้องใน environment จริง (ไม่ใช่แค่ unit test)
|
||||||
|
---
|
||||||
|
|
||||||
|
# Workflow: check-real-app
|
||||||
|
|
||||||
|
ใช้เมื่อ build/lint/test ผ่านแล้ว แต่ต้องการยืนยันว่าแอปจริงทำงานถูกต้อง
|
||||||
|
เน้นการตรวจที่ unit test ตรวจไม่ได้: UI flow, API response จริง, console errors, network requests
|
||||||
|
|
||||||
|
## ขั้นตอน
|
||||||
|
|
||||||
|
### 1. เริ่ม Dev Server (ถ้ายังไม่รัน)
|
||||||
|
|
||||||
|
ตรวจก่อนว่ามี dev server รันอยู่แล้วหรือไม่ เพื่อป้องกันรันซ้ำ:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backend
|
||||||
|
pnpm --filter backend run start:dev
|
||||||
|
|
||||||
|
# Frontend
|
||||||
|
pnpm --filter frontend run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. ตรวจ Endpoint / หน้าที่เปลี่ยน
|
||||||
|
|
||||||
|
- เปิด URL ที่เกี่ยวข้องกับงานที่เพิ่ง implement
|
||||||
|
- ตรวจ API endpoint ด้วย curl หรือ browser dev tools
|
||||||
|
- ดู network tab ว่า request/response ถูกต้อง
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ตัวอย่างตรวจ API จริง
|
||||||
|
curl -X GET http://localhost:3001/api/[endpoint] \
|
||||||
|
-H "Authorization: Bearer <token>" | jq .
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. ตรวจ Console / Log
|
||||||
|
|
||||||
|
- **Frontend**: เปิด browser DevTools → Console tab — ต้องไม่มี error หรือ warning ที่ไม่คาดเดา
|
||||||
|
- **Backend**: ดู terminal log — ตรวจว่าไม่มี unhandled exception หรือ SQL error
|
||||||
|
|
||||||
|
### 4. ตรวจ Happy Path + Edge Case หลัก
|
||||||
|
|
||||||
|
ตรวจ flow ที่เกี่ยวข้องอย่างน้อย:
|
||||||
|
- [ ] Happy path ทำงานถูกต้อง
|
||||||
|
- [ ] Input ผิดรูปแบบ → แสดง error message ที่เหมาะสม
|
||||||
|
- [ ] Unauthorized access → redirect/403 ถูกต้อง
|
||||||
|
- [ ] หน้าที่ไม่ได้แก้ยังทำงานปกติ (regression check)
|
||||||
|
|
||||||
|
### 5. ตรวจ NAP-DMS Specific
|
||||||
|
|
||||||
|
- [ ] UUID ใน URL และ response เป็น string format ถูกต้อง (ไม่ใช่ integer)
|
||||||
|
- [ ] ไม่มี `NaN` หรือ `undefined` ใน form values หรือ API payload
|
||||||
|
- [ ] Thai/English text แสดงผลถูกต้อง (i18n)
|
||||||
|
- [ ] RBAC: role ที่ไม่มีสิทธิ์ไม่เห็น/เข้าถึงไม่ได้
|
||||||
|
|
||||||
|
## 🚫 No Fake Evidence Rule
|
||||||
|
|
||||||
|
> **ห้ามรายงานว่าตรวจแอปจริงแล้ว ถ้าไม่ได้เปิดแอปและตรวจจริง**
|
||||||
|
> ถ้าตรวจไม่ได้ (เช่น ไม่มี DB, ไม่มี token) ให้ระบุเหตุผลชัดเจน
|
||||||
|
|
||||||
|
## ✅ Mandatory Output
|
||||||
|
|
||||||
|
รายงานท้ายงานต้องมีครบ:
|
||||||
|
|
||||||
|
### Commands run
|
||||||
|
```
|
||||||
|
✅ curl GET /api/correspondences → 200 OK, returned 3 records
|
||||||
|
✅ curl POST /api/correspondences → 201 Created, uuid: "019..."
|
||||||
|
❌ ไม่ได้ตรวจ: file upload flow → เหตุผล: ต้องการ ClamAV service ที่ไม่มีใน local
|
||||||
|
```
|
||||||
|
|
||||||
|
### Evidence
|
||||||
|
- URL ที่ตรวจ + HTTP status code
|
||||||
|
- Screenshot หรือ response body (ถ้า sensitive ให้ mask)
|
||||||
|
- Console log ที่พบ (ถ้ามี error ต้องระบุ)
|
||||||
|
|
||||||
|
### Limitations / Risks
|
||||||
|
- flow หรือ endpoint ที่ยังไม่ได้ตรวจ + เหตุผล
|
||||||
|
- ความเสี่ยงที่ควรตรวจใน staging ก่อน deploy
|
||||||
|
|
||||||
|
### Next steps
|
||||||
|
- งานที่ต้องทำต่อ หรือ flag สำหรับ QA
|
||||||
@@ -9,7 +9,7 @@ description: Disciplined diagnosis loop for hard bugs and performance regression
|
|||||||
- The user has provided a bug report or performance regression. Treat this as the primary input for the skill.
|
- The user has provided a bug report or performance regression. Treat this as the primary input for the skill.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/diagnose/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/diagnose/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
auto_execution_mode: 0
|
||||||
|
description: Playwright E2E testing patterns, Page Object Model, configuration, CI/CD integration, artifact management, and flaky test strategies for LCBP3-DMS
|
||||||
|
---
|
||||||
|
|
||||||
|
This workflow invokes the e2e-testing skill to help with Playwright E2E testing patterns for LCBP3-DMS.
|
||||||
|
|
||||||
|
Invoke the e2e-testing skill when:
|
||||||
|
- Creating new E2E tests for frontend features
|
||||||
|
- Debugging flaky Playwright tests
|
||||||
|
- Setting up CI/CD integration for E2E tests
|
||||||
|
- Optimizing test performance and reliability
|
||||||
|
- Implementing Page Object Model (POM) patterns
|
||||||
@@ -9,7 +9,7 @@ description: Grilling session that challenges your plan against the existing dom
|
|||||||
- The user has provided a plan to stress-test. Treat this as the primary input for the skill.
|
- The user has provided a plan to stress-test. Treat this as the primary input for the skill.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/grill-with-docs/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/grill-with-docs/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
---
|
||||||
|
auto_execution_mode: 0
|
||||||
|
description: Resume pending multi-session work — อ่าน context เดิม, หา last checkpoint, สรุปสถานะปัจจุบัน และวางแผนต่อ โดยไม่ทำงานซ้ำ
|
||||||
|
---
|
||||||
|
|
||||||
|
# Workflow: resume-pending-work
|
||||||
|
|
||||||
|
ใช้เมื่อกลับมาทำงานที่ค้างไว้ข้าม session — เช่น งานใหญ่ที่แบ่งเป็น phase, งาน migration, หรืองานที่หยุดกลางคัน
|
||||||
|
|
||||||
|
## ขั้นตอน
|
||||||
|
|
||||||
|
### 1. อ่าน Context เดิม
|
||||||
|
|
||||||
|
ตรวจแหล่งข้อมูลเหล่านี้ตามลำดับ:
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Memory system — ดู system-retrieved memories ที่เกี่ยวข้อง
|
||||||
|
2. specs/200-fullstacks/<feature>/tasks.md — ดู task status ล่าสุด
|
||||||
|
3. git log --oneline -20 — ดู commits ล่าสุด
|
||||||
|
4. progress.txt หรือ PROGRESS.md (ถ้ามี) — ดู notes ที่ทิ้งไว้
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. หา Last Checkpoint
|
||||||
|
|
||||||
|
ระบุให้ชัดว่า:
|
||||||
|
- **ทำไปถึงไหนแล้ว** — phase/task/file ที่ complete แล้ว
|
||||||
|
- **ค้างอยู่ที่ไหน** — step ที่กำลังทำอยู่ตอนหยุด
|
||||||
|
- **ยังไม่ได้ทำอะไร** — tasks ที่เหลือ
|
||||||
|
|
||||||
|
### 3. ตรวจสถานะ Build ปัจจุบัน
|
||||||
|
|
||||||
|
ก่อนทำงานต่อ ต้องรู้ว่า codebase ปัจจุบัน clean หรือไม่:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ตรวจ TypeScript errors
|
||||||
|
pnpm --filter backend run build 2>&1 | tail -20
|
||||||
|
pnpm --filter frontend run build 2>&1 | tail -20
|
||||||
|
|
||||||
|
# ดู uncommitted changes
|
||||||
|
git status --short
|
||||||
|
git diff --stat HEAD
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. สรุปสถานะและวางแผนต่อ
|
||||||
|
|
||||||
|
ก่อนลงมือ ให้สรุปให้ผู้ใช้เห็นก่อน:
|
||||||
|
|
||||||
|
```
|
||||||
|
✅ เสร็จแล้ว:
|
||||||
|
- Phase 1: Entity + Migration (commit abc1234)
|
||||||
|
- Phase 2: Service layer (commit def5678)
|
||||||
|
|
||||||
|
🔄 ค้างอยู่:
|
||||||
|
- Phase 3: Controller — เขียนครึ่งนึง, ยังไม่มี tests
|
||||||
|
|
||||||
|
⏳ ยังไม่ได้ทำ:
|
||||||
|
- Phase 4: Frontend integration
|
||||||
|
- Phase 5: E2E tests
|
||||||
|
|
||||||
|
🚩 Issues ที่พบ:
|
||||||
|
- build error ที่ correspondence.service.ts:142
|
||||||
|
```
|
||||||
|
|
||||||
|
จากนั้นถามผู้ใช้ว่าต้องการ:
|
||||||
|
- ทำงานต่อจาก checkpoint เดิม
|
||||||
|
- Skip ขั้นตอนที่ค้าง (พร้อมระบุ risk)
|
||||||
|
- Re-verify งานที่ทำไปแล้วก่อน
|
||||||
|
|
||||||
|
### 5. ตรวจ NAP-DMS Specific
|
||||||
|
|
||||||
|
ก่อน resume ให้ตรวจ:
|
||||||
|
- [ ] ADR ที่เกี่ยวข้องยังไม่เปลี่ยนแปลง (ดู git log ที่ `specs/06-Decision-Records/`)
|
||||||
|
- [ ] Schema ที่ใช้อยู่ตรงกับ `lcbp3-v1.9.0-schema-02-tables.sql`
|
||||||
|
- [ ] ไม่มี merge conflict หรือ stash ค้าง
|
||||||
|
|
||||||
|
## 🚫 No Fake Resume Rule
|
||||||
|
|
||||||
|
> **ห้ามบอกว่า "ทำต่อจากตรงนี้" โดยไม่ได้อ่าน context เดิมจริง**
|
||||||
|
> ต้องระบุหลักฐานที่ชัดเจนว่า checkpoint อยู่ที่ไหน
|
||||||
|
|
||||||
|
## ✅ Mandatory Output
|
||||||
|
|
||||||
|
### Last checkpoint summary
|
||||||
|
```
|
||||||
|
- เสร็จแล้ว: [phase/commit/task]
|
||||||
|
- ค้างอยู่: [file:line หรือ task ที่หยุด]
|
||||||
|
- ยังไม่ได้ทำ: [tasks ที่เหลือ]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build status
|
||||||
|
```
|
||||||
|
✅ backend build → clean
|
||||||
|
❌ frontend build → 2 errors (ระบุ errors)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Plan ต่อ
|
||||||
|
แผน 3-5 ข้อที่จะทำในส่วนที่เหลือ พร้อม verification method
|
||||||
|
|
||||||
|
### Risks / Blockers
|
||||||
|
สิ่งที่อาจ block งาน หรือต้องระวังก่อนทำต่อ
|
||||||
@@ -40,7 +40,7 @@ The following are **CI-blocking issues** that must be caught in code review. The
|
|||||||
|
|
||||||
- **❌ NO SQL Triggers for business logic** — use NestJS Service methods instead
|
- **❌ NO SQL Triggers for business logic** — use NestJS Service methods instead
|
||||||
- **❌ NO `.env` files in production** — use Docker environment variables
|
- **❌ NO `.env` files in production** — use Docker environment variables
|
||||||
- **❌ NO direct table/column name invention** — verify against `specs/03-Data-and-Storage/lcbp3-v1.8.0-schema-02-tables.sql`
|
- **❌ NO direct table/column name invention** — verify against `specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql`
|
||||||
|
|
||||||
### Security (ADR-016)
|
### Security (ADR-016)
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
description: บันทึก session log และอัปเดต project memory
|
||||||
|
---
|
||||||
|
|
||||||
|
# บันทึก Memory
|
||||||
|
|
||||||
|
ใช้ skill `save-memory` เพื่อบันทึก session log และอัปเดต project memory ตามโครงสร้างใหม่
|
||||||
|
|
||||||
|
```bash
|
||||||
|
skill save-memory
|
||||||
|
```
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
auto_execution_mode: 0
|
||||||
|
description: Comprehensive security review for LCBP3-DMS with OWASP Top 10 checklist, ADR compliance, and automated security testing patterns
|
||||||
|
---
|
||||||
|
|
||||||
|
This workflow invokes the security-review skill to perform comprehensive security review of LCBP3-DMS code changes.
|
||||||
|
|
||||||
|
Invoke the security-review skill when:
|
||||||
|
- Implementing authentication or authorization
|
||||||
|
- Handling user input or file uploads
|
||||||
|
- Creating new API endpoints
|
||||||
|
- Working with secrets or credentials
|
||||||
|
- Integrating AI features (Ollama/Qdrant)
|
||||||
|
- Storing or transmitting sensitive data
|
||||||
|
- Integrating third-party APIs
|
||||||
@@ -9,7 +9,7 @@ description: Test-driven development with red-green-refactor loop. Use when user
|
|||||||
- The user wants to build features or fix bugs using TDD. Treat this as the primary input for the skill.
|
- The user wants to build features or fix bugs using TDD. Treat this as the primary input for the skill.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/tdd/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/tdd/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
@@ -9,7 +9,7 @@ description: Break a plan, spec, or PRD into independently-grabbable issues on t
|
|||||||
- The user wants to convert a plan into issues. Treat this as the primary input for the skill.
|
- The user wants to convert a plan into issues. Treat this as the primary input for the skill.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/to-issues/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/to-issues/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
@@ -9,7 +9,7 @@ description: Turn the current conversation context into a PRD and publish it to
|
|||||||
- The user wants to create a PRD from the current context. Treat this as the primary input for the skill.
|
- The user wants to create a PRD from the current context. Treat this as the primary input for the skill.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/to-prd/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/to-prd/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
@@ -9,7 +9,7 @@ description: Triage issues through a state machine driven by triage roles. Use w
|
|||||||
- The user wants to triage issues. Treat this as the primary input for the skill.
|
- The user wants to triage issues. Treat this as the primary input for the skill.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/triage/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/triage/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
+1
-1
@@ -9,7 +9,7 @@ description: Generate a custom checklist for the current feature based on user r
|
|||||||
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-checklist/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-checklist/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
@@ -9,7 +9,7 @@ description: Compare two versions of a spec or plan to highlight changes.
|
|||||||
- The user has provided an input prompt (optional file paths or version references).
|
- The user has provided an input prompt (optional file paths or version references).
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-diff/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-diff/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
+1
-1
@@ -9,7 +9,7 @@ description: Migrate existing projects into the speckit structure by generating
|
|||||||
- The user has provided an input prompt (path to analyze, feature name).
|
- The user has provided an input prompt (path to analyze, feature name).
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-migrate/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-migrate/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
+1
-1
@@ -11,7 +11,7 @@ description: Challenge the specification with Socratic questioning to identify l
|
|||||||
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
- The user has provided an input prompt. Treat this as the primary input for the skill.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-quizme/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-quizme/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
+1
-1
@@ -11,7 +11,7 @@ description: Display a dashboard showing feature status, completion percentage,
|
|||||||
- The user may optionally specify a feature to focus on.
|
- The user may optionally specify a feature to focus on.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-status/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-status/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
+1
-1
@@ -9,7 +9,7 @@ description: Convert existing tasks into actionable, dependency-ordered issues o
|
|||||||
- The user may pass filters (e.g., phase, priority). Default: convert all pending tasks.
|
- The user may pass filters (e.g., phase, priority). Default: convert all pending tasks.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/speckit-taskstoissues/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-taskstoissues/SKILL.md`
|
||||||
- Also load `.agents/skills/_LCBP3-CONTEXT.md` for project conventions (labels, commit format).
|
- Also load `.agents/skills/_LCBP3-CONTEXT.md` for project conventions (labels, commit format).
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
auto_execution_mode: 0
|
||||||
|
description: A comprehensive verification system for LCBP3-DMS development sessions with build, type check, lint, test, security scan, and diff review phases
|
||||||
|
---
|
||||||
|
|
||||||
|
This workflow invokes the verification-loop skill to perform comprehensive verification of LCBP3-DMS code changes.
|
||||||
|
|
||||||
|
Invoke the verification-loop skill when:
|
||||||
|
|
||||||
|
- After completing a feature or significant code change
|
||||||
|
- Before creating a PR
|
||||||
|
- When you want to ensure quality gates pass
|
||||||
|
- After refactoring
|
||||||
|
- Before deploying to staging/production
|
||||||
|
|
||||||
|
## 🚫 No Fake Evidence Rule
|
||||||
|
|
||||||
|
> **ห้ามรายงานว่า test ผ่าน / build สำเร็จ ถ้าไม่ได้รันจริง**
|
||||||
|
> ถ้ารันไม่ได้ ให้ระบุเหตุผลอย่างชัดเจนแทน
|
||||||
|
|
||||||
|
## ✅ Mandatory Output (ทุก verification ต้องมีครบ)
|
||||||
|
|
||||||
|
รายงานท้ายงานต้องมี 5 หัวข้อนี้เสมอ:
|
||||||
|
|
||||||
|
### 1. Pipeline trace
|
||||||
|
|
||||||
|
ลำดับขั้นตอนที่ทำจริง: Understand → Plan → Execute → Verify → Handoff
|
||||||
|
|
||||||
|
### 2. Commands run
|
||||||
|
|
||||||
|
รายการคำสั่งที่รันจริงพร้อมผลสรุป:
|
||||||
|
|
||||||
|
```
|
||||||
|
✅ pnpm run build → Pass (0 errors)
|
||||||
|
✅ pnpm run lint → Pass (0 warnings)
|
||||||
|
✅ pnpm run test → 42 passed, 0 failed
|
||||||
|
❌ ไม่ได้รัน: e2e tests → เหตุผล: ต้องการ DB จริง, ไม่มีใน CI environment
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Verification / Evidence
|
||||||
|
|
||||||
|
หลักฐานจริง เช่น build output, test result, diff, screenshot, link
|
||||||
|
|
||||||
|
### 4. Limitations / Risks
|
||||||
|
|
||||||
|
สิ่งที่ยังไม่ได้ตรวจ, ความเสี่ยง, ข้อจำกัดของ environment
|
||||||
|
|
||||||
|
### 5. Next steps
|
||||||
|
|
||||||
|
งานที่ต้องทำต่อหลัง verification
|
||||||
@@ -9,7 +9,7 @@ description: Tell the agent to zoom out and give broader context or a higher-lev
|
|||||||
- The user is unfamiliar with a section of code or needs broader context. Treat this as the primary input.
|
- The user is unfamiliar with a section of code or needs broader context. Treat this as the primary input.
|
||||||
|
|
||||||
2. **Load Skill**:
|
2. **Load Skill**:
|
||||||
- Use the `view_file` tool to read the skill file at: `.windsurf/skills/zoom-out/SKILL.md`
|
- Use the `view_file` tool to read the skill file at: `.agents/skills/zoom-out/SKILL.md`
|
||||||
|
|
||||||
3. **Execute**:
|
3. **Execute**:
|
||||||
- Follow the instructions in the `SKILL.md` exactly.
|
- Follow the instructions in the `SKILL.md` exactly.
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
---
|
||||||
|
trigger: always_on
|
||||||
|
---
|
||||||
|
|
||||||
|
# NAP-DMS Project Context & Rules
|
||||||
|
|
||||||
|
- For: Devin Cascade (and compatible: Codex CLI, opencode, Amp, Antigravity, AGENTS.md tools)
|
||||||
|
- Version: 1.9.10 | Last synced from repo: 2026-06-06
|
||||||
|
- Repo: [https://git.np-dms.work/np-dms/lcbp3](https://git.np-dms.work/np-dms/lcbp3)
|
||||||
|
- Skill pack: `.agents/skills/` (v1.9.0, 21 skills) — see [`skills/README.md`](./.agents/skills/README.md) + [`skills/_LCBP3-CONTEXT.md`](./.agents/skills/_LCBP3-CONTEXT.md)
|
||||||
|
|
||||||
|
## 🧠 Role & Persona
|
||||||
|
|
||||||
|
Act as **Senior Full Stack Developer** specialized in NestJS, Next.js, TypeScript, DMS. Focus: Data Integrity, Security, Maintainability, Performance.
|
||||||
|
|
||||||
|
You are a **Document Intelligence Engine** — not a general chatbot. Every response must be **precise**, **spec-compliant**, and **production-ready**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 Thought & Planning Protocol (Powered by Everything-Claude-Code)
|
||||||
|
|
||||||
|
Before writing any code or taking any action in Tier 1 and Tier 2, the AI must demonstrate the following thinking process:
|
||||||
|
|
||||||
|
### 1. Analysis Phase (Explore & Analyze)
|
||||||
|
|
||||||
|
Problem Understanding: Restate what the user wants in clear, unambiguous terms.
|
||||||
|
Context Search: Identify the relevant Spec files or ADRs from the "Key Spec Files" table that must be read before starting.
|
||||||
|
Constraints Identification: Identify key constraints (e.g. Security rules, UUID patterns, or Domain terminology).
|
||||||
|
|
||||||
|
### 2. Planning Phase (Plan)
|
||||||
|
|
||||||
|
Alternative Exploration: Present at least 2 solution approaches (where possible) with pros/cons analysis.
|
||||||
|
Step-by-Step Roadmap: Write a file-by-file plan of changes before executing.
|
||||||
|
Verification Plan: Specify how to verify the work is complete (e.g. "which unit tests to write" or "which file to check the schema in").
|
||||||
|
|
||||||
|
### 3. Execution & Refinement (Execute & Refine)
|
||||||
|
|
||||||
|
Follow the plan step by step, and pause to ask if any uncertainty arises.
|
||||||
|
If significant logic changes are made, summarize what was done for the user after completion.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ DMS Workflow Engine Protocol
|
||||||
|
|
||||||
|
กฎนี้ใช้คุม Logic การไหลของเอกสาร (RFA, Transmittal, Correspondence) เพื่อป้องกัน Race Condition และรักษาความถูกต้องของสถานะ:
|
||||||
|
|
||||||
|
- **State Management:** ตรวจสอบสถานะปัจจุบันจาก DB ก่อนเสมอ เพื่อป้องกันการอนุมัติซ้ำซ้อน (ดู `05-06-code-snippets.md` `[workflow-transition]`)
|
||||||
|
- **Concurrency Control:** การจอนเลขที่เอกสารต้องใช้ **Redis Redlock** หรือ **TypeORM `@VersionColumn`** เท่านั้น (ADR-002)
|
||||||
|
- **Background Jobs:** งานนานหรือการแจ้งเตือนต้องส่งไปทำที่ **BullMQ** ห้ามเขียนแบบ Inline (ADR-008)
|
||||||
|
- **Term Consistency:** ห้ามใช้ "Approval Flow" ให้ใช้ **"Workflow Engine"** และห้ามใช้ "Letter" ให้ใช้ **"Correspondence"** (หมายเหตุ: "จดหมาย" ในคอมเมนต์ภาษาไทย = Correspondence ที่ครอบคลุมทุกประเภท)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛡️ Security & Integrity Audit Protocol
|
||||||
|
|
||||||
|
กฎนี้ให้ AI เป็น Gatekeeper ก่อน Commit โดยเน้น **Tier 1 — CRITICAL**:
|
||||||
|
|
||||||
|
- **UUID Validation:** ตรวจสอบว่าเป็น **UUIDv7** และห้ามใช้ `parseInt()` บน UUID (ADR-019)
|
||||||
|
- **RBAC Check:** API ใหม่ต้องมี **CASL Guard** และตรวจสอบ 4-Level RBAC Matrix (ADR-016)
|
||||||
|
- **Data Isolation:** AI ต้องรันผ่าน **Ollama บน Admin Desktop** เท่านั้น ห้ามเข้าถึง DB/storage โดยตรง (ADR-023)
|
||||||
|
- **Input Sanitization:** ไฟล์อัปโหลดต้องผ่าน **Two-Phase** (Temp → Commit) และสแกนด้วย **ClamAV** (ADR-016)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧭 Rule Enforcement Tiers
|
||||||
|
|
||||||
|
### 🔴 Tier 1 — CRITICAL (CI BLOCKER)
|
||||||
|
|
||||||
|
Build fails หากละเมิด:
|
||||||
|
|
||||||
|
- Security (Auth, RBAC, Validation)
|
||||||
|
- UUID Strategy (ADR-019) — no `parseInt` / `Number` / `+` on UUID
|
||||||
|
- Database correctness — verify schema before writing queries
|
||||||
|
- File upload security (ClamAV + whitelist)
|
||||||
|
- AI validation boundary (ADR-023)
|
||||||
|
- Error handling strategy (ADR-007)
|
||||||
|
- Forbidden patterns: `any`, `console.log`, UUID misuse, `id ?? ''` fallback
|
||||||
|
|
||||||
|
### 🟡 Tier 2 — IMPORTANT (CODE REVIEW)
|
||||||
|
|
||||||
|
Must fix ก่อน merge:
|
||||||
|
|
||||||
|
- Architecture patterns (thin controller, business logic in service)
|
||||||
|
- Test coverage (80%+ business logic, 70%+ backend overall)
|
||||||
|
- Cache invalidation
|
||||||
|
- Naming conventions
|
||||||
|
- **TypeScript Standards:** Missing JSDoc, explicit types, or file headers
|
||||||
|
|
||||||
|
### 🟢 Tier 3 — SPECIALIZED WORK
|
||||||
|
|
||||||
|
Requires domain-specific knowledge:
|
||||||
|
|
||||||
|
- **ADR-021 Integration:** Workflow Engine & Context implementation
|
||||||
|
- **AI Infrastructure:** ADR-023/023A boundary enforcement and pipeline usage
|
||||||
|
- **AI Runtime Layer:** ADR-024 Intent Classification, ADR-025 Tool Layer, ADR-026 Chat UI, ADR-027 Admin Console
|
||||||
|
- **Migration Pipeline:** ADR-028 Staging Queue & post-migration cleanup
|
||||||
|
- **Complex Business Logic:** Multi-step workflows with state management
|
||||||
|
- **Performance Optimization:** Database queries, caching strategies, bulk operations
|
||||||
|
|
||||||
|
### 🔵 Tier 4 — GUIDELINES
|
||||||
|
|
||||||
|
Best practice — follow when possible:
|
||||||
|
|
||||||
|
- Code style / formatting (Prettier handles)
|
||||||
|
- Comment completeness
|
||||||
|
- Minor optimizations
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📐 TypeScript Rules & Coding Standards
|
||||||
|
|
||||||
|
### 📝 Core Standards
|
||||||
|
|
||||||
|
- **Strict Mode** — all strict checks enforced.
|
||||||
|
- **ZERO `any` types** — use proper types or `unknown` + narrowing.
|
||||||
|
- **ZERO `console.log`** — use NestJS `Logger` (backend) or remove (frontend).
|
||||||
|
- **English for Code** — use English for all code identifiers, variables, and logic.
|
||||||
|
- **Thai for Comments** — use Thai for comments, documentation, and JSDoc.
|
||||||
|
- **Explicit Typing** — explicitly define types for all variables, parameters, and return values.
|
||||||
|
- **JSDoc** — use JSDoc for all public classes and methods.
|
||||||
|
|
||||||
|
### 🏗️ File & Function Structure
|
||||||
|
|
||||||
|
- **File Headers** — every file MUST start with `// File: path/filename` on the first line.
|
||||||
|
- **Change Log** — include `// Change Log` at the top of the file to track modifications.
|
||||||
|
- **Single Export** — export **only one main symbol** per file.
|
||||||
|
- **Function Style** — avoid unnecessary blank lines inside functions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚫 Forbidden Actions
|
||||||
|
|
||||||
|
| ❌ Forbidden | ✅ Correct Approach | ⚠️ Why |
|
||||||
|
| ----------------------------------------------- | ------------------------------------------------------- | ---------------------------------------------------- |
|
||||||
|
| SQL Triggers for business logic | NestJS Service methods | Untestable; bypasses audit log |
|
||||||
|
| `.env` files in production | `docker-compose.yml` environment section | Secrets exposed in version control |
|
||||||
|
| TypeORM migration files | Edit schema SQL directly (ADR-009) | Migration drift risk; schema managed via SQL delta |
|
||||||
|
| Inventing table/column names | Verify against `schema-02-tables.sql` | Schema mismatch causes silent runtime errors |
|
||||||
|
| `any` TypeScript type | Proper types / generics | Defeats strict mode; hides runtime type errors |
|
||||||
|
| `console.log` in committed code | NestJS Logger (backend) / remove (frontend) | Log flooding in production; risk of data leakage |
|
||||||
|
| `req: any` in controllers | `RequestWithUser` typed interface | Type safety lost; auth context unreachable |
|
||||||
|
| `parseInt()` on UUID values | Use UUID string directly (ADR-019) | `"0195…"` parsed to integer `19` — silently wrong |
|
||||||
|
| Exposing INT PK in API responses | UUIDv7 `publicId` (ADR-019) | Leaks row count; enables DB enumeration attacks |
|
||||||
|
| AI accessing DB/storage directly | AI → DMS API → DB (ADR-023/023A) | Bypasses RBAC, audit trail, and validation layer |
|
||||||
|
| Direct file operations bypassing StorageService | `StorageService` for all file moves | Orphaned files; broken ClamAV scan; no audit trail |
|
||||||
|
| Inline email/notification sending | BullMQ queue job (ADR-008) | Blocks request thread; no retry on transient failure |
|
||||||
|
| Deploying without Release Gates | Complete `04-08-release-management-policy.md` | Unverified deploy risks data loss in production |
|
||||||
|
| AI direct cloud API calls | On-premises Ollama only (ADR-023/023A) | Data privacy violation; no audit control |
|
||||||
|
| AI outputs without human validation | Human-in-the-loop validation required (ADR-023/023A) | Unvalidated AI metadata corrupts document records |
|
||||||
|
| n8n calling Ollama/Qdrant directly | n8n → DMS API → BullMQ → Ollama (ADR-023A) | Bypasses audit log, RBAC, and error handling layer |
|
||||||
|
| Qdrant query without `projectPublicId` filter | `QdrantService.search(projectPublicId, ...)` (ADR-023A) | Cross-project data leak via vector search |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚧 Out of Scope — Never Do Without Explicit Approval
|
||||||
|
|
||||||
|
| ❌ Never Do Autonomously | ⚠️ Why Approval Is Required |
|
||||||
|
| --------------------------------------------------------------- | ---------------------------------------------------------------- |
|
||||||
|
| `DROP` or `RENAME` a column / table | Irreversible data loss — requires DBA + PM sign-off |
|
||||||
|
| Push directly to `main` / `master` branch | Bypasses CI, code review, and release gates |
|
||||||
|
| Generate or insert seed data into production database | May corrupt live data or violate business state invariants |
|
||||||
|
| Delete files from permanent storage | Files may be referenced in active documents or audit trails |
|
||||||
|
| Modify RBAC permission matrix without security team approval | Defines access control for all users — security boundary change |
|
||||||
|
| Upgrade major library versions (NestJS, Next.js, TypeORM, etc.) | Breaking changes require full regression test cycle |
|
||||||
|
| Disable or modify authentication / authorization guards | Creates unguarded endpoints — immediate security risk |
|
||||||
|
| Change Redis lock TTL or disable Redlock | Risk of document number race condition (ADR-002) |
|
||||||
|
| Create or supersede an ADR unilaterally | Architecture decisions require team consensus and review process |
|
||||||
|
| Add new columns to production tables without schema review | Must update Data Dictionary + downstream queries simultaneously |
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user