ship: fix 7 review findings from Ganymede + Rhea

- auto-deploy.sh: fail hard on checkout error (was silent || true),
  show syntax check errors (was 2>/dev/null), add flock concurrency
  guard, quote rsync excludes, fix agent-state path, add telegram/
  rsync target, add smoke test failure comment
- prune-branches.sh: only delete merged branches (is-ancestor check),
  show delete errors (was 2>/dev/null)
- deploy.sh: show syntax check errors, add telegram/ rsync target
- evaluate-trigger.sh: remove stale ^diagnostics/ pattern
- AGENT-SOP.md: add stderr suppression rule, config.py constants rule

Pentagon-Agent: Ship <1A6F9A42-AC52-4027-B8C5-3CB5FA3F7C28>

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
m3taversal 2026-04-14 16:14:52 +01:00
parent 0d718f0786
commit acc5a9e7bb
5 changed files with 29 additions and 7 deletions

View file

@ -68,9 +68,11 @@ Check auto-deploy status: `journalctl -u teleo-auto-deploy -n 20`
## Shell and Python Safety
- Run `bash -n script.sh` after modifying any shell script.
- Never suppress stderr on critical git commands (`2>/dev/null || true`). Log errors, fail hard.
- Never interpolate shell variables into Python strings via `'$var'`.
Pass values via `os.environ` or `sys.argv`.
- Never write credentials to `.git/config`. Use per-command `git -c http.extraHeader`.
- Tunable constants live in `ops/pipeline-v2/lib/config.py`. Don't hardcode numbers in module files.
## Schema Changes

View file

@ -4,6 +4,13 @@
# Exits silently when nothing has changed.
set -euo pipefail
LOCK_FILE="/tmp/teleo-auto-deploy.lock"
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
logger -t "auto-deploy" "Another deploy is already running. Skipping."
exit 0
fi
DEPLOY_CHECKOUT="/opt/teleo-eval/workspaces/deploy"
PIPELINE_DIR="/opt/teleo-eval/pipeline"
DIAGNOSTICS_DIR="/opt/teleo-eval/diagnostics"
@ -33,7 +40,10 @@ fi
log "New commits: ${OLD_SHA:0:8} -> ${NEW_SHA:0:8}"
git checkout main --quiet 2>/dev/null || true
if ! git checkout main --quiet 2>&1; then
log "ERROR: git checkout main failed — dirty tree or corrupted index"
exit 1
fi
if ! git pull --ff-only --quiet 2>&1; then
log "ERROR: git pull --ff-only failed. Manual intervention needed."
exit 1
@ -43,7 +53,7 @@ fi
ERRORS=0
for f in ops/pipeline-v2/lib/*.py ops/pipeline-v2/*.py ops/diagnostics/*.py; do
[ -f "$f" ] || continue
if ! python3 -c "import ast, sys; ast.parse(open(sys.argv[1]).read())" "$f" 2>/dev/null; then
if ! python3 -c "import ast, sys; ast.parse(open(sys.argv[1]).read())" "$f" 2>&1; then
log "SYNTAX ERROR: $f"
ERRORS=$((ERRORS + 1))
fi
@ -55,7 +65,7 @@ fi
log "Syntax check passed"
# Sync to working directories (mirrors deploy.sh logic)
RSYNC_FLAGS="-az --exclude=__pycache__ --exclude=*.pyc --exclude=*.bak*"
RSYNC_FLAGS="-az --exclude='__pycache__' --exclude='*.pyc' --exclude='*.bak*'"
rsync $RSYNC_FLAGS ops/pipeline-v2/lib/ "$PIPELINE_DIR/lib/"
@ -63,6 +73,7 @@ for f in teleo-pipeline.py reweave.py batch-extract-50.sh; do
[ -f "ops/pipeline-v2/$f" ] && rsync $RSYNC_FLAGS "ops/pipeline-v2/$f" "$PIPELINE_DIR/$f"
done
rsync $RSYNC_FLAGS ops/pipeline-v2/telegram/ "$PIPELINE_DIR/telegram/"
rsync $RSYNC_FLAGS ops/diagnostics/ "$DIAGNOSTICS_DIR/"
rsync $RSYNC_FLAGS ops/agent-state/ "$AGENT_STATE_DIR/"
[ -f ops/research-session.sh ] && rsync $RSYNC_FLAGS ops/research-session.sh /opt/teleo-eval/research-session.sh
@ -117,7 +128,8 @@ if [ -n "$RESTART" ]; then
fi
if [ "$FAIL" -gt 0 ]; then
log "WARNING: Smoke test failures. NOT updating stamp. Will retry next cycle."
# Code is already synced — push a fix, don't wait for next cycle
log "WARNING: Smoke test failures. NOT updating stamp. Will retry next cycle. Push a fix."
exit 1
fi
else

View file

@ -43,7 +43,7 @@ echo "=== Pre-deploy syntax check ==="
ERRORS=0
for f in "$REPO_ROOT/ops/pipeline-v2/lib/"*.py "$REPO_ROOT/ops/pipeline-v2/"*.py "$REPO_ROOT/ops/diagnostics/"*.py; do
[ -f "$f" ] || continue
if ! python3 -c "import ast, sys; ast.parse(open(sys.argv[1]).read())" "$f" 2>/dev/null; then
if ! python3 -c "import ast, sys; ast.parse(open(sys.argv[1]).read())" "$f" 2>&1; then
echo "SYNTAX ERROR: $f"
ERRORS=$((ERRORS + 1))
fi
@ -76,6 +76,10 @@ echo "=== Diagnostics ==="
rsync $RSYNC_FLAGS "$REPO_ROOT/ops/diagnostics/" "$VPS_HOST:$VPS_DIAGNOSTICS/"
echo ""
echo "=== Telegram bot ==="
rsync $RSYNC_FLAGS "$REPO_ROOT/ops/pipeline-v2/telegram/" "$VPS_HOST:$VPS_PIPELINE/telegram/"
echo ""
echo "=== Agent state ==="
rsync $RSYNC_FLAGS "$REPO_ROOT/ops/agent-state/" "$VPS_HOST:$VPS_AGENT_STATE/"
echo ""

View file

@ -64,7 +64,7 @@ detect_code_pr() {
files=$(gh pr view "$pr_number" --json files --jq '.files[].path' 2>/dev/null || echo "")
if echo "$files" | grep -qE "^ops/|^diagnostics/|\.py$|\.sh$|\.js$|\.html$|\.css$|\.json$"; then
if echo "$files" | grep -qE "^ops/|\.py$|\.sh$|\.js$|\.html$|\.css$|\.json$"; then
echo "true"
else
echo "false"

View file

@ -41,9 +41,13 @@ while IFS= read -r branch; do
COUNT=$((COUNT + 1))
if [[ "$last_date" < "$CUTOFF" ]]; then
if ! git merge-base --is-ancestor "$branch" "$REMOTE/main" 2>/dev/null; then
echo " SKIP (unmerged): $short ($last_date)"
continue
fi
if $EXECUTE; then
echo " DELETE: $short ($last_date)"
git push "$REMOTE" --delete "$short" 2>/dev/null && DELETE_COUNT=$((DELETE_COUNT + 1)) || echo " FAILED: $short"
git push "$REMOTE" --delete "$short" 2>&1 && DELETE_COUNT=$((DELETE_COUNT + 1)) || echo " FAILED: $short"
else
echo " WOULD DELETE: $short ($last_date)"
DELETE_COUNT=$((DELETE_COUNT + 1))