KB-2970

P10B-2B-FIX — D28 Package Regen + Birth-Gate Compliance Matrix v0.2 (2026-04-29)

14 min read Revision 1
reportp10bp10b-2b-fixdieu-28birth-gatecompliance-matrixread-only

P10B-2B-FIX — Đ28 D28 Package Regen + Birth-Gate Compliance Matrix v0.2

Task: P10B-2B-FIX v0.2 Captured at: 2026-04-29 Mode: READ-ONLY package regeneration. No INSERT/UPDATE/DELETE/DDL on production. Verdict: PASS — 4 SQL files + machine-readable matrix produced; all blocking birth gates PASS for all 27 units.

P10B-2C-PF-R2 required before execute. Do not execute this package directly.


1. Input SHA verification

Field Value
KB path knowledge/dev/laws/dieu38-trien-khai/data/p10b-d28-candidate-units-r2.json
Acquisition Fetched via Agent Data API ?full=true on VPS
Bytes 22370
Source SHA256 e47775e33cc752656468edb287cca7b58539804678443b6c1b1dd03b165de8ad ✅ matches expected
Unit count 27 ✅

2. Birth-Gate Compliance Matrix (machine-readable)

Path on VPS: /tmp/p10b-2b-fix/birth-gate-requirements.json (13 335 bytes).

2.1 Gate inventory + mode + compliance

Gate Mode Blocking Source Applicable Exempt Status
BG-LU-01 block (hard-coded) True fn_tac_birth_gate_lu (lines 11-14) 27 0 PASS
BG-LU-02 block True fn_tac_birth_gate_lu (lines 16-18) 27 0 PASS
BG-LU-03 block True fn_tac_birth_gate_lu (lines 20-33) 27 0 PASS
BG-LU-04 block True fn_tac_birth_gate_lu (lines 35-41) 27 0 PASS
BG-LU-05 block True fn_tac_birth_gate_lu (lines 43-45) 27 0 PASS
BG-LU-06 block True fn_tac_birth_gate_lu (lines 47-49) 27 0 PASS
BG-UV-01 block True fn_tac_birth_gate_uv (lines 25-27) 27 0 PASS
BG-UV-02 block True fn_tac_birth_gate_uv (lines 29-41) 20 7 PASS (was FAIL in P10B-2B)
BG-UV-03 warn False fn_tac_birth_gate_uv (lines 43-55) 22 5 PASS
BG-UV-04 block True (none) — configured, not implemented 27 0 PASS (CONFIGURED-NO-TRIGGER)
BG-UV-05 warn False (none) — configured, not implemented 27 0 PASS
BG-UV-06 warn False fn_tac_birth_gate_uv (lines 57-59) 27 0 PASS

Blocking gates: 8 enforced + 1 configured-only (BG-UV-04). All blocking PASS = TRUE.

2.2 BG-UV-02 detail (the gate that failed P10B-2C)

  • Source: fn_tac_birth_gate_uv lines 29-41
  • Excerpt: IF v_desc_required AND (NEW.description IS NULL OR pg_catalog.length(pg_catalog.btrim(NEW.description)) = 0) THEN ... RAISE EXCEPTION 'BG-UV-02: description required cho section_type %' ...
  • Mode: block (per tac_birth_gate_config)
  • Required column: description
  • Fix strategy: description = title-stub for types with description_required=true; NULL for types where description_required=false (heading, checklist)
  • Compliance for 27 candidate units:
    • 20 applicable (paragraph, principle, technical_spec, governance_process, process) → PASS via title-stub
    • 7 exempt (heading × 5: ROOT, S1, S2, S3, S8; checklist × 2: S2-P5, S10) → description = NULL legal
    • Status: PASS

2.3 Gate config (modes per checker, live from tac_birth_gate_config)

checker_id mode enabled
BG-LU-02..06 block true
BG-UV-01 block true
BG-UV-02 block true
BG-UV-03 warn true
BG-UV-04 block true (no trigger reference)
BG-UV-05 warn true (no trigger reference)
BG-UV-06 warn true

BG-LU-01 has no row in tac_birth_gate_config — hard-coded in function, always blocking.

2.4 Vocab requirements (live, all 7 candidate types)

section_type body_required description_required
heading f f
paragraph t t
principle t t
technical_spec t t
governance_process t t
process t t
checklist t f

All 7 section_types have lifecycle_status='active' (BG-LU-04 requirement satisfied).


3. Fix applied

description = title-stub injected into UV INSERTs. Mechanics:

  • Generator (gen.py) iterates the candidate JSON and looks up each unit's section_type in the live tac_section_type_vocab table
  • If description_required=truedescription = title (same value as the title column)
  • If description_required=falsedescription = NULL
  • No other column changed; LU/PM/Publication unchanged from P10B-2B

Per-unit fix outcome (20 written, 7 NULL):

20 PASS  (description = title-stub):
  S0, S1-P1, S1-P2, S2-P1..S2-P4, S3-P1..S3-P5,
  S4, S5, S6, S7, S8-P1, S8-P2, S9, S11

7 EXEMPT (description = NULL, no trigger violation):
  ROOT, S1, S2, S3, S8       (heading × 5)
  S2-P5, S10                  (checklist × 2)

4. Regenerated SQL files + SHA256

File Bytes Lines SHA256
insert-candidate.sql 41 732 254 6dcab25c071d41a68b7c7faaf7b3dde66c714b823e14cede5b7ebeb057ecd9ee
render.sql 981 22 839ac900fa422795b006599da8d213be8af365e52d8d1a32dd7e15756b9e0f2f (unchanged)
rollback.sql 605 21 9a2def1e28021cf923fe48335062ecbf87c07aad8f86cd177292a2454f38b8af (unchanged)
verify-counts.sql 1 145 14 3bdfd8f2c8f92b5d7728f3e4bd4db6ccae10e6b56bffaf0037376461be217121 (unchanged)
birth-gate-requirements.json 13 335 29fef492403cf86188d17d8f6624d6327415612bd2fee35ae886dc9b1d0ab4c6
gen.py 9ab07f1a2472ee2ee99d9a4c29678ab64a7952840f3732627190edafa01bdcab
build_matrix.py dd63ef5fffb80a32858a7e8cc058dd3471e1dd15b8e70bc8bc611403e709c522
checks.py 859e68246ff32db1fad47f61428c87bf1c5e7720630fef8b8e0fbd82fad61df4

Both ROLLBACK; and COMMIT; lines remain commented in insert-candidate.sql — execute gate enforced at the SQL level.


5. Old vs new package diff (T6)

File Old SHA (P10B-2B) New SHA (FIX) Changed? What changed
insert-candidate.sql 767450fd3c4a8768577998c100f0cfc4c2db1500d41d37726b8e5fae0627489b 6dcab25c071d41a68b7c7faaf7b3dde66c714b823e14cede5b7ebeb057ecd9ee YES UV INSERT column list +description; 20 rows carry title-stub, 7 rows NULL
render.sql 839ac900fa422795b006599da8d213be8af365e52d8d1a32dd7e15756b9e0f2f same NO byte-identical copy from P10B-2B
rollback.sql 9a2def1e28021cf923fe48335062ecbf87c07aad8f86cd177292a2454f38b8af same NO byte-identical copy from P10B-2B
verify-counts.sql 3bdfd8f2c8f92b5d7728f3e4bd4db6ccae10e6b56bffaf0037376461be217121 same NO byte-identical copy from P10B-2B
candidate-units-r2.json e47775e33cc752656468edb287cca7b58539804678443b6c1b1dd03b165de8ad same NO input unchanged

Only insert-candidate.sql differs — minimal blast radius for review.


6. Column completeness check (parser-based, body-stripped)

Sanitizer: $tag$ ... $tag$ matched-pair replacement → STRING (handles arbitrary tag names; safe past $$ interior delimiters thanks to dollar-quoted nesting rules).

UV INSERT column list extracted from sanitized SQL:

[logical_unit_id, version_number, title, body, description, lifecycle_status,
 review_state, provenance, editor]
  • description PRESENT ✅ (the only added column)
  • INSERT row counts (sanitized): LU=27, UV=27, PM=27 ✅
  • Gate→column mapping verified:
    • BG-UV-01 needs title → PRESENT
    • BG-UV-02 needs description → PRESENT
    • BG-UV-03 needs body → PRESENT
    • BG-UV-06 needs provenance → PRESENT
    • LU gates (canonical_address, doc_code, parent_id, section_type, owner, sort_order) → all PRESENT in LU INSERT

7. Sample UV rows (line-range, not grep -A) (T5)

7.1 Sample S0 — paragraph, description_required=true

insert-candidate.sql lines 48–52:

INSERT INTO tac_unit_version (logical_unit_id, version_number, title, body, description, lifecycle_status, review_state, provenance, editor) SELECT lu.id, 1, $body$Preamble$body$, $body$> **v2.0 BAN HÀNH | S150 (2026-04-01) | Huyên đề xuất + Claude soạn**
> **Đổi tên:** "Luật Khuôn Mẫu Chuẩn" → "LUẬT KỸ THUẬT HIỂN THỊ"
> **Kế thừa:** v1.0 (S157). Mở rộng: +Collection PG, +Nuxt whitelist, +Checklist, +Quy trình test, +Chuyển giao, +Coverage scanner.
> **Hội đồng:** GPT 8.4/10 + Gemini 9.5/10. 2 vòng review. Đồng thuận ban hành.
> **Rà soát: 13/13 NT — 0 vi phạm (S165-KB rà soát).**$body$, $body$Preamble$body$, $body$draft$body$, $body$unreviewed$body$, $body$PROV-HUMAN$body$, $body$editor$body$ FROM tac_logical_unit lu WHERE lu.canonical_address=$body$D38-DIEU28-S0$body$ AND lu.doc_code=$body$DIEU-28$body$;
  • Column list contains description (5th) ✅
  • body uses $body$ ... $body$ outer wrapper, multi-line content preserved verbatim ✅
  • description = $body$Preamble$body$ = title-stub ✅

7.2 Sample S3-P3 — technical_spec, body has $$ LANGUAGE plpgsql

insert-candidate.sql lines 115–141:

INSERT INTO tac_unit_version (logical_unit_id, version_number, title, body, description, lifecycle_status, review_state, provenance, editor) SELECT lu.id, 1, $body$★ PG TRIGGER enforce lifecycle (Tuyên ngôn ②)$body$, $body$```sql
CREATE FUNCTION fn_template_lifecycle_guard() RETURNS TRIGGER AS $$
BEGIN
  -- CẤM nhảy cóc: draft→active (bỏ qua testing)
  IF OLD.status = 'draft' AND NEW.status = 'active' THEN
    RAISE EXCEPTION 'CẤM draft→active. PHẢI qua testing (5/5 PASS).';
  END IF;
  ...
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_template_lifecycle
  BEFORE UPDATE ON design_templates
  FOR EACH ROW WHEN (OLD.status IS DISTINCT FROM NEW.status)
  EXECUTE FUNCTION fn_template_lifecycle_guard();

→ Agent KHÔNG THỂ active khuôn chưa test hoặc checklist thiếu.$body$, $body$★ PG TRIGGER enforce lifecycle (Tuyên ngôn ②)$body$, $body$draft$body$, $body$unreviewed$body$, $body$PROV-HUMAN$body$, $body$editor$body$ FROM tac_logical_unit lu WHERE lu.canonical_address=$body$D38-DIEU28-S3-P3$body$ AND lu.doc_code=$body$DIEU-28$body$;


- Outer `$body$ ... $body$` survives nested `$$ ... $$` (different tag, no collision) ✅
- description = `$body$★ PG TRIGGER enforce lifecycle (Tuyên ngôn ②)$body$` = title-stub ✅
- SQL inside body intact — agents/Directus see source-of-truth bytes when rendering ✅

---

## 8. Collision check (T7)

SELECT count(*) FROM tac_publication WHERE doc_code='DIEU-28' AND version='v2.0'; → 0 ✅ (auto-rollback preserved baseline)

SELECT count(*) FROM tac_logical_unit WHERE canonical_address LIKE 'D38-DIEU28-%'; → 0 ✅


---

## 9. Hardcode audit (T8)

```bash
grep -rn 'DIEU28-S[0-9]' /tmp/p10b-2b-fix/*.py
  → only checks.py:72 (sample addr list, used for line-range demonstration only)

grep -rn '\[.*I.*II.*\]' /tmp/p10b-2b-fix/*.py    → (no output)
grep -rn 'if.*==.*VII'    /tmp/p10b-2b-fix/*.py   → (no output)

gen.py and build_matrix.py contain ZERO hardcoded unit addresses or section enumerations — they iterate the candidate JSON and live vocab/config queries.

The checks.py hits are sample-address constants used only for line-range demonstration in §7, not for SQL generation.

PASS.


10. Generator evidence (T9)

Generator script: /tmp/p10b-2b-fix/gen.py (SHA 9ab07f1a2472ee2ee99d9a4c29678ab64a7952840f3732627190edafa01bdcab).

Statement: SQL was generated by iterating candidate-units-r2.json (SHA-asserted in gen.py) and looking up description_required per section_type from the live tac_section_type_vocab table. No manual unit lists, no per-section case dispatch. The only difference from P10B-2B is the description column emission per the birth-gate compliance matrix.

Auxiliary scripts (also pure-iteration over JSON + DB):

  • build_matrix.py (SHA dd63ef5f...) — emits birth-gate-requirements.json
  • checks.py (SHA 859e6824...) — column completeness + collision + sample line ranges

11. PASS criteria checklist

# Criterion Status
1 birth-gate-requirements.json created ✅ 13 335 bytes
2 All blocking gates PASS for all 27 units
3 insert-candidate.sql regenerated with description column
4 Old vs new diff summary ✅ §5
5 Column completeness check (parser-based, body-stripped) ✅ §6
6 Sample UV rows (line-range) ✅ §7
7 Candidate JSON SHA unchanged e47775e3...
8 Collision still 0 ✅ §8
9 Hardcode audit PASS ✅ §9
10 Generator retained + hashed ✅ §10
11 Report explicitly states "P10B-2C-PF-R2 required before execute" ✅ (top + verdict)
12 Report uploaded KB ✅ (this document)
13 STOP after upload

12. Verdict

PASS. All 13 PASS criteria met. Birth-gate compliance for all 8 effective blocking gates verified mechanically against all 27 candidate units. The single semantic change vs P10B-2B is the addition of the description column to UV INSERTs, populated per the live vocab × tac_birth_gate_config matrix.

P10B-2C-PF-R2 required before execute. Do not execute this package directly.

The package is intended for review by P10B-2C-PF-R2 (preflight) before any decision to uncomment COMMIT;.


13. Đ41 Code Hygiene

No persistent tracked source files were created or modified by this task. All artifacts live under /tmp/p10b-2b-fix/ on the VPS — read-only, ephemeral. git status in /opt/incomex shows only a pre-existing modification to scripts/pg-backup.sh unrelated to this task.

STOP.