บันทึกวิจัย

test-t3-app: verification skill 41 บรรทัดของ T3 Code ที่ควรลอกไปใช้คู่ pstack

ตอนต่อจากบท pstack ในบทที่แล้ว pstack สร้าง verification skill ให้เราได้ บทนี้เปิดดู verification skill ตัวจริงที่ทีม T3 Code ใช้กับแอปของตัวเอง อ่านทีละท่อนว่าแต่ละบรรทัดกันความพังแบบไหน แล้วเอาไปต่อกับ pstack และเขียนเวอร์ชันของโปรเจกต์เราเอง

เผยแพร่ 12 ตุลาคม 2026


ในบท pstack เราได้เห็นแล้วว่า Lauren Tan ให้ verification skill มาก่อนทุกอย่าง และ pstack มีคำสั่ง /pstack:create-verification-skill ไว้สร้างมันให้ แต่ของที่สร้างออกมาจะดีหรือไม่ดี เราต้องรู้ก่อนว่าของดีหน้าตาเป็นยังไง

ตัวอย่างที่ดีที่สุดที่หาได้ตอนนี้อยู่ใน repo ของ T3 Code เป็นไฟล์ชื่อ .agents/skills/test-t3-app/SKILL.md ยาวแค่ 41 บรรทัด ทีมที่เขียนมันมีผู้ใช้หลักแสนคน (ไฟล์ AGENTS.md ของเขาเองเขียนว่าเกิน 500,000 คน ตัวเลขนี้เป็นของที่เขาอ้างเอง ผมยืนยันจากภายนอกไม่ได้) และในไฟล์เดียวกันเขียนไว้ด้วยว่างานส่วนใหญ่ใน repo นี้ทำโดย agent ที่ถูกสั่งผ่านตัว T3 Code เอง

แกะคำศัพท์

T3 Code อ่านว่า "ที-ทรี โค้ด" เป็นของทีม T3 Tools ของ Theo Browne คนเดียวกับที่ทำ T3 Stack และช่อง YouTube t3.gg ใน README เขาเรียกมันว่า agent harness control surface

harness อ่านว่า "ฮาร์เนส" แปลตรงตัวว่า "สายรัดบังเหียนม้า" ในวงการ AI ใช้เรียกโปรแกรมที่ห่อตัวโมเดลไว้ให้ทำงานได้ เช่น Claude Code หรือ Codex ถ้าโมเดลคือม้า harness ก็คือบังเหียนที่ทำให้ม้าลากรถได้ T3 Code จึงเป็น "แผงคุมบังเหียน" อีกชั้นหนึ่ง ที่จับบังเหียนหลายเส้นไว้ในมือเดียว


T3 Code คืออะไร ใน 1 นาที

T3 Code คือหน้าจอเดียวสำหรับสั่ง agent ที่เราลงไว้ในเครื่องอยู่แล้ว ทั้ง Claude Code, Codex, Cursor, Grok Build, OpenCode และ Antigravity โดยใช้ subscription ของเราเอง เว็บ t3.codes เขียนว่าเขาไม่ขาย token ต่อ ตัวโปรแกรมฟรีและเป็น open source (MIT) มีทั้งเว็บ แอปเดสก์ท็อป และแอปมือถือ ทุก thread ทำงานบน branch ของตัวเอง และ commit, push, เปิด PR ได้จากในแอป

ส่วนที่เกี่ยวกับบทนี้คือ T3 Code ยื่นเครื่องมือให้ agent ที่รันอยู่ข้างในใช้ด้วย ตัวสำคัญคือ Browser panel แผงเบราว์เซอร์ใน thread ที่ agent สั่งผ่านเครื่องมือชื่อ preview_* ได้ (เปิดหน้า คลิก พิมพ์ ถ่ายภาพ อัดวิดีโอ) และ Device panel ที่สั่ง simulator มือถือผ่าน device_* agent ภายนอกอย่าง Claude Code ในเทอร์มินัลของเราก็ต่อเข้ามาคุม thread ได้ผ่าน MCP

หน้าจอ (client) เว็บ app.t3.codes, npx t3 เดสก์ท็อป Electron มือถือ iOS, Android T3 server Node, คุยกับหน้าจอผ่าน WebSocket เก็บ project, thread, worktree หนึ่ง thread หนึ่ง branch agent ที่เรามี subscription อยู่แล้ว Claude Code Codex Cursor Grok Build OpenCode Antigravity เครื่องมือที่ T3 ยื่นให้ agent Browser panel preview_* Device panel device_* MCP ที่ /mcp agent ข้างนอกเข้ามาคุม thread
ภาพรวมของ T3 Code วาดจาก README, AGENTS.md และรายชื่อเครื่องมือใน apps/server/src/mcp ของ repo

อ่าน test-t3-app ทีละท่อน

ไฟล์มีส่วนหัวกับเนื้อหาสี่ท่อน ผมสรุปเป็นภาษาไทยทีละท่อน แนะนำให้เปิด ไฟล์ต้นฉบับ คู่กันไปด้วย เพราะสั้นจนอ่านจบในสองนาที

ส่วนหัว: บอกทั้งว่าใช้เมื่อไหร่ และไม่ใช้เมื่อไหร่

description บอกว่าใช้ทดสอบ UI ฝั่งเว็บและเดสก์ท็อปผ่าน Browser panel กับ state ที่แยกไว้สำหรับ dev แล้วปิดท้ายว่าถ้าเป็นแอปมือถือให้ไปใช้ test-t3-mobile แทน ประโยคสุดท้ายนี้สำคัญ เพราะ agent ตัดสินใจเรียก skill จาก description อย่างเดียว การบอกขอบเขตไว้ตรงนี้กัน agent เรียกผิดตัว

ท่อนแรก: ใช้ Browser panel เท่านั้น ถ้าไม่มีให้หยุด

ถ้าเครื่องมือของ Browser panel ไม่มี หรือแผงแจ้งว่าใช้ไม่ได้ ให้อธิบายว่าติดอะไรแล้วหยุดตรวจ ห้ามไปลงหรือสลับไปใช้เครื่องมือ automation ตัวอื่น บรรทัดนี้กันนิสัยที่ agent ชอบทำมากที่สุดตอนเจอทางตัน คือหาทางอ้อม เช่นลง Playwright เอง เปิดเบราว์เซอร์อีกตัว แล้วรายงานว่าตรวจแล้ว ทั้งที่ตรวจคนละที่กับที่ผู้ใช้เห็น

ท่อนสอง: เปิดแอปกับ state ที่แยกไว้

ใช้ dev server ที่เปิดอยู่แล้วในงานนี้ถ้ามันยังดี ถ้าไม่มีค่อยรัน vp run dev แล้วเก็บ terminal นั้นไว้ ข้อมูลของแอปต้องอยู่ในโฟลเดอร์ .t3 ของ worktree ซึ่ง git ไม่ติดตาม พอร์ตและ URL สำหรับจับคู่ (pairing URL) ให้อ่านจาก output ของ dev runner จริงทุกครั้ง แล้วมีข้อห้ามสองข้อ คือห้ามรันกับ ~/.t3/userdata ซึ่งเป็นฐานข้อมูลจริงของคนที่กำลังใช้แอปอยู่ และห้ามตั้งตัวแปร VITE_HTTP_URL หรือ VITE_WS_URL

ข้อห้ามตัวแปรสองตัวนั้นมีเหตุผลอยู่ใน AGENTS.md ตอน dev ทุกอย่างวิ่งผ่าน origin เดียว ถ้าไปตั้งค่าพวกนี้ ที่อยู่ localhost จะฝังติดเข้าไปในไฟล์ที่ build ออกมา แล้วทุกเบราว์เซอร์ที่ต่อมาจากเครื่องอื่นจะพังเงียบ ๆ ท่อนนี้ยังบอกด้วยว่าต้องทดสอบด้วยข้อมูล project และ thread ที่มีความหมาย และให้อ่านเอกสารเรื่อง fixture ของ SQLite เฉพาะตอนต้องดูหรือใส่ข้อมูลเท่านั้น

ท่อนสาม: ขั้นตอนบน Browser panel

เรียก preview_status ก่อน ถ้าแผงปิดอยู่ค่อย preview_open แล้วพาแท็บไปที่ pairing URL ตัวเต็ม ครั้งเดียว ด้วย preview_navigate จากนั้นใช้ preview_snapshot กับเครื่องมือคลิกพิมพ์ของ T3 ถ้า token ถูกใช้ไปแล้วหรือหมดอายุ ให้ออก token ใหม่ด้วย node apps/server/src/bin.ts pair และใช้แท็บเดิมตลอด

คำว่า "ครั้งเดียว" มีเหตุผล pairing URL ของ T3 เป็นลิงก์ใช้ได้ครั้งเดียว เปิดซ้ำก็ใช้ไม่ได้แล้ว และถ้า agent เผลอเปิดลิงก์ที่ตั้งใจจะให้ผู้ใช้ ผู้ใช้ก็จะเข้าไม่ได้

1 · dev server เดิม ถ้ายังดี ไม่งั้น vp run dev 2 · อ่านพอร์ตจริง จาก output ของ dev runner 3 · preview_status ไม่มีเครื่องมือ = หยุด 4 · preview_open ถ้าแผงยังปิดอยู่ 5 · preview_navigate pairing URL ครั้งเดียว 6 · snapshot + คลิก เดินเส้นทางที่แก้ 7 · เก็บหลักฐาน สภาพที่พิสูจน์ว่าใช้ได้ 8 · เปิดทิ้งไว้ให้ดู ปิดเฉพาะที่ตัวเองเปิด
ลำดับที่ test-t3-app สั่งให้ agent เดิน อ่านจากซ้ายไปขวาในแถวบน แล้วย้อนจากขวาไปซ้ายในแถวล่าง

ท่อนสี่: ตรวจแล้วเก็บไว้ให้ดู

เดินเส้นทางที่ถูกแก้แล้วเก็บสภาพที่พิสูจน์ว่าใช้ได้ เปิด server, state และแผงทิ้งไว้ระหว่างที่ผู้ใช้ยังดูหรือแก้ต่อ ในไฟล์มีประโยคหนึ่งที่ผมชอบมาก แปลได้ว่า การจบเทิร์นของ agent ไม่ได้แปลว่าถึงเวลาเก็บของ ส่วนตอนเก็บของจริง ให้ปิดเฉพาะ process ที่ตัวเองเปิด โดยใช้ terminal ที่เก็บไว้หรือ PID ที่จดไว้ตอนเปิด

ถ้าผู้ใช้ขอแชร์ให้ดูจากเครื่องอื่น ให้เริ่มด้วย vp run dev --share แล้วส่ง pairing URL ใหม่ที่ agent ยังไม่ได้ใช้ให้ผู้ใช้ และห้ามให้ credential อื่นหลุดเข้าไปในภาพหน้าจอ commit หรือข้อความตอบกลับ

ไฟล์อ้างอิง: เรื่อง fixture ของ SQLite

ไฟล์ references/sqlite-fixtures.md ถูกแยกออกมาและบอกให้โหลดเฉพาะตอนต้องดูหรือใส่ข้อมูลเท่านั้น ข้างในมีสคริปต์ช่วย apps/server/scripts/t3-sqlite-state.ts สำหรับ query และ exec ทีละคำสั่ง ห่อการเขียนไว้ใน transaction และพิมพ์ path ของไฟล์สำรองหลังเขียนเสร็จ แต่ประโยคที่ควรจำที่สุดในไฟล์นี้คือข้อจำกัดของ fixture ข้อมูลที่เขียนลงตารางโดยตรงเหมาะกับการจัดฉากหน้าจอ เช่นชื่อยาวมาก จำนวนเยอะผิดปกติ ไทม์ไลน์ยาว แต่ ห้ามใช้มันเป็นหลักฐานว่า logic ฝั่ง backend ทำงานถูก เพราะมันข้ามประวัติ event ทั้งหมด ถ้าจะทดสอบพฤติกรรมจริงต้องสั่งผ่านคำสั่งหรือ API ของแอป


บทเรียนแปดข้อจาก 41 บรรทัด

แต่ละข้อข้างล่างคือความพังที่ skill ตัวนี้กันไว้ ผมจับคู่กับหลักคิดของ pstack ที่ตรงกันไว้ด้วย เผื่ออยากกลับไปอ่านต่อในบทที่แล้ว

1 · ไม่มีเครื่องมือ ให้หยุดแล้วบอก

ตรวจด้วยเครื่องมือผิดตัวคือการตรวจของอีกอย่าง ผลที่ได้หน้าตาเหมือนหลักฐาน แต่พิสูจน์ได้แค่ว่าเบราว์เซอร์อีกตัวทำงาน

คู่กับ Prove It Works

2 · state ต้องแยก ข้อมูลจริงห้ามแตะ

agent ทำงานอยู่ในเครื่องเดียวกับคนที่กำลังใช้แอปตัวจริง ถ้าเปิด server ชี้ไปที่ฐานข้อมูลจริง ความผิดพลาดเล็ก ๆ ก็กลายเป็นข้อมูลของผู้ใช้หาย

คู่กับ Separate Before Serializing Shared State

3 · อ่านค่าจริง อย่าเดา

พอร์ตของแต่ละ worktree ไม่เท่ากัน และขยับได้ถ้าพอร์ตเดิมไม่ว่าง agent ที่เดาพอร์ตจะไปตรวจแอปของ worktree อื่นโดยไม่รู้ตัว

คู่กับ Fix Root Causes (เห็นของจริงก่อนสรุป)

4 · ฐานข้อมูลว่างคือการทดสอบที่แย่

AGENTS.md เขียนไว้ตรง ๆ ว่า An empty database is a bad test หน้าจอที่ไม่มีข้อมูลไม่เคยเจอชื่อยาว รายการเยอะ หรือสถานะแปลก ๆ

คู่กับ Test Behavior, Not Implementation

5 · รายละเอียดโหลดเมื่อต้องใช้

เรื่อง SQLite ยาวและใช้ไม่บ่อย จึงอยู่อีกไฟล์ ตัว skill เลยสั้นพอที่ agent อ่านทั้งไฟล์ได้ทุกครั้งโดยไม่เปลืองบริบท

คู่กับ Guard the Context Window

6 · หลักฐานต้องตรงกับสิ่งที่อ้าง

fixture ที่เขียนลงตารางพิสูจน์ได้แค่ว่าหน้าจอแสดงผลถูก ใครเอามันไปอ้างว่า backend ทำงานถูกคือการเคลมเกินหลักฐาน

คู่กับ Explain the Number

7 · จบเทิร์นไม่ได้แปลว่าจบงาน

agent ที่ปิด server ทันทีที่ตอบเสร็จ ทำให้ผู้ใช้เปิดดูของที่เพิ่งแก้ไม่ได้ ส่วนตอนปิดจริงต้องปิดตาม PID ที่จดไว้ ห้ามปิดตามชื่อ เพราะ process ของ agent เองก็มี path ของ worktree อยู่ในชื่อ

คู่กับ Never Block on the Human

8 · ของลับใช้ครั้งเดียว และไม่ออกนอกเครื่อง

pairing token ใช้ได้ครั้งเดียว agent ห้ามใช้ลิงก์ที่เตรียมไว้ให้ผู้ใช้ และห้ามให้ token ติดไปในภาพหน้าจอ commit หรือข้อความ

คู่กับ Boundary Discipline


ทำไมมันสั้นได้แค่ 41 บรรทัด

ถ้าเทียบกับ verification skill ที่ pstack สร้างให้ (มีหัวข้อ Launch, Doctor, Drive, Evidence, Cleanup, Helpers และแผนที่ฟีเจอร์) test-t3-app สั้นกว่ามาก เหตุผลคือน้ำหนักส่วนใหญ่ถูกย้ายไปอยู่ในไฟล์อื่นของ repo แล้ว ตรงกับกฎห้าชั้นที่ Lauren พูดในบท pstack ว่าบทเรียนไหนย้ายไปไว้ในโค้ดหรือสคริปต์ได้ ให้ย้าย อย่าเก็บไว้แค่ใน skill

t3code/ ├─AGENTS.md ├─CLAUDE.md ├─t3.json ├─.claude/skills → ../.agents/skills ├─.agents/skills/ ├─test-t3-app/ │ ├─SKILL.md │ └─references/sqlite-fixtures.md ├─test-t3-mobile/ └─contribution-triage/ กติกาทั้ง repo, คำศัพท์, ข้อห้าม มีบรรทัดเดียว: @AGENTS.md รันสคริปต์ตั้ง worktree ใหม่ให้เอง Claude Code กับ Codex ใช้ skill ชุดเดียวกัน 41 บรรทัด อ่านเฉพาะตอนจัดข้อมูลทดสอบ คู่แฝดสำหรับ iOS, Android คัดกรอง PR จากคนนอก
ไฟล์รอบ ๆ test-t3-app ใน repo pingdotgg/t3code (อ่านจาก main วันที่ 12 ตุลาคม 2026)

AGENTS.md คือที่ที่เนื้อหาหนัก ๆ อยู่ ส่วนที่เกี่ยวกับการตรวจงานมีสามท่อนที่ควรอ่านเต็ม ๆ

อีกท่อนที่ควรขโมยไปใช้ทั้งท่อนคือ Hit every surface ทีม T3 เขียนว่าความผิดที่เจอบ่อยที่สุดใน repo คือการแก้ที่ใช้ได้บนเส้นทางที่ทดสอบ แต่ไปไม่ถึงทางอื่น ก่อนบอกว่างานหน้าบ้านเสร็จ agent ต้องไล่รายการนี้แล้วบอกว่าข้อไหนเกี่ยว ทางเข้าของฟีเจอร์ (หน้าแชต หน้าตั้งค่า command palette คีย์ลัด), client ทุกตัว, provider ทุกตัว, เครื่องมือที่ agent ใช้, สัญญาข้อมูล (contract) ที่ข้ามสาย, ทางกลับ (เปิดได้ต้องปิดได้), โหมดการเชื่อมต่อ และเอกสาร

ส่วน .claude/skills ที่เป็น symlink ชี้ไปที่ .agents/skills เป็นเทคนิคเล็ก ๆ ที่ใช้ได้ทันที skill ชุดเดียววางไว้ที่เดียว Claude Code อ่านจาก .claude/skills ส่วน Codex และ harness อื่นอ่านจาก .agents/skills ไม่ต้องดูแลสองสำเนา


เอามาต่อกับ pstack ยังไง

ใน repo ของ T3 Code ไม่มีที่ไหนพูดถึง pstack ตรง ๆ แต่สองอย่างนี้ต่อกันได้พอดีตามการออกแบบ pstack บอก agent ว่าทำงานยังไง (เลือก playbook หาสาเหตุ แบ่งงาน) และใน playbook ทุกเล่มมีขั้น "ตรวจบนของจริง" ซึ่ง poteto-mode จะไปหา verification skill ของโปรเจกต์มาใช้ test-t3-app คือ skill ตัวนั้นสำหรับแอป T3 ส่วนตัว T3 Code เป็นทั้งที่ที่ agent รันและเป็นมือกับตาที่ทำให้ขั้นตรวจทำได้จริง

คำสั่งไหลลง หลักฐานไหลขึ้น pstack วิธีทำงาน: เลือก playbook หาสาเหตุ แบ่งงาน test-t3-app วิธีพิสูจน์บนแอปนี้: เปิดยังไง ข้อมูลไหน ห้ามอะไร T3 Code มือกับตา: preview_*, device_*, worktree ต่อ thread แอปตัวจริง กับ state ที่แยกไว้ใน .t3 ของ worktree
สามชั้นที่ทำงานต่อกัน คำสั่งไหลลงจากวิธีทำงานไปถึงแอปตัวจริง หลักฐานไหลกลับขึ้นมาให้เราอ่าน

การใช้งานจริงแบ่งได้สามแบบ ตามว่าเรากำลังแก้โค้ดของใคร

แบบที่ 1: แก้โค้ดของ T3 Code เอง

เพราะ .claude/skills ชี้ไปที่ .agents/skills ถ้าเปิด Claude Code ใน repo นี้ก็จะเห็น /test-t3-app ทันที แต่ poteto-mode ของตัวพอร์ต pstack-claude มองหา skill ชื่อ verify ที่ .claude/skills/verify/ ชื่อไม่ตรงกัน จึงต้องเรียกชื่อ test-t3-app ไว้ใน prompt เอง

ใน repo t3code
/pstack:poteto-mode ปุ่มปิด thread ในแถบด้านข้างกดแล้วไม่หาย
ช่วยหาสาเหตุแล้วแก้ ใช้ /test-t3-app ยืนยันบน Browser panel
แนบภาพก่อนและหลังแก้ แล้วเปิด server ทิ้งไว้ให้ผมลองเอง

Browser panel และเครื่องมือ preview_* เป็นของที่ T3 Code ยื่นให้ agent ที่รันอยู่ใน thread ของมัน ถ้ารัน Claude Code ในเทอร์มินัลธรรมดาที่ไม่ได้ต่อกับ T3 skill จะเจอว่าไม่มีเครื่องมือ แล้วหยุดตามที่เขียนไว้ ซึ่งเป็นพฤติกรรมที่ถูกต้อง ทางที่ใช้ได้คือสั่งงานนี้จาก thread ใน T3 Code ที่เลือก Claude เป็น provider ส่วนเรื่องจะส่ง PR ให้ T3 Code ควรรู้ไว้ก่อนว่า README เขียนว่าตอนนี้ยังแทบไม่รับ contribution จากคนนอก รับแค่การแก้เล็ก ๆ

แบบที่ 2: โปรเจกต์ของเรา โดยรัน agent ใน T3 Code

แบบนี้คือแบบที่ผมคิดว่าคุ้มที่สุด เปิดโปรเจกต์ของเราใน T3 Code เลือก provider เป็น Claude แล้วให้ pstack สร้าง verification skill ของเราเอง โดยบอกให้ใช้ test-t3-app เป็นต้นแบบ agent ที่รันอยู่ใน thread มีเครื่องมือ preview_* อยู่แล้ว skill ที่ได้จึงขับเบราว์เซอร์ได้โดยไม่ต้องลงอะไรเพิ่ม

ใน thread ของ T3 Code
/pstack:create-verification-skill
ใช้โครงเดียวกับ test-t3-app ของ T3 Code: แยก state ไว้ในโฟลเดอร์ของ worktree,
อ่านพอร์ตจริงจาก output, ถ้าไม่มีเครื่องมือขับ browser ให้หยุดแล้วบอก,
ปิดเฉพาะ process ที่ตัวเองเปิด, เปิด server ทิ้งไว้หลังตรวจเสร็จ

ถ้าอยากไปไกลกว่านั้น มีคนพอร์ต pstack มาให้ T3 Code โดยตรงชื่อ p3-stack เปลี่ยนเครื่องมือของ Cursor เป็นของ T3 ทั้งชุด เช่น delegate_task แทนการสร้าง subagent, t3_thread_launch ให้งานแต่ละชิ้นได้ worktree ของตัวเอง, watch_pull_request ปลุก thread เมื่อ CI เสร็จแทนการวนเช็ก, schedule_task แทน /loop และ preview_* กับ device_* แทน skill ขับเบราว์เซอร์ภายนอก ข้อควรรู้คือ repo นี้เป็นของบุคคลทั่วไป ไม่ใช่ของ poteto หรือทีม T3 ตอนที่ผมเปิดดูมี commit เดียว (5 ตุลาคม 2026) ลองได้ แต่ยังไม่ควรวางงานสำคัญไว้บนมัน

แบบที่ 3: สั่ง T3 Code จาก Claude Code ในเทอร์มินัล

T3 Code เปิด MCP server ไว้ที่ /mcp ของแต่ละ environment ให้ agent ภายนอกต่อเข้ามาอ่านและสั่ง thread ได้ หา URL ได้จาก Settings → Connections แล้วเลือก Copy MCP URL

ในเทอร์มินัล
claude mcp add --transport http t3 https://<ที่อยู่-environment>/mcp
claude mcp login t3
claude mcp list

ค่าเริ่มต้นคือสิทธิ์อ่านอย่างเดียว ถ้าจะให้สร้างหรือสั่ง thread ต้องเลือกสิทธิ์ที่สูงขึ้นตอนอนุมัติ การ sign-in มีอายุ 30 วัน และถอนสิทธิ์ได้ทันทีจากหน้า Connections ในเอกสารบอกเรื่อง thread, project และ provider แต่ไม่ได้บอกว่า agent ภายนอกใช้ Browser panel เองได้หรือไม่ ผมจึงไม่แนะนำให้หวังผลการตรวจบนเบราว์เซอร์จากทางนี้ ใช้มันเป็นทางสั่งงานให้ thread ข้างในไปตรวจแทน


แม่แบบ test-<app> สำหรับโปรเจกต์ของเรา

ข้างล่างคือแม่แบบที่ผมเรียบเรียงใหม่จากโครงของ test-t3-app ให้ใช้กับเว็บแอปทั่วไป (ไม่ได้คัดลอกต้นฉบับ) เขียนเป็นภาษาอังกฤษเพราะ agent อ่านได้ตรงที่สุด แทนค่าในวงเล็บแหลมด้วยของโปรเจกต์เรา วางไว้ที่ .agents/skills/test-<app>/SKILL.md แล้วทำ symlink ให้ .claude/skills ชี้มาที่โฟลเดอร์นี้แบบเดียวกับ T3

.agents/skills/test-<app>/SKILL.md
---
name: test-<app>
description: Verify <app>'s web UI in a real browser against isolated dev
  state. Use after any user-visible change. Not for unit tests.
---

# Test <app>

Use <browser tools available in this harness>. If they are missing, say what
is blocked and stop. Do not install another automation tool to get around it.

## Start
Reuse a healthy dev server from this task. Otherwise run `<dev command>`
and keep its terminal. Read the real port from its output; never guess it.
State lives in <worktree-local data dir>. Never point the app at <real data>.

## Data
Test with realistic data. Read references/fixtures.md only when seeding.
Stop the server before writing fixtures directly.

## Drive
Open <url>, take a snapshot, then walk the exact flow the change touched.

## Prove and keep
Capture what proves the flow works (screenshot, recording, DB row).
Leave the server running for the user. Ending a turn is not teardown.
Stop only processes you started, by the PID you recorded.
Keep tokens and secrets out of screenshots, commits and replies.

ก่อนใช้ ลองถามคำถามสามข้อกับโปรเจกต์ของเรา

ข้อมูลจริงของเราอยู่ที่ไหน และตอน dev มันถูกแยกออกมาแล้วหรือยัง ถ้ายัง ให้แก้ตรงนั้นก่อนเขียน skill เพราะกฎ "ห้ามแตะข้อมูลจริง" ที่เขียนไว้ในโค้ดเลย แข็งแรงกว่าที่เขียนไว้ใน skill มีวิธีเอาข้อมูลจริงมาทดสอบแบบทางเดียวหรือยัง (T3 มี vp run migrate-dev-db ที่คัดลอกข้อมูลจริงแบบอ่านอย่างเดียวมาไว้ใน worktree แล้วตัดงานที่ตั้งเวลาไว้กับ session ออก) และมีสคริปต์ช่วยใส่ fixture ที่สำรองข้อมูลให้ก่อนเขียนหรือยัง


ใช้ใน Claude Desktop (แท็บ Code)

ตัว test-t3-app เองเอามาลงใน Claude Desktop ตรง ๆ ไม่ได้ เพราะมันเรียกเครื่องมือ Browser panel ของ T3 ซึ่งไม่มีใน Claude ลงไปแล้วมันจะเจอว่าไม่มีเครื่องมือ แล้วหยุดตามกฎข้อแรกของตัวเองทุกรอบ ที่ทำได้คือเอาโครงของมันมาเขียน skill ของโปรเจกต์เราใหม่ ให้เรียกเบราว์เซอร์ในตัวของ Claude แทน

แท็บ Code ของ Claude Desktop คือ Claude Code ที่รันอยู่ในแอป ตอนเขียนบทความนี้ แอปมีแผงเบราว์เซอร์ในตัว (browser pane) ที่ agent สั่งได้ บทความในหมวดนี้ทุกบทก็ถูกตรวจด้วยแผงนี้ ส่วนนี้จึงเขียนจากของที่ใช้อยู่จริง ข้อควรรู้คือชื่อเครื่องมือของแอปเปลี่ยนตามเวอร์ชันได้ ถ้าไม่ตรง ให้เชื่อชื่อที่เห็นในแอป

ขั้นที่ 1 · บอกแอปว่าเปิด dev server ยังไง

สร้างไฟล์ .claude/launch.json ไว้ที่ราก repo ข้างล่างคือไฟล์จริงของเว็บนี้ agent จะใช้เครื่องมือ preview_start พร้อมชื่อ astro-dev เพื่อเปิด server แล้วเปิดแท็บที่พอร์ตนั้นให้เอง ถ้า server เปิดอยู่แล้วก็ใช้ตัวเดิม ข้อนี้ตรงกับกฎ "ใช้ dev server เดิมถ้ายังดี" ของ test-t3-app พอดี

.claude/launch.json
{
  "version": "0.0.1",
  "configurations": [
    {
      "name": "astro-dev",
      "runtimeExecutable": "npm",
      "runtimeArgs": ["run", "dev"],
      "port": 4321,
      "autoPort": true
    }
  ]
}

ขั้นที่ 2 · เทียบเครื่องมือของ T3 กับของ Claude

T3 CodeClaude Desktopใช้ทำอะไร
preview_statustabs_contextดูว่ามีแท็บอะไรเปิดอยู่
preview_open, preview_navigatepreview_start, navigateเปิด dev server และพาแท็บไปหน้าที่ต้องการ
preview_snapshotread_page, get_page_textอ่านโครงหน้าและข้อความ
preview_click, preview_typefind แล้ว computerหาปุ่มแล้วคลิกหรือพิมพ์
preview_evaluatejavascript_toolอ่านค่าจริงจากหน้าเว็บ
preview_resizeresize_windowลองขนาดจอมือถือ
preview_recording_*ยังไม่เจอตัวเทียบใช้ภาพหน้าจอกับค่าที่อ่านได้แทน

ใน Claude Desktop ยังมี preview_logs สำหรับอ่าน output ของ dev server, read_console_messages สำหรับดู error ใน console และ preview_stop สำหรับปิด server ที่ตัวเองเปิด สามตัวนี้ทำหน้าที่ข้อ "อ่านค่าจริง" กับ "ปิดเฉพาะที่ตัวเองเปิด" ของ test-t3-app ได้ตรงตัว

ขั้นที่ 3 · วาง skill

วางไฟล์ไว้ที่ .claude/skills/test-<app>/SKILL.md ใน repo (หรือที่ .agents/skills/ แล้วทำ symlink แบบ T3) แม่แบบข้างล่างแปลงมาจากแม่แบบก่อนหน้า และเพิ่มสองกฎที่ได้มาจากการใช้แผงนี้จริง

.claude/skills/test-<app>/SKILL.md
---
name: test-<app>
description: Verify <app>'s web UI in Claude's built-in browser against
  isolated dev state. Use after any user-visible change.
---

# Test <app> in Claude Desktop

Use the built-in browser tools (preview_start, navigate, read_page, find,
computer, javascript_tool). If they are missing, say what is blocked and
stop. Do not install Playwright or drive another browser instead.

## Start
Start the dev server with preview_start {name: "<name in launch.json>"}.
Never start it from a shell; preview_start reuses a running server.
Data lives in <isolated dev data>. Never point the app at <real data>.

## Check the pane before measuring
Read window.innerWidth first. If it is 0 the pane is hidden: call
resize_window with the desktop preset, then the size you need, and re-read.

## Drive
navigate to the page. Read it with read_page or get_page_text.
Act with find, then computer left_click or type.
Prefer reading the page over screenshots; screenshots can time out.

## Prove and keep
Capture what proves the flow: read_page output, values read with
javascript_tool, one screenshot if it works. Check read_console_messages.
Leave the server running while the user looks. Stop it later with
preview_stop, never by killing node by name.

กับดักของแผงเบราว์เซอร์ที่เจอจริงตอนตรวจบทความในหมวดนี้

ถ้าแผงถูกพับหรืออยู่ด้านหลัง window.innerWidth จะอ่านได้ 0 และทุกค่าที่วัดจากหน้าจอจะผิดหมด รอบหนึ่งวัดได้ว่ากรอบบทความกว้างแค่ 74 พิกเซล เกือบสรุปผิดไปแล้วว่าหน้าพัง พอปรับขนาดแผงใหม่ค่าก็กลับมาปกติ อีกเรื่องคือภาพหน้าจอบางครั้งค้างจนหมดเวลา ทั้งที่หน้าเว็บไม่ได้มีอะไรผิด สองเรื่องนี้คือเหตุผลที่แม่แบบสั่งให้เช็กความกว้างก่อนวัด และให้อ่านหน้าด้วยข้อความเป็นหลัก ใช้ภาพหน้าจอเป็นของเสริม

ขั้นที่ 4 · สั่งงานคู่กับ pstack

ในแท็บ Code ของ Claude Desktop
/pstack:poteto-mode ปุ่ม export CSV ในหน้ารายงานกดแล้วไม่มีไฟล์
ช่วยหาสาเหตุแล้วแก้ ใช้ /test-<app> ยืนยันในเบราว์เซอร์ของ Claude
บอกค่าที่อ่านได้จากหน้าจอก่อนและหลังแก้ แล้วเปิด server ทิ้งไว้

ประโยค "บอกค่าที่อ่านได้จากหน้าจอ" สำคัญกว่าที่เห็น ใน Claude Desktop การอ่านหน้าด้วย read_page หรือ javascript_tool เชื่อได้มากกว่าภาพหน้าจอ เพราะได้เป็นตัวเลขกับข้อความที่ตรวจซ้ำได้ ส่วน pairing URL กับ token ของ T3 ไม่มีในกรณีนี้ แต่หลักเดิมยังใช้ได้ คือรหัสผ่านและ token ห้ามติดไปในภาพ commit หรือข้อความตอบ


ลอง T3 Code

ต้องลงและ login agent อย่างน้อยหนึ่งตัวก่อน เช่น Claude Code ใช้ claude auth login หรือ Codex ใช้ codex login จากนั้นติดตั้ง T3 Code แบบใดแบบหนึ่ง

ติดตั้งแบบรันจากเทอร์มินัล
# macOS / Linux
curl -fsSL https://t3.codes/install.sh | sh

# Windows (PowerShell)
irm https://t3.codes/install.ps1 | iex

# หรือลองครั้งเดียวโดยไม่ติดตั้ง
npx t3@latest

แล้วพิมพ์ t3 เพื่อเปิด server กับเว็บแอปในเครื่อง ถ้าอยากได้แอปเดสก์ท็อป ใช้ตัวจัดการแพ็กเกจได้

ติดตั้งแอปเดสก์ท็อป
winget install T3Tools.T3Code        # Windows
brew install --cask t3-code          # macOS

เรื่องที่ควรรู้ก่อนใช้กับงานจริง

README ของ T3 Code เขียนเองว่าโปรเจกต์ยังอยู่ช่วงต้นมาก ให้เตรียมใจเจอบั๊ก ตัวเลขผู้ใช้กับจำนวนดาวบน GitHub ที่เห็นบนเว็บเป็นตัวเลขที่ทีมอ้างเอง และเครื่องมือ preview_* ที่บทนี้พูดถึงมาจากโค้ดใน repo วันที่ 12 ตุลาคม 2026 ซึ่งเปลี่ยนเร็วมาก ถ้าชื่อเครื่องมือไม่ตรงกับที่เห็นในแอป ให้เชื่อของในแอป


เริ่มพรุ่งนี้เช้า ทำตามนี้

  1. เปิด test-t3-app กับ AGENTS.md อ่านคู่กัน ใช้เวลาไม่เกินสิบนาที
  2. เช็กโปรเจกต์ของเราว่าตอน dev แยกข้อมูลออกจากของจริงแล้วหรือยัง ถ้ายัง ทำข้อนี้ก่อนข้ออื่น
  3. เลือกว่าจะตรวจผ่าน T3 Code หรือผ่าน Claude Desktop ถ้าใช้ T3 ให้ติดตั้งแล้วเปิดโปรเจกต์ เลือก provider เป็น Claude ถ้าใช้ Claude Desktop ให้สร้าง .claude/launch.json ก่อน
  4. ใน thread นั้น รัน /pstack:create-verification-skill พร้อมบอกให้ใช้โครงของ test-t3-app หรือเริ่มจากแม่แบบข้างบนแล้วให้มันเติม
  5. ให้มันลองรัน skill เองครบหนึ่งรอบ แล้วเช็กว่ามันปิดเฉพาะ process ที่ตัวเองเปิดจริง
  6. ทำ symlink .claude/skills ชี้ไป .agents/skills ถ้าทีมใช้มากกว่าหนึ่ง harness
  7. คัดลอกรายการ Hit every surface ไปใส่ใน AGENTS.md หรือ CLAUDE.md ของเรา แล้วแก้ให้ตรงกับหน้าจอและ client ของเราเอง

แหล่งอ้างอิง

  1. pingdotgg/t3code, test-t3-app/SKILL.md และ references/sqlite-fixtures.md · github.com/pingdotgg/t3code (อ่านจาก main วันที่ 12 ตุลาคม 2026 เนื้อหาในบทนี้เป็นการสรุปและแปลความของผม ไม่ได้คัดลอกต้นฉบับ)
  2. pingdotgg/t3code, AGENTS.md, README.md, test-t3-mobile/SKILL.md, t3.json และรายชื่อเครื่องมือใน apps/server/src/mcp/toolkits/preview/tools.ts · github.com/pingdotgg/t3code (ที่มาของกฎสามข้อ, Hit every surface, การตรวจ, คำสั่งติดตั้ง และชื่อเครื่องมือ preview_*)
  3. pingdotgg/t3code, docs/user/outside-agents.md · github.com (ที่มาของการต่อ Claude Code ผ่าน MCP สิทธิ์ และอายุ sign-in)
  4. T3 Tools, T3 Code · t3.codes (ข้อมูล open source, ไม่ขาย token, หนึ่ง thread หนึ่ง branch และตัวเลขที่ทีมอ้างเอง)
  5. Uzair Ansar, p3-stack · github.com/uzairansaruzi/p3-stack (pstack ที่พอร์ตมาใช้กับเครื่องมือของ T3 Code เป็นงานของบุคคล อ่านจาก commit เดียววันที่ 5 ตุลาคม 2026)
  6. Michael Denyer, pstack-claude · github.com/michael-denyer/pstack-claude (ที่มาของพฤติกรรม poteto-mode ที่มองหา skill ชื่อ verify)