Skip to content

RFC-0007 Amendment 2: supersede terminal ที่ไม่ได้ส่งมอบงานได้ทุกตัว (#21) - #25

Merged
monthop-gmail merged 1 commit into
mainfrom
feat/supersede-any-terminal
Aug 19, 2026
Merged

RFC-0007 Amendment 2: supersede terminal ที่ไม่ได้ส่งมอบงานได้ทุกตัว (#21)#25
monthop-gmail merged 1 commit into
mainfrom
feat/supersede-any-terminal

Conversation

@monthop-gmail

Copy link
Copy Markdown
Owner

ปิด #21RFC-0007 Amendment 2: supersedes_job_id อ้าง terminal ที่ ไม่ได้ส่งมอบงาน
ได้ทุกตัว ไม่ใช่แค่ FAILED

approval/v1 บอกว่า approval ที่หมดอายุ "ต้องขอใหม่" · #17 บังคับครึ่งแรกไปแล้ว (ExpiredApproval)
แต่ครึ่งหลังทำไม่ได้ — job ที่ approval หมดอายุจบที่ TIMED_OUT ซึ่งอ้างไม่ได้
การยื่นใหม่จึงเป็น job ที่ไม่มีอะไรลิงก์กลับ · สายโซ่ขาดตรงจุดที่คำถาม audit เริ่ม

กฎใหม่

SUPERSEDABLE = TERMINAL - {COMPLETED}      # FAILED · CANCELLED · TIMED_OUT

Decision 1 เขียนกฎไว้กับ FAILED เพราะตอนนั้นคิดถึงแต่การกู้จากความล้มเหลว
· แต่สิ่งที่มันต้องการจริงคือ สายโซ่ของความพยายามที่ตรวจสอบได้ — job ที่หมดเวลา
หรือถูกคนสั่งหยุด ก็เป็นความพยายามที่ใช้ไปแล้วไม่ต่างกัน

ไม่แตะตาราง transition เลย — ตรวจแล้วว่า TRANSITIONS เหมือน main ทุก edge
· FAILED ยัง terminal · ใบที่ถูก supersede ยังโยน TerminalState ถ้าพยายามปลุก (มีเทสยืนยัน)
· สิ่งที่เปลี่ยนคือ ใครถูกอ้างถึงได้ ไม่ใช่ใครเดินต่อได้

COMPLETED อ้างไม่ได้ — ตั้งใจ

การ supersede คือการอ้างว่า "ใบนี้ไม่ได้ส่งมอบ นี่คือการลองใหม่" · งานที่ COMPLETED
ส่งมอบไปแล้ว สิ่งที่ตามมาจากมัน (แก้บั๊ก · เฟสสอง · revert) คือ งานใหม่ที่มีเหตุผลของตัวเอง
ถ้าปล่อยให้อ้างได้จะเสียสามอย่าง:

  1. สายโซ่เลิกแปลว่าอย่างเดียว — วันนี้ไล่ย้อนกลับไป ทุกใบคือความพยายามที่ไม่ได้ผล
    ถ้ารับ COMPLETED คนอ่านต้องเปิด state ปลายทางทีละใบถึงจะรู้ว่าใบก่อนหน้าได้ผลอะไรออกมาไหม
  2. ผลที่ settle แล้วถูกตีความใหม่ย้อนหลัง — job ที่ยื่นทีหลังประกาศว่าผลเดิมใช้ไม่ได้
    โดยไม่มี decision record ที่ไหนพูดแบบนั้น
  3. ตอบคำถามที่ไม่มีใครถาม"ต้องขอใหม่" ที่เป็นต้นเรื่องของ lifecycle: approval หมดอายุแล้ว "ต้องขอใหม่" — แต่ไม่มีทางขอใหม่ที่ลิงก์กลับงานเดิม #21 พูดถึง approval ที่
    หมดอายุ ซึ่งเป็นเคสตรงข้ามกับงานที่เสร็จแล้ว

Job.supersede() บน COMPLETED จึงโยน InvalidTransition พร้อมข้อความที่บอกเหตุผล
· สิ่งที่ follow-up จริง ๆ ต้องการคือ link คนละตัว ("ต่อยอดจาก") ที่มีชื่อและกฎของตัวเอง
— เขียนไว้ใน Future Work ของ RFC-0007 ไม่เอา field นี้ไปใช้ซ้ำ

ผลตรวจ

gate ก่อน หลัง
pytest 504 520 passed
payload_check.py 20 20 passed · 0 fail
simulation/e2e_flow.py 34 41 passed · 0 fail

flow ใหม่ต่อจาก approval_expired ที่ #17 ทิ้งไว้ — เดินจนถึงการยื่นใหม่ที่ลิงก์กลับ
แล้ว replay พิสูจน์ว่าสายโซ่ครบ · ไม่ bump semantics_version (job_state_machine
อยู่ใต้ not_derived: นอก hash_scope ของ frozen: เส้นเดียวกับ RFC-0011)

ช่องว่างที่บันทึกไว้ ไม่ได้ปิด

Job(supersedes_job_id=...) ผ่าน constructor ยังไม่มีใครเฝ้า — ตรวจแค่รูปแบบ id
· ใบที่อ้างมีจริงไหม · tenant เดียวกันไหม · จบแบบไม่ได้ส่งมอบจริงไหม ยังไม่มีใครตรวจ
· supersede() บังคับครบเพราะถือ job ทั้งสองใบอยู่ในมือ แต่ทางที่ข้ามมันไปยังเปิดอยู่
· ปิดจริงต้องมี job registry ซึ่ง repo นี้ยังไม่มี → ลง Future Work + remaining: ไว้
แทนที่จะสร้างครึ่ง ๆ

เก็บถ้อยคำที่หมดอายุไปพร้อมกัน

  • RFC-0010 มีประโยคว่า "A FAILED job that never passed governance would have nothing
    coherent to supersede"
    — ใช้แยก FAILED ออกจากตัวเลือกอื่นไม่ได้แล้ว เพราะ job ที่ถูก
    cancel ตั้งแต่ DRAFT ตอนนี้เป็นเป้าที่ supersede ได้ · เป็นประโยคสนับสนุน ไม่ใช่ตัวข้อโต้แย้ง
    (Decision 1 ยืนบน "ยังไม่มีงานให้ล้ม" ซึ่งไม่ขยับ) → ใส่ note ลงวันที่ไว้ ไม่แก้คำตัดสิน
  • RFC-0011 Future Work อ้างวิธีที่ supersedes_job_id ชี้ FAILED เป็นการเปรียบเทียบ
    → แก้เป็นถ้อยคำที่ยังจริง พร้อมวงเล็บบอกว่าเดิมเป็นแบบนั้นจนถึง Amendment 2

Closes #21

… FAILED (#21)

approval/v1 บอกไว้สองครึ่ง: approval ที่หมดอายุใช้เดินงานไม่ได้ · ต้องขอใหม่
ครึ่งแรกบังคับแล้วใน #17 (ExpiredApproval + APPROVED เข้า TIMEOUTABLE) ครึ่งหลัง
ทำไม่ได้ — job ที่ approval หมดอายุจบที่ TIMED_OUT ซึ่งเข้า FAILED ไม่ได้ (RFC-0010)
และ supersedes_job_id ตาม RFC-0007 Decision 1 ผูกไว้กับ FAILED เท่านั้น การยื่นใหม่
จึงกลายเป็น job ที่ไม่มีอะไรลิงก์กลับ และสายโซ่ audit ขาดตรงจุดที่คนตรวจจะถามพอดี

RFC-0007 Amendment 2 ขยาย Decision 1 จาก FAILED เป็น terminal ทุกตัวที่จบโดย
"ไม่ได้ส่งมอบงาน" — FAILED · CANCELLED · TIMED_OUT (states.SUPERSEDABLE)
เจตนาของ Decision 1 คือสายโซ่ของความพยายามที่ตรวจสอบได้ ซึ่งใช้ได้กับงานที่หมดเวลา
หรือถูกสั่งหยุดเหมือนกัน ข้อจำกัดที่ FAILED เป็นของแถมจากตอนเขียน ไม่ใช่ตัวข้อโต้แย้ง

COMPLETED อ้างไม่ได้ · การ supersede คือการอ้างว่า "ใบนี้ไม่ได้ส่งมอบ ขอลองใหม่"
งานที่ส่งมอบแล้วไม่ใช่ความพยายามที่รอความพยายามถัดไป — งานที่ต่อยอดจากมันคืองานใหม่
ที่ต้องมีเหตุผลของตัวเอง ถ้าปล่อยให้อ้างได้ สายโซ่จะเลิกแปลว่า "ความพยายาม" และกลาย
เป็น pointer แบบ "เกี่ยวข้องกัน" ที่ต้องไล่ดู state ปลายทางทีละใบถึงจะรู้ว่าใบก่อนหน้า
ได้ผลอะไรออกมาไหม · เหตุผลเต็มอยู่ใน Amendment 2

ไม่แตะตาราง transition เลย — ไม่มี edge ใหม่ · terminal ยังคง terminal ·
ไม่มีการปลุก job เดิม · ที่ผ่อนคือ "ใครถูกอ้างได้" เท่านั้น
ไม่ bump semantics_version — job_state_machine อยู่ใต้ not_derived นอก subtree
frozen ที่ drift_check ระบุเป็น hash scope เหมือนตอน RFC-0011 เพิ่ม edge

- states.SUPERSEDABLE = TERMINAL - {COMPLETED} · Job.supersede() ตรวจกับชุดนี้
- TerminalState เลิกเสนอทาง recovery ให้ COMPLETED ซึ่งเป็นทางที่ถูกปฏิเสธ
- flows.approval_expired_then_resubmitted เดินต่อจาก flow ของ #17 จนถึงการยื่นใหม่
- e2e_flow เพิ่มหมวด [3c] · replay ไล่สายโซ่จาก audit log อย่างเดียวได้ครบ
- RFC-0010 กับ RFC-0011 อ้าง "FAILED" ในฐานะกฎเดิม — ใส่หมายเหตุชี้มาที่ Amendment 2
- platform-contract บันทึกช่องว่างที่ยังเหลือ: constructor รับ supersedes_job_id
  โดยไม่ตรวจว่าใบที่อ้างมีจริง/tenant เดียวกัน/จบแบบไม่ได้ส่งมอบจริง

pytest 520 · payload_check 20 · e2e_flow 41 — FAIL=0 ทั้งหมด
@monthop-gmail
monthop-gmail merged commit f382612 into main Aug 19, 2026
5 checks passed
@monthop-gmail
monthop-gmail deleted the feat/supersede-any-terminal branch August 19, 2026 17:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant