#!/usr/bin/env bash # archeflow-event.sh — Append a structured event to an ArcheFlow run's JSONL log. # # Usage: ./lib/archeflow-event.sh '' [parent_seqs] # # Examples: # ./lib/archeflow-event.sh 2026-04-03-der-huster run.start plan "" '{"task":"Write Der Huster"}' # ./lib/archeflow-event.sh 2026-04-03-der-huster agent.complete plan creator '{"duration_ms":167522}' 2 # ./lib/archeflow-event.sh 2026-04-03-der-huster phase.transition do "" '{"from":"plan","to":"do"}' 3,4 # ./lib/archeflow-event.sh 2026-04-03-der-huster fix.applied act "" '{"source":"guardian"}' 8 # # Parent seqs: comma-separated seq numbers of causal parent events (DAG). # "2" → single parent [2] # "3,4" → multiple parents [3,4] (fan-in) # "" → root event [] # # Events are appended to .archeflow/events/.jsonl # If the events directory doesn't exist, it is created automatically. set -euo pipefail if [[ $# -lt 4 ]]; then echo "Usage: $0 [json_data] [parent_seqs]" >&2 exit 1 fi RUN_ID="$1" TYPE="$2" PHASE="$3" AGENT="$4" DATA="${5:-"{}"}" PARENT_RAW="${6:-}" EVENTS_DIR=".archeflow/events" EVENT_FILE="${EVENTS_DIR}/${RUN_ID}.jsonl" mkdir -p "$EVENTS_DIR" # Determine sequence number (count existing lines + 1) if [[ -f "$EVENT_FILE" ]]; then SEQ=$(( $(wc -l < "$EVENT_FILE") + 1 )) else SEQ=1 fi TS=$(date -u +%Y-%m-%dT%H:%M:%SZ) # Validate JSON data if ! echo "$DATA" | jq empty 2>/dev/null; then echo "Error: invalid JSON in data argument: $DATA" >&2 exit 1 fi # Build parent array from comma-separated seq numbers if [[ -z "$PARENT_RAW" ]]; then PARENT_JSON="[]" elif [[ "$PARENT_RAW" =~ ^[0-9]+(,[0-9]+)*$ ]]; then PARENT_JSON="[${PARENT_RAW}]" else echo "Error: invalid parent format (expected comma-separated integers): $PARENT_RAW" >&2 exit 1 fi # Construct the event using jq for reliable JSON assembly # Agent is passed as --arg (string), then converted to null if empty via jq expression EVENT=$(jq -cn \ --arg ts "$TS" \ --arg run_id "$RUN_ID" \ --argjson seq "$SEQ" \ --argjson parent "$PARENT_JSON" \ --arg type "$TYPE" \ --arg phase "$PHASE" \ --arg agent_raw "$AGENT" \ --argjson data "$DATA" \ '{ts:$ts, run_id:$run_id, seq:$seq, parent:$parent, type:$type, phase:$phase, agent:(if $agent_raw == "" then null else $agent_raw end), data:$data}' ) echo "$EVENT" >> "$EVENT_FILE" # Print confirmation to stderr (non-intrusive) echo "[archeflow-event] #${SEQ} ${TYPE} (${PHASE}/${AGENT:-_})" >&2