KB-5C19

Phụ lục triển khai — Dự thảo SSOT HC Fix 24 — NT14 Compliance

12 min read Revision 1
appendiximplementationnt14hc-triggermigrationparityreference-examples178-fix24council-approved

PHỤ LỤC TRIỂN KHAI — DỰ THẢO SSOT HC FIX 24

Mục đích: Đảm bảo NT14 — agent cầm tài liệu này và code không cần hỏi lại. Liên kết: Dự thảo chính du-thao-sua-luat-ssot-noi-dung-luat-hc-ha-tang-fix24.md


APPENDIX A — HC-TRIGGER Implementation Contract

A1. Detect Query (đầy đủ, copy-paste được)

-- HC-TRIGGER: Bảng governed có cột description nhưng thiếu trigger trg_desc_guard_*
-- DB: directus | Schema: public | Read-only
SELECT cr.collection_name
FROM collection_registry cr
JOIN information_schema.columns c
  ON c.table_name = cr.collection_name
  AND c.column_name = 'description'
  AND c.table_schema = 'public'
WHERE cr.governance_role = 'governed'
  AND c.table_name IN (
    SELECT table_name FROM information_schema.tables
    WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
  )
  AND cr.collection_name NOT IN (
    SELECT DISTINCT event_object_table
    FROM information_schema.triggers
    WHERE trigger_name LIKE 'trg_desc_guard_%'
      AND trigger_schema = 'public'
  );

A2. Auto-attach pseudocode

-- Chạy cho MỖI bảng phát hiện thiếu trigger (từ A1)
-- Preconditions: 7 guards phải PASS trước khi chạy

DO $$
DECLARE
  _table TEXT;
  _trigger_name TEXT;
  _fn_exists BOOLEAN;
  _autofix_enabled TEXT;
BEGIN
  -- Guard 2: Config gate
  SELECT value INTO _autofix_enabled FROM dot_config
    WHERE key = 'hc_trigger_autofix_enabled';
  IF _autofix_enabled IS NULL OR _autofix_enabled != 'true' THEN
    RAISE NOTICE 'HC-TRIGGER: autofix disabled (dot_config.hc_trigger_autofix_enabled != true). Skip.';
    RETURN;
  END IF;

  -- Guard 1: Function tồn tại
  SELECT EXISTS(
    SELECT 1 FROM pg_proc WHERE proname = 'fn_description_birth_guard'
  ) INTO _fn_exists;
  IF NOT _fn_exists THEN
    -- Escalate CRITICAL — function chưa tồn tại
    INSERT INTO system_issues(category, severity, description, source)
    VALUES ('hc_trigger', 'critical',
      'fn_description_birth_guard() NOT FOUND — cannot auto-attach. Escalate.',
      'HC-TRIGGER');
    RETURN;
  END IF;

  -- Guard 4: Lock timeout
  SET LOCAL lock_timeout = '5s';

  FOR _table IN (
    -- A1 detect query (chỉ BASE TABLE — Guard 5: exclude scope)
    SELECT cr.collection_name
    FROM collection_registry cr
    JOIN information_schema.columns c
      ON c.table_name = cr.collection_name
      AND c.column_name = 'description'
      AND c.table_schema = 'public'
    WHERE cr.governance_role = 'governed'
      AND c.table_name IN (
        SELECT table_name FROM information_schema.tables
        WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
      )
      AND cr.collection_name NOT IN (
        SELECT DISTINCT event_object_table
        FROM information_schema.triggers
        WHERE trigger_name LIKE 'trg_desc_guard_%'
          AND trigger_schema = 'public'
      )
  ) LOOP
    _trigger_name := 'trg_desc_guard_' || _table;

    -- Guard 3: Idempotency — skip nếu trigger đã tồn tại
    IF EXISTS (
      SELECT 1 FROM information_schema.triggers
      WHERE trigger_name = _trigger_name AND trigger_schema = 'public'
    ) THEN
      CONTINUE;
    END IF;

    BEGIN
      EXECUTE format(
        'CREATE TRIGGER %I BEFORE INSERT OR UPDATE OF description ON %I
         FOR EACH ROW EXECUTE FUNCTION fn_description_birth_guard()',
        _trigger_name, _table
      );

      -- Guard 6: Post-attach verify
      IF NOT EXISTS (
        SELECT 1 FROM information_schema.triggers
        WHERE trigger_name = _trigger_name
          AND trigger_schema = 'public'
          AND action_statement LIKE '%fn_description_birth_guard%'
      ) THEN
        -- Verify FAIL
        INSERT INTO system_issues(category, severity, description, source)
        VALUES ('hc_trigger', 'critical',
          format('Post-attach verify FAILED for %s — trigger not found after CREATE', _table),
          'HC-TRIGGER');
        CONTINUE;
      END IF;

      -- Guard 7: Audit trail
      INSERT INTO admin_fallback_log(action_type, target_table, detail, performed_by)
      VALUES ('auto_attach_trigger', _table,
        format('{"trigger":"%s","function":"fn_description_birth_guard"}', _trigger_name),
        'HC-TRIGGER');

      INSERT INTO system_issues(category, severity, description, source)
      VALUES ('hc_trigger', 'info',
        format('Auto-attached %s on %s — verified OK', _trigger_name, _table),
        'HC-TRIGGER');

    EXCEPTION WHEN lock_not_available THEN
      -- Guard 4: Lock timeout → skip, retry next cycle
      INSERT INTO system_issues(category, severity, description, source)
      VALUES ('hc_trigger', 'warning',
        format('Lock timeout on %s — will retry next cycle', _table),
        'HC-TRIGGER');
    WHEN OTHERS THEN
      INSERT INTO system_issues(category, severity, description, source)
      VALUES ('hc_trigger', 'critical',
        format('Attach FAILED on %s: %s', _table, SQLERRM),
        'HC-TRIGGER');
    END;
  END LOOP;
END $$;

A3. Config key

INSERT INTO dot_config(key, value, description) VALUES
  ('hc_trigger_autofix_enabled', 'false',
   'Đ22 §4.4 HC-TRIGGER: true=auto-attach trigger thiếu, false=detect-only. Default false lần đầu, chuyển true sau verify manual.')
ON CONFLICT (key) DO NOTHING;

A4. Orchestration

  • DOT nào chạy: Generic executor của system_health_checks — DOT scanner Đ22 query WHERE is_active=true ORDER BY order_index, chạy từng check theo executor_type.
  • Cron: Theo schedule của DOT scanner Đ22 (khuyến nghị: cron daily hoặc mỗi 3h theo DOT context-pack-verify).
  • HC-TRIGGER là 1 row trong system_health_checks với executor_type='sql' → executor load SQL từ executor_ref path → chạy detect → nếu check_kind='detect_and_fix' AND auto_fix_action='auto_attach_trigger' → chạy A2 pseudocode.

A5. Fail path tổng hợp

Tình huống Severity Hành xử
Function fn_description_birth_guard chưa tồn tại CRITICAL Dừng toàn bộ, ghi system_issues, KHÔNG auto-attach
Config gate = false INFO Skip, chỉ detect, ghi kết quả
Lock timeout trên 1 bảng WARNING Skip bảng đó, retry next cycle
Post-attach verify fail CRITICAL Ghi system_issues, không count là success
Attach exception khác CRITICAL Ghi system_issues + SQLERRM
Attach thành công INFO Ghi admin_fallback_log + system_issues info

APPENDIX B — Transfer Parity & Migration Contract

B1. Bảng nguồn và mapping

Cột context_pack_health_checks (Đ43 cũ) Cột system_health_checks (Đ22 mới) Ghi chú
code code Giữ nguyên
name name Giữ nguyên
(không có) jurisdiction Thêm mới: set 'LAW-43' cho tất cả rows migrate
(không có) check_kind Thêm mới: set 'detect_only' (H1-H9), 'detect_only' (H11)
executor_type executor_type Giữ nguyên
executor_ref executor_ref Giữ nguyên
threshold_config threshold_config Giữ nguyên
severity_on_fail severity_on_fail Giữ nguyên
(không có) auto_fix_action Thêm mới: set NULL (tất cả H1-H11 là detect_only)
is_active is_active Giữ nguyên
order_index order_index Giữ nguyên
(không có) description Set từ name hoặc NULL
_dot_origin _dot_origin Giữ nguyên

B2. Migration SQL

-- Bước 1: Migrate từ Đ43 (context_pack_health_checks → system_health_checks)
INSERT INTO system_health_checks(
  code, name, jurisdiction, check_kind, executor_type, executor_ref,
  threshold_config, severity_on_fail, auto_fix_action,
  is_active, order_index, description, _dot_origin
)
SELECT
  code, name,
  'LAW-43',                    -- jurisdiction mới
  'detect_only',               -- tất cả H1-H11 chỉ detect
  executor_type, executor_ref,
  threshold_config, severity_on_fail,
  NULL,                        -- auto_fix_action = NULL (detect_only)
  is_active, order_index,
  name,                        -- description = name tạm
  _dot_origin
FROM context_pack_health_checks
ON CONFLICT (code) DO NOTHING;

B3. Parity check query

-- So sánh row count + code set giữa 2 bảng
WITH old AS (
  SELECT code, name, executor_type, executor_ref, threshold_config,
         severity_on_fail, is_active, order_index
  FROM context_pack_health_checks
),
new AS (
  SELECT code, name, executor_type, executor_ref, threshold_config,
         severity_on_fail, is_active, order_index
  FROM system_health_checks WHERE jurisdiction = 'LAW-43'
)
SELECT
  (SELECT count(*) FROM old) AS old_count,
  (SELECT count(*) FROM new) AS new_count,
  (SELECT count(*) FROM old o
   LEFT JOIN new n ON o.code = n.code
   WHERE n.code IS NULL) AS missing_in_new,
  (SELECT count(*) FROM old o
   JOIN new n ON o.code = n.code
   WHERE o.name != n.name
     OR o.executor_type != n.executor_type
     OR o.executor_ref != n.executor_ref
     OR o.threshold_config::text != n.threshold_config::text
     OR o.severity_on_fail != n.severity_on_fail
     OR o.is_active != n.is_active
     OR o.order_index != n.order_index
  ) AS field_mismatch;
-- PASS: old_count = new_count AND missing_in_new = 0 AND field_mismatch = 0

B4. Dual-read verify (48h)

Đ43 verify.sh trong 48h chạy BOTH queries:

# OLD path (sẽ DROP sau 48h)
psql -c "SELECT code, severity_on_fail FROM context_pack_health_checks WHERE is_active ORDER BY order_index"

# NEW path
psql -c "SELECT code, severity_on_fail FROM system_health_checks WHERE jurisdiction='LAW-43' AND is_active ORDER BY order_index"

# diff 2 kết quả → nếu khác nhau → DỪNG cut-over, ghi system_issues

B5. Cut-over rules

Điều kiện Hành xử
48h stable + parity PASS (B3 all zeros) + dual-read OK (B4 no diff) Tiến hành DROP context_pack_health_checks
Parity FAIL (missing_in_new > 0 hoặc field_mismatch > 0) DỪNG. Điều tra. Ghi system_issues CRITICAL.
Dual-read mismatch DỪNG. Rollback verify.sh về OLD path. Điều tra.

B6. DB target

  • Bảng system_health_checks tạo trong DB directus, schema public (cùng DB với context_pack_health_checks cũ).
  • normative_registry cũng ở DB directus, schema public → FK hợp lệ.

APPENDIX C — Header Reference Example

C1. Ví dụ mẫu hoàn chỉnh — Đ22 sau khi sửa

## THAM CHIẾU LIÊN LUẬT
- **SSOT chính:** Hạ tầng health check toàn hệ thống (bảng `system_health_checks`, generic executor). Triết lý 2 Động cơ (§5). Vòng lặp tự sửa chữa (§1). HC-TRIGGER enforcement gap (§4.4).
- **Phụ thuộc bắt buộc:** HP (13 NT, đặc biệt NT12 auto-scale + NT14 thực thi được ngay), Đ37 §4.12-13 (SSOT nội dung luật + trang bị đủ công cụ)
- **Mượn SSOT từ:** Trigger function → Đ4 §2.1 `fn_description_birth_guard()`. Governance role → Đ29 `collection_registry.governance_role`. Kết quả ghi → `system_issues` (Đ22 §1 vòng lặp). Provenance → Đ24 FAC-PROV.
- **Ưu tiên khi xung đột:** Theo HP và luật gốc đã xác định tại Đ37 §4.12. Health check infrastructure → Đ22 là SSOT (Đ37 §4.12 đã xác định). Nội dung check cụ thể → luật chuyên môn (Đ35/Đ36/Đ43) qua jurisdiction.

C2. Ví dụ inline reference đúng

-- Trong thân luật Đ22 §4.4:
"Trigger function `fn_description_birth_guard()` (SSOT tại Đ4 §2.1) phải tồn tại trước khi auto-attach."

"Chỉ áp dụng cho bảng có `governance_role='governed'` (theo Đ29 qua `collection_registry`)."

"Ghi kết quả vào `system_issues` (theo Đ22 §1 vòng lặp NHẬN DIỆN → LIỆT KÊ)."

C3. Rule coverage tối thiểu

Mỗi khoản sử dụng SSOT từ luật khác PHẢI có ít nhất 1 inline reference tại đúng chỗ dùng. "Sử dụng SSOT" = đọc/query/tham chiếu bảng, function, config, hoặc quy tắc được định nghĩa ở luật khác.

Thiếu inline reference = Council PHẢI yêu cầu bổ sung trước khi approve.