[QA Brain MVP] T5: Web Domain Commands with CAS Concurrency & Promotion Copy Engine
สรุปงาน (Summary)
พัฒนา Web Domain Commands และ REST Endpoints ใน Happy QA สำหรับวงจรชีวิตของบทเรียน (Gotcha Lifecycle): การแก้ไข (Edit), การรับรอง (Verify), การยกเลิก (Deprecate), และการยกระดับสู่ส่วนกลาง (Promote as Promoted Copy) โดยบังคับใช้ Optimistic Concurrency Control (CAS), การส่งผ่าน Sanitizer บน Edit Path, การลงบันทึกใน Immutable Audit Trail (appendAudit) ทุกขั้นตอน และกำหนดการตั้งค่า approvedRevision และ promotedAt ใน Promotion Lifecycle อย่างครบวงจร
อ้างอิงข้อกำหนดสถาปัตยกรรม: Canonical RFC v4.1 (Section 2.2, 2.3, 5.5)
ลำดับการทำงาน (Dependency): ขึ้นต่อกับ T1 (Data Model) และ T2 (Sanitizer)
ขอบเขตและงานที่ต้องทำ (Scope of Work)
1. กฎการควบคุมการทำงานพร้อมกัน (CAS Concurrency Enforcement)
- ทุกคำสั่งการเปลี่ยนแปลงสถานะหรือเนื้อหาต้องรับพารามิเตอร์
expectedVersion: number - ตรวจสอบ
currentGotcha.version === expectedVersionภายใน Transaction:- หากเวอร์ชันในฐานข้อมูลไม่ตรงกับ
expectedVersion$\rightarrow$ ปฏิเสธทันทีด้วย HTTP 409VERSION_CONFLICT
- หากเวอร์ชันในฐานข้อมูลไม่ตรงกับ
2. พัฒนาคำสั่ง POST /api/v1/workspaces/:wsId/brain/:gotchaId/edit
-
สิทธิ์การเข้าถึง:
- Project Gotcha: ต้องมีบทบาท
QA_EDITOR+ในโปรเจกต์นั้น - Workspace Gotcha: ต้องมีบทบาท
WORKSPACE_EDITOR+ใน Workspace
- Project Gotcha: ต้องมีบทบาท
-
พารามิเตอร์ Body:
expectedVersion,title,what,evidenceType,evidenceRef,evidenceDetail,howToApply,tags,changeReason -
การประมวลผล:
- ตรวจ CAS (
version == expectedVersion) - Sanitizer Enforcement: ส่งเนื้อหาใหม่ทั้งหมดผ่าน Sanitizer Policy v1.0.0 ก่อน เพื่อ Mask ข้อมูลความลับหรือ PII
- คัดลอกข้อมูลปัจจุบันลงในตาราง
QaBrainRevision(revisionNumber = gotcha.version) - ปรับปรุงเนื้อหาใน
QaBrainGotcha, ขยับversion + 1 -
Re-verification Invariant: ปรับสถานะกลับเป็น
DRAFTเสมอ พร้อมเคลียร์verifiedByUserId = null,verifiedAt = null,verifiedRevision = null, และapprovedRevision = nullเพื่อรอการรับรองใหม่ - บันทึก Audit Log ผ่าน
appendAudit(tx, ctx, { operation: "brain.edit_gotcha", ... })
- ตรวจ CAS (
3. พัฒนาคำสั่ง POST /api/v1/workspaces/:wsId/brain/:gotchaId/verify (พร้อม Promotion Completion)
-
สิทธิ์การเข้าถึง:
- Project Gotcha: ต้องเป็น
QA_LEADหรือADMINในโปรเจกต์นั้น - Workspace Gotcha: ต้องเป็น
WORKSPACE_QA_LEADหรือWORKSPACE_ADMINใน Workspace
- Project Gotcha: ต้องเป็น
-
พารามิเตอร์ Body:
expectedVersion -
การประมวลผล:
- ตรวจ CAS (
version == expectedVersion) - อัปเดตฟิลด์การรับรองมาตรฐาน:
status = 'VERIFIED'verifiedByUserId = user.idverifiedAt = now()verifiedRevision = version
-
Promotion Lifecycle Completion: หาก Gotcha นี้เป็น Promoted Copy (
promotedFromGotchaId != null):- อัปเดต
approvedRevision = version(Revision ปัจจุบันของ Promoted Copy ที่ Lead ได้ตรวจรับรอง) - อัปเดต
promotedAt = now()(วันเวลาที่มีผลประกาศใช้เป็นความรู้ระดับ Workspace อย่างเป็นทางการ)
- อัปเดต
- บันทึก Audit Log ผ่าน
appendAudit(tx, ctx, { operation: "brain.verify_gotcha", ... })
- ตรวจ CAS (
4. พัฒนาคำสั่ง POST /api/v1/workspaces/:wsId/brain/:gotchaId/deprecate
-
สิทธิ์การเข้าถึง:
QA_LEADหรือADMINในขอบเขตของ Gotcha นั้น -
พารามิเตอร์ Body:
expectedVersion,reason -
การประมวลผล:
- ตรวจ CAS
- อัปเดต
status = 'DEPRECATED'และบันทึกเหตุผลลงใน Revision History - บันทึก Audit Log ผ่าน
appendAudit(tx, ctx, { operation: "brain.deprecate_gotcha", ... })
5. พัฒนาคำสั่ง POST /api/v1/workspaces/:wsId/brain/:gotchaId/promote (Promotion Proposal Draft)
-
สิทธิ์การเข้าถึง:
WORKSPACE_QA_LEADหรือWORKSPACE_ADMINเท่านั้น (Lead ของโปรเจกต์ต้นทางไม่มีสิทธิ์อนุมัติข้ามระดับเอง) -
พารามิเตอร์ Body:
-
sourceExpectedVersion: เวอร์ชันของ Gotcha ต้นทาง -
neutralTitle,neutralWhat,neutralEvidenceType,neutralEvidenceRef,neutralEvidenceDetail,neutralHowToApply,neutralTags
-
-
การประมวลผล:
- ตรวจสอบ Gotcha ต้นทางต้องมีสถานะเป็น
VERIFIEDและsource.version === sourceExpectedVersion - นำข้อมูลฉบับกลาง (
neutral*) ผ่านโมดูล Pre-Ingress Sanitizer -
สร้างบทเรียนใหม่แบบ Promoted Copy (สถานะ DRAFT):
workspaceId = source.workspaceId-
projectId = null(เพื่อเป็นบทเรียนระดับ Workspace) promotedFromGotchaId = source.idsourceGotchaRevision = source.version-
approvedRevision = null(ยังไม่ได้รับการอนุมัติ) -
promotedAt = null(ยังไม่มีผลรับรองอย่างเป็นทางการ) -
status = 'DRAFT'(ต้องผ่านขั้นตอน Verify ในข้อ 3 โดย Workspace Lead)
- ต้นฉบับในโปรเจกต์เดิมยังคงอยู่ครบถ้วน ไม่ถูกลบหรือเปลี่ยนแปลง
- บันทึก Audit Log ผ่าน
appendAudit(tx, ctx, { operation: "brain.promote_gotcha", ... })
- ตรวจสอบ Gotcha ต้นทางต้องมีสถานะเป็น
เกณฑ์การตรวจรับ (Acceptance Criteria)
-
Concurrent Edit Test: ทดสอบส่งคำสั่ง Edit สองคำขอพร้อมกันด้วย
expectedVersionเดียวกัน คำขอแรกสำเร็จ คำขอที่สองได้VERSION_CONFLICT -
Sanitizer on Edit Test: ส่งคำขอ Edit ที่มี API Token ปะปน ข้อมูลใน Revision และ Gotcha ใหม่ต้องถูก Mask เป็น
[REDACTED_*]เสมอ -
Promotion Lifecycle Test:
- เมื่อ Promote จากโปรเจกต์ ได้แถวใหม่ที่มี
status = 'DRAFT',approvedRevision = null, และpromotedAt = null - เมื่อ Workspace Lead กด Verify แถว Promoted Copy นี้ สถานะเปลี่ยนเป็น
VERIFIED,approvedRevisionถูกเซ็ตเท่ากับversion, และpromotedAtถูกประทับเวลาnow()
- เมื่อ Promote จากโปรเจกต์ ได้แถวใหม่ที่มี
-
Demotion on Edit Test: Gotcha ส่วนกลางที่ผ่านการรับรองแล้ว เมื่อถูก Edit สถานะต้องเปลี่ยนกลับเป็น
DRAFTและล้างapprovedRevisionออกทันที -
Audit Event Generation: ทุก Action (edit, verify, deprecate, promote) สร้างแถวใหม่ใน
qa_audit_eventพร้อมต่อโซ่ Hash ถูกต้อง