786 lines
26 KiB
Markdown
786 lines
26 KiB
Markdown
# 主 Agent 线程状态文档与增量同步 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:** 让主 Agent 用“线程状态文档 + 最近进展事件 + 关键时刻深拉”的方式理解活跃线程,减少常态 token 消耗,同时保持关键时刻的接手能力与实时性。
|
||
|
||
**Architecture:** 在现有 `projectUnderstandingSnapshot` 自动同步链路上新增线程级 `ThreadStatusDocument` 和 `ThreadProgressEvent`,让 heartbeat / thread reply 先走轻量事件同步,再在关键场景触发全量理解刷新。主 Agent prompt 组装从“读项目理解快照”升级为“读线程状态文档 + 最近事件 + 项目记忆”,前台增加只读 `线程状态` 入口。
|
||
|
||
**Tech Stack:** Next.js App Router、TypeScript、文件型状态存储 `data/boss-state.json`、Android 原生客户端、Node test runner、Gradle unit tests
|
||
|
||
---
|
||
|
||
## 文件结构
|
||
|
||
### 新增/扩展数据模型与状态归一化
|
||
|
||
- Modify: `/Users/kris/code/boss/src/lib/boss-data.ts`
|
||
- Test: `/Users/kris/code/boss/tests/thread-status-sync.test.ts`
|
||
- Test: `/Users/kris/code/boss/tests/device-import-draft.test.ts`
|
||
|
||
职责:
|
||
- 在 `BossState` 里新增线程状态文档和进展事件
|
||
- 补 normalization / pruning / slicing
|
||
- 增加全量同步与轻量同步任务排队逻辑
|
||
|
||
### 主 Agent prompt 组装与读取逻辑
|
||
|
||
- Modify: `/Users/kris/code/boss/src/lib/boss-master-agent.ts`
|
||
- Test: `/Users/kris/code/boss/tests/master-agent-thread-status-prompt.test.ts`
|
||
|
||
职责:
|
||
- 让主 Agent 默认读取线程状态文档和最近进展事件
|
||
- 保留关键时刻深拉线程的兜底路径
|
||
|
||
### 线程状态 API 与会话信息展示
|
||
|
||
- Create: `/Users/kris/code/boss/src/app/api/v1/projects/[projectId]/thread-status/route.ts`
|
||
- Modify: `/Users/kris/code/boss/src/components/app-ui.tsx`
|
||
- Modify: `/Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/BossApiClient.java`
|
||
- Modify: `/Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/ConversationInfoActivity.java`
|
||
- Create: `/Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/ThreadStatusActivity.java`
|
||
- Test: `/Users/kris/code/boss/tests/thread-status-route.test.ts`
|
||
- Test: `/Users/kris/code/boss/android/app/src/test/java/com/hyzq/boss/ConversationInfoActivityTest.java`
|
||
- Test: `/Users/kris/code/boss/android/app/src/test/java/com/hyzq/boss/ThreadStatusActivityTest.java`
|
||
|
||
职责:
|
||
- 提供线程状态只读接口
|
||
- 在线程会话信息页增加 `线程状态` 入口
|
||
- Android 前台可查看线程状态
|
||
|
||
### 文档与回归
|
||
|
||
- 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`
|
||
|
||
职责:
|
||
- 记录线程状态文档和进展事件的运行方式
|
||
- 记录 API 与同步策略
|
||
|
||
---
|
||
|
||
### Task 1: 新增线程状态文档与进展事件模型
|
||
|
||
**Files:**
|
||
- Modify: `/Users/kris/code/boss/src/lib/boss-data.ts`
|
||
- Test: `/Users/kris/code/boss/tests/thread-status-sync.test.ts`
|
||
|
||
- [ ] **Step 1: 写失败测试,锁住状态模型归一化与裁剪行为**
|
||
|
||
```ts
|
||
import test from "node:test";
|
||
import assert from "node:assert/strict";
|
||
|
||
let readState: (typeof import("../src/lib/boss-data"))["readState"];
|
||
let writeState: (typeof import("../src/lib/boss-data"))["writeState"];
|
||
|
||
test("thread status documents and progress events normalize and trim correctly", async () => {
|
||
const state = await readState();
|
||
state.threadStatusDocuments = [
|
||
{
|
||
documentId: "doc-1",
|
||
projectId: "thread-a",
|
||
threadId: "thread-a-id",
|
||
threadDisplayName: "线程 A",
|
||
folderName: "Talking",
|
||
deviceId: "mac-studio",
|
||
projectGoal: "完成树莓派二代查询链路",
|
||
currentPhase: "功能实现",
|
||
currentProgress: "已打通查询接口,正在补手机端展示",
|
||
technicalArchitecture: "Next.js API + Android 原生客户端",
|
||
currentBlockers: "",
|
||
recommendedNextStep: "补会话页展示与排序",
|
||
keyFiles: ["src/lib/boss-data.ts"],
|
||
keyCommands: ["npm run build"],
|
||
updatedAt: "2026-04-04T18:00:00+08:00",
|
||
sourceTaskId: "task-1",
|
||
sourceKind: "full_sync",
|
||
},
|
||
];
|
||
state.threadProgressEvents = Array.from({ length: 30 }, (_, index) => ({
|
||
eventId: `event-${index}`,
|
||
projectId: "thread-a",
|
||
threadId: "thread-a-id",
|
||
threadDisplayName: "线程 A",
|
||
deviceId: "mac-studio",
|
||
eventType: "progress_updated",
|
||
summary: `进展 ${index}`,
|
||
phase: "功能实现",
|
||
blockerDelta: "",
|
||
nextStepDelta: "",
|
||
createdAt: `2026-04-04T18:${String(index).padStart(2, "0")}:00+08:00`,
|
||
sourceTaskId: `task-${index}`,
|
||
}));
|
||
|
||
await writeState(state);
|
||
const normalized = await readState();
|
||
|
||
assert.equal(normalized.threadStatusDocuments.length, 1);
|
||
assert.equal(normalized.threadStatusDocuments[0]?.projectGoal, "完成树莓派二代查询链路");
|
||
assert.equal(normalized.threadProgressEvents.length, 20);
|
||
assert.equal(normalized.threadProgressEvents[0]?.eventId, "event-29");
|
||
});
|
||
```
|
||
|
||
- [ ] **Step 2: 跑测试,确认它先失败**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
npx --yes tsx --test /Users/kris/code/boss/tests/thread-status-sync.test.ts
|
||
```
|
||
|
||
Expected:
|
||
- FAIL,提示 `threadStatusDocuments` 或 `threadProgressEvents` 不存在,或者长度/排序不符合预期
|
||
|
||
- [ ] **Step 3: 在 `BossState` 里新增模型与归一化逻辑**
|
||
|
||
在 `/Users/kris/code/boss/src/lib/boss-data.ts` 增加接口与默认值:
|
||
|
||
```ts
|
||
export interface ThreadStatusDocument {
|
||
documentId: string;
|
||
projectId: string;
|
||
threadId: string;
|
||
threadDisplayName: string;
|
||
folderName: string;
|
||
deviceId: string;
|
||
projectGoal: string;
|
||
currentPhase: string;
|
||
currentProgress: string;
|
||
technicalArchitecture: string;
|
||
currentBlockers: string;
|
||
recommendedNextStep: string;
|
||
keyFiles: string[];
|
||
keyCommands: string[];
|
||
updatedAt: string;
|
||
sourceTaskId: string;
|
||
sourceKind: "device_import" | "full_sync" | "incremental_sync";
|
||
}
|
||
|
||
export interface ThreadProgressEvent {
|
||
eventId: string;
|
||
projectId: string;
|
||
threadId: string;
|
||
threadDisplayName: string;
|
||
deviceId: string;
|
||
eventType:
|
||
| "phase_changed"
|
||
| "progress_updated"
|
||
| "blocker_added"
|
||
| "blocker_resolved"
|
||
| "next_step_changed"
|
||
| "architecture_updated"
|
||
| "handoff_ready";
|
||
summary: string;
|
||
phase?: string;
|
||
blockerDelta?: string;
|
||
nextStepDelta?: string;
|
||
createdAt: string;
|
||
sourceTaskId: string;
|
||
sourceMessageId?: string;
|
||
}
|
||
```
|
||
|
||
并把状态接入:
|
||
|
||
```ts
|
||
threadStatusDocuments: ensureArray(raw.threadStatusDocuments, []).map((item) => ({
|
||
...item,
|
||
keyFiles: ensureArray(item.keyFiles, []),
|
||
keyCommands: ensureArray(item.keyCommands, []),
|
||
})),
|
||
threadProgressEvents: ensureArray(raw.threadProgressEvents, [])
|
||
.map((item) => ({ ...item }))
|
||
.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
|
||
.slice(0, 400),
|
||
```
|
||
|
||
在 `syncDerivedState` 里裁剪每线程最近事件:
|
||
|
||
```ts
|
||
const eventBuckets = new Map<string, ThreadProgressEvent[]>();
|
||
for (const event of state.threadProgressEvents) {
|
||
const key = `${event.projectId}:${event.threadId}`;
|
||
const bucket = eventBuckets.get(key) ?? [];
|
||
if (bucket.length < 20) bucket.push(event);
|
||
eventBuckets.set(key, bucket);
|
||
}
|
||
state.threadProgressEvents = [...eventBuckets.values()].flat();
|
||
```
|
||
|
||
- [ ] **Step 4: 重新跑测试,确认通过**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
npx --yes tsx --test /Users/kris/code/boss/tests/thread-status-sync.test.ts
|
||
```
|
||
|
||
Expected:
|
||
- PASS
|
||
|
||
- [ ] **Step 5: 提交这一小步**
|
||
|
||
```bash
|
||
git add /Users/kris/code/boss/src/lib/boss-data.ts /Users/kris/code/boss/tests/thread-status-sync.test.ts
|
||
git commit -m "feat: add thread status document models"
|
||
```
|
||
|
||
### Task 2: 用线程状态文档替代常态全量理解
|
||
|
||
**Files:**
|
||
- Modify: `/Users/kris/code/boss/src/lib/boss-data.ts`
|
||
- Test: `/Users/kris/code/boss/tests/device-import-draft.test.ts`
|
||
- Test: `/Users/kris/code/boss/tests/thread-status-sync.test.ts`
|
||
|
||
- [ ] **Step 1: 写失败测试,锁住“heartbeat 优先记增量,不总是排全量理解”**
|
||
|
||
```ts
|
||
test("active thread updates create lightweight progress events before full re-sync", async () => {
|
||
const state = await readState();
|
||
const project = state.projects.find((item) => item.id !== "master-agent" && item.threadMeta.codexThreadRef);
|
||
assert.ok(project);
|
||
|
||
project!.projectUnderstanding = {
|
||
projectGoal: "目标 A",
|
||
currentProgress: "旧进度",
|
||
technicalArchitecture: "旧架构",
|
||
currentBlockers: "",
|
||
recommendedNextStep: "旧下一步",
|
||
sourceTaskId: "task-old",
|
||
updatedAt: "2026-04-04T10:00:00+08:00",
|
||
sourceKind: "thread_sync",
|
||
};
|
||
state.threadStatusDocuments = [
|
||
{
|
||
documentId: "doc-old",
|
||
projectId: project!.id,
|
||
threadId: project!.threadMeta.threadId,
|
||
threadDisplayName: project!.threadMeta.threadDisplayName,
|
||
folderName: project!.threadMeta.folderName,
|
||
deviceId: project!.deviceIds[0]!,
|
||
projectGoal: "目标 A",
|
||
currentPhase: "功能实现",
|
||
currentProgress: "旧进度",
|
||
technicalArchitecture: "旧架构",
|
||
currentBlockers: "",
|
||
recommendedNextStep: "旧下一步",
|
||
keyFiles: [],
|
||
keyCommands: [],
|
||
updatedAt: "2026-04-04T10:00:00+08:00",
|
||
sourceTaskId: "task-old",
|
||
sourceKind: "full_sync",
|
||
},
|
||
];
|
||
|
||
// 用已有 heartbeat 处理函数或相关 helper 驱动活跃更新
|
||
const result = await upsertThreadProgressEventInStateForTest(state, {
|
||
projectId: project!.id,
|
||
threadId: project!.threadMeta.threadId,
|
||
threadDisplayName: project!.threadMeta.threadDisplayName,
|
||
deviceId: project!.deviceIds[0]!,
|
||
eventType: "progress_updated",
|
||
summary: "已完成手机端排序修复",
|
||
phase: "功能实现",
|
||
createdAt: "2026-04-04T12:00:00+08:00",
|
||
sourceTaskId: "task-new",
|
||
});
|
||
|
||
assert.equal(result.threadProgressEvents[0]?.summary, "已完成手机端排序修复");
|
||
assert.equal(
|
||
result.masterAgentTasks.some((task) => task.projectUnderstandingTargetProjectId === project!.id),
|
||
false,
|
||
);
|
||
});
|
||
```
|
||
|
||
- [ ] **Step 2: 跑测试,确认先失败**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
npx --yes tsx --test /Users/kris/code/boss/tests/thread-status-sync.test.ts /Users/kris/code/boss/tests/device-import-draft.test.ts
|
||
```
|
||
|
||
Expected:
|
||
- FAIL,提示没有轻量事件 helper 或仍然直接走完整理解任务
|
||
|
||
- [ ] **Step 3: 在状态层新增轻量事件写入与全量理解判定**
|
||
|
||
在 `/Users/kris/code/boss/src/lib/boss-data.ts` 增加:
|
||
|
||
```ts
|
||
function upsertThreadStatusDocumentInState(
|
||
state: BossState,
|
||
input: {
|
||
projectId: string;
|
||
threadId: string;
|
||
threadDisplayName: string;
|
||
folderName: string;
|
||
deviceId: string;
|
||
projectGoal: string;
|
||
currentPhase: string;
|
||
currentProgress: string;
|
||
technicalArchitecture: string;
|
||
currentBlockers: string;
|
||
recommendedNextStep: string;
|
||
keyFiles: string[];
|
||
keyCommands: string[];
|
||
updatedAt: string;
|
||
sourceTaskId: string;
|
||
sourceKind: ThreadStatusDocument["sourceKind"];
|
||
},
|
||
) {
|
||
const existing = state.threadStatusDocuments.find(
|
||
(item) => item.projectId === input.projectId && item.threadId === input.threadId,
|
||
);
|
||
if (existing) {
|
||
Object.assign(existing, input);
|
||
return existing;
|
||
}
|
||
const document: ThreadStatusDocument = {
|
||
documentId: randomToken("thread-status"),
|
||
...input,
|
||
};
|
||
state.threadStatusDocuments.unshift(document);
|
||
return document;
|
||
}
|
||
|
||
function appendThreadProgressEventInState(
|
||
state: BossState,
|
||
input: Omit<ThreadProgressEvent, "eventId">,
|
||
) {
|
||
state.threadProgressEvents.unshift({
|
||
eventId: randomToken("thread-event"),
|
||
...input,
|
||
});
|
||
}
|
||
```
|
||
|
||
并把 heartbeat / thread reply 的同步策略改成:
|
||
|
||
```ts
|
||
if (shouldQueueProjectUnderstandingSync(project, observedActivityAt, state)) {
|
||
// 仍保留关键时刻全量理解
|
||
} else {
|
||
appendThreadProgressEventInState(state, {
|
||
projectId: project.id,
|
||
threadId: project.threadMeta.threadId,
|
||
threadDisplayName: project.threadMeta.threadDisplayName,
|
||
deviceId: project.deviceIds[0] ?? "mac-studio",
|
||
eventType: "progress_updated",
|
||
summary: "检测到线程有新活动",
|
||
phase: project.projectUnderstanding ? "功能实现" : undefined,
|
||
createdAt: observedActivityAt,
|
||
sourceTaskId: "heartbeat-auto",
|
||
});
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 4: 重新跑测试,确认通过**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
npx --yes tsx --test /Users/kris/code/boss/tests/thread-status-sync.test.ts /Users/kris/code/boss/tests/device-import-draft.test.ts
|
||
```
|
||
|
||
Expected:
|
||
- PASS
|
||
|
||
- [ ] **Step 5: 提交这一小步**
|
||
|
||
```bash
|
||
git add /Users/kris/code/boss/src/lib/boss-data.ts /Users/kris/code/boss/tests/thread-status-sync.test.ts /Users/kris/code/boss/tests/device-import-draft.test.ts
|
||
git commit -m "feat: add lightweight thread progress events"
|
||
```
|
||
|
||
### Task 3: 主 Agent 默认读取线程状态文档与最近事件
|
||
|
||
**Files:**
|
||
- Modify: `/Users/kris/code/boss/src/lib/boss-master-agent.ts`
|
||
- Test: `/Users/kris/code/boss/tests/master-agent-thread-status-prompt.test.ts`
|
||
|
||
- [ ] **Step 1: 写失败测试,锁住主 Agent prompt 读取顺序**
|
||
|
||
```ts
|
||
import test from "node:test";
|
||
import assert from "node:assert/strict";
|
||
import { readState } from "../src/lib/boss-data.ts";
|
||
import { buildMasterAgentExecutionPromptForTest } from "../src/lib/boss-master-agent.ts";
|
||
|
||
test("master agent prompt prefers thread status documents and recent events", async () => {
|
||
const state = await readState();
|
||
state.threadStatusDocuments = [
|
||
{
|
||
documentId: "doc-1",
|
||
projectId: "demo-thread",
|
||
threadId: "thread-1",
|
||
threadDisplayName: "树莓派二代查询",
|
||
folderName: "Talking",
|
||
deviceId: "mac-studio",
|
||
projectGoal: "完成树莓派二代问答",
|
||
currentPhase: "功能实现",
|
||
currentProgress: "已经打通线程排序与会话时间刷新",
|
||
technicalArchitecture: "Boss Web + Android + local-agent",
|
||
currentBlockers: "群聊回流文案还需要收口",
|
||
recommendedNextStep: "继续修群聊回流提示",
|
||
keyFiles: ["src/lib/boss-data.ts"],
|
||
keyCommands: ["npm run build"],
|
||
updatedAt: "2026-04-04T19:00:00+08:00",
|
||
sourceTaskId: "task-doc",
|
||
sourceKind: "full_sync",
|
||
},
|
||
];
|
||
state.threadProgressEvents = [
|
||
{
|
||
eventId: "event-1",
|
||
projectId: "demo-thread",
|
||
threadId: "thread-1",
|
||
threadDisplayName: "树莓派二代查询",
|
||
deviceId: "mac-studio",
|
||
eventType: "progress_updated",
|
||
summary: "已完成会话排序修复",
|
||
phase: "功能实现",
|
||
createdAt: "2026-04-04T19:10:00+08:00",
|
||
sourceTaskId: "task-event",
|
||
},
|
||
];
|
||
|
||
const prompt = buildMasterAgentExecutionPromptForTest(state, "请接手这个项目继续推进");
|
||
|
||
assert.match(prompt, /线程状态文档/);
|
||
assert.match(prompt, /完成树莓派二代问答/);
|
||
assert.match(prompt, /已完成会话排序修复/);
|
||
});
|
||
```
|
||
|
||
- [ ] **Step 2: 跑测试,确认先失败**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
npx --yes tsx --test /Users/kris/code/boss/tests/master-agent-thread-status-prompt.test.ts
|
||
```
|
||
|
||
Expected:
|
||
- FAIL,提示当前 prompt 里没有线程状态文档和进展事件
|
||
|
||
- [ ] **Step 3: 修改主 Agent prompt 组装**
|
||
|
||
在 `/Users/kris/code/boss/src/lib/boss-master-agent.ts` 增加线程状态摘要:
|
||
|
||
```ts
|
||
const activeThreadStatusDocs = state.threadStatusDocuments
|
||
.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))
|
||
.slice(0, 3)
|
||
.map((doc) =>
|
||
[
|
||
`线程状态文档:${doc.threadDisplayName}`,
|
||
doc.projectGoal ? `目标=${doc.projectGoal}` : undefined,
|
||
doc.currentPhase ? `阶段=${doc.currentPhase}` : undefined,
|
||
doc.currentProgress ? `进度=${doc.currentProgress}` : undefined,
|
||
doc.technicalArchitecture ? `架构=${doc.technicalArchitecture}` : undefined,
|
||
doc.currentBlockers ? `阻塞=${doc.currentBlockers}` : undefined,
|
||
doc.recommendedNextStep ? `下一步=${doc.recommendedNextStep}` : undefined,
|
||
]
|
||
.filter(Boolean)
|
||
.join(" / "),
|
||
)
|
||
.join("\\n");
|
||
|
||
const recentThreadProgressEvents = state.threadProgressEvents
|
||
.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
|
||
.slice(0, 5)
|
||
.map((event) => `${event.threadDisplayName}:${event.summary}`)
|
||
.join("\\n");
|
||
```
|
||
|
||
并把返回 prompt 的内容改成:
|
||
|
||
```ts
|
||
"线程状态文档:",
|
||
activeThreadStatusDocs || "无",
|
||
"",
|
||
"最近线程进展:",
|
||
recentThreadProgressEvents || "无",
|
||
```
|
||
|
||
- [ ] **Step 4: 重新跑测试,确认通过**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
npx --yes tsx --test /Users/kris/code/boss/tests/master-agent-thread-status-prompt.test.ts
|
||
```
|
||
|
||
Expected:
|
||
- PASS
|
||
|
||
- [ ] **Step 5: 提交这一小步**
|
||
|
||
```bash
|
||
git add /Users/kris/code/boss/src/lib/boss-master-agent.ts /Users/kris/code/boss/tests/master-agent-thread-status-prompt.test.ts
|
||
git commit -m "feat: read thread status documents in master agent prompts"
|
||
```
|
||
|
||
### Task 4: 新增线程状态只读 API
|
||
|
||
**Files:**
|
||
- Create: `/Users/kris/code/boss/src/app/api/v1/projects/[projectId]/thread-status/route.ts`
|
||
- Modify: `/Users/kris/code/boss/src/lib/boss-data.ts`
|
||
- Test: `/Users/kris/code/boss/tests/thread-status-route.test.ts`
|
||
|
||
- [ ] **Step 1: 写失败测试,锁住路由返回格式**
|
||
|
||
```ts
|
||
import test from "node:test";
|
||
import assert from "node:assert/strict";
|
||
import { GET } from "../src/app/api/v1/projects/[projectId]/thread-status/route.ts";
|
||
|
||
test("GET thread-status returns current document and recent events", async () => {
|
||
const response = await GET(
|
||
new Request("http://localhost/api/v1/projects/demo-thread/thread-status"),
|
||
{ params: Promise.resolve({ projectId: "demo-thread" }) },
|
||
);
|
||
|
||
assert.equal(response.status, 200);
|
||
const body = await response.json();
|
||
assert.ok(body.threadStatusDocument);
|
||
assert.ok(Array.isArray(body.recentProgressEvents));
|
||
});
|
||
```
|
||
|
||
- [ ] **Step 2: 跑测试,确认先失败**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
npx --yes tsx --test /Users/kris/code/boss/tests/thread-status-route.test.ts
|
||
```
|
||
|
||
Expected:
|
||
- FAIL,提示 route 不存在或返回格式不对
|
||
|
||
- [ ] **Step 3: 实现只读 route**
|
||
|
||
创建 `/Users/kris/code/boss/src/app/api/v1/projects/[projectId]/thread-status/route.ts`:
|
||
|
||
```ts
|
||
import { NextResponse } from "next/server";
|
||
import { readState } from "@/src/lib/boss-data";
|
||
|
||
export async function GET(
|
||
_request: Request,
|
||
{ params }: { params: Promise<{ projectId: string }> },
|
||
) {
|
||
const { projectId } = await params;
|
||
const state = await readState();
|
||
const project = state.projects.find((item) => item.id === projectId);
|
||
if (!project) {
|
||
return NextResponse.json({ error: "PROJECT_NOT_FOUND" }, { status: 404 });
|
||
}
|
||
const threadStatusDocument =
|
||
state.threadStatusDocuments.find((item) => item.projectId === projectId) ?? null;
|
||
const recentProgressEvents = state.threadProgressEvents
|
||
.filter((item) => item.projectId === projectId)
|
||
.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
|
||
.slice(0, 5);
|
||
return NextResponse.json({
|
||
projectId,
|
||
threadStatusDocument,
|
||
recentProgressEvents,
|
||
});
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 4: 重新跑测试,确认通过**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
npx --yes tsx --test /Users/kris/code/boss/tests/thread-status-route.test.ts
|
||
```
|
||
|
||
Expected:
|
||
- PASS
|
||
|
||
- [ ] **Step 5: 提交这一小步**
|
||
|
||
```bash
|
||
git add /Users/kris/code/boss/src/app/api/v1/projects/[projectId]/thread-status/route.ts /Users/kris/code/boss/tests/thread-status-route.test.ts
|
||
git commit -m "feat: add thread status read api"
|
||
```
|
||
|
||
### Task 5: 在线程会话信息页接入线程状态入口
|
||
|
||
**Files:**
|
||
- Modify: `/Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/BossApiClient.java`
|
||
- Modify: `/Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/ConversationInfoActivity.java`
|
||
- Create: `/Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/ThreadStatusActivity.java`
|
||
- Test: `/Users/kris/code/boss/android/app/src/test/java/com/hyzq/boss/ConversationInfoActivityTest.java`
|
||
- Test: `/Users/kris/code/boss/android/app/src/test/java/com/hyzq/boss/ThreadStatusActivityTest.java`
|
||
|
||
- [ ] **Step 1: 写失败测试,锁住入口和渲染**
|
||
|
||
```java
|
||
@Test
|
||
public void conversationInfoShowsThreadStatusEntryForThreadConversation() {
|
||
BossUi.ViewNode root = renderConversationInfoForThread();
|
||
assertTrue(viewTreeContainsText(root, "线程状态"));
|
||
}
|
||
|
||
@Test
|
||
public void threadStatusActivityRendersCurrentGoalProgressAndNextStep() {
|
||
BossUi.ViewNode root = renderThreadStatusActivityWithFixture();
|
||
assertTrue(viewTreeContainsText(root, "当前目标"));
|
||
assertTrue(viewTreeContainsText(root, "当前进度"));
|
||
assertTrue(viewTreeContainsText(root, "建议下一步"));
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 跑测试,确认先失败**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
cd /Users/kris/code/boss/android && ./gradlew testDebugUnitTest --tests com.hyzq.boss.ConversationInfoActivityTest --tests com.hyzq.boss.ThreadStatusActivityTest --no-daemon
|
||
```
|
||
|
||
Expected:
|
||
- FAIL,提示没有 `线程状态` 入口或 Activity 不存在
|
||
|
||
- [ ] **Step 3: 实现 Android 入口与只读页**
|
||
|
||
在 `/Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/BossApiClient.java` 增加:
|
||
|
||
```java
|
||
public JSONObject getThreadStatus(String projectId) throws IOException, ApiException {
|
||
return getJson("/api/v1/projects/" + Uri.encode(projectId) + "/thread-status");
|
||
}
|
||
```
|
||
|
||
在 `/Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/ConversationInfoActivity.java` 中,当当前会话是线程会话时增加:
|
||
|
||
```java
|
||
addMenuRow(container, "线程状态", v -> {
|
||
Intent intent = new Intent(this, ThreadStatusActivity.class);
|
||
intent.putExtra("projectId", projectId);
|
||
startActivity(intent);
|
||
});
|
||
```
|
||
|
||
创建 `/Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/ThreadStatusActivity.java`,读取接口并展示:
|
||
|
||
```java
|
||
renderSection("当前目标", document.optString("projectGoal"));
|
||
renderSection("当前阶段", document.optString("currentPhase"));
|
||
renderSection("当前进度", document.optString("currentProgress"));
|
||
renderSection("技术架构", document.optString("technicalArchitecture"));
|
||
renderSection("当前阻塞", document.optString("currentBlockers"));
|
||
renderSection("建议下一步", document.optString("recommendedNextStep"));
|
||
renderEvents(recentProgressEvents);
|
||
```
|
||
|
||
- [ ] **Step 4: 重新跑测试,确认通过**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
cd /Users/kris/code/boss/android && ./gradlew testDebugUnitTest --tests com.hyzq.boss.ConversationInfoActivityTest --tests com.hyzq.boss.ThreadStatusActivityTest --no-daemon
|
||
```
|
||
|
||
Expected:
|
||
- PASS
|
||
|
||
- [ ] **Step 5: 提交这一小步**
|
||
|
||
```bash
|
||
git add /Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/BossApiClient.java /Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/ConversationInfoActivity.java /Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/ThreadStatusActivity.java /Users/kris/code/boss/android/app/src/test/java/com/hyzq/boss/ConversationInfoActivityTest.java /Users/kris/code/boss/android/app/src/test/java/com/hyzq/boss/ThreadStatusActivityTest.java
|
||
git commit -m "feat: add thread status view in android"
|
||
```
|
||
|
||
### 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: 同步文档**
|
||
|
||
在文档里明确新增:
|
||
|
||
```md
|
||
- 主 Agent 当前会优先读取线程状态文档和最近进展事件,而不是常态反复重跑完整理解
|
||
- 已导入设备与新导入设备都走统一的线程状态同步链
|
||
- 线程会话信息页新增只读 `线程状态`
|
||
```
|
||
|
||
- [ ] **Step 2: 跑完整关键回归**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
npx --yes tsx --test /Users/kris/code/boss/tests/thread-status-sync.test.ts /Users/kris/code/boss/tests/device-import-draft.test.ts /Users/kris/code/boss/tests/master-agent-thread-status-prompt.test.ts /Users/kris/code/boss/tests/thread-status-route.test.ts
|
||
npm run lint
|
||
npm run build
|
||
cd /Users/kris/code/boss/android && ./gradlew testDebugUnitTest --tests com.hyzq.boss.ConversationInfoActivityTest --tests com.hyzq.boss.ThreadStatusActivityTest --no-daemon
|
||
cd /Users/kris/code/boss/android && ./gradlew assembleRelease --no-daemon
|
||
```
|
||
|
||
Expected:
|
||
- 全部 PASS
|
||
- build 成功
|
||
- Android release 包成功生成
|
||
|
||
- [ ] **Step 3: 部署服务器**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
sshpass -p 'Asd123456.' ssh -o StrictHostKeyChecking=no ubuntu@106.53.170.158 "sudo rm -rf /opt/boss/.next && sudo mkdir -p /opt/boss/.next && sudo chown -R ubuntu:ubuntu /opt/boss /opt/boss/.next"
|
||
sshpass -p 'Asd123456.' rsync -az --delete -e "ssh -o StrictHostKeyChecking=no" --exclude '.git/' --exclude 'node_modules/' --exclude 'data/' --exclude '.superpowers/' --exclude 'public/downloads/' /Users/kris/code/boss/ ubuntu@106.53.170.158:/opt/boss/
|
||
sshpass -p 'Asd123456.' ssh -o StrictHostKeyChecking=no ubuntu@106.53.170.158 "cd /opt/boss && sudo systemctl restart boss-web && sudo systemctl restart caddy && curl -fsS http://127.0.0.1:3000/api/health"
|
||
curl -fsS https://boss.hyzq.net/api/health
|
||
```
|
||
|
||
Expected:
|
||
- 远端本机健康检查返回 `ok:true`
|
||
- 公网健康检查返回 `ok:true`
|
||
|
||
- [ ] **Step 4: 最终提交**
|
||
|
||
```bash
|
||
git add /Users/kris/code/boss/README.md /Users/kris/code/boss/docs/architecture/current_runtime_and_deploy_status_cn.md /Users/kris/code/boss/docs/architecture/api_and_service_inventory_cn.md
|
||
git commit -m "feat: add thread status sync pipeline"
|
||
git push gitea codex/wechat-native-ui-rollback
|
||
```
|
||
|
||
---
|
||
|
||
## 自检
|
||
|
||
### Spec 覆盖
|
||
|
||
- 线程状态文档:Task 1
|
||
- 线程进展事件:Task 1 / Task 2
|
||
- 主 Agent 读取“文档 + 最近事件”:Task 3
|
||
- 关键时刻深拉保留:Task 2 / Task 3
|
||
- APP 只读查看线程状态:Task 4 / Task 5
|
||
- 不写入项目仓库:通过 Task 1-5 全部走 Boss 内部状态层实现
|
||
|
||
### Placeholder 扫描
|
||
|
||
- 没有 `TBD / TODO / implement later`
|
||
- 每个任务都给了文件、代码、命令和预期结果
|
||
|
||
### 类型一致性
|
||
|
||
- 线程状态文档统一命名为 `ThreadStatusDocument`
|
||
- 增量事件统一命名为 `ThreadProgressEvent`
|
||
- API 字段统一用 `threadStatusDocument / recentProgressEvents`
|
||
|