#!/usr/bin/env bash # Validate fact_id references in content YAML against canonical knowledge catalogs. # Part of pre-commit checks (ticket #393). Grep-based, targets <2s runtime. # # Modes: # Advisory — when knowledge catalogs have no fact definitions yet (exit 0) # Enforcing — when catalogs are populated; fails on unknown fact_ids (exit 1) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(dirname "$SCRIPT_DIR")" KNOWLEDGE_DIR="$REPO_ROOT/content/global/knowledge" CONTENT_DIR="$REPO_ROOT/content/campaigns" # --- Extract canonical fact_ids from knowledge catalogs --- # Matches YAML lines like: fact_id: some_value or fact_id: "some_value" # Excludes entity-attributes.yaml (different schema: attribute keys, not fact_ids) CANONICAL_IDS="" if [ -d "$KNOWLEDGE_DIR" ]; then CANONICAL_IDS=$( grep -rh 'fact_id:\s*' "$KNOWLEDGE_DIR" \ --include='*.yaml' \ --exclude='entity-attributes.yaml' \ | grep -v '^\s*#' \ | sed 's/.*fact_id:\s*//' \ | sed 's/\s*#.*//' \ | sed "s/^[\"']\(.*\)[\"']$/\1/" \ | sed 's/[[:space:]]*$//' \ | sort -u \ || true ) fi CANONICAL_COUNT=$(echo "$CANONICAL_IDS" | grep -c '\S' || true) # --- Extract referenced fact_ids from campaign content --- # Covers monologue prerequisites.facts[].fact_id and dialogue knowledge_grant.fact_id REFERENCED="" if [ -d "$CONTENT_DIR" ]; then REFERENCED=$( grep -rh 'fact_id:\s*' "$CONTENT_DIR" \ --include='*.yaml' \ | grep -v '^\s*#' \ | sed 's/.*fact_id:\s*//' \ | sed 's/\s*#.*//' \ | sed "s/^[\"']\(.*\)[\"']$/\1/" \ | sed 's/[[:space:]]*$//' \ || true ) fi REFERENCED_UNIQUE=$(echo "$REFERENCED" | sort -u | grep '\S' || true) REF_COUNT=$(echo "$REFERENCED_UNIQUE" | grep -c '\S' || true) # --- Compare --- if [ "$CANONICAL_COUNT" -eq 0 ]; then echo "check-fact-ids: WARNING — no canonical fact_ids in knowledge catalogs" echo " Catalogs not yet populated. Check is advisory only." if [ "$REF_COUNT" -gt 0 ]; then echo " $REF_COUNT unique fact_ids referenced in content:" echo "$REFERENCED_UNIQUE" | sed 's/^/ /' fi exit 0 fi # Enforcing mode: catalogs have definitions ERRORS=0 while IFS= read -r ref_id; do [ -z "$ref_id" ] && continue if ! echo "$CANONICAL_IDS" | grep -qxF "$ref_id"; then echo "ERROR: unknown fact_id '$ref_id' — not in knowledge catalogs" grep -rn "fact_id:\s*$ref_id" "$CONTENT_DIR" --include='*.yaml' \ | sed "s|$REPO_ROOT/||" \ | sed 's/^/ /' ERRORS=$((ERRORS + 1)) fi done <<< "$REFERENCED_UNIQUE" if [ "$ERRORS" -gt 0 ]; then echo "" echo "check-fact-ids: FAILED — $ERRORS unknown fact_id(s)" echo " Canonical fact_ids defined in: content/global/knowledge/*.yaml" echo " Run 'make check-fact-ids' to recheck." exit 1 fi echo "check-fact-ids: OK — $REF_COUNT references validated against $CANONICAL_COUNT canonical facts" exit 0