KB-52D7 rev 6

18b-P1 — Field Creation + Metadata Seed + PG Enforcement (Rev 6)

11 min read Revision 6

18b-P1 — Field Creation + Metadata Seed + PG Enforcement

Controlling: 18a rev4 + 18b rev3 + P0 + P1a + GPT decisions Rev 6: Fix Directus spec — inspect existing field template trước khi viết spec. Thêm tất cả boolean meta fields.


★ Anti-hardcode note

Live row counts = run-local baseline. Invariant tĩnh (fn hash) = hardcode OK.


Bước 0: Đọc controlling docs

Đọc trên KB: 18a rev4, 18b rev3, P0, P1a, GPT P1a decision. Xác nhận.


Bước 1: Baseline checks

SELECT column_name FROM information_schema.columns
WHERE table_name = 'collection_registry'
  AND column_name IN ('birth_code_strategy','birth_code_column','birth_identity_source');
-- 0 → OK. > 0 → STOP.

SELECT md5(pg_get_functiondef('fn_birth_registry_auto'::regproc)) AS fn_hash;
-- Must = 1f729b3571a74963089bb3ef388217f3.

SELECT count(*) AS trigger_count_baseline FROM pg_trigger
WHERE tgname LIKE 'trg_birth_%' AND NOT tgisinternal;

SELECT count(*) AS birth_count_baseline FROM birth_registry;

Bước 2: Env + path

set -a; . /opt/incomex/.env.local; set +a
. /opt/incomex/dot/config/environment.sh
command -v dot-schema-apply || ls -la /opt/incomex/dot/bin/dot-schema-apply

Bước 3: DOT-063 — Create 3 fields

3-pre: ★ Rev 6 — Inspect existing field template

Trước khi viết spec, inspect meta của 1 field có sẵn trên collection_registry để biết tất cả required fields:

SELECT field, meta FROM directus_fields
WHERE collection = 'collection_registry' AND field = 'collection_name';

Hoặc qua bash/curl:

# Lấy 1 field hiện có để xem structure đầy đủ
curl -s -H "Authorization: Bearer $DIRECTUS_TOKEN" \
  "$DIRECTUS_URL/fields/collection_registry/collection_name" | python3 -m json.tool

Agent PHẢI đọc response và list tất cả boolean fields trong meta object. Thường gồm: readonly, hidden, required, searchable, sortable, filterable, và có thể thêm.

Dùng danh sách này để build spec. Tất cả boolean meta fields = false cho 3 fields mới.

3a: Write spec JSON (★ Rev 6: đầy đủ boolean meta)

mkdir -p /opt/incomex/dot/specs

# Overwrite nếu tồn tại từ run trước

Tạo /opt/incomex/dot/specs/collection_registry_birth_identity_fields.json:

★ Agent PHẢI include TẤT CẢ boolean meta fields phát hiện ở 3-pre. Ví dụ (nếu Directus yêu cầu readonly + hidden + required + searchable + sortable + filterable):

{
  "collection": "collection_registry",
  "fields": [
    {
      "field": "birth_code_strategy",
      "type": "string",
      "schema": {"is_nullable": true},
      "meta": {
        "interface": "select-dropdown",
        "readonly": false,
        "hidden": false,
        "required": false,
        "searchable": false,
        "sortable": false,
        "filterable": false,
        "options": {"choices": [
          {"text": "column", "value": "column"},
          {"text": "synthetic_id", "value": "synthetic_id"},
          {"text": "legacy_id_single_colon", "value": "legacy_id_single_colon"},
          {"text": "subordinate", "value": "subordinate"},
          {"text": "disabled", "value": "disabled"},
          {"text": "unclassified", "value": "unclassified"}
        ]},
        "display": "raw",
        "note": "Birth identity strategy per 18a rev4. Đ4/Đ36."
      }
    },
    {
      "field": "birth_code_column",
      "type": "string",
      "schema": {"is_nullable": true},
      "meta": {
        "interface": "input",
        "readonly": false,
        "hidden": false,
        "required": false,
        "searchable": false,
        "sortable": false,
        "filterable": false,
        "display": "raw",
        "note": "Column name for entity_code. NULL for synthetic_id/subordinate/disabled."
      }
    },
    {
      "field": "birth_identity_source",
      "type": "string",
      "schema": {"is_nullable": true},
      "meta": {
        "interface": "select-dropdown",
        "readonly": false,
        "hidden": false,
        "required": false,
        "searchable": false,
        "sortable": false,
        "filterable": false,
        "options": {"choices": [
          {"text": "inferred_from_existing_trigger", "value": "inferred_from_existing_trigger"},
          {"text": "manual", "value": "manual"},
          {"text": "migration_seed", "value": "migration_seed"},
          {"text": "system_default", "value": "system_default"},
          {"text": "unclassified", "value": "unclassified"}
        ]},
        "display": "raw",
        "note": "How classification was determined. Đ36 provenance."
      }
    }
  ]
}

★ Nếu 3-pre phát hiện boolean fields KHÁC ngoài 6 fields trên → agent PHẢI thêm vào spec trước khi chạy.

md5sum /opt/incomex/dot/specs/collection_registry_birth_identity_fields.json

3b–3c: Dry-run + live

dot-schema-apply /opt/incomex/dot/specs/collection_registry_birth_identity_fields.json --cloud --dry-run
dot-schema-apply /opt/incomex/dot/specs/collection_registry_birth_identity_fields.json --cloud
# Fail → verify no partial state → STOP + partial report.

3d: Verify

SELECT column_name, data_type, is_nullable FROM information_schema.columns
WHERE table_name = 'collection_registry'
  AND column_name IN ('birth_code_strategy','birth_code_column','birth_identity_source');
-- MUST 3. < 3 → STOP.

SELECT field, collection, interface FROM directus_fields
WHERE collection = 'collection_registry'
  AND field IN ('birth_code_strategy','birth_code_column','birth_identity_source');
-- MUST 3. < 3 → STOP.

SELECT md5(pg_get_functiondef('fn_birth_registry_auto'::regproc));
-- Must = 1f729b3571a74963089bb3ef388217f3.

Bước 4: Seed metadata

SELECT count(*) AS birth_count_before_seed FROM birth_registry;
BEGIN;

UPDATE collection_registry SET birth_code_strategy='column', birth_code_column='code', birth_identity_source='inferred_from_existing_trigger'
WHERE collection_name IN ('agents','approval_requests','checkpoint_sets','checkpoint_types','collection_registry','dot_tools','entity_dependencies','entity_species','governance_registry','meta_catalog','modules','normative_registry','pivot_definitions','system_issues','tasks','taxonomy','taxonomy_facets','ui_pages','workflow_change_requests','workflow_steps');

UPDATE collection_registry SET birth_code_strategy='column', birth_code_column='law_code', birth_identity_source='inferred_from_existing_trigger' WHERE collection_name='law_catalog';
UPDATE collection_registry SET birth_code_strategy='column', birth_code_column='measurement_id', birth_identity_source='inferred_from_existing_trigger' WHERE collection_name='measurement_registry';
UPDATE collection_registry SET birth_code_strategy='column', birth_code_column='table_id', birth_identity_source='inferred_from_existing_trigger' WHERE collection_name='table_registry';
UPDATE collection_registry SET birth_code_strategy='column', birth_code_column='pivot_code', birth_identity_source='inferred_from_existing_trigger' WHERE collection_name='pivot_results';
UPDATE collection_registry SET birth_code_strategy='column', birth_code_column='process_code', birth_identity_source='inferred_from_existing_trigger' WHERE collection_name='workflows';

UPDATE collection_registry SET birth_code_strategy='legacy_id_single_colon', birth_code_column=NULL, birth_identity_source='inferred_from_existing_trigger'
WHERE collection_name IN ('law_jurisdiction','governance_relations','law_dot_enforcement');

UPDATE collection_registry SET birth_code_strategy='synthetic_id', birth_code_column=NULL, birth_identity_source='manual' WHERE collection_name='information_unit';
UPDATE collection_registry SET birth_code_strategy='subordinate', birth_code_column=NULL, birth_identity_source='manual' WHERE collection_name='unit_version';

UPDATE collection_registry SET birth_code_strategy='disabled', birth_identity_source='system_default'
WHERE governance_role = 'excluded' AND (birth_code_strategy IS NULL OR birth_code_strategy = 'unclassified');

UPDATE collection_registry SET birth_code_strategy='disabled', birth_identity_source='system_default'
WHERE collection_name IN ('v_registry_counts','v_registry_summary','birth_registry')
  AND (birth_code_strategy IS NULL OR birth_code_strategy != 'disabled');

UPDATE collection_registry SET birth_code_strategy='unclassified' WHERE birth_code_strategy IS NULL;
UPDATE collection_registry SET birth_identity_source='unclassified' WHERE birth_identity_source IS NULL;

-- VERIFY BEFORE COMMIT
SELECT birth_code_strategy, count(*) FROM collection_registry GROUP BY 1 ORDER BY 1;
-- column=25, disabled=63, legacy_id_single_colon=3, subordinate=1, synthetic_id=1, unclassified=73. Total=166.
SELECT count(*) FROM collection_registry WHERE birth_code_strategy IS NULL;
SELECT count(*) FROM collection_registry WHERE birth_identity_source IS NULL;
-- Both = 0.
SELECT md5(pg_get_functiondef('fn_birth_registry_auto'::regproc));
SELECT count(*) FROM pg_trigger WHERE tgname LIKE 'trg_birth_%' AND NOT tgisinternal;
SELECT count(*) AS birth_count_after_seed FROM birth_registry;
-- Any mismatch → ROLLBACK + STOP.
COMMIT;

Bước 5: PG enforcement

SELECT count(*) AS birth_count_before_constraints FROM birth_registry;
BEGIN;

ALTER TABLE collection_registry ALTER COLUMN birth_code_strategy SET DEFAULT 'unclassified';
ALTER TABLE collection_registry ALTER COLUMN birth_identity_source SET DEFAULT 'unclassified';
ALTER TABLE collection_registry ALTER COLUMN birth_code_strategy SET NOT NULL;
ALTER TABLE collection_registry ALTER COLUMN birth_identity_source SET NOT NULL;

ALTER TABLE collection_registry ADD CONSTRAINT collection_registry_birth_code_strategy_check
  CHECK (birth_code_strategy IN ('column','synthetic_id','legacy_id_single_colon','subordinate','disabled','unclassified'));
ALTER TABLE collection_registry ADD CONSTRAINT collection_registry_birth_identity_source_check
  CHECK (birth_identity_source IN ('inferred_from_existing_trigger','manual','migration_seed','system_default','unclassified'));

-- VERIFY BEFORE COMMIT
SELECT conname, contype, pg_get_constraintdef(oid) FROM pg_constraint
WHERE conrelid = 'collection_registry'::regclass AND conname LIKE '%birth%';
SELECT column_name, is_nullable, column_default FROM information_schema.columns
WHERE table_name = 'collection_registry' AND column_name IN ('birth_code_strategy','birth_code_column','birth_identity_source');
SELECT md5(pg_get_functiondef('fn_birth_registry_auto'::regproc));
SELECT count(*) AS birth_count_after_constraints FROM birth_registry;
-- Any mismatch → ROLLBACK + STOP.
COMMIT;

Bước 6–8: Final safety, report, HARD STOP

Report: reports/dot119-repair-p1-metadata-ddl-seed-report.md. Bất kỳ STOP → partial report. No DOT-119, no script, no fn edit, no IU, no Pack 2B.


18b-P1 rev 6 | 2026-05-05 | Opus 4.6 | Inspect existing field template trước spec. Tất cả boolean meta fields.