teach-me-ai / sources / MP-6
⟵ กลับหน้าแทร็กเสริม
MP-6 · mattpocock/skills

ลงมือจริง: เดิน pipeline grill → spec → tickets → implement → review

⏱️ ~25 นาที 🎯 ร้อยทุก skill เป็น pipeline เดียวบนงานจริง และรู้ว่าเมื่อไรใช้ MP เทียบ Superpowers 📦 ต้นทาง: ทั้งแทร็ก MP รวมกัน

ห้าบทที่ผ่านมาเราแยกดู 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 รับอะไรเข้า ส่งอะไรออก และใครขับใครอยู่ข้างใน

/grill-with-docs สัมภาษณ์ตัวเอง → CONTEXT.md + ADR /to-spec สังเคราะห์ ไม่ถาม → spec บน tracker /to-tickets tracer-bullet slice → tickets + blocking edge /implement ขับ /tdd ข้างใน → โค้ด + เทสผ่าน /code-review 2 แกนคู่ขนาน → Standards + Spec ticket ถัดไป → context ใหม่ (frontier) /tdd red→green แต่ละกล่องเรียกแยกได้เอง (user-invoked) — ไม่มีอะไรบังคับให้เดินครบทุกขั้นเสมอไป
pipeline หลักของ mattpocock/skills: grill-with-docs → to-spec → to-tickets → implement (ขับ /tdd ภายใน วนทีละ ticket) → code-review (แยก 2 รายงาน)
💡

สังเกตว่าทุก 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 จะไม่รีบเขียนโค้ด แต่ถามกลับแบบนี้:

  • "ไม่จ่ายเงิน" ตรงกับ OrderStatus enum ค่าไหนใน 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 ที่หยิบมาทำได้เลย

#TicketBlocked byสิ่งที่ส่งมอบ
01เพิ่มคอลัมน์ grace_deadline + migrationNone — เริ่มได้ทันทีSchema พร้อมใช้ (prefactor)
02NestJS: endpoint ตรวจสอบ/ยกเลิกออเดอร์เกิน deadline01เรียก API แล้ว order สถานะเปลี่ยนถูกต้อง คืน stock
03Go worker: poll ทุกนาทีแล้วเรียก endpoint 0201, 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/skillsSuperpowers
การเรียกใช้ 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 อีกที — เลือกเครื่องมือที่ตอบโจทย์แต่ละจังหวะ ไม่ต้องยึดติดแทร็กเดียวทั้งหมด

🎯 แบบฝึกหัด
  1. เลือกฟีเจอร์จริงที่ค้างอยู่ใน backlog ของคุณ (NestJS หรือ Go ก็ได้) เขียนสั้นๆ ว่าถ้าเดินผ่าน 5 ขั้นในบทนี้ แต่ละขั้นจะได้ output อะไรบ้าง (เช่น glossary term ใหม่คืออะไร, seam อยู่ตรงไหน, ticket กี่ใบ)
  2. ลองแตกฟีเจอร์นั้นเป็น tracer-bullet ticket อย่างน้อย 2 ใบ ระบุ blocking edges ให้ชัด แล้วบอกว่าตอนนี้ frontier (ticket ที่เริ่มได้ทันที) คือใบไหน
  3. เขียนย่อหน้าสั้นๆ ตัดสินใจว่าทีม/โปรเจกต์ของคุณตอนนี้เหมาะกับ mattpocock/skills, Superpowers, หรือผสมทั้งสอง — พร้อมเหตุผล 2-3 ข้อจากตารางเทียบด้านบน แล้วลองพิมพ์คุยกับ Claude ในแชท ให้ช่วยวิจารณ์การตัดสินใจนั้นอีกที