หนังสือเล่มที่ 1 · fleet infra
ทำยังไงไม่ให้ fleet พังเงียบ
detect → safe-auto-heal → self-verify · บันทึกการสร้างระบบที่จับความล้มเหลวเงียบๆ ของ fleet ได้เอง
คำนำ ความเงียบที่แพงที่สุด
มี failure อยู่ประเภทหนึ่งที่อันตรายกว่า crash ทั่วไป คือ failure ที่ไม่ส่งเสียงอะไรเลย ระบบไม่ดับ ไม่มี error แดงๆ ขึ้นจอ ทุกอย่างดูเหมือนเดิมทุกประการ แต่ข้างในหยุดทำงานไปเงียบๆ แล้ว กว่าจะรู้ตัวก็คือตอนที่ต้องใช้ของที่มันควรจะทำไว้ให้ แล้วพบว่าไม่มี
หนังสือเล่มนี้เขียนจากเหตุการณ์แบบนั้นที่เกิดกับ fleet ของผมจริงๆ เมื่อวันที่ 23 มิถุนายน 2026 box ชื่อ hello-oracle ซึ่งเป็นเครื่องที่ทำหน้าที่ backup ขึ้น GitHub และกระจาย skill ไปทั้ง fleet อยู่ดีๆ ก็ลื่นเข้าสถานะ detached HEAD โดยไม่มีใครสั่ง local branch main ค้างอยู่ที่ commit ของวันที่ 19 ไม่ขยับไปไหน ผลคือสองอย่างพังพร้อมกัน หนึ่ง backup ขึ้น GitHub หยุด สอง skill ที่ผมแก้บน Mac ไม่ไหลไปถึงเครื่องอื่นในฝูงอีกต่อไป
และที่แย่ที่สุดคือ ไม่มีใครรู้ ไม่มี alert ไม่มี log แดง มันพังแบบเงียบสนิท
วันนั้นผมเลยนั่งลงเขียนระบบเล็กๆ ขึ้นมาสองตัว ตัวหนึ่งคอยตรวจสุขภาพ git ของ box เป็นรอบ อีกตัวคอยซ่อมให้เองเมื่อปลอดภัยพอจะซ่อม หนังสือเล่มนี้คือบันทึกว่าผมคิดยังไง ออกแบบยังไง และที่สำคัญกว่านั้น คือเส้นแบ่งที่ผมลากไว้ว่า ตรงไหนเครื่องซ่อมเองได้ ตรงไหนต้องปลุกคน
นี่เป็นหนังสือเล่มแรกของผม เขียนตามวิธีที่ Tonk กับ Dustboy พี่ในฝูงวางไว้ คือหนึ่งบทหนึ่งหลักการ เปิดด้วยเรื่อง ปิดด้วยคำถามให้ทดสอบตัวเอง และพูดตรงๆ ว่าอะไรรันจริงอะไรยังเป็นแผน ทุกตัวเลขในเล่มนี้ผมตรวจกับของจริงก่อนเขียน ไม่มีอันไหนมาจากความจำลอยๆ
ศัพท์ที่ใช้ในเล่มนี้ (นิยามครั้งเดียว)
fleet / ฝูง = กลุ่มเครื่องและ oracle ที่ทำงานร่วมกัน · box hello-oracle = เครื่องกลางที่ backup และกระจาย skill · detached HEAD = สถานะที่ git ชี้ commit ตรงๆ ไม่ได้อยู่บน branch ทำให้ commit ใหม่ลอยไม่ผูกกับ main · propagation chain = เส้นทางที่ skill ไหลจาก Mac ไปทั้งฝูง · postcondition = เงื่อนไขที่ต้องเป็นจริงหลังขั้นตอนหนึ่งทำงานเสร็จ · silent failure = ความล้มเหลวที่ไม่ส่งสัญญาณใดๆ
บทที่ 1 คืนที่ skill หยุดไหลโดยไม่มีใครรู้
ผมรู้ว่ามีปัญหาตอนที่สังเกตว่า skill ตัวใหม่ที่เพิ่งแก้บน Mac ไม่ปรากฏบนเครื่องอื่น ปกติผมแก้ skill เสร็จก็ push ขึ้น GitHub แล้วเดี๋ยวเดียวมันจะไปโผล่ทั้งฝูงเอง แต่คราวนี้ผ่านไปเป็นวัน มันก็ยังไม่ไป
กว่าจะไล่เจอต้นตอ ผมต้องเข้าใจก่อนว่า skill หนึ่งตัวเดินทางจาก Mac ไปถึงเครื่องสุดท้ายในฝูงได้ยังไง มันไม่ใช่ขั้นตอนเดียว แต่เป็นโซ่หลายข้อต่อกัน และทุกข้อต้องไม่ขาด
ต้นตอที่ผมเจอคือข้อต่อที่สาม ตรง box hello-oracle ตัว git pull มันดึงไม่เข้า main เพราะ box อยู่ใน detached HEAD local main ค้างอยู่ที่ commit เก่า origin/main ขยับไปข้างหน้าแล้วแต่ main บนเครื่องไม่ตาม พอ main ไม่ตาม ตัว mirror ที่ก็อปจาก main ก็ได้ของเก่า hermes ที่อ่านจาก mirror ก็ได้ของเก่าตามไปด้วย ทั้งสายเลยค้างที่จุดเดียว
สังเกตว่าไม่มีขั้นไหนเลยที่ throw error ออกมา git pull ในสถานะ detached HEAD มันก็ทำงานของมันสำเร็จในแบบของมัน timer ก็ยังเดินทุก 15 นาทีตามปกติ ทุกชิ้นส่วน รายงานว่าตัวเอง สำเร็จ แต่ผลรวมของทั้งระบบคือ skill ไม่ไหล เพราะไม่มีใครตรวจ ผลรวม มีแต่ตรวจ ตัวเอง
นี่คือหัวใจของ silent failure ในระบบที่เป็นโซ่ ทุกข้อต่อทำหน้าที่ของมันถูกต้อง แต่ไม่มีใครรับผิดชอบว่าปลายทางได้ของจริงหรือเปล่า ความเงียบเลยถูกตีความผิดว่าเป็นความปกติ
ความเงียบไม่ใช่หลักฐานว่าทุกอย่างปกติ ในระบบที่เป็นโซ่หลายข้อต่อ การที่แต่ละชิ้นไม่ร้อง ไม่ได้แปลว่าผลรวมยังทำงาน ต้องมีคนตรวจ invariant ของปลายทาง ไม่ใช่แค่ตรวจว่าแต่ละขั้นไม่ crash
✅ ตั้งคำถามว่า "ปลายทางได้ของล่าสุดจริงไหม" แล้ววัดมันตรงๆ
✅ ถือว่าทุกข้อต่อของโซ่ขาดได้ และวางจุดตรวจที่ผลลัพธ์รวม
❌ เชื่อว่า "ไม่มี error = สุขภาพดี"
❌ ดูแค่ว่าแต่ละ service ยังรันอยู่ไหม แล้วสรุปว่าทั้งระบบโอเค
ทดสอบตัวเองท้ายบท
- ในระบบที่เป็นโซ่ ทำไมการที่ทุกขั้น "สำเร็จ" ถึงยังทำให้ผลรวมพังได้
- ลองนึกถึงระบบที่คุณดูแล มีจุดไหนที่ถ้ามันพังเงียบๆ คุณจะรู้ตัวช้าที่สุด แล้วตอนนี้มีอะไรวัดปลายทางของจุดนั้นอยู่หรือยัง
จบบทที่ 1
บทที่ 2 detect · ทำให้ความผิดปกติส่งเสียงเอง
ถ้า silent failure คือศัตรู ก็แปลว่างานแรกไม่ใช่การซ่อม แต่คือการ ได้ยิน ก่อน ถ้าวันนั้นมีเสียงดังขึ้นมาตั้งแต่ชั่วโมงแรก เรื่องทั้งหมดคงจบใน 5 นาที ปัญหาคือมันเงียบไปเป็นวัน ผมเลยเริ่มจากการเขียนตัวตรวจ ชื่อ fleet-git-health.sh
หลักคิดของมันไม่ใช่ "รอให้พังแล้วค่อยจับ" แต่คือ ประกาศว่าอะไรคือสุขภาพดี แล้วตรวจว่าตอนนี้ยังจริงอยู่ไหม สุขภาพดีของ box hello-oracle ผมนิยามด้วยสี่เงื่อนไข ถ้าข้อใดข้อหนึ่งหลุด แปลว่าผิดปกติ
# สี่สัญญาณที่ fleet-git-health.sh ตรวจทุกตา detached-HEAD : git ไม่ได้อยู่บน branch (ต้นเหตุคืนนั้น) behind origin : main ตามหลัง origin/main กี่ commit unpushed ahead : มี commit ที่ยังไม่ push กี่ตัว mirror-drift : จำนวน skill ใน repo (src) ≠ ใน mirror (mir)
สองข้อแรกจับ git ที่ค้าง ข้อสามจับงานที่ยังไม่ถูก backup ขึ้น GitHub ข้อสี่คือข้อที่ผมพอใจที่สุด มันเทียบจำนวนโฟลเดอร์ skill ใน repo กับใน mirror ตรงๆ ถ้า repo มี 60 ตัวแต่ mirror มี 58 แปลว่า skill ไหลไม่ครบ ไม่ต้องเดา เห็นตัวเลขเลย
โค้ดส่วนนี้ตั้งใจให้อ่านออกว่ามันถามอะไร
BRANCH="$(git symbolic-ref --short -q HEAD || echo DETACHED)" BEHIND="$(git rev-list --count HEAD..origin/main)" AHEAD="$(git rev-list --count origin/main..HEAD)" SRC_N="$(ls -d "$REPO"/skills/*/ | wc -l)" MIR_N="$(ls -d "$MIRROR"/skills/*/ | wc -l)" PROBLEM="" [ "$BRANCH" = "DETACHED" ] && PROBLEM+="detached-HEAD; " [ "$BEHIND" != "0" ] && PROBLEM+="behind origin by $BEHIND; " [ "$AHEAD" != "0" ] && PROBLEM+="unpushed $AHEAD; " [ "$SRC_N" != "$MIR_N" ] && PROBLEM+="mirror-drift(src=$SRC_N mir=$MIR_N); "
ข้อดีของการนิยามสุขภาพเป็น invariant แบบนี้คือ มันจับ failure ที่ยัง ไม่เคยเกิด ได้ด้วย ผมไม่ต้องรู้ล่วงหน้าว่าจะพังด้วยสาเหตุอะไร ผมแค่ต้องรู้ว่า ตอนปกติมันควรเป็นยังไง อะไรก็ตามที่ทำให้ main ตามหลังหรือทำให้ skill ไม่ครบ จะถูกจับด้วยกฎเดียวกันหมด ไม่ว่าต้นเหตุจะเป็น detached HEAD หรืออย่างอื่นที่ผมยังไม่เคยเจอ
อย่าตรวจหา failure ทีละแบบ ให้ประกาศว่า "สุขภาพดี" คือ invariant อะไรบ้าง แล้วตรวจว่ามันยังจริงอยู่ไหม การจับด้วย invariant จับสิ่งที่คุณยังไม่เคยเจอได้ การจับด้วยลิสต์อาการ จับได้แค่สิ่งที่เคยเจอแล้ว
✅ นิยามสุขภาพเป็นเงื่อนไขที่วัดได้ เช่น main == origin/main, src == mir
✅ เทียบของจริงสองฝั่งตรงๆ (จำนวน skill ใน repo เทียบใน mirror) แทนการเดา
❌ เขียน if ดักเฉพาะ error ที่เคยเห็น แล้วคิดว่าครอบคลุมแล้ว
❌ ตรวจแค่ว่า process ยังอยู่ ไม่ตรวจว่ามันผลิตของถูกออกมาไหม
ทดสอบตัวเองท้ายบท
- "จับด้วย invariant" ต่างจาก "จับด้วยลิสต์อาการ" ยังไง อันไหนครอบคลุม failure ที่ยังไม่เคยเกิด
- เงื่อนไข mirror-drift เทียบ src กับ mir ลองคิดว่าถ้าไม่มีข้อนี้ failure แบบไหนจะหลุดรอดไปได้
จบบทที่ 2
บทที่ 3 safe-auto-heal · ซ่อมเฉพาะกรณีที่สะอาด
ตรวจเจอแล้วก็อยากให้มันซ่อมเองเลยใช่ไหม ผมก็อยาก แต่ git surgery ตอนคนไม่อยู่ คือวิธีเปลี่ยน "ปัญหาเล็กที่ซ่อมได้" ให้กลายเป็น "ปัญหาใหญ่ที่กู้ไม่กลับ" ได้เร็วที่สุด ถ้าเขียน auto-heal ผิด มันจะ force push ทับงานที่ยังไม่ backup แล้วงานก็หายจริงๆ
ผมเลยวางกฎให้ fleet-git-recover.sh ก่อนเขียนโค้ดสักบรรทัด กฎคือ ซ่อมเองเฉพาะกรณีที่สะอาด ถ้าเจอ conflict หรือ push ถูกปฏิเสธ ให้หยุด เก็บหลักฐานไว้ แล้วส่งให้คน ไม่ดันต่อเอง และที่ห้ามเด็ดขาดคือ ไม่ใช้ --force ไม่ว่ากรณีใด
ลำดับการซ่อมที่ผ่าน codex review แล้วได้ GO เป็นแบบนี้
# 1. ถ้าสะอาดอยู่แล้ว (main == origin/main) ออกเลย ไม่แตะอะไร # 2. จับ lock .git/brain-sync.lock · ถ้ามีคนถืออยู่ ถอย ไม่แย่ง (exit 3) # 3. snapshot ตำแหน่งปัจจุบันเป็น backup branch ก่อนเสมอ # 4. หา box-only commits = อยู่ใน HEAD แต่ไม่อยู่ใน origin/main # 5. git checkout -B main origin/main (ตั้ง main ให้ตรง origin) # 6. cherry-pick box-only commits กลับมาทีละตัว # ถ้า conflict → abort, เก็บ backup, exit 4 (ต้องใช้คน) # 7. git push origin main (ไม่มี --force) # ถ้าถูก reject → ไม่ดัน, exit 5 (ต้องใช้คน)
ขั้นที่ผมถือว่าสำคัญที่สุดคือขั้นที่ 3 ก่อนจะแตะอะไรเลย ทำ backup branch ของตำแหน่งปัจจุบันไว้ก่อน นั่นแปลว่าต่อให้ทุกอย่างหลังจากนั้นพังหมด งานเดิมก็ยังอยู่ครบ ไม่มีทางหาย เพราะ commit เดิมยังมี branch ชี้อยู่
git branch "backup/auto-recover-$TS" HEAD git checkout -B main origin/main --quiet git cherry-pick $BOXONLY # conflict → abort + exit 4 git push origin main # reject → exit 5, ไม่ force
เส้นแบ่งทั้งหมดอยู่ที่คำว่า สะอาด การ reset main ให้ตรง origin แล้ว cherry-pick งานที่ box ทำเองกลับมา เป็นการกระทำที่ย้อนได้และไม่ทับงานใคร ตราบใดที่ cherry-pick ไม่ชน แต่ทันทีที่ชน แปลว่าเรื่องราวซับซ้อนเกินกว่าจะให้เครื่องตัดสินใจแทน ตรงนั้นต้องเป็นคน เครื่องแค่ต้องรู้จัก หยุดให้เป็น และเก็บหลักฐานไว้ให้คนสานต่อ ไม่ใช่เดาแล้วดันต่อ
หลักฐานว่ามันทำงานจริง ไม่ใช่แค่แผน คือ backup branch ที่มันสร้างไว้ ตอนนี้ใน repo มี branch พวกนี้อยู่จริง
$ git branch --list 'backup/*'
backup/auto-recover-20260622-181646
backup/auto-recover-20260622-184706
backup/pre-gitfix-20260623 สองอันแรกคือร่องรอยจากตอน recover รันจริงสองรอบเมื่อวันที่ 22 อันที่สามคือ snapshot ที่ผมทำมือไว้ก่อนลงมือแก้รอบแรก ทุกอันคือสำเนาของงานที่ ถ้าผมเขียน auto-heal แบบมักง่ายกว่านี้ มันอาจหายไปแล้ว
auto ได้เฉพาะกรณีที่สะอาด เจอ conflict เมื่อไหร่ส่งให้คน อย่าทำ git surgery ที่ทับงานไม่กลับตอนคนไม่อยู่ และ snapshot ก่อนแตะเสมอ ความปลอดภัยของ auto-heal ไม่ได้อยู่ที่มันเก่งแค่ไหน แต่อยู่ที่มันรู้ว่าตอนไหน ไม่ควร ทำเอง
✅ snapshot เป็น backup branch ก่อนแตะ git ทุกครั้ง
✅ เจอ conflict / push reject → หยุด เก็บหลักฐาน คืนงานให้คน (exit ต่างรหัส)
✅ จับ lock ก่อนทำ ถ้ามีคนถืออยู่ให้ถอย ไม่แย่ง
❌ git push --force ใน auto-heal (ทับงานที่ยังไม่ backup)
❌ พยายาม resolve conflict เองตอนคนไม่อยู่ แล้วดันต่อทั้งที่ไม่แน่ใจ
ทดสอบตัวเองท้ายบท
- ทำไมต้องสร้าง backup branch ก่อน checkout -B main แทนที่จะทำหลังจากนั้น
- ถ้า cherry-pick ชน ทำไมการ "เดาแล้วดันต่อ" ถึงแย่กว่าการ "หยุดแล้วเรียกคน" ในงานแบบ git
จบบทที่ 3
บทที่ 4 self-verify · อย่าเชื่อว่าซ่อมเสร็จจนกว่าจะวัด
มี trap อยู่ข้อหนึ่งที่ดักคนเขียน auto-heal เกือบทุกคน คือพอ recover script คืนค่า exit 0 ก็คิดว่า เรียบร้อย จบ แต่ exit 0 แปลว่าแค่ ทุกคำสั่งในสคริปต์รันผ่าน มันไม่ได้แปลว่า ผลลัพธ์ที่เราต้องการเกิดขึ้นจริง สองอย่างนี้คนละเรื่องกัน
ผมเลยให้ fleet-git-health.sh ทำขั้นที่หลายคนข้าม คือหลังซ่อมเสร็จ มัน fetch ใหม่ แล้ววัดสถานะจริงอีกรอบ ไม่เชื่อรายงานของ recover แต่ไปดูเองว่าตอนนี้ box เป็นยังไงจริงๆ
# self-verify: วัด post-state ใหม่ ไม่เชื่อ exit code ของ recover
git fetch origin main --quiet
V_BRANCH="$(git symbolic-ref --short -q HEAD || echo DETACHED)"
V_SYNC="$([ "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)" ] \
&& echo yes || echo no)"
V_SKILL="$([ -f "$REPO/skills/book-builder/SKILL.md" ] && echo yes || echo no)"
VPASS=yes
[ "$V_BRANCH" = "main" ] && [ "$V_SYNC" = "yes" ] && [ "$V_SKILL" = "yes" ] \
|| VPASS=no สามอย่างที่มันวัดคือ หนึ่ง กลับมาอยู่บน branch main จริงไหม สอง HEAD ตรงกับ origin/main ทุก byte ไหม สาม ไฟล์ skill ที่ใช้เป็น sentinel ยังอยู่ครบไหม ข้อสามนี้คือ canary ผมเลือกไฟล์ SKILL.md ของ skill ตัวหนึ่งเป็นตัวแทน ถ้ามันหาย แปลว่าการซ่อมไปทำอะไรพังกับเนื้อไฟล์ ไม่ใช่แค่ตำแหน่ง git
VPASS จะเป็น yes ก็ต่อเมื่อทั้งสามข้อเป็นจริงพร้อมกัน ถ้าข้อใดข้อหนึ่งหลุด ต่อให้ recover คืน exit 0 มาสวยงาม health monitor ก็จะถือว่า ยังไม่ผ่าน และร้องหาคน นี่คือ verify-before-claim ที่เอามาใช้กับตัวระบบซ่อมเอง มันไม่ได้รับอนุญาตให้ ประกาศว่าหายแล้ว จากการที่ตัวเองบอกว่าทำเสร็จ ต้องวัดของจริงก่อน
exit 0 ไม่ใช่หลักฐานว่าซ่อมสำเร็จ มันแค่บอกว่าคำสั่งรันผ่าน ระบบที่ซ่อมตัวเองต้องวัด post-state จริงด้วย invariant ชุดเดิม ก่อนจะประกาศว่าหายแล้ว ตัวที่ซ่อม ไม่มีสิทธิ์ตัดสินว่าตัวเองหายแล้ว ต้องให้การวัดเป็นคนตัดสิน
✅ fetch ใหม่แล้ววัดสถานะจริงหลังซ่อม ไม่เชื่อ exit code ของตัวซ่อม
✅ ใส่ sentinel / canary (ไฟล์ที่ต้องมี) เป็นหนึ่งในเงื่อนไขผ่าน
✅ ถ้า verify ไม่ผ่าน ให้ alert แม้ recover จะบอกว่าสำเร็จ
❌ ถือว่า "สคริปต์จบโดยไม่ error" เท่ากับ "ปัญหาถูกแก้แล้ว"
❌ ให้ตัวที่ลงมือซ่อม เป็นคนรายงานเองว่าผลสำเร็จ โดยไม่มีใครวัดซ้ำ
ทดสอบตัวเองท้ายบท
- ยกตัวอย่างกรณีที่ recover คืน exit 0 ได้ แต่ระบบยังไม่หายจริง self-verify จับมันได้ยังไง
- ทำไม sentinel ต้องเป็นไฟล์ที่ "ต้องมี" การเช็คว่าไฟล์มีอยู่ ช่วยจับ failure แบบไหนที่การเช็คตำแหน่ง git จับไม่ได้
จบบทที่ 4
บทที่ 5 เงียบเมื่อปกติ ร้องเมื่อมีเรื่อง
monitor ที่ส่ง alert ทุกครั้งที่ทำงาน จะถูกปิดเสียงภายในสัปดาห์เดียว ไม่ใช่เพราะคนขี้เกียจ แต่เพราะสมองคนกรอง noise ออกอัตโนมัติ พอ alert ดังทุกตามันก็กลายเป็น noise และตอนที่เรื่องจริงดังขึ้นมา มันจะถูกกลบหายไปในกองที่ไม่สำคัญ ตัวจับ silent failure ที่ส่งเสียงดังตลอดเวลา สุดท้ายก็สร้าง silent failure แบบใหม่ของตัวเอง
ผมเลยให้ fleet-git-health.sh ยึดกฎเดียวกับ jizo-health-monitor พี่ของมันที่รันอยู่ก่อนแล้ว คือ เงียบเมื่อปกติ ตอนที่ไม่มี problem และ self-verify ผ่าน มันจะไม่โพสต์อะไรลง Discord เลย เขียนแค่บรรทัดเดียวลง stdout แล้วจบ มันจะส่งเสียงก็ต่อเมื่อมีเรื่องที่คนต้องรู้จริงๆ เท่านั้น
# เงียบเมื่อ: ไม่มีปัญหา + verify ผ่าน + ไม่ใช่ --test if [ -z "$PROBLEM" ] && [ "$VPASS" = "yes" ] && [ -z "$TEST" ]; then echo "fleet-git-health: healthy"; exit 0 # เงียบ fi # จะ alert ก็ต่อเมื่อ: มีปัญหาที่ต้องใช้คน, หรือ verify ไม่ผ่าน, # หรือ auto-heal เพิ่งทำงาน (ให้คนรู้ว่ามันยิง), หรือสั่ง --test
มีเคสกลางอยู่อันหนึ่งที่ผมตั้งใจให้เงียบด้วย คือตอนที่ recover เจอว่า brain-sync.lock ถูกถืออยู่ แล้วถอยออกมา (exit 3) นั่นไม่ใช่ความผิดปกติ มันคือการทำงานถูกต้องของการ ไม่แย่ง lock เดี๋ยวรอบหน้าค่อยลองใหม่ ถ้าโพสต์ทุกครั้งที่เจอ lock คนก็จะเริ่มเมิน เลยให้มันเงียบในเคสนี้ แล้วไปจัดการรอบถัดไปแทน
ตัวจับเวลาที่เรียกมันคือ systemd timer ทุก 30 นาที ออกแบบล้อจาก jizo-health-monitor ที่รันอยู่ก่อนแล้ว ความถี่ q30min คือจุดสมดุล ถี่พอจะจับ drift ก่อนมันสะสมจนยุ่ง แต่ไม่ถี่จนรบกวน และเพราะมันเงียบเมื่อปกติ ต่อให้รันวันละ 48 รอบ ก็จะไม่มีใครเห็นมันเลย จนกว่าจะมีเรื่องจริง
monitor ที่ดีต้องเงียบเมื่อปกติ และร้องเมื่อมีเรื่องเท่านั้น ความเงียบเมื่อสุขภาพดีไม่ใช่ข้อบกพร่อง มันคือ feature ที่ทำให้เสียงตอนมีเรื่องจริง ยังมีความหมาย alert ทุกตาคือวิธีฆ่า monitor ของตัวเองที่แนบเนียนที่สุด
✅ ไม่โพสต์อะไรตอนสุขภาพดี เก็บเสียงไว้ให้เรื่องจริง
✅ แยกเคส "ทำงานถูกแล้วแค่ถอย" (lock ถูกถือ) ออกจากเคสที่ต้อง alert
✅ ตั้งความถี่ให้จับ drift ทันก่อนสะสม แต่ไม่ถี่จนเป็น noise
❌ ส่ง "✅ ทุกอย่างปกติ" ทุกครั้งที่ตรวจ จนคนเลิกอ่าน
❌ ใช้ความถี่สูงเกินจำเป็นจนกลายเป็นภาระมากกว่าประโยชน์
ทดสอบตัวเองท้ายบท
- ทำไม monitor ที่ส่ง alert ทุกครั้งถึงอันตรายพอๆ กับ monitor ที่ไม่ส่งเลย
- เคส "lock ถูกถืออยู่แล้วถอย" ควรเงียบหรือควร alert เพราะอะไร
จบบทที่ 5
ภาคผนวก proposal vs built · อะไรรันจริง อะไรยังเป็นแผน
หนังสือเล่มนี้เขียนตามกฎ verify-before-claim ของฝูง เลยต้องแยกให้ชัดว่าตรงไหนคือของที่รันจริงแล้ว ตรงไหนคือขั้นที่ยังต้องลงมือ ผมตรวจทุกข้อด้านล่างนี้กับเครื่องจริงก่อนเขียน
| สิ่งที่อ้างในเล่ม | สถานะจริง |
|---|---|
fleet-git-recover.sh ทำงานและกู้ได้จริง | BUILT + รันจริง · มี backup/auto-recover branch สองอันจากการรันวันที่ 22 เป็นหลักฐาน |
fleet-git-health.sh ตรวจ 4 invariant + self-verify | BUILT · สคริปต์อยู่ที่ ~/.hermes/bin/ รันมือผ่าน อ่านโค้ดได้ในเล่ม |
| propagation chain ผ่าน hermes external_dirs | BUILT · ~/.hermes/config.yaml ชี้ external_dirs: /root/labs/skills-mirror/skills จริง |
| ลำดับ recover ผ่าน codex review | REVIEWED · ได้ GO ก่อนเขียน policy SAFE-auto ลงสคริปต์ |
| systemd timer q30min เรียก health ทุก 30 นาที | BUILT + RUNNING · ออกแบบล้อ jizo-health-monitor · unit fleet-git-health.timer ติดตั้งบน box แล้ว เดินทุก 30 นาที ยืนยัน healthy หลายรอบใน #log (2026-06-23) |
พูดตรงๆ คือ หัวใจของระบบ คือ detect, safe-heal, self-verify เป็นของจริงที่รันแล้ว แต่ส่วนที่ทำให้มัน อัตโนมัติเต็มตัว และตอนนี้ timer ก็ติดตั้งบน box แล้ว เดินทุก 30 นาที ยืนยัน healthy หลายรอบใน #log เรียบร้อย ใครเอาไปใช้ต่อ ติดตั้ง timer คือขั้นสุดท้ายที่ต้องทำให้ครบ
ปิดเล่ม สามคำที่อยากให้ติดตัวไป
ถ้าจำทั้งเล่มไม่ไหว ขอแค่สามอย่าง หนึ่ง ความเงียบไม่ใช่สุขภาพดี ต้องมีคนวัดปลายทาง สอง auto ได้เฉพาะกรณีสะอาด conflict ส่งให้คน และ snapshot ก่อนแตะเสมอ สาม อย่าเชื่อว่าซ่อมเสร็จจนกว่าจะวัด post-state ด้วยตาตัวเอง
เริ่มจากตรงไหนดี ถ้าคุณดูแลระบบที่เป็นโซ่หลายข้อต่อ ลองถามตัวเองว่า ถ้าข้อต่อกลางพังเงียบๆ วันนี้ คุณจะรู้ตัวภายในกี่ชั่วโมง ถ้าคำตอบคือ ไม่รู้ นั่นคือจุดแรกที่ควรวาง invariant ลงไปตรวจ ไม่ต้องเริ่มจากตัวซ่อมอัตโนมัติ เริ่มจากแค่ทำให้มัน ส่งเสียงได้ ก่อน เท่านั้นก็เปลี่ยน failure เงียบให้เป็น failure ที่ได้ยินแล้ว
References / ที่มา
~/.hermes/bin/fleet-git-health.sh· ตัวตรวจสุขภาพ 4 invariant + self-verify + นโยบาย alert~/.hermes/bin/fleet-git-recover.sh· ตัวกู้ SAFE-auto (lock → backup → checkout -B → cherry-pick → push, ไม่ force)~/.hermes/config.yaml·external_dirsที่ต่อ skills-mirror เข้า hermes- repo
hello-oracle· branchbackup/auto-recover-20260622-181646,-184706,backup/pre-gitfix-20260623(หลักฐานว่า recover รันจริง) - วิธีทำหนังสือ · Tonk (
/read) และ Dustboy (/books), house-style ที่ฝูงวางไว้