Files
boss/docs/superpowers/plans/2026-04-02-boss-execution-foundation.md
2026-04-03 00:21:19 +08:00

29 KiB
Raw Permalink Blame History

Boss 执行底座抽象层重构 Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 在不改变现有生产行为的前提下,为 Boss 建立稳定的执行底座抽象层,把当前主 Agent、普通线程回复、群聊分发的底层执行职责从散落逻辑中抽出为后续接入 ClawBackendAdapterOmxTeamBackendAdapter 做准备。

Architecture: 先新增 src/lib/execution/ 下的接口与默认实现,再把 boss-master-agent.tslocal-agent/server.mjs 和关键 API route 逐步映射到这些抽象。整个过程坚持“先平移、不改行为”,所有已有主链继续沿用 boss-state.json + master-agent queue + local-agent + codex exec resume

Tech Stack: Next.js App Router、TypeScript、Node.js、文件型状态 data/boss-state.json、原生 local-agent、现有测试栈 tsx --test、Android/前台不改行为。


Task 1: 定义执行底座类型与默认 contract

Files:

  • Create: /Users/kris/code/boss/src/lib/execution/types.ts

  • Create: /Users/kris/code/boss/src/lib/execution/execution-backend.ts

  • Create: /Users/kris/code/boss/src/lib/execution/orchestration-backend.ts

  • Test: /Users/kris/code/boss/tests/execution-foundation-contracts.test.ts

  • Step 1: 写失败测试,定义执行底座 contract 期望

import assert from "node:assert/strict";
import test from "node:test";
import {
  createExecutionRequest,
  isQueuedExecutionResult,
  isImmediateExecutionResult,
} from "@/lib/execution/types";

test("ExecutionRequest 工厂会生成稳定默认字段", () => {
  const request = createExecutionRequest({
    kind: "master_agent_reply",
    projectId: "master-agent",
    requestMessageId: "msg-1",
    body: "你好",
  });

  assert.equal(request.kind, "master_agent_reply");
  assert.equal(request.projectId, "master-agent");
  assert.equal(request.requestMessageId, "msg-1");
  assert.equal(request.body, "你好");
  assert.equal(request.targetProjectId, undefined);
  assert.equal(request.targetThreadId, undefined);
});

test("ExecutionResult 类型守卫能区分 queued 与 immediate", () => {
  const queued = {
    status: "queued",
    taskId: "task-1",
    backendId: "master-codex-node",
  };
  const completed = {
    status: "completed",
    backendId: "openai-api",
    output: "done",
  };

  assert.equal(isQueuedExecutionResult(queued), true);
  assert.equal(isImmediateExecutionResult(queued), false);
  assert.equal(isQueuedExecutionResult(completed), false);
  assert.equal(isImmediateExecutionResult(completed), true);
});
  • Step 2: 运行测试,确认当前失败

Run:

cd /Users/kris/code/boss
npx --yes tsx --test tests/execution-foundation-contracts.test.ts

Expected:

ERR_MODULE_NOT_FOUND
  • Step 3: 写最小 contract 类型与工厂函数
// /Users/kris/code/boss/src/lib/execution/types.ts
import type { ReasoningEffort } from "@/lib/boss-data";

export type ExecutionRequestKind =
  | "master_agent_reply"
  | "thread_reply"
  | "dispatch_execution"
  | "attachment_analysis";

export interface ExecutionRequest {
  kind: ExecutionRequestKind;
  projectId: string;
  requestMessageId: string;
  body: string;
  requestedByAccount?: string;
  requestedByLabel?: string;
  taskId?: string;
  targetThreadId?: string;
  targetProjectId?: string;
  modelOverride?: string;
  reasoningEffortOverride?: ReasoningEffort;
}

export interface ExecutionQueuedResult {
  status: "queued" | "running";
  taskId: string;
  backendId: string;
  sessionId?: string;
}

export interface ExecutionImmediateResult {
  status: "completed" | "failed";
  backendId: string;
  output?: string;
  error?: string;
}

export interface ExecutionBackendDescription {
  backendId: string;
  label: string;
  mode: "local" | "remote" | "api";
}

export function createExecutionRequest(input: ExecutionRequest): ExecutionRequest {
  return { ...input };
}

export function isQueuedExecutionResult(
  value: ExecutionQueuedResult | ExecutionImmediateResult,
): value is ExecutionQueuedResult {
  return value.status === "queued" || value.status === "running";
}

export function isImmediateExecutionResult(
  value: ExecutionQueuedResult | ExecutionImmediateResult,
): value is ExecutionImmediateResult {
  return value.status === "completed" || value.status === "failed";
}
// /Users/kris/code/boss/src/lib/execution/execution-backend.ts
import type {
  ExecutionBackendDescription,
  ExecutionImmediateResult,
  ExecutionQueuedResult,
  ExecutionRequest,
} from "@/lib/execution/types";

export interface ExecutionBackend {
  backendId: string;
  canHandle(input: ExecutionRequest): Promise<boolean> | boolean;
  execute(input: ExecutionRequest): Promise<ExecutionQueuedResult | ExecutionImmediateResult>;
  describe(input: ExecutionRequest): Promise<ExecutionBackendDescription>;
}
// /Users/kris/code/boss/src/lib/execution/orchestration-backend.ts
export interface OrchestrationBackend {
  backendId: string;
  describe(): Promise<{ backendId: string; label: string }>;
}
  • Step 4: 再跑测试,确认 contract 生效

Run:

cd /Users/kris/code/boss
npx --yes tsx --test tests/execution-foundation-contracts.test.ts

Expected:

# pass 2
  • Step 5: 提交
cd /Users/kris/code/boss
git add tests/execution-foundation-contracts.test.ts src/lib/execution/types.ts src/lib/execution/execution-backend.ts src/lib/execution/orchestration-backend.ts
git commit -m "feat: add execution foundation contracts"

Task 2: 抽离 PromptAssembler 与 MemoryResolver

Files:

  • Create: /Users/kris/code/boss/src/lib/execution/prompt-assembler.ts

  • Create: /Users/kris/code/boss/src/lib/execution/memory-resolver.ts

  • Modify: /Users/kris/code/boss/src/lib/boss-master-agent.ts

  • Test: /Users/kris/code/boss/tests/execution-prompt-assembler.test.ts

  • Test: /Users/kris/code/boss/tests/execution-memory-resolver.test.ts

  • Test: /Users/kris/code/boss/tests/master-agent-config-resolution.test.ts

  • Step 1: 写失败测试,固定记忆筛选与 prompt 拼装顺序

import assert from "node:assert/strict";
import test from "node:test";
import {
  buildExecutionPromptForTesting,
  resolveRelevantMemoriesForTesting,
} from "@/lib/execution/prompt-assembler";

test("PromptAssembler 会按固定顺序拼管理员提示词、用户提示词、对话提示词和记忆", () => {
  const prompt = buildExecutionPromptForTesting({
    globalPrompt: "GLOBAL",
    userPrompt: "USER",
    conversationPrompt: "CONVERSATION",
    projectMemories: [{ title: "项目记忆", content: "PROJECT", projectId: "boss-console", tags: [] }],
    userMemories: [{ title: "用户记忆", content: "USER_MEMORY", tags: [] }],
    requestText: "继续",
  });

  assert.match(prompt, /GLOBAL/);
  assert.match(prompt, /USER/);
  assert.match(prompt, /CONVERSATION/);
  assert.match(prompt, /PROJECT/);
  assert.match(prompt, /USER_MEMORY/);
  assert.ok(prompt.indexOf("GLOBAL") < prompt.indexOf("USER"));
  assert.ok(prompt.indexOf("USER") < prompt.indexOf("CONVERSATION"));
});

test("MemoryResolver 在 master-agent 会话下优先挑当前请求命中的项目记忆", () => {
  const resolved = resolveRelevantMemoriesForTesting({
    projectId: "master-agent",
    requestText: "boss-console 的审批流",
    memories: [
      { memoryId: "m1", scope: "project", projectId: "boss-console", title: "审批流", content: "boss-console approval", tags: ["approval"] },
      { memoryId: "m2", scope: "project", projectId: "wenshenapp", title: "UI", content: "wechat ui", tags: ["ui"] },
    ],
  });

  assert.equal(resolved.projectMemories.length, 1);
  assert.equal(resolved.projectMemories[0]?.projectId, "boss-console");
});
  • Step 2: 运行测试,确认当前失败

Run:

cd /Users/kris/code/boss
npx --yes tsx --test tests/execution-prompt-assembler.test.ts tests/execution-memory-resolver.test.ts

Expected:

ERR_MODULE_NOT_FOUND
  • Step 3: 写最小实现,并让 boss-master-agent 改用新模块
// /Users/kris/code/boss/src/lib/execution/memory-resolver.ts
export function resolveRelevantMemories(input: {
  projectId: string;
  requestText?: string;
  memories: Array<{
    memoryId: string;
    scope: "global" | "project";
    projectId?: string;
    title: string;
    content: string;
    tags?: string[];
  }>;
}) {
  const lowered = input.requestText?.toLowerCase() ?? "";
  const projectMemories = input.memories.filter((memory) => {
    if (memory.scope !== "project") return false;
    if (input.projectId !== "master-agent") {
      return memory.projectId === input.projectId;
    }
    if (!lowered) return true;
    return [memory.projectId, memory.title, memory.content, ...(memory.tags ?? [])]
      .filter(Boolean)
      .some((value) => value!.toLowerCase().includes(lowered) || lowered.includes(value!.toLowerCase()));
  });
  const userMemories = input.memories.filter((memory) => memory.scope === "global");
  return {
    projectMemories: projectMemories.slice(0, 6),
    userMemories: userMemories.slice(0, 8),
  };
}

export const resolveRelevantMemoriesForTesting = resolveRelevantMemories;

为避免 master-agent 当前生产行为回归,允许在同一个 /Users/kris/code/boss/src/lib/execution/memory-resolver.ts 中额外提供一个 runtime-safe helper(例如 resolveRuntimeRelevantMemories(...)),专门给 boss-master-agent.ts 使用。约束如下:

  • resolveRelevantMemories(...) 仍保持上面的最小 contract供基础测试与后续 contract 使用。
  • runtime-safe helper 只负责把当前已存在的主 Agent 运行时保护内聚回执行模块,例如:
    • workflow_rule / user_preference 优先;
    • master-agent 非空请求但无 lexical 命中时,回退到前 6 个项目记忆。
  • 不允许把这些运行时保护继续散落在 boss-master-agent.ts 中;如果需要保留生产行为,优先放进同一个 execution 模块。
// /Users/kris/code/boss/src/lib/execution/prompt-assembler.ts
import { resolveRelevantMemories } from "@/lib/execution/memory-resolver";

export function buildExecutionPrompt(input: {
  globalPrompt?: string | null;
  userPrompt?: string | null;
  conversationPrompt?: string | null;
  projectMemories: Array<{ title: string; content: string; projectId?: string; tags?: string[] }>;
  userMemories: Array<{ title: string; content: string; tags?: string[] }>;
  requestText: string;
}) {
  return [
    input.globalPrompt?.trim() ? `管理员全局主提示词:\n${input.globalPrompt.trim()}` : null,
    input.userPrompt?.trim() ? `用户私有主提示词:\n${input.userPrompt.trim()}` : null,
    input.conversationPrompt?.trim() ? `当前对话附加提示词:\n${input.conversationPrompt.trim()}` : null,
    input.projectMemories.length
      ? `项目记忆:\n${input.projectMemories.map((item) => `- [${item.projectId ?? "unknown"}] ${item.title}: ${item.content}`).join("\n")}`
      : null,
    input.userMemories.length
      ? `用户通用记忆:\n${input.userMemories.map((item) => `- ${item.title}: ${item.content}`).join("\n")}`
      : null,
    `当前消息:\n${input.requestText}`,
  ].filter(Boolean).join("\n\n");
}

export const buildExecutionPromptForTesting = buildExecutionPrompt;
export { resolveRelevantMemoriesForTesting } from "@/lib/execution/memory-resolver";

/Users/kris/code/boss/src/lib/boss-master-agent.ts 中,把当前内联的 memory selection 与 prompt assembly 改成调用:

import { buildExecutionPrompt } from "@/lib/execution/prompt-assembler";
import { resolveRelevantMemories } from "@/lib/execution/memory-resolver";

并在 resolveMasterAgentExecutionConfig(...) 中改成:

const memoryScope = listUserMasterMemoriesView(state, resolvedAccountId, { includeArchived: false });
const { projectMemories, userMemories } = resolveRelevantMemories({
  projectId,
  requestText,
  memories: memoryScope,
});

const executionPrompt = buildExecutionPrompt({
  globalPrompt: promptPolicy?.globalPrompt ?? null,
  userPrompt: userPrompt?.content ?? null,
  conversationPrompt: scopedAgentControls?.promptOverride ?? null,
  projectMemories,
  userMemories,
  requestText: requestText ?? "",
});
  • Step 4: 跑新测试和既有主 Agent 测试

Run:

cd /Users/kris/code/boss
npx --yes tsx --test tests/execution-prompt-assembler.test.ts tests/execution-memory-resolver.test.ts tests/master-agent-config-resolution.test.ts

Expected:

# pass
  • Step 5: 提交
cd /Users/kris/code/boss
git add tests/execution-prompt-assembler.test.ts tests/execution-memory-resolver.test.ts tests/master-agent-config-resolution.test.ts src/lib/execution/prompt-assembler.ts src/lib/execution/memory-resolver.ts src/lib/boss-master-agent.ts
git commit -m "refactor: extract execution prompt assembly"

Task 3: 抽离 PermissionPolicy 与 ToolRegistry

Files:

  • Create: /Users/kris/code/boss/src/lib/execution/permission-policy.ts

  • Create: /Users/kris/code/boss/src/lib/execution/tool-registry.ts

  • Modify: /Users/kris/code/boss/src/app/api/v1/projects/[projectId]/messages/route.ts

  • Modify: /Users/kris/code/boss/src/lib/boss-data.ts

  • Test: /Users/kris/code/boss/tests/execution-permission-policy.test.ts

  • Test: /Users/kris/code/boss/tests/group-message-dispatch-plan.test.ts

  • Test: /Users/kris/code/boss/tests/dispatch-plan-confirmation.test.ts

  • Step 1: 写失败测试,固定 approval_required 和 tool policy 语义

import assert from "node:assert/strict";
import test from "node:test";
import { evaluatePermissionPolicyForTesting } from "@/lib/execution/permission-policy";

test("approval_required 群聊在已有待确认推荐时拒绝继续直接执行", () => {
  const result = evaluatePermissionPolicyForTesting({
    project: {
      id: "group-1",
      isGroup: true,
      collaborationMode: "approval_required",
      approvalState: "pending_user",
    },
    hasPendingDispatchPlan: true,
  });

  assert.equal(result.allowed, false);
  assert.equal(result.requiresApproval, true);
  assert.match(result.reason ?? "", /等待确认/);
});
  • Step 2: 运行测试确认失败

Run:

cd /Users/kris/code/boss
npx --yes tsx --test tests/execution-permission-policy.test.ts

Expected:

ERR_MODULE_NOT_FOUND
  • Step 3: 写权限策略与工具注册最小实现,并在消息 route 中复用
// /Users/kris/code/boss/src/lib/execution/permission-policy.ts
export function evaluatePermissionPolicy(input: {
  project?: {
    id: string;
    isGroup: boolean;
    collaborationMode: "development" | "approval_required";
    approvalState: "not_required" | "pending_agent" | "pending_user" | "approved" | "rejected";
  };
  hasPendingDispatchPlan?: boolean;
}) {
  const project = input.project;
  if (!project) {
    return {
      allowed: true,
      requiresApproval: false,
      toolPolicy: { allowedTools: [], deniedTools: [] },
      collaborationPolicy: {
        mode: "development" as const,
        canDispatchDirectly: true,
        canCrossThreadTalk: true,
      },
    };
  }

  if (project.isGroup && project.collaborationMode === "approval_required" && input.hasPendingDispatchPlan) {
    return {
      allowed: false,
      requiresApproval: true,
      reason: "当前还有一条主 Agent 推荐等待确认,请先确认或拒绝后再继续发送新指令。",
      toolPolicy: { allowedTools: [], deniedTools: ["dispatch_execution"] },
      collaborationPolicy: {
        mode: "approval_required" as const,
        canDispatchDirectly: false,
        canCrossThreadTalk: false,
      },
    };
  }

  return {
    allowed: true,
    requiresApproval: project.isGroup && project.collaborationMode === "approval_required",
    toolPolicy: { allowedTools: ["conversation_reply", "dispatch_execution"], deniedTools: [] },
    collaborationPolicy: {
      mode: project.collaborationMode,
      canDispatchDirectly: project.collaborationMode === "development",
      canCrossThreadTalk: project.collaborationMode === "development",
    },
  };
}

export const evaluatePermissionPolicyForTesting = evaluatePermissionPolicy;
// /Users/kris/code/boss/src/lib/execution/tool-registry.ts
export function listExecutionTools() {
  return [
    { name: "conversation_reply", kind: "execution" },
    { name: "dispatch_execution", kind: "execution" },
    { name: "attachment_analysis", kind: "analysis" },
  ] as const;
}

如果在落地时发现当前生产审批流需要把“先生成推荐、再等待确认”的动作显式建模,允许在同一个 tool-registry.ts 中补一个 group_dispatch_plan 工具定义,并同步把 PermissionPolicy 和测试调整为以真实运行时语义为准。约束如下:

  • approval_required 群聊不能再暴露与 canDispatchDirectly=false 矛盾的 dispatch_execution 直接权限;
  • toolPolicy 必须能清楚表达“当前允许先生成推荐,但不允许直接跨线程执行”的状态;
  • 任何新增工具定义都必须只服务于现有审批主链,不提前引入 Task 4+ 的执行后端能力。

/Users/kris/code/boss/src/app/api/v1/projects/[projectId]/messages/route.ts 中,把 approval_required + pending plan 判断替换成:

const permission = evaluatePermissionPolicy({
  project,
  hasPendingDispatchPlan: Boolean(pendingPlan),
});

if (!permission.allowed) {
  return NextResponse.json(
    {
      ok: false,
      message: permission.reason,
      pendingPlan,
      collaborationGate: buildCollaborationGate(project),
    },
    { status: 409 },
  );
}
  • Step 4: 跑权限测试与现有 dispatch 测试

Run:

cd /Users/kris/code/boss
npx --yes tsx --test tests/execution-permission-policy.test.ts tests/group-message-dispatch-plan.test.ts tests/dispatch-plan-confirmation.test.ts

Expected:

# pass
  • Step 5: 提交
cd /Users/kris/code/boss
git add tests/execution-permission-policy.test.ts tests/group-message-dispatch-plan.test.ts tests/dispatch-plan-confirmation.test.ts src/lib/execution/permission-policy.ts src/lib/execution/tool-registry.ts src/app/api/v1/projects/[projectId]/messages/route.ts
git commit -m "refactor: extract execution permission policy"

Task 4: 抽离 ExecutionBackendSelector 与默认执行后端

Files:

  • Create: /Users/kris/code/boss/src/lib/execution/backend-selector.ts

  • Create: /Users/kris/code/boss/src/lib/execution/backends/master-codex-node-backend.ts

  • Create: /Users/kris/code/boss/src/lib/execution/backends/openai-backend.ts

  • Create: /Users/kris/code/boss/src/lib/execution/backends/aliyun-qwen-backend.ts

  • Modify: /Users/kris/code/boss/src/lib/boss-master-agent.ts

  • Test: /Users/kris/code/boss/tests/execution-backend-selector.test.ts

  • Test: /Users/kris/code/boss/tests/master-agent-openai-fallback.test.ts

  • Test: /Users/kris/code/boss/tests/master-agent-message-queue.test.ts

  • Step 1: 写失败测试,固定后端选择优先级

import assert from "node:assert/strict";
import test from "node:test";
import { selectExecutionBackendForTesting } from "@/lib/execution/backend-selector";

test("主控可用时优先选择当前主控 backend", async () => {
  const backend = await selectExecutionBackendForTesting({
    primary: { provider: "master_codex_node", status: "ready" },
    backups: [{ provider: "aliyun_qwen_api", status: "ready" }],
  });

  assert.equal(backend.backendId, "master-codex-node");
});

test("主控 degraded 时回退到可用阿里备用 backend", async () => {
  const backend = await selectExecutionBackendForTesting({
    primary: { provider: "master_codex_node", status: "degraded" },
    backups: [{ provider: "aliyun_qwen_api", status: "ready" }],
  });

  assert.equal(backend.backendId, "aliyun-qwen");
});
  • Step 2: 运行测试确认失败

Run:

cd /Users/kris/code/boss
npx --yes tsx --test tests/execution-backend-selector.test.ts

Expected:

ERR_MODULE_NOT_FOUND
  • Step 3: 写 selector 和默认 backend 壳,并让 boss-master-agent 通过 selector 取 backend
// /Users/kris/code/boss/src/lib/execution/backend-selector.ts
export async function selectExecutionBackend(input: {
  primary: { provider: string; status: string };
  backups: Array<{ provider: string; status: string }>;
}) {
  const primaryBackend = resolveBackendByProvider(input.primary.provider);
  if (input.primary.status === "ready") {
    return primaryBackend;
  }
  const qwen = input.backups.find((item) => item.provider === "aliyun_qwen_api" && item.status === "ready");
  if (qwen) {
    return { backendId: "aliyun-qwen" };
  }
  const openai = input.backups.find((item) => item.provider === "openai_api" && item.status === "ready");
  if (openai) {
    return { backendId: "openai-api" };
  }
  const master = input.backups.find((item) => item.provider === "master_codex_node" && item.status === "ready");
  if (master) {
    return { backendId: "master-codex-node" };
  }
  return primaryBackend;
}

export const selectExecutionBackendForTesting = selectExecutionBackend;

补充说明:

  • selector 的运行时语义以“ready primary 优先,否则按 aliyun_qwen -> openai -> master_codex_node 顺序回退,最后再回 primary 兜底”为准。
  • 如果同一 provider 存在多个账号,只要其中任何一个是 ready,该 backend 就视为可选。

/Users/kris/code/boss/src/lib/boss-master-agent.ts 中,把 provider fallback 的选择逻辑收进一个单独调用:

const selectedBackend = await selectExecutionBackend({
  primary: { provider: runtime.account.provider, status: runtime.account.status },
  backups: state.aiAccounts
    .filter((item) => item.accountId !== runtime.account.accountId)
    .map((item) => ({ provider: item.provider, status: item.status })),
});

并让后续 provider-specific 分支改成按 selectedBackend.backendId 分派,而不是继续直接拼条件。

  • Step 4: 跑 selector 测试和既有 fallback 测试

Run:

cd /Users/kris/code/boss
npx --yes tsx --test tests/execution-backend-selector.test.ts tests/master-agent-openai-fallback.test.ts tests/master-agent-message-queue.test.ts

Expected:

# pass
  • Step 5: 提交
cd /Users/kris/code/boss
git add tests/execution-backend-selector.test.ts tests/master-agent-openai-fallback.test.ts tests/master-agent-message-queue.test.ts src/lib/execution/backend-selector.ts src/lib/execution/backends/master-codex-node-backend.ts src/lib/execution/backends/openai-backend.ts src/lib/execution/backends/aliyun-qwen-backend.ts src/lib/boss-master-agent.ts
git commit -m "refactor: add execution backend selector"

Task 5: 抽离 RemoteRuntimeAdapter 与 OrchestrationBackend

Files:

  • Create: /Users/kris/code/boss/src/lib/execution/remote-runtime-adapter.ts

  • Create: /Users/kris/code/boss/src/lib/execution/backends/boss-native-orchestrator.ts

  • Modify: /Users/kris/code/boss/local-agent/server.mjs

  • Modify: /Users/kris/code/boss/src/app/api/v1/master-agent/tasks/[taskId]/complete/route.ts

  • Test: /Users/kris/code/boss/tests/remote-runtime-adapter.test.ts

  • Test: /Users/kris/code/boss/tests/dispatch-execution-result.test.ts

  • Test: /Users/kris/code/boss/tests/local-agent-codex-task-runner.test.mjs

  • Step 1: 写失败测试,固定远程执行结果和编排分层

import assert from "node:assert/strict";
import test from "node:test";
import { normalizeRemoteExecutionResultForTesting } from "@/lib/execution/remote-runtime-adapter";

test("RemoteRuntimeAdapter 会把 local-agent 回写标准化成统一结果", () => {
  const normalized = normalizeRemoteExecutionResultForTesting({
    status: "completed",
    dispatchExecutionId: "dx-1",
    targetProjectId: "project-1",
    targetThreadId: "thread-1",
    rawThreadReply: "链路正常",
  });

  assert.equal(normalized.status, "completed");
  assert.equal(normalized.targetProjectId, "project-1");
  assert.equal(normalized.rawThreadReply, "链路正常");
});
  • Step 2: 运行测试确认失败

Run:

cd /Users/kris/code/boss
npx --yes tsx --test tests/remote-runtime-adapter.test.ts

Expected:

ERR_MODULE_NOT_FOUND
  • Step 3: 写最小 adapter并让 complete route 和 local-agent 对齐这个结构
// /Users/kris/code/boss/src/lib/execution/remote-runtime-adapter.ts
export function normalizeRemoteExecutionResult(input: {
  status: "completed" | "failed";
  dispatchExecutionId?: string;
  targetProjectId?: string;
  targetThreadId?: string;
  rawThreadReply?: string;
  replyBody?: string;
  errorMessage?: string;
}) {
  return {
    status: input.status,
    dispatchExecutionId: input.dispatchExecutionId,
    targetProjectId: input.targetProjectId,
    targetThreadId: input.targetThreadId,
    rawThreadReply: input.rawThreadReply,
    replyBody: input.replyBody,
    errorMessage: input.errorMessage,
  };
}

export const normalizeRemoteExecutionResultForTesting = normalizeRemoteExecutionResult;

/Users/kris/code/boss/src/app/api/v1/master-agent/tasks/[taskId]/complete/route.ts 中增加:

import { normalizeRemoteExecutionResult } from "@/lib/execution/remote-runtime-adapter";

并把 body 传入 completeMasterAgentTask 前先标准化:

const normalized = normalizeRemoteExecutionResult(body);

/Users/kris/code/boss/local-agent/server.mjs 中,把 conversation_reply / dispatch_execution 结果拼装收进一个小 helper输出结构与 normalizeRemoteExecutionResult 对齐。

  • Step 4: 跑 adapter 测试和既有 dispatch/local-agent 测试

Run:

cd /Users/kris/code/boss
npx --yes tsx --test tests/remote-runtime-adapter.test.ts tests/dispatch-execution-result.test.ts
node --test tests/local-agent-codex-task-runner.test.mjs

Expected:

# pass
  • Step 5: 提交
cd /Users/kris/code/boss
git add tests/remote-runtime-adapter.test.ts tests/dispatch-execution-result.test.ts tests/local-agent-codex-task-runner.test.mjs src/lib/execution/remote-runtime-adapter.ts src/lib/execution/backends/boss-native-orchestrator.ts src/app/api/v1/master-agent/tasks/[taskId]/complete/route.ts local-agent/server.mjs
git commit -m "refactor: add remote runtime adapter"

Task 6: 跑整体验证并补文档

Files:

  • Modify: /Users/kris/code/boss/README.md

  • Modify: /Users/kris/code/boss/docs/architecture/current_runtime_and_deploy_status_cn.md

  • Modify: /Users/kris/code/boss/docs/architecture/api_and_service_inventory_cn.md

  • Step 1: 跑本轮完整验证矩阵

Run:

cd /Users/kris/code/boss
npx --yes tsx --test \
  tests/execution-foundation-contracts.test.ts \
  tests/execution-prompt-assembler.test.ts \
  tests/execution-memory-resolver.test.ts \
  tests/execution-permission-policy.test.ts \
  tests/execution-backend-selector.test.ts \
  tests/remote-runtime-adapter.test.ts \
  tests/master-agent-config-resolution.test.ts \
  tests/master-agent-openai-fallback.test.ts \
  tests/master-agent-message-queue.test.ts \
  tests/group-message-dispatch-plan.test.ts \
  tests/dispatch-plan-confirmation.test.ts \
  tests/dispatch-execution-result.test.ts

node --test tests/local-agent-codex-task-runner.test.mjs
npm run lint
npm run build

Expected:

All tests pass
Next.js build succeeds
  • Step 2: 更新文档,说明执行底座抽象层已落地,但不改变生产主链

在文档中至少补下面这些事实:

- 当前 Boss 已新增 `src/lib/execution/` 执行底座抽象层
- 当前生产主链仍然沿用 `local-agent -> codex exec resume`
- 当前已完成 `ExecutionBackend / PromptAssembler / PermissionPolicy / RemoteRuntimeAdapter / OrchestrationBackend` 的默认实现
- 当前 `claw-code``oh-my-codex` 尚未正式接入生产链,只是 contract ready
  • Step 3: 再跑一次 lint/build 确认文档与代码最终状态一致

Run:

cd /Users/kris/code/boss
npm run lint
npm run build

Expected:

No new errors
  • Step 4: 提交
cd /Users/kris/code/boss
git add README.md docs/architecture/current_runtime_and_deploy_status_cn.md docs/architecture/api_and_service_inventory_cn.md
git commit -m "docs: record execution foundation refactor"

Self-Review

Spec coverage

  • 已覆盖执行底座抽象层总目标
  • 已覆盖 ExecutionBackend / Selector / SessionRuntime / PermissionPolicy / ToolRegistry / PromptAssembler / MemoryResolver / RemoteRuntimeAdapter / OrchestrationBackend
  • 已覆盖现有主链不变的约束
  • 已覆盖后续接 claw-code / oh-my-codex 前的 contract-ready 目标

Placeholder scan

  • TODO / TBD / implement later
  • 每个任务都给了明确文件、测试、命令和 commit 边界

Type consistency

  • ExecutionRequest / ExecutionQueuedResult / ExecutionImmediateResult / PermissionCheckResult 在各任务中保持一致
  • ExecutionBackendOrchestrationBackend 已分层,没有混用

Execution Handoff

Plan complete and saved to /Users/kris/code/boss/docs/superpowers/plans/2026-04-02-boss-execution-foundation.md. Two execution options:

1. Subagent-Driven (recommended) - I dispatch a fresh subagent per task, review between tasks, fast iteration

2. Inline Execution - Execute tasks in this session using executing-plans, batch execution with checkpoints

Which approach?