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 queryWHERE is_active=true ORDER BY order_index, chạy từng check theoexecutor_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_checksvớiexecutor_type='sql'→ executor load SQL từexecutor_refpath → chạy detect → nếucheck_kind='detect_and_fix'ANDauto_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_checkstạo trong DB directus, schema public (cùng DB vớicontext_pack_health_checkscũ). normative_registrycũ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.