S178 Fix30 Gen Code Adaptive Report
S178 Fix30 — gen_code adaptive width + sequence align + v_registry_counts insert verify
Date: 2026-04-23
Executor: Codex
Scope: Production PostgreSQL on Contabo /opt/incomex/docker, DB directus, user directus.
Bước 0 — 3 câu Tuyên ngôn
- Vĩnh viễn? Có. Root fix nằm ở toàn bộ
gen_code_*trigger functions: width =GREATEST(_min_width, length(_next::text)), không tăng hardcode từ 3 lên 4/7. - Nhầm được không? Khó nhầm hơn. Sequence được căn bằng actual max từ dữ liệu thật; future nextval không quay lại code đã cấp.
- 100% tự động? Có cho code mới. Trigger tự sinh code theo sequence; không cần operator sửa width khi vượt 999/9999.
Luật đã đọc
.claude/skills/incomex-rules.md: 36 mục, quy trình 8 bước.search_knowledge("operating rules SSOT"): tìm thấyknowledge/dev/ssot/operating-rules.mdtrong context-pack; query đầu bị lỗi transport, retry thành công.search_knowledge("hiến pháp v4.0 constitution"): trả context architecture/constitution và các luật Điều 14/15/16.search_knowledge("Luật Registry 3 chữ số tối thiểu code prefix PREFIX-NNN gen_code"): đọcknowledge/dev/laws/law-02-registry.md— ID duy nhất PREFIX-NNN do hệ thống cấp.search_knowledge("law-01-foundation-principles SSOT tự động DOT dual-trigger metadata code"): đọc Điều 35 DOT Governance v5.2 và related governance context.
1. TRƯỚC KHI SỬA — toàn bộ gen_code_* function body
Evidence query:
SELECT p.oid::regprocedure AS signature, p.proname, p.prosrc
FROM pg_proc p
JOIN pg_namespace n ON n.oid=p.pronamespace
WHERE n.nspname='public' AND p.proname LIKE 'gen_code_%'
ORDER BY p.proname;
Pre-state bodies all used fixed-width lpad(nextval(...)::text, 3, '0') except these fixed-width 4 variants: gen_code_entity_dependencies, gen_code_system_issues, gen_code_workflow_steps.
gen_code_agents: NEW.code := 'AGT' || '-' || lpad(nextval('seq_agents_code')::text, 3, '0');
gen_code_checkpoint_instances: NEW.code := 'CPI' || '-' || lpad(nextval('seq_checkpoint_instances_code')::text, 3, '0');
gen_code_checkpoint_sets: NEW.code := 'CPS' || '-' || lpad(nextval('seq_checkpoint_sets_code')::text, 3, '0');
gen_code_checkpoint_types: NEW.code := 'CP' || '-' || lpad(nextval('seq_checkpoint_types_code')::text, 3, '0');
gen_code_collection_registry: NEW.code := 'COL' || '-' || lpad(nextval('seq_collection_registry_code')::text, 3, '0');
gen_code_dot_tools: NEW.code := 'DOT' || '-' || lpad(nextval('seq_dot_tools_code')::text, 3, '0');
gen_code_entity_dependencies: NEW.code := 'DEP' || '-' || lpad(nextval('seq_entity_dependencies_code')::text, 4, '0');
gen_code_meta_catalog: NEW.code := 'CAT' || '-' || lpad(nextval('seq_meta_catalog_code')::text, 3, '0');
gen_code_modules: NEW.code := 'MOD' || '-' || lpad(nextval('seq_modules_code')::text, 3, '0');
gen_code_system_issues: NEW.code := 'ISS' || '-' || lpad(nextval('seq_system_issues_code')::text, 4, '0');
gen_code_table_proposals: NEW.code := 'TP' || '-' || lpad(nextval('seq_table_proposals_code')::text, 3, '0');
gen_code_table_registry: NEW.table_id := 'TBL' || '-' || lpad(nextval('seq_table_registry_code')::text, 3, '0');
gen_code_tasks: NEW.code := 'TSK' || '-' || lpad(nextval('seq_tasks_code')::text, 3, '0');
gen_code_taxonomy: NEW.code := 'LBL-' || lpad(nextval('seq_taxonomy_code')::text, 3, '0');
gen_code_trigger_registry: NEW.code := 'TRG-' || lpad(nextval('seq_trigger_registry_code')::text, 3, '0');
gen_code_ui_pages: NEW.code := 'PG' || '-' || lpad(nextval('seq_ui_pages_code')::text, 3, '0');
gen_code_workflow_change_requests: NEW.code := 'WCR' || '-' || lpad(nextval('seq_workflow_change_requests_code')::text, 3, '0');
gen_code_workflow_steps: NEW.code := 'ND' || '-' || lpad(nextval('seq_workflow_steps_code')::text, 4, '0');
gen_code_workflows: NEW.process_code := 'WF' || '-' || lpad(nextval('seq_workflows_code')::text, 3, '0');
Preflight INSERT evidence before fix:
BEGIN
WARNING: Birth Gate [meta_catalog]: Code không trùng: Code "CAT-999" đã tồn tại;
ERROR: duplicate key value violates unique constraint "idx_meta_catalog_meta_catalog_code_unique"
DETAIL: Key (code)=(CAT-999) already exists.
2. TRƯỚC KHI SỬA — sequence last_value vs actual max
seq_name|last_value|actual_max|diff
seq_agents_code|10|6|4
seq_checkpoint_instances_code|121|121|0
seq_checkpoint_sets_code|2|2|0
seq_checkpoint_types_code|36|31|5
seq_collection_registry_code|175|175|0
seq_dot_tools_code|308|317|-9
seq_entity_dependencies_code|357|345|12
seq_meta_catalog_code|998|1010|-12
seq_modules_code|4|5|-1
seq_system_issues_code|3912|3912|0
seq_table_proposals_code|1|0|1
seq_table_registry_code|1|0|1
seq_tasks_code|12|17|-5
seq_taxonomy_code|510|506|4
seq_trigger_registry_code|89|86|3
seq_ui_pages_code|38|37|1
seq_workflow_change_requests_code|3|3|0
seq_workflows_code|4|2|2
seq_workflow_steps_code|70|70|0
3. TRƯỚC KHI SỬA — v_registry_counts relation + trigger body
v_registry_counts is not a view in current production; it is a table:
relation | v_registry_counts
relkind | r
kind | table
Columns relevant to TD-S178-21:
id | is_nullable=NO | data_type=integer | default=nextval('v_registry_counts_id_seq'::regclass)
cat_code | nullable YES
entity_type | nullable YES
record_count | default 0
orphan_count | default 0
composition_level | default ''::text
code_column | default ''::text
active_count | default 0
Trigger on meta_catalog:
tgname: trg_auto_sync_registry_counts
table_name: meta_catalog
function_name: fn_auto_sync_v_registry_counts
trigger_def: CREATE TRIGGER trg_auto_sync_registry_counts AFTER INSERT ON public.meta_catalog FOR EACH ROW EXECUTE FUNCTION fn_auto_sync_v_registry_counts()
Function body:
BEGIN
IF NEW.identity_class != 'managed' OR NEW.code IS NULL THEN
RETURN NEW;
END IF;
IF NOT EXISTS (SELECT 1 FROM v_registry_counts WHERE cat_code = NEW.code) THEN
INSERT INTO v_registry_counts (cat_code, entity_type, record_count, orphan_count, composition_level, code_column, prev_count, count_b, count_c, cross_check, active_count)
VALUES (
NEW.code,
COALESCE(NEW.entity_type, ''),
COALESCE(NEW.record_count, 0),
COALESCE(NEW.orphan_count, 0),
COALESCE(NEW.composition_level, ''),
'code',
0,
COALESCE(NEW.record_count, 0),
0,
'NEW',
COALESCE(NEW.active_count, 0)
);
RAISE NOTICE 'Auto-added % to v_registry_counts', NEW.code;
END IF;
RETURN NEW;
END;
Conclusion before fix: TD-S178-21 symptom was not current id NOT NULL view DDL; id has a default. The observed INSERT block was caused by seq_meta_catalog_code generating duplicate CAT-999.
4. SAU KHI SỬA — function body mới
Applied in one production transaction:
CREATE OR REPLACE FUNCTIONfor all 19gen_code_*.ALTER SEQUENCE seq_table_proposals_code MINVALUE 0andseq_table_registry_code MINVALUE 0so empty tables can havelast_value=actual_max=0while next code still starts at 001.setval()for all 19 sequence values to actual max.
Canonical new body pattern:
DECLARE _next bigint; _min_width int := 3;
BEGIN
IF NEW.code IS NULL OR NEW.code = '' THEN
_next := nextval('seq_<name>_code');
NEW.code := '<PREFIX>-' || lpad(_next::text, GREATEST(_min_width, length(_next::text)), '0');
END IF;
RETURN NEW;
END;
Variants retain existing field/prefix:
gen_code_table_registry uses NEW.table_id and prefix TBL-
gen_code_workflows uses NEW.process_code and prefix WF-
gen_code_taxonomy uses btrim(NEW.code) and prefix LBL-
gen_code_trigger_registry uses btrim(NEW.code) and prefix TRG-
Verification output:
VERIFY 1: gen_code fixed-width lpad scan
proname | has_fixed_lpad | has_adaptive_width
gen_code_agents | f | t
gen_code_checkpoint_instances | f | t
gen_code_checkpoint_sets | f | t
gen_code_checkpoint_types | f | t
gen_code_collection_registry | f | t
gen_code_dot_tools | f | t
gen_code_entity_dependencies | f | t
gen_code_meta_catalog | f | t
gen_code_modules | f | t
gen_code_system_issues | f | t
gen_code_table_proposals | f | t
gen_code_table_registry | f | t
gen_code_tasks | f | t
gen_code_taxonomy | f | t
gen_code_trigger_registry | f | t
gen_code_ui_pages | f | t
gen_code_workflow_change_requests | f | t
gen_code_workflow_steps | f | t
gen_code_workflows | f | t
(19 rows)
Sequence after fix:
seq_name|last_value|actual_max|diff
seq_agents_code|6|6|0
seq_checkpoint_instances_code|121|121|0
seq_checkpoint_sets_code|2|2|0
seq_checkpoint_types_code|31|31|0
seq_collection_registry_code|175|175|0
seq_dot_tools_code|317|317|0
seq_entity_dependencies_code|345|345|0
seq_meta_catalog_code|1010|1010|0
seq_modules_code|5|5|0
seq_system_issues_code|3912|3912|0
seq_table_proposals_code|0|0|0
seq_table_registry_code|0|0|0
seq_tasks_code|17|17|0
seq_taxonomy_code|506|506|0
seq_trigger_registry_code|86|86|0
seq_ui_pages_code|37|37|0
seq_workflow_change_requests_code|3|3|0
seq_workflows_code|2|2|0
seq_workflow_steps_code|70|70|0
5. SAU KHI SỬA — test tràn qua 999
Direct meta_catalog insert with setval(999) cannot commit because CAT-1000 already exists under HP NT1. Dry-run before rollback produced duplicate on CAT-1000, proving generation is not truncated.
Clean trigger-level overflow test used a temp table with gen_code_meta_catalog, then reset sequence to actual max before rollback because PostgreSQL sequence changes are non-transactional:
VERIFY 3: overflow through 999 via gen_code_meta_catalog on temp trigger table
BEGIN
CREATE TABLE
WARNING: [TRIGGER-GUARD] DDL detected: CREATE TRIGGER on s178_tmp_meta_code_bi on pg_temp.s178_tmp_meta_code
CREATE TRIGGER
forced_seq
------------
999
(1 row)
INSERT 0 1
generated_code
----------------
CAT-1000
(1 row)
reset_seq
-----------
1010
(1 row)
ROLLBACK
6. SAU KHI SỬA — test INSERT meta_catalog
Actual rollback INSERT into meta_catalog at actual max, then reset sequence before rollback:
VERIFY 4: meta_catalog INSERT rollback at actual max
BEGIN
INSERT 0 1
id | code | name
-----+----------+-------------------------------------
239 | CAT-1011 | S178 Fix30 post-fix insert rollback
(1 row)
reset_seq
-----------
1010
(1 row)
ROLLBACK
Backward compatibility:
VERIFY 5: backward compatibility existing codes
code
--------
CAT-001
CAT-100
CAT-1000
CAT-1010
(4 rows)
7. KẾT LUẬN — TD status
- TD-S178-20 CLOSED. All 19 code sequences aligned to actual max;
diff=0for every checked sequence. - TD-S178-21 CLOSED.
meta_catalogINSERT no longer blocked; currentv_registry_countsis a table withiddefault and trigger insert path works. No view DDL change was necessary because root failure was duplicate generated code. - TD-S178-22 CLOSED. All 19
gen_code_*functions use adaptive width. No fixedlpad(..., 3, ...)orlpad(..., 4, ...)remains.
Remaining TD: _min_width int := 3 is still in function body. This satisfies current Điều 2 minimum-three rule and prompt acceptance, but HP NT13 would be stronger if _min_width came from dot_config. Proposed follow-up TD: move code minimum width to PG config table and use one shared helper function for all code generation.
Git/Repo note
DB production was changed directly through PostgreSQL transaction. /opt/incomex and /opt/incomex/dot were clean before DB work; no source file changed yet. A migration SQL should be committed to both repos if git history must mirror the production DB hotfix.