Q53: stalled goal reviews leave a visible state and a resume path

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-08 09:25:46 -04:00
1 parent 69e991ff93
commit ec2698575d
15 files changed
+149 -45

No files matched your search

+1 -1
View File
@@ -83,7 +83,7 @@ export async function completeGoalTool(args: GoalCompletionArgs): Promise<string
if (!verdict.approved) {
return `The independent audit rejected closing the goal: ${verdict.reason} The goal stays active — close that gap or create the tasks that will.`;
}
deps.db.prepare("update goals set status = 'achieved', updated_at = ? where id = ? and org_id = ?")
deps.db.prepare("update goals set status = 'achieved', stalled_reason = null, updated_at = ? where id = ? and org_id = ?")
.run(new Date().toISOString(), goal.id, ctx.orgId);
new AuditService(deps.db).record({
orgId: ctx.orgId,
+80 -38
View File
@@ -1,6 +1,7 @@
import type { DatabaseSync } from "node:sqlite";
import { RISK_LEVEL, TASK_PRIORITY, TASK_STATUS } from "../../shared/domain";
import { ensureAgentHeartbeat, resolveGoalKickoff } from "./goal-kickoff";
import { AuditService } from "./audit-service";
import type { OrganizationService } from "./organization-service";
interface ReviewableGoalRow {
@@ -11,20 +12,7 @@ interface ReviewableGoalRow {
owner_agent_id: string | null;
}
/**
* Closes the loop the task pipeline leaves open: tasks end at `done`, but a
* company is finished when its GOAL is achieved. Whenever an active goal has
* no open tasks left and real work landed since the last checkpoint, the goal
* owner (usually the CEO) gets one review task: verify the goal against the
* actual state of the workspace, then either mark it achieved (complete_goal)
* or plan the next wave of tasks. Without this, the company drains its board,
* declares nothing, and idles forever under a still-active goal.
*
* Termination is structural: chatter (`chat_wake`) and reviews themselves
* never count as progress, so a review that ends with no new work and no
* completed goal does NOT respawn — the loop only continues while real tasks
* keep completing or the goal is closed.
*/
/** When an active goal's board drains after real progress, hands its owner a review: close the goal or plan the next wave. */
export function spawnGoalReviews(db: DatabaseSync, organization: OrganizationService): string[] {
const goals = db.prepare(`
select g.id, g.org_id, g.title, g.description, g.owner_agent_id
@@ -55,34 +43,88 @@ export function spawnGoalReviews(db: DatabaseSync, organization: OrganizationSer
const created: string[] = [];
for (const goal of goals) {
const plan = resolveGoalKickoff(db, {
orgId: goal.org_id,
goalTitle: goal.title,
goalDescription: goal.description,
ownerAgentId: goal.owner_agent_id,
});
if (!plan) continue;
ensureAgentHeartbeat(db, goal.org_id, plan.assigneeAgentId);
const taskId = organization.createTask({
orgId: goal.org_id,
title: `Goal review: ${goal.title.trim()}`,
description: goalReviewDescription(goal.title),
status: TASK_STATUS.BACKLOG,
assigneeAgentId: plan.assigneeAgentId,
risk: RISK_LEVEL.MEDIUM,
projectId: null,
goalId: goal.id,
workspaceId: plan.workspaceId,
priority: TASK_PRIORITY.HIGH,
requiresFilesystem: plan.workspaceId !== null,
origin: "goal_review",
});
created.push(taskId);
console.info(`[goal-review] all tasks for goal "${goal.title}" are finished; created a review checkpoint for the goal owner.`);
const taskId = createReviewTask(db, organization, goal);
if (taskId) created.push(taskId);
}
return created;
}
/** Closes a stalled goal's stuck review and starts a fresh one; returns the new review task id. */
export function resumeGoal(db: DatabaseSync, organization: OrganizationService, orgId: string, goalId: string): string {
const goal = db.prepare("select id, org_id, title, description, owner_agent_id, stalled_reason from goals where id = ? and org_id = ? and status = 'active'")
.get(goalId, orgId) as (ReviewableGoalRow & { stalled_reason: string | null }) | undefined;
if (!goal) throw new Error("Active goal was not found.");
if (!goal.stalled_reason) throw new Error(`Goal "${goal.title}" is not stalled.`);
db.prepare("update tasks set status = 'done', updated_at = ? where goal_id = ? and origin = 'goal_review' and status <> 'done'")
.run(new Date().toISOString(), goalId);
const taskId = createReviewTask(db, organization, goal);
if (!taskId) throw new Error(`Goal "${goal.title}" has no active agent to review it.`);
new AuditService(db).record({ orgId, entityType: "goal", entityId: goalId, actorType: "employee", actorId: null, action: "resumed", payload: { reviewTaskId: taskId } });
return taskId;
}
function createReviewTask(db: DatabaseSync, organization: OrganizationService, goal: ReviewableGoalRow): string | null {
const plan = resolveGoalKickoff(db, {
orgId: goal.org_id,
goalTitle: goal.title,
goalDescription: goal.description,
ownerAgentId: goal.owner_agent_id,
});
if (!plan) return null;
ensureAgentHeartbeat(db, goal.org_id, plan.assigneeAgentId);
const taskId = organization.createTask({
orgId: goal.org_id,
title: `Goal review: ${goal.title.trim()}`,
description: goalReviewDescription(goal.title),
status: TASK_STATUS.BACKLOG,
assigneeAgentId: plan.assigneeAgentId,
risk: RISK_LEVEL.MEDIUM,
projectId: null,
goalId: goal.id,
workspaceId: plan.workspaceId,
priority: TASK_PRIORITY.HIGH,
requiresFilesystem: plan.workspaceId !== null,
origin: "goal_review",
});
db.prepare("update goals set stalled_reason = null where id = ?").run(goal.id);
console.info(`[goal-review] created a review checkpoint for goal "${goal.title}".`);
return taskId;
}
/** Gives an active goal a visible stalled_reason when its review loop can no longer move on its own; returns how many stalled now. */
export function markStalledGoals(db: DatabaseSync): number {
const stalls = db.prepare(`
select g.id, g.org_id, g.title,
case when exists (
select 1 from tasks r where r.goal_id = g.id and r.origin = 'goal_review' and r.status <> 'done' and r.stalled_reason is not null
) then 'budget' else 'silent' end as kind
from goals g
where g.status = 'active' and g.stalled_reason is null
and (
exists (select 1 from tasks r where r.goal_id = g.id and r.origin = 'goal_review' and r.status <> 'done' and r.stalled_reason is not null)
or (
not exists (select 1 from tasks t where t.goal_id = g.id and t.status in ('backlog', 'in_progress', 'review'))
and exists (select 1 from tasks r where r.goal_id = g.id and r.origin = 'goal_review')
and not exists (
select 1 from tasks t
where t.goal_id = g.id and t.status = 'done' and t.origin is null
and t.updated_at > (select max(r.created_at) from tasks r where r.goal_id = g.id and r.origin = 'goal_review')
)
)
)
`).all() as Array<{ id: string; org_id: string; title: string; kind: "budget" | "silent" }>;
const audit = new AuditService(db);
for (const goal of stalls) {
const reason = goal.kind === "budget"
? "Stalled: the goal review spent its run budget without a decision. Resume it to run a fresh review."
: "Stalled: the last goal review neither closed the goal nor planned new work. Resume it to run a fresh review.";
db.prepare("update goals set stalled_reason = ? where id = ?").run(reason, goal.id);
console.warn(`[goal-review] goal "${goal.title}" ${reason}`);
audit.record({ orgId: goal.org_id, entityType: "goal", entityId: goal.id, actorType: "system", actorId: null, action: "stalled", payload: { reason } });
}
return stalls.length;
}
function goalReviewDescription(goalTitle: string): string {
return [
`Every task under the goal "${goalTitle.trim()}" is finished, but the goal itself is still open. Decide what happens next — this is a checkpoint, not a formality.`,