P11A Production Schema Inventory — 2026-05-01
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
- 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. - 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).
- 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
codePKs — vocab-driven enum behaviour. tac_publication_memberhas 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_versionenforces(logical_unit_id, version_number)UNIQUE — matches version monotonicity.
- 6 distinct trigger functions (
- JSONB profile columns are GIN-indexed:
tac_logical_unit.identity_profile,tac_publication.publication_profile,tac_unit_version.content_profile— all haveginindexes.tac_change_set_member.snapshot_datahas no GIN index (immutable snapshot, no query path).
- Vector sync hook present, infrastructure absent:
tac_unit_versionhasvector_sync_status,vector_synced_at,vector_chunk_countcolumns + partial index onvector_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.
- Directus UI metadata for TAC is missing:
directus_fieldshas 0 rows fortac_*collections.directus_relationshas 0 rows fortac_*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.
- Universal edges layer exists alongside TAC —
universal_edges(2199 rows, 22 source / 10 target collections) plusgovernance_relationsandnormative_relations. None referencetac_*collections at the column level (verified by edge schema — collection names are runtime values; sample inspection deferred). - Birth-gate config is checker-keyed: PK on
checker_id(text), withmodedefault'block'andenableddefaulttrue. Single-row per checker; populated externally byfn_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 ininformation_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_memberrow 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 separateSELECT * FROM tac_*_vocabpass is needed. universal_edgescollection-name distribution — aggregate only; no sample of which 22 source / 10 target collections are involved (no per-collection breakdown). If TAC references insideuniversal_edges.source_collection/target_collectionneed verification, run:SELECT DISTINCT source_collection FROM universal_edges WHERE source_collection LIKE 'tac_%' OR target_collection LIKE 'tac_%';.directus_collectionsfor TAC has emptyaccountability— 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.