refactor(ai): OCR sidecar canonical naming cleanup — typhoon→np-dms, remove hardcoded keys, asyncio.to_thread, ADR-040/041
CI / CD Pipeline / build (push) Successful in 7m37s
CI / CD Pipeline / deploy (push) Failing after 20m15s

This commit is contained in:
2026-06-20 16:37:04 +07:00
parent d418d791a4
commit a80ebef285
70 changed files with 5762 additions and 452 deletions
+7
View File
@@ -36,4 +36,11 @@
| 2026-06-19 | v1.9.10 | Feature-240 AI Admin Console Collapsible Cards — เพิ่มปุ่มและฟังก์ชันพับ/คลี่การ์ดและเซกชัน พร้อมบันทึกสถานะลง localStorage และรักษา background query polling | ✅ Complete |
| 2026-06-19 | v1.9.10 | Deployment Timeout Fix — Added clamav health check before recreation (skip if healthy), increased CI timeout 20→30 min | ✅ Complete |
| 2026-06-19 | v1.9.10 | AI Admin Response Normalization — recursive data unwrap for VRAM/prompt payloads, fixed Sandbox `.map()` crash and false OOM Guard | ✅ Complete |
| 2026-06-19 | v1.9.2 | SQL Delta Consolidation — merged applied deltas into schema/seed files, updated data dictionary to v1.9.2, cleaned up deltas directory, moved INSERT statements from schema to seed file | ✅ Complete |
| 2026-06-20 | v1.9.10 | ADR-040 OCR Sidecar Refactor — Pure compute worker, async I/O, residency wiring, path hardening, network isolation (supersedes ADR-033 §7) | ✅ Proposed |
| 2026-06-20 | v1.9.10 | ADR-041 Server Consolidation — Single Docker host (Ryzen 5 5600/32GB/RTX 5060 Ti 16GB), ASUSTOR as Primary NAS, QNAP as backup | ✅ Proposed |
| 2026-06-20 | v1.9.10 | OCR Sidecar Refactor (Speckit-140) — spec.md, plan.md, tasks.md generated, 5 analysis issues fixed, ready for implementation | ✅ Ready for Implement |
| 2026-06-20 | v1.9.10 | OCR Sidecar Refactor Phase 6+8+9 — async I/O (lifespan + AsyncClient + asyncio.to_thread), ลบ /normalize endpoint, Dockerfile curl, docker-compose stale config cleanup, README.md, quickstart.md fix — 19/19 Python tests pass | ✅ Complete (Phase 7 blocked by ADR-041) |
| 2026-06-20 | v1.9.10 | OCR Backend Cleanup — typhoon-llm → np-dms-ai (processor+queue+module), tesseract → fast-path (enum+entity+controller+service+tests), P1-P3 fixes (keep_alive removal, hardcoded API key removal, env var alignment, Dockerfile 3.11, asyncio.to_thread VRAM calls) | ✅ Complete (pending tsc verify) |
| 2026-06-20 | v1.9.10 | OCR Naming Refactor — TyphoonOcr → NpDmsOcr (processor/queue/Redis key/aiModel), OcrTyphoonOptions → OcrNpDmsOptions, typhoonOptions → ocrOptions (backend 7 files + 3 tests), frontend typhoon state vars → ocr, isTyphoon → isAiPowered, Tesseract mocks → Fast Path, dead typhoon_ocr checks removed, page.tsx model name constants | ✅ Complete (pending tsc verify) |
@@ -0,0 +1,32 @@
# Session — 2026-06-19 (SQL Delta Consolidation)
## Summary
รวม SQL delta files ที่ apply แล้วเข้ากับ schema และ seed files หลัก, ลบ rollback files, อัปเดต data dictionary, และย้าย INSERT statements จาก schema file ไป seed file
## ปัญหาที่พบ (Root Cause)
ไม่มีปัญหา - เป็นงาน maintenance ปกติ
## การแก้ไข (Fix)
| ไฟล์ | การเปลี่ยนแปลง |
| -------------- | ---------------------- |
| `specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql` | อัปเดต `tags`, `correspondence_tags`, `system_settings`, `migration_review_queue`, `ai_audit_logs` tables; เพิ่ม `ai_available_models`, `ai_prompts`, `ai_execution_profiles`, `ai_sandbox_profiles`, `migration_errors` tables; ลบ INSERT statements |
| `specs/03-Data-and-Storage/lcbp3-v1.9.0-seed-basic.sql` | เพิ่ม AI seed data (ai_available_models, ai_execution_profiles, ai_sandbox_profiles); เพิ่ม system_settings INSERT statements |
| `specs/03-Data-and-Storage/03-01-data-dictionary.md` | อัปเดต version เป็น 1.9.2; อัปเดต `ai_audit_logs` definition; เพิ่ม entries สำหรับ `ai_available_models`, `ai_prompts`, `ai_execution_profiles`, `ai_sandbox_profiles`, `migration_errors` |
| `specs/03-Data-and-Storage/deltas/` | ลบ rollback files 15 ไฟล์และ .sql files 26 ไฟล์ทั้งหมด |
## กฎที่ Lock แล้ว
- **Schema Management**: ใช้ ADR-009 (no migrations) - แก้ SQL schema โดยตรง และใช้ delta files สำหรับ tracking
- **Seed Data Separation**: INSERT statements ต้องอยู่ใน seed files ไม่ใช่ schema files
- **Data Dictionary Sync**: เมื่อแก้ schema ต้องอัปเดต data dictionary พร้อม version bump
## Verification
- [x] Schema file ไม่มี INSERT statements
- [x] Seed file มี system_settings INSERT statements
- [x] AI seed data ถูกเพิ่มใน seed-basic.sql
- [x] Data dictionary version ถูก bump เป็น 1.9.2
- [x] Delta directory ถูก clean up (เหลือเฉพาะ README.md)
@@ -0,0 +1,79 @@
# Session 2026-06-20 — OCR Backend Cleanup (Legacy Alias Removal)
## Summary
ทำความสะอาด backend code ให้ใช้ canonical naming อย่างสม่ำเสมอ: เปลี่ยน `typhoon-llm``np-dms-ai`, ลบ `tesseract` references ทั้งหมด, และ apply recommended fixes (P1P3) จาก code review
## การเปลี่ยนแปลง (Fix)
### P1: Critical Fixes
| ไฟล์ | การเปลี่ยนแปลง |
| --- | --- |
| `backend/src/modules/ai/services/ocr.service.ts` | P1-1: ลบ `keep_alive` ออกจาก multipart form data (sidecar คำนวณ internally); P1-2: ลบ hardcoded API key default — throw ถ้า `OCR_SIDECAR_API_KEY` ไม่ set; เปลี่ยน `processWithTyphoon``processWithNpDmsOcr`, `processWithTesseract``processWithFastPath`; audit log model names เปลี่ยนเป็น `fast-path`/`pymupdf` และ `np-dms-ocr` |
| `backend/src/modules/ai/services/sandbox-ocr-engine.service.ts` | P1-2: ลบ hardcoded API key default — throw ถ้า `OCR_SIDECAR_API_KEY` ไม่ set; ลบ `'tesseract'` ออกจาก `SandboxOcrEngineType`; อัปเดต routing condition และ fallback comments |
### P2: Important Fixes
| ไฟล์ | การเปลี่ยนแปลง |
| --- | --- |
| `backend/.env.example` | P2-1: `OCR_API_KEY``OCR_SIDECAR_API_KEY` (align กับ code); P2-2: OCR URL `192.168.10.8``192.168.10.100` (Desk-5439); ลบ `THAI_PREPROCESS_URL` (endpoint deleted in ADR-040 Phase 8); comment "PaddleOCR" → "np-dms-ocr" |
| `specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/ocr-sidecar/Dockerfile` | P2-5: `python:3.10-slim``python:3.11-slim` |
### P3: Medium Priority
| ไฟล์ | การเปลี่ยนแปลง |
| --- | --- |
| `specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/ocr-sidecar/app.py` | P3-1/P3-2: Wrap sync VRAM calls ใน `asyncio.to_thread()``calculate_ocr_residency()` ใน `process_ocr`, `get_vram_headroom()` ใน `/embed` และ `/rerank` |
| `specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/ocr-sidecar/services/residency_policy.py` | อัปเดต comment "Typhoon OCR" → "np-dms-ocr" |
### typhoon-llm → np-dms-ai Rename
| ไฟล์ | การเปลี่ยนแปลง |
| --- | --- |
| `backend/src/modules/ai/processors/np-dms-ai.processor.ts` | **สร้างใหม่**`NpDmsAiProcessor`, `QUEUE_NP_DMS_AI = 'np-dms-ai'`, `NpDmsAiJobData`, Redis key `ai:np-dms-ai:llm:`, audit `aiModel: 'np-dms-ai'` |
| `backend/src/modules/ai/processors/typhoon-llm.processor.ts` | **ลบ** — แทนที่ด้วย `np-dms-ai.processor.ts` |
| `backend/src/modules/ai/ai.module.ts` | เปลี่ยน import จาก `typhoon-llm.processor``np-dms-ai.processor`; queue name `QUEUE_TYPHOON_LLM``QUEUE_NP_DMS_AI`; provider `TyphoonLlmProcessor``NpDmsAiProcessor` |
### Tesseract Cleanup
| ไฟล์ | การเปลี่ยนแปลง |
| --- | --- |
| `backend/src/modules/ai/entities/ocr-engine-configuration.entity.ts` | `TESSERACT``FAST_PATH`, `TYPHOON_OCR``NP_DMS_OCR` ใน enum |
| `backend/src/modules/ai/ai.controller.ts` | ลบ `'tesseract'` ออกจาก Swagger enum และ `validEngineTypes` |
| `backend/src/modules/ai/entities/ai-audit-log.entity.ts` | อัปเดต comment examples: `tesseract``fast-path`, `typhoon-ocr-3b``np-dms-ocr` |
### Test Files Updated
| ไฟล์ | การเปลี่ยนแปลง |
| --- | --- |
| `backend/src/modules/ai/services/sandbox-ocr-engine.service.spec.ts` | ลบ tesseract test block; อัปเดต `engineUsed` expectations จาก `'tesseract'``'fast-path'`; อัปเดต fallback test descriptions และ mock text |
### User Manual Changes (หลัง session)
| ไฟล์ | การเปลี่ยนแปลง |
| --- | --- |
| `backend/src/modules/ai/processors/typhoon-ocr.processor.ts` | **ลบโดย user** — renamed ไป `np-dms-ocr-processor.ts` |
| `backend/src/modules/ai/processors/np-dms-ocr-processor.ts` | **สร้างโดย user** — renamed file; class name ยังเป็น `TyphoonOcrProcessor` และ queue `QUEUE_TYPHOON_OCR` |
| `backend/src/modules/ai/ai.module.ts` | User อัปเดต import path เป็น `./processors/np-dms-ocr-processor` |
## กฎที่ Lock แล้ว
- **Canonical naming:** `np-dms-ai` (LLM processor/queue), `np-dms-ocr` (OCR engine), `fast-path` (PyMuPDF text layer) — ไม่ใช้ `typhoon-llm`, `tesseract`, หรือ `Typhoon OCR` ใน code ใหม่
- **API Key:** `OCR_SIDECAR_API_KEY` เป็น mandatory env var — ห้ามมี hardcoded default
- **keep_alive:** Backend ไม่ส่ง `keep_alive` ใน form data — sidecar คำนวณผ่าน `calculate_ocr_residency()` เท่านั้น
- **VRAM calls:** Sync VRAM/residency calls ใน async endpoints ต้อง wrap ใน `asyncio.to_thread()`
## Remaining Work (Next Session)
- [ ] **Rename `TyphoonOcrProcessor` → `NpDmsOcrProcessor`** ใน `np-dms-ocr-processor.ts` (class name + queue constant `QUEUE_TYPHOON_OCR``QUEUE_NP_DMS_OCR`)
- [ ] **อัปเดต `ai.module.ts`** import ให้ใช้ `NpDmsOcrProcessor` และ `QUEUE_NP_DMS_OCR`
- [ ] **อัปเดต `typhoon-ocr.processor.spec.ts`** ถ้ามี — rename และ update references
- [ ] **tsc --noEmit verification** หลัง rename ครบ
- [ ] **Backend build** เพื่อยืนยันไม่มี broken imports
## Verification
- [ ] `pnpm --filter backend exec tsc --noEmit` — ยังไม่ได้รัน (pending rename TyphoonOcrProcessor)
- [ ] Backend unit tests — ยังไม่ได้รัน
- [ ] Python tests — ยังไม่ได้รันหลัง asyncio.to_thread changes
@@ -0,0 +1,51 @@
# Session — 2026-06-20 (OCR Naming Refactor: typhoon → np-dms-ocr)
## Summary
ทำการ refactor naming conventions ที่เกี่ยวข้องกับ OCR engine จาก "typhoon" เป็น "np-dms-ocr" ทั้ง backend และ frontend อย่างครบถ้วน รวมถึงการ cleanup Tesseract references ที่เหลืออยู่ใน test files และ source code
## การเปลี่ยนแปลง (Changes)
### Backend (7 files + 3 test files)
| ไฟล์ | การเปลี่ยนแปลง |
| --- | --- |
| `sandbox-ocr-engine.service.ts` | `OcrTyphoonOptions``OcrNpDmsOptions`, `typhoonOptions``ocrOptions`, log messages |
| `np-dms-ocr-processor.ts` | Import `OcrNpDmsOptions`, field `typhoonOptions``ocrOptions` |
| `ai.controller.ts` | `typhoonOptions``ocrOptions` ใน `submitSandboxOcr` |
| `ai-batch.processor.ts` | Import + all `typhoonOptions``ocrOptions` (5 locations) |
| `ai-queue.service.ts` | Field `typhoonOptions``ocrOptions` ใน payload type + job data |
| `ocr.service.ts` | `OcrDetectionInput.typhoonOptions``ocrOptions`, override logic |
| `ai.module.ts` | Change log comment: `TyphoonOcrProcessor``NpDmsOcrProcessor` |
| `ai-batch.processor.spec.ts` | Test assertion: `typhoonOptions``ocrOptions` |
| `ocr.service.spec.ts` | Test input: `typhoonOptions``ocrOptions` |
| `sandbox-ocr-engine.service.spec.ts` | Test description: `typhoonOptions``ocrOptions` |
### Frontend (7 files)
| ไฟล์ | การเปลี่ยนแปลง |
| --- | --- |
| `admin-ai.service.ts` | Param `typhoonOptions``ocrOptions` ใน `submitSandboxOcr` |
| `OcrSandboxPromptManager.tsx` | State vars `typhoon*``ocr*`, UI label "Typhoon OCR Options" → "OCR Options", engineType mapping `tesseract``fast_path``auto`, ลบ dead `typhoon_ocr` check, fallback label "Tesseract" → "Fast Path (OCR)" |
| `OcrEngineSelector.tsx` | `isTyphoon``isAiPowered`, ลบ dead `typhoon_ocr` check |
| `SandboxTestArea.tsx` | UI labels "Typhoon OCR" → "np-dms-ocr" |
| `page.tsx` | ลบ `name.includes('typhoon')` 4 จุด, เปลี่ยน `name.includes('ocr')``name.includes(OCR_MODEL_NAME)` 4 จุด |
| `ocr-engine-selector.test.tsx` | Mock Tesseract → Fast Path (PyMuPDF), assertions อัปเดต |
| `OcrEngineSelector.test.tsx` | Mock Tesseract → Fast Path (PyMuPDF), assertions อัปเดต |
| `ocr-sandbox-prompt-manager.test.tsx` | Mock `engineType: 'typhoon_ocr'``'np_dms_ocr'` |
## กฎที่ Lock แล้ว
- **D28 (เดิม):** Canonical naming: `np-dms-ai` (LLM), `np-dms-ocr` (OCR), `fast-path` (PyMuPDF) — ครบทุก layer แล้ว
- **เพิ่มเติม:** Frontend model name normalization ใช้ constants `OCR_MODEL_NAME` และ `MAIN_MODEL_NAME` เท่านั้น — ห้าม hardcoded strings
- **เพิ่มเติม:** Backend `OcrEngineType` enum มีแค่ `FAST_PATH` และ `NP_DMS_OCR` — ไม่มี `TESSERACT` หรือ `TYPHOON_OCR` แล้ว
## Verification
- [ ] `pnpm --filter backend exec tsc --noEmit` — ยังไม่ได้รัน
- [ ] `pnpm --filter lcbp3-frontend exec tsc --noEmit` — ยังไม่ได้รัน
- [ ] Backend unit tests — ยังไม่ได้รัน
- [ ] Frontend unit tests — ยังไม่ได้รัน
- [x] `grep_search` สำหรับ `typhoon|Typhoon|TYPHOON` ใน frontend — เหลือเฉพาะ change log comments
- [x] `grep_search` สำหรับ `tesseract|Tesseract` ใน frontend — เหลือเฉพาะ change log comments
- [x] `grep_search` สำหรับ `typhoonOptions|OcrTyphoonOptions|QUEUE_TYPHOON_OCR|TyphoonOcrProcessor` ใน backend — ไม่พบ
@@ -0,0 +1,41 @@
<!-- File: specs/88-logs/session-2026-06-20-ocr-sidecar-refactor-adr.md -->
# Session — 2026-06-20 (OCR Sidecar Refactor & Server Consolidation ADRs)
## Summary
สร้าง ADR-040 (OCR Sidecar Refactor) และ ADR-041 (Server Consolidation) โดย reconcile 2 แผน refactor (Claude + Qwen) กับ canonical specs (AGENTS.md, CONTEXT.md, ADR-033/034/036) และอัปเดต CONTEXT.md flagged ambiguities
## ปัญหาที่พบ (Root Cause)
- แผน refactor ทั้งสอง (Claude + Qwen) มี conflicts กับ resolved policies:
- ลบ `vram_monitor.py` / `residency_policy.py` → ละเมิด Adaptive OCR Residency + CPU Fallback Retrieval
- Force BGE+Reranker GPU-resident → ละเมิด LLM-First GPU Ownership
- Fixed `keep_alive` → ละเมิด ADR-036 Gap-2 (keep_alive เป็น lazy resource param)
- Cross-host trust gap: sidecar อยู่บน Desk-5439, backend อยู่บน QNAP → "Docker internal isolation" เป็นเท็จ
## การแก้ไข (Fix)
| ไฟล์ | การเปลี่ยนแปลง |
| ----- | ----------------- |
| `specs/06-Decision-Records/ADR-040-ocr-sidecar-refactor.md` | สร้าง ADR ใหม่สำหรับ OCR sidecar refactor — preserve GPU policies, async I/O, path hardening, network isolation (supersedes ADR-033 §7) |
| `specs/06-Decision-Records/ADR-041-server-consolidation.md` | สร้าง ADR ใหม่สำหรับ server consolidation — single Docker host, ASUSTOR as Primary NAS, QNAP as backup |
| `CONTEXT.md` | เพิ่ม 2 resolved ambiguities: OCR Sidecar X-API-Key (network isolation only), Cross-host trust gap (server consolidation) |
## กฎที่ Lock แล้ว
| ID | Decision | ADR |
| -- | -------- | --- |
| D21 | OCR Sidecar = Pure Compute Worker — orchestration/params อยู่ใน backend existing services | ADR-040 D1 |
| D22 | Wire `calculate_ocr_residency()` ใน `process_ocr` — keep_alive เป็น lazy resource param (ADR-036 Gap-2) | ADR-040 D3 |
| D23 | Retain vram_monitor + CPU-fallback for `/embed`,`/rerank` — ห้าม force GPU-resident | ADR-040 D4 |
| D24 | Remove X-API-Key — auth = network isolation (supersedes ADR-033 §7) | ADR-040 D5 |
| D25 | Server Consolidation — co-locate ทุก services บน single Docker host (Ryzen 5 5600 / 32GB / RTX 5060 Ti 16GB) | ADR-041 D1 |
| D26 | ASUSTOR (192.168.10.9) = Primary NAS, QNAP = Backup server | ADR-041 D2 |
| D27 | Docker-internal network only for sidecar/Ollama — enables ADR-040 D5 network-only auth | ADR-041 D3 |
## Verification
- [ ] ADR-040 และ ADR-041 ถูก review และ approve
- [ ] Implementation tasks ใน ADR-040/041 ถูก execute
- [ ] Server consolidation cutover สำเร็จ
- [ ] X-API-Key removal สำเร็จหลัง consolidation cutover
@@ -0,0 +1,67 @@
# Session — 2026-06-20 (OCR Sidecar Refactor Phase 6-9 Implementation)
## Summary
Implemented Phases 6, 8, and 9 of the OCR Sidecar Refactor (Speckit-140) following ADR-040. Phase 6 refactored the sidecar to async I/O with lifespan context manager. Phase 8 removed the unused `/normalize` endpoint. Phase 9 polished Dockerfile, docker-compose.yml, created README.md, and validated quickstart.md. All 19 Python tests pass.
## ปัญหาที่พบ (Root Cause)
ไม่มี bug ใน session นี้ — เป็นการ implement feature ใหม่ตาม ADR-040:
- **Sync I/O bottleneck**: `process_ocr` ใช้ `httpx.Client` แบบ sync ทำให้ block FastAPI event loop
- **Stale startup pattern**: `@app.on_event("startup")` deprecate แล้วใน FastAPI 0.111+
- **Unused /normalize endpoint**: ไม่มี consumers ใน backend codebase
- **Stale Docker config**: `OCR_LANG`, `USE_GPU` เป็น Tesseract config ที่ไม่ใช้แล้ว
- **Missing curl**: Dockerfile ไม่มี `curl` ทำให้ HEALTHCHECK ล้มเหลว
## การแก้ไข (Fix)
### Phase 6: Async I/O Performance (T041-T046)
| ไฟล์ | การเปลี่ยนแปลง |
|------|----------------|
| `specs/04-Infrastructure-OPS/.../ocr-sidecar/app.py` | `process_ocr``async def`, `_process_pdf_doc``async def`, `/ocr` + `/ocr-upload``async def` |
| `app.py` | เพิ่ม `ollama_client` global (`httpx.AsyncClient`) สร้างใน lifespan context manager |
| `app.py` | แทน `@app.on_event("startup")` ด้วย `@asynccontextmanager lifespan` |
| `app.py` | Model loading ผ่าน `asyncio.to_thread(_load_bge_models)` |
| `app.py` | แทน `httpx.Client` ด้วย `await client.post()` (AsyncClient) |
| `tests/integration/ocr-sidecar/test_async_performance.py` | **New file**: 6 tests (coroutine check, lifespan, ollama_client global, /normalize removed, concurrent requests) |
| `tests/unit/ocr-sidecar/test_residency_wiring.py` | Updated: `FakeClient``FakeAsyncClient`, sync → `asyncio.run()` |
| `tests/integration/ocr-sidecar/test_parameter_governance.py` | Updated: async mock, patch `ollama_client` |
| `tests/integration/ocr-sidecar/test_active_prompt.py` | Updated: async mock, patch `ollama_client` |
### Phase 8: Remove /normalize (T054-T055)
| ไฟล์ | การเปลี่ยนแปลง |
|------|----------------|
| `app.py` | ลบ `NormalizeRequest`, `NormalizeResponse`, `/normalize` endpoint, `pythainlp` imports |
| `requirements.txt` | ลบ `pythainlp==5.0.4` และ `Pillow==10.0.0` |
| (grep verified) | ไม่มี `/normalize` หรือ `THAI_PREPROCESS_URL` consumers ใน backend |
### Phase 9: Polish (T056-T063)
| ไฟล์ | การเปลี่ยนแปลง |
|------|----------------|
| `Dockerfile` | เพิ่ม `curl` สำหรับ HEALTHCHECK, change log entry |
| `docker-compose.yml` | ลบ `OCR_LANG`, `USE_GPU`; เพิ่ม `OCR_SIDECAR_API_KEY`, `OCR_ACTIVE_PROFILE` |
| `README.md` | **New file**: architecture, endpoints, env vars, deploy guide, test coverage |
| `quickstart.md` | แก้ stale requirements (ลบ pythainlp/Pillow), แก้ `TYPHOON_OCR_MODEL``OCR_MODEL` |
| `tasks.md` | Mark T041-T046, T054-T063 as `[x]` |
## กฎที่ Lock แล้ว
- **Async I/O pattern**: `process_ocr` ต้องเป็น `async def` และใช้ `httpx.AsyncClient` ผ่าน `ollama_client` global (สร้างใน lifespan)
- **Lifespan over startup event**: ใช้ `@asynccontextmanager lifespan` แทน `@app.on_event("startup")` — deprecate แล้วใน FastAPI 0.111+
- **Model loading non-blocking**: ใช้ `asyncio.to_thread()` สำหรับ model loading ใน lifespan เพื่อไม่ block startup
- **No /normalize**: endpoint ถูกลบแล้ว — ไม่มี consumers ใน backend
- **Test mock pattern**: ใช้ `FakeAsyncClient` (async `post()` + `aclose()`) แทน `FakeClient` (sync) สำหรับทุก test ที่ mock Ollama API
## Verification
- [x] `python -m pytest tests/ -v`**19/19 tests passed** in 4.81s
- [x] `test_process_ocr_is_coroutine_function` — process_ocr เป็น async ✅
- [x] `test_process_pdf_doc_is_coroutine_function` — _process_pdf_doc เป็น async ✅
- [x] `test_app_uses_lifespan_not_startup_event` — ใช้ lifespan ไม่ใช่ on_event ✅
- [x] `test_app_has_async_client_global` — ollama_client global มีอยู่ ✅
- [x] `test_normalize_endpoint_removed` — /normalize ถูกลบแล้ว ✅
- [x] `test_concurrent_ocr_requests_dont_block` — 3 concurrent requests สำเร็จ ✅
- [x] Existing tests (path traversal, API key, residency, CPU fallback, parameter governance, active prompt) — ทั้งหมดผ่าน ✅
@@ -0,0 +1,59 @@
# Session — 2026-06-20 (OCR Sidecar Refactor Speckit Workflow)
## Summary
Executed complete `/speckit.prepare` workflow for OCR Sidecar Refactor (ADR-040). Generated spec.md, plan.md, research.md, data-model.md, contracts/sidecar-api.md, quickstart.md, and tasks.md. Performed consistency analysis and fixed all identified issues (1 CRITICAL, 2 MEDIUM, 2 LOW).
## ปัญหาที่พบ (Root Cause)
None. This was a planning/specification workflow, not a bug fix session.
## การแก้ไข (Fix)
N/A - Specification generation workflow.
## สิ่งที่ทำใน Session
| ไฟล์ | การเปลี่ยนแปลง |
| ----- | ------------------ |
| `specs/100-Infrastructures/140-ocr-sidecar-refactor/spec.md` | Created feature specification with 5 user stories, 20 functional requirements, 8 success criteria |
| `specs/100-Infrastructures/140-ocr-sidecar-refactor/plan.md` | Created implementation plan with technical context, constitution check, and phase structure |
| `specs/100-Infrastructures/140-ocr-sidecar-refactor/research.md` | Created technical decisions documentation from ADR-040 |
| `specs/100-Infrastructures/140-ocr-sidecar-refactor/data-model.md` | Created data contracts and entity relationships |
| `specs/100-Infrastructures/140-ocr-sidecar-refactor/contracts/sidecar-api.md` | Created sidecar API specification |
| `specs/100-Infrastructures/140-ocr-sidecar-refactor/quickstart.md` | Created deployment and testing guide |
| `specs/100-Infrastructures/140-ocr-sidecar-refactor/tasks.md` | Created 63 implementation tasks organized by user story |
| `specs/100-Infrastructures/140-ocr-sidecar-refactor/checklists/requirements.md` | Created specification quality validation checklist |
## Analysis & Fixes
| Issue | Severity | Fix |
| ----- | -------- | --- |
| C1 - Constitution Check ADR-019 | CRITICAL | Updated plan.md to acknowledge ADR-019 applies to backend services (parameter resolution in OcrService/SandboxOcrEngineService) |
| U1 - Symlink resolution edge case | MEDIUM | Updated spec.md edge case to reference test T007 |
| U2 - Ollama unavailability edge case | MEDIUM | Updated spec.md edge case to note handled by FastAPI exception handling per ADR-007 |
| I1 - IP address inconsistency | LOW | Standardized IP to 192.168.10.100 in spec.md and plan.md |
| I2 - Task description clarity | LOW | Changed tasks T022/T023 from "Verify" to "Retain" |
## กฎที่ Lock แล้ว
- OCR sidecar is a pure compute worker (no DB/storage access per ADR-023/023A)
- Backend services handle all parameter governance (ai_execution_profiles, ai_prompts)
- Adaptive OCR Residency must be preserved (vram_monitor.py, residency_policy.py retained)
- CPU fallback for BGE-M3/FlagReranker must be preserved
- Phase 2 (X-API-Key removal) is BLOCKED until ADR-041 consolidation completes
## Verification
- [x] All 5 user stories have acceptance criteria
- [x] All 20 functional requirements have task coverage (100%)
- [x] Constitution check passes with proper ADR-019 acknowledgment
- [x] No ambiguities or duplications found
- [x] All 5 analysis issues fixed
- [x] Ready for `/speckit-implement`
## Next Steps
- Execute `/speckit-implement` to begin implementation
- Start with MVP (User Stories 1-2: Security Hardening + GPU Resource Management)
- User Story 5 (Network Isolation Auth Phase 2) remains BLOCKED until ADR-041 consolidation