feat(statusline): objetivo fijo (identificativo), solo el DoD se refina

Simplifica el modelo segun feedback:
- El OBJETIVO (target) es el identificativo de la terminal: se genera una vez y
  NUNCA cambia automaticamente. goal_refine deja de tocarlo.
- goal_refine ahora ajusta SOLO el DoD para mantenerlo coherente con los prompts.
- Se elimina la deteccion de cambio de tarea y el icono de alerta ⚠️ (campo alert
  ya no se escribe ni se lee; queda inocuo en JSONs antiguos).
- Se elimina el comando 'recalcular' y goal_refine.sh modo force.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 16:23:20 +02:00
parent a3ecb6a4cf
commit 50290a71e7
3 changed files with 31 additions and 76 deletions
+15 -44
View File
@@ -1,22 +1,13 @@
#!/bin/bash
# Mantiene objetivo+DoD coherentes con los prompts acumulados del usuario y avisa
# si la terminal empieza a mezclar tareas. Lo lanza goal_tracker.sh en background
# (no bloquea). Usa ask_llm (haiku, API directa; nunca `claude -p`).
# Ajusta SOLO el DoD para mantenerlo coherente con los prompts del usuario hacia
# el objetivo. El OBJETIVO es fijo (identificativo de la terminal) y NUNCA se
# toca aqui. Lo lanza goal_tracker.sh en background (no bloquea). Usa ask_llm
# (haiku, API directa; nunca `claude -p`, ver regla llm_invocation.md).
#
# Args: <session_id> <goal_json_file> [force]
# force: regenera siempre objetivo+DoD desde los prompts y limpia la alerta
# (lo usa el comando "recalcular").
#
# Decision normal (sin force): el modelo devuelve
# {"action":"same|refine|switch","goal":"...","dod":"..."}
# - same : el objetivo ya describe bien el conjunto -> no se toca nada.
# - refine : ajusta objetivo/DoD para englobar mejor TODO lo pedido (estable).
# - switch : el ultimo prompt es una tarea COMPLETAMENTE distinta -> alert=true
# (una terminal deberia ser una sola tarea). No cambia el objetivo.
# Args: <session_id> <goal_json_file>
SID="$1"
F="$2"
FORCE="$3"
[ -f "$F" ] || exit 0
PY="$HOME/fn_registry/python/.venv/bin/python3"
@@ -30,48 +21,28 @@ DOD=$(jq -r '.dod // ""' "$F" 2>/dev/null)
PROMPTS=$(jq -r '(.prompts // []) | to_entries | map("\(.key+1). \(.value)") | join("\n")' "$F" 2>/dev/null)
[ -z "$PROMPTS" ] && exit 0
if [ "$FORCE" = "force" ]; then
SYS="Regenera el OBJETIVO y el DoD de una terminal de trabajo a partir de la lista de prompts del usuario, sintetizando lo que se esta haciendo realmente. Responde SOLO JSON en una linea, sin markdown: {\"action\":\"refine\",\"goal\":\"...\",\"dod\":\"...\"}. goal: maximo 9 palabras en espanol. dod: condicion concreta de terminado, maximo 9 palabras en espanol."
else
SYS="Mantienes coherentes el OBJETIVO y el DoD de una terminal de trabajo segun los prompts del usuario. Principio: una terminal = una sola tarea. Analiza si el conjunto de prompts sigue describiendo UNA tarea coherente. Responde SOLO JSON en una linea, sin markdown: {\"action\":\"same|refine|switch\",\"goal\":\"...\",\"dod\":\"...\"}. action=same: el objetivo actual ya describe bien el conjunto (deja goal y dod vacios, no se cambia nada). action=refine: conviene ajustar objetivo/DoD para englobar mejor TODO lo pedido, manteniendolo estable y breve (goal y dod max 9 palabras cada uno, en espanol). action=switch: el ULTIMO prompt introduce una tarea COMPLETAMENTE distinta y no relacionada con el objetivo actual (la terminal esta mezclando tareas); deja goal y dod vacios. Se conservador con 'switch': uselo solo ante un cambio claro de tema, no por matices o sub-tareas del mismo objetivo."
fi
SYS="Dado un OBJETIVO fijo de una terminal de trabajo y los prompts del usuario, define o ajusta el DoD (definition of done): la condicion concreta de 'terminado' para ese objetivo, coherente con lo que el usuario va pidiendo. El OBJETIVO no se toca, solo el DoD. Responde SOLO JSON en una linea, sin markdown: {\"dod\":\"...\"}. dod en espanol, breve, maximo 9 palabras. Si el DoD actual ya es adecuado, devuelvelo igual."
PROMPT="OBJETIVO ACTUAL: ${GOAL}
PROMPT="OBJETIVO (fijo, no lo cambies): ${GOAL}
DoD ACTUAL: ${DOD}
PROMPTS DEL USUARIO (orden cronologico):
${PROMPTS}
Responde el JSON:"
Responde el JSON con el DoD:"
RAW=$("$PY" "$ASK" --system "$SYS" "$PROMPT" 2>/dev/null)
[ -z "$RAW" ] && exit 0
JSON=$(printf '%s' "$RAW" | tr '\n' ' ' | grep -o '{[^{}]*}' | head -1)
[ -z "$JSON" ] && exit 0
ACTION=$(printf '%s' "$JSON" | jq -r '.action // ""' 2>/dev/null)
NG=$(printf '%s' "$JSON" | jq -r '.goal // ""' 2>/dev/null)
ND=$(printf '%s' "$JSON" | jq -r '.dod // ""' 2>/dev/null)
[ -z "$ND" ] && exit 0
case "$ACTION" in
refine)
TMP="${F}.tmp.$$"
if jq --arg g "$NG" --arg d "$ND" '
(if $g != "" then .goal=$g else . end)
| (if $d != "" then .dod=$d else . end)
| .alert=false
' "$F" > "$TMP" 2>/dev/null; then
mv "$TMP" "$F"
else
rm -f "$TMP"
fi
;;
switch)
TMP="${F}.tmp.$$"
if jq '.alert=true' "$F" > "$TMP" 2>/dev/null; then mv "$TMP" "$F"; else rm -f "$TMP"; fi
;;
*)
: # same / desconocido -> no tocar
;;
esac
TMP="${F}.tmp.$$"
if jq --arg d "$ND" '.dod=$d' "$F" > "$TMP" 2>/dev/null; then
mv "$TMP" "$F"
else
rm -f "$TMP"
fi
exit 0