KB-7204 rev 2

P3D Pack 1 — Read-Only Inventory Prompt

11 min read Revision 2
p3dpack1inventoryreadonlypromptgpt-hardened

P3D Pack 1 — Read-Only Inventory Prompt

Date: 2026-05-10 Status: PROMPT v2 — GPT hardened, READY_FOR_AGENT_DISPATCH Scope: Read-only PG inventory — compare TAC schema vs native IU schema Hard boundary: NO MUTATION. Read-only queries only. Report path: knowledge/dev/laws/dieu44-trien-khai/reports/p3d-pack1-readonly-inventory-report.md

Mission

Run a read-only inventory to verify the live schema before Pack 1 design is approved for any implementation prompt. This inventory must not mutate DB, Directus, Nuxt, DOT, or files except the KB report.

Hard boundaries

  • No DDL.
  • No INSERT/UPDATE/DELETE.
  • No migration.
  • No table/view creation.
  • No function/trigger/index/permission changes.
  • No Nuxt code.
  • No Directus mutation except KB report write.
  • No DOT-119 execution/rewrite.
  • Do not direct-write information_unit or unit_version.

Shell template

#!/usr/bin/env bash
set -uo pipefail

CONTAINER="${PG_CONTAINER:-postgres}"
DB="${PG_DB:-directus}"
DBUSER="${PG_USER:-directus}"
PSQL=(docker exec -i "$CONTAINER" psql -U "$DBUSER" -d "$DB" -v ON_ERROR_STOP=1)
TS=$(date +%Y%m%d-%H%M%S)
LOG="/tmp/p3d-pack1-inventory-${TS}.log"
FAIL=0
WARN=0

exec > >(tee -a "$LOG") 2>&1

echo "# P3D Pack 1 Read-Only Inventory"
echo "timestamp=$TS"
echo "no_mutation=true"
echo

run_sql() {
  local label="$1"
  local sql="$2"
  echo "--- ${label} ---"
  if ! "${PSQL[@]}" -P pager=off -c "$sql"; then
    echo "${label}=FAIL"
    FAIL=$((FAIL+1))
  fi
  echo
}

run_sql_file() {
  local label="$1"
  local sql="$2"
  echo "--- ${label} ---"
  if ! "${PSQL[@]}" -P pager=off <<SQL
${sql}
SQL
  then
    echo "${label}=FAIL"
    FAIL=$((FAIL+1))
  fi
  echo
}

# S0 — table existence first, because later queries depend on it
run_sql "S0_TABLE_EXISTENCE" "
SELECT relname AS table_name, relkind
FROM pg_class
WHERE relnamespace='public'::regnamespace
  AND relname IN (
    'tac_publication','tac_logical_unit','tac_unit_version','tac_publication_member',
    'information_unit','unit_version'
  )
ORDER BY relname;
"

# S1 — TAC logical unit
run_sql "S1_TAC_LOGICAL_UNIT_COLUMNS" "
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema='public' AND table_name='tac_logical_unit'
ORDER BY ordinal_position;
"
run_sql "S1B_TAC_LOGICAL_UNIT_COUNT_SAMPLE" "
SELECT count(*) AS total FROM public.tac_logical_unit;
SELECT id, canonical_address,
       CASE WHEN EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='tac_logical_unit' AND column_name='section_type') THEN section_type::text ELSE NULL END AS section_type,
       lifecycle_status,
       CASE WHEN EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='tac_logical_unit' AND column_name='parent_id') THEN (parent_id IS NOT NULL)::text ELSE NULL END AS has_parent
FROM public.tac_logical_unit
ORDER BY canonical_address
LIMIT 5;
"

# S2 — TAC unit version
run_sql "S2_TAC_UNIT_VERSION_COLUMNS" "
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema='public' AND table_name='tac_unit_version'
ORDER BY ordinal_position;
"
run_sql "S2B_TAC_UNIT_VERSION_COUNTS" "
SELECT count(*) AS total FROM public.tac_unit_version;
SELECT lifecycle_status, count(*) FROM public.tac_unit_version GROUP BY lifecycle_status ORDER BY lifecycle_status;
SELECT count(*) AS hash_mismatch_count FROM public.tac_unit_version WHERE content_hash IS DISTINCT FROM md5(body);
"

# S3 — publication
run_sql "S3_TAC_PUBLICATION_COLUMNS" "
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema='public' AND table_name='tac_publication'
ORDER BY ordinal_position;
"
run_sql "S3B_TAC_PUBLICATION_ROWS" "
SELECT * FROM public.tac_publication ORDER BY 1 LIMIT 20;
"

# S4 — publication member and render order
run_sql "S4_TAC_PUBLICATION_MEMBER_COLUMNS" "
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema='public' AND table_name='tac_publication_member'
ORDER BY ordinal_position;
"
run_sql "S4B_RENDER_ORDER_SUMMARY" "
SELECT publication_id, count(*) AS members, min(render_order) AS min_ro, max(render_order) AS max_ro,
       count(DISTINCT render_order) AS distinct_render_orders
FROM public.tac_publication_member
GROUP BY publication_id
ORDER BY publication_id;
"

# S5 — native IU
run_sql "S5_INFORMATION_UNIT_COLUMNS" "
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema='public' AND table_name='information_unit'
ORDER BY ordinal_position;
"
run_sql "S5B_INFORMATION_UNIT_COUNT_SAMPLE" "
SELECT count(*) AS total FROM public.information_unit;
SELECT id, canonical_address, lifecycle_status,
       CASE WHEN EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='information_unit' AND column_name='parent_or_container_ref') THEN (parent_or_container_ref IS NOT NULL)::text ELSE NULL END AS has_parent
FROM public.information_unit
ORDER BY canonical_address
LIMIT 10;
"

# S6 — native UV
run_sql "S6_UNIT_VERSION_COLUMNS" "
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema='public' AND table_name='unit_version'
ORDER BY ordinal_position;
"
run_sql "S6B_UNIT_VERSION_COUNTS" "
SELECT count(*) AS total FROM public.unit_version;
SELECT lifecycle_status, count(*) FROM public.unit_version GROUP BY lifecycle_status ORDER BY lifecycle_status;
SELECT count(*) AS hash_mismatch_count FROM public.unit_version WHERE content_hash IS DISTINCT FROM md5(body);
"

# S7 — side-by-side columns
run_sql_file "S7_COLUMN_COMPARISON_LU" "
SELECT COALESCE(t.column_name, i.column_name) AS concept_column,
       t.data_type AS tac_type,
       i.data_type AS iu_type,
       CASE
         WHEN t.column_name IS NOT NULL AND i.column_name IS NOT NULL THEN 'both'
         WHEN t.column_name IS NOT NULL THEN 'tac_only'
         ELSE 'iu_only'
       END AS presence
FROM (
  SELECT column_name, data_type FROM information_schema.columns WHERE table_schema='public' AND table_name='tac_logical_unit'
) t
FULL OUTER JOIN (
  SELECT column_name, data_type FROM information_schema.columns WHERE table_schema='public' AND table_name='information_unit'
) i USING (column_name)
ORDER BY concept_column;
"
run_sql_file "S7B_COLUMN_COMPARISON_UV" "
SELECT COALESCE(t.column_name, i.column_name) AS concept_column,
       t.data_type AS tac_type,
       i.data_type AS iu_type,
       CASE
         WHEN t.column_name IS NOT NULL AND i.column_name IS NOT NULL THEN 'both'
         WHEN t.column_name IS NOT NULL THEN 'tac_only'
         ELSE 'iu_only'
       END AS presence
FROM (
  SELECT column_name, data_type FROM information_schema.columns WHERE table_schema='public' AND table_name='tac_unit_version'
) t
FULL OUTER JOIN (
  SELECT column_name, data_type FROM information_schema.columns WHERE table_schema='public' AND table_name='unit_version'
) i USING (column_name)
ORDER BY concept_column;
"

# S8 — constraints, indexes, triggers
run_sql_file "S8_FOREIGN_KEYS" "
SELECT tc.table_name, tc.constraint_name, kcu.column_name, ccu.table_name AS foreign_table, ccu.column_name AS foreign_column
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu
  ON tc.constraint_name = kcu.constraint_name AND tc.table_schema=kcu.table_schema
JOIN information_schema.constraint_column_usage ccu
  ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema=tc.table_schema
WHERE tc.constraint_type = 'FOREIGN KEY'
  AND tc.table_schema='public'
  AND tc.table_name IN ('tac_logical_unit','tac_unit_version','tac_publication','tac_publication_member','information_unit','unit_version')
ORDER BY tc.table_name, tc.constraint_name;
"
run_sql "S8B_INDEXES" "
SELECT tablename, indexname, indexdef
FROM pg_indexes
WHERE schemaname='public'
  AND tablename IN ('tac_logical_unit','tac_unit_version','tac_publication','tac_publication_member','information_unit','unit_version')
ORDER BY tablename, indexname;
"
run_sql "S8C_TRIGGERS" "
SELECT tgname, tgrelid::regclass AS table_name, tgenabled
FROM pg_trigger
WHERE NOT tgisinternal
  AND tgrelid::regclass::text IN ('information_unit','unit_version','tac_logical_unit','tac_unit_version','tac_publication','tac_publication_member')
ORDER BY table_name, tgname;
"

# S9 — IU functions/signatures
run_sql "S9_IU_FUNCTIONS" "
SELECT p.proname, p.pronargs, pg_get_function_identity_arguments(p.oid) AS args,
       pg_get_function_result(p.oid) AS result_type
FROM pg_proc p
JOIN pg_namespace n ON n.oid=p.pronamespace
WHERE n.nspname='public'
  AND p.proname IN ('fn_iu_create','fn_iu_create_plan','fn_iu_verify_invariants','fn_iu_apply_edit_draft','fn_iu_edit','fn_iu_save')
ORDER BY p.proname, p.pronargs;
"

# S10 — dot_config IU keys
run_sql "S10_IU_DOT_CONFIG" "
SELECT key, value FROM public.dot_config WHERE key LIKE 'iu_%' ORDER BY key;
"

# S11 — exact TAC↔IU overlap evidence
run_sql_file "S11_CANONICAL_ADDRESS_OVERLAP" "
SELECT
  (SELECT count(*) FROM public.tac_logical_unit) AS tac_lu_count,
  (SELECT count(*) FROM public.information_unit) AS iu_count,
  (SELECT count(*) FROM public.tac_logical_unit t JOIN public.information_unit i USING (canonical_address)) AS overlap_count;
"

# S12 — publication render summary
run_sql_file "S12_PUBLICATION_MEMBER_VERSION_BINDING" "
SELECT count(*) AS total_members,
       count(*) FILTER (WHERE logical_unit_id IS NULL) AS null_lu,
       count(*) FILTER (WHERE unit_version_id IS NULL) AS null_uv
FROM public.tac_publication_member;
"

echo "=== VERDICT ==="
echo "FAIL_COUNT=$FAIL"
echo "WARN_COUNT=$WARN"
if [ "$FAIL" -eq 0 ]; then
  echo "phase_status=PASS"
else
  echo "phase_status=FAIL"
fi
echo "no_mutation_performed=true"
echo "log_path=$LOG"
echo "report_path=knowledge/dev/laws/dieu44-trien-khai/reports/p3d-pack1-readonly-inventory-report.md"
echo "=== END INVENTORY $TS ==="

Required KB report content

Upload report to:

knowledge/dev/laws/dieu44-trien-khai/reports/p3d-pack1-readonly-inventory-report.md

Report must include:

phase_status=PASS|FAIL|BLOCKED
mode=READ_ONLY_INVENTORY
no_mutation_performed=true
fail_count=<n>
warn_count=<n>
log_path=<path>
key_findings=<short bullets>
impact_on_pack1_design=<confirm EVOLVE assumptions or list contradictions>

If a query fails, do not fix. Upload the report anyway.