eCourt-Doc-Edit/docs/acceptance-review.md
Suphonchai Phoonsawat 70f71f4c5c Phase 12 v9: draft opens in Word + Thai word-breaking fix (FR-16)
แก้ตามผลใช้งานจริง 2 ประเด็น:
1. เปิดใน Word ทันที — generate-draft เปลี่ยนจากคืนไฟล์ตรงๆ เป็นเก็บ
   ชั่วคราวใต้ drafts/ (lifecycle ลบรายวัน, config Minio__DraftRetentionDays)
   + คืน presigned URL ให้เว็บเปิดผ่าน ms-word:ofe พร้อมลิงก์สำรอง
   (useVersionOpen.openDocxUrl แยกแกนเปิด docx URL ใช้ร่วมกับ flow v0)
2. ตัดคำไทย — T14 แตก placeholder เป็น [ / ข้อความ / ] โดย run ข้อความ
   ถือ w:hint="cs" + w:cs เท่านั้น; เดิมเขียนค่าลง run ปีกกาทำให้ Word
   จัดหน้าไทยผิด — แก้เป็นเขียนที่ run ข้อความยาวสุด + SDT เปล่าสืบทอด
   rPr จาก sdtPr (ใช้ร่วมทั้ง generate และ generate-draft)

- ถอดของที่เลิกใช้: CORS expose Content-Disposition, downloadFile init,
  useApi.spec · Spec v9 + ADR 0002 Amendment + acceptance-review AC-19
- Tests: backend 84 / web 56 + build; QA reviewer PASS (2 rounds, 10 findings
  แก้ครบ — รวม bug จริงที่ test ใหม่จับได้: lifecycle Rules null ทำ startup
  ตั้ง rule ล้มแบบเงียบ) · ค้าง manual บน Word จริง

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-10 11:49:16 +07:00

169 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

# ตรวจ Acceptance Criteria 1–19 (Phase 8 + 9 + 10 + 11 + 12)
> วันที่ตรวจ: 2026-08-20 (AC 1–16) · 2026-08-22 (AC-17) · 2026-09-08 (AC-18) · 2026-09-10 (AC-19) · อ้างอิง AC จาก [`specs/spec-officejs.md`](../specs/spec-officejs.md) ส่วน 10
>
> สัญลักษณ์: ✅ ผ่าน (มี automated test คุ้ม) · 🖐️ ผ่านจากการทดสอบด้วยมือ · 🔶 ผ่านบางส่วน/รอยืนยันด้วยมือบน platform ที่เหลือ · ❌ ไม่ผ่าน
สรุปรวม: **AC ทั้ง 17 ข้อผ่านในระดับที่ตรวจอัตโนมัติได้ครบ** (backend ครอบทุก flow ด้วย integration tests บน PostgreSQL + MinIO + Keycloak จริงผ่าน Testcontainers) — ส่วนที่เหลือคือการยืนยันด้วยมือบน browser/Word จริง (โดยเฉพาะ Word Online + Windows) ตามรายการท้ายเอกสาร
## ผลทีละข้อ
### AC-1 — Sideload บน Word Desktop และ Word Online ได้ทั้งคู่ ไม่มี error ตอนโหลด task pane — 🔶
- 🖐️ **Word for Mac (Desktop): ผ่าน** — sideload ผ่าน `npm start` (manifest symlink) ตรวจตอน Phase 5 พร้อมแก้ปัญหาตกมุม 4 รอบ (dialog https / slice format / mixed content / blob download)
- ✅ อัตโนมัติ: `npm run build` + `npm run lint` + `npm run validate` (manifest) ผ่านทุกครั้ง
- 🔶 **Word Online + Word for Windows: ยังไม่ได้ทดสอบ** (ต้องมีเครื่อง/บัญชีทดสอบ)
### AC-2 — เปิดครั้งแรกยังไม่ login → Sign in → dialog → login สำเร็จ dialog ปิดเอง ได้ token — ✅ (automated) + 🖐️ (Mac)
- ✅ `authService.test.ts` (14 tests): PKCE pair format, state validation, token exchange, refresh, dialog message flow
- 🖐️ ทดสอบจริงบน Word for Mac ผ่าน (Keycloak https:8443 + Office Dialog API)
### AC-3 — เรียก backend โดยไม่แนบ token → 401 — ✅
- `AuthTests` (xUnit + Keycloak จริง): ครบทุก endpoint ทั้ง GET/POST + กรณี token ขยะ + กรณี token จริงได้ 200
### AC-4 — ไฟล์ใหม่ไม่เคย track → ปุ่ม "Start Versioning This Document" — ✅
- `metadataService.test.ts`: ตรวจ `SavedByAddIn` เทียบ ADDIN_SIGNATURE_GUID → state `untracked` (App state machine)
- 🖐️ ยืนยันแล้วบน Word for Mac ตอน Phase 5
### AC-5 — Start Versioning → stamp metadata, สร้าง Document, v1, `CreatedByUserId` ตรง `sub` ใน JWT — ✅
- `UploadVersionTests.FirstUpload_CreatesDocumentAndVersionRow_FromJwtClaims`: token จริงจาก Keycloak → ตรวจ row ใน Postgres ตรงๆ ว่า `CreatedByUserId == sub` + SHA256 + object key format ถูกต้อง
### AC-6 — ปิดแล้วเปิดไฟล์เดิม → ตรวจพบ tracked + โหลด history ถูก — ✅
- `UploadVersionTests.SecondUpload_...` + `GetVersions` ordering (ใหม่ → เก่า) + snapshot ต่อ version
- 🖐️ ยืนยันบน Mac แล้ว (round 3 ของ Phase 5)
### AC-7 — แก้ไขแล้ว Save & Version → version ใหม่ + `LastVersionId`/`LastVersionTimestamp` อัปเดต — ✅
- `versionFlow.test.ts` (7 ขั้นตาม spec ครบ) + metadataService stamp tests + `SecondUpload_IncrementsVersionNumber`
- หมายเหตุจาก Phase 5: `LastVersionId` ในไฟล์ = `clientVersionId` (ต้อง stamp ก่อน `document.save()`)
### AC-8 — ดาวน์โหลด version เก่าได้ไฟล์ที่เปิดได้ เนื้อหาตรงตอน upload — ✅
- `UploadVersionTests.Download_ReturnsExactUploadedBytes_WithDocxContentType`: byte-for-byte ผ่าน MinIO ไป-กลับ + content-type ถูก
- 🖐️ บน Mac ใช้ presigned link + `openBrowserWindow` (แก้จาก blob download ที่ WKWebView ทิ้งเงียบๆ) — ผ่านการตรวจ curl แล้ว ยืนยัน click จริงอีกครั้งพร้อม Word Online
### AC-9 — token หมดอายุกลาง session → silent refresh ผู้ใช้ไม่รู้สึก — ✅
- `apiClient.test.ts`: 401 → refresh → retry ครั้งเดียว → ยัง 401 → สัญญาณกลับหน้า sign-in · `useAuth`/`useApi` ของ web ใช้พฤติกรรมเดียวกัน
- 🔶 รอยืนยันด้วยมือ (ปล่อย session ทิ้งไว้จริง)
### AC-10 — XML Part fail silent บน Mac → ระบบยังทำงานถูก (backend = source of truth) — ✅
- `metadataService.test.ts`: XML write fail → catch ไม่ throw · ทุก history อ่านจาก backend เสมอ
### AC-11 — upload template → detect fields อัตโนมัติ + บันทึก definitions — ✅
- `TemplateTests`: SDT ครบทุกรูปแบบ (run เดี่ยว / multi-run คนละ formatting / block / tag ซ้ำ → dedup) + defaults (label=tag, text, required=false) + PUT บันทึกนิยาม + reject key นอกไฟล์/type ผิด
- 🖐️ หน้าเว็บอัปโหลด: ตรวจ SSR + จำลอง request sequence แล้ว (Phase 6) รอ click-through บน browser
### AC-12 — กรอก form บนเว็บ → docx ค่าครบ formatting ไม่พัง → เปิดใน Word เห็น tracked + v1 ทันที — ✅ (backend) + 🔶 (เต็มรูปแบบรอมือ)
- `GenerateTests.Generate_FillsAllFields_...`: ค่าครบทุก SDT + **multi-run รวมเป็น run เดียวโดยรักษา bold ของ run แรก** + custom properties (`SavedByAddIn` = GUID ตรงเป๊ะ, `DocumentTrackingId` = documentId) + Document/v1/field values ใน Postgres ครบ
- 🖐️ ส่วน "เปิดใน Word แล้ว add-in เห็น v1 ทันที": ตรวจด้วย unzip/OpenXML แล้ว ต้องยืนยัน click จริงบน Word
### AC-13 — แก้ค่าใน content control แล้ว Save & Version → field value ใน DB เปลี่ยน + snapshot ต่อ version — ✅
- `FieldSyncTests.UploadWithChangedFields_...`: latest values อัปเดตเฉพาะ field ที่เปลี่ยน (`SourceVersionId` ชี้ v2) + snapshot v1/v2 เก็บค่าต่างกันตามเวลา (diff ได้)
### AC-14 — ลบ content control แล้ว Save & Version → flow สำเร็จ ค่าเดิมคงอยู่ mark missing ไม่ error — ✅
- `FieldSyncTests.UploadWithMissingFields_...`: request 201 · field ที่หายคงค่า v1 + `SourceVersionId` ไม่ขยับ · snapshot `missing:true` พร้อมค่าเดิม · field เกินนิยามถูก ignore ไม่เก็บ
### AC-15 — หน้ารายการเอกสารของ template + detail (fields + history + snapshot ต่อ version + ดาวน์โหลด) + re-generate เป็น v1 โดยต้นทางไม่ถูกแตะ — ✅
- `GenerateTests.ListDocuments_ShowsVersionCounts_LatestFirst` (versionCount/Latest + เรียงล่าสุดก่อน) + `GetDetail_MergesDefsAndValues_BothOrphanWays` (merge นิยาม × ค่า รองรับ orphan สองทาง) + `GetDetail_OfStandaloneDocument`
- Re-generate: ผ่าน composition เดิม (detail → prefill → generate) — ตรวจ curl จริง Phase 6.4 ว่า **versionCount ของเอกสารต้นทางคงเดิม** + `TemplateTests.Deactivate_...` ยืนยัน policy deactivate
- 🖐️ หน้าเว็บ: SSR ทุก route 200 แล้ว รอ click-through
### AC-16 — (v5) พิมพ์คำอธิบาย (commit message) ตอนสร้าง version ได้ทุก flow → เก็บกับ version นั้น + แสดงใน history ทุกหน้า · เว้นว่างได้ — ✅ (backend + service) + 🔶 (dialog ใน Word รอมือ)
- ✅ `VersionMessageTests` (backend): upload พร้อม message → ตรวจ row ใน Postgres + echo ใน history · blank/whitespace → null · trim · oversize 2049 → 400 ไม่มี side effect · ยาวพอดี 2048 ผ่าน · ต่อ version มี message ของตัวเอง · generate v1 พร้อม message · generate oversize → 400 ก่อนแตะ MinIO
- ✅ `versionFlow.test.ts` (addin): `saveAndVersion`/`startVersioning` แนบ `message` ใน multipart · whitespace ล้วน → ไม่แนบฟิลด์
- ✅ Web: `VersionListItem.message` + build ผ่าน (type-only ตาม convention ไม่มี component test)
- 🔶 dialog + textarea ใน task pane (Save & Version / Start Versioning) และการแสดงใน `VersionHistoryPanel` ต้องยืนยันด้วยมือบน Word จริง
### AC-17 — (v6) admin เลือก editor ต่อ field ได้ (text → textbox/textarea) → ฟอร์มสร้างเอกสารใช้ control ตาม editor · number/date ใช้ control ตาม type · editor ผิด → 400 · ไม่ส่ง → default — ✅ (backend + web unit) + 🔶 (browser รอมือ)
- ✅ `FieldEditorTests` (backend): PUT editor=textarea → persist + echo · ไม่ส่ง → resolve default ตาม type + DB เก็บ null · ส่งค่า = default → normalize เป็น null · editor ไม่รู้จัก → 400 · ไม่เข้ากับ type (3 คู่) → 400 · tag ใหม่จาก replace ได้ default
- ✅ `fieldEditor.spec.ts` (web): `defaultEditor`/`resolveEditor`/`editorOptions`/`dateInputValue` (prefill re-generate ของ field date จาก timestamp เต็ม)
- 🔶 Click-through บน browser: admin editor select (desktop + mobile) · ฟอร์มสร้างเอกสาร render textarea/number/date ตาม editor
## ชุด test ที่คุ้ม AC (สรุป)
| ชุด | จำนวน | ครอบ |
|---|---|---|
| backend integration (Testcontainers: Postgres + MinIO + Keycloak จริง) | 60 | AC-3, 5, 6, 7 (backend), 8, 11–17 |
| addin unit (Jest) | 50 | AC-2, 4, 7 (client flow), 9, 10, 16 (ส่วน form) |
| web unit (Vitest) | 46 | AC-11/12 (validation ฝั่งฟอร์ม), theme, snapshot rendering, AC-17 (ส่วน editor util) |
## ปัญหาที่เจอระหว่างไล่ AC (และการแก้)
1. **Keycloak ใน Testcontainers พร้อมช้ากว่า port เปิด** — docker-proxy รับ connection ก่อนแล้วตัดสาย ("response ended prematurely") → fixture poll discovery endpoint จนได้ 200 จริง + แนบ container logs ตอน timeout
2. **`WithResourceMapping` ของ Testcontainers 4.14 รับ target เป็น directory** — ชี้ path ไฟล์ตรงๆ ทำให้ realm ไม่ถูก import (discovery 404 ตลอด 2 นาที) → ชี้ `/opt/keycloak/data/import` แล้วไฟล์เข้าไปชื่อเดิม
3. **Keycloak 26 บังคับ HTTPS บน token endpoint ของ master realm แม้ใน container http** → เลิกใช้ master admin API สร้าง test user ตามแผน ใช้ `testuser` ที่มากับ realm import + password grant แทน (backend ยัง validate JWKS/issuer/audience เต็มขั้น)
4. **template .docx ที่ test สร้างเองผิด schema** — วาง run-level SDT เป็นลูกตรงของ Body (OOXML ห้าม; Word วางไว้ใน paragraph เสมอ) ตอน reload กลายเป็น SdtBlock + unknown elements ทั้งชุด ทำให้ generate เข้า branch ผิด → แก้ `TestDocx` ให้ถูก schema (service ฝั่ง backend ถูกอยู่แล้ว — ยืนยันด้วย template จริงตั้งแต่ Phase 3)
5. **`import.meta.client` ของ Nuxt ไม่ถูก replace ใน Vitest** (vite-node ไม่ apply define กับ import.meta.*) → `useTheme` เปลี่ยน guard เป็น `typeof window !== 'undefined'` (semantics เทียบเท่าใน SSR/client)
6. **ยืนยัน behavior ผ่าน test (ไม่ใช่ bug):** field sync update latest value ของทุก field ที่ "ส่งมา" แม้ค่าไม่เปลี่ยน (`SourceVersionId` ขยับทุกตัวที่ส่ง) — เฉพาะ field ที่ "ไม่ได้ส่ง" เท่านั้นที่คงค่า+source เดิม ตาม policy FR-11
7. (ย้อนอดีต — จาก Phase 5) Office Dialog API ปฏิเสธ http, slice.data หลายรูปแบบ, WKWebView บล็อก mixed content + blob download — แก้ครบแล้ว มี regression coverage ใน unit tests
## ที่เหลือ (ต้องทำด้วยมือ — ทำแทนไม่ได้)
- [ ] Word Online: sideload + login + full flow (AC-1, 2, 4–8 บน Online)
- [ ] Word for Windows (ถ้ามีเครื่อง)
- [ ] ปล่อย session จริงจน token หมดอายุ → ยืนยัน silent refresh (AC-9)
- [ ] Click-through บน browser: สร้าง/แก้ template → สร้างเอกสาร → ดาวน์โหลด → เปิดใน Word ยืนยัน add-in เห็น v1 ทันที (AC-11, 12) + ดาวน์โหลด version ผ่าน presigned link บน Mac อีกครั้ง (AC-8)
- [ ] Theme toggle จำค่าหลัง reload บน browser จริง (Phase 6 DoD ที่เหลือ)
- [ ] (v5) ใน Word จริง: textarea ใน dialog ของทั้ง Save & Version และ Start Versioning + ข้อความโชว์ใน `VersionHistoryPanel` (AC-16) · และบน browser: ฟอร์มสร้างเอกสารพร้อม message + แสดงใน history ทั้ง desktop/mobille
- [ ] (v6) บน browser: หน้าแก้ template เลือก editor `textarea` (desktop + mobile) · ฟอร์มสร้างเอกสาร render textarea/ช่องตัวเลข/date picker ตาม editor · re-generate เอกสารที่ field date เก็บ timestamp เต็มแล้วช่อง date แสดงวันที่ถูกต้อง (AC-17)
## AC-18 — Version PDF (v7)
**ผล: ✅ ผ่าน — automated verification + 🖐️ manual acceptance บน Word for Mac (2026-09-08)**. AC เดิมที่กล่าว generate=v1 ให้อ่านเป็น v0 ตาม spec v7; Start Versioning ยังคงเริ่ม v1.
| เกณฑ์ | หลักฐาน |
|---|---|
| generate v0 ไม่มี PDF → upload v1 มี PDF | `VersionPdfTests.Generate_V0WithoutPdf_ThenUpload_V1WithPdf`; migration `AddVersionPdf` ผ่าน PostgreSQL จริง |
| PDF sibling key, hasPdf, bytes/type/ชื่อไทย, presigned signature | `Upload_WithPdf_PersistsSiblingKey_AndDownloadsExactBytes` อ่าน proxy และ presigned URL จาก MinIO จริงทั้ง PDF/docx |
| ไม่มี PDF → false + 404 ทั้งสอง download endpoint | `Upload_WithoutPdf_HasFalse_AndBothPdfDownloadsReturn404` |
| นามสกุล/type ผิดหรือว่าง → 400 ก่อนสร้าง document | `InvalidPdf_Returns400_BeforeCreatingDocument` (3 cases) |
| PDF storage ล้ม → 201/false; docx storage ล้ม → 502/rollback | `StorageFailure_PdfIsBestEffort_DocxStillRollsBack` (2 cases; fault injection + PostgreSQL จริง) |
| ทั้งสอง add-in flows capture docx → PDF, fail/empty ไม่บล็อก upload | `versionFlow.test.ts` FR-15 matrix: start/save × success/failure/empty/storage-failure |
| PDF runtime guard, normalize slices, ปิด File object | `fileService.test.ts` PDF cases; manifest ไม่เพิ่ม requirement |
| multipart PDF + format query | `versioningService.test.ts` |
| URL ms-word คง signed URL, v0 filename, PDF path | `versionDownload.spec.ts` |
| PDF popup/fallback/reset/error และ Word timeout cleanup | `useVersionOpen.spec.ts` 6 lifecycle tests |
ชุดเต็ม: backend **68 ผ่าน**, add-in **63 ผ่าน** + build/lint ผ่าน, web **55 ผ่าน** + production build ผ่าน. Add-in build มี webpack bundle-size warnings; ไม่มี compile/lint errors.
### ยืนยันด้วยมือ — ผ่านบน Word for Mac (2026-09-08)
- [x] Mac: เว็บ v0 → เปิดใน Word → Save & Version → v1 + PDF ไม่มี warning, ปุ่ม PDF เฉพาะ v1 · Start Versioning → v1 + PDF ทันที
- [x] เทียบ PDF กับ Word Save As PDF: ฟอนต์ไทย TH Sarabun New render + embed, หัวท้ายกระดาษ — ตรง
- [x] Track Changes เปิดทิ้งไว้ → Save & Version → **PDF ไม่มี markup ปน** — ไม่มีข้อจำกัดต้องบันทึกเพิ่มใน spec
- [x] Warning path เมื่อ PDF capture ล้ม; ปุ่ม PDF ขึ้นเฉพาะ version ที่มี
- [x] เว็บ: docx เปิด Word ผ่าน `ms-word` พร้อม fallback ลิงก์สำรอง, PDF เปิดแท็บใหม่, ชื่อไฟล์สวยทั้งสองกรณี
- [ ] ค้างตามเดิม (ต้องมีเครื่อง/บัญชีทดสอบ): **Word for Windows + Word on the web** — sideload + full flow; เป็นข้อค้างเดิมตั้งแต่ AC-1 ไม่ใช่ของใหม่ใน AC-18
รายการเต็ม: [แผน FR-15](plans/version-pdf.md). ไม่มีการอ้างว่า unit tests ยืนยัน layout/Word protocol handler ได้.
QA reviewer: **PASS**, 2 rounds, 3 findings fixed (build compatibility, pending PDF tab cleanup, PDF success copy); 2026-09-08. Manual acceptance ผ่านบน Word for Mac 2026-09-08 — **Phase 11 ปิดแล้ว**; ค้างเฉพาะ Word for Windows / Word on the web ตามรายการด้านบน.
## AC-19 — ไฟล์ร่าง (v8 · ปรับ v9)
**ผล: 🔶 ผ่านด้วย automated tests — ค้างยืนยันด้วยมือบน Word จริง** (ตาม pattern เดิม: unit/integration tests ยืนยัน contract และข้อมูลใน DB ได้ แต่ไม่ยืนยันพฤติกรรม add-in/การเรนเดอร์ Word จริงได้)
| เกณฑ์ | หลักฐาน |
|---|---|
| สร้างไฟล์ร่างแล้วไม่เกิด Document/version/field values ใน DB + ค่าครบ + formatting รักษาไว้ + **presigned URL ดึงไฟล์จริงได้ทันที** (v9) | `DraftTests.Draft_FillsAllFields_PreservesFormatting_StampsOnlyTemplateId_CreatesNothing` |
| stamp เฉพาะ `TemplateId` — ไม่มี SavedByAddIn/DocumentTrackingId | test เดียวกัน (อ่าน custom properties จากไฟล์จริง) |
| **ค่าที่แทนที่ได้ complex script ของ run ข้อความไทย (w:hint="cs"+w:cs) · SDT เปล่าสืบทอด sdtPr rPr** (v9 — แก้การตัดคำไทย) | `DraftTests.Draft_ThaiPlaceholder_UsesComplexScriptRun_ForWordBreaking` (template จำลองโครงสร้าง T14) |
| validate เหมือน FR-10: required ขาด/type ผิด → 400, unknown → 404, deactivate → 400, ค่าเกินนิยาม ignore | `DraftTests` ที่เหลือ (5 tests) |
| Start Versioning ของไฟล์ร่าง: upload + templateId → **v1** + Document ผูก template + field sync ครบ + snapshot แบบมีนิยาม | `TemplateLinkingTests.Upload_WithTemplateId_OnNewDocument_CreatesLinkedDocV1WithFieldSync` |
| upload ถัดไปไม่ส่ง templateId → v2, TemplateId คงเดิม | test เดียวกัน (upload รอบสอง) |
| templateId ไม่ใช่ GUID / ไม่พบ → 400 (แม้ document มีอยู่แล้ว) | `Upload_TemplateIdNotGuid_Returns400`, `Upload_TemplateIdUnknown_Returns400`, `Upload_ExistingDocument_BadTemplateId_Returns400` |
| template deactivate หลังสร้างไฟล์ร่าง → ยังผูกได้ | `Upload_TemplateIdDeactivated_StillLinks` |
| document เดิม (standalone) ส่ง templateId มา → เฉย | `Upload_ExistingDocument_IgnoresTemplateId` |
| endpoint ใหม่ต้อง [Authorize] | `AuthTests` เพิ่ม `POST api/templates/{0}/generate-draft` |
| add-in: อ่าน TemplateId / ส่งเฉพาะ startVersioning / append form field | `metadataService.test.ts` (ไฟล์ร่างอ่าน TemplateId), `versionFlow.test.ts` (แนบ/ไม่แนบ templateId), `versioningService.test.ts` (form field) |
| เว็บ: เปิด docx URL ที่ถืออยู่เอง (ไฟล์ร่าง) ผ่าน ms-word + fallback + notice | `useVersionOpen.spec.ts` `openDocxUrl` test |
ชุดเต็ม (v9): backend **84 ผ่าน**, add-in **66 ผ่าน**, web **56 ผ่าน** + production build ผ่าน (backend 82 / web 59 ของ v8 + แก้ v9)
### ค้างยืนยันด้วยมือ (ต้องมี Word จริง)
- [ ] Mac: เว็บสร้างไฟล์ร่าง → **Word เปิดไฟล์ทันที** พร้อมลิงก์สำรอง → ตรวจการตัดคำ/จัดหน้าภาษาไทยของ field ที่ค่ายาว (เช่น Detail ของ T14) ตรงตาม template → task pane แสดง untracked + hint ไฟล์ร่าง → Start Versioning → v1 + หน้า document detail ของเว็บแสดง template และค่า fields
- [ ] Word for Windows / Word on the web — ตามข้อค้างเดิมตั้งแต่ AC-1
รายการเต็ม: [แผน FR-16](plans/fr16-draft-generation-plan.md).
QA reviewer: **PASS**, 2 rounds, 8 findings แก้ครบไม่มีที่ถูกปฏิเสธ (2026-09-10) — round 1: blocker `.at(-1)` ใน test ใหม่ตัดกับ tsconfig `lib: es2020` ทำ `addin npm run build` พัง (jest ไม่ type-check จึงรอดมา) + minors 4 (นับจำนวน test ผิดในตารางนี้, stale notices ระหว่างสองปุ่ม submit, AlertBox สำเร็จไม่ใช่ `type="success"`, `isTrackedDocument` ไม่มี production caller → เก็บไว้พร้อมหมายเหตุเหตุผล) · round 2: minors 2 + pre-existing 1 (หัวเอกสาร/สถานะเก่าใน acceptance-review, CLAUDE.md, implementation-plan header). ค้าง manual บน Word จริงตามรายการด้านบน.
QA reviewer รอบ v9 (เปิดใน Word + ตัดคำไทย): **PASS**, 2 rounds, 10 findings แก้ครบไม่มีที่ถูกปฏิเสธ (2026-09-10) — round 1: minors 6 (dead `downloadFile`/`filenameFrom`, glossary "stateless" เก่า, lifecycle config เปลี่ยนแล้วเป็น no-op เงียบๆ + days ไม่ validate, ไม่มี test คุม lifecycle, comment เก่าใน TestDocx, typo ใน spec) · round 2: minors 2 + nits 2 (comment เก่าอีก 2 จุด, "(stateless)" ตกค้าง 2 บรรทัด, lifecycle test เพิ่ม assertion ผ่านค่าคืน `true/false`, ตัว verdict นี้เอง) — และ test ใหม่จับ bug จริงได้: `Configuration.Rules` เป็น null ได้ทำให้ startup ตั้ง rule ล้มแบบเงียบมาตั้งแต่แรก (โดน catch กลืน) แก้ด้วย `?? []`.