ความคืบหน้างาน ไฟล์ต้นฉบับ

tracker/data/tests.json

[
  {
    "id": "R-021",
    "suite": "Mockup workspace: Sitemap / Page flow / Screen Listing",
    "date": "2026-10-31T11:21:44+07:00",
    "summary": "Mockup browser 43 ข้อ + regression 70/25/9 รวม 147 ข้อผ่าน พร้อม Go/race/vet และ artifact validation",
    "groups": [
      {
        "name": "แบบหน้าจอและเส้นทาง",
        "audience": "ba",
        "purpose": "ตรวจหน้าจออ้างอิงร่วมกันและไม่ตีความ mapping ร่างเป็นข้อยืนยัน",
        "cases": [
          {
            "name": "Screen Listing มี 164 screen IDs และ proposal references 6 ภาพ พร้อม detail/variants/provenance/component links",
            "status": "pass"
          },
          {
            "name": "Sitemap ใช้ภาพ shape เรียงบนลงล่าง จำกัดความกว้างแถว และกรอง Module/Channel โดยไม่ลบข้อมูลที่ซ่อน",
            "status": "pass"
          },
          {
            "name": "Page flow เลือกครบ 162 Use cases และรักษา Decision/label/เส้นทางจาก canonical source",
            "status": "pass"
          },
          {
            "name": "W-41 แสดง A01 สร้างบัญชี A05 แนบหลักฐาน และทางข้ามผู้ปกครอง พร้อมภาพ state ของผลลัพธ์",
            "status": "pass"
          },
          {
            "name": "หน้าจอเดิมใช้หลาย occurrence ได้ แต่ละ occurrence เปลี่ยน state และ mapping แยกกัน",
            "status": "pass"
          },
          {
            "name": "Editor และ detail แสดงผลได้บนมือถือ พร้อม grid/zoom/label/arrow editing",
            "status": "pass"
          }
        ]
      },
      {
        "name": "ข้อมูลและการบันทึกร่วม",
        "audience": "tech",
        "purpose": "ตรวจ catalog/API/storage และไม่รบกวนเครื่องมือเดิม",
        "cases": [
          {
            "name": "ตรวจ 170 screen records, 990 preview variants, 6 component contracts/previews และ 162 flow mappings",
            "status": "pass"
          },
          {
            "name": "go test -race -count=1 ./... และ go vet ./... ผ่าน รวม catalog/reference validation ทุก flow",
            "status": "pass"
          },
          {
            "name": "Same-origin write, invalid references, path/symlink confinement และ concurrent stale saves ถูกตรวจ",
            "status": "pass"
          },
          {
            "name": "Saved graph ไม่ถูกแทนที่เมื่อ source เปลี่ยน และ JSON เสียไม่ถูก reset เงียบ",
            "status": "pass"
          },
          {
            "name": "Save/Restore เก็บ revision snapshots และปฏิเสธ stale save โดยคง local edits",
            "status": "pass"
          },
          {
            "name": "mockups-check.mjs ผ่าน 43 assertions; Use cases 70; workflow review 25; task briefs 9",
            "status": "pass"
          },
          {
            "name": "ตรวจ GET บน 8099 ครบเมนู/guide/catalog/drafts และไม่มี real mockup graph ถูกสร้างโดย tests",
            "status": "pass"
          }
        ]
      }
    ],
    "command": "python3 mockups/tools/build_catalog.py --check; cd tracker && go test -race -count=1 ./... && go vet ./...; Playwright mockups-check/usecases-check/workflow-review-check/task-brief-check บน temporary data",
    "passed": 147,
    "failed": 0,
    "skipped": 0,
    "notes": "147 คือ browser assertions 43+70+25+9; Go/data checks เพิ่มแยกกัน ทดสอบเขียนเฉพาะ /tmp/tnsu-mockup-final/data ไม่ approve Use cases จริง"
  },
  {
    "id": "R-020",
    "suite": "ทบทวน Use cases และ canonical decision diagrams",
    "date": "2026-10-31T02:59:35+07:00",
    "summary": "ตรวจครบ 162 กรณี/47 decisions พร้อม browser 95 assertions โดยใช้ temporary data เท่านั้น",
    "groups": [
      {
        "name": "พฤติกรรมและขอบเขต",
        "audience": "ba",
        "purpose": "ตรวจข้อเสนอทางเลือกและไม่ข้ามเงื่อนไขที่มีผลต่อผู้เรียน",
        "cases": [
          {
            "name": "ผลตรวจครบ 162 ID และหมายเหตุสอดคล้องกับ canonical/generated catalog",
            "status": "pass"
          },
          {
            "name": "W-41 ข้ามข้อมูลผู้ปกครองเมื่อไม่จำเป็นได้จริง ไม่บังคับ Parent Portal ทุกคน",
            "status": "pass"
          },
          {
            "name": "W-77 ส่งเอกสารเพิ่มกลับไปตรวจใหม่ และทางปฏิเสธไม่ไปผลสำเร็จ",
            "status": "pass"
          },
          {
            "name": "W-147 ต่อสัญญา/ย้ายห้องไม่ผ่านคืนมัดจำแบบย้ายออก",
            "status": "pass"
          },
          {
            "name": "W-14 มีเส้นฉุกเฉินแยกก่อนสายอนุมัติปกติ; W-96 รอ EC ไม่ปิด milestone",
            "status": "pass"
          },
          {
            "name": "W-163 ยืนยันซ้ำแล้วข้ามการหมดสิทธิ์ และไม่ตอบไม่เท่ากับสละโดยอัตโนมัติ",
            "status": "pass"
          }
        ]
      },
      {
        "name": "โครงสร้าง แผนภาพและการถดถอย",
        "audience": "tech",
        "purpose": "รักษาความสอดคล้องของต้นฉบับ ภาพ และ approval/history",
        "cases": [
          {
            "name": "go test -race -count=1 ./... และ go vet ./... ผ่าน",
            "status": "pass"
          },
          {
            "name": "ทุกขั้นทั้ง 162 use cases เข้าถึงได้ ไม่มี dangling edge/dead end และมีทางถึง terminal outcome",
            "status": "pass"
          },
          {
            "name": "เงื่อนไข workflow ที่เปลี่ยนทำ approval stale โดยไม่เขียนทับ saved diagram",
            "status": "pass"
          },
          {
            "name": "สร้าง generated catalog ซ้ำได้ผลเหมือนเดิมทั้ง 37 ไฟล์",
            "status": "pass"
          },
          {
            "name": "usecases-check.mjs ผ่าน 70 assertions รวม grid zoom export PNG และ revision/approval",
            "status": "pass"
          },
          {
            "name": "workflow-review-check.mjs ผ่าน 25 assertions รวม branch labels, reset, saved revision, fit และ mobile",
            "status": "pass"
          },
          {
            "name": "ตรวจหน้า 8099 แบบอ่านอย่างเดียวแสดง canonical draft รุ่นใหม่โดยไม่สร้าง approval จริง",
            "status": "pass"
          }
        ]
      }
    ],
    "command": "python3 docs/agent/tools/check_use_case_review.py; cd tracker && go test -race -count=1 ./... && go vet ./...; Playwright usecases-check.mjs + workflow-review-check.mjs บน temporary server",
    "passed": 95,
    "failed": 0,
    "skipped": 0,
    "notes": "95 คือ browser assertions (70+25); Go และ data checks ตรวจเพิ่มแยกกัน การรันทดสอบก่อนหน้าพบ pointer นอก viewport และแก้ให้ scroll canvas ก่อนลากแล้วรันครบผ่าน ดู P-013"
  },
  {
    "id": "R-019",
    "suite": "ปิดงานจัดทำ Role/Security Matrix ฉบับร่าง",
    "date": "2026-10-31T02:19:03+07:00",
    "summary": "ตรวจหลักฐานฉบับร่างและเปลี่ยนสถานะ Done เฉพาะ T-201/T-202 โดยคงรายการรอ BA/Security ยืนยัน",
    "groups": [
      {
        "name": "สถานะและหลักฐาน",
        "audience": "both",
        "purpose": "ปิดงาน Agent ที่เสร็จแล้วโดยไม่อนุมัติข้อกำหนดแทนผู้ตรวจ",
        "cases": [
          {
            "name": "Role/Security Matrix draft v0.1 มีไฟล์และหัวข้อครบตาม checkpoint C-004",
            "status": "pass"
          },
          {
            "name": "เปลี่ยนเป็น Done เฉพาะ T-201/T-202 และเวลา finished อ้างอิง C-004",
            "status": "pass"
          },
          {
            "name": "ข้อมูล review และ task อื่นทุก field คงเดิม",
            "status": "pass"
          },
          {
            "name": "go test -count=1 ./... ผ่าน",
            "status": "pass"
          }
        ]
      }
    ],
    "command": "ตรวจ artifacts/status diff และ cd tracker && go test -count=1 ./...",
    "passed": 4,
    "failed": 0,
    "skipped": 0,
    "notes": "Done คือจัดทำฉบับร่างเสร็จ ไม่ใช่ยืนยัน business/security requirements; review.todo คงไว้",
    "failures": []
  },
  {
    "id": "R-018",
    "suite": "ตรวจส่งมอบงานที่ Reviewed แล้ว",
    "date": "2026-10-31T02:12:41+07:00",
    "summary": "Go race/vet ผ่าน Browser 70+9 รวม 79 assertions ผ่าน ใช้ข้อมูลชั่วคราวเพื่อปิดชุดงาน T-408–T-417",
    "groups": [
      {
        "name": "Use cases และ diagram",
        "audience": "both",
        "purpose": "ตรวจฟีเจอร์ที่ผ่านรีวิวทั้งชุดก่อนเก็บ Git",
        "cases": [
          {
            "name": "ตารางแบ่ง module/tabs/filter/collapse และ editor ทั้งชุดผ่าน 70 assertions",
            "status": "pass"
          },
          {
            "name": "Save/revision history/rollback และ approval invalidation ทำงานครบ",
            "status": "pass"
          },
          {
            "name": "PNG ส่งออกครบและ background White/Transparent ถูกต้องจริง",
            "status": "pass"
          },
          {
            "name": "UI review panels, dark mode, mobile และ unsaved edits ผ่าน",
            "status": "pass"
          }
        ]
      },
      {
        "name": "Task briefs และคุณภาพโค้ด",
        "audience": "both",
        "purpose": "รักษาผลรีวิวและรายละเอียดงานที่ตรวจแล้ว",
        "cases": [
          {
            "name": "Task briefs ผ่าน 9 assertions รวม T-303 และคำถาม 39 ข้อ",
            "status": "pass"
          },
          {
            "name": "Go test -race -count=1 ./... ผ่าน",
            "status": "pass"
          },
          {
            "name": "go vet ./... ผ่าน",
            "status": "pass"
          },
          {
            "name": "JSON ทุกไฟล์ JavaScript syntax และ git diff --check ผ่าน",
            "status": "pass"
          },
          {
            "name": "สถานะ T-408–T-417 ยังเป็น Reviewed และไม่มี Reject/Hold ค้าง",
            "status": "pass"
          }
        ]
      }
    ],
    "command": "cd tracker && go test -race -count=1 ./... && go vet ./...; node tracker/e2e/usecases-check.mjs <chrome> <temporary-server> <shots> <playwright> <temporary-data>; node tracker/e2e/task-brief-check.mjs <chrome> <temporary-server> <shots> <playwright>",
    "passed": 79,
    "failed": 0,
    "skipped": 0,
    "notes": "79 คือจำนวน browser assertions; Go suite ตรวจแยก ไม่มีการอนุมัติหรือแก้ diagram จริงใน repository",
    "failures": []
  },
  {
    "id": "R-017",
    "suite": "Task briefs และ stakeholder questions",
    "date": "2026-10-30T21:19:31+07:00",
    "summary": "Go race/vet ผ่าน และ browser read-only 9 ข้อผ่าน ครอบคลุม task brief, T-303, questions, mobile และ SQL Server decision",
    "groups": [
      {
        "name": "ความครบถ้วนของแผนงาน",
        "audience": "ba",
        "purpose": "ให้ task อ่านแล้วเริ่มงานและรู้ว่าจะถามใครได้",
        "cases": [
          {
            "name": "งานรอดำเนินการ 13 งานแสดงรายละเอียด outputs และคำถาม 39 ข้อ",
            "status": "pass"
          },
          {
            "name": "T-303 แสดงคำถาม hosting 5 ข้อ และสถานะยังรอข้อมูล",
            "status": "pass"
          },
          {
            "name": "คำถามระบุผู้ที่ควรตอบและผลต่อการตัดสินใจ",
            "status": "pass"
          },
          {
            "name": "dependencies เป็นลิงก์กดไปงานที่เกี่ยวข้องได้",
            "status": "pass"
          },
          {
            "name": "ผู้รับผิดชอบที่เสนอมีป้ายรอยืนยัน ไม่ถูกตั้งเป็นผู้ได้รับมอบหมายจริง",
            "status": "pass"
          },
          {
            "name": "คำถามบนมือถือโหมดมืดไม่ทำให้หน้าล้น",
            "status": "pass"
          },
          {
            "name": "summary T-302 แสดงผลเลือก SQL Server ที่ยืนยันแล้ว",
            "status": "pass"
          },
          {
            "name": "งานเสร็จแล้วมีบริบทให้อ่านโดยยังรักษาสถานะเดิม",
            "status": "pass"
          }
        ]
      },
      {
        "name": "โครงสร้างข้อมูลและการแสดงผล",
        "audience": "tech",
        "purpose": "คง review history และไม่บันทึกข้อสรุปที่ไม่มีคำตอบ",
        "cases": [
          {
            "name": "Go test -race -count=1 ./... และ go vet ./... ผ่าน",
            "status": "pass"
          },
          {
            "name": "รหัสคำถามไม่ซ้ำ dependencies อ้างอิง task ที่มีจริง และคำถาม answered ต้องมี answer",
            "status": "pass"
          },
          {
            "name": "SSR Task แสดง description/deliverables/questions/ผู้ตอบครบ พร้อม escape ข้อความ",
            "status": "pass"
          },
          {
            "name": "refs ทุกไฟล์ของ task ที่เติมรายละเอียดมีอยู่จริง",
            "status": "pass"
          },
          {
            "name": "Browser read-only 9 assertions ผ่าน ไม่เปลี่ยนข้อมูล/ผลรีวิว",
            "status": "pass"
          }
        ]
      }
    ],
    "command": "cd tracker && go test -race -count=1 ./...; go vet ./...; node e2e/task-brief-check.mjs <headless-shell> <base-url> <output-dir> <playwright-module>",
    "passed": 9,
    "failed": 0,
    "skipped": 0,
    "notes": "passed 9 คือ browser assertions; งาน hosting/data model/scope ยังคง todo ตามเดิม การเติม brief ไม่ใช่การปิดงานเหล่านั้น",
    "failures": []
  },
  {
    "id": "R-016",
    "suite": "Review panels UI และ PNG export",
    "date": "2026-10-30T21:08:18+07:00",
    "summary": "Browser 70 ข้อผ่าน รวมไฟล์ PNG จริง และ UI Approval/History ทั้งจอเล็กและโหมดมืด",
    "groups": [
      {
        "name": "Review panels",
        "audience": "ba",
        "purpose": "อ่านสถานะและประวัติได้ง่าย",
        "cases": [
          {
            "name": "Approval ที่อนุมัติแล้วแสดงผู้ตรวจและหมายเหตุแทนฟอร์มว่าง",
            "status": "pass"
          },
          {
            "name": "Revision history มี empty state และ Current version ที่ชัดเจน",
            "status": "pass"
          },
          {
            "name": "Refresh ประวัติล้มเหลวแล้วยังเก็บรายการเดิมไว้",
            "status": "pass"
          },
          {
            "name": "Review panels บนมือถือไม่ทำให้หน้าล้น และ screenshot สว่าง/มืดอ่านได้",
            "status": "pass"
          }
        ]
      },
      {
        "name": "PNG export และ regression",
        "audience": "tech",
        "purpose": "ส่งออกได้ครบและรักษาความโปร่งใสจริง",
        "cases": [
          {
            "name": "เปิด Export PNG dialog และเลือกพื้นหลังได้",
            "status": "pass"
          },
          {
            "name": "PNG พื้นหลัง White มี pixel มุมเป็น RGBA 255,255,255,255",
            "status": "pass"
          },
          {
            "name": "PNG Transparent มี alpha channel ที่มุมเท่ากับ 0",
            "status": "pass"
          },
          {
            "name": "ขนาด PNG ตรงกับ full graph bounds แม้ซูม viewport",
            "status": "pass"
          },
          {
            "name": "Browser regression ทั้ง 70 ข้อผ่าน และ JavaScript syntax ถูกต้อง",
            "status": "pass"
          }
        ]
      }
    ],
    "command": "node tracker/e2e/usecases-check.mjs <headless-shell> <temporary-server> <screenshots> <existing-playwright> <temporary-data>",
    "passed": 70,
    "failed": 0,
    "skipped": 0,
    "notes": "ทดสอบการ Save/Approve เฉพาะ /tmp; ตรวจ PNG ที่ดาวน์โหลดจริง ไม่ใช้ screenshot แทนการส่งออก",
    "failures": []
  },
  {
    "id": "R-015",
    "suite": "Horizontal workflow, revision history, approval และ module groups",
    "date": "2026-10-30T20:44:48+07:00",
    "summary": "Browser 59 ข้อผ่าน Go race suite และ vet ผ่าน ทดสอบการเขียน/Approve เฉพาะข้อมูล /tmp",
    "groups": [
      {
        "name": "Workflow และรายการ Use cases",
        "audience": "ba",
        "purpose": "จัดและค้นหากระบวนการได้ง่าย พร้อมตรวจยืนยันเป็นราย Use case",
        "cases": [
          {
            "name": "ร่างใหม่ไหลซ้ายไปขวาและเว้นช่องตามขนาด shape",
            "status": "pass"
          },
          {
            "name": "Arrange horizontally เก็บ shape/connection เดิมครบและ Undo คืนตำแหน่งได้",
            "status": "pass"
          },
          {
            "name": "Toolbar ใช้ปุ่มอังกฤษที่ชัดเจน",
            "status": "pass"
          },
          {
            "name": "กลุ่ม Module แสดงชื่อเต็ม หุบ/ขยาย และ Expand all / Collapse all ได้",
            "status": "pass"
          },
          {
            "name": "หุบ Module แล้ว reload ยังจำสถานะ ใช้กับ channel tabs และ filters ได้",
            "status": "pass"
          },
          {
            "name": "Approve เก็บชื่อ เวลา หมายเหตุ และแสดงสถานะในตาราง",
            "status": "pass"
          },
          {
            "name": "แก้/Save/Restore แล้วสถานะอนุมัติต้องยืนยันใหม่และไม่ล้าง approval history",
            "status": "pass"
          },
          {
            "name": "Restore คืนเนื้อหาจาก revision เดิมเป็น revision ใหม่ เก็บ intermediate revisions ครบ",
            "status": "pass"
          }
        ]
      },
      {
        "name": "ความถูกต้องและ concurrent edits",
        "audience": "tech",
        "purpose": "ไม่ทับงาน ไม่ยืนยันข้อมูลที่ยังไม่ได้ตรวจ และไม่ลบประวัติ",
        "cases": [
          {
            "name": "Go test -race -count=1 ./... และ go vet ./... ผ่าน",
            "status": "pass"
          },
          {
            "name": "ทุก snapshot และ current graph เขียน atomic และอ่านกลับหลังสร้าง store ใหม่ได้",
            "status": "pass"
          },
          {
            "name": "การ Save พร้อมกันยอมรับครั้งเดียวและเก็บ history ถูกต้อง",
            "status": "pass"
          },
          {
            "name": "stale restore / stale source / stale diagram approval ถูกปฏิเสธ",
            "status": "pass"
          },
          {
            "name": "CSRF และ approval ที่ไม่มีชื่อหรือไม่ยืนยันถูกปฏิเสธ",
            "status": "pass"
          },
          {
            "name": "ไฟล์เดิมที่ยังไม่มี history ถูกเก็บ revision เดิมก่อนบันทึกถัดไป",
            "status": "pass"
          },
          {
            "name": "Approve ร่างเริ่มต้นจะ Save layout เป็น revision 1 ก่อนและบันทึกการยืนยัน",
            "status": "pass"
          },
          {
            "name": "Browser regression ครบ 59 ข้อไม่พบ JavaScript exceptions",
            "status": "pass"
          }
        ]
      }
    ],
    "command": "cd tracker && go test -race -count=1 ./...; go vet ./...; node e2e/usecases-check.mjs <headless-shell> <temporary-server> <screenshots> <existing-playwright> <temporary-data>",
    "passed": 59,
    "failed": 0,
    "skipped": 0,
    "notes": "59 คือจำนวน browser assertions; tests ของ approval ไม่เคยเขียนข้อมูลจริงใน repo",
    "failures": []
  },
  {
    "id": "R-014",
    "suite": "Use cases tabs / full descriptions / Ctrl-wheel และ Overview order",
    "date": "2026-10-30T20:14:46+07:00",
    "summary": "Browser regression ผ่าน 41 ข้อ พร้อม Go race suite และ vet ผ่าน ตรวจหน้า 8099 แบบอ่านอย่างเดียวผ่าน",
    "groups": [
      {
        "name": "การอ่านและค้นหา Use cases",
        "audience": "ba",
        "purpose": "อ่านรายการตามช่องทางและอ่านเนื้อหา diagram ครบ",
        "cases": [
          {
            "name": "แท็บทั้งหมด Frontend Backoffice และ Integration ใช้ร่วมกับ filters ได้",
            "status": "pass"
          },
          {
            "name": "ตารางแสดงยาวลงมาตามหน้าโดยไม่มีความสูงจำกัด",
            "status": "pass"
          },
          {
            "name": "description จากต้นฉบับแสดงครบ ตัดเป็นบรรทัดสั้น และร่างใหม่ไม่ซ้อนกัน",
            "status": "pass"
          },
          {
            "name": "description ยาวหลายบรรทัดอยู่ภายใน Start End Process และ Decision ครบทุกตัวอักษร",
            "status": "pass"
          },
          {
            "name": "Current Task อยู่ก่อน Milestone ทั้งหมดบนหน้าภาพรวมจริงพอร์ต 8099",
            "status": "pass"
          }
        ]
      },
      {
        "name": "การซูมและ regression",
        "audience": "tech",
        "purpose": "ตรวจการทำงานร่วมกับ editor และการเก็บข้อมูลเดิม",
        "cases": [
          {
            "name": "Ctrl + scroll ขึ้นซูมเข้า ลงซูมออก โดยรักษาพิกัดใต้เมาส์",
            "status": "pass"
          },
          {
            "name": "dropdown แสดงสเกลตรงและไม่เปลี่ยนระดับ zoom ของ browser",
            "status": "pass"
          },
          {
            "name": "scroll ไม่กด Ctrl ยังคง pan และ zoom/pan ไม่ทำให้ข้อมูลมีสถานะยังไม่บันทึก",
            "status": "pass"
          },
          {
            "name": "Integration ต้องมีชื่อระบบที่ระบุ ไม่อนุมานจากขั้นตอนระบบทั่วไป",
            "status": "pass"
          },
          {
            "name": "Browser ทั้ง 41 ข้อผ่าน รวมกริด เส้นหักมุม สองผู้ใช้ SSE save/reload และมือถือ",
            "status": "pass"
          },
          {
            "name": "Go test -race -count=1 ./... และ go vet ./... ผ่าน",
            "status": "pass"
          }
        ]
      }
    ],
    "command": "cd tracker && go test -race -count=1 ./...; go vet ./...; node e2e/usecases-check.mjs <headless-shell> <temporary-server> <screenshots> <existing-playwright> <temporary-data>; main read-only smoke",
    "passed": 41,
    "failed": 0,
    "skipped": 0,
    "notes": "passed 41 หมายถึง browser regression; ยังตรวจ main 8099 แบบอ่านอย่างเดียวและ Go suite แยกต่างหาก",
    "failures": []
  },
  {
    "id": "R-013",
    "suite": "Diagram Snap to grid และเส้นเชื่อมหักมุม",
    "date": "2026-10-30T20:04:29+07:00",
    "summary": "Browser ผ่าน 24 ข้อหลังปรับ Snap to grid และเส้น orthogonal บนข้อมูล /tmp",
    "groups": [
      {
        "name": "การจัดตำแหน่งและเส้นเชื่อม",
        "audience": "both",
        "purpose": "จัด diagram ให้อยู่แนวเดียวกันและอ่านทางเดินของเส้นได้ง่าย",
        "cases": [
          {
            "name": "ลาก shape แล้วพิกัดขยับตรงกริด 20 หน่วย",
            "status": "pass"
          },
          {
            "name": "ทุกช่วงของเส้นเชื่อมเป็นแนวนอนหรือแนวตั้ง หักมุม 90°",
            "status": "pass"
          },
          {
            "name": "shape ที่เพิ่มและพิกัดหลังลากยังตรงกริดหลังบันทึกและเปิดใหม่",
            "status": "pass"
          },
          {
            "name": "Regression browser ทั้ง 24 ข้อผ่าน รวม Fit/กึ่งกลาง ซูม ลากเส้น สองผู้ใช้ SSE undo/redo โหมดมืดและมือถือ",
            "status": "pass"
          }
        ]
      }
    ],
    "command": "node tracker/e2e/usecases-check.mjs <headless-shell> <temporary-server> <screenshots> <existing-playwright> <temporary-data>",
    "passed": 24,
    "failed": 0,
    "skipped": 0,
    "notes": "การลากทดสอบใช้จุดที่ไม่อยู่กึ่งกลางระหว่างสองช่องกริด เพื่อไม่ผูก assertion กับทศนิยมจาก SVG zoom",
    "failures": []
  },
  {
    "id": "R-012",
    "suite": "Use cases Table view และ Diagram editor",
    "date": "2026-10-30T20:00:07+07:00",
    "summary": "Go suite พร้อม race detector และ go vet ผ่าน Browser ผ่าน 23 ข้อบนข้อมูลสำเนา รวมการจัดกึ่งกลางและ Fit ทั้งหมดตามคำขอล่าสุด",
    "groups": [
      {
        "name": "Catalog และการแสดงผล",
        "audience": "ba",
        "purpose": "ใช้ Use cases เดิมเพื่ออ่านและแก้ workflow ได้สะดวก",
        "cases": [
          {
            "name": "ตารางแสดงครบ 162 Use cases และกรอง Module / คำค้นหาได้",
            "status": "pass"
          },
          {
            "name": "Diagram ฉบับร่างเก็บขั้นตอนต้นฉบับครบและมีข้อมูลผู้เกี่ยวข้อง",
            "status": "pass"
          },
          {
            "name": "เปิด diagram แล้วเห็นทุก shape อยู่กึ่งกลางทั้งสองแนว",
            "status": "pass"
          },
          {
            "name": "ผู้ใช้ซูมเข้าและกลับมาดูพอดีทั้งหมดได้",
            "status": "pass"
          },
          {
            "name": "เพิ่ม shape 4 แบบ แก้ข้อความ และลาก shape กับเส้นเชื่อมได้",
            "status": "pass"
          },
          {
            "name": "แก้ข้อความและหัวลูกศรต้น–ปลายของเส้นเชื่อมได้",
            "status": "pass"
          },
          {
            "name": "ลบ shape พร้อมเส้นที่เกี่ยวข้อง และ undo/redo คืนได้",
            "status": "pass"
          },
          {
            "name": "ข้อมูลบันทึกส่วนกลางยังอยู่หลังเปิดใหม่ โหมดมืดและจอมือถือใช้งานได้",
            "status": "pass"
          }
        ]
      },
      {
        "name": "ความถูกต้องและการบันทึก",
        "audience": "tech",
        "purpose": "รักษาข้อมูลและไม่ทับงานของผู้ใช้อื่น",
        "cases": [
          {
            "name": "Go test -race -count=1 ./... ผ่านทุก test รวม SSE",
            "status": "pass"
          },
          {
            "name": "go vet ./... และ JavaScript syntax check ผ่าน",
            "status": "pass"
          },
          {
            "name": "ปฏิเสธ CSRF รหัสไม่รู้จัก JSON ผิดรูปแบบ ข้อมูลเกินขนาด shape/เส้นไม่ถูกต้อง และ revision เก่า",
            "status": "pass"
          },
          {
            "name": "บันทึกแล้วอ่านกลับได้ และรองรับวงจรกับ canvas ว่าง",
            "status": "pass"
          },
          {
            "name": "ทดสอบสองผู้ใช้จริง: บันทึกเวอร์ชันเก่าไม่ทับงานอีกคนและเก็บข้อความในหน้าจอไว้",
            "status": "pass"
          },
          {
            "name": "SSE จริงไม่ล้าง diagram ที่ยังไม่บันทึก",
            "status": "pass"
          },
          {
            "name": "Browser ทดสอบ 23 ข้อผ่านและไม่พบ JavaScript exception",
            "status": "pass"
          }
        ]
      }
    ],
    "command": "cd tracker && go test -race -count=1 ./...; go vet ./...; node e2e/usecases-check.mjs <headless-shell> <temporary-server> <screenshots> <existing-playwright> <temporary-data>",
    "passed": 23,
    "failed": 0,
    "skipped": 0,
    "notes": "passed 23 คือ browser assertions; Go suite ผ่านทุกข้อด้วย ใช้ Playwright ที่มีอยู่แล้ว ไม่ได้เพิ่ม dependency ให้ tracker ทดสอบการเขียนบนข้อมูล /tmp เท่านั้น",
    "failures": []
  },
  {
    "id": "R-011",
    "suite": "Tracker โหลดช้าเมื่อเปิดหลาย tab",
    "date": "2026-10-30",
    "summary": "ตรวจว่า tab ที่ไม่ได้ดูคืนช่องเชื่อมต่อ, กลับมาแล้ว live update ทำงานต่อ และ CSS/JS โหลดจาก cache ผ่านทุกข้อ",
    "groups": [
      {
        "name": "Server",
        "audience": "tech",
        "purpose": "ยืนยันว่า server ไม่ใช่ต้นเหตุ",
        "cases": [
          {
            "name": "ทุกหน้าตอบภายใน 2 ms บนเครื่อง และ 5–25 ms ผ่าน tailnet",
            "status": "pass"
          },
          {
            "name": "CSS/JS ที่มี ?v ตรงกับ build ได้ Cache-Control immutable, ไม่มีหรือไม่ตรงได้ no-cache",
            "status": "pass",
            "detail": "TestStaticCaching"
          },
          {
            "name": "go test ทั้งหมด",
            "status": "pass",
            "detail": "27 ผ่าน"
          }
        ]
      },
      {
        "name": "Browser (E2E)",
        "audience": "ba",
        "purpose": "เปิดหลาย tab แล้วไม่ช้า",
        "cases": [
          {
            "name": "tab ที่ถูกซ่อนปิดการเชื่อมต่อ live update",
            "status": "pass",
            "detail": "การเชื่อมต่อลดลง 1"
          },
          {
            "name": "กลับมาที่ tab แล้วป้ายเป็น \"อัปเดตแบบเรียลไทม์\" อีกครั้ง",
            "status": "pass"
          },
          {
            "name": "เปิดหน้าที่สองแล้ว CSS/JS มาจาก cache ทั้งหมด",
            "status": "pass"
          },
          {
            "name": "Live update ยังทำงาน (live-check บนข้อมูลสำเนา)",
            "status": "pass",
            "detail": "32 → 33 task โดยไม่ reload"
          },
          {
            "name": "Timeline (timeline-check)",
            "status": "pass",
            "detail": "9 ข้อ"
          }
        ]
      }
    ]
  },
  {
    "id": "R-010",
    "suite": "Timeline เต็มจอและเส้นแบ่ง",
    "date": "2026-10-30",
    "summary": "ตรวจว่า Timeline ใช้พื้นที่เต็มจอ, chart ยืดถึงขอบขวา, ลากเส้นแบ่งได้และจำค่า, ย่อ/ขยายแถวได้ และตำแหน่งแถบยังถูกต้องหลังเปลี่ยนเป็น % ผ่านทุกข้อ",
    "groups": [
      {
        "name": "Timeline layout",
        "audience": "tech",
        "purpose": "ตำแหน่งแถบยังถูกต้องหลังเปลี่ยนจาก px เป็น %",
        "cases": [
          {
            "name": "Task 1 วันกว้าง 1 หน่วยพอดี และความกว้างขั้นต่ำ = จำนวนหน่วย × 44 px",
            "status": "pass"
          },
          {
            "name": "Task ที่ยังเปิดอยู่ยืดถึงเส้นตอนนี้",
            "status": "pass"
          },
          {
            "name": "แถบ Epic ครอบทุก task ข้างใน",
            "status": "pass"
          },
          {
            "name": "go test ทั้งหมด",
            "status": "pass",
            "detail": "26 ผ่าน"
          }
        ]
      },
      {
        "name": "Full width (E2E)",
        "audience": "ba",
        "purpose": "chart ใช้พื้นที่จอได้เต็ม",
        "cases": [
          {
            "name": "หน้า Timeline กว้างถึงขอบ browser",
            "status": "pass",
            "detail": "main 1348 + เมนู 252 = 1600 px"
          },
          {
            "name": "chart ยืดถึงขอบขวา ไม่มีที่ว่าง",
            "status": "pass"
          }
        ]
      },
      {
        "name": "เส้นแบ่งรายละเอียด / chart (E2E)",
        "audience": "ba",
        "purpose": "ปรับความกว้างช่องรายละเอียดได้ตามต้องการ",
        "cases": [
          {
            "name": "ลากที่หัวตารางแล้วช่องรายละเอียดกว้างขึ้น",
            "status": "pass",
            "detail": "380 → 540 px"
          },
          {
            "name": "ลากที่ขอบแถวแล้วช่องรายละเอียดแคบลง",
            "status": "pass",
            "detail": "540 → 440 px"
          },
          {
            "name": "กดลูกศรขวาแล้วขยับ 20 px",
            "status": "pass"
          },
          {
            "name": "reload แล้วจำความกว้างไว้",
            "status": "pass"
          },
          {
            "name": "กด Home แล้วกลับค่าเดิม",
            "status": "pass",
            "detail": "380 px"
          }
        ]
      },
      {
        "name": "ย่อ / ขยาย (E2E)",
        "audience": "ba",
        "purpose": "ซ่อนแถวข้างใน Milestone / Epic ได้จริง",
        "cases": [
          {
            "name": "กดย่อ Epic แล้ว task ข้างในหายจากจอ",
            "status": "pass",
            "detail": "32 → 25 แถว (7 task)"
          },
          {
            "name": "กดขยายแล้วกลับมาครบ",
            "status": "pass"
          }
        ]
      }
    ]
  },
  {
    "id": "R-009",
    "suite": "Timeline (Gantt chart)",
    "date": "2026-10-30",
    "summary": "ตรวจหน้า Timeline: คำนวณช่วงเวลาและตำแหน่งแถบถูกต้องทุกระดับเวลา, ย่อ/ขยาย, เลื่อนไปที่ตอนนี้ และ Live update ไม่ทำให้ตำแหน่ง scroll หาย ผ่านทุกข้อ",
    "groups": [
      {
        "name": "Timeline layout",
        "audience": "ba",
        "purpose": "ความยาวของแถบตรงกับเวลาที่ใช้จริง",
        "cases": [
          {
            "name": "Task 1 วันกว้าง 1 วันพอดีในมุมมองวัน",
            "status": "pass",
            "detail": "44 px"
          },
          {
            "name": "Task ที่ In Progress / Wait for review ยืดถึงตอนนี้",
            "status": "pass"
          },
          {
            "name": "แผน (start → due) แสดงเป็นกรอบเส้นประ",
            "status": "pass"
          },
          {
            "name": "Task ที่ไม่มีวันขึ้นว่า \"ยังไม่ได้กำหนดวัน\" และนับเป็น Unscheduled",
            "status": "pass"
          },
          {
            "name": "แถบ Epic / Milestone ครอบทุก task ข้างใน และแสดง % ที่เสร็จ",
            "status": "pass"
          },
          {
            "name": "Milestone target แสดงเป็น ◆",
            "status": "pass"
          }
        ]
      },
      {
        "name": "Scales (Hour / Day / Week)",
        "audience": "both",
        "purpose": "สลับระดับเวลาได้และหน้าไม่หนักเกินไป",
        "cases": [
          {
            "name": "ทั้ง 3 scale เปิดได้ (HTTP 200) และ scale ที่ไม่รู้จักกลับเป็น Day",
            "status": "pass"
          },
          {
            "name": "Week เริ่มวันจันทร์",
            "status": "pass"
          },
          {
            "name": "Hour view จำกัด 21 วันรอบตอนนี้เมื่อแผนยาว",
            "status": "pass"
          },
          {
            "name": "Minimum span: Day 21 วัน, Week 12 สัปดาห์, Hour 48 ชม. ไม่ให้ chart แคบเกินไป",
            "status": "pass"
          }
        ]
      },
      {
        "name": "Interaction (E2E)",
        "audience": "both",
        "purpose": "ใช้งานใน browser จริง",
        "cases": [
          {
            "name": "เปิดหน้าแล้วเส้น \"ตอนนี้\" อยู่ในจอ",
            "status": "pass"
          },
          {
            "name": "ย่อ Milestone แล้วซ่อนแถวลูกทั้งหมด และจำค่าหลัง reload",
            "status": "pass"
          },
          {
            "name": "Live update ไม่ทำให้ตำแหน่ง scroll แนวนอนหาย",
            "status": "pass",
            "detail": "120 → 120 px ในมุมมองชั่วโมง"
          },
          {
            "name": "Light / Dark / Mobile แสดงผลถูกต้อง",
            "status": "pass"
          }
        ]
      }
    ],
    "command": "go test -count=1 -v ./... ; timeline browser check",
    "passed": 30,
    "failed": 0,
    "skipped": 0,
    "duration": "0.3s (go test) + 20s (E2E)",
    "notes": "go test: 26 ผล (20 test + 6 subtest), E2E timeline 4 checks"
  },
  {
    "id": "R-008",
    "suite": "File viewer, Reviewed status และ Done สีเขียว",
    "date": "2026-10-30",
    "summary": "ตรวจว่าไฟล์แนบเปิดเป็นหน้า HTML ในแท็บใหม่ได้ และปลอดภัย, สถานะ Reviewed ทำงานตามที่ตกลง และทุก flow เดิมยังผ่านหลังเปลี่ยน ผ่านทุกข้อ",
    "groups": [
      {
        "name": "File viewer (refs)",
        "audience": "ba",
        "purpose": "กดไฟล์แนบแล้วเปิดเป็นหน้าที่อ่านง่ายในแท็บใหม่",
        "cases": [
          {
            "name": "Refs ในหน้า Wait for review เปิด /files/... ในแท็บใหม่",
            "status": "pass"
          },
          {
            "name": "Role Matrix แสดงเป็น HTML: 10 ตาราง, 115 แถว, TOC 13 หัวข้อ",
            "status": "pass"
          },
          {
            "name": "Row filter: พิมพ์ \"การเงิน\" แล้วเหลือแถวที่เกี่ยวข้อง",
            "status": "pass",
            "detail": "พบ 2 แถว"
          },
          {
            "name": "Anchor link ไปหัวข้อถูกจุด",
            "status": "pass",
            "detail": "#3-approval-authority-matrix"
          },
          {
            "name": "Mermaid diagram render ครบ",
            "status": "pass",
            "detail": "13 จาก 13 diagram ใน 05-workflows.md"
          },
          {
            "name": "Path ใน code span ของข้อความรีวิวกดเปิดไฟล์ได้",
            "status": "pass"
          },
          {
            "name": "โฟลเดอร์แสดงรายการไฟล์ และ /files/tracker redirect ไป /files/tracker/",
            "status": "pass"
          }
        ]
      },
      {
        "name": "Security (file viewer)",
        "audience": "tech",
        "purpose": "ตัวเปิดไฟล์ต้องไม่เปิดอะไรนอก repo",
        "cases": [
          {
            "name": "Path traversal (../ และ %2e%2e) ได้ 404 หลัง clean path",
            "status": "pass"
          },
          {
            "name": ".git และไฟล์ซ่อนเปิดไม่ได้",
            "status": "pass"
          },
          {
            "name": "Symlink ที่ชี้ออกนอก repo เปิดไม่ได้ (EvalSymlinks + prefix check)",
            "status": "pass"
          },
          {
            "name": "Text ที่ไม่ใช่ path ใน repo ไม่ถูกทำเป็นลิงก์",
            "status": "pass"
          }
        ]
      },
      {
        "name": "Markdown renderer",
        "audience": "tech",
        "purpose": "เอกสารใน docs/ แสดงครบเหมือนบน GitLab",
        "cases": [
          {
            "name": "Pipe table รวม escaped pipe และ code ในช่อง",
            "status": "pass"
          },
          {
            "name": "Heading ID แบบ GitHub รองรับภาษาไทย",
            "status": "pass"
          },
          {
            "name": "ยังตัด <script>, javascript: และ HTML tag ออก",
            "status": "pass"
          }
        ]
      },
      {
        "name": "Task status: Reviewed",
        "audience": "ba",
        "purpose": "Done = Agent ทำเสร็จและทดสอบผ่าน, Reviewed = ผู้รีวิว Approve แล้ว",
        "cases": [
          {
            "name": "กด Approve แล้ว task เป็น Reviewed",
            "status": "pass"
          },
          {
            "name": "Progress % นับทั้ง Done และ Reviewed",
            "status": "pass"
          },
          {
            "name": "Done สีเขียว, Reviewed สีน้ำเงินพร้อม ✓ ทั้ง Light และ Dark",
            "status": "pass"
          },
          {
            "name": "Task ที่ Approve ก่อนหน้านี้ 4 งานย้ายเป็น Reviewed",
            "status": "pass",
            "detail": "T-302, T-401, T-403, T-404"
          }
        ]
      },
      {
        "name": "Regression (E2E)",
        "audience": "both",
        "purpose": "flow เดิมยังทำงานหลังเปลี่ยน",
        "cases": [
          {
            "name": "Live update: browser เห็น task count เปลี่ยนเองโดยไม่ reload",
            "status": "pass"
          },
          {
            "name": "Theme: Light / Dark / System ผ่าน 5 กรณี",
            "status": "pass"
          },
          {
            "name": "Review form: Reject + Checklist + Save ผ่าน 7 checks บนข้อมูลสำเนา",
            "status": "pass"
          }
        ]
      }
    ],
    "command": "go test -count=1 -v ./... ; node e2e/{live,theme,review}-check.mjs ; viewer check",
    "passed": 43,
    "failed": 0,
    "skipped": 0,
    "duration": "0.3s (go test) + ~40s (E2E)",
    "notes": "go test: 25 ผล (19 test + 6 subtest). E2E: live 1, theme 5, review form 7, viewer 5 checks"
  },
  {
    "id": "R-007",
    "suite": "Review form: Approve / Reject / Hold",
    "date": "2026-10-30",
    "summary": "ตรวจ Review form ที่ให้กรอกผลรีวิวแล้วกด Save ได้จากหน้าเว็บ: บันทึกลง milestones.json ถูกต้อง, กัน CSRF และไม่ทับข้อความที่กำลังพิมพ์ตอนมี Live update ผ่านทุกข้อ",
    "groups": [
      {
        "name": "Review workflow",
        "audience": "ba",
        "purpose": "กด Approve / Reject / Hold แล้วสถานะงานเปลี่ยนตามที่ตกลงกัน",
        "cases": [
          {
            "name": "Approve: review เป็น Done และ task เป็น Done พร้อมวันที่ Finished",
            "status": "pass"
          },
          {
            "name": "Reject: review เป็น Rejected และ task กลับไปเป็น In Progress",
            "status": "pass"
          },
          {
            "name": "Hold: review เป็น Hold และ task ยังอยู่ใน Wait for review",
            "status": "pass"
          },
          {
            "name": "Checklist ที่ติ๊กถูกบันทึกไว้",
            "status": "pass"
          },
          {
            "name": "ทุกครั้งที่ Save จะต่อท้ายใน Review history ไม่ทับของเดิม",
            "status": "pass"
          },
          {
            "name": "หลัง Save กลับไปที่ tab ตามผล พร้อม notice ว่าบันทึกแล้ว",
            "status": "pass"
          }
        ]
      },
      {
        "name": "Form validation",
        "audience": "ba",
        "purpose": "ผู้รีวิวรู้ทันทีว่ากรอกอะไรไม่ครบ และไม่ต้องพิมพ์ใหม่",
        "cases": [
          {
            "name": "Reject / Hold โดยไม่เขียนรายละเอียด ระบบไม่ยอมบันทึก",
            "status": "pass"
          },
          {
            "name": "ฟอร์มแสดงซ้ำพร้อมข้อความและชื่อที่กรอกไว้",
            "status": "pass"
          },
          {
            "name": "Task ที่ยังไม่ได้ส่งรีวิว บันทึกผลไม่ได้",
            "status": "pass"
          }
        ]
      },
      {
        "name": "Data integrity (milestones.json)",
        "audience": "tech",
        "purpose": "Save แล้วเปลี่ยนเฉพาะ field ที่เกี่ยวข้อง diff ใน git อ่านง่าย",
        "cases": [
          {
            "name": "Ordered JSON round-trip: อ่านแล้วเขียนกลับได้ไฟล์เหมือนเดิมทุก byte",
            "status": "pass",
            "detail": "ทดสอบกับไฟล์ข้อมูลจริงทั้ง 4 ไฟล์"
          },
          {
            "name": "Field อื่นและลำดับ key ไม่เปลี่ยน ภาษาไทยและ <b> ไม่ถูก escape",
            "status": "pass"
          },
          {
            "name": "Atomic write: เขียนไฟล์ชั่วคราวแล้ว rename ไม่มีไฟล์ครึ่ง ๆ",
            "status": "pass"
          }
        ]
      },
      {
        "name": "Security",
        "audience": "tech",
        "purpose": "เปิดผ่าน Tailscale แล้ว ต้องกันการส่งฟอร์มจากเว็บอื่น",
        "cases": [
          {
            "name": "CSRF: POST จาก Origin อื่นได้ 403",
            "status": "pass"
          },
          {
            "name": "POST ที่ไม่มี Origin และ Referer ได้ 403",
            "status": "pass"
          },
          {
            "name": "Request ที่ถูกปฏิเสธไม่แก้ไฟล์ข้อมูล",
            "status": "pass"
          },
          {
            "name": "Task ที่ไม่มีจริงได้ 404",
            "status": "pass"
          },
          {
            "name": "Form size limit 64 KB และ Decision ไม่เกิน 10,000 ตัวอักษร",
            "status": "pass"
          }
        ]
      },
      {
        "name": "Browser (E2E)",
        "audience": "both",
        "purpose": "ทดสอบใน headless Chrome กับข้อมูลชุดสำเนา ไม่แตะข้อมูลจริง",
        "cases": [
          {
            "name": "Live update ไม่ทับข้อความที่กำลังพิมพ์ในฟอร์ม",
            "status": "pass"
          },
          {
            "name": "เลือก Reject, ติ๊ก Checklist, กรอกชื่อ แล้ว Save ได้",
            "status": "pass"
          },
          {
            "name": "Redirect ไป tab Rejected พร้อม notice",
            "status": "pass"
          },
          {
            "name": "Decision แสดงผลเป็น Markdown",
            "status": "pass"
          },
          {
            "name": "ไฟล์ข้อมูลบันทึกผลและชื่อผู้รีวิว",
            "status": "pass"
          },
          {
            "name": "Browser จำชื่อผู้รีวิวไว้ใช้ครั้งต่อไป",
            "status": "pass"
          }
        ]
      }
    ],
    "command": "go test -count=1 -v ./... ; node e2e/review-check.mjs <chrome> http://localhost:8098 <copy>/milestones.json T-201",
    "passed": 29,
    "failed": 0,
    "skipped": 0,
    "duration": "0.3s (go test) + 12s (E2E)",
    "notes": "go test: 22 ผล (16 test + 6 subtest), E2E review form: 7 checks บน server แยกที่ port 8098 กับข้อมูลสำเนา ข้อมูลจริงไม่ถูกแก้"
  },
  {
    "id": "R-006",
    "suite": "Tracker รอบ 2: หน้าย่อย, Checkpoint, Test แบบจัดกลุ่ม, Review tabs",
    "date": "2026-10-30",
    "summary": "ตรวจว่าข้อมูลงานถูกต้อง หน้าเว็บทุกหน้าเปิดได้ การรีวิวแยกตามสถานะ และการอัปเดตสดยังทำงาน หลังเพิ่มหน้าย่อย Milestone / Epic / Task, ประวัติ Checkpoint จาก Agent, หน้าผลทดสอบแบบจัดกลุ่ม และ tab การรีวิว ผ่านทุกข้อ",
    "groups": [
      {
        "name": "Data validation",
        "audience": "ba",
        "purpose": "ถ้ามีคนแก้ไฟล์ข้อมูลผิด ระบบต้องไม่แสดงข้อมูลเพี้ยน และบอกได้ว่าผิดตรงไหน",
        "cases": [
          {
            "name": "JSON เสีย: หน้าเว็บยังแสดงข้อมูลชุดก่อนหน้า พร้อม error message",
            "status": "pass"
          },
          {
            "name": "ไม่รับ status ที่สะกดผิด",
            "status": "pass",
            "detail": "ใช้ได้เฉพาะ todo, in_progress, review, done"
          },
          {
            "name": "ไม่รับ ID ซ้ำ (task / epic / milestone)",
            "status": "pass"
          },
          {
            "name": "ไม่รับ milestone / epic / task ที่ไม่มี ID",
            "status": "pass"
          },
          {
            "name": "ไม่รับ review state ที่ไม่รู้จัก",
            "status": "pass",
            "detail": "ใช้ได้เฉพาะ todo, rejected, hold, done"
          },
          {
            "name": "tests.json / problems.json / checkpoints.json เป็น optional",
            "status": "pass"
          }
        ]
      },
      {
        "name": "Progress calculation",
        "audience": "ba",
        "purpose": "ตัวเลขบนหน้าภาพรวมต้องตรงกับงานจริง",
        "cases": [
          {
            "name": "Task count ตาม status ถูกต้องทุกระดับ (project / milestone / epic)",
            "status": "pass"
          },
          {
            "name": "Current Task: แยก steps done / remaining และ % ถูกต้อง",
            "status": "pass"
          },
          {
            "name": "Epic code สร้างอัตโนมัติ",
            "status": "pass",
            "detail": "เช่น E0.1, E1.2"
          },
          {
            "name": "Review queue เรียงตาม requested date (เก่าสุดก่อน)",
            "status": "pass"
          },
          {
            "name": "Date format เป็นปี พ.ศ.",
            "status": "pass",
            "detail": "30 ต.ค. 2569"
          }
        ]
      },
      {
        "name": "Pages & Routing",
        "audience": "both",
        "purpose": "ทุกหน้าเปิดได้ และไปถึงรายละเอียดของงานได้ทุกระดับ",
        "cases": [
          {
            "name": "ทุก page ได้ HTTP 200 จากข้อมูลจริง",
            "status": "pass",
            "detail": "ทดสอบกับ server ที่รันอยู่ด้วย 11 หน้า ได้ 200 ทุกหน้า"
          },
          {
            "name": "Milestone / Epic / Task page มี breadcrumb",
            "status": "pass"
          },
          {
            "name": "Multi-milestone: sidebar แสดงทุก milestone, epic code เป็น E1.x",
            "status": "pass"
          },
          {
            "name": "URL ที่ไม่มีจริงได้ 404",
            "status": "pass",
            "detail": "milestone / epic / task ที่ไม่มี และ epic ที่อยู่ผิด milestone"
          }
        ]
      },
      {
        "name": "Review tabs & Checklist",
        "audience": "ba",
        "purpose": "ผู้รีวิวเห็นงานแยกตามผลการรีวิว และรู้ว่าต้องทำอะไรเป็นขั้นตอน",
        "cases": [
          {
            "name": "Tab Todo / Rejected / Hold / Done แยกถูกต้อง",
            "status": "pass",
            "detail": "ค่าที่ไม่รู้จักกลับไปที่ Todo"
          },
          {
            "name": "Overview นับเฉพาะ Todo เป็น Review",
            "status": "pass"
          },
          {
            "name": "Decision render เป็น Markdown",
            "status": "pass"
          }
        ]
      },
      {
        "name": "Test results view (BA / Technical)",
        "audience": "both",
        "purpose": "แต่ละฝ่ายเลือกดูเฉพาะหัวข้อที่เกี่ยวกับงานของตัวเองได้",
        "cases": [
          {
            "name": "Filter BA / Technical แสดง group และ count ถูกต้อง",
            "status": "pass"
          },
          {
            "name": "มี case fail แล้ว run เป็น \"ไม่ผ่าน\"",
            "status": "pass"
          }
        ]
      },
      {
        "name": "Checkpoint log",
        "audience": "ba",
        "purpose": "บันทึกสรุปจาก Agent เรียงถูกและไม่มีรายการเสีย",
        "cases": [
          {
            "name": "เรียงล่าสุดก่อน รองรับทั้ง timestamp และ date",
            "status": "pass"
          },
          {
            "name": "ไม่รับ checkpoint ที่ไม่มี ID, ID ซ้ำ หรือ timestamp ผิด",
            "status": "pass"
          }
        ]
      },
      {
        "name": "Markdown sanitize",
        "audience": "tech",
        "purpose": "ข้อความจากไฟล์ข้อมูลต้องแสดงสวยงาม แต่ฝังสคริปต์หรือ HTML ไม่ได้",
        "cases": [
          {
            "name": "Render bold / italic / code / link / list ถูกต้อง",
            "status": "pass"
          },
          {
            "name": "Sanitize: ตัด <script>, javascript: link และ HTML tag",
            "status": "pass"
          },
          {
            "name": "ข้อความใน code span ไม่ถูก format ซ้ำ",
            "status": "pass"
          }
        ]
      },
      {
        "name": "Live update (SSE)",
        "audience": "tech",
        "purpose": "หน้าที่เปิดค้างไว้ต้องเปลี่ยนเองเมื่อข้อมูลเปลี่ยน",
        "cases": [
          {
            "name": "File watcher: แก้ไฟล์แล้ว reload และ broadcast",
            "status": "pass"
          },
          {
            "name": "SSE /events ส่ง hello แล้ว update",
            "status": "pass"
          },
          {
            "name": "E2E: browser เห็น task count เปลี่ยนเองโดยไม่ reload",
            "status": "pass",
            "detail": "27 → 28 ใน headless Chrome แล้วคืนไฟล์ข้อมูลเหมือนเดิม"
          }
        ]
      },
      {
        "name": "Theme (Light / Dark / System)",
        "audience": "both",
        "purpose": "เลือกธีมได้และจำค่าที่เลือก",
        "cases": [
          {
            "name": "ระบบตั้งเป็นโหมดมืด + เลือก \"ตามระบบ\" ได้ธีมมืด",
            "status": "pass"
          },
          {
            "name": "ระบบตั้งเป็นโหมดสว่าง + เลือก \"ตามระบบ\" ได้ธีมสว่าง",
            "status": "pass"
          },
          {
            "name": "กด \"มืด\" แล้วเปลี่ยนเป็นธีมมืดทันที",
            "status": "pass"
          },
          {
            "name": "reload แล้วยังเป็นธีมมืด",
            "status": "pass"
          },
          {
            "name": "กลับไป \"ตามระบบ\" แล้วลบค่าที่จำไว้",
            "status": "pass"
          }
        ]
      }
    ],
    "command": "go test -count=1 -v ./... ; node e2e/live-check.mjs ; node e2e/theme-check.mjs",
    "passed": 25,
    "failed": 0,
    "skipped": 0,
    "duration": "0.5s (go test) + 15s (e2e)",
    "notes": "go test: 19 ผล (13 test + 6 subtest), e2e อัปเดตสด 1 ชุด, e2e ธีม 5 กรณี และตรวจ 11 หน้าบน server ที่รันอยู่"
  },
  {
    "id": "R-005",
    "suite": "tracker: หลังเปลี่ยนเป็น Milestone → Epic → Task",
    "date": "2026-10-30",
    "command": "go test -count=10 ./... และ e2e live-check, theme-check",
    "passed": 152,
    "failed": 0,
    "skipped": 0,
    "duration": "25s",
    "notes": "go test 10 รอบ (15 test รวม subtest ต่อรอบ = 150) กับ e2e อัปเดตสดและธีมอย่างละ 1 ชุด ผ่านทั้งหมด ตรวจหน้าจอที่ 1440px และ 390px",
    "summary": "ตรวจหลังเปลี่ยนโครงสร้างเป็น Milestone → Epic → Task"
  },
  {
    "id": "R-004",
    "suite": "tracker: dark mode ในเบราว์เซอร์ (e2e)",
    "date": "2026-10-30",
    "command": "node e2e/theme-check.mjs <chrome> <out-dir>",
    "passed": 5,
    "failed": 0,
    "skipped": 0,
    "duration": "12s",
    "notes": "ตรวจ 5 กรณี: OS มืดกับตามระบบ, OS สว่างกับตามระบบ, กดมืด, reload แล้วยังมืด, กลับเป็นตามระบบแล้วลบค่าที่จำไว้",
    "summary": "ตรวจการสลับธีมสว่าง / มืด / ตามระบบ"
  },
  {
    "id": "R-003",
    "suite": "tracker: go test ซ้ำ 30 รอบ",
    "date": "2026-10-30",
    "command": "go test -count=30 ./...",
    "passed": 360,
    "failed": 0,
    "skipped": 0,
    "duration": "13s",
    "notes": "รันซ้ำเพื่อจับ test ที่ผ่านบ้างไม่ผ่านบ้าง หลังแก้ watcher ให้รอไฟล์เขียนเสร็จ (ดู P-007) ผ่านครบทุกรอบ",
    "summary": "รันซ้ำ 30 รอบเพื่อยืนยันว่าไม่มี test ที่ผ่านบ้างไม่ผ่านบ้าง"
  },
  {
    "id": "R-002",
    "suite": "tracker: อัปเดตสดในเบราว์เซอร์ (e2e)",
    "date": "2026-10-30",
    "command": "node e2e/live-check.mjs <chrome> data/milestones.json",
    "passed": 1,
    "failed": 0,
    "skipped": 0,
    "duration": "6s",
    "notes": "เปิดหน้าภาพรวมใน headless Chrome แล้วเพิ่ม task ชั่วคราวในไฟล์ข้อมูล จำนวน Task บนหน้าที่เปิดอยู่เพิ่มจาก 22 เป็น 23 โดยไม่ reload จากนั้นคืนไฟล์ข้อมูลเป็นเหมือนเดิม",
    "summary": "ตรวจว่าหน้าเว็บอัปเดตเองในเบราว์เซอร์จริง"
  },
  {
    "id": "R-001",
    "suite": "tracker: go test",
    "date": "2026-10-30",
    "command": "cd tracker && go test ./...",
    "passed": 12,
    "failed": 0,
    "skipped": 0,
    "duration": "0.3s",
    "notes": "9 test (12 รวม subtest): โหลดและตรวจข้อมูล, เก็บข้อมูลชุดเดิมเมื่อ JSON เสีย, นับสถานะ, แยกขั้นตอน Current Task, อ่าน git log, วันที่ พ.ศ., render ทุกหน้า, watcher และ SSE",
    "summary": "ชุดทดสอบแรกของ tracker"
  }
]