Forráskód Böngészése

integration: repo-qualified goal_branches rows, plan-12 doc rot swept

parse_push_state labels array rows 'repo: branch' when the daemon's
full {repo, branch, pushed} shape arrives (multi-repo goals stay
distinguishable); bare-branch and map forms render unchanged. Swept
the subtask-era remnants: dead parent_task_id view fields, stale
next-task/tasks-list/callback doc comments, the timeline park wording,
the task-create form doc.
clanker 1 hónapja
szülő
commit
0cc0b32eb3

+ 8 - 7
src/Api/SaddleApi.vala

@@ -366,9 +366,9 @@ namespace ClankerSaddle.Api {
      * API behind the daemon's `saddle tasks` CLI (wire contract):
      * authenticated by the per-saddle bearer token (plan 10 — any
      * registered saddle's credential authorises the read-only view).
-     * With `project_id` only that project's tasks; `q` matches title,
-     * description or plan (case-insensitive substring). Compact rows —
-     * id, title, status, parent_task_id.
+     * With `project_id` only that project's tasks; `q` matches title
+     * or description (case-insensitive substring). Compact rows — id,
+     * title, status, goal_id.
      */
     public class SaddleTaskListEndpoint : Object, Endpoint {
 
@@ -918,10 +918,11 @@ namespace ClankerSaddle.Api {
      * to exactly one winner.
      *
      * - **200** + the full assignment payload (today's `/start` shape:
-     *   `task` — with pending block answers — `rules`, `subtasks`,
-     *   `parent` for subtasks, `project_notes`, `callback`,
-     *   `llm_config`, `git_identity` from the saddle row, `session_id`,
-     *   and for resumed workspaces `resume_notice`/`branch_advanced`).
+     *   `task` — with pending block answers — `rules`,
+     *   `project_notes`, `callback`, `llm_config`, `git_identity`
+     *   from the saddle row, the plan-12 `kind`/`mode`/`creator`/
+     *   `goal`/`ephemeral_branch` context, `session_id`, and for
+     *   resumed workspaces `resume_notice`/`branch_advanced`).
      * - **204** — nothing is offerable; the daemon sleeps until woken.
      */
     public class SaddleNextTaskEndpoint : Object, Endpoint {

+ 1 - 1
src/Services/CapabilityService.vala

@@ -50,7 +50,7 @@ namespace ClankerSaddle.Services {
         }
 
         /**
-         * The capability named exactly `name`, or null — the subtask
+         * The capability named exactly `name`, or null — the task
          * callback's tag validation lookup (names are unique).
          */
         public async Capability? find_by_name(string name) throws Error {

+ 1 - 1
src/Services/TimelineService.vala

@@ -92,7 +92,7 @@ namespace ClankerSaddle.Services {
                     events.add(new TimelineEvent() {
                         kind = "suspended", at_epoch = ((!)task.suspended_at).to_unix(),
                         task_id = task.id, task_title = title,
-                        text = "Parked — waiting on subtasks", sequence = sequence++
+                        text = "Parked — goal agent awaiting its tasks", sequence = sequence++
                     });
                 }
                 if (task.completed_at != null) {

+ 5 - 6
src/actions/TaskActions.vala

@@ -36,12 +36,11 @@ namespace ClankerSaddle {
     /**
      * Creates a task from the queue form (project and title required).
      * The task lands in DRAFT — the human Enqueue action queues it;
-     * there is no agent path. Priority clamps to 1–9; the optional
-     * parent id supports manual subtasks. Branch fields are optional —
-     * `branch_from` prefills from the project's default branch and
-     * `task_branch` is inherited from the parent task when left blank.
-     * The plan-auto-approve and auto-enqueue-subtasks options store
-     * the per-task plan-11 levers.
+     * there is no agent path. Priority clamps to 1–9. Branch fields
+     * are optional — `branch_from` prefills from the project's default
+     * branch. The mode select and capabilities apply until a worker is
+     * assigned; `enqueue_after_task_id` is the human-only auto-enqueue
+     * predecessor (goal tasks sequence via the goal agent instead).
      */
     public class CreateTaskAction : StatumAction {
 

+ 16 - 5
src/entrypoints/GoalDetailEntrypoint.vala

@@ -461,8 +461,10 @@ namespace ClankerSaddle {
 
         /**
          * Parses the daemon's goal_branches report into display rows:
-         * both the array form ([{"branch": "g/x", "pushed": true}])
-         * and the object form ({"g/x": true}) are accepted.
+         * both the array form ([{"repo": "r", "branch": "g/x",
+         * "pushed": true}] — the daemon's shape, labelled "r: g/x" so
+         * multi-repo rows stay distinguishable) and the object form
+         * ({"g/x": true}) are accepted.
          */
         public static void parse_push_state(string raw, Vector<GoalPushBranchView> rows) {
             try {
@@ -474,10 +476,19 @@ namespace ClankerSaddle {
                         }
                         var obj = element.get_object();
                         var row = new GoalPushBranchView();
+                        string repo = obj.has_member("repo")
+                            ? (obj.get_string_member("repo") ?? "") : "";
+                        string branch = "";
                         if (obj.has_member("branch")) {
-                            row.branch = obj.get_string_member("branch") ?? "";
-                        } else if (obj.has_member("repo")) {
-                            row.branch = obj.get_string_member("repo") ?? "";
+                            branch = obj.get_string_member("branch") ?? "";
+                        } else if (repo.length > 0) {
+                            branch = repo;
+                            repo = "";
+                        }
+                        if (repo.length > 0 && branch.length > 0) {
+                            row.branch = @"$(repo): $(branch)";
+                        } else {
+                            row.branch = branch;
                         }
                         if (obj.has_member("pushed")) {
                             var pushed = obj.get_member("pushed");

+ 1 - 2
src/entrypoints/TaskDetailEntrypoint.vala

@@ -127,7 +127,6 @@ namespace ClankerSaddle {
         public int64 project_id { get; set; }
         public string project_name { get; set; default = ""; }
         public string git_url { get; set; default = ""; }
-        public int64 parent_task_id { get; set; }
         /** Plan 12: the goal this task belongs to (0 = standalone). */
         public int64 goal_id { get; set; }
         public string goal_title { get; set; default = ""; }
@@ -518,7 +517,7 @@ namespace ClankerSaddle {
     /**
      * Entrypoint for /tasks/{task_id}: the task detail page — plan,
      * progress, blocks (pending ones carry the answer form), the review
-     * banner, comments thread, subtask tree, dependencies, session
+     * banner, comments thread, goal context, dependencies, session
      * history links and the lifecycle actions. The page subscribes to the
      * global pulse and re-runs its sealed refresh action whenever task
      * state changes, so live updates arrive without polling.

+ 0 - 1
src/entrypoints/TasksEntrypoint.vala

@@ -17,7 +17,6 @@ namespace ClankerSaddle {
         public bool terminal { get; set; }
         public bool failed { get; set; }
         public int64 priority { get; set; }
-        public int64 parent_task_id { get; set; }
         public string project_name { get; set; default = ""; }
         public string dependency_summary { get; set; default = ""; }
         public bool has_dependencies { get; set; }

+ 10 - 0
test/web_stack_test.py

@@ -5453,6 +5453,16 @@ def bundle_goals(ctx):
     check({(b["branch"], b["pushed"]) for b in branches}
           == {("goal/flange", True), ("goal/other-repo", False)},
           "the push state renders per branch")
+    # the daemon's real shape carries repo too — multi-repo rows stay
+    # distinguishable as "repo: branch" labels
+    fake.status_extra = {"goal_branches": [
+        {"repo": "repoa", "branch": "goal/flange", "pushed": True},
+        {"repo": "repob", "branch": "goal/flange", "pushed": False}]}
+    act(auth, fake_card["refresh"], {})
+    check(wait_for(lambda: {b["branch"] for b in goal_state(goal1).get("push_branches") or []}
+                   >= {"repoa: goal/flange"}, timeout=30,
+                   what="repo-qualified push state lands") is not None,
+          "repo-qualified goal_branches rows render as 'repo: branch'")
     fake.status_extra = None
 
     # `saddle done` in goal context: the completion review.