|
@@ -145,14 +145,14 @@ namespace ClankerSaddle {
|
|
|
/**
|
|
/**
|
|
|
* Cancels a goal: the cascade cancels every non-terminal task under
|
|
* Cancels a goal: the cascade cancels every non-terminal task under
|
|
|
* it (drafts are deleted); live daemons hosting its running tasks
|
|
* it (drafts are deleted); live daemons hosting its running tasks
|
|
|
- * are aborted first (the cancel action can reach SaddleService
|
|
|
|
|
- * without the service dependency cycle).
|
|
|
|
|
|
|
+ * are aborted first — the orchestration lives in
|
|
|
|
|
+ * {@link LifecycleService.cancel_goal} (no service dependency
|
|
|
|
|
+ * cycle).
|
|
|
*/
|
|
*/
|
|
|
public class CancelGoalAction : SealedGoalAction {
|
|
public class CancelGoalAction : SealedGoalAction {
|
|
|
|
|
|
|
|
private GoalService goals = inject<GoalService>();
|
|
private GoalService goals = inject<GoalService>();
|
|
|
- private SaddleService saddles = inject<SaddleService>();
|
|
|
|
|
- private TaskService tasks = inject<TaskService>();
|
|
|
|
|
|
|
+ private LifecycleService lifecycle = inject<LifecycleService>();
|
|
|
private UserService users = inject<UserService>();
|
|
private UserService users = inject<UserService>();
|
|
|
|
|
|
|
|
public override async DirectiveBuilder handle() throws GLib.Error {
|
|
public override async DirectiveBuilder handle() throws GLib.Error {
|
|
@@ -166,17 +166,7 @@ namespace ClankerSaddle {
|
|
|
if (goal == null) {
|
|
if (goal == null) {
|
|
|
return directives().notify("error", @"No goal exists with id $(goal_id)").navigate("/goals");
|
|
return directives().notify("error", @"No goal exists with id $(goal_id)").navigate("/goals");
|
|
|
}
|
|
}
|
|
|
- foreach (var task in yield tasks.list_tasks_for_goal(goal_id)) {
|
|
|
|
|
- if (task.status == STATUS_ASSIGNED || task.status == STATUS_WORKING) {
|
|
|
|
|
- try {
|
|
|
|
|
- yield saddles.abort_live_session(tasks, task.id, "goal cancelled");
|
|
|
|
|
- } catch (Error e) {
|
|
|
|
|
- warning("[ClankerSaddle] Goal-cancel abort of task %s failed: %s",
|
|
|
|
|
- task.id.to_string(), e.message);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- yield goals.cancel_goal(goal_id);
|
|
|
|
|
|
|
+ yield lifecycle.cancel_goal(goal_id);
|
|
|
return directives().notify("success", @"Goal '$(((!)goal).title)' cancelled — its tasks went with it")
|
|
return directives().notify("success", @"Goal '$(((!)goal).title)' cancelled — its tasks went with it")
|
|
|
.navigate(@"/goals/$(goal_id)");
|
|
.navigate(@"/goals/$(goal_id)");
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
@@ -191,14 +181,14 @@ namespace ClankerSaddle {
|
|
|
* 'active' and its run task re-queues for another attempt — the
|
|
* 'active' and its run task re-queues for another attempt — the
|
|
|
* failure summary clears with the attempt (plan 16 S4: the
|
|
* failure summary clears with the attempt (plan 16 S4: the
|
|
|
* terminal cache is never copied to the thread), exactly like
|
|
* terminal cache is never copied to the thread), exactly like
|
|
|
- * {@link RetryTaskAction}.
|
|
|
|
|
|
|
+ * {@link RetryTaskAction}. The abort-first orchestration and the
|
|
|
|
|
+ * goal-row revival live in {@link LifecycleService.retry_goal_run}
|
|
|
|
|
+ * + {@link GoalService.retry_goal}.
|
|
|
*/
|
|
*/
|
|
|
public class RetryGoalRunAction : SealedGoalAction {
|
|
public class RetryGoalRunAction : SealedGoalAction {
|
|
|
|
|
|
|
|
private GoalService goals = inject<GoalService>();
|
|
private GoalService goals = inject<GoalService>();
|
|
|
- private TaskService tasks = inject<TaskService>();
|
|
|
|
|
- private SaddleService saddles = inject<SaddleService>();
|
|
|
|
|
- private InvercargillSql.Orm.OrmSession db = Inversion.inject<InvercargillSql.Orm.OrmSession>();
|
|
|
|
|
|
|
+ private LifecycleService lifecycle = inject<LifecycleService>();
|
|
|
private UserService users = inject<UserService>();
|
|
private UserService users = inject<UserService>();
|
|
|
|
|
|
|
|
public override async DirectiveBuilder handle() throws GLib.Error {
|
|
public override async DirectiveBuilder handle() throws GLib.Error {
|
|
@@ -223,21 +213,7 @@ namespace ClankerSaddle {
|
|
|
"The goal agent's run task is not in a failed state")
|
|
"The goal agent's run task is not in a failed state")
|
|
|
.navigate(@"/goals/$(goal_id)");
|
|
.navigate(@"/goals/$(goal_id)");
|
|
|
}
|
|
}
|
|
|
- try {
|
|
|
|
|
- yield saddles.abort_live_session(tasks, ((!)run).id, "supervision retried");
|
|
|
|
|
- } catch (Error e) {
|
|
|
|
|
- warning("[ClankerSaddle] Goal-retry abort of run task %s failed: %s",
|
|
|
|
|
- ((!)run).id.to_string(), e.message);
|
|
|
|
|
- }
|
|
|
|
|
- yield tasks.retry_task(((!)run).id);
|
|
|
|
|
- ((!)goal).status = GOAL_ACTIVE;
|
|
|
|
|
- ((!)goal).completed_at = null;
|
|
|
|
|
- yield db.update_async<Goal>((!)goal);
|
|
|
|
|
- // Plan 15 C3: the retry is harness bookkeeping — a
|
|
|
|
|
- // system row on the run thread (the narrative derives
|
|
|
|
|
- // it at read time).
|
|
|
|
|
- yield tasks.add_system_comment(((!)run).id,
|
|
|
|
|
- @"Supervision retried — the goal agent runs again (run task #$(((!)run).id))");
|
|
|
|
|
|
|
+ yield lifecycle.retry_goal_run(goal_id);
|
|
|
return directives().notify("success",
|
|
return directives().notify("success",
|
|
|
@"Goal '$(((!)goal).title)' — supervision requeued for another attempt")
|
|
@"Goal '$(((!)goal).title)' — supervision requeued for another attempt")
|
|
|
.navigate(@"/goals/$(goal_id)");
|
|
.navigate(@"/goals/$(goal_id)");
|
|
@@ -253,13 +229,13 @@ namespace ClankerSaddle {
|
|
|
* task page's ResumeTaskAction): the goal agent's run is stale or
|
|
* task page's ResumeTaskAction): the goal agent's run is stale or
|
|
|
* parked on a dead saddle — abort its session best-effort, close
|
|
* parked on a dead saddle — abort its session best-effort, close
|
|
|
* it and requeue the run; the next dispatch restores the daemon's
|
|
* it and requeue the run; the next dispatch restores the daemon's
|
|
|
- * workspace context (restore-on-dispatch).
|
|
|
|
|
|
|
+ * workspace context (restore-on-dispatch). Owned by
|
|
|
|
|
+ * {@link LifecycleService.requeue_goal_run}.
|
|
|
*/
|
|
*/
|
|
|
public class RequeueGoalRunAction : SealedGoalAction {
|
|
public class RequeueGoalRunAction : SealedGoalAction {
|
|
|
|
|
|
|
|
private GoalService goals = inject<GoalService>();
|
|
private GoalService goals = inject<GoalService>();
|
|
|
- private TaskService tasks = inject<TaskService>();
|
|
|
|
|
- private SaddleService saddles = inject<SaddleService>();
|
|
|
|
|
|
|
+ private LifecycleService lifecycle = inject<LifecycleService>();
|
|
|
private UserService users = inject<UserService>();
|
|
private UserService users = inject<UserService>();
|
|
|
|
|
|
|
|
public override async DirectiveBuilder handle() throws GLib.Error {
|
|
public override async DirectiveBuilder handle() throws GLib.Error {
|
|
@@ -281,10 +257,7 @@ namespace ClankerSaddle {
|
|
|
"The goal agent's run is not requeueable")
|
|
"The goal agent's run is not requeueable")
|
|
|
.navigate(@"/goals/$(goal_id)");
|
|
.navigate(@"/goals/$(goal_id)");
|
|
|
}
|
|
}
|
|
|
- yield saddles.abort_live_session(tasks, ((!)run).id, "requeued");
|
|
|
|
|
- yield tasks.requeue_task(((!)run).id);
|
|
|
|
|
- yield tasks.add_system_comment(((!)run).id,
|
|
|
|
|
- @"Run requeued — the daemon restores the agent's workspace on the next dispatch (run task #$(((!)run).id))");
|
|
|
|
|
|
|
+ yield lifecycle.requeue_goal_run(goal_id);
|
|
|
return directives().notify("success",
|
|
return directives().notify("success",
|
|
|
"Run requeued; the daemon restores the agent's context on the next dispatch")
|
|
"Run requeued; the daemon restores the agent's context on the next dispatch")
|
|
|
.navigate(@"/goals/$(goal_id)");
|
|
.navigate(@"/goals/$(goal_id)");
|