Skip to content

Governance decision interface — decision เป็น record ไม่ใช่ flag (#5) - #18

Merged
monthop-gmail merged 1 commit into
mainfrom
feat/governance-decision-interface
Aug 19, 2026
Merged

Governance decision interface — decision เป็น record ไม่ใช่ flag (#5)#18
monthop-gmail merged 1 commit into
mainfrom
feat/governance-decision-interface

Conversation

@monthop-gmail

Copy link
Copy Markdown
Owner

ปิด #5 · decision กลายเป็น record ที่ตรวจสอบได้ ไม่ใช่ flag ที่ใครก็ตั้งได้

แกนของการเปลี่ยน

Job._approved: boolJob._approval: Decision | None

ข้อห้าม "execution ก่อน APPROVED" เดิมผูกกับ boolean ที่ตั้งเป็น True ได้โดยไม่มีใครรับผิดชอบ
ตอนนี้ผูกกับ record ที่บังคับ 4 field ตาม RFC-0002decision · reason · decided_at · authority
ถ้าไม่มีใบอนุมัติจริง งานเดินต่อไม่ได้ และ "ใครอนุมัติ" ตอบได้เสมอ

Decision เป็น frozen dataclass · serialize เป็นรูป approval/v1 ของ agent-platform ตรง ๆ
(decision_idapproval_id ตาม RFC-0005 Rule 1) · self-approval ของ agent ถูกปฏิเสธ
ตาม invariant ที่สัญญาเขียนไว้ ("no agent has total authority")

REQUIRE_CHANGES — รับเป็นคำ แต่ยังใช้ไม่ได้ โดยตั้งใจ

vocabulary มีครบ 3 ค่าตามสัญญา (ชุดปิด ขาดไม่ได้) แต่ engine ปฏิเสธ REQUIRE_CHANGES
ด้วย UnmappedDecision
เพราะ ไม่มีเอกสารไหนบอกว่างานไปสถานะไหนต่อ:

  • approval/v1 guarantee บอกแค่ว่า "ไม่ใช่ REJECT — งานยังมีชีวิตและกลับมายื่นใหม่ได้"
  • GOVERNANCE_ANALYSIS มีทางออกแค่ APPROVED กับ REJECTED

ทางเลือกทั้งสามทาง (เพิ่ม edge → DRAFT · ใช้ REJECTED · เพิ่ม state ที่ 14) เป็น lifecycle change
ที่ต้องมี RFC ก่อนตาม CONTRIBUTING.md · PR นี้จึงไม่เลือกให้ — ถามไว้ที่
agent-platform#22 แล้ว

ยอมให้ค่าหนึ่งใช้ไม่ได้ชั่วคราว ดีกว่าปล่อยให้โค้ดเลือกกฎ lifecycle เองแล้วเอกสารตามไม่ทัน
ซึ่งเป็นสิ่งที่เพิ่งเกิดกับเราใน #14 มาแล้วรอบหนึ่ง · ไม่บล็อก PR นี้

audit trail

ทุก APPROVE/REJECT emit GOVERNANCE_DECISION คู่กับ STATE_TRANSITION เสมอ
ไม่ว่าจะเข้าผ่าน decide() หรือ transition() ทั่วไป — guarantee ผูกกับ state ไม่ใช่กับ method
จึงไม่มีทางเข้า APPROVED แบบข้ามการบันทึกได้

known gap ที่ลงทะเบียนไว้

approval/v1 บังคับว่า "การเปลี่ยนใจคือ approval ใบใหม่ที่อ้างใบเดิม" แต่ schema ไม่มี field ให้อ้าง
· ใส่ supersedes_decision_id ฝั่งเราไปก่อน (schema ไม่ได้ปิด additionalProperties)
และลงใน gaps: ของ platform-contract.yaml ชี้ไป agent-platform#22 · ถ้าเขาตั้งชื่อ field เอง
งานที่เหลือคือ rename ไม่ใช่ออกแบบใหม่

ผลตรวจ

  • pytest 350 passed (เดิม 302 · เทสใหม่ 48 ตัวใน test_decisions.py)
  • conformance/payload_check.py 13 passed · 0 fail — validate decision จริงกับ approval/v1
    และ probe ว่า REQUIRE_CHANGES ถูกปฏิเสธจริง
  • ไม่ bump semantics_version · ไม่แตะบล็อก frozen

breaking ระดับ in-repo

approve() / reject() คืน Decision แทน Event — call site ในเรโปแก้ครบแล้ว

ที่ยังไม่ทำในนี้

  • ปลายทางของ REQUIRE_CHANGES → รอ agent-platform#22 แล้วค่อยออก RFC
  • คนอนุมัติงานตัวเองยังทำได้ (ปฏิเสธเฉพาะ agent ตามที่สัญญาเขียน) — บันทึกไว้ใน
    state-machine.md §Open questions ถ้าจะขยายให้ครอบคลุมคนต้องเป็น RFC

Closes #5

`Decision` เป็น frozen dataclass ที่บังคับ 4 field ตาม RFC-0002 (decision · reason
· decided_at · authority) และ serialize เป็นรูป approval/v1 ของ agent-platform
· `Job._approved: bool` กลายเป็น `Job._approval: Decision | None` — ข้อห้าม
"execution ก่อน APPROVED" จึงผูกกับ record จริงแทน flag ที่ใครก็ตั้งได้

REQUIRE_CHANGES: vocabulary มีครบ 3 ค่าตามสัญญา แต่ engine ปฏิเสธค่านี้ด้วย
UnmappedDecision เพราะไม่มีเอกสารไหนบอกว่า job ไปสถานะไหนต่อ — approval/v1
บอกแค่ว่า "ไม่ใช่ REJECT · งานยังมีชีวิต" และ GOVERNANCE_ANALYSIS มีทางออก
แค่ APPROVED/REJECTED · ไม่เดาปลายทาง ไม่เพิ่ม edge ไม่เพิ่ม state ที่ 14
ถามไว้ที่ agent-platform#22 แล้ว รอ RFC ก่อนค่อยเปิดใช้

ทุก APPROVE/REJECT emit GOVERNANCE_DECISION คู่กับ STATE_TRANSITION เสมอ
ไม่ว่าจะเข้าผ่าน decide() หรือ transition() ทั่วไป — guarantee ผูกกับ state
ไม่ใช่กับ method

known gap: approval/v1 บังคับให้ "อ้างใบเดิม" แต่ schema ไม่มี field ให้
ใส่ supersedes_decision_id ไปก่อนและลงทะเบียนใน gaps: → agent-platform#22

pytest 350 passed (เดิม 302) · payload_check 13 passed 0 fail

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@monthop-gmail
monthop-gmail force-pushed the feat/governance-decision-interface branch from 53e961b to 801ee9a Compare August 19, 2026 12:12
@monthop-gmail
monthop-gmail merged commit 1067ef1 into main Aug 19, 2026
4 checks passed
@monthop-gmail
monthop-gmail deleted the feat/governance-decision-interface branch August 19, 2026 12:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phase 1: Implement Governance Decision Interface

1 participant