#!/bin/bash # verify-repl — compliance checker for the OpenCode Engineering Discipline # # Usage: verify-repl # Scans all .org files in the given directory for violations of: # 1. REPL-First: every (defun|defmacro|defvar|defparameter|defstruct|defmethod|defclass) # block must have ";; REPL-VERIFIED:" on the line above #+begin_src lisp # 2. One-per-block: each #+begin_src lisp block must contain exactly one top-level form # 3. Prose-before-code: each code block must be preceded by an Org headline # # Returns 0 if all checks pass, 1 if violations found. set -euo pipefail ORG_DIR="${1:-}" if [ -z "$ORG_DIR" ] || [ ! -d "$ORG_DIR" ]; then echo "Usage: verify-repl " exit 1 fi VIOLATIONS=0 FILES_CHECKED=0 # Blacklist: files exempt from REPL verification (core infrastructure, tests) # Override with $VERIFY_REPL_EXCLUDE (space-separated filenames). # Example: VERIFY_REPL_EXCLUDE="setup.org package.lisp" verify-repl org/ DEFAULT_BLACKLIST=( "core-defpackage.org" "core-manifest.org" "core-skills.org" "core-communication.org" "package.lisp" "setup.org" ) if [ -n "${VERIFY_REPL_EXCLUDE:-}" ]; then IFS=' ' read -ra BLACKLIST <<< "$VERIFY_REPL_EXCLUDE" else BLACKLIST=("${DEFAULT_BLACKLIST[@]}") fi is_blacklisted() { local fname fname=$(basename "$1") for bl in "${BLACKLIST[@]}"; do [ "$fname" = "$bl" ] && return 0 done return 1 } check_file() { local file="$1" local fname fname=$(basename "$file") local in_block=0 local block_start=0 local prev_line="" local prev_was_headline=0 local block_content="" local line_no=0 local has_repl_verify=0 local def_count=0 local violations_in_file=0 is_blacklisted "$file" && return 0 FILES_CHECKED=$((FILES_CHECKED + 1)) while IFS= read -r line || [ -n "$line" ]; do line_no=$((line_no + 1)) local trimmed="${line#"${line%%[![:space:]]*}"}" # Track headlines if echo "$trimmed" | grep -qE '^\*+[[:space:]]'; then prev_was_headline=1 fi # Enter code block if echo "$trimmed" | grep -qE '^#\+begin_src[[:space:]]+lisp'; then in_block=1 block_start=$line_no block_content="" def_count=0 has_repl_verify=0 # Check for REPL-VERIFIED comment on previous line(s) # Matches ";; REPL-VERIFIED:" or ";; REPL-VERIFIED:" etc. if echo "$prev_line" | grep -qE ';; +REPL[-_]VERIFIED:'; then has_repl_verify=1 fi # Check for prose requirement: was there a headline before this block? if [ "$prev_was_headline" -eq 0 ]; then echo " $fname:$block_start: PROSE-BEFORE-CODE: no Org headline before code block" violations_in_file=$((violations_in_file + 1)) fi fi # Inside code block: collect content if [ "$in_block" -eq 1 ]; then if echo "$trimmed" | grep -qE '^#\+end_src'; then in_block=0 # Check: REPL verification for definition blocks if [ "$def_count" -gt 0 ] && [ "$has_repl_verify" -eq 0 ]; then echo " $fname:$block_start: REPL-FIRST: $(if [ "$def_count" -gt 1 ]; then echo "$def_count definitions"; else echo "defun/defmacro"; fi) without REPL-VERIFIED comment" violations_in_file=$((violations_in_file + 1)) fi # Check: one-per-block if [ "$def_count" -gt 1 ]; then echo " $fname:$block_start: ONE-PER-BLOCK: $def_count definitions in a single block (must be exactly 1)" violations_in_file=$((violations_in_file + 1)) fi prev_was_headline=0 block_content="" else # Count definitions in block if echo "$trimmed" | grep -qE '^\((defun|defmacro|defvar|defparameter|defstruct|defmethod|defclass)[[:space:](]'; then def_count=$((def_count + 1)) fi fi fi prev_line="$line" done < "$file" if [ "$violations_in_file" -gt 0 ]; then echo " ✗ $fname: $violations_in_file violation(s)" VIOLATIONS=$((VIOLATIONS + violations_in_file)) fi } echo "=== REPL Compliance Check ===" echo "Directory: $ORG_DIR" echo "" for file in "$ORG_DIR"/*.org; do [ -f "$file" ] || continue check_file "$file" done echo "" echo "Files checked: $FILES_CHECKED" echo "Violations: $VIOLATIONS" if [ "$VIOLATIONS" -eq 0 ]; then echo "Status: ✓ PASS — all checks satisfied" exit 0 else echo "Status: ✗ FAIL — fix violations before committing" exit 1 fi