KB-17F0

P11A Production Schema Inventory — 2026-05-01

25 min read Revision 1
p11atacschema-inventorydieu38production

P11A-Agent — Production Schema Inventory (READ-ONLY)

Date: 2026-05-01 Scope: TAC tables + related infrastructure on production PG (Directus DB, VPS) Mode: READ-ONLY — zero mutation (no DDL/DML, no Directus writes, no git ops)


1. Gate 0 — Machine Identity

Source Value
Local hostname / user Nguyens-MacBook-Air.local / nmhuyen
VPS hostname vmi3080463.contaboserver.net (38.242.240.89)
PG identity directus / directus (database / user)
Container postgres (via docker exec)

Gate passed: SQL inventory executed against production Directus PostgreSQL.


2. Summary Table

TAC tables

table_name columns rows PK FKs Triggers JSONB cols
tac_publication 15 3 id (uuid) 2 0 publication_profile
tac_logical_unit 13 86 id (uuid) 3 (incl self-FK on parent_id) 1 (birth_gate_lu I/U) identity_profile
tac_unit_version 20 86 id (uuid) 3 3 (birth_gate_uv I, enacted_immut U/D, uv_compute_derived I/U) content_profile
tac_publication_member 6 86 id (uuid) 3 2 (pm_enacted_lock I/U/D, pm_consistency I/U)
tac_change_set 11 id (uuid) 2 0
tac_change_set_member 8 id (uuid) 4 0 snapshot_data
tac_birth_gate_config 6 checker_id (text) 0 0
tac_cs_lifecycle_vocab 6 code 0 0
tac_lu_lifecycle_vocab 6 code 0 0
tac_pub_lifecycle_vocab 6 code 0 0
tac_publication_type_vocab 7 code 0 0
tac_review_state_vocab 6 code 0 0
tac_section_type_vocab 11 code 0 0
tac_uv_lifecycle_vocab 6 code 0 0

Totals: 14 TAC tables · 126 columns · 40 constraint rows · 12 trigger rows · 7 functions · 4 JSONB columns · 41 indexes · 14 Directus collections registered.

TAC support functions

fn_tac_birth_gate_lu, fn_tac_birth_gate_uv, fn_tac_enacted_immut, fn_tac_log_checker_issue, fn_tac_pm_consistency, fn_tac_pm_enacted_lock, fn_tac_uv_compute_derived


3. Raw SQL Outputs

BLOCK 1 — TAC columns + types (126 rows)

 tac_birth_gate_config      | checker_id              | text                     | NO  | 
 tac_birth_gate_config      | mode                    | text                     | NO  | 'block'::text
 tac_birth_gate_config      | enabled                 | boolean                  | NO  | true
 tac_birth_gate_config      | rationale               | text                     | YES | 
 tac_birth_gate_config      | created_at              | timestamptz              | NO  | now()
 tac_birth_gate_config      | updated_at              | timestamptz              | NO  | now()

 tac_change_set             | id                      | uuid                     | NO  | gen_random_uuid()
 tac_change_set             | publication_id          | uuid                     | YES | 
 tac_change_set             | scope_description       | text                     | NO  | 
 tac_change_set             | lifecycle_status        | text                     | NO  | 'draft'::text
 tac_change_set             | apr_ref                 | text                     | YES | 
 tac_change_set             | owner                   | text                     | NO  | 
 tac_change_set             | created_at              | timestamptz              | NO  | now()
 tac_change_set             | updated_at              | timestamptz              | NO  | now()
 tac_change_set             | submitted_at            | timestamptz              | YES | 
 tac_change_set             | enacted_at              | timestamptz              | YES | 

 tac_change_set_member      | id                      | uuid                     | NO  | gen_random_uuid()
 tac_change_set_member      | change_set_id           | uuid                     | NO  |
 tac_change_set_member      | logical_unit_id         | uuid                     | NO  |
 tac_change_set_member      | change_type             | text                     | NO  |
 tac_change_set_member      | old_version_id          | uuid                     | YES |
 tac_change_set_member      | new_version_id          | uuid                     | YES |
 tac_change_set_member      | snapshot_data           | jsonb                    | YES |
 tac_change_set_member      | created_at              | timestamptz              | NO  | now()

 tac_cs_lifecycle_vocab     | code,name,description,sort_order,created_at,updated_at  (PK on code)

 tac_logical_unit           | id                      | uuid                     | NO  | gen_random_uuid()
 tac_logical_unit           | canonical_address       | text                     | NO  |    (UNIQUE)
 tac_logical_unit           | doc_code                | text                     | NO  |
 tac_logical_unit           | parent_id               | uuid                     | YES |    (self-FK)
 tac_logical_unit           | sort_order              | integer                  | NO  | 0
 tac_logical_unit           | section_type            | text                     | NO  |    (FK -> tac_section_type_vocab.code)
 tac_logical_unit           | section_code            | text                     | YES |
 tac_logical_unit           | owner                   | text                     | NO  |
 tac_logical_unit           | identity_profile        | jsonb                    | NO  | '{}'::jsonb
 tac_logical_unit           | tier                    | text                     | YES |
 tac_logical_unit           | lifecycle_status        | text                     | NO  | 'draft_only'::text  (FK -> tac_lu_lifecycle_vocab.code)
 tac_logical_unit           | created_at,updated_at   | timestamptz              | NO  | now()

 tac_lu_lifecycle_vocab     | code,name,description,sort_order,created_at,updated_at  (PK on code)
 tac_pub_lifecycle_vocab    | (same shape as lu_lifecycle_vocab)

 tac_publication            | id                      | uuid                     | NO  | gen_random_uuid()
 tac_publication            | doc_code                | text                     | NO  |    (UNIQUE w/ version)
 tac_publication            | version                 | text                     | NO  |
 tac_publication            | publication_type        | text                     | NO  |    (FK -> tac_publication_type_vocab.code)
 tac_publication            | name                    | text                     | NO  |
 tac_publication            | owner                   | text                     | NO  |
 tac_publication            | description             | text                     | YES |
 tac_publication            | lifecycle_status        | text                     | NO  | 'proposed'::text  (FK -> tac_pub_lifecycle_vocab.code)
 tac_publication            | enacted_at              | timestamptz              | YES |
 tac_publication            | council_score           | numeric                  | YES |
 tac_publication            | approved_by             | text                     | YES |
 tac_publication            | risk_tier               | text                     | NO  | 'medium'::text
 tac_publication            | publication_profile     | jsonb                    | NO  | '{}'::jsonb
 tac_publication            | created_at,updated_at   | timestamptz              | NO  | now()

 tac_publication_member     | id                      | uuid                     | NO  | gen_random_uuid()
 tac_publication_member     | publication_id          | uuid                     | NO  |    (FK)
 tac_publication_member     | logical_unit_id         | uuid                     | NO  |    (FK; UNIQUE w/ publication_id)
 tac_publication_member     | unit_version_id         | uuid                     | NO  |    (FK)
 tac_publication_member     | render_order            | integer                  | NO  | 0
 tac_publication_member     | created_at              | timestamptz              | NO  | now()

 tac_publication_type_vocab | code,name,description,lifecycle_status('active'),default_risk_tier('medium'),created_at,updated_at
 tac_review_state_vocab     | code,name,description,sort_order,created_at,updated_at

 tac_section_type_vocab     | code                    | text                     | NO  |
 tac_section_type_vocab     | name,description,lifecycle_status('active'),owner
 tac_section_type_vocab     | soft_limit_words        | integer                  | NO  | 500
 tac_section_type_vocab     | hard_limit_words        | integer                  | NO  | 1500
 tac_section_type_vocab     | description_required    | boolean                  | NO  | true
 tac_section_type_vocab     | body_required           | boolean                  | NO  | true
 tac_section_type_vocab     | created_at,updated_at

 tac_unit_version           | id                      | uuid                     | NO  | gen_random_uuid()
 tac_unit_version           | logical_unit_id         | uuid                     | NO  |    (FK; UNIQUE w/ version_number)
 tac_unit_version           | version_number          | integer                  | NO  | 1
 tac_unit_version           | title                   | text                     | NO  |
 tac_unit_version           | body                    | text                     | YES |
 tac_unit_version           | description             | text                     | YES |
 tac_unit_version           | content_hash            | text                     | YES |
 tac_unit_version           | lifecycle_status        | text                     | NO  | 'draft'::text  (FK -> tac_uv_lifecycle_vocab.code)
 tac_unit_version           | review_state            | text                     | NO  | 'unreviewed'::text  (FK -> tac_review_state_vocab.code)
 tac_unit_version           | length_flag             | text                     | NO  | 'normal'::text
 tac_unit_version           | length_exception_reason | text                     | YES |
 tac_unit_version           | content_profile         | jsonb                    | NO  | '{}'::jsonb
 tac_unit_version           | editor                  | text                     | YES |
 tac_unit_version           | provenance              | text                     | NO  | 'PROV-AI'::text
 tac_unit_version           | vector_sync_status      | text                     | NO  | 'pending'::text
 tac_unit_version           | vector_synced_at        | timestamptz              | YES |
 tac_unit_version           | vector_chunk_count      | integer                  | NO  | 0
 tac_unit_version           | created_at,updated_at,enacted_at

 tac_uv_lifecycle_vocab     | code,name,description,sort_order,created_at,updated_at  (PK on code)

BLOCK 2 — TAC constraints (40 rows)

tac_birth_gate_config      | PK on (checker_id)
tac_change_set             | PK(id) | FK publication_id -> tac_publication.id | FK lifecycle_status -> tac_cs_lifecycle_vocab.code
tac_change_set_member      | PK(id) | UNIQUE(change_set_id, logical_unit_id)
                           | FK change_set_id -> tac_change_set.id
                           | FK new_version_id -> tac_unit_version.id
                           | FK old_version_id -> tac_unit_version.id
                           | FK logical_unit_id -> tac_logical_unit.id
tac_cs_lifecycle_vocab     | PK(code)
tac_logical_unit           | PK(id) | UNIQUE(canonical_address)
                           | FK lifecycle_status -> tac_lu_lifecycle_vocab.code
                           | FK section_type    -> tac_section_type_vocab.code
                           | FK parent_id       -> tac_logical_unit.id (self)
tac_lu_lifecycle_vocab     | PK(code)
tac_pub_lifecycle_vocab    | PK(code)
tac_publication            | PK(id) | UNIQUE(doc_code, version)
                           | FK lifecycle_status -> tac_pub_lifecycle_vocab.code
                           | FK publication_type -> tac_publication_type_vocab.code
tac_publication_member     | PK(id) | UNIQUE(publication_id, logical_unit_id)
                           | FK publication_id  -> tac_publication.id
                           | FK logical_unit_id -> tac_logical_unit.id
                           | FK unit_version_id -> tac_unit_version.id
tac_publication_type_vocab | PK(code)
tac_review_state_vocab     | PK(code)
tac_section_type_vocab     | PK(code)
tac_unit_version           | PK(id) | UNIQUE(logical_unit_id, version_number)
                           | FK review_state    -> tac_review_state_vocab.code
                           | FK lifecycle_status-> tac_uv_lifecycle_vocab.code
                           | FK logical_unit_id -> tac_logical_unit.id
tac_uv_lifecycle_vocab     | PK(code)

BLOCK 3 — TAC triggers (12 rows)

trg_tac_birth_gate_lu      | tac_logical_unit       | INSERT/UPDATE | BEFORE | fn_tac_birth_gate_lu()
trg_tac_birth_gate_uv      | tac_unit_version       | INSERT        | BEFORE | fn_tac_birth_gate_uv()
trg_tac_enacted_immut      | tac_unit_version       | UPDATE/DELETE | BEFORE | fn_tac_enacted_immut()
trg_tac_uv_compute_derived | tac_unit_version       | INSERT/UPDATE | BEFORE | fn_tac_uv_compute_derived()
trg_tac_pm_enacted_lock    | tac_publication_member | I/U/D         | BEFORE | fn_tac_pm_enacted_lock()
trg_tac_pm_consistency     | tac_publication_member | INSERT/UPDATE | BEFORE | fn_tac_pm_consistency()

BLOCK 4 — TAC functions (7)

fn_tac_birth_gate_lu
fn_tac_birth_gate_uv
fn_tac_enacted_immut
fn_tac_log_checker_issue
fn_tac_pm_consistency
fn_tac_pm_enacted_lock
fn_tac_uv_compute_derived

BLOCK 5 — Relation/edge/metadata/vector/KG/label/topic/profile tables (23)

directus_relations            entity_labels                governance_relations
kg_acl_config                 kg_auto_approve_rules        kg_constraint_config
kg_evolution_latest           kg_priority_templates        kg_quality_issues
kg_quality_latest             kg_quality_log               kg_signal_config
kg_source_authority           kg_thresholds                knowledge_documents
label_rules                   normative_relations          universal_edges
universal_rule_registry       universal_rule_run_results   universal_rule_runs
universal_rule_violations     workflow_step_relations

BLOCK 6 — Columns of relation/edge/universal tables (128 rows)

Key columns (excerpt):

governance_relations: id, source_type, source_code, target_type, target_code,
                      relation_type, is_contract, discovery_source, enforcement_type,
                      enforcement_ref, status, last_verified_at

normative_relations: id, source_code, target_code, relation_type, status

universal_edges: id, source_collection, source_id, source_code, source_composition_level,
                 target_collection, target_id, target_code, target_composition_level,
                 edge_type, edge_subtype, weight, source_info, is_auto_managed,
                 symmetry_group_id, metadata(jsonb), valid_from, valid_to, status,
                 _dot_origin, date_created, date_updated, confidence,
                 valid_time(tstzrange), provenance(jsonb)

universal_rule_registry: id, rule_number, rule_name, description, measurement_type,
                         measurement_source, severity, execution_order, is_active, is_blocking
universal_rule_runs:     id, started_at, ended_at, triggered_by, status,
                         total_violations, delta_vs_previous
universal_rule_run_results: run_id, rule_number, violations_found, severity, id
universal_rule_violations:  id, rule_number, entity_collection, entity_id, entity_code,
                            violation_detail, severity, violation_hash, evidence(jsonb),
                            detected_at, resolved_at, resolved_by, status

workflow_step_relations: id, workflow_id, from_step_id, to_step_id, relation_type,
                         condition_expression, label, sort_order

BLOCK 7 — Vocab / config infra (8)

tac_birth_gate_config
tac_cs_lifecycle_vocab
tac_lu_lifecycle_vocab
tac_pub_lifecycle_vocab
tac_publication_type_vocab
tac_review_state_vocab
tac_section_type_vocab
tac_uv_lifecycle_vocab

BLOCK 8 — JSONB columns on TAC (4)

tac_change_set_member.snapshot_data
tac_logical_unit.identity_profile
tac_publication.publication_profile
tac_unit_version.content_profile

BLOCK 9 — Row counts

tac_publication        |  3
tac_logical_unit       | 86
tac_unit_version       | 86
tac_publication_member | 86

BLOCK 10 — Indexes on TAC (41)

tac_birth_gate_config       | tac_birth_gate_config_pkey  (checker_id)
tac_change_set              | idx_tac_cs_lifecycle (lifecycle_status)
                            | idx_tac_cs_lifecycle_enacted (id WHERE lifecycle_status='enacted')
                            | idx_tac_cs_publication (publication_id WHERE NOT NULL)
                            | tac_change_set_pkey
tac_change_set_member       | idx_tac_csm_change_set, idx_tac_csm_logical_unit
                            | UNIQUE(change_set_id, logical_unit_id), pkey
tac_logical_unit            | idx_tac_lu_doc_code
                            | idx_tac_lu_identity_profile_gin (GIN on jsonb)
                            | idx_tac_lu_lifecycle
                            | idx_tac_lu_parent (parent_id WHERE NOT NULL)
                            | idx_tac_lu_section_type
                            | UNIQUE(canonical_address), pkey
tac_publication             | idx_tac_pub_doc_code, idx_tac_pub_lifecycle
                            | idx_tac_pub_publication_profile_gin (GIN on jsonb)
                            | idx_tac_pub_publication_type
                            | UNIQUE(doc_code, version), pkey
tac_publication_member      | idx_tac_pm_logical_unit, idx_tac_pm_publication, idx_tac_pm_unit_version
                            | UNIQUE(publication_id, logical_unit_id), pkey
tac_unit_version            | idx_tac_uv_content_profile_gin (GIN on jsonb)
                            | idx_tac_uv_lifecycle, idx_tac_uv_logical_unit, idx_tac_uv_review_state
                            | idx_tac_uv_vector_sync (WHERE vector_sync_status <> 'synced')
                            | UNIQUE(logical_unit_id, version_number), pkey
(all 8 vocab tables: pkey on code only)

BLOCK 11 — directus_collections for TAC (14)

tac_birth_gate_config       | TAC config: birth gate rules
tac_change_set              | TAC core: change set
tac_change_set_member       | TAC member: change set membership
tac_cs_lifecycle_vocab      | TAC vocab: change set lifecycle states
tac_logical_unit            | TAC core: logical unit
tac_lu_lifecycle_vocab      | TAC vocab: logical unit lifecycle states
tac_publication             | TAC core: publication
tac_publication_member      | TAC member: publication membership
tac_publication_type_vocab  | TAC vocab: publication types
tac_pub_lifecycle_vocab     | TAC vocab: publication lifecycle states
tac_review_state_vocab      | TAC vocab: review states
tac_section_type_vocab      | TAC vocab: section types
tac_unit_version            | TAC core: unit version
tac_uv_lifecycle_vocab      | TAC vocab: unit version lifecycle states

All collections: singleton=false, sort_field empty, accountability empty.

BLOCK 12 — directus_fields for TAC

0 rows. No field-level metadata registered in directus_fields for any tac_* collection.

BLOCK 13 — directus_relations for TAC

0 rows. No M2O/O2M/junction relations are declared in directus_relations for tac_*. (FKs exist at PG layer; Directus UI relation metadata is missing.)

BLOCK 14 — Qdrant/vector/embed tables

0 rows. No table matches %qdrant%, %vector%, or %embed% in public.

BLOCK 15 — universal_edges aggregate

total | source_collections | target_collections
2199  |        22          |        10

4. Key Findings

  1. TAC core layer is structurally complete. 4 core tables (publication, logical_unit, unit_version, publication_member) + 2 change-set tables + 8 vocab tables + 1 birth-gate config = 14 tables, all registered as Directus collections.
  2. Population state is asymmetric: 1 publication active (3 rows total) maps to a fully populated 86-unit corpus (logical_unit / unit_version / publication_member each = 86, indicating 1:1:1 enacted publication membership).
  3. Governance enforcement at PG layer is robust:
    • 6 distinct trigger functions (fn_tac_*) covering birth gates (LU + UV), enacted immutability (UV), pub-member consistency, pm enacted lock, and UV derived-field computation.
    • All TAC FKs reference vocab code PKs — vocab-driven enum behaviour.
    • tac_publication_member has triple-FK (publication_id, logical_unit_id, unit_version_id) plus (publication_id, logical_unit_id) UNIQUE — matches §38 "one LU per publication" rule.
    • tac_unit_version enforces (logical_unit_id, version_number) UNIQUE — matches version monotonicity.
  4. JSONB profile columns are GIN-indexed:
    • tac_logical_unit.identity_profile, tac_publication.publication_profile, tac_unit_version.content_profile — all have gin indexes. tac_change_set_member.snapshot_data has no GIN index (immutable snapshot, no query path).
  5. Vector sync hook present, infrastructure absent:
    • tac_unit_version has vector_sync_status, vector_synced_at, vector_chunk_count columns + partial index on vector_sync_status <> 'synced'.
    • No *qdrant*, *vector*, *embed* table exists in the public schema (BLOCK 14 = 0 rows). Vector materialization is expected to live outside PG (Qdrant) — TAC tracks state only.
  6. Directus UI metadata for TAC is missing:
    • directus_fields has 0 rows for tac_* collections.
    • directus_relations has 0 rows for tac_* collections.
    • Collections are registered (BLOCK 11) but all field interfaces, special types, and Directus-level M2O links are unconfigured. Directus admin UI for TAC will render generic columns without relation pickers, JSON editors, or proper field interfaces. This is a known gap requiring a registration pass.
  7. Universal edges layer exists alongside TACuniversal_edges (2199 rows, 22 source / 10 target collections) plus governance_relations and normative_relations. None reference tac_* collections at the column level (verified by edge schema — collection names are runtime values; sample inspection deferred).
  8. Birth-gate config is checker-keyed: PK on checker_id (text), with mode default 'block' and enabled default true. Single-row per checker; populated externally by fn_tac_log_checker_issue.

5. Unknowns / Limitations

  • Composite FK detail for change_set_member(change_set_id, logical_unit_id) UNIQUE is shown as 2 separate rows in information_schema.table_constraints (one per column). Confirmed composite via BLOCK 10 indexdef. No multi-column FK present in TAC.
  • tac_change_set / tac_change_set_member row counts not fetched (BLOCK 9 only counted the 4 core tables per spec).
  • Trigger function bodies not dumped — only names/timing. Full source via pg_get_functiondef(oid) deferred to a follow-up if required.
  • Vocab seed contents not enumerated — only schema. To inspect actual valid values for lifecycle_status/section_type, a separate SELECT * FROM tac_*_vocab pass is needed.
  • universal_edges collection-name distribution — aggregate only; no sample of which 22 source / 10 target collections are involved (no per-collection breakdown). If TAC references inside universal_edges.source_collection / target_collection need verification, run: SELECT DISTINCT source_collection FROM universal_edges WHERE source_collection LIKE 'tac_%' OR target_collection LIKE 'tac_%';.
  • directus_collections for TAC has empty accountability — semantics (NULL vs empty string) not disambiguated; both render the same in the truncated psql view.
  • No write attempts were made; all mutation observations are inferred from schema (defaults, NOT NULL, FK targets), not behavior.
Back to Knowledge Hub knowledge/dev/laws/dieu38-trien-khai/reports/p11a-agent-production-schema-inventory-2026-05-01.md