From 362fb9ada98c474bfa00c4e35be97c7f469de06d Mon Sep 17 00:00:00 2001 From: Christian Nennemann Date: Sat, 4 Apr 2026 08:44:06 +0200 Subject: [PATCH] fix: address v0.5.0 review findings - Add --to/--test-cmd mutual exclusivity guard in rollback script - Convert all jq string interpolation to --arg (cmd_extract, cmd_inject, cmd_forget) - Fix CRITICAL/WARNING grep to match table rows only (not prose) - Add thorough+cycle-1 guard to fast-path bash snippet in check-phase - Clarify prev_run_id selection comment (tail -1 = most recent non-current) --- lib/archeflow-memory.sh | 34 ++++++++++++++++++---------------- lib/archeflow-rollback.sh | 6 ++++++ skills/check-phase/SKILL.md | 13 +++++++------ 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/lib/archeflow-memory.sh b/lib/archeflow-memory.sh index 6c47cf7..3bfcd17 100755 --- a/lib/archeflow-memory.sh +++ b/lib/archeflow-memory.sh @@ -141,14 +141,14 @@ cmd_extract() { if [[ "$overlap" -ge 50 ]]; then # Match found — update existing lesson local tmp_file="${LESSONS_FILE}.tmp" - jq -c " - if .id == \"$lesson_id\" then + jq -c --arg lid "$lesson_id" --arg ts "$(now_ts)" --arg rid "$run_id" ' + if .id == $lid then .frequency += 1 | - .ts = \"$(now_ts)\" | - .last_seen_run = \"$run_id\" | + .ts = $ts | + .last_seen_run = $rid | .runs_since_last_seen = 0 else . end - " "$LESSONS_FILE" > "$tmp_file" + ' "$LESSONS_FILE" > "$tmp_file" mv "$tmp_file" "$LESSONS_FILE" matched=true updated=$((updated + 1)) @@ -224,25 +224,25 @@ cmd_inject() { # - Filter by domain (match or "general") and archetype (if provided) # - Sort by frequency desc, cap at 10 local lessons - lessons=$(jq -c " + lessons=$(jq -c --arg domain "$domain" --arg archetype "$archetype" ' select( - (.type == \"preference\") or + (.type == "preference") or (.frequency >= 5) or ( (.frequency >= 2) and ( - (\"$domain\" == \"\") or - (.domain == \"$domain\") or - (.domain == \"general\") + ($domain == "") or + (.domain == $domain) or + (.domain == "general") ) and ( - (\"$archetype\" == \"\") or + ($archetype == "") or (.archetype == null) or - (.archetype == \"$archetype\") + (.archetype == $archetype) ) ) ) - " "$LESSONS_FILE" 2>/dev/null | jq -sc 'sort_by(-.frequency) | .[:10][]' 2>/dev/null || true) + ' "$LESSONS_FILE" 2>/dev/null | jq -sc 'sort_by(-.frequency) | .[:10][]' 2>/dev/null || true) if [[ -z "$lessons" ]]; then return 0 @@ -382,7 +382,9 @@ cmd_regression_check() { fi local prev_run_id + # Get the most recent run that is not the current one (index is append-newest-last) prev_run_id=$(jq -r --arg rid "$run_id" 'select(.run_id != $rid) | .run_id' "$INDEX_FILE" 2>/dev/null | tail -1) + # Note: tail -1 gives the last non-current entry, which is the most recent previous run if [[ -z "$prev_run_id" ]]; then echo "[archeflow-memory] No previous run found — skipping regression check." >&2 @@ -555,17 +557,17 @@ cmd_forget() { ensure_dir # Check if the lesson exists - if ! jq -e "select(.id == \"$target_id\")" "$LESSONS_FILE" > /dev/null 2>&1; then + if ! jq -e --arg tid "$target_id" 'select(.id == $tid)' "$LESSONS_FILE" > /dev/null 2>&1; then echo "Error: lesson $target_id not found." >&2 exit 1 fi # Archive the lesson - jq -c "select(.id == \"$target_id\")" "$LESSONS_FILE" >> "$ARCHIVE_FILE" + jq -c --arg tid "$target_id" 'select(.id == $tid)' "$LESSONS_FILE" >> "$ARCHIVE_FILE" # Remove from lessons local tmp_file="${LESSONS_FILE}.tmp" - jq -c "select(.id != \"$target_id\")" "$LESSONS_FILE" > "$tmp_file" + jq -c --arg tid "$target_id" 'select(.id != $tid)' "$LESSONS_FILE" > "$tmp_file" mv "$tmp_file" "$LESSONS_FILE" echo "[archeflow-memory] Forgot lesson $target_id (moved to archive)" >&2 diff --git a/lib/archeflow-rollback.sh b/lib/archeflow-rollback.sh index 50fec3a..08658f4 100755 --- a/lib/archeflow-rollback.sh +++ b/lib/archeflow-rollback.sh @@ -29,6 +29,12 @@ while [[ $# -gt 0 ]]; do esac done +# Mutual exclusivity check +if [[ -n "$TARGET_PHASE" && -n "$TEST_CMD" ]]; then + echo "ERROR: --to and --test-cmd are mutually exclusive." >&2 + exit 2 +fi + # --- Phase rollback mode --- if [[ -n "$TARGET_PHASE" ]]; then # Validate phase name diff --git a/skills/check-phase/SKILL.md b/skills/check-phase/SKILL.md index 40170ab..1fab47d 100644 --- a/skills/check-phase/SKILL.md +++ b/skills/check-phase/SKILL.md @@ -109,18 +109,19 @@ Save output to `.archeflow/artifacts/${RUN_ID}/check-guardian.md`. After Guardian completes, parse its output before spawning other reviewers: ```bash -CRITICAL_COUNT=$(grep -ci "CRITICAL" ".archeflow/artifacts/${RUN_ID}/check-guardian.md" || echo 0) -WARNING_COUNT=$(grep -ci "WARNING" ".archeflow/artifacts/${RUN_ID}/check-guardian.md" || echo 0) +CRITICAL_COUNT=$(grep -c "| CRITICAL |" ".archeflow/artifacts/${RUN_ID}/check-guardian.md" || echo 0) +WARNING_COUNT=$(grep -c "| WARNING |" ".archeflow/artifacts/${RUN_ID}/check-guardian.md" || echo 0) -if [[ "$CRITICAL_COUNT" -eq 0 && "$WARNING_COUNT" -eq 0 && "$ESCALATED" != "true" ]]; then - # A2 fast-path: skip remaining reviewers +# A2 fast-path: skip remaining reviewers if Guardian is clean +# Exception: first cycle of thorough workflows always spawns all reviewers +if [[ "$CRITICAL_COUNT" -eq 0 && "$WARNING_COUNT" -eq 0 \ + && "$ESCALATED" != "true" \ + && ! ("$WORKFLOW" == "thorough" && "$CYCLE" -eq 1) ]]; then echo "Guardian fast-path: 0 CRITICAL, 0 WARNING — skipping remaining reviewers." # Proceed directly to Act phase fi ``` -**Exception:** First cycle of `thorough` workflows always spawns all reviewers, even if Guardian is clean. - ### Step 3: Parallel Reviewer Spawning If A2 does not trigger, spawn remaining reviewers in parallel based on workflow: