|
|
@@ -157,6 +157,83 @@ def run(ctx):
|
|
|
status, body = saddle_api("GET", "/api/saddle/task/99999", r4_token)
|
|
|
check(status == 404, "unknown task id returns 404")
|
|
|
|
|
|
+ print("== goal 9: the read-only goals API (list_goals wire) ==")
|
|
|
+ # The daemon's list_goals tool reads this endpoint: bearer-only,
|
|
|
+ # project-scoped (primary OR linked), newest first, with a
|
|
|
+ # goal_id detail carrying the plan/report presence flags.
|
|
|
+ status, _ = saddle_api("GET", "/api/saddle/goals", None)
|
|
|
+ check(status == 401, "goals endpoint rejects a missing token")
|
|
|
+ status, _ = saddle_api("GET", "/api/saddle/goals?project_id=1", r4_token)
|
|
|
+ check(status == 200, "goals endpoint ignores stray params (project is by name)")
|
|
|
+
|
|
|
+ # A linked-project goal: the primary's rows plus a goal that only
|
|
|
+ # references the project through goal_links.
|
|
|
+ conn = db()
|
|
|
+ conn.execute("insert into projects (name, git_url, default_branch, created_at)"
|
|
|
+ " values ('goal-fabrica',"
|
|
|
+ " 'ssh://fabrica.unitatem.net:222/clanker/clanker-saddle.git',"
|
|
|
+ " 'main', strftime('%s','now'))")
|
|
|
+ conn.commit()
|
|
|
+ conn.close()
|
|
|
+ state = slot_public(auth, "/projects", "projects")
|
|
|
+ goal_fabrica = int([p["id"] for p in state["projects"]
|
|
|
+ if p["name"] == "goal-fabrica"][0])
|
|
|
+ goal_via_link = create_goal_row({
|
|
|
+ "project_id": "1", "title": "Linked-context goal",
|
|
|
+ "brief": "Touches goal-fabrica only through its link.",
|
|
|
+ "branch_name": "goal/linked-context"})
|
|
|
+ act(auth, goal_state(goal_via_link)["add_link"],
|
|
|
+ {"linked_project_id": str(goal_fabrica),
|
|
|
+ "relationship": "co-developed"})
|
|
|
+
|
|
|
+ status, body = saddle_api("GET", "/api/saddle/goals?project=demo",
|
|
|
+ r4_token)
|
|
|
+ check(status == 200 and any(int(g["id"]) == goal for g in body.get("goals", [])),
|
|
|
+ "the primary project's goals list (name lookup)")
|
|
|
+ row = [g for g in body["goals"] if int(g["id"]) == goal_via_link][0]
|
|
|
+ check(row["title"] == "Linked-context goal"
|
|
|
+ and row["status"] in ("draft", "planning", "plan_review", "active", "cancelled", "done")
|
|
|
+ and row["branch"] == "goal/linked-context"
|
|
|
+ and len(row.get("updated_at") or "") > 0,
|
|
|
+ "compact goal rows carry id/title/status/branch/updated_at")
|
|
|
+ ids = [int(g["id"]) for g in body["goals"]]
|
|
|
+ check(ids == sorted(ids, reverse=True),
|
|
|
+ "the goal list reads newest first")
|
|
|
+ check(not any(int(g["id"]) == goal_via_link
|
|
|
+ for g in body["goals"] if int(g["id"]) != goal_via_link),
|
|
|
+ "no duplicate rows for a goal listed once")
|
|
|
+
|
|
|
+ status, body = saddle_api("GET", "/api/saddle/goals?project=goal-fabrica",
|
|
|
+ r4_token)
|
|
|
+ check(status == 200
|
|
|
+ and [int(g["id"]) for g in body.get("goals", [])] == [goal_via_link],
|
|
|
+ "a linked-only project lists its linked goals (not the primary's)")
|
|
|
+
|
|
|
+ status, body = saddle_api("GET", "/api/saddle/goals", r4_token)
|
|
|
+ check(status == 200 and body.get("goals") == [],
|
|
|
+ "no project param yields an empty list (strict, human-confirmed)")
|
|
|
+ status, body = saddle_api("GET",
|
|
|
+ "/api/saddle/goals?project=no-such-project", r4_token)
|
|
|
+ check(status == 200 and body.get("goals") == [],
|
|
|
+ "an unknown project name yields an empty list, not an error")
|
|
|
+
|
|
|
+ status, body = saddle_api("GET",
|
|
|
+ "/api/saddle/goals?goal_id=%d" % goal, r4_token)
|
|
|
+ detail = body.get("goal") or {}
|
|
|
+ check(status == 200 and int(detail.get("id") or 0) == goal
|
|
|
+ and detail.get("title") == "Lineage goal"
|
|
|
+ and detail.get("branch") == "goal/lineage"
|
|
|
+ and "Carry the goal lineage." in detail.get("brief", ""),
|
|
|
+ "the goal_id detail carries id/title/branch/brief")
|
|
|
+ check(detail.get("has_plan") is True and detail.get("has_report") is False,
|
|
|
+ "the detail's has_plan/has_report reflect the goal's documents")
|
|
|
+ detail_link = saddle_api("GET", "/api/saddle/goals?goal_id=%d" % goal_via_link,
|
|
|
+ r4_token)[1].get("goal") or {}
|
|
|
+ check(detail_link.get("has_plan") is False,
|
|
|
+ "a goal without an approved plan reports has_plan false")
|
|
|
+ status, body = saddle_api("GET", "/api/saddle/goals?goal_id=99999", r4_token)
|
|
|
+ check(status == 404, "an unknown goal_id returns 404")
|
|
|
+
|
|
|
print("== settle: released workers resume the parked agent (coalesced) ==")
|
|
|
# Release the held workers: their completions wake the parked goal
|
|
|
# agent (affinity — the same fake hosts it) with the batched
|