Files
Kai 88b06d782f feat(curator): vendor the application backend as the scenario's tracked source
The curator Python backend (package, tests, systemd units, config templates, scripts) now lives under scenarios/curator/backend and is the single source of truth; the live checkout at the workspace path is a runtime copy. Exported from the app repo's tracked tree via git archive (no history, .pi/venv/caches excluded). 149 unit tests pass from the new location.

profile.toml backend is now repo-relative (scenarios/curator/backend); verify-generated.sh resolves a relative backend against REPO_ROOT. verify-no-secrets ASSIGN heuristic now requires value entropy so vendored kwargs like token=extraction_token no longer false-positive. README documents the backend/ layout and the operator-owned app rollout step.
2026-08-30 18:49:10 -07:00

66 lines
2.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# ---------------------------------------------------------------------------
# Check that generated regions in tracked files are current.
#
# The tool list inside a scenario's system prompt is generated from the backend's
# contracts module. It has to be generated rather than referenced because pi does
# not put the tool list in the prompt when --system-prompt is used: the
# customPrompt branch returns before `toolsList` is assembled, so `promptSnippet`
# and `promptGuidelines` never reach the model.
#
# That makes the prompt a copy of the tool definitions, and a copy drifts. The
# failure is quiet and bad: the model is told about a tool that no longer exists,
# or not told about one that does, and answers from memory instead of asking.
#
# Run by the pre-commit hook. The backend path comes from profile.toml, so this
# checks against the code that will actually serve the tools.
# ---------------------------------------------------------------------------
# shellcheck source=lib/common.sh
. "$(dirname "${BASH_SOURCE[0]}")/lib/common.sh"
FIX=0
[ "${1:-}" = "--fix" ] && FIX=1
STALE=0
CHECKED=0
while IFS= read -r profile; do
name="$(basename "$(dirname "$profile")")"
backend="$(toml_get "$profile" scenario backend)"
[ -n "$backend" ] || continue
case "$backend" in /*) ;; *) backend="$REPO_ROOT/$backend" ;; esac
[ -d "$backend" ] || { warn "$name: backend not found: $backend"; continue; }
while IFS= read -r prompt; do
[ -f "$prompt" ] || continue
grep -q 'BEGIN GENERATED TOOL LIST' "$prompt" || continue
CHECKED=$((CHECKED + 1))
rel="${prompt#"$REPO_ROOT"/}"
if PYTHONPATH="$backend" python3 - "$prompt" "$FIX" <<'PY'
import pathlib, sys
from curator import contracts
path, fix = pathlib.Path(sys.argv[1]), sys.argv[2] == "1"
text = path.read_text(encoding="utf-8")
current = contracts.splice_tool_prose(text)
if current == text:
sys.exit(0)
if fix:
path.write_text(current, encoding="utf-8")
sys.exit(0)
sys.exit(1)
PY
then
if [ "$FIX" -eq 1 ]; then ok "$rel"; else ok "$rel"; fi
else
STALE=$((STALE + 1))
warn "stale generated region: $rel"
fi
done < <(find "$(dirname "$profile")/workspace" -name '*.md' -type f 2>/dev/null | sort)
done < <(find "$REPO_ROOT/scenarios" -mindepth 2 -maxdepth 2 -name profile.toml | sort)
if [ "$STALE" -gt 0 ]; then
die "$STALE generated region(s) out of date. Regenerate with:
scripts/verify-generated.sh --fix"
fi
ok "verify-generated: $CHECKED file(s) checked, all current"