mirror of
https://github.com/boshu2/agentops.git
synced 2026-09-14 15:08:13 +08:00
d297e035df
Encodes the 4 load-bearing lessons from the 170-cycle /evolve run that ended in operator override at cycle 154 + 13-cycle rescope arc: 1. Mechanical session-state persistence — new scripts/evolve-update-session-state.sh derives session-state.json from cycle-history.jsonl tail. Fixes the cycle 91 → 169 drift that accumulated when Claude forgot to write state at each cycle boundary. Also computes mode_repeat_streak + idle_streak as derived signals. 2. Sticky dormancy marker — Step 3 hard-gate now writes .agents/evolve/DORMANT, Step 1 short-circuits on subsequent fires with zero further tool calls. Eliminates the post-dormancy cron-fire inference burn. 3. Metronome detector — Step 3.0 reads mode_repeat_streak, soft-blocks at >=3 (skip rung), hard-blocks at >=5 (operator override required). Would have caught the cycles 144-154 CLI-wiring template-application loop 5 cycles sooner. 4. Pre-flight schema check — Step 4 samples 2 consumer call sites before architectural migration cycles, compares field-use against port surface. Would have caught cycles 156-157 narrowness post-mortem before burning the /rpi cost. Net SKILL.md change: +14/-7 lines (inline gates only; detail in references/). Two new reference docs document thresholds, naming discipline, and worked examples from this repo's actual cycle history. Validated: skill-lint passes, codex parity passes, go build + vet clean, derive script tested with synthetic histories (streak math correct for all 3 cases: metronome=3, dormancy=2, idempotence). bd: soc-p5nh
147 lines
4.3 KiB
Bash
Executable File
147 lines
4.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Derive .agents/evolve/session-state.json from cycle-history.jsonl tail.
|
|
#
|
|
# session-state.json is *resume-only* state — derived signals computed from
|
|
# the canonical cycle ledger. Running this after every cycle-history write
|
|
# keeps state coherent without depending on Claude remembering to update it.
|
|
#
|
|
# Computed fields:
|
|
# cycle last cycle number in history
|
|
# last_result last entry's result
|
|
# last_mode last entry's mode
|
|
# last_selected_source last entry's selected_source
|
|
# idle_streak trailing run of result in {idle, unchanged}
|
|
# mode_repeat_streak trailing run of identical mode values
|
|
# updated_at this script's run timestamp (ISO 8601 UTC)
|
|
#
|
|
# Preserves any other keys already present in session-state.json (skill-owned
|
|
# fields like program_path, validation_commands, generator_empty_streak).
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: scripts/evolve-update-session-state.sh [options]
|
|
|
|
Derive session-state.json fields from cycle-history.jsonl tail.
|
|
|
|
Options:
|
|
--history <path> History file (default: .agents/evolve/cycle-history.jsonl)
|
|
--state <path> State file (default: .agents/evolve/session-state.json)
|
|
--print Print the merged state to stdout in addition to writing
|
|
-h, --help Show help
|
|
|
|
Exit codes:
|
|
0 state written
|
|
1 invalid args or unwritable destination
|
|
2 history file missing or empty (state untouched)
|
|
EOF
|
|
}
|
|
|
|
HISTORY=".agents/evolve/cycle-history.jsonl"
|
|
STATE=".agents/evolve/session-state.json"
|
|
PRINT=false
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--history) HISTORY="${2:-}"; shift 2 ;;
|
|
--state) STATE="${2:-}"; shift 2 ;;
|
|
--print) PRINT=true; shift ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) echo "ERROR: unknown option: $1" >&2; usage >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
command -v jq >/dev/null 2>&1 || { echo "ERROR: jq required" >&2; exit 1; }
|
|
|
|
if [[ ! -s "$HISTORY" ]]; then
|
|
echo "WARN: history missing or empty: $HISTORY (state untouched)" >&2
|
|
exit 2
|
|
fi
|
|
|
|
mkdir -p "$(dirname "$STATE")"
|
|
|
|
# Read the last well-formed JSON line.
|
|
LAST="$(awk 'NF { line = $0 } END { print line }' "$HISTORY")"
|
|
printf '%s' "$LAST" | jq -e . >/dev/null 2>&1 || {
|
|
echo "ERROR: last history line is not valid JSON" >&2
|
|
exit 1
|
|
}
|
|
|
|
CYCLE="$(printf '%s' "$LAST" | jq -r '.cycle // empty')"
|
|
LAST_RESULT="$(printf '%s' "$LAST" | jq -r '.result // ""')"
|
|
LAST_MODE="$(printf '%s' "$LAST" | jq -r '.mode // ""')"
|
|
LAST_SOURCE="$(printf '%s' "$LAST" | jq -r '.selected_source // ""')"
|
|
UPDATED_AT="$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date -Iseconds)"
|
|
|
|
# Trailing run of idle/unchanged results.
|
|
IDLE_STREAK="$(
|
|
awk -F'"result"[[:space:]]*:[[:space:]]*"' '
|
|
NF > 1 {
|
|
v = $2; sub(/".*/, "", v)
|
|
if (v == "idle" || v == "unchanged") streak++
|
|
else streak = 0
|
|
}
|
|
END { print streak + 0 }
|
|
' "$HISTORY"
|
|
)"
|
|
|
|
# Trailing run of identical mode values.
|
|
MODE_REPEAT_STREAK="$(
|
|
awk -F'"mode"[[:space:]]*:[[:space:]]*"' -v last="$LAST_MODE" '
|
|
NF > 1 {
|
|
v = $2; sub(/".*/, "", v)
|
|
lines[NR] = v
|
|
total = NR
|
|
}
|
|
END {
|
|
if (last == "") { print 0; exit }
|
|
streak = 0
|
|
for (i = total; i >= 1; i--) {
|
|
if (lines[i] == last) streak++
|
|
else break
|
|
}
|
|
print streak
|
|
}
|
|
' "$HISTORY"
|
|
)"
|
|
|
|
# Load existing state (or empty object) and merge derived fields on top.
|
|
EXISTING="{}"
|
|
if [[ -s "$STATE" ]]; then
|
|
if jq -e . "$STATE" >/dev/null 2>&1; then
|
|
EXISTING="$(cat "$STATE")"
|
|
else
|
|
echo "WARN: existing state is not valid JSON, rebuilding from scratch" >&2
|
|
fi
|
|
fi
|
|
|
|
MERGED="$(
|
|
jq -n \
|
|
--argjson existing "$EXISTING" \
|
|
--argjson cycle "${CYCLE:-0}" \
|
|
--arg last_result "$LAST_RESULT" \
|
|
--arg last_mode "$LAST_MODE" \
|
|
--arg last_source "$LAST_SOURCE" \
|
|
--argjson idle_streak "${IDLE_STREAK:-0}" \
|
|
--argjson mode_repeat_streak "${MODE_REPEAT_STREAK:-0}" \
|
|
--arg updated_at "$UPDATED_AT" \
|
|
'$existing + {
|
|
cycle: $cycle,
|
|
last_result: $last_result,
|
|
last_mode: $last_mode,
|
|
last_selected_source: $last_source,
|
|
idle_streak: $idle_streak,
|
|
mode_repeat_streak: $mode_repeat_streak,
|
|
updated_at: $updated_at
|
|
}'
|
|
)"
|
|
|
|
TMP="$(mktemp "${STATE}.XXXXXX")"
|
|
printf '%s\n' "$MERGED" >"$TMP"
|
|
mv "$TMP" "$STATE"
|
|
|
|
if [[ "$PRINT" == true ]]; then
|
|
printf '%s\n' "$MERGED"
|
|
fi
|