จากบทสนทนา → Spec → Tickets
บท 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 นี้คือ:
- Explore repo — ถ้ายังไม่ได้ explore มาก่อน ให้เข้าใจ codebase ปัจจุบันก่อน ใช้ศัพท์ตาม domain glossary ของโปรเจกต์ตลอดทั้ง spec และเคารพ ADR ที่มีอยู่แล้วในพื้นที่ที่กำลังแตะ
- ร่าง seam ที่จะเทสต์ feature นี้ — เลือก seam ที่มีอยู่แล้วก่อนเสมอ ถ้าต้อง seam ใหม่ ให้เสนอที่จุดสูงสุดที่ทำได้ (ยิ่ง seam ข้ามทั้ง codebase น้อย ยิ่งดี — เป้าหมายในอุดมคติคือ seam เดียว) แล้วเช็กกับผู้ใช้ก่อนว่าตรงกับที่คาดไว้ไหม
-
เขียน 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 Decisions | module ที่จะแก้/สร้าง, 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
สังเกตว่ามีข้อยกเว้นสำคัญเรียกว่า 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 อัน:
| ประเภท | ค่า | ความหมาย |
|---|---|---|
| Category | bug | มีอะไรพัง |
enhancement | ฟีเจอร์ใหม่/ปรับปรุง | |
| State | needs-triage | รอ maintainer ประเมิน |
needs-info | รอผู้รายงานให้ข้อมูลเพิ่ม | |
ready-for-agent | สเปกครบแล้ว พร้อมให้ agent ทำงานแบบ AFK | |
ready-for-human | ต้องให้มนุษย์ทำ (judgment call, external access, manual test) | |
wontfix | จะไม่ทำ (ทำแล้ว หรือถูกปฏิเสธ) |
ขั้นตอน triage issue หนึ่งใบตามไฟล์จริง:
- Gather context — อ่าน body/comment/label ทั้งหมด เช็ก 2 อย่างกับ codebase: (a) redundancy — มี implement อยู่แล้วหรือยัง (ค้นตามแนวคิดโดเมน ไม่ใช่แค่คำที่ผู้ใช้พิมพ์) (b) prior rejection — เคยถูกปฏิเสธมาก่อนไหม (ดูใน
.out-of-scope/*.md) - Recommend — บอก category/state ที่แนะนำพร้อมเหตุผลและสรุป codebase ที่เกี่ยวข้อง แล้วรอ maintainer สั่งต่อ
- Verify the claim — สำหรับ bug ต้อง reproduce ตามขั้นตอนที่รายงาน สำหรับ PR ต้อง checkout แล้วรันเทสจริงว่าทำตามที่อ้างจริงไหม
- Grill (ถ้าจำเป็น) — ถ้ายังไม่ชัด ใช้
/grilling+/domain-modelingคุยทีละคำถาม - 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 นี้เชื่อมกันเป็นเส้นเดียว:
- คุยจน align (MP-1) →
to-specสังเคราะห์เป็น spec เดียว publish ขึ้น tracker →to-ticketsแตก spec เป็น tracer-bullet tickets พร้อม blocking edges →- งานใหญ่เกินคาด/มีหมอกเยอะ → พักไปใช้
wayfinderวางแผนก่อน แล้วค่อยกลับมา to-spec → 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 ตอนนี้
-
เปิดไฟล์จริง
sources/skills/skills/engineering/to-spec/SKILL.mdแล้วเขียนด้วยคำของคุณเองว่า seam ที่ไฟล์พูดถึงคืออะไร ทำไมถึงต้อง "เช็กกับผู้ใช้ก่อน" ว่า seam ตรงกับที่คาดไว้ -
ลองแตก feature หนึ่งที่คุณคุ้นเคย (เช่น "เพิ่ม pagination ให้ endpoint
GET /ordersใน NestJS" หรือ "เพิ่ม soft-delete ให้ table ใน PostgreSQL") เป็น 3-4 tickets แบบ tracer-bullet — เขียนลงกระดาษหรือแชทว่าแต่ละใบ "Blocked by" ใบไหน และ "What it delivers" คืออะไร (ต้อง demo ได้เองทุกใบ) -
เทียบ breakdown ของคุณกับกฎ 4 ข้อใน
vertical-slice-rules— มีใบไหนที่จริง ๆ แล้วเป็น horizontal slice (ตัด layer เดียว) แอบซ่อนอยู่ไหม ถ้ามี ลองแก้ให้เป็น vertical slice ใหม่ -
เปิด
sources/skills/skills/engineering/triage/SKILL.mdแล้วหาว่า state ไหนที่ "ต้องมี codebase evidence" ก่อนถึงจะกำหนดได้ (hint: ดูหัวข้อ Verify the claim)