ลงมือจริง: เดิน pipeline grill → spec → tickets → implement → review
ห้าบทที่ผ่านมาเราแยกดู skill ของ mattpocock/skills ทีละตัว บทนี้เป็นบทปิดแทร็ก
ที่เราจะ ไม่แนะนำ skill ใหม่ แต่จะเอา 5 skill ที่เป็น "เส้นทางหลัก" มาเดินต่อกันจริงบนโจทย์เดียว
ตั้งแต่ยังไม่มีอะไรในหัวชัดเจน จนถึงโค้ด merge เข้า branch พร้อมรีวิวผ่าน
เพื่อให้เห็นภาพว่า pipeline ที่ประกอบจาก skill เดี่ยวๆ หน้าตาเป็นยังไงตอนใช้งานจริง
ไม่ใช่แค่ทฤษฎีแยกส่วน
และท้ายบทเราจะตอบคำถามที่ค้างมาตั้งแต่ MP-0 ให้จบ: เมื่อไรควรใช้ mattpocock/skills เทียบกับ Superpowers (แทร็กที่เราเรียนไปก่อนหน้านี้ใน SP-0 ถึง SP-7) และผสมสองแทร็กเข้าด้วยกันได้ไหม
ตั้งโจทย์จริง 1 อัน — ฟีเจอร์ยกเลิกออเดอร์อัตโนมัติ
สมมติทีมของคุณมีระบบ e-commerce: NestJS เป็น API หลักคุยกับ PostgreSQL และมี Go worker service แยกต่างหากสำหรับงาน background job เช่น cron/queue processing ตอนนี้ product owner ขอฟีเจอร์ใหม่:
"ถ้าลูกค้าสร้างออเดอร์แล้วไม่จ่ายเงินภายใน 30 นาที ให้ระบบยกเลิกออเดอร์นั้นอัตโนมัติ คืน stock กลับเข้าคลัง"
โจทย์นี้เหมาะกับการเดิน pipeline เต็มเพราะ มีความกำกวมที่ต้องสัมภาษณ์ตัวเองก่อน (30 นาทีนับจากไหน?
"ไม่จ่ายเงิน" หมายถึงสถานะไหนใน order เป๊ะๆ?), แตะหลาย layer (schema, NestJS API, Go worker),
และเป็นงานที่ยาวเกินจะทำในหน้าต่างเดียวจบ — พอดีกับที่ ask-matt
(router ของ mattpocock/skills) เรียกว่า
multi-session build
แผนที่เต็มเส้นทาง — 5 skill ต่อกันเป็น pipeline เดียว
ก่อนเดินทีละก้าว มาดูภาพรวมก่อนว่าแต่ละ skill รับอะไรเข้า ส่งอะไรออก และใครขับใครอยู่ข้างใน
สังเกตว่าทุก skill ในนี้ประกาศ disable-model-invocation: true ใน SKILL.md ของมัน —
แปลว่า agent จะไม่เรียกเองอัตโนมัติ ต้องเป็นคุณพิมพ์ slash command เรียกทีละตัว
นี่คือปรัชญาการออกแบบของทั้งแทร็ก: คุณคุมจังหวะทุกก้าว ไม่ใช่ agent ตัดสินใจแทน
ก้าวที่ 1 — /grill-with-docs: สัมภาษณ์ตัวเองให้คมก่อนเขียน spec
SKILL.md ของ grill-with-docs สั้นมาก แค่บอกให้รัน /grilling โดยใช้
/domain-modeling เป็นเครื่องมือ พูดง่ายๆ คือ agent จะสัมภาษณ์คุณแบบ "ไม่ปล่อยผ่าน"
ทุกคำที่คลุมเครือ แล้วบันทึกผลลัพธ์ลง CONTEXT.md (glossary ของโปรเจกต์) และ
ADR
(บันทึกการตัดสินใจที่ย้อนกลับยาก) ไปพร้อมกัน — ต่างจาก /grill-me ที่ไม่มี codebase ให้อ้างอิง
จึงไม่เก็บอะไรถาวร (stateless)
สมมติคุณพิมพ์:
/grill-with-docs ฟีเจอร์ยกเลิกออเดอร์อัตโนมัติถ้าไม่จ่ายเงินใน 30 นาที
agent จะไม่รีบเขียนโค้ด แต่ถามกลับแบบนี้:
- "ไม่จ่ายเงิน" ตรงกับ
OrderStatusenum ค่าไหนใน DB ปัจจุบัน —PENDING_PAYMENTเท่านั้น หรือรวมPAYMENT_FAILEDด้วย? - 30 นาทีนับจาก
created_atหรือจาก event "สร้าง payment intent"? สองอย่างนี้ไม่เท่ากันถ้าลูกค้าเปิดหน้าจ่ายเงินทิ้งไว้นาน - ทำไมต้องยกเลิกใน Go worker แทนที่จะเป็น cron ใน NestJS เอง? (คำตอบอาจเป็น: worker คุมโหลด queue รวมอยู่แล้ว ไม่อยากเพิ่ม scheduler ซ้ำสอง service)
หลังคุยจบ ผลลัพธ์ที่ได้ไม่ใช่แค่คำตอบในแชท แต่ถูกบันทึกจริง เช่น
// CONTEXT.md — เพิ่ม glossary term ใหม่
unpaid grace window: ช่วงเวลา 30 นาทีนับจาก order.created_at
ที่ order สถานะ PENDING_PAYMENT ยังไม่ถูกยกเลิก
// ADR-0007.md — บันทึกการตัดสินใจ
Decision: การยกเลิกออเดอร์อัตโนมัติทำใน Go worker ไม่ใช่ NestJS cron
Reason: worker คุม queue/cron ของระบบอยู่แล้ว รวม scheduler ที่เดียว
นี่คือความต่างสำคัญจาก "คุยเฉยๆ ในแชท": พอปิดแชทวันนี้ พรุ่งนี้เปิด session ใหม่มา
CONTEXT.md และ ADR ยังอยู่ — agent (หรือเพื่อนร่วมทีมคนอื่น) อ่านแล้วเข้าใจ "ทำไม" ได้ทันที
ไม่ต้องสัมภาษณ์ซ้ำ
ก้าวที่ 2 — /to-spec: สังเคราะห์สิ่งที่คุยไปแล้ว ไม่ใช่ถามต่อ
จุดสำคัญที่สุดของ to-spec ที่ SKILL.md เขียนตัวหนาไว้เลย:
Do NOT interview the user — just synthesize what you already know.
พูดอีกแบบ: ก้าวที่ 1 คือช่วง "ขุดคำถาม" ก้าวที่ 2 คือช่วง "เก็บของที่ขุดได้มาจัดเรียงเป็นเอกสาร" agent จะสำรวจ repo ต่อ (ถ้ายังไม่ได้ทำ) แล้วหา seam สำหรับเทสฟีเจอร์นี้ — เลือก seam ที่มีอยู่แล้วก่อนสร้างใหม่ และให้อยู่ "จุดสูงสุด" เท่าที่ทำได้ (ยิ่งน้อย seam ทั่ว codebase ยิ่งดี อุดมคติคือ 1 จุด) แล้วเช็กกับคุณว่าตรงกับที่คาดไว้ไหม ก่อนเขียน spec จริง
spec ใช้ template ตายตัว 7 ส่วน: Problem Statement, Solution, User Stories (list ยาวแบบ "As a <actor>, I want <feature>, so that <benefit>"), Implementation Decisions, Testing Decisions, Out of Scope, Further Notes — ห้ามใส่ file path หรือโค้ดเจาะจง เพราะมันล้าสมัยเร็วเกินไป (ยกเว้นถ้ามี prototype ที่ให้ snippet แม่นยำกว่าคำบรรยาย เช่น schema/state machine)
สำหรับฟีเจอร์ของเรา spec ส่วน seam กับ Implementation Decisions อาจหน้าตาแบบนี้:
## Implementation Decisions
- Seam เดียว: OrdersService.cancelIfUnpaid() — รวม logic ตรวจสอบ + ยกเลิก + คืน stock ไว้จุดเดียว
- Go worker เรียก seam นี้ผ่าน internal API ที่ NestJS เปิดให้ ไม่ query DB ตรงเอง
(กันไม่ให้ business rule กระจายสอง service)
- คืน stock ต้องอยู่ transaction เดียวกับการเปลี่ยนสถานะ order เป็น CANCELLED
## Testing Decisions
- เทสที่ seam cancelIfUnpaid() เท่านั้น ไม่ต้อง mock queue ของ Go worker
- Prior art: เทสรูปแบบเดียวกับ OrdersService.markAsPaid() ที่มีอยู่แล้ว
เสร็จแล้ว agent publish spec นี้ขึ้น issue tracker ของโปรเจกต์ พร้อมติด label ready-for-agent
— ไม่ต้อง triage เพิ่ม เพราะ spec ที่มาจาก to-spec ถือว่า "agent-ready" โดยตัวมันเองอยู่แล้ว
ก้าวที่ 3 — /to-tickets: ตัดเป็น vertical slice พร้อม blocking edges
ก้าวนี้แตกงานจาก spec เป็น ticket แบบ tracer-bullet vertical slice — แต่ละใบต้องตัดผ่านทุก layer แบบแคบแต่ครบ จบแล้ว demo/verify ได้ด้วยตัวเอง และพอดีกับ context window เดียว ถ้ามี prefactor (ปรับโครงให้ทำ feature ง่ายขึ้น) ให้ทำก่อนเสมอ — "Make the change easy, then make the easy change"
แต่ละ ticket ต้องประกาศ blocking edges — ใครต้องเสร็จก่อนตัวเองถึงเริ่มได้ ticket ที่ไม่มี blocker คือ frontier ที่หยิบมาทำได้เลย
| # | Ticket | Blocked by | สิ่งที่ส่งมอบ |
|---|---|---|---|
| 01 | เพิ่มคอลัมน์ grace_deadline + migration | None — เริ่มได้ทันที | Schema พร้อมใช้ (prefactor) |
| 02 | NestJS: endpoint ตรวจสอบ/ยกเลิกออเดอร์เกิน deadline | 01 | เรียก API แล้ว order สถานะเปลี่ยนถูกต้อง คืน stock |
| 03 | Go worker: poll ทุกนาทีแล้วเรียก endpoint 02 | 01, 02 | ฟีเจอร์ทำงานจบ end-to-end แบบอัตโนมัติ |
ก่อน publish agent ต้องเอา breakdown นี้มา "quiz" คุณก่อน: granularity โอเคไหม (หยาบ/ละเอียดไป),
blocking edges ถูกไหม, ต้องรวม/แยก ticket ไหนเพิ่มไหม — เมื่อคุณ approve แล้วค่อย publish จริง
ถ้าใช้ tracker จริงก็ publish เป็น issue เรียงตาม dependency ใช้ native blocking link
ถ้าเป็น local ก็เขียนไฟล์แยกใต้ .scratch/<feature-slug>/issues/01-...md ทีละไฟล์
ข้อยกเว้นที่ SKILL.md เตือนไว้: ถ้างานเป็น "wide refactor" (เช่น rename column ที่มี call site เป็นพันจุดทั่ว repo) อย่าฝืนตัดเป็น tracer bullet เพราะจะไม่มี slice ไหนเขียวได้เดี่ยวๆ ให้ใช้แพทเทิร์น expand–contract แทน: expand เพิ่มรูปแบบใหม่คู่ของเดิมก่อน, migrate ทีละ batch ตาม blast radius โดย CI เขียวตลอด, แล้ว contract ลบของเก่าทิ้งท้ายสุด
ก้าวที่ 4 — /implement: ทำทีละ ticket ขับด้วย /tdd
SKILL.md ของ implement สั้นแค่ไม่กี่บรรทัดแต่ครบ: ใช้ /tdd ที่ seam ที่ตกลงกันไว้แล้ว
(จากขั้น to-spec), รัน typecheck และเทสไฟล์เดี่ยวเป็นระยะ, รัน full test suite ครั้งเดียวตอนจบ,
แล้วปิดท้ายด้วย /code-review ก่อน commit เข้า branch ปัจจุบัน
หัวใจของก้าวนี้คือ ทำทีละ ticket จาก frontier — เริ่มจากใบที่ไม่มี blocker
(ticket 01) ทำจบ commit แล้ว เคลียร์ context ก่อนเปิดใบถัดไป (ticket 02) เพื่อไม่ให้
session รกจนโมเดล "คิดหลุด" — นี่คือ context hygiene ที่ ask-matt เน้นย้ำ:
เก็บ grill → spec → tickets ไว้ใน context เดียวไม่ compact จนกว่าจะจบ to-tickets
แต่พอเข้า implement ให้แต่ละ ticket เริ่ม context ใหม่เสมอ
// ticket 01: implement + tdd loop (red → green)
/implement ticket 01 — grace_deadline column
// เทสแดงก่อน: migration ยังไม่มีคอลัมน์
test("order มี grace_deadline หลัง migration", ...)
// เขียน migration ให้น้อยที่สุดจนเทสเขียว
ALTER TABLE orders ADD COLUMN grace_deadline TIMESTAMP;
// typecheck + เทสไฟล์นี้ผ่าน → commit → เคลียร์ context → เปิด ticket 02
พอถึง ticket 03 (ฝั่ง Go worker) หลักการเดียวกันเป๊ะ แค่คำสั่งเทสเปลี่ยนเป็น go test ./...
และ seam ที่เทสคือฟังก์ชัน poll ของ worker แทน — /implement ไม่สนใจว่าเป็นภาษาไหน
มันสนใจแค่ "มี spec/ticket ไหม ใช้ /tdd ที่ seam ไหน"
ก้าวที่ 5 — /code-review: สองรายงานคู่ขนาน ไม่ปนกัน
จบทุก ticket แล้ว ก้าวสุดท้ายคือรีวิว diff ทั้งหมดตั้งแต่จุดเริ่มฟีเจอร์ (git diff base...HEAD
แบบ three-dot เทียบกับ merge-base) ผ่านสอง sub-agent คู่ขนานที่ทำงานคนละแกน:
| แกน | ตรวจอะไร |
|---|---|
| Standards | โค้ดตรงกับมาตรฐานที่ repo เขียนไว้ไหม (เช่น CODING_STANDARDS.md) บวก "smell baseline" 12 ข้อจาก Fowler เช่น Feature Envy, Primitive Obsession, Shotgun Surgery — repo override ได้เสมอ และทุกจุดเป็น judgement call ไม่ใช่ violation ตายตัว |
| Spec | โค้ดทำตามที่ spec/ticket ขอจริงไหม มีอะไรขาด, มีอะไรเกิน (scope creep), มีอะไรดูเหมือนทำแล้วแต่ผิด |
Code that follows every standard but implements the wrong thing → Standards pass, Spec fail. Code that does exactly what the issue asked but breaks the project's conventions → Spec pass, Standards fail.
เหตุผลที่แยกสองรายงานไม่ merge กัน: ถ้าเอามารวมกันจะมีแกนหนึ่ง "บดบัง" อีกแกน เช่น โค้ดที่ผ่าน Standards ทุกข้อ อาจทำให้ทุกคนละเลยว่ามันลืมใส่ logic ที่ spec ขอ (คืน stock) ไปเลยก็ได้ รายงานสุดท้ายจึงมีแค่สรุปบรรทัดเดียวต่อแกน ไม่มีการเลือก "ผู้ชนะ" ข้ามแกน
ทำไม pipeline นี้ถึง "composable" — หยิบทีละตัวได้ ควบคุมทุกจังหวะ
จุดสำคัญที่ต่างจากหลาย workflow อัตโนมัติ: ไม่มีอะไรบังคับให้เดินครบ 5 ขั้นเสมอไป
ทุก skill เรียกแยกได้เอง เพราะออกแบบเป็น disable-model-invocation: true ทั้งหมด — คุณสั่งเอง
ทีละคำสั่ง ไม่ใช่โมเดลตัดสินใจเรียกให้ ตัว ask-matt (router ของแทร็กนี้) วางเงื่อนไข "ทางแยก" ไว้ชัดว่า:
- คุยในแชทตอบคำถามได้ครบไหม? ถ้าต้องพิสูจน์ด้วยโค้ดจริง (state, UI) ให้แวะ
/handoffออกไป/prototypeก่อน - เป็นงาน multi-session ไหม? ใช่ → เดินเต็ม to-spec + to-tickets ก่อนแตก implement ทีละใบ
ไม่ใช่ → ข้าม to-spec/to-tickets ไปเลย ใช้
/implementจบใน context เดียวได้ทันที
ตัวอย่างจริง: ถ้าโจทย์ของคุณแค่ "แก้ validation message ผิดจุดเดียวใน NestJS DTO" — ไม่ต้อง grill
ไม่ต้อง spec ไม่ต้อง ticket เลย พิมพ์ /implement ตรงๆ พร้อมอธิบายสั้นๆ ก็จบ
แต่ถ้าโจทย์ใหญ่แบบฟีเจอร์ยกเลิกออเดอร์ที่คุยกันมาทั้งบท — เดินเต็ม pipeline คุ้มค่ากว่ามาก
เพราะมันข้าม 2 service และมีการตัดสินใจที่ย้อนกลับยาก (ADR) แฝงอยู่
กฎง่ายๆ ในการเลือกว่าจะเดินกี่ขั้น: ถามตัวเองว่า "ถ้าฟีเจอร์นี้ทำพลาด จะเสียเวลาแก้กี่วัน?" ถ้าคำตอบคือ "ไม่กี่นาที" ข้ามไป implement ตรงๆ ถ้าคำตอบคือ "หลายวันเพราะกระทบหลาย service" ให้เดินเต็ม grill → spec → tickets ก่อนเสมอ
ตารางเทียบ: เมื่อไรใช้ mattpocock/skills เทียบ Superpowers
ทั้งสองแทร็กที่เราเรียนมา (SP-0 ถึง SP-7 และ MP-0 ถึงบทนี้) แก้ปัญหาเดียวกัน — ทำให้ agent ทำงานกับโค้ดจริงอย่างมีวินัย — แต่เลือก "จุดควบคุม" ต่างกัน:
| แกน | mattpocock/skills | Superpowers |
|---|---|---|
| การเรียกใช้ | slash command ทีละตัว disable-model-invocation: true ทั้งหมด — คุณสั่งเองทุกจังหวะ |
มี core loop (brainstorm → plan → implement → review) ที่วนต่อกันลื่นกว่า และหลาย skill model-invoked เรียกเองได้ |
| หน่วยของงาน | ticket = tracer-bullet vertical slice ผูก blocking edge บน tracker จริง (frontier ชัดเจนว่าทำใบไหนได้) | plan.md เป็น task list ในโปรเจกต์ ขับด้วย TDD loop ต่อ task |
| Context hygiene | ระบุชัดเจนเป็นกฎ: เคลียร์ context ระหว่าง ticket, อ้างถึง "smart zone" เป็นขีดจำกัดตรงๆ | ใช้ subagent แยก context เหมือนกัน (ดู SP-4 multi-agent) แต่เน้นการ dispatch agent มากกว่าผูกกับ tracker |
| Review | /code-review เดียว รัน 2 แกนคู่ขนาน (Standards + Spec) แยกรายงาน ไม่ merge |
แยกเป็น 3 skill: requesting-code-review, receiving-code-review, finishing-a-development-branch (ดู SP-5) |
| เหมาะกับ | ทีมที่มี issue tracker จริงจัง อยากได้ artifact เป็นเอกสาร (spec, ticket, ADR) ที่คนอื่นอ่านต่อได้ | งานเดี่ยวหรือทีมเล็กที่อยากให้ agent ขับ loop เองได้มากกว่า เน้น skill library ที่เรียกได้ทั้งจากคนและโมเดล |
ผสมกันได้ไหม? ได้ — และหลายทีมทำแบบนี้จริง เพราะทั้งสองแทร็กสุดท้ายก็เป็นแค่ไฟล์
SKILL.md ที่ agent อ่านแล้วเรียกผ่าน slash command ไม่มีอะไรห้ามผสม เช่น
ใช้ /grill-with-docs + /to-spec + /to-tickets จาก MP
เพื่อได้เอกสาร spec/ticket ที่ชัดเจนก่อน แล้วสลับไปใช้ subagent-driven development loop ของ Superpowers
ตอน implement แต่ละ ticket จริง จากนั้นปิดท้ายด้วย /code-review สองแกนของ MP อีกที
— เลือกเครื่องมือที่ตอบโจทย์แต่ละจังหวะ ไม่ต้องยึดติดแทร็กเดียวทั้งหมด
- เลือกฟีเจอร์จริงที่ค้างอยู่ใน backlog ของคุณ (NestJS หรือ Go ก็ได้) เขียนสั้นๆ ว่าถ้าเดินผ่าน 5 ขั้นในบทนี้ แต่ละขั้นจะได้ output อะไรบ้าง (เช่น glossary term ใหม่คืออะไร, seam อยู่ตรงไหน, ticket กี่ใบ)
- ลองแตกฟีเจอร์นั้นเป็น tracer-bullet ticket อย่างน้อย 2 ใบ ระบุ blocking edges ให้ชัด แล้วบอกว่าตอนนี้ frontier (ticket ที่เริ่มได้ทันที) คือใบไหน
- เขียนย่อหน้าสั้นๆ ตัดสินใจว่าทีม/โปรเจกต์ของคุณตอนนี้เหมาะกับ mattpocock/skills, Superpowers, หรือผสมทั้งสอง — พร้อมเหตุผล 2-3 ข้อจากตารางเทียบด้านบน แล้วลองพิมพ์คุยกับ Claude ในแชท ให้ช่วยวิจารณ์การตัดสินใจนั้นอีกที