Goals end at achieved, not at an empty board: the goal-review loop
The board draining to 280/280 done left the company idle under a still-active goal, with 24 markdown files and zero code as its life's work. Two structural gaps caused it: goals had no lifecycle (status was written once as 'active' and never read again — nothing re-engages anyone when the last task closes), and nothing anywhere distinguished plans from product, so every "engineering" objective closed on documents. The loop, closed: - tasks.origin (migration 055) records provenance: chat_wake for mention vehicles (backfilled from their runs), goal_review for checkpoints, null for real work. Chatter and checkpoints can therefore never count as progress. - spawnGoalReviews (each worker tick): an active goal with zero open tasks and real progress since the last review gets ONE review task for the goal owner. The review demands evidence — list the workspace, read the code, run the tests; documents are how work starts, not proof it happened — and must end in exactly one of: complete_goal, or the next wave of tasks with requires_filesystem engineering. A review closed with neither spawns nothing: termination is structural, not a counter. - complete_goal (CEO or goal owner) finally lets someone say a goal is achieved, audited. - CEO and department-head voices now state the doctrine: a goal that promises software closes on code that builds and passes tests in the workspace, and implementation is delegated with requires_filesystem: true. On the live profile this arms one review: the active platform goal has 0 open tasks and 21 real completed ones, so the CEO gets the checkpoint on next launch and the company starts building instead of stopping.
This commit is contained in:
1 parent
291816aa06
commit
26de3129b1
14 files changed
+365
-4
No files matched your search
@@ -0,0 +1,56 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { DatabaseConnection } from "../../src/main/database/connection";
|
||||
import { OrganizationService } from "../../src/main/services/organization-service";
|
||||
import { ChatService } from "../../src/main/services/chat-service";
|
||||
import { dispatchAgentTool, type AgentToolContext, type AgentToolDeps } from "../../src/main/services/agent-tools";
|
||||
import type { ToolCall } from "../../src/main/services/provider-tool-turn";
|
||||
|
||||
function call(name: string): ToolCall {
|
||||
return { id: `call_${randomUUID()}`, name, arguments: {} };
|
||||
}
|
||||
|
||||
describe("complete_goal tool", () => {
|
||||
test("the CEO can close their goal; a staff engineer cannot", () => {
|
||||
const connection = new DatabaseConnection(join(tmpdir(), `ac-goal-tool-${randomUUID()}.db`));
|
||||
const organization = new OrganizationService(connection.db);
|
||||
let orgId = "";
|
||||
let ceoId = "";
|
||||
let staffId = "";
|
||||
let goalId = "";
|
||||
connection.transaction(() => {
|
||||
orgId = organization.createOrganization({ organizationName: "GoalCorp", employeeName: "Yang", mission: "m", goal: "g" });
|
||||
const providerId = (connection.db.prepare("select id from providers where org_id = ? and provider_key = 'openai'").get(orgId) as { id: string }).id;
|
||||
ceoId = organization.createAgent({
|
||||
orgId, name: "CEO", role: "CEO", department: "Executive",
|
||||
modelProviderId: providerId, modelId: "gpt-5.4", budgetCents: 25000, managerAgentId: null, roleLevel: "c_suite",
|
||||
});
|
||||
staffId = organization.createAgent({
|
||||
orgId, name: "Dev", role: "Engineer", department: "Engineering",
|
||||
modelProviderId: providerId, modelId: "gpt-5.4", budgetCents: 10000, managerAgentId: ceoId, roleLevel: "staff",
|
||||
});
|
||||
goalId = organization.createGoal({ orgId, title: "Ship it", description: "For real.", ownerAgentId: ceoId });
|
||||
});
|
||||
const deps: AgentToolDeps = { db: connection.db, organization, chat: new ChatService(connection.db) };
|
||||
const ctxFor = (agentId: string): AgentToolContext => ({
|
||||
orgId, agent: { id: agentId, modelProviderId: null, modelId: null },
|
||||
taskProjectId: null, taskGoalId: goalId, workspace: null,
|
||||
});
|
||||
|
||||
expect(dispatchAgentTool(deps, ctxFor(staffId), call("complete_goal"))).toContain("ERROR");
|
||||
expect(connection.db.prepare("select status from goals where id = ?").get(goalId)).toEqual({ status: "active" });
|
||||
|
||||
expect(dispatchAgentTool(deps, ctxFor(ceoId), call("complete_goal"))).toContain("achieved");
|
||||
expect(connection.db.prepare("select status from goals where id = ?").get(goalId)).toEqual({ status: "achieved" });
|
||||
|
||||
// Idempotent, and honest about it.
|
||||
expect(dispatchAgentTool(deps, ctxFor(ceoId), call("complete_goal"))).toContain("already achieved");
|
||||
|
||||
// A task with no goal attached cannot complete anything.
|
||||
const noGoalCtx = { ...ctxFor(ceoId), taskGoalId: null };
|
||||
expect(dispatchAgentTool(deps, noGoalCtx, call("complete_goal"))).toContain("ERROR");
|
||||
connection.close();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user