P7B — Sandbox PG Prototype Design v0.3
P7B — Sandbox PG Prototype Design
Loại: Prototype design — Điều 38 Text as Code Phase: P7B (Sandbox Prototype), thiết kế kho mẫu PG Trạng thái: OFFICIAL v0.3 — GPT PASS. User PASS. Ngày soạn: 2026-04-26 | Phiên: S182 Agent soạn: Opus 4.6 (Desktop) GPT review: v0.1 PASS CĐK (14) → v0.2 → v0.3 PASS CĐK nhẹ (4) → FINAL PASS Inputs: P7 v0.2, P7A report (76 units), P5 v0.2, P6 v0.2, Đ33 v2.1, Đ24
1. Mục tiêu
Thiết kế sandbox PG prototype — "kho mẫu" có kệ thật nhưng KHÔNG nối production — để kiểm chứng P5 schema, birth gate checkers (P6), constraints, round-trip lossless.
P7B chỉ thiết kế. Dựng thật = bước riêng sau P7B PASS + User duyệt execution riêng (Cổng 2).
P7B PASS = permission to REQUEST User approval for sandbox execution. KHÔNG tự cho phép chạy DDL.
2. Phạm vi / Không làm
Trong: Sandbox schema design, DDL candidates, seed plan, checker plan, hash plan, round-trip plan, rollback, no-production-change guard, pre-flight dependencies, execution pipeline, PASS/FAIL.
Không: Chạy DDL thật, đụng production, Directus collection, sửa P5/P5b/P6/P7/C1–C3/LSL/L1–L5, Component/BOM (round 2), Qdrant, DOT cron, migration production, cross-read public.entity_labels.
3. Hiện trạng từ P7A
76 units (HOWTO: 8, C1A: 34, P5: 34). 3 proposed publications. 228 label mappings. 0 ERROR simulated checkers. Round-trip 0 content loss.
4. Sandbox Boundary
4.1 Phương án: PG Schema riêng trong directus DB
Tạo PG schema sandbox_tac trong database directus (Lớp KHO, Đ33 §0.1). Rollback sạch: DROP SCHEMA sandbox_tac CASCADE.
4.2 Giả định cần verify (pre-flight)
GĐ-1: Directus CMS chỉ scan schema
publicvà không tự expose tables trongsandbox_tac. CẦN VERIFY trong pre-flight: (a) kiểm Directus configDB_SEARCH_PATH, (b) sau tạo schema, kiểm admin UI. Nếu Directus scan cả sandbox_tac → revoke usage hoặc chọn DB riêng.
4.3 Naming
Schema: sandbox_tac. Tables: giữ P5 names. Indexes: idx_sbx_{table}_{field}. Functions: fn_sbx_{purpose}.
4.4 Sandbox ≠ Production SoT
Sandbox = prototype, không authoritative, temporary. Production SoT unchanged.
4.5 User / Role
Đề xuất: workflow_admin (owner directus DB per Đ33 §14.2). Cần verify trước execution: (a) CREATE privilege, (b) phù hợp Đ33 E1 gateway rules. DDL qua docker exec postgres psql, idempotent, có action log.
5. Pre-flight Dependency Checklist
| # | Dependency | Check | Required | Fallback |
|---|---|---|---|---|
| PF-1 | pgcrypto | SELECT FROM pg_extension WHERE extname='pgcrypto' |
Exists | Remediation riêng: CREATE EXTENSION IF NOT EXISTS pgcrypto cần superuser (docker exec postgres psql -U postgres), chỉ chạy nếu User/DBA approve |
| PF-2 | CREATE SCHEMA privilege | SELECT has_database_privilege(...) |
TRUE | Request grant |
| PF-3 | PG version ≥ 13 | SELECT version() |
PG 16 | — |
| PF-4 | Directus không scan sandbox | Sau CREATE SCHEMA → check admin UI | No sandbox tables visible | DROP SCHEMA sandbox_tac CASCADE + ABORT |
| PF-5 | Production clean | SELECT count(*) FROM pg_tables WHERE schemaname='public' AND tablename LIKE '%logical_unit%' |
0 | ABORT |
| PF-6 | Password GSM | .env at /opt/incomex/.env |
Exists, chmod 600 | — |
6. Sandbox Table Proposal
6.1 Tables (8)
| # | Table | Source | Notes |
|---|---|---|---|
| 1 | section_type_vocab | P5 §5.5 | 11 types seed |
| 2 | publication_type_vocab | P5 §5.6 | 1 type seed |
| 3 | logical_unit | P5 §5.1 | 76 rows |
| 4 | unit_version | P5 §5.2 | 76 rows |
| 5 | publication | P5 §5.3 | 3 rows |
| 6 | publication_member | P5 §5.4 | 76 rows |
| 7 | change_set | P5 §5.7 | 0 rows (hook) |
| 8 | change_set_member | P5 §5.8 | 0 rows (hook) |
6.2 Sandbox DDL Candidates
⚠️ NOT TO RUN UNTIL USER APPROVES SANDBOX EXECUTION (Cổng 2) ⚠️ DDL dưới đây đã executable. CHECK enum trong sandbox OK cho prototype. Production (P9) PHẢI dùng FK vocab/config per NT4 nếu enum governance mở rộng.
-- SANDBOX DDL CANDIDATE — NOT TO RUN UNTIL USER APPROVES
-- 0. Schema
-- pgcrypto PHẢI có sẵn (PF-1 pre-flight verify).
-- Nếu thiếu → PF-1 remediation riêng (cần superuser approve).
CREATE SCHEMA IF NOT EXISTS sandbox_tac;
-- 1. Vocabulary tables
CREATE TABLE sandbox_tac.section_type_vocab (
code TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
lifecycle_status TEXT NOT NULL DEFAULT 'active'
CHECK (lifecycle_status IN ('active', 'deprecated', 'retired')),
soft_limit_words INTEGER DEFAULT 500,
hard_limit_words INTEGER DEFAULT 1500,
description_required BOOLEAN DEFAULT TRUE,
body_required BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE sandbox_tac.publication_type_vocab (
code TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
lifecycle_status TEXT NOT NULL DEFAULT 'active'
CHECK (lifecycle_status IN ('active', 'deprecated', 'retired')),
default_risk_tier TEXT NOT NULL DEFAULT 'medium'
CHECK (default_risk_tier IN ('low', 'medium', 'high', 'highest')),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- 2. Core tables
CREATE TABLE sandbox_tac.logical_unit (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
canonical_address TEXT NOT NULL UNIQUE,
doc_code TEXT NOT NULL,
parent_id UUID REFERENCES sandbox_tac.logical_unit(id),
sort_order INTEGER NOT NULL DEFAULT 0,
section_type TEXT NOT NULL
REFERENCES sandbox_tac.section_type_vocab(code),
section_code TEXT,
owner TEXT NOT NULL,
identity_profile JSONB DEFAULT '{}',
lifecycle_status TEXT NOT NULL DEFAULT 'draft_only'
CHECK (lifecycle_status IN ('active', 'draft_only', 'retired')),
tier TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_sbx_lu_doc_code ON sandbox_tac.logical_unit(doc_code);
CREATE INDEX idx_sbx_lu_parent ON sandbox_tac.logical_unit(parent_id);
CREATE TABLE sandbox_tac.unit_version (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
logical_unit_id UUID NOT NULL
REFERENCES sandbox_tac.logical_unit(id),
version_number INTEGER NOT NULL DEFAULT 1,
title TEXT NOT NULL,
body TEXT,
description TEXT,
content_hash TEXT,
lifecycle_status TEXT NOT NULL DEFAULT 'draft'
CHECK (lifecycle_status IN (
'draft', 'enacted', 'superseded', 'retired')),
review_state TEXT DEFAULT 'unreviewed'
CHECK (review_state IN (
'unreviewed', 'in_review', 'review_passed',
'review_failed', 'needs_re_review')),
length_flag TEXT DEFAULT 'normal'
CHECK (length_flag IN ('normal', 'soft_limit', 'hard_limit')),
length_exception_reason TEXT,
content_profile JSONB DEFAULT '{}',
editor TEXT,
provenance TEXT DEFAULT 'PROV-AI',
vector_sync_status TEXT DEFAULT 'pending'
CHECK (vector_sync_status IN (
'pending', 'synced', 'stale', 'error')),
-- Sandbox: tất cả giữ 'pending' vì không có Qdrant.
-- Enum giữ nguyên P5 v0.2, không thêm 'skip'.
vector_synced_at TIMESTAMPTZ,
vector_chunk_count INTEGER DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
enacted_at TIMESTAMPTZ,
UNIQUE (logical_unit_id, version_number)
);
CREATE INDEX idx_sbx_uv_lu ON sandbox_tac.unit_version(logical_unit_id);
CREATE INDEX idx_sbx_uv_lifecycle ON sandbox_tac.unit_version(lifecycle_status);
CREATE TABLE sandbox_tac.publication (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
doc_code TEXT NOT NULL,
version TEXT NOT NULL,
publication_type TEXT NOT NULL
REFERENCES sandbox_tac.publication_type_vocab(code),
name TEXT NOT NULL,
owner TEXT NOT NULL,
description TEXT,
lifecycle_status TEXT NOT NULL DEFAULT 'proposed'
CHECK (lifecycle_status IN (
'proposed', 'enacted', 'superseded', 'retired')),
enacted_at TIMESTAMPTZ,
council_score NUMERIC,
approved_by TEXT,
risk_tier TEXT NOT NULL DEFAULT 'medium'
CHECK (risk_tier IN ('low', 'medium', 'high', 'highest')),
publication_profile JSONB DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (doc_code, version)
);
CREATE TABLE sandbox_tac.publication_member (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
publication_id UUID NOT NULL
REFERENCES sandbox_tac.publication(id),
logical_unit_id UUID NOT NULL
REFERENCES sandbox_tac.logical_unit(id),
unit_version_id UUID NOT NULL
REFERENCES sandbox_tac.unit_version(id),
render_order INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (publication_id, logical_unit_id)
);
-- 3. Hook tables (empty)
CREATE TABLE sandbox_tac.change_set (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
publication_id UUID REFERENCES sandbox_tac.publication(id),
scope_description TEXT NOT NULL,
lifecycle_status TEXT NOT NULL DEFAULT 'draft'
CHECK (lifecycle_status IN (
'draft', 'submitted', 'review_passed',
'approval_passed', 'enacted', 'rejected', 'withdrawn')),
apr_ref TEXT,
owner TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
submitted_at TIMESTAMPTZ,
enacted_at TIMESTAMPTZ
);
CREATE TABLE sandbox_tac.change_set_member (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
change_set_id UUID NOT NULL
REFERENCES sandbox_tac.change_set(id),
logical_unit_id UUID NOT NULL
REFERENCES sandbox_tac.logical_unit(id),
change_type TEXT NOT NULL
CHECK (change_type IN (
'create', 'new_version', 'retire', 'structural')),
old_version_id UUID REFERENCES sandbox_tac.unit_version(id),
new_version_id UUID REFERENCES sandbox_tac.unit_version(id),
snapshot_data JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (change_set_id, logical_unit_id)
);
6.3 Function — content hash
-- SANDBOX DDL CANDIDATE
CREATE OR REPLACE FUNCTION sandbox_tac.fn_sbx_compute_content_hash(
p_title TEXT, p_body TEXT, p_description TEXT, p_profile JSONB
) RETURNS TEXT AS $$
BEGIN
RETURN encode(sha256(convert_to(
COALESCE(p_title,'') || '||' || COALESCE(p_body,'') || '||' ||
COALESCE(p_description,'') || '||' || COALESCE(p_profile::text,'{}'),
'UTF8')), 'hex');
END;
$$ LANGUAGE plpgsql IMMUTABLE;
6.4 Trigger — auto hash + length flag
-- SANDBOX DDL CANDIDATE
CREATE OR REPLACE FUNCTION sandbox_tac.fn_sbx_uv_before_insert()
RETURNS TRIGGER AS $$
DECLARE v_wc INTEGER; v_soft INTEGER; v_hard INTEGER;
BEGIN
NEW.content_hash := sandbox_tac.fn_sbx_compute_content_hash(
NEW.title, NEW.body, NEW.description, NEW.content_profile);
IF NEW.body IS NOT NULL THEN
v_wc := array_length(regexp_split_to_array(trim(NEW.body),'\s+'),1);
ELSE v_wc := 0; END IF;
SELECT soft_limit_words, hard_limit_words INTO v_soft, v_hard
FROM sandbox_tac.section_type_vocab stv
JOIN sandbox_tac.logical_unit lu ON lu.section_type = stv.code
WHERE lu.id = NEW.logical_unit_id;
IF v_wc > COALESCE(v_hard,1500) THEN NEW.length_flag := 'hard_limit';
ELSIF v_wc > COALESCE(v_soft,500) THEN NEW.length_flag := 'soft_limit';
ELSE NEW.length_flag := 'normal'; END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_sbx_uv_before_insert
BEFORE INSERT ON sandbox_tac.unit_version
FOR EACH ROW EXECUTE FUNCTION sandbox_tac.fn_sbx_uv_before_insert();
6.5 Trigger — parent same doc_code
-- SANDBOX DDL CANDIDATE
CREATE OR REPLACE FUNCTION sandbox_tac.fn_sbx_lu_parent_same_doc()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.parent_id IS NOT NULL THEN
IF NOT EXISTS (SELECT 1 FROM sandbox_tac.logical_unit
WHERE id = NEW.parent_id AND doc_code = NEW.doc_code) THEN
RAISE EXCEPTION 'Parent doc_code mismatch: unit % doc_code %',
NEW.canonical_address, NEW.doc_code;
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_sbx_lu_parent_doc
BEFORE INSERT OR UPDATE ON sandbox_tac.logical_unit
FOR EACH ROW EXECUTE FUNCTION sandbox_tac.fn_sbx_lu_parent_same_doc();
6.6 Đ24 cross-read — DEFERRED round 1
Không cross-read public.entity_labels. Cần explicit read-only approval. Round 1: verify label format trong P7A staging JSON (đã PASS).
7. Data Seed Plan
7.1 Seed order
- section_type_vocab (11) → 2. publication_type_vocab (1) → 3. logical_unit ROOTs (3) → 4. depth-1 (~38) → 5. depth-2 (~35) → 6. unit_version (76) → 7. publication (3) → 8. publication_member (76).
7.2 Address→UUID mapping for parent_id
Agent seed script: INSERT ROOTs (parent=NULL), capture UUID via RETURNING id. Use CTE/temp lookup canonical_address → UUID để resolve parent_id cho children.
7.3 Vocabulary seed
-- SANDBOX DDL CANDIDATE — NOT TO RUN UNTIL USER APPROVES
INSERT INTO sandbox_tac.section_type_vocab
(code, name, description, soft_limit_words, hard_limit_words,
description_required, body_required) VALUES
('heading', 'Heading', 'Heading có authority',
500, 1500, FALSE, FALSE),
-- body_required=FALSE, description_required=FALSE
-- Structural nodes dùng heading nhưng body=NULL, description=NULL
('paragraph', 'Paragraph', 'Đoạn văn thường', 500, 1500, TRUE, TRUE),
('definition', 'Definition', 'Định nghĩa thuật ngữ', 500, 1500, TRUE, TRUE),
('principle', 'Principle', 'Nguyên tắc/doctrine', 500, 1500, TRUE, TRUE),
('process', 'Process', 'Quy trình multi-step', 500, 1500, TRUE, TRUE),
('technical_spec', 'Technical Spec', 'Đặc tả kỹ thuật', 800, 2000, TRUE, TRUE),
('governance_process', 'Governance Process', 'Quy trình governance', 500, 1500, TRUE, TRUE),
('reference_mapping', 'Reference Mapping', 'Bảng crosswalk/mapping', 500, 1500, TRUE, TRUE),
('invariant_list', 'Invariant List', 'Danh sách invariants', 500, 1500, TRUE, TRUE),
('open_decision_list', 'Open Decision List', 'Danh sách ODs', 500, 1500, TRUE, TRUE),
('changelog', 'Changelog', 'Nhật ký thay đổi', 1000, 3000, FALSE, TRUE);
INSERT INTO sandbox_tac.publication_type_vocab
(code, name, description, default_risk_tier) VALUES
('design_note', 'Design Note', 'Tài liệu thiết kế tiền-schema', 'medium');
7.4 Body, structural nodes, vector fields
Body = actual source content từ 3 KB docs. 5 structural nodes: body=NULL. vector_sync_status = 'pending' (no Qdrant). Trigger auto-computes content_hash + length_flag.
8. Hash Plan
Content hash: SHA-256(title||body||description||profile) via fn_sbx_compute_content_hash(). Source file hash: sha256sum trên VPS. Post-seed verify: recompute → 0 mismatches.
9. Checker Plan
Birth gate BG-LU/UV 01→06, pre-enactment PE-PUB 01→06 (trivially PASS), daily DOT-LU/UV/PUB 01→03 — as SQL queries on sandbox_tac.
Round-trip with DFS sort_path:
WITH RECURSIVE tree AS (
SELECT lu.id, lu.canonical_address, lu.parent_id, lu.sort_order,
lu.section_type, 0 AS depth, ARRAY[lu.sort_order] AS sort_path
FROM sandbox_tac.logical_unit lu
WHERE lu.doc_code = 'D38-HOWTO' AND lu.parent_id IS NULL
UNION ALL
SELECT lu.id, lu.canonical_address, lu.parent_id, lu.sort_order,
lu.section_type, t.depth + 1, t.sort_path || lu.sort_order
FROM sandbox_tac.logical_unit lu JOIN tree t ON lu.parent_id = t.id
)
SELECT t.depth, t.canonical_address, t.section_type,
uv.title, uv.body, uv.length_flag, t.sort_path
FROM tree t
JOIN sandbox_tac.unit_version uv ON uv.logical_unit_id = t.id
AND uv.version_number = 1
ORDER BY t.sort_path;
10. No-Production-Change Guard
10.1 Isolation
Separate schema. Verify GĐ-1 (PF-4). No cross-schema FK. No cross-schema read. No production mutation.
10.2 Execution pipeline with action log
PHASE 0: PRE-FLIGHT → PF-1→PF-6 all PASS → If FAIL → ABORT
PHASE 1: CREATE → BEGIN → schema + tables + functions + triggers → COMMIT
→ PF-4 Directus check → If sees sandbox → DROP CASCADE + ABORT
→ If ERROR → ROLLBACK + DROP + report
PHASE 2: SEED → vocab → LU ROOTs → depth-1 → depth-2 → UV → pub → pm
→ If ERROR → DROP CASCADE + report
PHASE 3: VERIFY → checkers → hash recompute → round-trip → PF-5 re-run
PHASE 4: REPORT → action log → PASS/FAIL → report to User/GPT
Rollback on error: DROP SCHEMA sandbox_tac CASCADE. Action log: reports/p7b-sandbox-execution-log-YYYY-MM-DD.md.
10.3 Đ33 compliance
directus DB ✅, Lớp KHO ✅, naming ✅, E1 DDL ✅, password GSM ✅.
10.4 DOT registration — Open decision
OD-P7B-02: Option A (register temporary DOT) nếu Đ33 §13.3 literal. Option B (action log đủ) nếu sandbox exception. User quyết.
11. Risks
| # | Risk | Mitigation |
|---|---|---|
| 1 | Directus scan sandbox | PF-4 verify + DROP+ABORT |
| 2 | pgcrypto missing | PF-1 + remediation |
| 3 | CREATE SCHEMA privilege | PF-2 + request grant |
| 4 | Seed FK violation | Strict order §7.1 |
| 5 | Hash mismatch | Trigger + post-verify |
| 6 | Scope creep | 8 tables only |
12. PASS/FAIL Criteria
12.1 P7B design PASS (Cổng 1)
8 DDL candidates consistent, pre-flight covers deps, seed plan clear, checker covers P6, DFS round-trip, pipeline + rollback, no production mutation, Đ33/P5/P6 compliance.
12.2 Sandbox execution PASS (Cổng 2)
PF-1→PF-6 PASS, 8 tables created, 76+76+3+76 seeded, 0 constraint violations, parent trigger works, 76/76 hashes, 76/76 length flags, 0 ERROR checkers, 3/3 round-trip, 10/10 code blocks, production unchanged.
13. Open Decisions
| Code | Câu hỏi | Phase |
|---|---|---|
| OD-P7B-01 | pgcrypto cần superuser? | Execution |
| OD-P7B-02 | DOT registration? A=register, B=action log | Execution |
| OD-P7B-03 | technical_spec 800/2000? | Execution |
| OD-P7B-04 | Sandbox lifespan (DROP khi nào)? | Post-P7B |
| OD-P7B-05 | Đ24 cross-read approval? | Post-execution |
| OD-P7B-06 | CHECK→FK cho production? | P8 |
14. Constitutional Check
NT1/NT13 PASS (sandbox≠SoT). NT2 PASS (constraints+triggers). NT4 PASS CĐK (CHECK sandbox OK, production FK). NT11 PASS. Đ24 PASS (defer). Đ32 PASS. Đ33 PASS (E1, GSM). Đ35 PASS. P5 PASS (8/8). P6 PASS (BG+DOT coverage).
15. Recommendation
Cổng 1 — Duyệt tài liệu: P7B v0.3 PASS → upload KB. ✅ DONE.
Cổng 2 — Duyệt execution: User approve riêng → agent chạy DDL+seed+verify trên VPS. Xem Appendix A.
Roadmap: P7B design PASS → User approve execution (Cổng 2) → sandbox execution PASS → P8 implementation design → P9 production migration (approval riêng).
Appendix A — Draft Execution Prompt
⚠️ NOT TO RUN UNTIL USER APPROVES CỔNG 2 ⚠️
Mục tiêu: Tạo sandbox_tac, seed 76 units, chạy checkers, round-trip. KHÔNG đụng production.
Pre-flight PF-1→PF-6 PHẢI PASS. PF fail → ABORT. PF-4 fail → DROP+ABORT.
DDL/DML chỉ sandbox_tac. Không cross-read public.entity_labels. Không Directus collection.
Action log: reports/p7b-sandbox-execution-log-YYYY-MM-DD.md. Bất kỳ lỗi → DROP SCHEMA sandbox_tac CASCADE.
PASS/FAIL theo §12.2.
Patch log
v0.1 → v0.2 (14 patches)
| # | Điểm |
|---|---|
| 1 | Directus scan = giả định GĐ-1 |
| 2 | User/role verify + approval |
| 3 | Pre-flight PF-1→PF-6 |
| 4 | DDL candidate naming |
| 5 | CHECK vs FK (sandbox vs production) |
| 6 | Đ24 cross-read defer |
| 7 | Seed count 11 |
| 8 | vector_sync_status giữ P5 enum |
| 9 | Address→UUID mapping |
| 10 | Round-trip sort_path DFS |
| 11 | Execution pipeline + action log |
| 12 | P7B PASS = request approval |
| 13 | DOT registration = OD |
| 14 | Sandbox execution wording |
v0.2 → v0.3 (4 patches)
| # | Điểm |
|---|---|
| 1 | heading.body_required=FALSE — structural nodes body=NULL pass BG-UV-03 |
| 2 | heading.description_required=FALSE — structural nodes pass BG-UV-02 |
| 3 | CREATE EXTENSION pgcrypto tách khỏi DDL, PF-1 remediation riêng |
| 4 | PF-4: Directus sees sandbox → DROP CASCADE + ABORT |
P7B v0.3 | OFFICIAL | S182 | 2026-04-26 | Opus 4.6 GPT: v0.1 PASS CĐK (14) → v0.2 → v0.3 PASS CĐK nhẹ (4) → FINAL PASS User: PASS