KB-7AC8 rev 5

dot-context-pack-verify.sh rev 5 (Đ43 audit R2: Fix B)

32 min read Revision 5
dieu43context-packaudit-r2

#!/usr/bin/env bash

=============================================================================

dot-context-pack-verify — Context Pack Health Verifier (Đ43 "động cơ phụ")

=============================================================================

@version 0.1-v1

@date 2026-04-18

@author Claude Code CLI, S178 Fix 12 Phase 4b V1

@spec Điều 43 v1.2 FINAL rev 6 §8.2 + §9 (generic executor) + §5.8 guard

@paired-dot dot-context-pack-build.sh (NT12 cặp đôi bắt buộc)

@db-access directus (KHO): R context_pack_health_checks, manifest, sections,

dot_config; W via fn_log_issue (stub) + UPDATE

manifest.health_status (SSOT sức khoẻ — NT10)

incomex_metadata (KHO): R kb_documents (H4 KB mirror diff)

FS: R $OUTPUT_ROOT/current/* (H2/H3/H5/H6/H7/H8)

@gateway psql TCP localhost:5432 (postgresql-client host-side). Password

từ /opt/incomex/secrets/.env.production (Đ33 §14 SSOT).

Role context_pack_readonly cho read cross-DB (§5.8 guard 3).

Role directus cho W (UPDATE manifest, fn_log_issue).

@exit-codes 0 OK (healthy OR warn)

1 FAIL (có critical fail) — caller (cron/verify job) xử lý alert

2 USAGE

3 PRECHECK_FAIL (PG/KB/FS)

@scope V1: skeleton + generic dispatcher + H1/H2/H3 builtin handlers.

V2: H4-H9 builtin + SQL executor + function executor.

@bootstrap Đ33 §13 ngoại lệ — chạy manual dev. Register dot_tools ở Phase 5.

=============================================================================

GENERIC EXECUTOR §9 (CẤM TUYỆT ĐỐI):

- CẤM switch-case theo check code (Hx codes) — dispatch theo executor_type

- CẤM hardcode threshold/section list — tất cả đọc từ threshold_config JSONB

- CẤM fallback default khi JSONB thiếu key — FAIL-FAST

- CẤM executor_type='sql' load ngoài whitelist

- CẤM SQL có DML/DDL/privilege token

- CẤM SQL chạy với directus/workflow_admin role — PHẢI context_pack_readonly

=============================================================================

set -euo pipefail

VERSION="1.0-rev5-d43r6-audit-r2" readonly VERSION

SCRIPT_NAME="$(basename "$0")" readonly SCRIPT_NAME

Shared test hook: simulate fail for UPDATE threshold test (V2)

DOT_TEST_ONLY_CHECK=H1 — chỉ run 1 check (test setup)

-----------------------------------------------------------------------------

Runtime state (env_load populates)

-----------------------------------------------------------------------------

PGHOST PGPORT PG_USER_RW PG_DB_MAIN PG_PASSWORD_RW

PG_USER_RO PG_PASSWORD_RO PG_DB_NAO

AGENT_DATA_URL AGENT_DATA_API_KEY

-----------------------------------------------------------------------------

DRY_RUN=0 VERBOSE=0 ONLY_CHECK="" MANIFEST_ID_OVERRIDE="" OUTPUT_ROOT=""

-----------------------------------------------------------------------------

Logging helpers (same shape as build.sh)

-----------------------------------------------------------------------------

log_info() { printf '[INFO] %s\n' "$"; } log_ok() { printf '[OK] %s\n' "$"; } log_skip() { printf '[SKIP] %s\n' "$"; } log_dry() { printf '[DRY] %s\n' "$"; } log_debug() { [[ "$VERBOSE" -eq 1 ]] && printf '[DEBUG] %s\n' "$" >&2; return 0; } log_warn() { printf '[WARN] %s\n' "$" >&2; } log_err() { printf '[ERR] %s\n' "$" >&2; } log_fatal() { printf '[FATAL] %s\n' "$" >&2; }

Đ22 stub — fn_log_issue sẽ implement khi Đ22 triển khai (TD-S178-31)

log_issue() { local severity="${1:-warn}" local category="${2:-generic}" local summary="${3:-}" log_warn "log_issue STUB: severity=${severity} category=${category} summary="${summary}"" }

-----------------------------------------------------------------------------

ERR trap

-----------------------------------------------------------------------------

on_err() { local line="${1:-?}" log_fatal "Error at line ${line} (exit $?)" exit 1 } trap 'on_err $LINENO' ERR

-----------------------------------------------------------------------------

Env SSOT (Đ33 §14) + PG helpers (same shape as build.sh — no shared lib per scope)

-----------------------------------------------------------------------------

env_load() { local env_file="${ENV_FILE:-/opt/incomex/secrets/.env.production}" if [[ ! -r "$env_file" ]]; then log_fatal "env file not readable (Đ33 §14 SSOT): ${env_file}" exit 3 fi set -a

shellcheck source=/dev/null

source "$env_file" set +a

local missing=() local k for k in PGHOST PGPORT PG_USER_RW PG_DB_MAIN PG_PASSWORD_RW
PG_USER_RO PG_PASSWORD_RO PG_DB_NAO
AGENT_DATA_URL AGENT_DATA_API_KEY; do [[ -z "${!k:-}" ]] && missing+=("$k") done if [[ ${#missing[@]} -gt 0 ]]; then log_fatal "env file missing required keys: ${missing[*]} (file=${env_file})" exit 3 fi }

R/W on directus (UPDATE manifest health_status, log_issue via PG trigger Đ22)

run_pg_rw() { PGPASSWORD="$PG_PASSWORD_RW" LC_ALL=C.UTF-8 LANG=C.UTF-8
psql -h "$PGHOST" -p "$PGPORT"
-U "$PG_USER_RW" -d "$PG_DB_MAIN" -tAXq -v ON_ERROR_STOP=1 <<< "$1" }

R/O cross-DB — verify SQL executor sẽ dùng fn này sau guard check (V2)

run_pg_ro_db() { local db="$1" local sql="$2" PGPASSWORD="$PG_PASSWORD_RO" LC_ALL=C.UTF-8 LANG=C.UTF-8
PGOPTIONS='-c default_transaction_read_only=on -c statement_timeout=30s'
psql -h "$PGHOST" -p "$PGPORT"
-U "$PG_USER_RO" -d "$db" -tAXq -v ON_ERROR_STOP=1 <<< "$sql" }

dot_config_get() { local key="$1" local value value="$(run_pg_rw "SELECT value FROM dot_config WHERE key='${key}'")" if [[ -z "$value" ]]; then log_fatal "dot_config key missing: ${key} (§6.X P2 CẤM fallback)" exit 3 fi printf '%s' "$value" }

-----------------------------------------------------------------------------

Usage

-----------------------------------------------------------------------------

usage() { cat <<USAGE Usage: ${SCRIPT_NAME} [OPTIONS]

Đ43 v1.2 rev 6 §9 — Context pack health verifier (generic executor).

OPTIONS: --help, -h In hướng dẫn --dry-run Chạy check KHÔNG UPDATE manifest.health_status --only-check=<code> Chỉ chạy 1 check (vd: H1). Default: tất cả is_active --manifest-id=<id> Override latest live manifest (debug/test) --verbose Debug log

EXIT CODES: 0 OK (healthy OR warn) 1 FAIL (ít nhất 1 check critical fail) 2 USAGE 3 PRECHECK_FAIL USAGE }

parse_args() { while [[ $# -gt 0 ]]; do case "$1" in --help|-h) usage; exit 0 ;; --dry-run) DRY_RUN=1; shift ;; --verbose) VERBOSE=1; shift ;; --only-check=) ONLY_CHECK="${1#=}"; shift ;; --only-check) [[ $# -lt 2 ]] && { log_err "--only-check requires value"; exit 2; } ONLY_CHECK="$2"; shift 2 ;; --manifest-id=) MANIFEST_ID_OVERRIDE="${1#=}"; shift ;; --manifest-id) [[ $# -lt 2 ]] && { log_err "--manifest-id requires value"; exit 2; } MANIFEST_ID_OVERRIDE="$2"; shift 2 ;; *) log_err "Unknown option: $1" usage >&2 exit 2 ;; esac done }

-----------------------------------------------------------------------------

Precheck — env + PG healthy + latest live manifest + output root

-----------------------------------------------------------------------------

VERIFY_MANIFEST_ID="" VERIFY_LIVE_DIR=""

precheck() { env_load log_ok "env loaded from SSOT (Đ33 §14)"

if ! run_pg_rw "SELECT 1" >/dev/null 2>&1; then log_fatal "PG health fail (${PGHOST}:${PGPORT} ${PG_USER_RW}/${PG_DB_MAIN})" exit 3 fi log_ok "PG healthy (${PG_USER_RW}@${PGHOST}:${PGPORT}/${PG_DB_MAIN})"

OUTPUT_ROOT="$(dot_config_get 'context_pack_output_root')" VERIFY_LIVE_DIR="${OUTPUT_ROOT}/current" log_info "OUTPUT_ROOT=${OUTPUT_ROOT}" if [[ ! -L "$VERIFY_LIVE_DIR" ]]; then log_fatal "live symlink missing: ${VERIFY_LIVE_DIR} (build.sh chưa publish lần nào?)" exit 3 fi

Pick manifest: override hoặc latest live

if [[ -n "$MANIFEST_ID_OVERRIDE" ]]; then VERIFY_MANIFEST_ID="$MANIFEST_ID_OVERRIDE" else VERIFY_MANIFEST_ID="$(run_pg_rw "SELECT id FROM context_pack_manifest WHERE publish_status='live' ORDER BY generated_at DESC, id DESC LIMIT 1")" fi if [[ -z "$VERIFY_MANIFEST_ID" || ! "$VERIFY_MANIFEST_ID" =~ ^[0-9]+$ ]]; then log_fatal "no live manifest in PG (manifest_id='${VERIFY_MANIFEST_ID}')" exit 3 fi log_ok "verify target manifest_id=${VERIFY_MANIFEST_ID}" }

=============================================================================

BUILTIN HANDLERS — named match executor_ref column in context_pack_health_checks

Contract:

arg $1 = threshold_config JSON string

stdout = one-line summary

return 0 = PASS, 1 = FAIL, 2 = WARN (soft — counted in rollup)

CẤM hardcode threshold inside handler; read từ $1 via jq.

Shellcheck SC2317 disabled cho các handler bên dưới: dispatch động qua

"$executor_ref" "$cfg" trong dispatch_builtin() — shellcheck không detect

được, báo "unreachable" false-positive.

shellcheck disable=SC2317

=============================================================================

H1 — check_manifest_age

check_manifest_age() { local cfg="$1" local critical_hours warn_hours critical_hours="$(jq -r '.critical_hours // empty' <<< "$cfg")" warn_hours="$(jq -r '.warn_hours // empty' <<< "$cfg")" if [[ -z "$critical_hours" || -z "$warn_hours" ]]; then echo "threshold_config thiếu critical_hours/warn_hours" return 1 fi local age_hours age_hours="$(run_pg_rw "SELECT ROUND(EXTRACT(EPOCH FROM (now() - generated_at)) / 3600.0, 2) FROM context_pack_manifest WHERE id=${VERIFY_MANIFEST_ID}")" if [[ -z "$age_hours" ]]; then echo "cannot compute age for manifest_id=${VERIFY_MANIFEST_ID}" return 1 fi

Bash compare với float: dùng awk

local is_critical is_warn is_critical="$(awk -v a="$age_hours" -v t="$critical_hours" 'BEGIN{print (a>=t)?1:0}')" is_warn="$(awk -v a="$age_hours" -v t="$warn_hours" 'BEGIN{print (a>=t)?1:0}')" if [[ "$is_critical" == "1" ]]; then echo "age=${age_hours}h >= critical_hours=${critical_hours}" return 1 fi if [[ "$is_warn" == "1" ]]; then echo "age=${age_hours}h >= warn_hours=${warn_hours} (below critical=${critical_hours}h)" return 2 fi echo "age=${age_hours}h < warn_hours=${warn_hours}" return 0 }

H2 — check_section_exists

check_section_exists() {

shellcheck disable=SC2034

local cfg="$1" local rows local total=0 local n_missing=0 rows="$(run_pg_rw "SELECT section_code, file_path FROM context_pack_sections WHERE manifest_id=${VERIFY_MANIFEST_ID}")" local code path while IFS='|' read -r code path; do [[ -z "$code" ]] && continue total=$((total + 1)) if [[ ! -f "$path" ]]; then n_missing=$((n_missing + 1)) log_warn " missing: ${code} → ${path}" fi done <<< "$rows" if [[ $total -eq 0 ]]; then echo "0 sections row trong manifest_id=${VERIFY_MANIFEST_ID}" return 1 fi if [[ $n_missing -gt 0 ]]; then echo "missing ${n_missing}/${total} section files" return 1 fi echo "${total}/${total} section files exist" return 0 }

H3 — check_checksum_match (compare actual file sha256 vs stored file_checksum_sha256)

check_checksum_match() {

shellcheck disable=SC2034

local cfg="$1" local rows local total=0 local mismatches=0 rows="$(run_pg_rw "SELECT section_code || '|' || file_path || '|' || file_checksum_sha256 FROM context_pack_sections WHERE manifest_id=${VERIFY_MANIFEST_ID}")" local code path expected actual while IFS='|' read -r code path expected; do [[ -z "$code" ]] && continue total=$((total + 1)) if [[ ! -f "$path" ]]; then mismatches=$((mismatches + 1)) log_warn " missing file for checksum check: ${code} → ${path}" continue fi actual="$(sha256sum "$path" | awk '{print $1}')" if [[ "$actual" != "$expected" ]]; then mismatches=$((mismatches + 1)) log_warn " checksum drift: ${code} expected=${expected:0:12} actual=${actual:0:12}" fi done <<< "$rows" if [[ $total -eq 0 ]]; then echo "0 sections row trong manifest_id=${VERIFY_MANIFEST_ID}" return 1 fi if [[ $mismatches -gt 0 ]]; then echo "checksum drift ${mismatches}/${total}" return 1 fi echo "${total}/${total} checksum match" return 0 }

H4 — check_kb_mirror_diff: compare FS file vs KB document content byte-by-byte.

Dùng jq -j (no trailing newline) + file compare via sha256sum để tránh

command-substitution strip \n và ${#var} char vs byte count issues.

check_kb_mirror_diff() { local cfg="$1" local warn_pct critical_pct warn_pct="$(jq -r '.warn_pct // empty' <<< "$cfg")" critical_pct="$(jq -r '.critical_pct // empty' <<< "$cfg")" if [[ -z "$warn_pct" || -z "$critical_pct" ]]; then echo "threshold_config thiếu warn_pct/critical_pct" return 1 fi local rows code path kbpath max_pct=0 n_drift=0 local tmp_json tmp_content tmp_json="$(mktemp -t dcp-verify-kb-json.XXXXXX)" tmp_content="$(mktemp -t dcp-verify-kb-content.XXXXXX)"

shellcheck disable=SC2064

trap "rm -f '$tmp_json' '$tmp_content'" RETURN rows="$(run_pg_rw "SELECT section_code || '|' || file_path || '|' || COALESCE(kb_document_path, '') FROM context_pack_sections WHERE manifest_id=${VERIFY_MANIFEST_ID}")" while IFS='|' read -r code path kbpath; do [[ -z "$code" || -z "$kbpath" || ! -f "$path" ]] && continue local doc_id="${kbpath//__//}" curl -sS --max-time 15
-H "X-API-Key: ${AGENT_DATA_API_KEY}"
"${AGENT_DATA_URL}/documents/${doc_id}?full=true" > "$tmp_json" 2>/dev/null # jq -j = raw output WITHOUT trailing newline (preserves exact body bytes) jq -j '.content // ""' "$tmp_json" > "$tmp_content" 2>/dev/null if [[ ! -s "$tmp_content" ]]; then log_warn " KB fetch empty: ${code} doc_id=${doc_id}" n_drift=$((n_drift + 1)) max_pct=100 continue fi local fs_sha kb_sha fs_size kb_size fs_sha="$(sha256sum "$path" | awk '{print $1}')" kb_sha="$(sha256sum "$tmp_content" | awk '{print $1}')" if [[ "$fs_sha" != "$kb_sha" ]]; then fs_size="$(wc -c < "$path")" kb_size="$(wc -c < "$tmp_content")" n_drift=$((n_drift + 1)) local abs_diff max_size pct abs_diff=$((kb_size > fs_size ? kb_size - fs_size : fs_size - kb_size)) max_size=$((kb_size > fs_size ? kb_size : fs_size)) pct=$((max_size > 0 ? abs_diff * 100 / max_size : 100)) [[ $pct -gt $max_pct ]] && max_pct=$pct log_warn " drift ${code}: fs=${fs_size}B kb=${kb_size}B abs_diff=${abs_diff}B ~${pct}%" fi done <<< "$rows" if awk -v m="$max_pct" -v t="$critical_pct" 'BEGIN{exit !(m>=t)}'; then echo "max_diff=${max_pct}% >= critical=${critical_pct}% (${n_drift} section drift)" return 1 fi if awk -v m="$max_pct" -v t="$warn_pct" 'BEGIN{exit !(m>=t)}'; then echo "max_diff=${max_pct}% >= warn=${warn_pct}% (${n_drift} section drift)" return 2 fi echo "max_diff=${max_pct}% < warn=${warn_pct}% (drift=${n_drift})" return 0 }

H5 — check_section_size: section-specific size vs warn_kb/critical_kb

check_section_size() { local cfg="$1" local section warn_kb critical_kb section="$(jq -r '.section // empty' <<< "$cfg")" warn_kb="$(jq -r '.warn_kb // empty' <<< "$cfg")" critical_kb="$(jq -r '.critical_kb // empty' <<< "$cfg")" if [[ -z "$section" || -z "$warn_kb" || -z "$critical_kb" ]]; then echo "threshold_config thiếu section/warn_kb/critical_kb" return 1 fi local size_bytes size_bytes="$(run_pg_rw "SELECT size_bytes FROM context_pack_sections WHERE manifest_id=${VERIFY_MANIFEST_ID} AND section_code='${section}'")" if [[ -z "$size_bytes" ]]; then echo "section '${section}' not found in manifest" return 1 fi local size_kb=$((size_bytes / 1024)) if [[ $size_kb -ge $critical_kb ]]; then echo "size=${size_kb}KB >= critical=${critical_kb}KB (section=${section})" return 1 fi if [[ $size_kb -ge $warn_kb ]]; then echo "size=${size_kb}KB >= warn=${warn_kb}KB (section=${section})" return 2 fi echo "size=${size_kb}KB < warn=${warn_kb}KB (section=${section})" return 0 }

H6 — check_section_headers: volatile header present per format

check_section_headers() { local cfg="$1"

shellcheck disable=SC2034

local _cfg="$cfg" local rows code path fmt total=0 bad=0 rows="$(run_pg_rw "SELECT cps.section_code || '|' || cps.file_path || '|' || cpd.format FROM context_pack_sections cps JOIN context_pack_section_definitions cpd ON cps.section_code=cpd.code WHERE cps.manifest_id=${VERIFY_MANIFEST_ID}")" while IFS='|' read -r code path fmt; do [[ -z "$code" || ! -f "$path" ]] && continue total=$((total + 1)) case "$fmt" in markdown|mermaid) if ! grep -q '^<!-- VOLATILE HEADER -->$' "$path"
|| ! grep -q '^<!-- /VOLATILE HEADER -->$' "$path"; then bad=$((bad + 1)) log_warn " ${code} (${fmt}) missing volatile header delimiters" else # Đ43 §6 Bước 5 rev 5 — 4 common runtime fields bắt buộc. # Đ43 audit R2 Fix B — grep chỉ trong header block (giữa 2 delimiter), # tránh false pass khi body chứa key trùng tên. local vh_block vh_block="$(sed -n '/^<!-- VOLATILE HEADER -->$/,/^<!-- \/VOLATILE HEADER -->$/p' "$path")" local missing_keys="" local vh_key for vh_key in generated_at build_id git_commit trigger_source; do grep -qE "^${vh_key}:[[:space:]]+[^[:space:]]" <<<"$vh_block"
|| missing_keys="${missing_keys} ${vh_key}" done if [[ -n "$missing_keys" ]]; then bad=$((bad + 1)) log_warn " ${code} (${fmt}) volatile header thiếu 4-field (trong block):${missing_keys}" fi fi ;; json) if ! jq -e 'has("_volatile_header")' "$path" >/dev/null 2>&1; then bad=$((bad + 1)) log_warn " ${code} (json) missing _volatile_header key" elif ! jq -e '._volatile_header | has("generated_at") and has("build_id") and has("git_commit") and has("trigger_source")' "$path" >/dev/null 2>&1; then bad=$((bad + 1)) log_warn " ${code} (json) _volatile_header thiếu 4-field" fi ;; esac done <<< "$rows" if [[ $bad -gt 0 ]]; then echo "headers bad ${bad}/${total}" return 1 fi echo "${total}/${total} headers valid" return 0 }

H7 — check_mermaid_parse: mmdc probe (soft skip nếu không có binary)

check_mermaid_parse() { local cfg="$1" local section section="$(jq -r '.section // empty' <<< "$cfg")" if [[ -z "$section" ]]; then echo "threshold_config thiếu section" return 1 fi local path path="$(run_pg_rw "SELECT file_path FROM context_pack_sections WHERE manifest_id=${VERIFY_MANIFEST_ID} AND section_code='${section}'")" if [[ -z "$path" || ! -f "$path" ]]; then echo "section '${section}' file missing" return 1 fi if ! command -v mmdc >/dev/null 2>&1; then echo "mmdc binary not available — soft skip (install mermaid-cli để enable)" return 2 fi local svg="/tmp/dcp-verify-mmdc-$$.svg" if mmdc -i "$path" -o "$svg" >/dev/null 2>&1; then rm -f "$svg" echo "mmdc parse OK (section=${section})" return 0 else rm -f "$svg" echo "mmdc parse FAIL (section=${section})" return 1 fi }

H8 — check_json_valid: jq parse + _volatile_header key

check_json_valid() { local cfg="$1" local section section="$(jq -r '.section // empty' <<< "$cfg")" if [[ -z "$section" ]]; then echo "threshold_config thiếu section" return 1 fi local path path="$(run_pg_rw "SELECT file_path FROM context_pack_sections WHERE manifest_id=${VERIFY_MANIFEST_ID} AND section_code='${section}'")" if [[ -z "$path" || ! -f "$path" ]]; then echo "section '${section}' file missing" return 1 fi if ! jq empty "$path" >/dev/null 2>&1; then echo "jq parse FAIL (section=${section})" return 1 fi if ! jq -e 'has("_volatile_header")' "$path" >/dev/null 2>&1; then echo "_volatile_header key missing (section=${section})" return 1 fi echo "json valid + _volatile_header present (section=${section})" return 0 }

H9 — check_publish_state: detect requests stuck in running state

check_publish_state() { local cfg="$1" local timeout_min timeout_min="$(jq -r '.staging_timeout_min // empty' <<< "$cfg")" if [[ -z "$timeout_min" ]]; then echo "threshold_config thiếu staging_timeout_min" return 1 fi local stuck stuck="$(run_pg_rw "SELECT count(*) FROM context_pack_requests WHERE status='running' AND started_at < now() - interval '${timeout_min} minutes'")" if [[ -z "$stuck" ]]; then stuck=0; fi if [[ $stuck -gt 0 ]]; then echo "stuck requests: ${stuck} (older than ${timeout_min}min in running state)" return 1 fi echo "0 stuck requests (timeout=${timeout_min}min)" return 0 }

=============================================================================

Executor dispatchers — 3 loại executor_type (§9)

=============================================================================

builtin — call bash function matching $executor_ref

dispatch_builtin() { local executor_ref="$1" local cfg="$2" if ! declare -F "$executor_ref" >/dev/null; then log_warn " builtin handler '${executor_ref}' chưa define — treat as WARN" echo "handler not implemented" return 2 fi "$executor_ref" "$cfg" }

sql — §5.8 5 guards + compare result to threshold

threshold_config: {threshold, comparator, result_field}

comparator: gt|lt|gte|lte|eq|ne

PATH_PREFIX_QUERY_WHITELIST='knowledge__current-state__queries__' BANNED_SQL_TOKENS='\b(INSERT|UPDATE|DELETE|ALTER|DROP|TRUNCATE|GRANT|REVOKE|COPY|CALL|VACUUM|ANALYZE|CREATE|DO)\b|SET[[:space:]]+(ROLE|SESSION)'

dispatch_sql() { local executor_ref="$1" local cfg="$2"

Guard 1: path whitelist

if [[ "$executor_ref" != "$PATH_PREFIX_QUERY_WHITELIST"* ]]; then echo "§5.8 guard 1 path whitelist FAIL: ${executor_ref}" return 1 fi

Load SQL from KB

local doc_id sql doc_id="${executor_ref//__//}" sql="$(curl -sS --max-time 15 -H "X-API-Key: ${AGENT_DATA_API_KEY}"
"${AGENT_DATA_URL}/documents/${doc_id}?full=true" 2>/dev/null
| jq -r '.content // empty' 2>/dev/null)" if [[ -z "$sql" ]]; then echo "KB fetch empty for ${doc_id}" return 1 fi

Guard 2: banned token scan

if grep -iqE "$BANNED_SQL_TOKENS" <<< "$sql"; then echo "§5.8 guard 2 banned token detected in ${executor_ref}" return 1 fi

Single-statement check (no mid-body ;)

local stripped stripped="$(printf '%s' "$sql" | sed 's/--.$//' | tr -d '\n' | sed 's/;[[:space:]]$//')" if [[ "$stripped" == ";" ]]; then echo "§5.8 guard 2 multi-statement forbidden in ${executor_ref}" return 1 fi

Threshold parse

local threshold comparator result_field target_db threshold="$(jq -r '.threshold // empty' <<< "$cfg")" comparator="$(jq -r '.comparator // empty' <<< "$cfg")" result_field="$(jq -r '.result_field // empty' <<< "$cfg")"

target_db optional — default PG_DB_MAIN (rev 6 §5.7 multi-DB dispatch pattern)

target_db="$(jq -r '.target_db // empty' <<< "$cfg")" [[ -z "$target_db" ]] && target_db="$PG_DB_MAIN" if [[ -z "$threshold" || -z "$comparator" || -z "$result_field" ]]; then echo "threshold_config thiếu threshold/comparator/result_field" return 1 fi

Execute with 5 guards (PGOPTIONS handles guard 4+5, user=ro handles guard 3, wrap row_to_json)

local wrapped wrapped="SELECT row_to_json(t) FROM (${sql}) t LIMIT 1" local out row_json value out="$(run_pg_ro_db "$target_db" "$wrapped" 2>/dev/null)" if [[ -z "$out" ]]; then echo "SQL returned 0 rows" return 1 fi row_json="$out" value="$(jq -r ".${result_field} // empty" <<< "$row_json")" if [[ -z "$value" ]]; then echo "result_field '${result_field}' missing in SQL result" return 1 fi

Compare using awk (handles both int and float)

local ok ok="$(awk -v v="$value" -v t="$threshold" -v c="$comparator" 'BEGIN{ pass=0 if (c=="gt") pass=(v>t) else if (c=="gte") pass=(v>=t) else if (c=="lt") pass=(v<t) else if (c=="lte") pass=(v<=t) else if (c=="eq") pass=(v==t) else if (c=="ne") pass=(v!=t) else { print "bad_comparator"; exit 2 } print (pass?"PASS":"FAIL") }')" if [[ "$ok" == "PASS" ]]; then echo "${result_field}=${value} ${comparator} ${threshold} @${target_db} → PASS (guards 1-5 OK)" return 0 fi if [[ "$ok" == "bad_comparator" ]]; then echo "unknown comparator '${comparator}'" return 1 fi echo "${result_field}=${value} NOT ${comparator} ${threshold} @${target_db}" return 1 }

function — verify function exists, call with threshold_config JSONB

dispatch_function() { local executor_ref="$1" local cfg="$2"

Verify function exists (any arity — use proname)

local exists exists="$(run_pg_rw "SELECT 1 FROM pg_proc p JOIN pg_namespace n ON p.pronamespace=n.oid WHERE p.proname='${executor_ref}' AND n.nspname='public' LIMIT 1")" if [[ "$exists" != "1" ]]; then echo "function '${executor_ref}' không tồn tại (pg_proc)" return 1 fi

Escape threshold_config single quotes for SQL literal

local cfg_sql="${cfg//'/''}" local result result="$(run_pg_rw "SELECT ${executor_ref}('${cfg_sql}'::jsonb)::text")" if [[ "$result" == "t" || "$result" == "true" ]]; then echo "${executor_ref}(${cfg}) → TRUE" return 0 fi if [[ "$result" == "f" || "$result" == "false" ]]; then echo "${executor_ref}(${cfg}) → FALSE" return 1 fi echo "${executor_ref}(${cfg}) returned non-boolean: '${result}'" return 1 }

=============================================================================

Main verify loop

=============================================================================

verify_all() { local where="" if [[ -n "$ONLY_CHECK" ]]; then where=" AND code = '${ONLY_CHECK}'" log_info "only-check filter: ${ONLY_CHECK}" fi

JSON array cho loop (dễ parse hơn đọc từng dòng)

local checks_json total checks_json="$(run_pg_rw "SELECT COALESCE(json_agg(json_build_object( 'code', code, 'name', name, 'executor_type', executor_type, 'executor_ref', executor_ref, 'threshold_config', threshold_config::text, 'severity_on_fail', severity_on_fail, 'order_index', order_index ) ORDER BY order_index), '[]'::json) FROM context_pack_health_checks WHERE is_active = true${where}")" total="$(jq 'length' <<< "$checks_json")" if [[ "$total" -eq 0 ]]; then log_fatal "0 active health_checks row (filter=${ONLY_CHECK:-<none>})" exit 1 fi log_info "verify: ${total} active health_check(s)"

local i=0 local n_pass=0 local n_fail_warn=0 local n_fail_crit=0 local n_soft_warn=0 local has_critical_fail=0 local results_tsv="/tmp/dcp-verify-results-$$.tsv" : > "$results_tsv"

while [[ $i -lt $total ]]; do local row code name etype eref tcfg sev rc summary row="$(jq -c ".[$i]" <<< "$checks_json")" code="$(jq -r '.code' <<< "$row")" name="$(jq -r '.name' <<< "$row")" etype="$(jq -r '.executor_type' <<< "$row")" eref="$(jq -r '.executor_ref' <<< "$row")" tcfg="$(jq -r '.threshold_config' <<< "$row")" sev="$(jq -r '.severity_on_fail' <<< "$row")"

log_info "check[${code}] ${name} (type=${etype} ref=${eref} sev=${sev})"
set +e
case "$etype" in
  builtin)  summary="$(dispatch_builtin  "$eref" "$tcfg")" ; rc=$? ;;
  sql)      summary="$(dispatch_sql      "$eref" "$tcfg")" ; rc=$? ;;
  function) summary="$(dispatch_function "$eref" "$tcfg")" ; rc=$? ;;
  *)
    log_err "  unknown executor_type='${etype}' — skip"
    summary="unknown executor_type"
    rc=1
    ;;
esac
set -e

local result_label
if [[ $rc -eq 0 ]]; then
  result_label="PASS"
  n_pass=$((n_pass + 1))
  log_ok "  ${code} PASS — ${summary}"
elif [[ $rc -eq 2 ]]; then
  result_label="WARN"
  n_soft_warn=$((n_soft_warn + 1))
  log_warn "  ${code} WARN — ${summary}"
else
  # rc=1 FAIL — ghi log_issue với severity
  if [[ "$sev" == "critical" ]]; then
    has_critical_fail=1
    n_fail_crit=$((n_fail_crit + 1))
    result_label="FAIL_CRITICAL"
  else
    n_fail_warn=$((n_fail_warn + 1))
    result_label="FAIL_WARN"
  fi
  log_err "  ${code} FAIL (sev=${sev}) — ${summary}"
  log_issue "$sev" "${eref}" "${code} ${name}: ${summary}"
fi
printf '%s\t%s\t%s\t%s\t%s\n' "$code" "$etype" "$eref" "$sev" "$result_label" >> "$results_tsv"
i=$((i + 1))

done

Rollup

local rollup if [[ $has_critical_fail -eq 1 ]]; then rollup="fail" elif [[ $n_fail_warn -gt 0 || $n_soft_warn -gt 0 ]]; then rollup="warn" else rollup="healthy" fi log_info "rollup: pass=${n_pass} soft_warn=${n_soft_warn} fail_warn=${n_fail_warn} fail_crit=${n_fail_crit} → health_status=${rollup}"

UPDATE manifest.health_status (nếu không dry-run)

if [[ $DRY_RUN -eq 1 ]]; then log_dry "would UPDATE context_pack_manifest SET health_status='${rollup}' WHERE id=${VERIFY_MANIFEST_ID}" elif [[ -n "$ONLY_CHECK" ]]; then log_info "only-check mode: skip UPDATE manifest.health_status (partial result)" else run_pg_rw "UPDATE context_pack_manifest SET health_status='${rollup}' WHERE id=${VERIFY_MANIFEST_ID}" >/dev/null log_ok "manifest_id=${VERIFY_MANIFEST_ID} health_status=${rollup}" fi

Results table summary

log_info "=== Results ===" local fmt=" %-5s %-10s %-28s %-10s %s"

shellcheck disable=SC2059

printf "${fmt}\n" "CODE" "TYPE" "EXECUTOR_REF" "SEV" "RESULT" while IFS=$'\t' read -r c t r s l; do # shellcheck disable=SC2059 printf "${fmt}\n" "$c" "$t" "$r" "$s" "$l" done < "$results_tsv" rm -f "$results_tsv"

Exit code: 1 nếu có critical fail, else 0

if [[ $has_critical_fail -eq 1 ]]; then return 1 fi return 0 }

=============================================================================

Main

=============================================================================

main() { parse_args "$@"

log_info "${SCRIPT_NAME} v${VERSION}" log_info "only_check=${ONLY_CHECK:-<all>} dry_run=${DRY_RUN} verbose=${VERBOSE}"

log_info "=== PRECHECK ===" precheck

log_info "=== HEALTH CHECKS (§9 generic executor) ===" if ! verify_all; then log_err "${SCRIPT_NAME} completed with critical failures" exit 1 fi log_ok "${SCRIPT_NAME} completed" exit 0 }

main "$@"