2026-03-25 15:28:29 +00:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# =============================================================================
|
|
|
|
|
# exec-briefing.sh — Daily morning briefing via the executive assistant
|
|
|
|
|
#
|
|
|
|
|
# Cron wrapper: spawns a one-shot Claude session that gathers factory state
|
|
|
|
|
# and posts a morning briefing to Matrix. Unlike the interactive session,
|
|
|
|
|
# this runs, posts, and exits.
|
|
|
|
|
#
|
|
|
|
|
# Usage:
|
|
|
|
|
# exec-briefing.sh [projects/disinto.toml]
|
|
|
|
|
#
|
|
|
|
|
# Cron:
|
|
|
|
|
# 0 7 * * * /path/to/disinto/exec/exec-briefing.sh
|
|
|
|
|
# =============================================================================
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
|
FACTORY_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
|
|
|
|
|
|
|
|
export PROJECT_TOML="${1:-$FACTORY_ROOT/projects/disinto.toml}"
|
|
|
|
|
# shellcheck source=../lib/env.sh
|
|
|
|
|
source "$FACTORY_ROOT/lib/env.sh"
|
|
|
|
|
# shellcheck source=../lib/agent-session.sh
|
|
|
|
|
source "$FACTORY_ROOT/lib/agent-session.sh"
|
|
|
|
|
# shellcheck source=../lib/formula-session.sh
|
|
|
|
|
source "$FACTORY_ROOT/lib/formula-session.sh"
|
|
|
|
|
# shellcheck source=../lib/guard.sh
|
|
|
|
|
source "$FACTORY_ROOT/lib/guard.sh"
|
|
|
|
|
|
|
|
|
|
LOG_FILE="$SCRIPT_DIR/exec.log"
|
|
|
|
|
# shellcheck disable=SC2034 # consumed by run_formula_and_monitor
|
|
|
|
|
SESSION_NAME="exec-briefing-${PROJECT_NAME}"
|
|
|
|
|
PHASE_FILE="/tmp/exec-briefing-${PROJECT_NAME}.phase"
|
|
|
|
|
# shellcheck disable=SC2034
|
|
|
|
|
PHASE_POLL_INTERVAL=10
|
|
|
|
|
|
|
|
|
|
log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%S)Z] $*" >> "$LOG_FILE"; }
|
|
|
|
|
|
|
|
|
|
# ── Guards ────────────────────────────────────────────────────────────────
|
|
|
|
|
check_active exec
|
|
|
|
|
acquire_cron_lock "/tmp/exec-briefing.lock"
|
|
|
|
|
check_memory 2000
|
|
|
|
|
|
|
|
|
|
log "--- Exec briefing start ---"
|
|
|
|
|
|
2026-03-25 15:34:55 +00:00
|
|
|
# ── Load compass (required) ────────────────────────────────────────────
|
2026-03-25 15:45:15 +00:00
|
|
|
COMPASS_FILE="${EXEC_COMPASS:-${HOME}/.disinto/compass.md}"
|
|
|
|
|
if [ ! -f "$COMPASS_FILE" ]; then
|
|
|
|
|
log "FATAL: compass not found at ${COMPASS_FILE} — exec agent refuses to start without its compass"
|
2026-03-25 15:34:55 +00:00
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
COMPASS_BLOCK=$(cat "$COMPASS_FILE")
|
|
|
|
|
|
|
|
|
|
# ── Load character (voice/relationships from repo) ────────────────────
|
2026-03-25 15:28:29 +00:00
|
|
|
CHARACTER_FILE="${EXEC_CHARACTER:-$SCRIPT_DIR/CHARACTER.md}"
|
|
|
|
|
CHARACTER_BLOCK=""
|
|
|
|
|
if [ -f "$CHARACTER_FILE" ]; then
|
|
|
|
|
CHARACTER_BLOCK=$(cat "$CHARACTER_FILE")
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-25 15:34:55 +00:00
|
|
|
# Merge: compass first, then character
|
|
|
|
|
CHARACTER_BLOCK="${COMPASS_BLOCK}
|
|
|
|
|
|
|
|
|
|
${CHARACTER_BLOCK}"
|
|
|
|
|
|
2026-03-25 15:28:29 +00:00
|
|
|
# ── Load memory ─────────────────────────────────────────────────────────
|
|
|
|
|
MEMORY_BLOCK="(no previous memory)"
|
|
|
|
|
MEMORY_FILE="$PROJECT_REPO_ROOT/exec/MEMORY.md"
|
|
|
|
|
if [ -f "$MEMORY_FILE" ]; then
|
|
|
|
|
MEMORY_BLOCK=$(cat "$MEMORY_FILE")
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ── Gather factory state ───────────────────────────────────────────────
|
|
|
|
|
# Open issues count
|
|
|
|
|
OPEN_ISSUES=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
|
|
|
|
"${FORGE_API}/issues?state=open&type=issues&limit=1" 2>/dev/null \
|
|
|
|
|
| jq 'length' 2>/dev/null || echo "?")
|
|
|
|
|
|
|
|
|
|
# Open PRs
|
|
|
|
|
OPEN_PRS=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
|
|
|
|
"${FORGE_API}/pulls?state=open&limit=1" 2>/dev/null \
|
|
|
|
|
| jq 'length' 2>/dev/null || echo "?")
|
|
|
|
|
|
|
|
|
|
# Pending vault items
|
|
|
|
|
VAULT_PENDING=$(ls "$FACTORY_ROOT/vault/pending/" 2>/dev/null | wc -l || echo 0)
|
|
|
|
|
|
|
|
|
|
# Recent agent activity (last 24h log lines)
|
|
|
|
|
RECENT_ACTIVITY=""
|
|
|
|
|
for agent_dir in supervisor planner predictor gardener dev review; do
|
|
|
|
|
latest_log="$FACTORY_ROOT/${agent_dir}/${agent_dir}.log"
|
|
|
|
|
if [ -f "$latest_log" ]; then
|
|
|
|
|
lines=$(grep "$(date -u +%Y-%m-%d)" "$latest_log" 2>/dev/null | tail -5 || true)
|
|
|
|
|
if [ -n "$lines" ]; then
|
|
|
|
|
RECENT_ACTIVITY="${RECENT_ACTIVITY}
|
|
|
|
|
### ${agent_dir} (today)
|
|
|
|
|
${lines}
|
|
|
|
|
"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# ── Build briefing prompt ──────────────────────────────────────────────
|
|
|
|
|
# shellcheck disable=SC2034 # consumed by run_formula_and_monitor
|
|
|
|
|
PROMPT="You are the executive assistant for ${FORGE_REPO}. This is a morning briefing run.
|
|
|
|
|
|
|
|
|
|
## Your character
|
|
|
|
|
${CHARACTER_BLOCK}
|
|
|
|
|
|
|
|
|
|
## Your memory
|
|
|
|
|
${MEMORY_BLOCK}
|
|
|
|
|
|
|
|
|
|
## Current factory state
|
|
|
|
|
- Open issues: ${OPEN_ISSUES}
|
|
|
|
|
- Open PRs: ${OPEN_PRS}
|
|
|
|
|
- Pending vault items: ${VAULT_PENDING}
|
|
|
|
|
|
|
|
|
|
${RECENT_ACTIVITY}
|
|
|
|
|
|
|
|
|
|
## Task
|
|
|
|
|
|
|
|
|
|
Produce a morning briefing for the executive. Be concise — 10-15 lines max.
|
|
|
|
|
Cover:
|
|
|
|
|
1. What happened overnight (merges, CI failures, agent activity)
|
|
|
|
|
2. What needs attention today (blocked issues, vault items, stale work)
|
|
|
|
|
3. One observation or recommendation
|
|
|
|
|
|
|
|
|
|
Fetch additional data if needed:
|
|
|
|
|
- Open issues: curl -sf -H \"Authorization: token \${FORGE_TOKEN}\" '${FORGE_API}/issues?state=open&type=issues&limit=20'
|
|
|
|
|
- Recent closed: curl -sf -H \"Authorization: token \${FORGE_TOKEN}\" '${FORGE_API}/issues?state=closed&type=issues&limit=10&sort=updated&direction=desc'
|
|
|
|
|
- Prerequisite tree: cat ${PROJECT_REPO_ROOT}/planner/prerequisite-tree.md
|
|
|
|
|
|
|
|
|
|
Write your briefing between markers:
|
|
|
|
|
\`\`\`
|
|
|
|
|
---EXEC-RESPONSE-START---
|
|
|
|
|
Your briefing here.
|
|
|
|
|
---EXEC-RESPONSE-END---
|
|
|
|
|
\`\`\`
|
|
|
|
|
|
|
|
|
|
Then log the briefing to: ${PROJECT_REPO_ROOT}/exec/journal/\$(date -u +%Y-%m-%d).md
|
|
|
|
|
(append, don't overwrite — there may be interactive sessions later today)
|
|
|
|
|
|
|
|
|
|
Then: echo 'PHASE:done' > '${PHASE_FILE}'
|
|
|
|
|
|
|
|
|
|
## Environment
|
|
|
|
|
FACTORY_ROOT=${FACTORY_ROOT}
|
|
|
|
|
PROJECT_REPO_ROOT=${PROJECT_REPO_ROOT}
|
|
|
|
|
PHASE_FILE=${PHASE_FILE}"
|
|
|
|
|
|
|
|
|
|
# ── Run session ──────────────────────────────────────────────────────────
|
|
|
|
|
export CLAUDE_MODEL="${CLAUDE_MODEL:-sonnet}"
|
|
|
|
|
run_formula_and_monitor "exec-briefing" 600
|
|
|
|
|
|
|
|
|
|
log "--- Exec briefing done ---"
|