3 Commits

Author SHA1 Message Date
egutierrez a42cad769a chore: auto-commit (1 archivos)
- .claude/hooks/goal_tracker.sh

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-24 14:37:06 +02:00
egutierrez e355a65b5b Merge orq/dod-no-haiku: poblar goal de ejecutores + quitar regeneracion LLM del dod 2026-06-21 15:14:26 +02:00
integrador d4640a0660 fix(goal): poblar goal de ejecutores spawneados (statusline/FleetView sin objetivo)
spawn_fleet_agent pre-crea ~/.claude/goals/<sid>.json con solo
{role, parent_orchestrator} antes del primer prompt. goal_tracker.sh usaba
'el archivo existe' como proxy de 'objetivo definitivo', así que para esos
ejecutores nunca lanzaba goal_autogen: el goal quedaba vacío para siempre y el
statusline (LINE0) y FleetView mostraban '(sin objetivo)'.

Fix: 'definitivo' ahora exige .goal NO vacío (no solo que el archivo exista).
Cuando el archivo existe pero sin goal (ejecutor spawneado), se fija un goal
provisional PRESERVANDO role/parent_orchestrator y se lanza autogen, que lo pisa
con el definitivo. No regresiona el caso definitivo ni el primer-prompt sin
archivo (verificado con 3 casos).
2026-06-21 14:59:15 +02:00
+30 -7
View File
@@ -27,8 +27,16 @@ PROMPT_TRIM=$(printf '%s' "$PROMPT" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$
# Bloquea el prompt (no llega al agente) y muestra <reason> al usuario.
block() { jq -n --arg r "$1" '{decision:"block", reason:$r}'; exit 0; }
# Los comandos meta (objetivo:/dod:/pausa) solo cuentan en la PRIMERA linea del
# prompt. Sin este recorte, un prompt multilinea que CONTENGA una linea
# "objetivo:"/"dod:" en su interior (p.ej. una <task-notification> de un subagente
# que trae DoD-contratos esbozados, o un mensaje que cita un DoD) se malinterpreta
# como comando meta: se bloquea con decision:block (atrapando la notificacion) y se
# corrompe el .dod del goal con esa linea interior. Anclar a la 1a linea lo evita.
FIRST_LINE=$(printf '%s' "$PROMPT" | head -1)
# --- objetivo: <texto> (manual; preserva el DoD si ya existia) ---
GOAL_LINE=$(printf '%s' "$PROMPT" | grep -ioE '^[[:space:]]*(objetivo|meta|goal)[[:space:]]*:[[:space:]]*.+' | head -1)
GOAL_LINE=$(printf '%s' "$FIRST_LINE" | grep -ioE '^[[:space:]]*(objetivo|meta|goal)[[:space:]]*:[[:space:]]*.+' | head -1)
if [ -n "$GOAL_LINE" ]; then
NEWGOAL=$(printf '%s' "$GOAL_LINE" | sed -E 's/^[^:]*:[[:space:]]*//; s/[[:space:]]+$//')
case "$NEWGOAL" in
@@ -57,7 +65,7 @@ fi
# asi el built-in /rename de Claude Code queda libre para renombrar la sesion.
# --- dod: <texto> ---
DOD_LINE=$(printf '%s' "$PROMPT" | grep -ioE '^[[:space:]]*dod[[:space:]]*:[[:space:]]*.+' | head -1)
DOD_LINE=$(printf '%s' "$FIRST_LINE" | grep -ioE '^[[:space:]]*dod[[:space:]]*:[[:space:]]*.+' | head -1)
if [ -n "$DOD_LINE" ]; then
NEWDOD=$(printf '%s' "$DOD_LINE" | sed -E 's/^[^:]*:[[:space:]]*//; s/[[:space:]]+$//')
[ -f "$F" ] || block "Fija primero un objetivo (\"objetivo: ...\") antes del DoD."
@@ -100,9 +108,18 @@ esac
# + `dod_status` (set_dod_contract.py, sin LLM). El DoD inicial lo fija autogen
# una vez; el usuario lo ajusta a mano con "dod: ...".
PROV="false"
[ -f "$F" ] && PROV=$(jq -r '.provisional // false' "$F" 2>/dev/null)
GOAL_NOW=""
if [ -f "$F" ]; then
PROV=$(jq -r '.provisional // false' "$F" 2>/dev/null)
GOAL_NOW=$(jq -r '.goal // ""' "$F" 2>/dev/null)
fi
if [ -f "$F" ] && [ "$PROV" != "true" ]; then
# "Objetivo definitivo" = archivo con goal NO vacio y no provisional. El check de
# goal no vacio es clave para los ejecutores lanzados por spawn_fleet_agent: su
# goal.json se PRE-CREA con solo {role, parent_orchestrator} (sin goal). Sin este
# guard, el hook tomaria ese archivo como objetivo definitivo y nunca lanzaria
# autogen, dejando el goal vacio para siempre (statusline y FleetView sin objetivo).
if [ -f "$F" ] && [ "$PROV" != "true" ] && [ -n "$GOAL_NOW" ]; then
G=$(jq -r '.goal // ""' "$F" 2>/dev/null)
P=$(jq -r '.phase // ""' "$F" 2>/dev/null)
D=$(jq -r '.dod // ""' "$F" 2>/dev/null)
@@ -114,11 +131,17 @@ else
# con el definitivo al terminar (su guard respeta .provisional).
if [ "${#PROMPT_TRIM}" -ge 12 ]; then
TMP="${F}.tmp.$$"
if [ -f "$F" ]; then
# Ya habia provisional: conserva su goal, solo acumula el prompt.
PROV_GOAL=$(printf '%s' "$PROMPT_TRIM" | head -c 70)
if [ -n "$GOAL_NOW" ]; then
# Ya habia goal provisional: conserva su goal, solo acumula el prompt.
jq --arg p "$PROMPT_TRIM" '.prompts = ((.prompts // []) + [$p])[-12:]' "$F" > "$TMP" 2>/dev/null && mv "$TMP" "$F" || rm -f "$TMP"
elif [ -f "$F" ]; then
# Archivo PRE-CREADO por spawn_fleet_agent ({role, parent_orchestrator})
# sin goal: fija el provisional PRESERVANDO los campos existentes (role,
# parent_orchestrator) y deja que autogen lo pise con el definitivo.
jq --arg g "$PROV_GOAL" --arg p "$PROMPT_TRIM" \
'. + {goal:$g, phase:"planificando", history:["planificando"], prompts:[$p], provisional:true}' "$F" > "$TMP" 2>/dev/null && mv "$TMP" "$F" || rm -f "$TMP"
else
PROV_GOAL=$(printf '%s' "$PROMPT_TRIM" | head -c 70)
jq -n --arg g "$PROV_GOAL" --arg p "$PROMPT_TRIM" \
'{goal:$g, phase:"planificando", history:["planificando"], prompts:[$p], provisional:true}' > "$TMP" 2>/dev/null && mv "$TMP" "$F" || rm -f "$TMP"
fi