18b-P1 — Field Creation + Metadata Seed + PG Enforcement (Rev 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.