operate

Review and state workflows

Inspect task graph and project projections, mutate project Git state, and apply project review actions from the browser or SDK when a run becomes larger than a transcript.

Operationsbeginner0.7.0 Stable
Verified 2026-03-20View sourceReport a docs issue

This is the missing middle layer between “an agent replied” and “an operator can actually manage ongoing work.” Kestrel exposes several stateful control-plane surfaces for that job:

  • task.graph.get
  • task.graph.update
  • project.snapshot.get
  • project.action
  • project.review.get
  • project.review.action
  • mission_control.project.get
  • mission_control.action.execute

Use them when a conversation and memory record are no longer enough to show the current plan, project status, or review decision.

Choose the right state record

RecordUse it forDo not use it for
task graphdurable structured task state tied to threads and sessionsgeneral project metadata
Mission Control projectthe project-scoped work-item lifecycle, attempts, evidence, and acceptancesession-scoped task or board state
project snapshotread-only workspace, policy, review, and activity projectionwork-item lifecycle authority
project actionexplicit project Git mutationstask or work-item lifecycle editing
project reviewcode/file review workflows tied to the active sessionbroad project state storage

Browser workflow through /api/kchat/control

The Kestrel One can use the control route to move through the full inspection path.

TypeScript
const inbox = await sendRunnerControl({
  command: "operator.inbox",
  sessionId: "reference-session-001",
});
 
const thread = await sendRunnerControl({
  command: "operator.thread",
  threadId: "thread-main",
});
 
const graph = await sendRunnerControl({
  command: "task.graph.get",
  sessionId: "reference-session-001",
  threadId: "thread-main",
});
 
const snapshot = await sendRunnerControl({
  command: "project.snapshot.get",
  sessionId: "reference-session-001",
});
 
const review = await sendRunnerControl({
  command: "project.review.get",
  sessionId: "reference-session-001",
  reviewTarget: {
    taskId: "task:thread:thread-main",
    filePath: "src/project/workspace.ts",
  },
});

That flow matches the current browser state-sync code: read the operator signals first, then fetch task graph, project snapshot, and review detail as needed. The browser is using the same runtime model the SDK uses, which is why inspection and control stay aligned across surfaces.

Browser review action example

When an operator wants to leave or apply a review action, the control route accepts a structured payload:

JSON
{
  "command": "project.review.action",
  "sessionId": "reference-session-001",
  "reviewAction": {
    "type": "review.comment.create",
    "sessionId": "reference-session-001",
    "target": {
      "taskId": "task:thread:thread-main",
      "pullRequestNumber": 42,
      "filePath": "src/project/workspace.ts"
    },
    "body": "Please tighten this parser.",
    "path": "src/project/workspace.ts",
    "line": 12,
    "side": "RIGHT"
  }
}

The response includes a project.review event so other clients and subscribers can observe the same saved review decision.

SDK workflow with KestrelClient

If the product is server-owned rather than browser-driven, use the runner client directly.

TypeScript
import { KestrelClient } from "@kestrel-agents/sdk/runner";
 
const client = new KestrelClient({
  target: {
    kind: "remote",
    baseUrl: process.env.KESTREL_RUNNER_SERVICE_URL!,
    authToken: process.env.KESTREL_RUNNER_SERVICE_TOKEN!,
  },
});
 
const context = {
  actor: {
    actorId: "user-123",
    actorType: "operator",
    displayName: "Taylor Example",
    tenantId: "acme",
  },
  tenantId: "acme",
};
 
const graph = await client.getTaskGraph(
  {
    sessionId: "reference-session-001",
    threadId: "thread-main",
  },
  context,
);
 
const snapshot = await client.getProjectSnapshot(
  { sessionId: "reference-session-001" },
  context,
);
 
const detail = await client.getProjectReview(
  {
    sessionId: "reference-session-001",
    target: {
      taskId: "task:thread:thread-main",
      filePath: "src/project/workspace.ts",
    },
  },
  context,
);

Updating state through the SDK

Reading the surfaces is only half the workflow. The same client can update them with explicit payloads:

TypeScript
await client.updateTaskGraph(
  {
    sessionId: "reference-session-001",
    threadId: "thread-main",
    graph: {
      tasks: {
        "task:thread:thread-main": {
          status: "in_review",
          summary: "Waiting on operator review for the workspace parser changes.",
        },
      },
    },
  },
  context,
);
 
await client.runProjectAction(
  {
    type: "branch.create",
    sessionId: "reference-session-001",
    branchName: "docs/search-hardening",
  },
  context,
);

Example control responses

Task graph response:

JSON
{
  "type": "task.graph",
  "sessionId": "reference-session-001",
  "threadId": "thread-main",
  "graph": {
    "taskId": "task:thread:thread-main",
    "status": "in_review",
    "summary": "Waiting on operator review for the workspace parser changes."
  }
}

Project snapshot response:

JSON
{
  "type": "project.snapshot",
  "sessionId": "reference-session-001",
  "snapshot": {
    "branch": "docs/search-hardening",
    "focus": "review-state and search behavior",
    "nextMilestone": "ship the docs example pass"
  }
}

Choose where to store the state

Use these records in this order:

  1. read operator.thread if the operator needs thread context first
  2. read task.graph when the question is "what work items exist and what state are they in?"
  3. read project.snapshot when the question is "what is the higher-level project state right now?"
  4. read or apply project.review when the question is "what review work or comment actions are attached to this state?"
  5. use project.action only for intentional project-level mutations

That keeps the state model legible. It stops teams from forcing every state question into one oversized snapshot object or a transcript-only workflow.

What these records enable

Task graphs, project snapshots, and project reviews let your application manage plans, project status, and review decisions without burying them in conversation text.

Keep state out of transcripts and side stores

Store task and review state in these shared records instead of burying it in transcripts or prompts. The browser, CLI, and server integrations can then read and update the same information.

Continue with operator control