Skip to content

End-to-end flow simulation + replay ที่พิสูจน์ว่า audit trail ครบ (#7) - #19

Merged
monthop-gmail merged 1 commit into
mainfrom
feat/e2e-flow-simulation
Aug 19, 2026
Merged

End-to-end flow simulation + replay ที่พิสูจน์ว่า audit trail ครบ (#7)#19
monthop-gmail merged 1 commit into
mainfrom
feat/e2e-flow-simulation

Conversation

@monthop-gmail

Copy link
Copy Markdown
Owner

ปิด #7 — ประตูสุดท้ายของ v0.1.0 · job ถูกขับผ่าน state machine จริง สิ่งที่มัน emit ถูกเก็บใน
audit log จริง แล้ว อ่านย้อนกลับเพื่อพิสูจน์ว่า trail อธิบายสิ่งที่เกิดขึ้นได้ครบ

รันยังไง

python3 simulation/e2e_flow.py            # simulation + ผลตรวจ
python3 simulation/e2e_flow.py --trail    # + audit trail ที่ผลิตได้
python3 -m pytest simulation/tests        # guarantee ชุดเดียวกันแบบ test suite

script คือสิ่งที่ issue ขอและเป็นของที่คนนั่งดูได้ · suite คือของที่กันไม่ให้ guarantee ถอยหลัง
ตอนไม่มีใครดู · CI รันทั้งคู่

ผลตรวจ

ผล
pytest ทั้งชุด 441 passed (เดิม 350 · +91)
simulation/e2e_flow.py 29 checks passed · 0 fail exit 0
conformance/payload_check.py 13 passed · 0 fail

จุดที่ตั้งใจออกแบบให้ไม่หลอกตัวเอง

flow หลัก derive จากตาราง ไม่ใช่เขียนตามที่ issue บอกMAIN_LINE คำนวณจาก _PROGRESSION
แล้ว assert ว่าตรงกับลำดับที่ #7 เขียนไว้ ถ้าตารางกับ issue ขัดกันเมื่อไหร่ check ข้อนี้แดง
แทนที่จะเดินตาม issue แล้วรายงานผ่านทั้งที่ตารางเปลี่ยนไปแล้ว

governance gate ถูกทดสอบจากสองทาง — เข้า POST_APPROVAL โดยไม่มี Decision ไม่ได้ทั้ง 5 state
· และ ยัด _state=APPROVED เข้าไปตรง ๆ ก็ยังโดน ExecutionBeforeApproval เพราะ gate ผูกกับ
record ไม่ใช่ flag (ผลของ #5) · อ่านย้อนจาก log ด้วย: ลบ GOVERNANCE_DECISION ออก → replay ปฏิเสธ

replay พิสูจน์ด้วยการทำลาย — ลบ transition ใบไหนก็ได้ใน 7 ใบ · สลับลำดับ · ปลอม edge
IN_PROGRESS→COMPLETED ที่ตารางไม่มี → จับได้ทุกกรณี (UndeclaredTransition)

FAILED ครบทั้ง 5 state ที่ FAILABLE อนุญาต และถูกปฏิเสธครบทั้ง 4 state ที่เหลือ
· recovery ผ่าน job ใหม่ที่ supersedes ต้องผ่าน governance ใหม่ตาม RFC-0007 Decision 1

⚠️ ช่องว่างที่เจอและ ไม่ได้ปิด — ตั้งใจ

trail ที่ถูกตัดท้ายตรวจไม่ได้ ยกเว้น job ที่จบด้วย COMPLETED

ทุก record ถูกตรวจโดย record ถัดไป (ใบถัดไปบอกว่าออกจาก state ไหน) แต่ ไม่มีอะไรตรวจใบสุดท้าย
JOB_COMPLETED ช่วยได้เฉพาะ job ที่จบสวย · ผลคือ trail ที่หาย event ท้ายสุดของ job ที่จบเป็น
FAILED/CANCELLED/TIMED_OUT หรือ job ที่ยังเดินอยู่ replay ผ่านเรียบร้อยโดยได้ state ก่อนหน้าใบที่หาย

ปิดจริงต้องมี per-job sequence number ใน event/v1 ซึ่งเป็น wire shape ของ agent-platform
(RFC-0005 Rule 1) จึงไม่ใช่สิ่งที่ตัดสินใน PR นี้ · สิ่งที่ทำแทน:

  • เพิ่ม IncompleteSettlement ปิดเท่าที่ปิดได้โดยไม่แตะสัญญา
  • เขียน limit ไว้ใน docstring และ README
  • มีเทสชื่อ test_a_trail_cut_short_at_a_terminal_other_than_completed_still_replays
    ที่ยืนยันว่ามันตรวจไม่ได้
    — บันทึกช่องว่างไว้เป็นโค้ด ไม่ใช่ซ่อน

→ ควรเปิด issue แยกเรื่อง sequence number

ที่ไม่แตะตามขอบเขต

REQUIRE_CHANGES (ยังไม่มีปลายทาง — payload_check ยังยืนยันว่าถูกปฏิเสธ) ·
APPROVED timeout (#17) · ไม่มี transition ใดถูก hard-code นอก states.py ·
ไม่มี guarantee เดิมถูกลดความเข้มลง

Closes #7

…plete (#7)

Milestone v0.1's closing gate: a governed job driven end to end through the real
state machine, everything it emits filed in the real audit log, and the trail
then read back to check it accounts for what happened.

Delivered as both forms issue #7 allows. `simulation/e2e_flow.py` is what a
person runs to watch a job move; `simulation/tests/test_e2e_flow.py` is what
fails a pull request when a guarantee regresses. Both run in CI.

The forward path is read out of `states.py`, not retyped
--------------------------------------------------------
`flows.main_line()` derives DRAFT → … → COMPLETED from the transition table by
discarding the exits available from nearly everywhere, and raises rather than
choosing if the table ever stops naming one way forward. The flow #7 spells out
appears exactly once, as the thing that derivation is compared *against* — so a
disagreement between the issue and the table fails the check instead of the
simulation quietly following the issue and reporting success.

`states.reachable_from()` folds AWAITING_APPROVAL's per-job return edge into the
table read, so the engine and anything reading the trail back ask the same
question of the same source.

Replay is a completeness proof, not a convenience
-------------------------------------------------
`devfactory_observability.replay` rebuilds a job from its events alone. Every
STATE_TRANSITION names the state it left, so a replay holding a running state
notices a record that is missing or out of order — which the engine cannot,
having written them. It re-checks the guarantees against what was actually
written: every edge against `states.reachable_from`, every APPROVED against a
GOVERNANCE_DECISION that really produced it, and the direction lock against the
record rather than the engine's memory of it.

`conformance/payload_check.py` now drives the same flows from `flows.py`. It asks
a different question — do the payloads conform — but it should not be asking it
about a different journey.

Two limits recorded rather than smoothed over
---------------------------------------------
A trail truncated at the end is detectable only for a job that completed:
JOB_COMPLETED is the one record that says a transition should have followed.
Nothing says so for FAILED, CANCELLED, TIMED_OUT, or a job still in flight, and
closing that needs a per-job sequence number in event/v1 — a contract change.

`UnauditedExecution` cannot fire on a table-consistent trail, since TASK_PLANNING
is reachable only from APPROVED and APPROVED is refused without a decision. It is
a structural backstop, kept for the reason job.py keeps ExecutionBeforeApproval.

Nothing here weakens an existing guarantee, and no transition is declared outside
`states.py`. 350 tests → 441; payload conformance unchanged at 13 passed, 0 fail.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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: End-to-end flow simulation

1 participant