KB-695E rev 11

Điều 35 v5.1 DRAFT rev 2 — Sau Council Round 1

38 min read Revision 11

⚠️ 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 qua fn_log_issue, §8.3 fn_birth_gate configurable 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):

  1. dot_config.value cho phép NULL + sentinel law_v5_1_enacted_at + UTC ISO format (§4.4)
  2. H14 mới — bắt law_v5_1_enacted_at chưa set > 24h (§8.1)
  3. fn_log_issue BỎ severity khỏi signature → severity leo thang trên cùng row (§4.1B)
  4. PRECHECK dedupe system_issues open trước CREATE UNIQUE INDEX (§4.1B)
  5. §5.1 viết cứng: infer fail = CẤM POST partial row (§5.1)
  6. H13 chỉ báo động, KHÔNG self-repair dot-metadata-repair (§5.2)
  7. 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:

  1. Quản trị sổ đăng ký DOT (dot_tools — SSOT duy nhất)
  2. Phân loại DOT theo domain, tier, operation, trigger_type — TẤT CẢ qua PG SSOT tables, KHÔNG text tự do
  3. Quản lý domain DOT (dot_domains — FK enforce, 4 lớp bảo vệ)
  4. Đo coverage: domain × operation × tier — mẫu số TỰ ĐỘNG cập nhật
  5. Quản lý vòng đời DOT: tạo → active → deprecated → retired
  6. Kiểm tra sức khoẻ DOT: pairing, domain, target, file_path, cron, metadata completeness (v5.1)
  7. DOT 2 cấp: Cấp A (kiểm tra) + Cấp B (thực thi) — paired bắt buộc
  8. ★ Retrofit luật mới cho entity cũ (v5.1 — §11)

★ v5.1 LƯU Ý VẬN HÀNH:

  • fn_birth_gate mặc định mode='warn' ngay sau ban hành. Chuyển 'block' qua UPDATE 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_dot FK → 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 NULL chặn 2 row cùng có cùng file_path non-null. Cho phép nhiều row file_path IS NULL cù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 INDEX FAIL 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-health H14 (v5.1 FINAL): nếu dot_config.law_v5_1_enacted_at IS NULLBLOCK 1 đã chạy > 24hfn_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-register infer 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ào dot_tools. Bắt buộc:

  1. Log issue qua fn_log_issue('dot-dot-register','warning','infer_fail',...)
  2. Tạo APR request_type='backfill_metadata' với evidence (file_path, fields fail infer)
  3. SKIP file đó trong run hiện tại
  4. 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-repair hỏng → escalate qua fn_log_issue + tạo APR request_type='fix_repair_dot' để owner xử lý. KHÔNG có self-repair tự động cho dot-metadata-repair (tránh self-paradox: DOT canh gác chính nó). Khi repair DOT chết, hệ thống dựa vào fn_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-audit scan dot_tools WHERE 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_path từ 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-repair cron (§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_gate trong Đ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 cho dot_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_tools metadata 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_gate v5.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_AUDIT còn drift → KHÔNG được chuyển mode='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_tools có 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 Tools ACTIVE (lưới đỡ phụ giai đoạn warn) + dot-metadata-repair cron LIVE (lưới đỡ chính)
  • v5.1: system_issues nhậ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:

  1. DOT_METADATA_AUDIT báo 0 drift trong 3 ngày liên tiếp
  2. 0 row duplicate non-null trên file_path (SELECT file_path, COUNT(*) FROM dot_tools WHERE file_path IS NOT NULL GROUP BY 1 HAVING COUNT(*) > 1 = empty)
  3. dot-metadata-repair cron LIVE + last_executed < 2h (H13 PASS)
  4. ✅ 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)

  1. 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.
  2. Paired với DOT_<LAW_ID>_VERIFY (tier A) — báo entity chưa đáp ứng → INSERT system_issues nếu > 0.
  3. Chạy 1 lần sau ban hành TRƯỚC khi bật NOT NULL §4.1.
  4. 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
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:

  1. 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ố"
  2. 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