#!/usr/bin/env bash # # Refuse to archive an OpenSpec change while tracked text still cites the # notebook. # # Archiving is the moment a change's files freeze (status/MAINTAINING.md, # "Where things live": archives are not rewritten), so a proposal or design # that still names a path under status/explorations/ has to be harvested # before the move, not after. Any harness that runs shell commands calls # this with the command it is about to run: it exits 0 at once for anything # that is not an archive, and for an archive (an `mv` into # openspec/changes/archive/, or `openspec archive`) it runs # scripts/notebook-citations.sh and exits 1 with the hits and the # instruction when there are any. The rule and the message live here; a # harness adapter only extracts the command and relays the exit: # .claude/hooks/archive-gate.sh for Claude Code, .opencode/plugin/ # archive-gate.ts for opencode. # # Usage: ./scripts/archive-gate.sh '' set -euo pipefail cd "$(dirname "$0")/.." cmd=${1:-} move='(^|[;&|[:space:]])(git[[:space:]]+)?mv[[:space:]].*openspec/changes/archive' archive='(^|[;&|[:space:]])openspec[[:space:]]+archive([[:space:]]|$)' if ! [[ $cmd =~ $move || $cmd =~ $archive ]]; then exit 0 fi if hits=$(./scripts/notebook-citations.sh 2>&1); then exit 0 fi { echo "archive-gate: not archiving while tracked text cites the notebook." echo "$hits" echo echo "Harvest first (the harvest skill; status/MAINTAINING.md, \"Landing a change\"): state each fact in its tracked home, then archive." } >&2 exit 1