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)
This commit is contained in:
2026-04-04 08:44:06 +02:00
parent c3f5df8161
commit 362fb9ada9
3 changed files with 31 additions and 22 deletions

View File

@@ -141,14 +141,14 @@ cmd_extract() {
if [[ "$overlap" -ge 50 ]]; then if [[ "$overlap" -ge 50 ]]; then
# Match found — update existing lesson # Match found — update existing lesson
local tmp_file="${LESSONS_FILE}.tmp" local tmp_file="${LESSONS_FILE}.tmp"
jq -c " jq -c --arg lid "$lesson_id" --arg ts "$(now_ts)" --arg rid "$run_id" '
if .id == \"$lesson_id\" then if .id == $lid then
.frequency += 1 | .frequency += 1 |
.ts = \"$(now_ts)\" | .ts = $ts |
.last_seen_run = \"$run_id\" | .last_seen_run = $rid |
.runs_since_last_seen = 0 .runs_since_last_seen = 0
else . end else . end
" "$LESSONS_FILE" > "$tmp_file" ' "$LESSONS_FILE" > "$tmp_file"
mv "$tmp_file" "$LESSONS_FILE" mv "$tmp_file" "$LESSONS_FILE"
matched=true matched=true
updated=$((updated + 1)) updated=$((updated + 1))
@@ -224,25 +224,25 @@ cmd_inject() {
# - Filter by domain (match or "general") and archetype (if provided) # - Filter by domain (match or "general") and archetype (if provided)
# - Sort by frequency desc, cap at 10 # - Sort by frequency desc, cap at 10
local lessons local lessons
lessons=$(jq -c " lessons=$(jq -c --arg domain "$domain" --arg archetype "$archetype" '
select( select(
(.type == \"preference\") or (.type == "preference") or
(.frequency >= 5) or (.frequency >= 5) or
( (
(.frequency >= 2) and (.frequency >= 2) and
( (
(\"$domain\" == \"\") or ($domain == "") or
(.domain == \"$domain\") or (.domain == $domain) or
(.domain == \"general\") (.domain == "general")
) and ) and
( (
(\"$archetype\" == \"\") or ($archetype == "") or
(.archetype == null) 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 if [[ -z "$lessons" ]]; then
return 0 return 0
@@ -382,7 +382,9 @@ cmd_regression_check() {
fi fi
local prev_run_id 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) 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 if [[ -z "$prev_run_id" ]]; then
echo "[archeflow-memory] No previous run found — skipping regression check." >&2 echo "[archeflow-memory] No previous run found — skipping regression check." >&2
@@ -555,17 +557,17 @@ cmd_forget() {
ensure_dir ensure_dir
# Check if the lesson exists # 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 echo "Error: lesson $target_id not found." >&2
exit 1 exit 1
fi fi
# Archive the lesson # 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 # Remove from lessons
local tmp_file="${LESSONS_FILE}.tmp" 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" mv "$tmp_file" "$LESSONS_FILE"
echo "[archeflow-memory] Forgot lesson $target_id (moved to archive)" >&2 echo "[archeflow-memory] Forgot lesson $target_id (moved to archive)" >&2

View File

@@ -29,6 +29,12 @@ while [[ $# -gt 0 ]]; do
esac esac
done 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 --- # --- Phase rollback mode ---
if [[ -n "$TARGET_PHASE" ]]; then if [[ -n "$TARGET_PHASE" ]]; then
# Validate phase name # Validate phase name

View File

@@ -109,18 +109,19 @@ Save output to `.archeflow/artifacts/${RUN_ID}/check-guardian.md`.
After Guardian completes, parse its output before spawning other reviewers: After Guardian completes, parse its output before spawning other reviewers:
```bash ```bash
CRITICAL_COUNT=$(grep -ci "CRITICAL" ".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 -ci "WARNING" ".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 if Guardian is clean
# A2 fast-path: skip remaining reviewers # 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." echo "Guardian fast-path: 0 CRITICAL, 0 WARNING — skipping remaining reviewers."
# Proceed directly to Act phase # Proceed directly to Act phase
fi fi
``` ```
**Exception:** First cycle of `thorough` workflows always spawns all reviewers, even if Guardian is clean.
### Step 3: Parallel Reviewer Spawning ### Step 3: Parallel Reviewer Spawning
If A2 does not trigger, spawn remaining reviewers in parallel based on workflow: If A2 does not trigger, spawn remaining reviewers in parallel based on workflow: