vestige/hooks/synthesis-stop-validator.sh
Sam Valladares 9936928be9
Some checks failed
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
Test Suite / Unit Tests (push) Has been cancelled
Test Suite / MCP E2E Tests (push) Has been cancelled
Test Suite / Dashboard Build (push) Has been cancelled
Test Suite / Code Coverage (push) Has been cancelled
CI / Release Build (aarch64-apple-darwin) (push) Has been cancelled
CI / Release Build (x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / Release Build (x86_64-apple-darwin) (push) Has been cancelled
Test Suite / User Journey Tests (push) Has been cancelled
v2.1.2 Honest Memory
Concrete search, irreversible purge, first-class contradictions tool, vestige update CLI, dense dream persistence fix, embedding-model upgrade repair, and a /dashboard/waitlist Pro early-access preview.

25 MCP tools. SQLite migration v13. Backwards compatible: 'delete' remains as a 'purge' alias.

Closes #50, #51.
2026-05-06 02:22:24 -05:00

118 lines
3.5 KiB
Bash
Executable file

#!/bin/bash
# synthesis-stop-validator.sh — optional Stop hook
#
# Blocks a narrow failure mode: a response that cites multiple memories but
# stops at summary instead of composing them into a decision. This public-safe
# version contains no private examples or local-user paths.
set -euo pipefail
INPUT="$(cat)"
TRANSCRIPT_PATH="$(printf '%s' "$INPUT" | /usr/bin/python3 -c 'import sys,json;d=json.load(sys.stdin);print(d.get("transcript_path",""))' 2>/dev/null || printf '')"
if [ -z "$TRANSCRIPT_PATH" ] || [ ! -f "$TRANSCRIPT_PATH" ]; then
exit 0
fi
export TRANSCRIPT_PATH
PYFILE=$(mktemp -t vestige-stop-validator.XXXXXX)
trap 'rm -f "$PYFILE"' EXIT
cat > "$PYFILE" <<'PYEOF'
import json, os, re, sys
transcript = os.environ.get("TRANSCRIPT_PATH", "")
last_user = ""
last_assistant = ""
try:
with open(transcript) as f:
for line in f:
line = line.strip()
if not line:
continue
try:
obj = json.loads(line)
except Exception:
continue
role = obj.get("role") or obj.get("type", "")
content = obj.get("message", {}).get("content", obj.get("content", ""))
text = ""
if isinstance(content, list):
for block in content:
if isinstance(block, dict) and block.get("type") == "text":
text += block.get("text", "") + "\n"
elif isinstance(content, str):
text = content
if role == "user":
last_user = text
elif role == "assistant":
last_assistant = text
except Exception:
sys.exit(0)
decision_re = re.compile(
r"(submit|submission|final|ship|launch|deploy|commit|decide|decision|"
r"recommend|should i|what should|purchase|buy|invest|architect|architecture|"
r"strategy|prep|prioriti|compose|tradeoff|trade-off|config|which "
r"(should|model|approach|one)|pick|choose|benchmark|competition|perform)",
re.IGNORECASE,
)
if not decision_re.search(last_user):
sys.exit(0)
memory_re = re.compile(
r"(memory|vestige|recall|retriev|saved memor|stored memor|prior memor|"
r"fsrs|trust score|deep_reference|smart_ingest)",
re.IGNORECASE,
)
if not memory_re.search(last_assistant):
sys.exit(0)
summary_patterns = [
r"memory\s+[a-f0-9]{4,}",
r"saved memory",
r"according to memory",
r"the memory (says|states|notes|indicates)",
r"per memory",
r"memories? (say|says|state|note|indicate)",
]
summary_hits = 0
for pat in summary_patterns:
summary_hits += len(re.findall(pat, last_assistant, re.IGNORECASE))
composition_re = re.compile(
r"(compos|combin|together|concrete action|recommend(ation)? [:\-]|"
r"never[- ]composed|the synthesis is|therefore|so the action is)",
re.IGNORECASE,
)
composition_hits = len(composition_re.findall(last_assistant))
if summary_hits >= 3 and composition_hits == 0:
print("BLOCK_SUMMARY")
sys.exit(0)
print("PASS")
PYEOF
RESULT="$(/usr/bin/python3 "$PYFILE")"
case "$RESULT" in
BLOCK_SUMMARY)
cat >&2 <<'BLOCKMSG'
[STOP BLOCKED — VESTIGE SYNTHESIS VALIDATOR]
The response cites multiple memories but does not compose them into a decision.
Rewrite it so the retrieved evidence becomes:
1. Evidence: the memory facts that matter.
2. Implication: what those facts change.
3. Action: the concrete recommendation.
Do not stop at "Memory A says X, Memory B says Y." Compose the evidence.
BLOCKMSG
exit 2
;;
*)
exit 0
;;
esac