Điều 35 v5.1 DRAFT rev 2 — Sau Council Round 1
⚠️ FILE NÀY ĐÃ SUPERSEDED — xem dieu35-dot-governance-law.md (v5.1 FINAL BAN HÀNH)
Ban hành: 2026-04-14 S177 Fix 8. File này là bằng chứng quá trình Council 2 vòng, giữ lại theo AP-21 để audit. KHÔNG tham chiếu file này trong code/luật/prompt mới. Dùng
dieu35-dot-governance-law.md(file chính). Backup v5.0 FINAL:dieu35-dot-governance-law-v5-0-backup.md.
ĐIỀU 35: LUẬT QUẢN TRỊ DOT — v5.1 FINAL (BAN HÀNH 2026-04-14 S177 Fix 8)
v5.1 FINAL | 2026-04-14 S177 Fix 8 — BAN HÀNH sau Council 2 vòng (Gemini 9.8/10 APPROVE FINAL + GPT 9.1/10 APPROVE WITH MINOR — đã apply 7 fix inline). Base: v5.0 FINAL (S151 2026-03-31). Tham chiếu HP: v4.5.1 (11 NT). 5 khoản amend so với v5.0: §4.1 UNIQUE partial + NOT NULL +
_dot_origin, §8.1 H10-H14 + escalation quafn_log_issue, §8.3fn_birth_gateconfigurable mode, §11 retrofit clause MỚI, §5.4 dual-trigger (pre-POST inference + cron repair). Council R1 — 5 blocker: ĐÓNG. Council R2 — 7 minor fix: ĐÓNG inline. 0 blocker còn lại. TUÂN THỦ: HP v4.5.1, Đ0-G, Đ0-H, Đ22, Đ26, Đ31, Đ32, Đ36, Đ41.
★ 7 fix Council R2 đã apply inline (không cần Round 3):
dot_config.valuecho phép NULL + sentinellaw_v5_1_enacted_at+ UTC ISO format (§4.4)- H14 mới — bắt
law_v5_1_enacted_atchưa set > 24h (§8.1) fn_log_issueBỎ severity khỏi signature → severity leo thang trên cùng row (§4.1B)- PRECHECK dedupe
system_issuesopen trước CREATE UNIQUE INDEX (§4.1B) - §5.1 viết cứng: infer fail = CẤM POST partial row (§5.1)
- H13 chỉ báo động, KHÔNG self-repair
dot-metadata-repair(§5.2) - BLOCK 4 thêm bước 4.0 snapshot baseline + 4.3 lưu old_file_path + 4.10 atomic transaction + 4.13 cleanup snapshot (§9.2)
§1. MỤC TIÊU
"DOT là cổng duy nhất thao tác dữ liệu (Điều 0-H). Nhưng nếu không quản trị CHÍNH DOT — ai biết DOT nào đang làm gì, thiếu gì, hỏng gì?"
3 mục tiêu (giữ từ v5.0):
| # | Mục tiêu | Đo bằng | Tính chất |
|---|---|---|---|
| MT1 | Nhìn toàn cảnh: Biết mọi DOT đang làm gì, thuộc domain nào, phục vụ luật nào, thiếu/thừa/hỏng ở đâu | dot_tools có đầy đủ metadata cho 100% DOT |
Vĩnh viễn |
| MT2 | Đo được bằng số: Coverage, pairing rate, automation rate — đếm bằng 1 query PG | dot_coverage_required + pivot = % coverage |
Tự thích nghi |
| MT3 | Tự vận hành khép kín: Phát hiện → tạo APR → sửa → verify | 3+ DOT tự quản trị + dual-trigger | Tự động 100% |
Thứ tự ưu tiên: Nhìn thấy → Đo được → Tự vận hành.
★ MT4 MỚI (v5.1): Luật áp cả entity CŨ, không chỉ entity MỚI
Luật ban hành không để lại legacy mãi vênh. Mọi luật có impact trên bảng sẵn data CŨ → bắt buộc kèm retrofit mechanism (§11). Áp cho cả luật mới VÀ luật đã ban hành (xem §11.4).
§2. PHẠM VI
TRONG phạm vi Điều 35:
- Quản trị sổ đăng ký DOT (
dot_tools— SSOT duy nhất) - Phân loại DOT theo domain, tier, operation, trigger_type — TẤT CẢ qua PG SSOT tables, KHÔNG text tự do
- Quản lý domain DOT (
dot_domains— FK enforce, 4 lớp bảo vệ) - Đo coverage: domain × operation × tier — mẫu số TỰ ĐỘNG cập nhật
- Quản lý vòng đời DOT: tạo → active → deprecated → retired
- Kiểm tra sức khoẻ DOT: pairing, domain, target, file_path, cron, metadata completeness (v5.1)
- DOT 2 cấp: Cấp A (kiểm tra) + Cấp B (thực thi) — paired bắt buộc
- ★ Retrofit luật mới cho entity cũ (v5.1 — §11)
★ v5.1 LƯU Ý VẬN HÀNH:
fn_birth_gatemặc định mode='warn' ngay sau ban hành. Chuyển 'block' quaUPDATE dot_config SET value='block' WHERE key='birth_gate_mode'khi đạt tiêu chí cứng §10 (xem checklist warn→block).UNIQUE(file_path) WHERE file_path IS NOT NULL: partial index chỉ cho phép nhiều row có file_path NULL cùng tồn tại; KHÔNG cho phép duplicate non-null. Vì vậy 45 cặp duplicate hiện hữu PHẢI dedupe TRƯỚC khi tạo index — xem BLOCK 4 §9.2.
NGOÀI phạm vi (luật khác):
| Vấn đề | Luật phụ trách | Đ35 chỉ làm |
|---|---|---|
| Nội dung cụ thể DOT xử lý | Luật tương ứng (Đ36, Đ37...) | Biết DOT phục vụ luật nào |
| Collection mà DOT tác động | Đ36 | Khai báo target_collections |
| Khai sinh entity DOT | Đ0-G | Trigger khai sinh khi tạo DOT |
| Phê duyệt thay đổi DOT | Đ32 | Tạo APR, tuân thủ quy trình |
| Toàn vẹn hệ thống | Đ31 | Báo cáo sức khoẻ DOT |
| Escalation cảnh báo → con người | Đ22 Self-healing | Đẩy vào system_issues, Đ22 đọc tiếp |
§3. DOT 2 CẤP
| Thuộc tính | Cấp A — Kiểm tra | Cấp B — Thực thi |
|---|---|---|
| Vai trò | Giám sát, quét, báo cáo | Ghi, sửa, tạo dữ liệu |
| Quyền | Read-only | Read + Write (có secret GSM) |
| Ví dụ | dot-dot-health, dot-collection-health | dot-schema-ensure, dot-apr-execute |
| Auto-approve | ✅ Có | ❌ Pending duyệt |
| Bắt buộc paired | Không | PHẢI có DOT Cấp A tương ứng (paired_dot FK) |
Quy tắc paired_dot (luật nội tại Đ35 — không viện NT số):
- Mỗi DOT Cấp B ghi dữ liệu → PHẢI có DOT Cấp A kiểm tra cùng scope.
paired_dotFK →dot_tools.code. FK enforce.- Cấp A IDLE = Cấp B làm đúng. Cấp A phát hiện lỗi → APR → Cấp B sửa → A re-verify.
★ PG TRIGGER enforce paired_dot:
CREATE OR REPLACE FUNCTION fn_dot_enforce_paired() RETURNS TRIGGER AS $$
BEGIN
IF NEW.tier = 'B' AND NEW.paired_dot IS NULL THEN
RAISE EXCEPTION 'DOT Cấp B (%) PHẢI có paired_dot. CẤM tạo B không paired.', NEW.code;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_dot_enforce_paired
BEFORE INSERT OR UPDATE ON dot_tools
FOR EACH ROW EXECUTE FUNCTION fn_dot_enforce_paired();
§4. SCHEMA
4.1 dot_tools — 11 fields (v5.1: thêm _dot_origin chính thức)
| Field | Type | Constraint v5.0 | Constraint v5.1 | Mục đích |
|---|---|---|---|---|
| tier | TEXT | FK → dot_tiers.code | Giữ | Cấp DOT (A/B) |
| domain | TEXT | FK → dot_domains.code, NOT NULL, DEFAULT 'unclassified' | Giữ | Domain hoạt động |
| operation | TEXT | FK → dot_operations.code, nullable | + NOT NULL sau grace + Block 4 verify | Loại thao tác |
| paired_dot | TEXT | FK → dot_tools.code, nullable | Giữ (trigger enforce B) | DOT Cấp A tương ứng |
| trigger_type | TEXT | FK → dot_trigger_types.code | + NOT NULL sau grace | Cơ chế kích hoạt |
| cron_schedule | TEXT | nullable | + NOT NULL sau grace (empty string '' cho manual/on-demand — KHÔNG dùng NULL) |
Lịch cron |
| file_path | TEXT | nullable | + NOT NULL sau grace + UNIQUE partial WHERE NOT NULL | Đường dẫn file |
| last_executed | TIMESTAMPTZ | nullable | Giữ (DOT runtime tự update) | Lần chạy cuối |
| coverage_status | TEXT | FK → dot_coverage_statuses.code | + NOT NULL sau grace | Trạng thái bao phủ |
_dot_origin |
TEXT | (không có v5.0) | ★ v5.1 MỚI: NOT NULL DEFAULT 'unknown', identify công cụ tạo DOT | Truy vết RC-3 (3 lớp registry trùng — biết DOT do dot-dot-register hay [AUTO-ID] flow hay tay tạo) |
| extra_metadata | JSONB | DEFAULT '{}' | Giữ | Metadata mở rộng |
★ v5.1 KHOẢN 1 — UNIQUE PARTIAL + NOT NULL + REFERENCE TABLES
WORDING ĐÚNG về UNIQUE partial (rev 2 sửa từ rev 1):
UNIQUE INDEX ... WHERE file_path IS NOT NULLchặn 2 row cùng có cùngfile_pathnon-null. Cho phép nhiều rowfile_path IS NULLcùng tồn tại (vì NULL không tham gia comparison).- 45 cặp duplicate non-null hiện hữu trên VPS sẽ làm
CREATE UNIQUE INDEXFAIL ngay. - → BLOCK 4 §9.2 PHẢI dedupe TRƯỚC khi CREATE INDEX (xem checklist cứng).
-- v5.1 BLOCK 4 — CHỈ chạy SAU dedupe verify 0 row duplicate non-null:
CREATE UNIQUE INDEX uq_dot_tools_file_path
ON dot_tools(file_path) WHERE file_path IS NOT NULL;
-- v5.1 BLOCK 4 — CHỈ chạy SAU DOT_METADATA_AUDIT báo 0 drift:
ALTER TABLE dot_tools
ALTER COLUMN operation SET NOT NULL,
ALTER COLUMN coverage_status SET NOT NULL,
ALTER COLUMN trigger_type SET NOT NULL,
ALTER COLUMN file_path SET NOT NULL,
ALTER COLUMN cron_schedule SET NOT NULL,
ALTER COLUMN _dot_origin SET NOT NULL;
Reference tables (v5.1 mở rộng dot_operations từ 9 → 18 op):
CREATE TABLE dot_tiers (code TEXT PRIMARY KEY, name TEXT NOT NULL, description TEXT);
INSERT INTO dot_tiers VALUES ('A', 'Kiểm tra', 'Read-only'), ('B', 'Thực thi', 'Read+Write');
CREATE TABLE dot_operations (code TEXT PRIMARY KEY, name TEXT NOT NULL, description TEXT);
INSERT INTO dot_operations VALUES
-- 9 op từ v5.0:
('health','Kiểm tra sức khoẻ',''), ('create','Tạo mới',''), ('sync','Đồng bộ',''),
('ensure','Đảm bảo tồn tại',''), ('register','Đăng ký',''), ('report','Báo cáo',''),
('execute','Thực thi',''), ('refresh','Làm mới',''), ('classify','Phân loại',''),
-- 9 op MỚI v5.1 (CLI S177 C6 phát hiện 8/12 op runtime ngoài luật seed v5.0):
('verify','Kiểm tra hợp lệ',''), ('delete','Xoá',''), ('update','Cập nhật',''),
('restore','Khôi phục',''), ('import','Nhập liệu',''), ('snapshot','Chụp ảnh',''),
('seed','Gieo dữ liệu',''), ('audit','Kiểm toán',''), ('backfill','Điền ngược','');
CREATE TABLE dot_trigger_types (code TEXT PRIMARY KEY, name TEXT NOT NULL);
INSERT INTO dot_trigger_types VALUES
('cron','Lịch định kỳ'), ('event','Sự kiện Directus'), ('on-deploy','Khi deploy'),
('on-demand','Khi yêu cầu'), ('manual','Thủ công'), ('one-off','1 lần duy nhất');
CREATE TABLE dot_coverage_statuses (code TEXT PRIMARY KEY, name TEXT NOT NULL);
INSERT INTO dot_coverage_statuses VALUES
('complete','Đầy đủ'), ('partial','Một phần'), ('orphan','Mồ côi'),
('phantom','Ma'), ('deprecated','Lỗi thời');
★ v5.1 KHOẢN 1B — ĐỊNH NGHĨA 2 HÀM PHỤ TRỢ (đáp blocker B1+B2 Council R1)
-- B1: fn_is_in_grace_period()
-- Đọc `dot_config.grace_period_days` so với `dot_config.law_v5_1_enacted_at`.
CREATE OR REPLACE FUNCTION fn_is_in_grace_period()
RETURNS BOOLEAN
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public, pg_catalog
AS $$
DECLARE
v_days INT;
v_enacted TIMESTAMPTZ;
BEGIN
SELECT value::INT INTO v_days FROM dot_config WHERE key = 'grace_period_days';
SELECT value::TIMESTAMPTZ INTO v_enacted FROM dot_config WHERE key = 'law_v5_1_enacted_at';
IF v_days IS NULL OR v_enacted IS NULL THEN
RETURN TRUE; -- thận trọng: không xác định = giả định grace (fail-open)
END IF;
RETURN NOW() < v_enacted + (v_days || ' days')::INTERVAL;
END;
$$;
ALTER FUNCTION fn_is_in_grace_period() OWNER TO workflow_admin;
-- B2: fn_log_issue() — wrapper INSERT system_issues thay HTTP curl
-- v5.1 FINAL fix Council R2 GPT #2: BỎ severity khỏi signature để severity leo thang trên CÙNG row,
-- không tạo 2 row open song song khi warning → critical.
CREATE OR REPLACE FUNCTION fn_log_issue(
p_source TEXT,
p_severity TEXT, -- 'critical' | 'warning' | 'info'
p_category TEXT,
p_summary TEXT,
p_entity_code TEXT DEFAULT NULL
) RETURNS BIGINT
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public, pg_catalog
AS $$
DECLARE
v_id BIGINT;
v_signature TEXT;
v_severity_rank INT;
BEGIN
-- v5.1 FINAL: signature KHÔNG còn severity → 1 issue/(source,category,entity), severity leo thang trên cùng row
v_signature := md5(coalesce(p_source,'') || '|' || coalesce(p_category,'') || '|' || coalesce(p_entity_code,''));
-- Severity rank để chỉ leo lên (critical > warning > info), không tụt xuống
v_severity_rank := CASE p_severity WHEN 'critical' THEN 3 WHEN 'warning' THEN 2 WHEN 'info' THEN 1 ELSE 0 END;
INSERT INTO system_issues (source, severity, category, summary, entity_code, issue_signature, status, created_at, updated_at)
VALUES (p_source, p_severity, p_category, p_summary, p_entity_code, v_signature, 'open', NOW(), NOW())
ON CONFLICT (issue_signature) WHERE status = 'open'
DO UPDATE SET
-- Chỉ leo severity lên, không tụt
severity = CASE
WHEN (CASE EXCLUDED.severity WHEN 'critical' THEN 3 WHEN 'warning' THEN 2 ELSE 1 END)
> (CASE system_issues.severity WHEN 'critical' THEN 3 WHEN 'warning' THEN 2 ELSE 1 END)
THEN EXCLUDED.severity
ELSE system_issues.severity
END,
summary = EXCLUDED.summary,
updated_at = NOW()
RETURNING id INTO v_id;
RETURN v_id;
END;
$$;
ALTER FUNCTION fn_log_issue(TEXT,TEXT,TEXT,TEXT,TEXT) OWNER TO workflow_admin;
GRANT EXECUTE ON FUNCTION fn_log_issue(TEXT,TEXT,TEXT,TEXT,TEXT) TO incomex;
-- Yêu cầu phụ: system_issues phải có cột `issue_signature` UNIQUE WHERE status='open'.
-- BLOCK 1 thêm:
-- ALTER TABLE system_issues ADD COLUMN IF NOT EXISTS issue_signature TEXT;
--
-- ★ v5.1 FINAL fix Council R2 GPT #3: PRECHECK DEDUPE TRƯỚC khi tạo unique partial index.
-- Nếu production đang có nhiều open issue cùng (source,category,entity) → CREATE INDEX sẽ FAIL.
-- BLOCK 1 BẮT BUỘC chạy preflight:
--
-- WITH dups AS (
-- SELECT md5(coalesce(source,'')||'|'||coalesce(category,'')||'|'||coalesce(entity_code,'')) AS sig,
-- MIN(id) AS keep_id, COUNT(*) AS cnt
-- FROM system_issues WHERE status='open'
-- GROUP BY 1 HAVING COUNT(*) > 1
-- )
-- UPDATE system_issues SET status='resolved', updated_at=NOW(),
-- summary = summary || ' [auto-merged duplicate sig pre-v5.1]'
-- WHERE id IN (
-- SELECT s.id FROM system_issues s
-- JOIN dups d ON md5(coalesce(s.source,'')||'|'||coalesce(s.category,'')||'|'||coalesce(s.entity_code,''))=d.sig
-- WHERE s.status='open' AND s.id <> d.keep_id
-- );
--
-- Sau đó mới tạo index:
-- CREATE UNIQUE INDEX IF NOT EXISTS uq_system_issues_open_sig ON system_issues(issue_signature) WHERE status='open';
Lưu ý SECURITY DEFINER (Council non-blocker GPT): Mọi function SECDEF v5.1 đặt SET search_path = public, pg_catalog để tránh search_path hijack. Owner = workflow_admin.
4.2 dot_domains — SSOT Domain (giữ v5.0)
CREATE TABLE dot_domains (
code TEXT PRIMARY KEY,
name TEXT NOT NULL,
parent_domain TEXT REFERENCES dot_domains(code)
);
4 lớp bảo vệ (giữ): DEFAULT 'unclassified' + NOT NULL + FK + ON DELETE RESTRICT. Seed 24 domains: Phụ lục A.
4.3 dot_coverage_required — Ma trận bao phủ (giữ v5.0)
CREATE TABLE dot_coverage_required (
id SERIAL PRIMARY KEY,
domain TEXT NOT NULL REFERENCES dot_domains(code),
operation TEXT NOT NULL REFERENCES dot_operations(code),
tier TEXT NOT NULL REFERENCES dot_tiers(code),
description TEXT,
UNIQUE(domain, operation, tier)
);
4.4 dot_config — Config (v5.1 + 2 key mới)
-- v5.1 FINAL fix Council R2 GPT #1: dot_config.value cho phép NULL để seed law_v5_1_enacted_at
-- Lý do: nếu ép NOT NULL thì INSERT seed law_v5_1_enacted_at=NULL sẽ fail migration đầu tiên.
-- Triết lý: value NULL = "chưa set" = fail-open (xem fn_is_in_grace_period).
CREATE TABLE dot_config (
key TEXT PRIMARY KEY,
value TEXT, -- v5.1: nullable (cho key chưa set sentinel)
description TEXT,
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Seed: dùng NOW() AT TIME ZONE 'UTC' ISO format để tránh timezone drift
INSERT INTO dot_config VALUES
('dot_base_path', 'bin/dot/', 'Thư mục gốc DOT'),
('stale_threshold_days', '7', 'Ngày stale'),
('grace_period_days', '7', 'Grace mặc định sau ban hành luật mới — có thể UPDATE để gia hạn nếu retrofit chưa xong'),
('health_cron', '0 3 * * *', ''),
('coverage_cron', '0 2 * * 0', ''),
('birth_gate_mode', 'warn', 'v5.1: warn mặc định. UPDATE = block khi đạt §10 checklist'),
('law_v5_1_enacted_at', NULL, 'v5.1: SET tại thời điểm ban hành luật (UTC ISO). H14 báo nếu NULL > 24h sau migration');
-- BƯỚC BẮT BUỘC khi ban hành v5.1 (BLOCK 2 cuối):
-- UPDATE dot_config SET value=to_char(NOW() AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"')
-- WHERE key='law_v5_1_enacted_at';
★ GUARD: H14 health check mới (đáp Council R2 GPT — chống grace mãi mãi):
dot-dot-healthH14 (v5.1 FINAL): nếudot_config.law_v5_1_enacted_at IS NULLVÀBLOCK 1 đã chạy > 24h→fn_log_issue('dot-dot-health','critical','grace_never_set','law_v5_1_enacted_at chưa set, grace đang treo vĩnh viễn — phải UPDATE ngay'). Đưa H14 vào bảng §8.1.
§5. QUY TRÌNH TẠO DOT MỚI — 8 BƯỚC (v5.1 strengthened bước 4)
| Bước | Hành động | Cơ chế | Luật |
|---|---|---|---|
| 1 | Tạo APR request_type='new_dot' | Đ32 | Đ32 |
| 2 | Cấp A auto-approve. Cấp B pending duyệt | APR system | Đ32 |
| 3 | Tạo file .ts tại dot_config.dot_base_path |
Agent | — |
| 4 | Register vào dot_tools 11/11 fields + dual-trigger (v5.1) | dot-dot-register + cron repair | Đ22 |
| 5 | Khai báo target_collections | dot-dot-register | Đ8 |
| 6 | Khai sinh birth_registry | Trigger tự động | Đ0-G |
| 7 | dot-dot-health kiểm tra DOT mới | Cron/manual | Đ31 |
| 8 | Health PASS → active. FAIL → APR sửa | Tự động | Đ22 |
★ v5.1 KHOẢN 5 (BƯỚC 4 STRENGTHENED — đáp blocker B5 Council R1)
5.1 Payload TỐI THIỂU ở API boundary (11 fields)
{
"code": "DOT-...", // REQUIRED, unique
"name": "...", // REQUIRED
"tier": "A|B", // REQUIRED, FK
"domain": "xxx.yyy", // REQUIRED dotted, FK
"operation": "...", // REQUIRED, FK
"trigger_type": "...", // REQUIRED, FK
"cron_schedule": "...", // REQUIRED ('' cho manual/on-demand)
"coverage_status": "partial", // REQUIRED, default 'partial'
"file_path": "/opt/incomex/...", // REQUIRED absolute path
"_dot_origin": "dot-dot-register" // REQUIRED — identify công cụ tạo
}
WORDING NT11 (đáp non-blocker GPT): Payload 11 fields = API boundary contract, KHÔNG phải buộc người khai tay 11 cột. Nguồn ưu tiên = suy luận tự động từ file_path content + naming convention + dot_config. dot-dot-register PHẢI infer trước khi POST. Chỉ khi infer fail → APR (xem rule auto-backfill §11.2.1).
★ v5.1 FINAL fix Council R2 GPT #4 — INFER FAIL = CẤM POST PARTIAL ROW:
Khi
dot-dot-registerinfer thất bại bất kỳ field bắt buộc nào (operation/coverage_status/trigger_type/file_path/_dot_origin), TUYỆT ĐỐI CẤM POST row partial/placeholder vàodot_tools. Bắt buộc:
- Log issue qua
fn_log_issue('dot-dot-register','warning','infer_fail',...)- Tạo APR
request_type='backfill_metadata'với evidence (file_path, fields fail infer)- SKIP file đó trong run hiện tại
- Chờ APR duyệt → human/agent supply metadata → re-register
Pattern CẤM: "POST trước cho có row, cron sửa sau" — đây chính là RC-4 đẻ ra 49 DOT POST-S151 vẫn 94% NULL. Không lặp lại.
5.2 DUAL-TRIGGER MỚI — sống cả khi birth_gate_mode='block' (đáp blocker B5)
Lý do thiết kế lại: Council R1 (GPT) chỉ ra: flow [AUTO-ID] PATCH-after-insert chỉ sống khi mode='warn'. Khi DB chuyển mode='block', row thiếu metadata bị reject ngay tầng PG → flow không có gì để PATCH. Lưới đỡ đặt dưới đáy hố thay vì trước hố = thất bại NT7 dual-trigger.
v5.1 fix — 2 path độc lập đứng vững mọi mode:
| Path | Vai trò | Khi nào fire | Sống khi mode=block? |
|---|---|---|---|
Trigger chính: pre-POST inference trong dot-dot-register |
Engine chính. Infer 11 fields từ file_path content + naming + dot_config TRƯỚC khi POST. |
On-deploy + on-demand | ✅ Có — vì infer xong rồi mới POST, POST đã đủ field |
Trigger phụ: cron dot-metadata-repair (Tier A→B paired) |
Lưới đỡ. Cron mỗi giờ scan dot_tools row có coverage_status='partial' HOẶC bất kỳ field bắt buộc IS NULL → infer + auto-PATCH (idempotent). Nếu infer fail → tạo APR backfill_metadata. |
Cron 0 * * * * |
✅ Có — vì repair sau khi insert đã thành công, độc lập với gate |
Health check: dot-dot-health H13 (v5.1 mới) — DOT dot-metadata-repair không tồn tại HOẶC last_executed > 2h → INSERT system_issues severity='critical'.
★ v5.1 FINAL fix Council R2 GPT #5 — H13 chỉ là BÁO ĐỘNG, không tự fix:
H13 phát hiện
dot-metadata-repairhỏng → escalate quafn_log_issue+ tạo APRrequest_type='fix_repair_dot'để owner xử lý. KHÔNG có self-repair tự động chodot-metadata-repair(tránh self-paradox: DOT canh gác chính nó). Khi repair DOT chết, hệ thống dựa vàofn_birth_gate(đã active sẵn) làm hàng phòng thủ tạm thời cho DOT mới — đợi human/agent fix repair DOT qua APR.
Flow Directus [AUTO-ID] DOT Tools (lưới đỡ phụ trong giai đoạn warn): vẫn giữ ACTIVE để bắt insert qua Directus UI. Nhưng không phải lưới đỡ chính sau khi block — vai trò chính chuyển sang cron repair.
5.3 Reject row không đầy đủ sau grace + mode=block
Kết hợp §8.3: sau grace period AND birth_gate_mode='block', INSERT thiếu field bị PG reject ngay. Script phải respect reject, không silent-fail.
★ Bước 3 — Ranh giới (giữ v5.0):
File .ts = code mới agent viết → ngoại lệ. Sau khi file tạo → dot-dot-register tự phát hiện + đăng ký. File không register → dot-dot-health H7 tạo APR.
§6. VÒNG ĐỜI DOT (giữ nguyên v5.0)
created → active → deprecated → retired
Quy trình Retire khép kín 6 bước + Luật Bảo toàn (CẤM rename/xoá file).
§7. ĐO LƯỜNG (giữ v5.0 + bổ sung 7.6)
7.1-7.5 (giữ v5.0)
★ 7.6 Metadata Completeness Rate (v5.1 MỚI)
(DOT có 11/11 fields NOT NULL) / (Tổng DOT) × 100- Mục tiêu: 100% sau grace period + Block 4 retrofit
- Đo qua
dot-metadata-auditscandot_toolsWHERE bất kỳ cột bắt buộc IS NULL
§8. DOT TỰ QUẢN TRỊ — 4 CẶP (v5.1 mở rộng)
| DOT | Cấp | Chức năng | Paired với | Trigger |
|---|---|---|---|---|
| dot-dot-health | A | Kiểm tra dot_tools (13 checks v5.1) |
— (self-monitoring) | cron (dot_config.health_cron) |
| dot-dot-coverage | A | Đo coverage + auto-suggest required | — | cron weekly |
| dot-dot-register | B | Đăng ký DOT mới (v5.1: 11/11 fields infer) | dot-dot-health | on-deploy + on-demand |
| dot-metadata-repair (v5.1 MỚI) | B | Cron repair: scan row partial → infer + PATCH; infer fail → APR | dot-dot-health (H13) | cron 0 * * * * |
8.1 dot-dot-health (Cấp A) — v5.1 mở rộng 13 health checks
BẮT BUỘC đọc config từ PG trước khi chạy.
| Check | Phát hiện | Hành động (v5.1: dùng fn_log_issue()) |
|---|---|---|
| H1 | DOT domain = 'unclassified' | fn_log_issue('dot-dot-health','warning','dot_unclassified',...) + APR classify |
| H2 | DOT Cấp B thiếu paired_dot | APR pairing |
| H3 | DOT thiếu target_collections | APR khai báo |
| H4 | DOT file_path không tồn tại trên disk | APR fix |
| H5 | DOT retired nhưng cron còn chạy | APR disable cron |
| H6 | DOT active mà last_executed > threshold | Cảnh báo stale |
| H7 | File trên disk không có trong registry | APR register |
| H8 | Coverage_status không nhất quán | APR update |
| H9 | DOT retired mà register cố re-register | Block |
| H10 | v5.1: operation IS NULL sau grace | fn_log_issue('dot-dot-health', CASE fn_is_in_grace_period() WHEN true THEN 'warning' ELSE 'critical' END, ...) + APR backfill_operation |
| H11 | v5.1: coverage_status IS NULL sau grace | Như H10 |
| H12 | v5.1: file_path drift — 2 row cùng trỏ 1 file (dedupe check) | fn_log_issue(... 'critical' ...) + APR dedupe_registry |
| H13 | v5.1: dot-metadata-repair không tồn tại / last_executed > 2h | fn_log_issue(... 'critical' ...) |
| H14 | v5.1 FINAL fix Council R2 GPT #2: law_v5_1_enacted_at IS NULL AND BLOCK 1 đã chạy > 24h |
fn_log_issue('dot-dot-health','critical','grace_never_set','law_v5_1_enacted_at chưa set — grace đang treo vĩnh viễn, BLOCK 4 không bao giờ enforce được. UPDATE dot_config SET value=NOW()::TEXT WHERE key=...') + APR set_law_enacted_at |
Tiêu chí severity (đáp Đ31 cross-law GPT):
warning= trong grace period HOẶC drift mới (< 24h)critical= sau grace period HOẶC drift > 24h chưa fix HOẶC duplicate file_path
★ v5.1 KHOẢN 2 — ESCALATION BẮT BUỘC qua fn_log_issue()
Mọi H1-H13 phát hiện issue → BẮT BUỘC gọi fn_log_issue() (PG function trực tiếp). CẤM dùng curl ... || true (silent-fail). Đây là cầu nối Đ22 self-healing.
Nguồn: CLI V2 S177 phát hiện dot-dot-health có gọi log_system_issue nhưng curl || true → 0 row escalate.
8.2 dot-dot-coverage (Cấp A) — giữ v5.0
8.3 dot-dot-register (Cấp B) — v5.1 strengthened
- Đọc
dot_base_pathtừdot_config - Quét → so sánh với
dot_tools - File mới chưa có → infer 11/11 fields rồi POST (§5.1)
- Dual-trigger: kết hợp với
dot-metadata-repaircron (§5.2) - FK enforce: domain/operation/tier phải tồn tại reference tables
- File retired → SKIP
★ v5.1 KHOẢN 3 — fn_birth_gate configurable mode + scope rõ
SCOPE LƯU Ý (đáp non-blocker Council R1 — không nhầm với Đ0-G birth gate):
fn_birth_gatetrong Đ35 là gate cho dot_tools metadata, KHÔNG phải birth gate generic của Đ0-G cho mọi collection. Tên giữ nguyên (đã deploy thật trên VPS chodot_tools, đổi tên = migrate trigger 133 collection — rủi ro lớn). Trong tài liệu kỹ thuật + code comment gọi đầy đủ làfn_birth_gate(Đ35-scope: dot_tools) để phân biệt.
CREATE OR REPLACE FUNCTION fn_birth_gate()
RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER
SET search_path = public, pg_catalog
AS $$
DECLARE
v_missing text[] := ARRAY[]::text[];
v_mode text;
v_grace boolean;
BEGIN
SELECT value INTO v_mode FROM dot_config WHERE key='birth_gate_mode';
v_grace := fn_is_in_grace_period();
-- Check 5 cột critical + _dot_origin
IF NEW.operation IS NULL THEN v_missing := array_append(v_missing, 'operation'); END IF;
IF NEW.coverage_status IS NULL THEN v_missing := array_append(v_missing, 'coverage_status'); END IF;
IF NEW.trigger_type IS NULL THEN v_missing := array_append(v_missing, 'trigger_type'); END IF;
IF NEW.file_path IS NULL THEN v_missing := array_append(v_missing, 'file_path'); END IF;
IF NEW._dot_origin IS NULL THEN NEW._dot_origin := 'unknown'; END IF; -- backfill default
IF array_length(v_missing, 1) > 0 THEN
-- LUÔN escalate qua fn_log_issue (cầu nối Đ22)
PERFORM fn_log_issue(
'fn_birth_gate',
CASE WHEN v_grace THEN 'warning' ELSE 'critical' END,
'dot_metadata_missing',
format('DOT %s thiếu: %s', NEW.code, array_to_string(v_missing, ',')),
NEW.code
);
-- BLOCK chỉ khi hết grace AND mode='block'
IF NOT v_grace AND v_mode = 'block' THEN
RAISE EXCEPTION 'DOT % missing required fields: % (grace expired, mode=block)',
NEW.code, array_to_string(v_missing, ',');
ELSE
RAISE WARNING 'DOT % missing fields: %', NEW.code, array_to_string(v_missing, ',');
END IF;
END IF;
RETURN NEW;
END;
$$;
ALTER FUNCTION fn_birth_gate() OWNER TO workflow_admin;
§9. BOOTSTRAP — 4 BLOCKS (v5.1 thêm Block 4 retrofit)
§9.1 Vấn đề gà-trứng (giữ v5.0)
§9.2 Migration PR — 4 BLOCKS (v5.1)
BLOCK 1 — SCHEMA (giữ v5.0 + v5.1 bổ sung):
- Tạo
dot_domains,dot_coverage_required, 4 reference tables,dot_config - Thêm 11 fields nullable vào
dot_tools(FK trỏ reference tables) — bao gồm_dot_origin - v5.1: Thêm
system_issues.issue_signature+ UNIQUE WHERE status='open' (nếu chưa có)
BLOCK 2 — DATA (giữ v5.0 + v5.1 bổ sung):
- Seed 24 domains, reference tables, dot_config (thêm
birth_gate_mode='warn'+law_v5_1_enacted_at=NOW()) - Backfill
dot_toolsmetadata từ naming convention,coverage_status='partial',_dot_origin='legacy_pre_v5_1' - Register 6 bootstrap DOT
BLOCK 3 — CONSTRAINTS + ENABLE (v5.1 strengthened):
- FK constraints
- PG trigger
trg_dot_enforce_paired(§3) - v5.1: Định nghĩa
fn_is_in_grace_period()+fn_log_issue()(§4.1B) - v5.1:
fn_birth_gatev5.1 configurable mode (§8.3) - KHÔNG bật NOT NULL (chờ BLOCK 4)
- KHÔNG bật cron cho đến BLOCK 4 verify PASS
BLOCK 4 (v5.1 MỚI) — RETROFIT + DEDUPE + NOT NULL — CHECKLIST CỨNG (đáp Council R1 GPT + R2 cả 2)
Thứ tự BẮT BUỘC, không nhảy bước. Mỗi bước "destructive" chạy trong transaction riêng:
[ ] 4.0 ★ v5.1 FINAL fix Council R2 — SNAPSHOT BASELINE (rollback safety):
- CREATE TABLE dot_tools_pre_v5_1 AS SELECT * FROM dot_tools;
- CREATE TABLE system_issues_pre_v5_1 AS SELECT * FROM system_issues WHERE status='open';
- Snapshot 45 cặp duplicate file_path → reports/s177-block4-baseline-<date>.md
- Mapping canonical row ↔ row sẽ retire → reports/
[ ] 4.1 Snapshot 45 cặp duplicate file_path (lưu vào reports/) — chi tiết format
[ ] 4.2 Cho mỗi cặp: chọn canonical row (theo coverage_status='complete' > created_at sớm hơn)
[ ] 4.3 Row dư trong cặp: APR retire — TRƯỚC khi NULL file_path PHẢI lưu vào extra_metadata:
BEGIN;
UPDATE dot_tools
SET extra_metadata = extra_metadata || jsonb_build_object(
'retired_at', NOW(),
'retired_reason', 'block4_dedup_v5_1',
'old_file_path', file_path,
'canonical_dot', <canonical_code>
),
status = 'retired',
file_path = NULL
WHERE code IN (<row_du_codes>);
COMMIT;
(★ v5.1 FINAL fix Council R2 GPT #5: lưu old_file_path để không mất audit trail)
[ ] 4.4 VERIFY: 0 row duplicate non-null file_path
SELECT file_path, COUNT(*) FROM dot_tools
WHERE file_path IS NOT NULL GROUP BY 1 HAVING COUNT(*)>1; -- phải empty
[ ] 4.5 Kích DOT_BIRTH_BACKFILL → scan ngược 272 DOT cũ, tạo birth_registry nếu thiếu
(4.5 TRƯỚC 4.6: phải có giấy khai sinh trước, mới hoàn thiện hồ sơ — Council R2 confirm)
[ ] 4.6 Kích DOT_METADATA_FILL → infer + PATCH operation/coverage/trigger_type/file_path/_dot_origin
(Chạy trong transaction; fail → rollback toàn batch, restore từ snapshot 4.0)
[ ] 4.7 Kích DOT_METADATA_AUDIT → báo cáo drift còn lại
[ ] 4.8 VERIFY: DOT_METADATA_AUDIT báo 0 drift
[ ] 4.9 CREATE UNIQUE INDEX uq_dot_tools_file_path WHERE NOT NULL
(Bảng 272 row → CREATE INDEX thường đủ; với bảng >100K row → khuyến nghị CONCURRENTLY)
[ ] 4.10 ★ v5.1 FINAL fix Council R2 GPT #6 — ALTER NOT NULL ATOMIC TRANSACTION:
BEGIN;
ALTER TABLE dot_tools
ALTER COLUMN operation SET NOT NULL,
ALTER COLUMN coverage_status SET NOT NULL,
ALTER COLUMN trigger_type SET NOT NULL,
ALTER COLUMN file_path SET NOT NULL,
ALTER COLUMN cron_schedule SET NOT NULL,
ALTER COLUMN _dot_origin SET NOT NULL;
COMMIT;
(Fail bất kỳ cột nào → rollback toàn bộ 6 cột, không nửa NOT NULL nửa không)
[ ] 4.11 Enable cron dot-dot-health + dot-dot-coverage + dot-metadata-repair
[ ] 4.12 Quan sát 24h. Nếu 0 critical issue mới → BLOCK 4 PASS.
Nếu phát sinh critical → restore từ snapshot 4.0:
BEGIN;
TRUNCATE dot_tools;
INSERT INTO dot_tools SELECT * FROM dot_tools_pre_v5_1;
COMMIT;
+ investigate root cause + iterate.
[ ] 4.13 Sau 7 ngày BLOCK 4 PASS ổn định → DROP TABLE dot_tools_pre_v5_1, system_issues_pre_v5_1
(giữ snapshot tối thiểu 7 ngày làm safety net)
§9.3 Grace period (v5.1 enforce)
- Mặc định 7 ngày (
dot_config.grace_period_days). - Có thể gia hạn qua config mà không cần sửa luật (đáp non-blocker GPT). Khi
DOT_METADATA_AUDITcòn drift → KHÔNG được chuyểnmode='block'và KHÔNG được bật NOT NULL — gia hạn grace. - Tiêu chí cứng warn → block (§10): xem checklist.
§9.4 Maintenance window (giữ v5.0)
§10. SUCCESS METRICS + TIÊU CHÍ CỨNG warn → block (v5.1 mở rộng)
Success Metrics
- 100%
dot_toolscó 11/11 fields NOT NULL - 100% DOT Cấp B có paired_dot
- 0 hardcode trong DOT scripts
- 0 duplicate file_path (UNIQUE partial enforce)
- 13/13 health checks LIVE
- v5.1: flow
[AUTO-ID] DOT ToolsACTIVE (lưới đỡ phụ giai đoạn warn) +dot-metadata-repaircron LIVE (lưới đỡ chính) - v5.1:
system_issuesnhận events từ H10-H13 +fn_birth_gate(cầu nối Đ22) - last_executed cập nhật tự động
- Reference tables tồn tại + FK active
★ TIÊU CHÍ CỨNG chuyển birth_gate_mode='warn' → 'block' (đáp non-blocker GPT)
Chỉ được
UPDATE dot_config SET value='block' WHERE key='birth_gate_mode'khi 4 điều kiện cùng đạt:
- ✅
DOT_METADATA_AUDITbáo 0 drift trong 3 ngày liên tiếp - ✅
0 row duplicate non-nulltrênfile_path(SELECT file_path, COUNT(*) FROM dot_tools WHERE file_path IS NOT NULL GROUP BY 1 HAVING COUNT(*) > 1= empty) - ✅
dot-metadata-repaircron LIVE + last_executed < 2h (H13 PASS) - ✅ H10-H13 PASS liên tục 3 ngày (0 critical issue mới)
Chưa đủ 4 → giữ warn + gia hạn grace.
§11. RETROFIT CLAUSE (v5.1 MỚI) — ÁP LUẬT CHO ENTITY CŨ + LUẬT ĐÃ BAN HÀNH
§11.1 Nguyên tắc
Mọi luật mới áp dụng cho bảng có DATA CŨ BẮT BUỘC kèm DOT retrofit để 100% entity cũ đáp ứng luật mới. Luật không có §retrofit = legacy mãi vênh = vi phạm HP NT1.
§11.2 Yêu cầu với mọi luật áp dot_tools (và bất kỳ bảng quản trị nào)
- Phải có
DOT_<LAW_ID>_BACKFILL(tier B) — scan ngược toàn bảng, infer + backfill, hoặc tạo APR nếu không suy luận được. - Paired với
DOT_<LAW_ID>_VERIFY(tier A) — báo entity chưa đáp ứng → INSERT system_issues nếu > 0. - Chạy 1 lần sau ban hành TRƯỚC khi bật NOT NULL §4.1.
- Cron daily
DOT_<LAW_ID>_VERIFYđể catch drift tương lai.
§11.2.1 RULE auto-backfill vs APR (đáp non-blocker GPT)
| Trường hợp | Hành động |
|---|---|
| Infer chắc chắn + idempotent + có verify pair (vd: parse file_path → operation theo regex chuẩn) | Auto-PATCH ngay, log vào kb_audit_log |
| Infer mơ hồ HOẶC không idempotent HOẶC không verify được | Tạo APR backfill_<field> chờ duyệt |
§11.3 Áp dụng cho Đ35 v5.1 — 6 DOT birth-related đã có nhưng chưa chạy
| DOT | Vai trò | Kích sau v5.1 ban hành |
|---|---|---|
DOT_BIRTH_BACKFILL |
Scan ngược 272 DOT cũ, tạo birth_registry nếu thiếu | ✅ BLOCK 4 bước 4.5 |
DOT_METADATA_AUDIT |
Scan dot_tools, report số DOT thiếu metadata | ✅ Bước 4.7 |
DOT_METADATA_FILL |
Backfill operation/coverage/file_path/_dot_origin | ✅ Bước 4.6 |
DOT_KG_COMPLETENESS |
Verify paired với FILL | ✅ Cron daily sau ban hành |
DOT_SCHEMA_BIRTH_REGISTRY_ENSURE |
Đảm bảo schema birth_registry đúng | ✅ Trước BLOCK 3 |
DOT_BIRTH_TRIGGER_SETUP |
Verify fn_birth_gate live trên 133 collections | ✅ Sau BLOCK 3 |
Race condition (đáp Council R1 cả 2): Chạy tuần tự có barrier theo thứ tự checklist BLOCK 4. KHÔNG kích đồng thời. Mỗi DOT xong → audit → mới sang DOT tiếp theo.
§11.4 Áp cho luật ĐÃ BAN HÀNH (đáp blocker non-blocker GPT — wording cứng hơn)
Mọi luật đang có hiệu lực mà tác động bảng có data cũ NHƯNG chưa có retrofit clause đều phải mở amend bổ sung retrofit trong kỳ sửa gần nhất. TRƯỚC KHI amend xong, KHÔNG được bật constraint/block mới làm legacy kẹt cứng.
Cụ thể: Đ36, Đ37, Đ38, Đ39, Đ41 đã ban hành mà tác động bảng có data cũ → audit chéo + amend retrofit. Tracker: knowledge/current-state/retrofit-audit-tracker.md (tạo trong cùng PR ban hành v5.1).
§11.5 Áp cho luật tương lai
OR soạn luật v1.0 → v1.1 (amend song song cùng PR ban hành v5.1) bổ sung checklist bắt buộc: "Luật có impact lên bảng có data cũ → kèm §retrofit + DOT_<LAW>BACKFILL + DOT<LAW>_VERIFY".
§12. (Đã bỏ) — DOT_LAW_VERIFY chuyển ra OR + Skill Handoff
Pattern PROMPT-LAW DRIFT (S177) không xử ở Đ35 vì DOT không đọc được markdown. Xử 2 nơi trong cùng PR:
- OR soạn luật v1.0 → v1.1: checklist "Prompt bootstrap đối chiếu §4+§9 luật, ghi TÊN bảng/operation, không chỉ số"
- Skill Handoff v7 + AP-22 mới "Đếm số không đo tên": handoff ghi "N artifact" không kèm danh sách = anti-pattern. Lint scan regex.
PHỤ LỤC A — 24 Domain Seed (giữ nguyên v5.0)
(Bảng 24 dòng — giữ nguyên từ v5.0, không lặp lại để tiết kiệm dung lượng.)
CHANGELOG
| Ver | Ngày | Nội dung |
|---|---|---|
| v5.1 FINAL | 2026-04-14 S177 Fix 8 BAN HÀNH | Council 2 vòng DONE. R1: Gemini 9/10 + GPT 8.3/10 APPROVE WITH CHANGES → fix 5 blocker. R2: Gemini 9.8/10 APPROVE FINAL + GPT 9.1/10 APPROVE WITH MINOR → apply 7 fix inline: (1) dot_config.value nullable + UTC ISO sentinel; (2) H14 mới bắt grace_never_set > 24h; (3) fn_log_issue bỏ severity khỏi signature + escalate trên cùng row; (4) precheck dedupe system_issues trước CREATE UNIQUE INDEX; (5) §5.1 viết cứng infer fail = CẤM POST partial; (6) H13 chỉ báo động không self-repair; (7) BLOCK 4 thêm 4.0 snapshot + 4.3 lưu old_file_path + 4.10 atomic transaction + 4.13 cleanup. 0 blocker còn lại → BAN HÀNH chính thức. |
| v5.1 DRAFT rev 2 | 2026-04-14 S177 Fix 8 | Sau Council R1. Fix 5 blocker B1-B5 + 5 non-blocker. Sửa lỗi viện dẫn NT12/NT13 (HP v4.5.1 chỉ có 11 NT). Chờ R2. |
| v5.1 DRAFT rev 1 | 2026-04-14 S177 Fix 7 | 4 khoản gốc + §11 retrofit clause. Council R1 nêu 5 blocker + 5 non-blocker |
| v5.0 FINAL | 2026-03-31 S151 | Ban hành gốc. Phát hiện S177: prompt bootstrap S155 thiếu 5 bảng so với §9.2 luật → 49 DOT POST-S151 vẫn 94% NULL operation |
Điều 35 v5.1 FINAL | BAN HÀNH 2026-04-14 S177 Fix 8 | Council 2 vòng DONE | 0 blocker | Sẵn sàng deploy theo BLOCK 4 checklist §9.2