Skip to content

policy_result เลิกเป็นสำเนามือของ Decision — policy/v1 v1.3.0 · event/v1 v1.5.0 - #34

Merged
monthop-gmail merged 1 commit into
mainfrom
policy-result-single-source
Aug 21, 2026
Merged

policy_result เลิกเป็นสำเนามือของ Decision — policy/v1 v1.3.0 · event/v1 v1.5.0#34
monthop-gmail merged 1 commit into
mainfrom
policy-result-single-source

Conversation

@monthop-gmail

Copy link
Copy Markdown
Owner

ADR-0018 option B · ปิดข้อค้นพบ 3 ของ ADR-0016

ปัญหา — พิสูจน์แล้วว่าเพี้ยนจริง ไม่ใช่ความเสี่ยงทางทฤษฎี

event/v1.policy_result ประกาศรูปของตัวเอง ไม่ได้ $ref ไป policy/v1 Decision → มีสองที่ที่บอกว่า "ผลของ policy หน้าตาอย่างไร" และไม่มีอะไรคอยจับว่ายังตรงกัน

consent ที่ ADR-0016 เพิ่งเพิ่มเข้า Decision ไม่ไหลไปที่นั่นเอง — ต้องอาศัยคนจำ · เป็น drift ชนิดเดียวกับที่ drift_check มีไว้จับข้าม repo แต่เกิดภายในไฟล์ของเราเอง ซึ่งไม่มี check ตัวไหนมอง

ทำไมไม่ยกทั้ง Decision มาใส่ event

ตรวจก่อนแล้วว่า ไม่มี consumer รายไหนใช้ policy_result เลย (grep ทั้ง devfactory-core = 0 · GitHub code search ใน care-agent-platform = 0) ทั้งที่ทั้งคู่ pin event/v1 — ทางที่เข้มขึ้นจึงไม่ทำให้ใครพังวันนี้ แต่ยังไม่เลือก ด้วยสองเหตุผล:

  1. Decision.required มี 4 ตัว → policy_result ที่ใส่ไม่ครบกลายเป็น invalid = เข้มขึ้น = breaking ตามตัวอักษรของ ADR-0006 · การเอาหลักฐาน "ไม่มีใครใช้" มาแทนกฎ ควรเป็นทางเลือกสุดท้าย ไม่ใช่ทางแรก
  2. Decision มี reason ที่เป็น free text — ถ้าไหลเข้า audit record เองผ่าน $ref จะเฉียดกับ guarantee ข้อ 7 ของ event/v1 (🔒 ห้ามเก็บ private reasoning เป็น audit record) โดยไม่มีใครตัดสินใจ · และ expires_at ก็ไม่มีความหมายในบันทึกที่ immutable แล้ว

การที่ policy_result เป็นชุดย่อยเป็นเรื่องถูกต้อง — ที่ผิดคือมันถูกประกาศซ้ำ ไม่ใช่ที่มันย่อ

สิ่งที่เปลี่ยน

# policy/v1
$defs:
  DecisionSummary:          # effect · authority · action_risk · policy_id — ไม่มี required
Decision:
  allOf: [ { $ref: '#/$defs/DecisionSummary' } ]
  required: [effect, authority, policy_id, evaluated_at]
  properties: [constraint, reason, evaluated_at, expires_at, consent]   # ส่วนที่เกิน

# event/v1
policy_result: { $ref: policy/v1#/$defs/DecisionSummary }

🔒 field ใหม่ใน Decision ไม่ไหลเข้า Summary เอง — ต้องเคาะทุกครั้ง · เขียนกำกับไว้ในตัว schema เพื่อไม่ให้คนถัดไปคิดว่าเป็นความหลงลืม · consent จงใจไม่อยู่ใน Summary เพราะ event มี consent ของตัวเองที่ระดับบนสุดแล้ว (v1.4.0) ใส่ซ้ำสองที่ในบันทึกเดียวคือ drift ที่รอเกิด

ไม่ breaking — และ ไม่เข้มขึ้นแม้แต่นิดเดียว

DecisionSummary ไม่มี required เหมือนที่ policy_result เดิมไม่มี · ชุด property และ required ของ Decision เท่าเดิมทุกตัว — ย้ายที่ประกาศ ไม่ได้ย้ายความหมาย · guarantees ไม่ขยับ · semantics_version ยัง "1.1"

ตรวจแล้ว

drift_check.pypassed=20 FAIL=0 WARN=0 · payload จริง 13 เคส ตรงตามคาดทั้งหมด

กลุ่ม เคส ผล
Decision พฤติกรรมต้องไม่เปลี่ยนจากก่อน allOf ครบ 4 required · เต็มรูปทุก field · ขาด effect · ขาด evaluated_at · effect/authority นอก enum ✅ ตรงเดิมทุกเคส
policy_result รูปที่ยอมรับต้องเหมือนเดิม ไม่มีเลย · ครบ 4 · ใส่บางส่วน · {} ว่างเปล่า ✅ ยัง valid — พิสูจน์ว่าไม่เข้มขึ้น
policy_result ค่าผิด effect · authority · action_risk นอก enum ✅ ถูก reject

ผลต่อ consumer

ไม่มีใครต้องทำอะไร — ไม่มีใครใช้ policy_result และรูปที่ยอมรับไม่เปลี่ยน

🤖 Generated with Claude Code

https://claude.ai/code/session_01LHv7HRmnnGAoKT5BvxDWHs

…/v1 v1.5.0

ADR-0018 เคาะ option B

event/v1.policy_result เคยประกาศรูปของตัวเอง ทำให้มีสองที่ที่บอกว่าผลของ policy
หน้าตาอย่างไร และไม่มีอะไรคอยจับว่ายังตรงกัน — consent ที่ ADR-0016 เพิ่มเข้า
Decision ไม่ไหลไปที่นั่นเอง พิสูจน์แล้วว่าเพี้ยนจริง ไม่ใช่ความเสี่ยงทางทฤษฎี

ไม่เลือกทางยกทั้ง Decision ไปให้ event สองเหตุผล หนึ่ง Decision.required มี 4 ตัว
ทำให้ policy_result ที่ใส่ไม่ครบกลายเป็น invalid = เข้มขึ้น = breaking ตามตัวอักษร
สอง Decision มี reason ที่เป็น free text ซึ่งถ้าไหลเข้า audit เองจะเฉียดกับ
guarantee ข้อ 7 ของ event/v1 โดยไม่มีใครตัดสินใจ

การที่ policy_result เป็นชุดย่อยจึงเป็นเรื่องถูกต้อง ที่ผิดคือมันถูกประกาศซ้ำ
DecisionSummary จึงประกาศสี่ field ที่ใช้ร่วมกันครั้งเดียว Decision อ้างผ่าน allOf
และ event/v1 อ้างตัวเดียวกัน

field ใหม่ใน Decision จะไม่ไหลเข้า Summary เอง ต้องเคาะทุกครั้ง เขียนกำกับไว้ในตัว
schema เพื่อไม่ให้คนถัดไปคิดว่าเป็นความหลงลืม

ไม่มีอะไรเข้มขึ้น Summary ไม่มี required เหมือนที่ policy_result เดิมไม่มี
รูปที่ยอมรับเหมือนเดิมเป๊ะ guarantees ไม่ขยับ semantics_version ยัง 1.1

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHv7HRmnnGAoKT5BvxDWHs
@monthop-gmail
monthop-gmail merged commit 16d66e4 into main Aug 21, 2026
1 check passed
@monthop-gmail
monthop-gmail deleted the policy-result-single-source branch August 21, 2026 09:56
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.

1 participant