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

จากบทสนทนา → Spec → Tickets

⏱️ ~22 นาที 🎯 แปลงสิ่งที่คุยเป็น spec และแตกเป็นตั๋วงานที่ทำได้จริง 📦 ต้นทาง: skills/to-spec · to-tickets · wayfinder · triage

บท MP-1 เราคุยกันจนได้ทิศทางที่ทั้งสองฝ่าย (คุณกับ agent) เห็นตรงกันแล้ว — alignment เกิดขึ้นแล้ว แต่บทสนทนาในหัว agent ไม่ใช่สิ่งที่ทีมคุณเอาไป track งานได้ บทนี้คือขั้นถัดไป: เอาบทสนทนานั้นมา สังเคราะห์เป็นเอกสารที่จับต้องได้ (spec) แล้ว แตกเป็นตั๋วงาน (tickets) ที่คนอื่นในทีม — หรือ agent อีกตัวที่ไม่มี context การคุยนี้เลย — หยิบไปทำต่อได้ทันที

ทำไมสำคัญกับงาน dev จริง? เพราะปัญหาที่พบบ่อยที่สุดของทีมที่ใช้ AI ช่วยงาน คือ "คุยกันดีมาก แต่ไม่มีอะไรหลงเหลือเป็นเอกสาร" — พอ session จบ context หาย คนต่อไป (หรือแม้แต่ agent คนเดิมใน session ใหม่) ต้องเริ่มถามใหม่หมด 4 skill ในบทนี้ — to-spec, to-tickets, wayfinder, triage — คือชุดเครื่องมือที่แก้ปัญหานี้ตรง ๆ โดยผูกกับ issue tracker ของทีมคุณเสมอ ไม่ใช่แค่คุยแล้วจบ

1. to-spec — สังเคราะห์ ไม่ใช่สัมภาษณ์ซ้ำ

จุดที่ผู้เรียนพลาดบ่อยที่สุด: คิดว่า to-spec จะมาถามคำถามคุณอีกรอบ เหมือน brainstorming ใน Superpowers — แต่ไฟล์จริงบอกตรงข้ามเป๊ะ ในบรรทัดแรกของ SKILL.md:

# to-spec/SKILL.md
description: Turn the current conversation into a spec
and publish it to the project issue tracker —
no interview, just synthesis of what you've already discussed.

หมายความว่า ณ จุดที่คุณเรียก to-spec คุณต้องคุยกับ agent มาพอสมควรแล้ว (นี่คือสิ่งที่ MP-1 เตรียมไว้ให้) หน้าที่ของ skill นี้คือ:

  1. Explore repo — ถ้ายังไม่ได้ explore มาก่อน ให้เข้าใจ codebase ปัจจุบันก่อน ใช้ศัพท์ตาม domain glossary ของโปรเจกต์ตลอดทั้ง spec และเคารพ ADR ที่มีอยู่แล้วในพื้นที่ที่กำลังแตะ
  2. ร่าง seam ที่จะเทสต์ feature นี้ — เลือก seam ที่มีอยู่แล้วก่อนเสมอ ถ้าต้อง seam ใหม่ ให้เสนอที่จุดสูงสุดที่ทำได้ (ยิ่ง seam ข้ามทั้ง codebase น้อย ยิ่งดี — เป้าหมายในอุดมคติคือ seam เดียว) แล้วเช็กกับผู้ใช้ก่อนว่าตรงกับที่คาดไว้ไหม
  3. เขียน spec ตาม template แล้ว publish ขึ้น issue tracker — ติด label ready-for-agent ทันที ไม่ต้อง triage เพิ่ม

โครง spec ตาม template จริงในไฟล์มี 6 ส่วน:

ส่วนใส่อะไร
Problem Statementปัญหาจากมุมมองผู้ใช้ ไม่ใช่มุมมอง dev
Solutionทางแก้จากมุมมองผู้ใช้เช่นกัน
User Storiesลิสต์ยาว ๆ ในรูป "As a <actor>, I want <feature>, so that <benefit>" — ต้องครอบคลุมทุกมุมของ feature
Implementation Decisionsmodule ที่จะแก้/สร้าง, interface, schema, API contract — ห้ามใส่ path ไฟล์หรือโค้ดจริง (ล้าสมัยไว) ยกเว้นเป็น snippet ที่มาจาก prototype ที่เข้ารหัส decision ได้แม่นกว่าคำบรรยาย
Testing Decisionsอะไรคือเทสที่ดี (เทสแค่ external behavior ไม่ใช่ implementation detail), module ไหนต้องเทส, มี prior art ในโค้ดเดิมไหม
Out of Scope / Further Notesกันขอบเขตบวมและกันลืมประเด็นย่อย
💡

เทียบกับงานที่คุณคุ้นเคย: spec แบบนี้เหมือน PRD ที่ทีม PM เขียน แต่เพิ่มส่วน Implementation Decisions กับ Testing Decisions ที่ dev อ่านแล้วเริ่มงานได้ทันที — ไม่ต้องเดาว่า "ควร validate ตรงไหน" หรือ "ทีมนี้เทสยังไง" เช่น ถ้าเป็น NestJS module คุณจะเห็นระบุ interface ของ service/DTO ไว้ตรงนี้เลย โดยไม่ผูกกับชื่อไฟล์

2. to-tickets — แตก spec เป็น tracer-bullet tickets

spec เดียวมักใหญ่เกินกว่าจะทำในเซสชันเดียว to-tickets แตกมันเป็นตั๋วงาน ที่เป็น tracer bullet — คำนี้สำคัญมาก มาดูกฎ 4 ข้อจาก vertical-slice-rules ในไฟล์จริง:

  • แต่ละ slice ตัดแนวตั้ง (vertical) ผ่านทุก layer — schema, API, UI, test — ให้ครบวงจร ไม่ใช่ตัดแนวนอน (horizontal) แค่ layer เดียว
  • slice ที่เสร็จแล้วต้อง demo หรือ verify ได้ด้วยตัวมันเอง
  • แต่ละ slice ต้องพอดีกับ context window สดใหม่หนึ่งรอบ (1 session)
  • งาน prefactor (ปรับโครงให้ทำง่ายขึ้น) ต้องทำก่อนเสมอ — "Make the change easy, then make the easy change"

สิ่งที่ทำให้ต่างจาก to-do list ทั่วไปคือทุกตั๋วต้องประกาศ blocking edges — ตั๋วอื่นที่ต้องเสร็จก่อนตัวนี้ถึงจะเริ่มได้ ตั๋วที่ไม่มี blocker เริ่มได้ทันที นี่คือสิ่งที่ทำให้ทีม (หรือ agent หลายตัว) รู้ได้ว่า "ตอนนี้ทำอะไรได้บ้างพร้อมกัน" — เรียกจุดนั้นว่า frontier

SPEC export ข้อมูล user เป็น CSV 01 — prefactor แยก QueryBuilder Blocked by: None → เริ่มได้ทันที 02 — ExportService query → CSV string Blocked by: 01 03 — Endpoint + Auth GET /users/export Blocked by: 02 04 — ปุ่ม Export ใน UI Blocked by: 03 demo ได้ครบ end-to-end
spec หนึ่งใบแตกเป็น 4 tracer-bullet tickets — ตั๋ว 01 (prefactor) ไม่มี blocker เริ่มได้ทันที ตั๋วที่เหลือไล่ตามลูกศร blocking ทีละชั้นจนครบ vertical slice

สังเกตว่ามีข้อยกเว้นสำคัญเรียกว่า wide refactor — งานแบบ "เปลี่ยนชื่อ column" หรือ "retype symbol ที่ใช้ทั่ว codebase" ที่ blast radius กว้างมากจนไม่มี vertical slice ไหน "เขียว" (ผ่านเทส) ได้เดี่ยว ๆ กรณีนี้ไม่บังคับ tracer bullet แต่ใช้แพทเทิร์น expand → migrate (เป็น batch ตาม blast radius) → contract แทน — expand คือเพิ่มของใหม่ข้างของเก่าโดยไม่พังอะไร, migrate ทีละ batch (เช่น ทีละ package) โดย CI ต้องเขียวตลอด, contract คือลบของเก่าทิ้งเมื่อไม่มีคนเรียกแล้ว

⚠️

Pitfall ที่พบบ่อย: แตกตั๋วแบบแนวนอนโดยไม่รู้ตัว เช่น "ตั๋ว 1 = เขียน schema ทั้งหมด", "ตั๋ว 2 = เขียน API ทั้งหมด", "ตั๋ว 3 = เขียน UI ทั้งหมด" — แบบนี้ไม่มีตั๋วไหน demo ได้เองเลยจนกว่าจะทำครบทั้ง 3 ผิดหลัก tracer bullet ตรง ๆ ให้แตกตาม "feature ที่ใช้งานได้จบในตัว" แทน เช่นตัวอย่างข้างบน: ตั๋ว 04 คือ export ทำงานได้ครบ end-to-end

ก่อน publish ต้อง quiz ผู้ใช้ก่อนเสมอ — เสนอ breakdown เป็นลิสต์ พร้อม Title / Blocked by / What it delivers แล้วถามว่า granularity โอเคไหม, blocking edges ถูกไหม, ควรรวม/แยกตั๋วไหนเพิ่มไหม — วนจนผู้ใช้อนุมัติ ค่อย publish จริง โดยรูปแบบขึ้นกับ tracker:

  • Local files — เขียนไฟล์ละใบใต้ .scratch/<feature-slug>/issues/NN-slug.md เรียงเลขตามลำดับ dependency (blocker มาก่อน)
  • Real tracker (GitHub, Linear) — publish ทีละ issue ตามลำดับเดียวกัน ใช้ native blocking/sub-issue link ของ platform ถ้ามี ติด label ready-for-agent เพราะตั๋วถูกออกแบบให้ agent หยิบไปทำได้เลยตั้งแต่แรก

ผูกกับงานจริง: ถ้าคุณใช้ Go เขียน microservice แล้วมี spec "เพิ่ม rate limiting ให้ทุก endpoint" — ตั๋วที่ดีอาจเป็น (01) prefactor middleware chain ให้ pluggable, (02) implement token-bucket limiter + unit test, (03) wire เข้า 1 endpoint จริง แล้ว demo ว่า 429 ทำงาน, (04) roll out ไป endpoint ที่เหลือเป็น batch — ตั๋ว 03 คือจุดที่ "verify ได้เอง" แล้ว ไม่ต้องรอ 04 เพื่อพิสูจน์ว่า concept ทำงาน

3. wayfinder — เมื่องานใหญ่เกิน 1 session

บางครั้งงานไม่ใช่แค่ "spec เดียวที่แตกเป็นตั๋วได้ตรง ๆ" แต่เป็นไอเดียหลวม ๆ ที่ใหญ่เกินหนึ่ง session agent จะถือไหว และยังมี "หมอก" ปกคลุมทาง — ยังมองไม่เห็นทางไปถึงdestinationชัดเจน wayfinder คือ skill ที่วางแผนงานแบบนี้เป็น แผนที่ร่วม (shared map) บน issue tracker แทนการเดาทำไปเรื่อย ๆ

หลักการสำคัญ: "Plan, don't do" — ทุกตั๋วบนแผนที่นี้คือการ ตัดสินใจ (resolve a decision) ไม่ใช่การส่งมอบงาน (deliverable) แผนที่จบเมื่อ "ทางชัดแล้ว" ไม่เหลืออะไรต้องตัดสินใจก่อนใครสักคนจะไปลงมือทำจริง

โครงสร้างของแผนที่ (map) มี 4 ส่วนหลัก อยู่ใน issue เดียว label wayfinder:map:

ส่วนความหมาย
Destinationปลายทาง 1-2 บรรทัด ที่ทุก session ต้อง orient ก่อนเลือกตั๋ว
Decisions so farดัชนี (index) หนึ่งบรรทัดต่อตั๋วที่ปิดแล้ว — ไม่ก็อปรายละเอียดมาซ้ำ แค่ gist + link ไปตั๋วจริง
Not yet specified"หมอกของสงคราม" (fog of war) — คำถามที่รู้ว่ากำลังจะมา แต่ยังตั้งเป็นตั๋วชัด ๆ ไม่ได้
Out of scopeงานที่ตัดสินใจแล้วว่าอยู่นอกขอบเขต destination นี้ ไม่มีวัน "graduate" กลับมาเป็นตั๋ว

แต่ละตั๋ว (child issue ของ map) มีป้ายกำกับประเภทเป็น wayfinder:<type> หนึ่งใน 4 แบบ:

  • Research (AFK — agent ทำเองได้) — อ่านเอกสาร/API ภายนอก สรุปเป็น markdown asset
  • Prototype (HITL — ต้องมีมนุษย์คุยด้วย) — ทำของหยาบ ๆ ให้จับต้องได้ก่อนตัดสินใจ "ควรหน้าตา/พฤติกรรมเป็นยังไง"
  • Grilling (HITL) — คุยทีละคำถามผ่าน /grilling และ /domain-modeling — เคสมาตรฐานที่สุด
  • Task (HITL หรือ AFK) — งานที่ต้องทำก่อนถึงจะตัดสินใจได้ (เช่น สมัคร service เพื่อดู API จริง) ไม่ใช่ตัวปลายทางเอง แต่ปลดล็อกการตัดสินใจ

กลไกสำคัญคือ frontier — ตั๋วที่เปิดอยู่ ไม่ถูก block แล้ว และยังไม่มีใคร claim ระบบใช้ native blocking relationship ของ tracker เพื่อให้ frontier "มองเห็นได้" ในหน้า UI ของ tracker เอง มนุษย์ไม่ต้องเปิด map มาอ่านทุกครั้ง

การทำงานแบ่งเป็น 2 โหมด: Chart the map (สร้างแผนที่ครั้งแรก — หาชื่อ destination ก่อน แล้ว grill แบบ breadth-first เพื่อกวาดตั๋วที่ทำได้ตอนนี้ พร้อม sketch ส่วนที่ยังเป็นหมอกไว้) กับ Work through the map (แก้ทีละตั๋ว — claim ก่อนเริ่มเสมอ, resolve, บันทึกคำตอบเป็น resolution comment, ปิด issue, แล้วอัปเดต Decisions-so-far) — กฎเหล็ก: ห้าม resolve เกิน 1 ตั๋วต่อ session

💡

ผูกกับงานจริง: สมมติทีมคุณอยากย้ายจาก MongoDB ไป PostgreSQL สำหรับ module หนึ่ง — นี่คือโจทย์ที่ "ใหญ่เกิน session เดียว" แน่นอน wayfinder จะช่วยแตกเป็น ตั๋ว research ("Postgres JSONB รองรับ query pattern ปัจจุบันไหม"), ตั๋ว grilling ("ทีมยอมรับ downtime ระหว่าง migrate ได้กี่นาที"), ตั๋ว task ("provision Postgres instance ใน staging") — ก่อนที่จะไปถึงขั้น เขียน spec ของการ migrate จริง ๆ ด้วย to-spec

4. triage — ขยับ issue ผ่าน state machine

spec/ticket ที่สร้างมาแล้วไม่ได้แปลว่าจบ — issue tracker ของทีมจริงมี issue จากหลายที่ (bug report, feature request, PR ภายนอก) ไหลเข้ามาตลอด triage คือ skill ที่ขยับ issue เหล่านี้ผ่าน state machine ที่มีบทบาทตายตัว

issue ทุกใบต้องมี category role 1 อัน และ state role 1 อัน:

ประเภทค่าความหมาย
Categorybugมีอะไรพัง
enhancementฟีเจอร์ใหม่/ปรับปรุง
Stateneeds-triageรอ maintainer ประเมิน
needs-infoรอผู้รายงานให้ข้อมูลเพิ่ม
ready-for-agentสเปกครบแล้ว พร้อมให้ agent ทำงานแบบ AFK
ready-for-humanต้องให้มนุษย์ทำ (judgment call, external access, manual test)
wontfixจะไม่ทำ (ทำแล้ว หรือถูกปฏิเสธ)
unlabeled needs-triage needs-info reporter ตอบ → กลับ needs-triage ready-for-human ready-for-agent wontfix
state machine ของ triage: issue ไหลจาก unlabeled → needs-triage แล้วแตกไปสี่ทาง — needs-info วนกลับ needs-triage เมื่อผู้รายงานตอบ

ขั้นตอน triage issue หนึ่งใบตามไฟล์จริง:

  1. Gather context — อ่าน body/comment/label ทั้งหมด เช็ก 2 อย่างกับ codebase: (a) redundancy — มี implement อยู่แล้วหรือยัง (ค้นตามแนวคิดโดเมน ไม่ใช่แค่คำที่ผู้ใช้พิมพ์) (b) prior rejection — เคยถูกปฏิเสธมาก่อนไหม (ดูใน .out-of-scope/*.md)
  2. Recommend — บอก category/state ที่แนะนำพร้อมเหตุผลและสรุป codebase ที่เกี่ยวข้อง แล้วรอ maintainer สั่งต่อ
  3. Verify the claim — สำหรับ bug ต้อง reproduce ตามขั้นตอนที่รายงาน สำหรับ PR ต้อง checkout แล้วรันเทสจริงว่าทำตามที่อ้างจริงไหม
  4. Grill (ถ้าจำเป็น) — ถ้ายังไม่ชัด ใช้ /grilling + /domain-modeling คุยทีละคำถาม
  5. Apply outcome — โพสต์ agent brief (ถ้า ready-for-agent), triage notes (ถ้า needs-info), หรือปิดพร้อมเหตุผล (ถ้า wontfix)
⚠️

กฎที่มักถูกมองข้าม: ทุกคอมเมนต์หรือ issue ที่โพสต์ระหว่าง triage ต้องขึ้นต้นด้วยข้อความ > *This was generated by AI during triage.* เพื่อความโปร่งใสกับทีม — ห้ามข้าม แม้จะแก้ไขแค่ label ก็ตาม และถ้า maintainer สั่ง override ตรง ๆ เช่น "move #42 to ready-for-agent" ให้เชื่อและทำตามได้เลย ข้าม grilling ไปได้ แต่ต้อง confirm สิ่งที่กำลังจะทำก่อน

สังเกตว่าสถานะ wontfix แยกเหตุผลเป็น 3 แบบ — "already implemented" (ชี้ไปที่โค้ดที่มีอยู่แล้ว ไม่เขียนลง out-of-scope เพราะ KB นั้นสำหรับของที่ถูกปฏิเสธ ไม่ใช่ของที่สร้างแล้ว), "rejected (bug)" (อธิบายสุภาพแล้วปิด), และ "rejected (enhancement)" (บันทึกลง .out-of-scope/ ก่อนปิด กันไม่ให้มีคน เสนอซ้ำในอนาคตแล้วต้องเถียงกันใหม่)

5. ผูกทั้ง 4 skill เข้ากับ workflow จริงของทีมคุณ

ไม่ว่าทีมคุณจะใช้ GitHub Issues, Linear, หรือแค่ไฟล์ markdown ใน .scratch/ ของ local repo — 4 skill นี้เชื่อมกันเป็นเส้นเดียว:

  1. คุยจน align (MP-1) →
  2. to-spec สังเคราะห์เป็น spec เดียว publish ขึ้น tracker →
  3. to-tickets แตก spec เป็น tracer-bullet tickets พร้อม blocking edges →
  4. งานใหญ่เกินคาด/มีหมอกเยอะ → พักไปใช้ wayfinder วางแผนก่อน แล้วค่อยกลับมา to-spec →
  5. triage คอยดูแล issue ที่ไหลเข้ามาใหม่ระหว่างทาง ให้ทุกใบมี state ที่ถูกต้องเสมอ

จุดร่วมที่ทั้ง 4 skill ย้ำเหมือนกันคือ "ห้ามใส่ path ไฟล์หรือโค้ดจริงในเอกสาร" (ยกเว้น snippet จาก prototype ที่เข้ารหัส decision แม่นกว่าคำบรรยาย เช่น state machine, reducer, schema) เพราะโค้ดในโปรเจกต์เปลี่ยนไวกว่าที่เอกสาร จะตามทัน — สิ่งที่ spec/ticket ควรบันทึกคือ การตัดสินใจ ไม่ใช่ implementation ที่จะล้าสมัยในอีกไม่กี่วัน

ทำไม tracer-bullet ticket ถึงดีกับทีม: ทีมเห็น progress จริงทุกครั้งที่ตั๋วปิด (เพราะแต่ละใบ demo ได้ด้วยตัวเอง) ไม่ใช่ต้องรอให้ทุก layer เสร็จพร้อมกันถึงจะเห็นผล แถม blocking edges ที่ชัดเจนทำให้ agent หลายตัว (หรือ dev หลายคน) ทำงานคู่ขนานกันได้ โดยไม่ชนกัน — แค่ดูว่าใครอยู่ที่ frontier ตอนนี้

🎯 แบบฝึกหัด
  1. เปิดไฟล์จริง sources/skills/skills/engineering/to-spec/SKILL.md แล้วเขียนด้วยคำของคุณเองว่า seam ที่ไฟล์พูดถึงคืออะไร ทำไมถึงต้อง "เช็กกับผู้ใช้ก่อน" ว่า seam ตรงกับที่คาดไว้
  2. ลองแตก feature หนึ่งที่คุณคุ้นเคย (เช่น "เพิ่ม pagination ให้ endpoint GET /orders ใน NestJS" หรือ "เพิ่ม soft-delete ให้ table ใน PostgreSQL") เป็น 3-4 tickets แบบ tracer-bullet — เขียนลงกระดาษหรือแชทว่าแต่ละใบ "Blocked by" ใบไหน และ "What it delivers" คืออะไร (ต้อง demo ได้เองทุกใบ)
  3. เทียบ breakdown ของคุณกับกฎ 4 ข้อใน vertical-slice-rules — มีใบไหนที่จริง ๆ แล้วเป็น horizontal slice (ตัด layer เดียว) แอบซ่อนอยู่ไหม ถ้ามี ลองแก้ให้เป็น vertical slice ใหม่
  4. เปิด sources/skills/skills/engineering/triage/SKILL.md แล้วหาว่า state ไหนที่ "ต้องมี codebase evidence" ก่อนถึงจะกำหนดได้ (hint: ดูหัวข้อ Verify the claim)