9365def3dd
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>
102 lines
3.3 KiB
Python
102 lines
3.3 KiB
Python
"""Tests para set_dod_contract."""
|
|
|
|
import json
|
|
import os
|
|
|
|
import pytest
|
|
|
|
from .set_dod_contract import set_dod_contract
|
|
|
|
|
|
def _read(path: str) -> dict:
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
return json.load(f)
|
|
|
|
|
|
def test_escribe_contrato_en_goal_json_existente_y_preserva_otros_campos(tmp_path):
|
|
sid = "sess-existente"
|
|
path = os.path.join(str(tmp_path), f"{sid}.json")
|
|
existing = {
|
|
"goal": "implementar la fase 2",
|
|
"phase": "build",
|
|
"dod": "tests verdes (volatil, lo escribe el hook)",
|
|
"history": [{"turn": 1, "action": "init"}],
|
|
"prompts": ["primer prompt"],
|
|
"emojis": "🚀",
|
|
}
|
|
with open(path, "w", encoding="utf-8") as f:
|
|
json.dump(existing, f)
|
|
|
|
res = set_dod_contract(sid, "criterio fijo de aceptacion", status="pending", goals_dir=str(tmp_path))
|
|
|
|
assert res["written"] is True
|
|
assert res["session_id"] == sid
|
|
assert res["path"] == path
|
|
assert res["dod_contract"] == "criterio fijo de aceptacion"
|
|
assert res["dod_status"] == "pending"
|
|
|
|
data = _read(path)
|
|
# Las dos claves nuevas estan.
|
|
assert data["dod_contract"] == "criterio fijo de aceptacion"
|
|
assert data["dod_status"] == "pending"
|
|
# TODO el resto se preserva intacto.
|
|
assert data["goal"] == "implementar la fase 2"
|
|
assert data["phase"] == "build"
|
|
assert data["dod"] == "tests verdes (volatil, lo escribe el hook)"
|
|
assert data["history"] == [{"turn": 1, "action": "init"}]
|
|
assert data["prompts"] == ["primer prompt"]
|
|
assert data["emojis"] == "🚀"
|
|
|
|
|
|
def test_crea_el_archivo_si_no_existe(tmp_path):
|
|
sid = "sess-nueva"
|
|
path = os.path.join(str(tmp_path), f"{sid}.json")
|
|
assert not os.path.exists(path)
|
|
|
|
res = set_dod_contract(sid, "contrato nuevo", goals_dir=str(tmp_path))
|
|
|
|
assert os.path.exists(path)
|
|
assert res["dod_status"] == "pending" # default
|
|
data = _read(path)
|
|
assert data == {"dod_contract": "contrato nuevo", "dod_status": "pending"}
|
|
|
|
|
|
def test_status_invalido_lanza_valueerror_sin_escribir(tmp_path):
|
|
sid = "sess-bad-status"
|
|
path = os.path.join(str(tmp_path), f"{sid}.json")
|
|
|
|
with pytest.raises(ValueError):
|
|
set_dod_contract(sid, "un contrato", status="done", goals_dir=str(tmp_path))
|
|
|
|
assert not os.path.exists(path)
|
|
|
|
|
|
def test_contract_vacio_lanza_valueerror_sin_escribir(tmp_path):
|
|
sid = "sess-empty"
|
|
path = os.path.join(str(tmp_path), f"{sid}.json")
|
|
|
|
with pytest.raises(ValueError):
|
|
set_dod_contract(sid, "", goals_dir=str(tmp_path))
|
|
with pytest.raises(ValueError):
|
|
set_dod_contract(sid, " ", goals_dir=str(tmp_path))
|
|
|
|
assert not os.path.exists(path)
|
|
|
|
|
|
def test_reescribir_actualiza_sin_duplicar(tmp_path):
|
|
sid = "sess-reescribe"
|
|
path = os.path.join(str(tmp_path), f"{sid}.json")
|
|
|
|
set_dod_contract(sid, "contrato v1", status="pending", goals_dir=str(tmp_path))
|
|
set_dod_contract(sid, "contrato v2", status="met", goals_dir=str(tmp_path))
|
|
|
|
data = _read(path)
|
|
assert data["dod_contract"] == "contrato v2"
|
|
assert data["dod_status"] == "met"
|
|
# Solo dos claves, ninguna duplicada ni residual.
|
|
assert sorted(data.keys()) == ["dod_contract", "dod_status"]
|
|
# El archivo es JSON valido unico (no concatenado).
|
|
raw = open(path, "r", encoding="utf-8").read()
|
|
assert raw.count('"dod_contract"') == 1
|
|
assert raw.count('"dod_status"') == 1
|