feat: cerebro reactivo del meta-orquestador (flow 0012, fase 2)
Primitivas (python/functions/infra): - drain_fleet_events: consume la cola del watcher (~/.claude/fleet/ events.jsonl) desde un cursor, agrupa por clasificacion, marca urgentes. 7 tests. - set_dod_contract: escribe el DoD-contrato fijo (dod_contract/dod_status) en el goal.json de un agente sin pisar el resto (escritura atomica). 5 tests. Skill /orquestador evolucionado (sin romper lo existente): vigila la flota por su DoD (no por 'esta vivo'). Nueva seccion 'Consumo de la cola de la flota': DoD-contrato obligatorio al lanzar, drenar la cola, politicas por clasificacion (RECLAMA escala / DICE_TERMINADO verifica / ESTANCADO nudge / MAL_LANZADO re-DoD), verificador independiente del ejecutor (lee el report vs dod_contract), splitter con tope de fan-out, y cadencia (drain al actuar + heartbeat). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
"""Escribe un DoD-contrato fijo y su estado en el sidecar goal.json de una sesion Claude."""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
_VALID_STATUS = ("pending", "met", "failed")
|
||||
|
||||
|
||||
def set_dod_contract(
|
||||
session_id: str,
|
||||
contract: str,
|
||||
status: str = "pending",
|
||||
goals_dir: str | None = None,
|
||||
) -> dict:
|
||||
"""Escribe el DoD-contrato y su estado en el goal.json de una sesion Claude.
|
||||
|
||||
Lee el sidecar `<goals_dir>/<session_id>.json` si existe, preservando
|
||||
TODOS sus campos (goal, phase, dod, history, prompts, etc.), y actualiza
|
||||
solo las claves `dod_contract` y `dod_status`. La escritura es atomica
|
||||
(tmp + os.replace) para no corromper el archivo si el proceso muere a mitad.
|
||||
|
||||
Args:
|
||||
session_id: ID de la sesion Claude. Da nombre al archivo
|
||||
`<session_id>.json` dentro de goals_dir.
|
||||
contract: Criterio de aceptacion estable contra el que se evalua la
|
||||
terminacion del agente. No puede ser vacio.
|
||||
status: Estado del contrato. Uno de "pending", "met" o "failed".
|
||||
goals_dir: Directorio de sidecars de goal. Por defecto
|
||||
`~/.claude/goals`.
|
||||
|
||||
Returns:
|
||||
dict con session_id, path, dod_contract, dod_status y written.
|
||||
|
||||
Raises:
|
||||
ValueError: si contract es vacio o status no es valido. No escribe nada.
|
||||
"""
|
||||
if not contract or not contract.strip():
|
||||
raise ValueError("contract no puede ser vacio: un DoD-contrato vacio no tiene sentido")
|
||||
if status not in _VALID_STATUS:
|
||||
raise ValueError(
|
||||
f"status invalido: {status!r}. Debe ser uno de {_VALID_STATUS}"
|
||||
)
|
||||
|
||||
if goals_dir is None:
|
||||
goals_dir = os.path.join(os.path.expanduser("~"), ".claude", "goals")
|
||||
|
||||
os.makedirs(goals_dir, exist_ok=True)
|
||||
path = os.path.join(goals_dir, f"{session_id}.json")
|
||||
|
||||
data: dict = {}
|
||||
if os.path.exists(path):
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
loaded = json.load(f)
|
||||
if isinstance(loaded, dict):
|
||||
data = loaded
|
||||
except (json.JSONDecodeError, OSError):
|
||||
# Archivo corrupto o ilegible: no se pierde lo nuevo, se parte limpio.
|
||||
data = {}
|
||||
|
||||
# Solo se tocan estas dos claves; el resto del dict se preserva intacto.
|
||||
data["dod_contract"] = contract
|
||||
data["dod_status"] = status
|
||||
|
||||
tmp_path = f"{path}.tmp"
|
||||
with open(tmp_path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
f.write("\n")
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
os.replace(tmp_path, path)
|
||||
|
||||
return {
|
||||
"session_id": session_id,
|
||||
"path": path,
|
||||
"dod_contract": contract,
|
||||
"dod_status": status,
|
||||
"written": True,
|
||||
}
|
||||
Reference in New Issue
Block a user