Compare commits
111 Commits
1139e54086
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 |
@@ -1,7 +1,7 @@
|
||||
# NAP-DMS Project Context & Rules
|
||||
|
||||
- For: Windsurf Cascade (and compatible: Codex CLI, opencode, Amp, Antigravity, AGENTS.md tools)
|
||||
- Version: 1.9.6 | Last synced from repo: 2026-05-22
|
||||
- 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)
|
||||
|
||||
@@ -126,25 +126,25 @@ Best practice — follow when possible:
|
||||
|
||||
## 🚫 Forbidden Actions
|
||||
|
||||
| ❌ Forbidden | ✅ Correct Approach |
|
||||
| ----------------------------------------------- | ------------------------------------------------------- |
|
||||
| SQL Triggers for business logic | NestJS Service methods |
|
||||
| `.env` files in production | `docker-compose.yml` environment section |
|
||||
| TypeORM migration files | Edit schema SQL directly (ADR-009) |
|
||||
| Inventing table/column names | Verify against `lcbp3-v1.9.0-schema-02-tables.sql` |
|
||||
| `any` TypeScript type | Proper types / generics |
|
||||
| `console.log` in committed code | NestJS Logger (backend) / remove (frontend) |
|
||||
| `req: any` in controllers | `RequestWithUser` typed interface |
|
||||
| `parseInt()` on UUID values | Use UUID string directly (ADR-019) |
|
||||
| Exposing INT PK in API responses | UUIDv7 (ADR-019) |
|
||||
| AI accessing DB/storage directly | AI → DMS API → DB (ADR-023) |
|
||||
| Direct file operations bypassing StorageService | `StorageService` for all file moves |
|
||||
| Inline email/notification sending | BullMQ queue job |
|
||||
| Deploying without Release Gates | Complete `04-08-release-management-policy.md` |
|
||||
| AI direct cloud API calls | On-premises Ollama only (ADR-023) |
|
||||
| AI outputs without human validation | Human-in-the-loop validation required (ADR-023) |
|
||||
| n8n calling Ollama/Qdrant directly | n8n → DMS API → BullMQ → Ollama/Qdrant (ADR-023A) |
|
||||
| Qdrant query without `projectPublicId` filter | `QdrantService.search(projectPublicId, ...)` (ADR-023A) |
|
||||
| ❌ 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 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
|
||||
## DMS Glossary
|
||||
|
||||
| ✅ Use | ❌ Don't Use |
|
||||
| ------------------ | ------------------------------------- |
|
||||
| Correspondence | Letter, Communication, Document |
|
||||
| RFA | Approval Request, Submit for Approval |
|
||||
| Transmittal | Delivery Note, Cover Letter |
|
||||
| Circulation | Distribution, Routing |
|
||||
| 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) |
|
||||
| ✅ 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
|
||||
|
||||
|
||||
@@ -2,25 +2,25 @@
|
||||
|
||||
## ❌ Never Do This
|
||||
|
||||
| ❌ Forbidden | ✅ Correct Approach |
|
||||
| ----------------------------------------------- | ----------------------------------------------------------------- |
|
||||
| SQL Triggers for business logic | NestJS Service methods |
|
||||
| `.env` files in production | `docker-compose.yml` environment section |
|
||||
| TypeORM migration files | Edit schema SQL directly (ADR-009) |
|
||||
| Inventing table/column names | Verify against `lcbp3-v1.9.0-schema-02-tables.sql` |
|
||||
| `any` TypeScript type | Proper types / generics |
|
||||
| `console.log` in committed code | NestJS Logger (backend) / remove (frontend) |
|
||||
| `req: any` in controllers | `RequestWithUser` typed interface |
|
||||
| `parseInt()` on UUID values | Use UUID string directly (ADR-019) |
|
||||
| Exposing INT PK in API responses | UUIDv7 (ADR-019) |
|
||||
| AI accessing DB/storage directly | AI → DMS API → DB (ADR-023) |
|
||||
| Direct file operations bypassing StorageService | `StorageService` for all file moves |
|
||||
| Inline email/notification sending | BullMQ queue job |
|
||||
| Deploying without Release Gates | Complete `04-08-release-management-policy.md` |
|
||||
| AI direct cloud API calls | On-premises Ollama only (ADR-023) |
|
||||
| AI outputs without human validation | Human-in-the-loop validation required (ADR-023) |
|
||||
| n8n calling Ollama/Qdrant directly | n8n → DMS API → BullMQ → Ollama/Qdrant (ADR-023A) |
|
||||
| Qdrant query without projectPublicId filter | QdrantService.search(projectPublicId: string) required (ADR-023A) |
|
||||
| ❌ 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)
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
# Frontend Patterns (Next.js)
|
||||
|
||||
## Form Handling
|
||||
@@ -17,7 +16,7 @@ interface ProjectOption {
|
||||
}
|
||||
|
||||
// Select options
|
||||
const options = contracts.map(c => ({
|
||||
const options = contracts.map((c) => ({
|
||||
label: `${c.contractName} (${c.contractCode})`,
|
||||
value: c.publicId!, // Use publicId, no fallback to id
|
||||
}));
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
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-018, ADR-019)
|
||||
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
|
||||
@@ -80,30 +80,131 @@
|
||||
|
||||
## 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.md` | RHF+Zod + TanStack Query + Thai comments |
|
||||
| "เพิ่ม field ใหม่" | `ADR-009`, `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" | `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 |
|
||||
| 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 ที่ชัดเจนและไม่ซ้ำกัน** — เพื่อป้องกันความสับสน
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
# Commit Checklist
|
||||
|
||||
## Pre-Commit Verification
|
||||
@@ -28,6 +27,7 @@ type(scope): description
|
||||
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`
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
# ADR-007 Error Handling Strategy
|
||||
|
||||
## CRITICAL RULES
|
||||
@@ -11,23 +10,18 @@
|
||||
|
||||
## 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 |
|
||||
| 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
|
||||
) {
|
||||
constructor(message: string, userMessage: string, recoveryAction?: string, errorCode?: string) {
|
||||
super({ message, userMessage, recoveryAction, errorCode }, 400);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## CRITICAL RULES
|
||||
|
||||
- **ALWAYS** follow ADR-023 AI boundary policy (isolation on Admin Desktop)
|
||||
- **ALWAYS** use ADR-023A 2-model stack (gemma4:e4b Q8_0 + nomic-embed-text)
|
||||
- **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
|
||||
@@ -26,8 +26,8 @@ n8n (Migration) → DMS API → BullMQ → Admin Desktop (Ollama) → Backend Va
|
||||
| ----------------- | ------------------------- | ------------------------------------------------------------------------ |
|
||||
| **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) | gemma4:e4b Q8_0 (LLM) + nomic-embed-text (Embedding) |
|
||||
| **OCR Engine** | Admin Desktop (Desk-5439) | PaddleOCR + PyThaiNLP (Thai/English text extraction) |
|
||||
| **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)
|
||||
@@ -76,7 +76,7 @@ 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 (gemma4:e4b Q8_0)
|
||||
// 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
|
||||
@@ -113,12 +113,12 @@ const DocumentReviewForm = ({ document, aiSuggestions }) => {
|
||||
- **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-023A Specific Rules
|
||||
## ADR-034 Model Stack (supersedes ADR-023A §2.1)
|
||||
|
||||
- **2-Model Stack:** gemma4:e4b Q8_0 (~4.0GB) + nomic-embed-text (~0.3GB) = ~4.3GB VRAM peak
|
||||
- **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 PaddleOCR
|
||||
- **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
|
||||
|
||||
@@ -129,7 +129,7 @@ const DocumentReviewForm = ({ document, aiSuggestions }) => {
|
||||
- [ ] BullMQ 2-queue setup (ai-realtime + ai-batch)
|
||||
- [ ] QdrantService with projectPublicId enforcement
|
||||
- [ ] DocumentReviewForm reusable component
|
||||
- [ ] Admin Desktop Ollama (gemma4:e4b Q8_0 + nomic-embed-text) + PaddleOCR setup
|
||||
- [ ] 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
|
||||
@@ -138,3 +138,8 @@ const DocumentReviewForm = ({ document, aiSuggestions }) => {
|
||||
|
||||
- `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)
|
||||
|
||||
@@ -42,7 +42,7 @@ init_agent_registry() {
|
||||
[qwen]="Qwen Code"
|
||||
[opencode]="opencode"
|
||||
[codex]="Codex CLI"
|
||||
[windsurf]="Windsurf"
|
||||
[devin]="Devin"
|
||||
[kilocode]="Kilo Code"
|
||||
[auggie]="Auggie CLI"
|
||||
[roo]="Roo Code"
|
||||
|
||||
@@ -99,14 +99,13 @@ find_feature_dir_by_prefix() {
|
||||
|
||||
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=()
|
||||
if [[ -d "$specs_dir" ]]; then
|
||||
for dir in "$specs_dir"/"$prefix"-*; do
|
||||
if [[ -d "$dir" ]]; then
|
||||
matches+=("$(basename "$dir")")
|
||||
fi
|
||||
done
|
||||
# ค้นหาโฟลเดอร์ที่ตรงกับ prefix ในระบบย่อย
|
||||
while IFS= read -r -d '' dir; do
|
||||
matches+=("$dir")
|
||||
done < <(find "$specs_dir" -maxdepth 3 -type d -name "${prefix}-*" -print0 2>/dev/null)
|
||||
fi
|
||||
|
||||
# Handle results
|
||||
@@ -115,12 +114,12 @@ find_feature_dir_by_prefix() {
|
||||
echo "$specs_dir/$branch_name"
|
||||
elif [[ ${#matches[@]} -eq 1 ]]; then
|
||||
# Exactly one match - perfect!
|
||||
echo "$specs_dir/${matches[0]}"
|
||||
echo "${matches[0]}"
|
||||
else
|
||||
# Multiple matches - this shouldn't happen with proper naming convention
|
||||
echo "ERROR: Multiple spec directories found with prefix '$prefix': ${matches[*]}" >&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
|
||||
}
|
||||
|
||||
|
||||
@@ -30,12 +30,12 @@
|
||||
#
|
||||
# 5. Multi-Agent Support
|
||||
# - 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
|
||||
# - Creates default Claude file if no agent files exist
|
||||
#
|
||||
# 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
|
||||
|
||||
set -e
|
||||
@@ -609,8 +609,8 @@ update_specific_agent() {
|
||||
codex)
|
||||
update_agent_file "$AGENTS_FILE" "Codex CLI"
|
||||
;;
|
||||
windsurf)
|
||||
update_agent_file "$WINDSURF_FILE" "Windsurf"
|
||||
devin)
|
||||
update_agent_file "$DEVIN_FILE" "Devin"
|
||||
;;
|
||||
kilocode)
|
||||
update_agent_file "$KILOCODE_FILE" "Kilo Code"
|
||||
@@ -681,8 +681,8 @@ update_all_existing_agents() {
|
||||
found_agent=true
|
||||
fi
|
||||
|
||||
if [[ -f "$WINDSURF_FILE" ]]; then
|
||||
update_agent_file "$WINDSURF_FILE" "Windsurf"
|
||||
if [[ -f "$DEVIN_FILE" ]]; then
|
||||
update_agent_file "$DEVIN_FILE" "Devin"
|
||||
found_agent=true
|
||||
fi
|
||||
|
||||
|
||||
+15
-16
@@ -1,8 +1,8 @@
|
||||
# `.agents/skills/` — LCBP3 Agent Skill Pack
|
||||
|
||||
**Version:** 1.9.0 | **Last Updated:** 2026-05-17 | **Total Skills:** 23
|
||||
**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,6 +14,7 @@ Agent skills for AI-assisted development in **Windsurf IDE** (and compatible age
|
||||
├── skills.md # Overview + dependency matrix + health monitoring
|
||||
├── _LCBP3-CONTEXT.md # Shared LCBP3 context injected into every speckit-* skill
|
||||
├── README.md # (this file)
|
||||
├── save-memory/ # Session log & project memory update
|
||||
├── nestjs-best-practices/ # Backend rules (40 rules across 10 categories)
|
||||
├── next-best-practices/ # Frontend rules (Next.js 15+)
|
||||
├── e2e-testing/ # Playwright E2E testing patterns (POM, flaky tests, CI/CD)
|
||||
@@ -30,12 +31,10 @@ Each skill directory contains:
|
||||
|
||||
---
|
||||
|
||||
## 🚀 How Windsurf Invokes These Skills
|
||||
## 🚀 How Devin Invokes These Skills
|
||||
|
||||
Windsurf exposes two entry points:
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Both paths end up executing the same `SKILL.md` instructions.
|
||||
|
||||
@@ -65,14 +64,14 @@ Use `/00-speckit.all` to run specify → clarify → plan → tasks → analyze
|
||||
|
||||
From repo root:
|
||||
|
||||
| Script | Purpose |
|
||||
| --------------------------------------------------------- | ----------------------------------------------------------- |
|
||||
| `./.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/update-agent-context.sh windsurf` | Append tech entries to `AGENTS.md` |
|
||||
| `./.agents/scripts/bash/audit-skills.sh` | Validate all `SKILL.md` frontmatter + presence |
|
||||
| `./.agents/scripts/bash/validate-versions.sh` | Version consistency check |
|
||||
| `./.agents/scripts/bash/sync-workflows.sh` | Verify every skill has a `.windsurf/workflows/*.md` wrapper |
|
||||
| Script | Purpose |
|
||||
| ------------------------------------------------------ | ---------------------------------------------------------- |
|
||||
| `./.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/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/validate-versions.sh` | Version consistency check |
|
||||
| `./.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.
|
||||
|
||||
@@ -97,7 +96,7 @@ To add a new skill:
|
||||
|
||||
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`.
|
||||
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.
|
||||
5. Run `./.agents/scripts/bash/audit-skills.sh` → must pass.
|
||||
|
||||
|
||||
@@ -6454,7 +6454,7 @@ CREATE TABLE ai_audit_log (
|
||||
user_id INT NOT NULL,
|
||||
action VARCHAR(64) NOT NULL, -- 'ai.extract_metadata', 'ai.classify', etc.
|
||||
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),
|
||||
input_hash CHAR(64), -- SHA-256 of input for replay detection
|
||||
output_summary JSON,
|
||||
|
||||
@@ -137,7 +137,7 @@ CREATE TABLE ai_audit_log (
|
||||
user_id INT NOT NULL,
|
||||
action VARCHAR(64) NOT NULL, -- 'ai.extract_metadata', 'ai.classify', etc.
|
||||
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),
|
||||
input_hash CHAR(64), -- SHA-256 of input for replay detection
|
||||
output_summary JSON,
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
ไฟล์นี้กำหนดทักษะและความสามารถเฉพาะทางของ Document Intelligence Engine สำหรับโครงการ LCBP3 v1.9.0 เพื่อรักษามาตรฐานสูงสุดด้าน Security และ Data Integrity
|
||||
|
||||
**Status**: Production Ready | **Last Updated**: 2026-05-17 | **Total Skills**: 23
|
||||
**Status**: Production Ready | **Last Updated**: 2026-06-07 | **Total Skills**: 24
|
||||
|
||||
> 📌 Shared context for all speckit-\* skills: see [`_LCBP3-CONTEXT.md`](./_LCBP3-CONTEXT.md).
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
| **speckit-status** | None | None | Progress tracking |
|
||||
| **speckit-taskstoissues** | speckit-tasks | None | Issue sync |
|
||||
| **speckit-checklist** | speckit-plan | None | Requirements validation |
|
||||
| **save-memory** | None | None | Session log & memory update |
|
||||
| **nestjs-best-practices** | None | speckit-implement | Backend patterns |
|
||||
| **next-best-practices** | None | speckit-implement | Frontend patterns |
|
||||
| **speckit-security-audit** | None | speckit-reviewer | Security validation |
|
||||
@@ -99,7 +100,7 @@
|
||||
|
||||
### Health Metrics
|
||||
|
||||
- **Total Skills**: 23 implemented
|
||||
- **Total Skills**: 24 implemented
|
||||
- **Version Alignment**: v1.9.0 across all skills
|
||||
- **Template Coverage**: 100% for skills requiring templates
|
||||
- **Documentation**: Complete front matter + shared `_LCBP3-CONTEXT.md` appendix
|
||||
|
||||
@@ -15,220 +15,249 @@ const WORKFLOWS_DIR = path.join(BASE_DIR, '.windsurf', 'workflows');
|
||||
|
||||
// Test utilities
|
||||
class SkillTestSuite {
|
||||
constructor() {
|
||||
this.results = {
|
||||
passed: 0,
|
||||
failed: 0,
|
||||
errors: []
|
||||
};
|
||||
constructor() {
|
||||
this.results = {
|
||||
passed: 0,
|
||||
failed: 0,
|
||||
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') {
|
||||
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}`);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
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
|
||||
const testSuite = new SkillTestSuite();
|
||||
|
||||
function runAllTests() {
|
||||
testSuite.log('=== .agents Integration Test Suite ===', 'info');
|
||||
testSuite.log(`Base directory: ${BASE_DIR}`, 'info');
|
||||
testSuite.log(`Started: ${new Date().toISOString()}`, 'info');
|
||||
testSuite.log('');
|
||||
testSuite.log('=== .agents Integration Test Suite ===', 'info');
|
||||
testSuite.log(`Base directory: ${BASE_DIR}`, 'info');
|
||||
testSuite.log(`Started: ${new Date().toISOString()}`, 'info');
|
||||
testSuite.log('');
|
||||
|
||||
// Test 1: Directory Structure
|
||||
testSuite.log('Test 1: Directory Structure', 'info');
|
||||
testSuite.testDirectoryExists(AGENTS_DIR, '.agents directory');
|
||||
testSuite.testDirectoryExists(SKILLS_DIR, 'skills directory');
|
||||
testSuite.testDirectoryExists(WORKFLOWS_DIR, 'workflows directory');
|
||||
testSuite.testDirectoryExists(path.join(AGENTS_DIR, 'scripts'), 'scripts directory');
|
||||
testSuite.testDirectoryExists(path.join(AGENTS_DIR, 'rules'), 'rules directory');
|
||||
testSuite.log('');
|
||||
// Test 1: Directory Structure
|
||||
testSuite.log('Test 1: Directory Structure', 'info');
|
||||
testSuite.testDirectoryExists(AGENTS_DIR, '.agents directory');
|
||||
testSuite.testDirectoryExists(SKILLS_DIR, 'skills directory');
|
||||
testSuite.testDirectoryExists(WORKFLOWS_DIR, 'workflows directory');
|
||||
testSuite.testDirectoryExists(path.join(AGENTS_DIR, 'scripts'), 'scripts directory');
|
||||
testSuite.testDirectoryExists(path.join(AGENTS_DIR, 'rules'), 'rules directory');
|
||||
testSuite.log('');
|
||||
|
||||
// Test 2: Core Files
|
||||
testSuite.log('Test 2: Core Files', 'info');
|
||||
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, 'skills.md'), 'skills.md documentation');
|
||||
testSuite.log('');
|
||||
// Test 2: Core Files
|
||||
testSuite.log('Test 2: Core Files', 'info');
|
||||
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, 'skills.md'), 'skills.md documentation');
|
||||
testSuite.log('');
|
||||
|
||||
// Test 3: Script Files
|
||||
testSuite.log('Test 3: Validation Scripts', 'info');
|
||||
testSuite.testFileExists(path.join(AGENTS_DIR, 'scripts', 'bash', 'validate-versions.sh'), 'bash validate-versions.sh');
|
||||
testSuite.testFileExists(path.join(AGENTS_DIR, 'scripts', 'bash', 'audit-skills.sh'), 'bash audit-skills.sh');
|
||||
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 3: Script Files
|
||||
testSuite.log('Test 3: Validation Scripts', 'info');
|
||||
testSuite.testFileExists(
|
||||
path.join(AGENTS_DIR, 'scripts', 'bash', 'validate-versions.sh'),
|
||||
'bash validate-versions.sh'
|
||||
);
|
||||
testSuite.testFileExists(path.join(AGENTS_DIR, 'scripts', 'bash', 'audit-skills.sh'), 'bash audit-skills.sh');
|
||||
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
|
||||
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(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(AGENTS_DIR, 'rules', '00-project-context.md'), /v1\.8\.6/, 'project context version');
|
||||
testSuite.log('');
|
||||
// Test 4: Version Consistency
|
||||
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(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(AGENTS_DIR, 'rules', '00-project-context.md'),
|
||||
/v1\.8\.6/,
|
||||
'project context version'
|
||||
);
|
||||
testSuite.log('');
|
||||
|
||||
// Test 5: Skills Structure
|
||||
testSuite.log('Test 5: Skills Structure', 'info');
|
||||
const skillDirs = fs.readdirSync(SKILLS_DIR).filter(item => {
|
||||
const itemPath = path.join(SKILLS_DIR, item);
|
||||
return fs.statSync(itemPath).isDirectory() && item.startsWith('speckit-') || item === 'nestjs-best-practices' || item === 'next-best-practices';
|
||||
});
|
||||
// Test 5: Skills Structure
|
||||
testSuite.log('Test 5: Skills Structure', 'info');
|
||||
const skillDirs = fs.readdirSync(SKILLS_DIR).filter((item) => {
|
||||
const itemPath = path.join(SKILLS_DIR, item);
|
||||
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})`);
|
||||
|
||||
// Test a few key skills
|
||||
const keySkills = ['speckit-plan', 'speckit-implement', 'speckit-specify', 'speckit-validate'];
|
||||
keySkills.forEach(skill => {
|
||||
const skillPath = path.join(SKILLS_DIR, skill);
|
||||
const skillMdPath = path.join(skillPath, 'SKILL.md');
|
||||
testSuite.testDirectoryExists(skillPath, `${skill} directory`);
|
||||
testSuite.testFileExists(skillMdPath, `${skill} SKILL.md`);
|
||||
|
||||
if (fs.existsSync(skillMdPath)) {
|
||||
testSuite.testFileContent(skillMdPath, /^name:/, `${skill} has name field`);
|
||||
testSuite.testFileContent(skillMdPath, /^description:/, `${skill} has description field`);
|
||||
testSuite.testFileContent(skillMdPath, /^version:/, `${skill} has version field`);
|
||||
testSuite.testFileContent(skillMdPath, /^## Role$/, `${skill} has Role section`);
|
||||
testSuite.testFileContent(skillMdPath, /^## Task$/, `${skill} has Task section`);
|
||||
}
|
||||
});
|
||||
testSuite.log('');
|
||||
testSuite.assert(skillDirs.length >= 20, `Found at least 20 skill directories (found ${skillDirs.length})`);
|
||||
|
||||
// Test 6: Workflows Structure
|
||||
testSuite.log('Test 6: Workflows Structure', 'info');
|
||||
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})`);
|
||||
|
||||
// Test key workflows
|
||||
const keyWorkflows = ['00-speckit.all.md', '02-speckit.specify.md', '04-speckit.plan.md', '07-speckit.implement.md'];
|
||||
keyWorkflows.forEach(workflow => {
|
||||
const workflowPath = path.join(WORKFLOWS_DIR, workflow);
|
||||
testSuite.testFileExists(workflowPath, `${workflow} file`);
|
||||
});
|
||||
testSuite.log('');
|
||||
// Test a few key skills
|
||||
const keySkills = ['speckit-plan', 'speckit-implement', 'speckit-specify', 'speckit-validate'];
|
||||
keySkills.forEach((skill) => {
|
||||
const skillPath = path.join(SKILLS_DIR, skill);
|
||||
const skillMdPath = path.join(skillPath, 'SKILL.md');
|
||||
testSuite.testDirectoryExists(skillPath, `${skill} directory`);
|
||||
testSuite.testFileExists(skillMdPath, `${skill} SKILL.md`);
|
||||
|
||||
// Test 7: Rules Structure
|
||||
testSuite.log('Test 7: Rules Structure', 'info');
|
||||
const rulesDir = path.join(AGENTS_DIR, 'rules');
|
||||
const ruleFiles = fs.readdirSync(rulesDir).filter(item => item.endsWith('.md'));
|
||||
testSuite.assert(ruleFiles.length >= 10, `Found at least 10 rule files (found ${ruleFiles.length})`);
|
||||
|
||||
// Test key rules
|
||||
const keyRules = ['00-project-context.md', '01-adr-019-uuid.md', '02-security.md'];
|
||||
keyRules.forEach(rule => {
|
||||
const rulePath = path.join(rulesDir, rule);
|
||||
testSuite.testFileExists(rulePath, `${rule} file`);
|
||||
});
|
||||
testSuite.log('');
|
||||
if (fs.existsSync(skillMdPath)) {
|
||||
testSuite.testFileContent(skillMdPath, /^name:/, `${skill} has name field`);
|
||||
testSuite.testFileContent(skillMdPath, /^description:/, `${skill} has description field`);
|
||||
testSuite.testFileContent(skillMdPath, /^version:/, `${skill} has version field`);
|
||||
testSuite.testFileContent(skillMdPath, /^## Role$/, `${skill} has Role section`);
|
||||
testSuite.testFileContent(skillMdPath, /^## Task$/, `${skill} has Task section`);
|
||||
}
|
||||
});
|
||||
testSuite.log('');
|
||||
|
||||
// Test 8: Script Execution (if on Unix-like system)
|
||||
if (process.platform !== 'win32') {
|
||||
testSuite.log('Test 8: Script Execution', 'info');
|
||||
|
||||
// Test version validation script
|
||||
const versionScript = path.join(AGENTS_DIR, 'scripts', 'bash', 'validate-versions.sh');
|
||||
if (fs.existsSync(versionScript)) {
|
||||
try {
|
||||
// Make executable
|
||||
fs.chmodSync(versionScript, '755');
|
||||
testSuite.runScript(versionScript, 'Version validation script');
|
||||
} catch (error) {
|
||||
testSuite.log(` SKIP: Cannot execute version script - ${error.message}`, 'warn');
|
||||
}
|
||||
}
|
||||
|
||||
testSuite.log('');
|
||||
// Test 6: Workflows Structure
|
||||
testSuite.log('Test 6: Workflows Structure', 'info');
|
||||
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})`);
|
||||
|
||||
// Test key workflows
|
||||
const keyWorkflows = ['00-speckit.all.md', '02-speckit.specify.md', '04-speckit.plan.md', '07-speckit.implement.md'];
|
||||
keyWorkflows.forEach((workflow) => {
|
||||
const workflowPath = path.join(WORKFLOWS_DIR, workflow);
|
||||
testSuite.testFileExists(workflowPath, `${workflow} file`);
|
||||
});
|
||||
testSuite.log('');
|
||||
|
||||
// Test 7: Rules Structure
|
||||
testSuite.log('Test 7: Rules Structure', 'info');
|
||||
const rulesDir = path.join(AGENTS_DIR, 'rules');
|
||||
const ruleFiles = fs.readdirSync(rulesDir).filter((item) => item.endsWith('.md'));
|
||||
testSuite.assert(ruleFiles.length >= 10, `Found at least 10 rule files (found ${ruleFiles.length})`);
|
||||
|
||||
// Test key rules
|
||||
const keyRules = ['00-project-context.md', '01-adr-019-uuid.md', '02-security.md'];
|
||||
keyRules.forEach((rule) => {
|
||||
const rulePath = path.join(rulesDir, rule);
|
||||
testSuite.testFileExists(rulePath, `${rule} file`);
|
||||
});
|
||||
testSuite.log('');
|
||||
|
||||
// Test 8: Script Execution (if on Unix-like system)
|
||||
if (process.platform !== 'win32') {
|
||||
testSuite.log('Test 8: Script Execution', 'info');
|
||||
|
||||
// Test version validation script
|
||||
const versionScript = path.join(AGENTS_DIR, 'scripts', 'bash', 'validate-versions.sh');
|
||||
if (fs.existsSync(versionScript)) {
|
||||
try {
|
||||
// Make executable
|
||||
fs.chmodSync(versionScript, '755');
|
||||
testSuite.runScript(versionScript, 'Version validation script');
|
||||
} catch (error) {
|
||||
testSuite.log(` SKIP: Cannot execute version script - ${error.message}`, 'warn');
|
||||
}
|
||||
}
|
||||
|
||||
// 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('');
|
||||
}
|
||||
|
||||
// Results Summary
|
||||
testSuite.log('=== Test Results Summary ===', 'info');
|
||||
testSuite.log(`Passed: ${testSuite.results.passed}`, 'pass');
|
||||
testSuite.log(`Failed: ${testSuite.results.failed}`, testSuite.results.failed > 0 ? 'fail' : 'pass');
|
||||
|
||||
if (testSuite.results.errors.length > 0) {
|
||||
testSuite.log('Errors:', 'fail');
|
||||
testSuite.results.errors.forEach(error => {
|
||||
testSuite.log(` - ${error}`, 'fail');
|
||||
});
|
||||
}
|
||||
|
||||
testSuite.log(`Completed: ${new Date().toISOString()}`, 'info');
|
||||
|
||||
return testSuite.results.failed === 0;
|
||||
// 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('');
|
||||
|
||||
// Results Summary
|
||||
testSuite.log('=== Test Results Summary ===', 'info');
|
||||
testSuite.log(`Passed: ${testSuite.results.passed}`, 'pass');
|
||||
testSuite.log(`Failed: ${testSuite.results.failed}`, testSuite.results.failed > 0 ? 'fail' : 'pass');
|
||||
|
||||
if (testSuite.results.errors.length > 0) {
|
||||
testSuite.log('Errors:', 'fail');
|
||||
testSuite.results.errors.forEach((error) => {
|
||||
testSuite.log(` - ${error}`, 'fail');
|
||||
});
|
||||
}
|
||||
|
||||
testSuite.log(`Completed: ${new Date().toISOString()}`, 'info');
|
||||
|
||||
return testSuite.results.failed === 0;
|
||||
}
|
||||
|
||||
// Export for use in other modules
|
||||
@@ -236,6 +265,6 @@ module.exports = { SkillTestSuite, runAllTests };
|
||||
|
||||
// Run tests if called directly
|
||||
if (require.main === module) {
|
||||
const success = runAllTests();
|
||||
process.exit(success ? 0 : 1);
|
||||
const success = runAllTests();
|
||||
process.exit(success ? 0 : 1);
|
||||
}
|
||||
|
||||
@@ -13,216 +13,218 @@ const AGENTS_DIR = path.join(BASE_DIR, '.agents');
|
||||
|
||||
// Test utilities
|
||||
class WorkflowTestSuite {
|
||||
constructor() {
|
||||
this.results = {
|
||||
passed: 0,
|
||||
failed: 0,
|
||||
errors: []
|
||||
};
|
||||
constructor() {
|
||||
this.results = {
|
||||
passed: 0,
|
||||
failed: 0,
|
||||
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') {
|
||||
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}`);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Expected workflows mapping
|
||||
const expectedWorkflows = {
|
||||
'00-speckit.all.md': 'Full pipeline workflow',
|
||||
'01-speckit.constitution.md': 'Constitution workflow',
|
||||
'02-speckit.specify.md': 'Specification workflow',
|
||||
'03-speckit.clarify.md': 'Clarification workflow',
|
||||
'04-speckit.plan.md': 'Planning workflow',
|
||||
'05-speckit.tasks.md': 'Task breakdown workflow',
|
||||
'06-speckit.analyze.md': 'Analysis workflow',
|
||||
'07-speckit.implement.md': 'Implementation workflow',
|
||||
'08-speckit.checker.md': 'Static analysis workflow',
|
||||
'09-speckit.tester.md': 'Testing workflow',
|
||||
'10-speckit.reviewer.md': 'Code review workflow',
|
||||
'11-speckit.validate.md': 'Validation workflow',
|
||||
'speckit.prepare.md': 'Preparation workflow',
|
||||
'schema-change.md': 'Schema change workflow',
|
||||
'create-backend-module.md': 'Backend module creation',
|
||||
'create-frontend-page.md': 'Frontend page creation',
|
||||
'deploy.md': 'Deployment workflow',
|
||||
'review.md': 'Code review workflow',
|
||||
'util-speckit.checklist.md': 'Checklist utility',
|
||||
'util-speckit.diff.md': 'Diff utility',
|
||||
'util-speckit.migrate.md': 'Migration utility',
|
||||
'util-speckit.quizme.md': 'Quiz utility',
|
||||
'util-speckit.status.md': 'Status utility',
|
||||
'util-speckit.taskstoissues.md': 'Task to issues utility'
|
||||
'00-speckit.all.md': 'Full pipeline workflow',
|
||||
'01-speckit.constitution.md': 'Constitution workflow',
|
||||
'02-speckit.specify.md': 'Specification workflow',
|
||||
'03-speckit.clarify.md': 'Clarification workflow',
|
||||
'04-speckit.plan.md': 'Planning workflow',
|
||||
'05-speckit.tasks.md': 'Task breakdown workflow',
|
||||
'06-speckit.analyze.md': 'Analysis workflow',
|
||||
'07-speckit.implement.md': 'Implementation workflow',
|
||||
'08-speckit.checker.md': 'Static analysis workflow',
|
||||
'09-speckit.tester.md': 'Testing workflow',
|
||||
'10-speckit.reviewer.md': 'Code review workflow',
|
||||
'11-speckit.validate.md': 'Validation workflow',
|
||||
'speckit.prepare.md': 'Preparation workflow',
|
||||
'schema-change.md': 'Schema change workflow',
|
||||
'create-backend-module.md': 'Backend module creation',
|
||||
'create-frontend-page.md': 'Frontend page creation',
|
||||
'deploy.md': 'Deployment workflow',
|
||||
'review.md': 'Code review workflow',
|
||||
'util-speckit.checklist.md': 'Checklist utility',
|
||||
'util-speckit.diff.md': 'Diff utility',
|
||||
'util-speckit.migrate.md': 'Migration utility',
|
||||
'util-speckit.quizme.md': 'Quiz utility',
|
||||
'util-speckit.status.md': 'Status utility',
|
||||
'util-speckit.taskstoissues.md': 'Task to issues utility',
|
||||
};
|
||||
|
||||
// Test suite implementation
|
||||
const workflowTestSuite = new WorkflowTestSuite();
|
||||
|
||||
function runWorkflowTests() {
|
||||
workflowTestSuite.log('=== Workflow Validation Test Suite ===', 'info');
|
||||
workflowTestSuite.log(`Workflows directory: ${WORKFLOWS_DIR}`, 'info');
|
||||
workflowTestSuite.log(`Started: ${new Date().toISOString()}`, 'info');
|
||||
workflowTestSuite.log('');
|
||||
workflowTestSuite.log('=== Workflow Validation Test Suite ===', 'info');
|
||||
workflowTestSuite.log(`Workflows directory: ${WORKFLOWS_DIR}`, 'info');
|
||||
workflowTestSuite.log(`Started: ${new Date().toISOString()}`, 'info');
|
||||
workflowTestSuite.log('');
|
||||
|
||||
// Test 1: Workflows directory exists
|
||||
workflowTestSuite.log('Test 1: Directory Structure', 'info');
|
||||
workflowTestSuite.assert(fs.existsSync(WORKFLOWS_DIR), 'Workflows directory exists');
|
||||
workflowTestSuite.log('');
|
||||
// Test 1: Workflows directory exists
|
||||
workflowTestSuite.log('Test 1: Directory Structure', 'info');
|
||||
workflowTestSuite.assert(fs.existsSync(WORKFLOWS_DIR), 'Workflows directory exists');
|
||||
workflowTestSuite.log('');
|
||||
|
||||
// Test 2: Expected workflow files exist
|
||||
workflowTestSuite.log('Test 2: Expected Workflow Files', 'info');
|
||||
let foundWorkflows = 0;
|
||||
|
||||
for (const [filename, description] of Object.entries(expectedWorkflows)) {
|
||||
const filePath = path.join(WORKFLOWS_DIR, filename);
|
||||
workflowTestSuite.testWorkflowFile(filePath, description);
|
||||
if (fs.existsSync(filePath)) {
|
||||
foundWorkflows++;
|
||||
}
|
||||
}
|
||||
|
||||
workflowTestSuite.assert(foundWorkflows >= 20, `Found at least 20 workflows (found ${foundWorkflows})`);
|
||||
workflowTestSuite.log('');
|
||||
// Test 2: Expected workflow files exist
|
||||
workflowTestSuite.log('Test 2: Expected Workflow Files', 'info');
|
||||
let foundWorkflows = 0;
|
||||
|
||||
// Test 3: Workflow content validation
|
||||
workflowTestSuite.log('Test 3: Content Validation', 'info');
|
||||
|
||||
for (const [filename, description] of Object.entries(expectedWorkflows)) {
|
||||
const filePath = path.join(WORKFLOWS_DIR, filename);
|
||||
|
||||
if (fs.existsSync(filePath)) {
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
|
||||
// Check for proper workflow structure
|
||||
workflowTestSuite.assert(content.includes('#'), `${filename} has markdown headers`);
|
||||
workflowTestSuite.assert(content.length > 100, `${filename} has substantial content`);
|
||||
|
||||
// Validate skill dependencies
|
||||
workflowTestSuite.validateWorkflowDependency(filename, content);
|
||||
|
||||
} catch (error) {
|
||||
workflowTestSuite.assert(false, `${filename} - content validation error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
for (const [filename, description] of Object.entries(expectedWorkflows)) {
|
||||
const filePath = path.join(WORKFLOWS_DIR, filename);
|
||||
workflowTestSuite.testWorkflowFile(filePath, description);
|
||||
if (fs.existsSync(filePath)) {
|
||||
foundWorkflows++;
|
||||
}
|
||||
workflowTestSuite.log('');
|
||||
}
|
||||
|
||||
// Test 4: Workflow naming consistency
|
||||
workflowTestSuite.log('Test 4: Naming Consistency', 'info');
|
||||
const actualFiles = fs.readdirSync(WORKFLOWS_DIR).filter(file => file.endsWith('.md'));
|
||||
|
||||
for (const actualFile of actualFiles) {
|
||||
if (!expectedWorkflows[actualFile]) {
|
||||
workflowTestSuite.log(` UNEXPECTED: ${actualFile} not in expected list`, 'warn');
|
||||
}
|
||||
}
|
||||
|
||||
for (const expectedFile of Object.keys(expectedWorkflows)) {
|
||||
if (!actualFiles.includes(expectedFile)) {
|
||||
workflowTestSuite.assert(false, `Missing expected workflow: ${expectedFile}`);
|
||||
}
|
||||
}
|
||||
workflowTestSuite.log('');
|
||||
workflowTestSuite.assert(foundWorkflows >= 20, `Found at least 20 workflows (found ${foundWorkflows})`);
|
||||
workflowTestSuite.log('');
|
||||
|
||||
// Test 5: Cross-reference validation
|
||||
workflowTestSuite.log('Test 5: Cross-Reference Validation', 'info');
|
||||
|
||||
// Check if README.md references workflows correctly
|
||||
const readmePath = path.join(AGENTS_DIR, 'README.md');
|
||||
if (fs.existsSync(readmePath)) {
|
||||
const readmeContent = fs.readFileSync(readmePath, 'utf8');
|
||||
workflowTestSuite.assert(
|
||||
readmeContent.includes('.windsurf/workflows'),
|
||||
'README.md references correct workflows path'
|
||||
);
|
||||
}
|
||||
workflowTestSuite.log('');
|
||||
// Test 3: Workflow content validation
|
||||
workflowTestSuite.log('Test 3: Content Validation', 'info');
|
||||
|
||||
// Results Summary
|
||||
workflowTestSuite.log('=== Workflow Test Results Summary ===', 'info');
|
||||
workflowTestSuite.log(`Passed: ${workflowTestSuite.results.passed}`, 'pass');
|
||||
workflowTestSuite.log(`Failed: ${workflowTestSuite.results.failed}`, workflowTestSuite.results.failed > 0 ? 'fail' : 'pass');
|
||||
|
||||
if (workflowTestSuite.results.errors.length > 0) {
|
||||
workflowTestSuite.log('Errors:', 'fail');
|
||||
workflowTestSuite.results.errors.forEach(error => {
|
||||
workflowTestSuite.log(` - ${error}`, 'fail');
|
||||
});
|
||||
for (const [filename, description] of Object.entries(expectedWorkflows)) {
|
||||
const filePath = path.join(WORKFLOWS_DIR, filename);
|
||||
|
||||
if (fs.existsSync(filePath)) {
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
|
||||
// Check for proper workflow structure
|
||||
workflowTestSuite.assert(content.includes('#'), `${filename} has markdown headers`);
|
||||
workflowTestSuite.assert(content.length > 100, `${filename} has substantial content`);
|
||||
|
||||
// Validate skill dependencies
|
||||
workflowTestSuite.validateWorkflowDependency(filename, content);
|
||||
} catch (error) {
|
||||
workflowTestSuite.assert(false, `${filename} - content validation error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
workflowTestSuite.log(`Completed: ${new Date().toISOString()}`, 'info');
|
||||
|
||||
return workflowTestSuite.results.failed === 0;
|
||||
}
|
||||
workflowTestSuite.log('');
|
||||
|
||||
// Test 4: Workflow naming consistency
|
||||
workflowTestSuite.log('Test 4: Naming Consistency', 'info');
|
||||
const actualFiles = fs.readdirSync(WORKFLOWS_DIR).filter((file) => file.endsWith('.md'));
|
||||
|
||||
for (const actualFile of actualFiles) {
|
||||
if (!expectedWorkflows[actualFile]) {
|
||||
workflowTestSuite.log(` UNEXPECTED: ${actualFile} not in expected list`, 'warn');
|
||||
}
|
||||
}
|
||||
|
||||
for (const expectedFile of Object.keys(expectedWorkflows)) {
|
||||
if (!actualFiles.includes(expectedFile)) {
|
||||
workflowTestSuite.assert(false, `Missing expected workflow: ${expectedFile}`);
|
||||
}
|
||||
}
|
||||
workflowTestSuite.log('');
|
||||
|
||||
// Test 5: Cross-reference validation
|
||||
workflowTestSuite.log('Test 5: Cross-Reference Validation', 'info');
|
||||
|
||||
// Check if README.md references workflows correctly
|
||||
const readmePath = path.join(AGENTS_DIR, 'README.md');
|
||||
if (fs.existsSync(readmePath)) {
|
||||
const readmeContent = fs.readFileSync(readmePath, 'utf8');
|
||||
workflowTestSuite.assert(
|
||||
readmeContent.includes('.windsurf/workflows'),
|
||||
'README.md references correct workflows path'
|
||||
);
|
||||
}
|
||||
workflowTestSuite.log('');
|
||||
|
||||
// Results Summary
|
||||
workflowTestSuite.log('=== Workflow Test Results Summary ===', 'info');
|
||||
workflowTestSuite.log(`Passed: ${workflowTestSuite.results.passed}`, 'pass');
|
||||
workflowTestSuite.log(
|
||||
`Failed: ${workflowTestSuite.results.failed}`,
|
||||
workflowTestSuite.results.failed > 0 ? 'fail' : 'pass'
|
||||
);
|
||||
|
||||
if (workflowTestSuite.results.errors.length > 0) {
|
||||
workflowTestSuite.log('Errors:', 'fail');
|
||||
workflowTestSuite.results.errors.forEach((error) => {
|
||||
workflowTestSuite.log(` - ${error}`, 'fail');
|
||||
});
|
||||
}
|
||||
|
||||
workflowTestSuite.log(`Completed: ${new Date().toISOString()}`, 'info');
|
||||
|
||||
return workflowTestSuite.results.failed === 0;
|
||||
}
|
||||
|
||||
// Export for use in other modules
|
||||
@@ -230,6 +232,6 @@ module.exports = { WorkflowTestSuite, runWorkflowTests };
|
||||
|
||||
// Run tests if called directly
|
||||
if (require.main === module) {
|
||||
const success = runWorkflowTests();
|
||||
process.exit(success ? 0 : 1);
|
||||
const success = runWorkflowTests();
|
||||
process.exit(success ? 0 : 1);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
description: บันทึก session log และอัปเดต project memory
|
||||
---
|
||||
|
||||
# บันทึก Memory
|
||||
|
||||
ใช้ skill `save-memory` เพื่อบันทึก session log และอัปเดต project memory ตามโครงสร้างใหม่
|
||||
|
||||
```bash
|
||||
skill save-memory
|
||||
```
|
||||
@@ -4,8 +4,8 @@ trigger: always_on
|
||||
|
||||
# NAP-DMS Project Context & Rules
|
||||
|
||||
- For: Windsurf Cascade (and compatible: Codex CLI, opencode, Amp, Antigravity, AGENTS.md tools)
|
||||
- Version: 1.9.6 | Last synced from repo: 2026-05-22
|
||||
- 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)
|
||||
|
||||
@@ -130,25 +130,25 @@ Best practice — follow when possible:
|
||||
|
||||
## 🚫 Forbidden Actions
|
||||
|
||||
| ❌ Forbidden | ✅ Correct Approach |
|
||||
| ----------------------------------------------- | ------------------------------------------------------- |
|
||||
| SQL Triggers for business logic | NestJS Service methods |
|
||||
| `.env` files in production | `docker-compose.yml` environment section |
|
||||
| TypeORM migration files | Edit schema SQL directly (ADR-009) |
|
||||
| Inventing table/column names | Verify against `lcbp3-v1.9.0-schema-02-tables.sql` |
|
||||
| `any` TypeScript type | Proper types / generics |
|
||||
| `console.log` in committed code | NestJS Logger (backend) / remove (frontend) |
|
||||
| `req: any` in controllers | `RequestWithUser` typed interface |
|
||||
| `parseInt()` on UUID values | Use UUID string directly (ADR-019) |
|
||||
| Exposing INT PK in API responses | UUIDv7 (ADR-019) |
|
||||
| AI accessing DB/storage directly | AI → DMS API → DB (ADR-023) |
|
||||
| Direct file operations bypassing StorageService | `StorageService` for all file moves |
|
||||
| Inline email/notification sending | BullMQ queue job |
|
||||
| Deploying without Release Gates | Complete `04-08-release-management-policy.md` |
|
||||
| AI direct cloud API calls | On-premises Ollama only (ADR-023) |
|
||||
| AI outputs without human validation | Human-in-the-loop validation required (ADR-023) |
|
||||
| n8n calling Ollama/Qdrant directly | n8n → DMS API → BullMQ → Ollama/Qdrant (ADR-023A) |
|
||||
| Qdrant query without `projectPublicId` filter | `QdrantService.search(projectPublicId, ...)` (ADR-023A) |
|
||||
| ❌ 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 |
|
||||
|
||||
---
|
||||
|
||||
@@ -6,17 +6,17 @@ trigger: always_on
|
||||
|
||||
## DMS Glossary
|
||||
|
||||
| ✅ Use | ❌ Don't Use |
|
||||
| ------------------ | ------------------------------------- |
|
||||
| Correspondence | Letter, Communication, Document |
|
||||
| RFA | Approval Request, Submit for Approval |
|
||||
| Transmittal | Delivery Note, Cover Letter |
|
||||
| Circulation | Distribution, Routing |
|
||||
| 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) |
|
||||
| ✅ 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
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
trigger: always_on
|
||||
---
|
||||
|
||||
# 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,214 @@
|
||||
---
|
||||
trigger: always_on
|
||||
---
|
||||
|
||||
# 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 ที่ชัดเจนและไม่ซ้ำกัน** — เพื่อป้องกันความสับสน
|
||||
@@ -7,7 +7,7 @@ trigger: always_on
|
||||
## CRITICAL RULES
|
||||
|
||||
- **ALWAYS** follow ADR-023 AI boundary policy (isolation on Admin Desktop)
|
||||
- **ALWAYS** use ADR-023A 2-model stack (gemma4:e4b Q8_0 + nomic-embed-text)
|
||||
- **ALWAYS** use ADR-023A 2-model stack (gemma4:e2b + 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
|
||||
@@ -30,7 +30,7 @@ n8n (Migration) → DMS API → BullMQ → Admin Desktop (Ollama) → Backend Va
|
||||
| ----------------- | ------------------------- | ------------------------------------------------------------------------ |
|
||||
| **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) | gemma4:e4b Q8_0 (LLM) + nomic-embed-text (Embedding) |
|
||||
| **Ollama Engine** | Admin Desktop (Desk-5439) | gemma4:e2b (LLM) + nomic-embed-text (Embedding) |
|
||||
| **OCR Engine** | Admin Desktop (Desk-5439) | PaddleOCR + PyThaiNLP (Thai/English text extraction) |
|
||||
| **Orchestrator** | QNAP NAS (n8n) | Migration Phase orchestrator only (calls DMS API, never Ollama directly) |
|
||||
|
||||
@@ -80,7 +80,7 @@ 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 (gemma4:e4b Q8_0)
|
||||
// 3. Worker sends to Admin Desktop AI (gemma4:e2b)
|
||||
// 4. Validate AI response
|
||||
// 5. Log audit trail to ai_audit_logs
|
||||
// 6. Return validated results
|
||||
@@ -119,7 +119,7 @@ const DocumentReviewForm = ({ document, aiSuggestions }) => {
|
||||
|
||||
## ADR-023A Specific Rules
|
||||
|
||||
- **2-Model Stack:** gemma4:e4b Q8_0 (~4.0GB) + nomic-embed-text (~0.3GB) = ~4.3GB VRAM peak
|
||||
- **2-Model Stack:** gemma4:e2b + nomic-embed-text
|
||||
- **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 PaddleOCR
|
||||
@@ -133,7 +133,7 @@ const DocumentReviewForm = ({ document, aiSuggestions }) => {
|
||||
- [ ] BullMQ 2-queue setup (ai-realtime + ai-batch)
|
||||
- [ ] QdrantService with projectPublicId enforcement
|
||||
- [ ] DocumentReviewForm reusable component
|
||||
- [ ] Admin Desktop Ollama (gemma4:e4b Q8_0 + nomic-embed-text) + PaddleOCR setup
|
||||
- [ ] Admin Desktop Ollama (gemma4:e2b + nomic-embed-text) + PaddleOCR setup
|
||||
- [ ] n8n workflow orchestration (Migration Phase only)
|
||||
- [ ] AI audit logging and monitoring (ai_audit_logs)
|
||||
- [ ] Human-in-the-loop validation workflows
|
||||
@@ -142,3 +142,8 @@ const DocumentReviewForm = ({ document, aiSuggestions }) => {
|
||||
|
||||
- `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,30 @@
|
||||
# lcbp3 Development Guidelines
|
||||
|
||||
Auto-generated from all feature plans. Last updated: 2026-05-30
|
||||
|
||||
## Active Technologies
|
||||
|
||||
- TypeScript 5.x (NestJS 11 backend, Next.js 16 frontend), Python 3.11 (OCR sidecar)
|
||||
+ Ollama (AI runtime), BullMQ (job queues), TypeORM (ORM), Redis (caching/locks), MariaDB 11.8 (database)
|
||||
(232-typhoon-ocr-integration)
|
||||
|
||||
## Project Structure
|
||||
|
||||
```text
|
||||
backend/
|
||||
frontend/
|
||||
tests/
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
cd src [ONLY COMMANDS FOR ACTIVE TECHNOLOGIES][ONLY COMMANDS FOR ACTIVE TECHNOLOGIES] pytest [ONLY COMMANDS FOR ACTIVE TECHNOLOGIES][ONLY COMMANDS FOR ACTIVE TECHNOLOGIES] ruff check .
|
||||
|
||||
## Code Style
|
||||
|
||||
TypeScript 5.x (NestJS 11 backend, Next.js 16 frontend), Python 3.11 (OCR sidecar)
|
||||
: Follow standard conventions
|
||||
|
||||
## Recent Changes
|
||||
|
||||
- 232-typhoon-ocr-integration: Added TypeScript 5.x (NestJS 11 backend, Next.js 16 frontend), Python 3.11 (OCR sidecar)
|
||||
@@ -1,8 +1,8 @@
|
||||
# `.agents/skills/` — LCBP3 Agent Skill Pack
|
||||
|
||||
**Version:** 1.9.0 | **Last Updated:** 2026-05-17 | **Total Skills:** 23
|
||||
**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,6 +14,7 @@ Agent skills for AI-assisted development in **Windsurf IDE** (and compatible age
|
||||
├── skills.md # Overview + dependency matrix + health monitoring
|
||||
├── _LCBP3-CONTEXT.md # Shared LCBP3 context injected into every speckit-* skill
|
||||
├── README.md # (this file)
|
||||
├── save-memory/ # Session log & project memory update
|
||||
├── nestjs-best-practices/ # Backend rules (40 rules across 10 categories)
|
||||
├── next-best-practices/ # Frontend rules (Next.js 15+)
|
||||
├── e2e-testing/ # Playwright E2E testing patterns (POM, flaky tests, CI/CD)
|
||||
@@ -30,12 +31,10 @@ Each skill directory contains:
|
||||
|
||||
---
|
||||
|
||||
## 🚀 How Windsurf Invokes These Skills
|
||||
## 🚀 How Devin Invokes These Skills
|
||||
|
||||
Windsurf exposes two entry points:
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Both paths end up executing the same `SKILL.md` instructions.
|
||||
|
||||
@@ -65,14 +64,14 @@ Use `/00-speckit.all` to run specify → clarify → plan → tasks → analyze
|
||||
|
||||
From repo root:
|
||||
|
||||
| Script | Purpose |
|
||||
| --------------------------------------------------------- | ----------------------------------------------------------- |
|
||||
| `./.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/update-agent-context.sh windsurf` | Append tech entries to `AGENTS.md` |
|
||||
| `./.agents/scripts/bash/audit-skills.sh` | Validate all `SKILL.md` frontmatter + presence |
|
||||
| `./.agents/scripts/bash/validate-versions.sh` | Version consistency check |
|
||||
| `./.agents/scripts/bash/sync-workflows.sh` | Verify every skill has a `.windsurf/workflows/*.md` wrapper |
|
||||
| Script | Purpose |
|
||||
| ------------------------------------------------------ | ---------------------------------------------------------- |
|
||||
| `./.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/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/validate-versions.sh` | Version consistency check |
|
||||
| `./.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.
|
||||
|
||||
@@ -97,7 +96,7 @@ To add a new skill:
|
||||
|
||||
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`.
|
||||
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.
|
||||
5. Run `./.agents/scripts/bash/audit-skills.sh` → must pass.
|
||||
|
||||
+1
-1
@@ -6454,7 +6454,7 @@ CREATE TABLE ai_audit_log (
|
||||
user_id INT NOT NULL,
|
||||
action VARCHAR(64) NOT NULL, -- 'ai.extract_metadata', 'ai.classify', etc.
|
||||
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),
|
||||
input_hash CHAR(64), -- SHA-256 of input for replay detection
|
||||
output_summary JSON,
|
||||
+1
-1
@@ -137,7 +137,7 @@ CREATE TABLE ai_audit_log (
|
||||
user_id INT NOT NULL,
|
||||
action VARCHAR(64) NOT NULL, -- 'ai.extract_metadata', 'ai.classify', etc.
|
||||
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),
|
||||
input_hash CHAR(64), -- SHA-256 of input for replay detection
|
||||
output_summary JSON,
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user