207 lines
28 KiB
Markdown
207 lines
28 KiB
Markdown
# U3 r0.5 Safe Terminal Evidence and Fast Feedback 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:** 上传材料后立即给出可信的收件与工作状态,让实质结论经来源核对后逐步上同一张卡;为未见 `[DONE]` 的模型流保存无内容诊断,同时继续拒绝执行不完整工具调用。
|
||
|
||
**Architecture:** Agent-Native 的 OpenAI SSE 解析器为每次终态附加有界、无原文的读取观察;Backend 评测入口把它与实际 run ID、Usage 和停止决定一起落到受限记录。Backend 在系统预启动材料整理的首回合选择宿主确认 runner,不调用前台模型,因此不会先流出无法核对的自由文本;后台仍按 r0.4 的来源、阶段和失败规则运行。普通文字+小材料与已完成任务上的新提问继续原路径,并单独验证其范围限制。
|
||
|
||
**Tech Stack:** Go 1.27、Agent-Native SSE/runtime、Backend runmgr/受控评测、SQLite/隔离 PostgreSQL、React/Vite/Playwright、Dev `make test`/`make build` 与 Gitea CI。
|
||
|
||
**Spec:** [已批准的 r0.5 书面设计](2026-10-14-fast-slow-u3-r05-terminal-evidence-and-fast-feedback-design.md),Documents `0d473fe866703eab32f715ddf12d1b25913b822b`;[r0.4 A01 核验记录](../reports/2026-10-13-fast-slow-u2-u3-validation.md)。用户只批准**编写本计划**;计划本身待阶段 5 人工评审,不授权产品代码、付费模型、合入或部署。
|
||
|
||
## Global Constraints
|
||
|
||
- r0.4 原始 A01(Dev `d38dbdc`,2 个付费 run/3 次调用/29,062 返回 Usage,0 个检查点)与受限归档原样保留,质量判定 **FAIL**。Backend `0637547` 的失败子 run Usage 修复也不追溯改写样本。
|
||
- 缺 `[DONE]`、读取错误、取消、工具身份或对象 JSON 参数不完整时,工具执行次数必须为 0;合法 JSON、`finish_reason`、Usage 或正常 EOF 均不能单独替代完整终止标记。不新增自动重试、补参数、供应商切换或模型摘要调用。
|
||
- 新终态诊断只保存枚举、布尔值、计数、HTTP 状态、实际 run ID/调用序号和 Usage;**不保存新的原始 SSE、prompt、正文、思考文本、工具名/参数、API key、请求头**。旧历史原件保持只读;新评测记录要移除现有 `messages`、`toolDeclarations`、`terminal` 的原始内容副本。
|
||
- 保留 r0.3/r0.4 的 sourceId、owner/project、artifact hash/解析版本、原句、数字归属、CAS、seq、失败通知与建议选择条件。Core `TaskSnapshot`、HTTP 公开契约、数据库表、生产模型路由、预算与既有普通聊天入口不改。
|
||
- 宿主确认不计入两个有用阶段成果。自动材料入口只有在系统确实预启动或复用活动任务时跳过前台模型;排队、运行、复用、启动失败与已完成复用按真实卡片状态分流。用户原文字重点继续进入慢任务;文字+小材料仍走原直连路径,不能用 A01 证明其质量。
|
||
- 默认仍是三栏与一张任务卡。若实际浏览器证明必须修改 UI 代码,先读 Dev `DESIGN.md`,只消费 `@ui` 令牌、`mk-*` 动效和 Collapsible 原语;相关计划范围与组件 PR 需按影响重新评审。异步投影仍按 seq 丢弃过期事件,不抢文件、焦点和阅读位置。
|
||
- 所有实施、真实 HTTP、PG、浏览器、审阅与 CI 都是**零付费**;只用 `httptest`/本地假流,不能运行 live `TestWorkCheckpoints` 或受保护模型路由。后续受限供应商探针或 A01 必须分别提交资源提案;本计划不预填、不授权任何 token/run 额度。
|
||
- Dev `AGENTS.md` 的唯一 precheck 收据、`task-resume`、请求审核/验收/合入前 `task-sync`、`make evidence`、独立 REPLAY、准确 HEAD GATE 和任务工作区保护全部适用。计划实施需另获用户阶段 5 批准;质量 FAIL 时八个 PR 保持草稿。
|
||
|
||
## Review Focus
|
||
|
||
| 容易伤害用户的输入/状态 | 所属任务的固定用例与预期 |
|
||
|---|---|
|
||
| `tool_calls` 有合法参数、Usage、正常 EOF,却没有 `[DONE]` | Task 1 `TestTerminalObservationCleanEOFWithoutDone`、Task 3 `TestEvaluationStopsAfterUnmarkedToolEOF`:记录 clean EOF,0 次工具执行、0 次后继请求。 |
|
||
| SSE 最后一帧半个 JSON,或 HTTP 读取错误/取消 | Task 1 `TestTerminalObservationMalformedAndReadError`:分类不同、已返回 Usage 不丢、原始内容不入诊断。 |
|
||
| 三个后台槽已满,材料排队;同批材料重发 | Task 2 `TestMaterialAckQueueAndReuse`:确认文字与卡片真实状态一致,只有一个材料子任务、前台 provider 0 次调用。 |
|
||
| 用户带文字+长材料,或文字+小材料 | Task 2 `TestMaterialAckPreservesUserFocusAndSmallPath`、Task 4 浏览器用例:前者重点进入慢任务且前台只确认,后者仍走原模型路径。 |
|
||
| prompt、工具参数或 provider 错误含敏感文本;诊断文件写入失败 | Task 3 `TestEvaluationTerminalRecordContainsNoPayload`、`TestEvaluationStopsWhenTerminalRecordUnavailable`:文件无哨兵原文,写入失败不放行工具终态或下一次请求。 |
|
||
|
||
## 文件责任与依赖顺序
|
||
|
||
| 单元 | 文件(相对对应仓根) | 产物/消费者 |
|
||
|---|---|---|
|
||
| 1 Agent-Native 终态观察 | 修改 `runtime/llm/{types,sse,client,sse_test}.go`,新建 `runtime/llm/client_test.go` | `llm.Event.TerminalObservation`;Task 3 消费。Agent-Squad 与 Backend 的 Agent-Native gitlink随后固定同一 SHA。 |
|
||
| 2 Backend 可信快反馈 | `internal/runmgr/{material,material_ack,runmgr,material_test,material_ack_test}.go` | 内部 `materialIntakeDecision` 与 `materialAckRunner`;Task 4 的 HTTP/浏览器消费。 |
|
||
| 3 Backend 受控评测记录 | `internal/server/{work_checkpoint_eval_test,work_checkpoint_eval_budget_test}.go` | 内容最小化的 `provider-records.json` 与故障停止证据;Task 5 消费。 |
|
||
| 4 整条用户链 | Backend `internal/runmgr/material_notice_failure_test.go`、`internal/server/work_checkpoint_eval_test.go`;Frontend `e2e/work-checkpoints.playwright.ts` | 真实 HTTP、SQLite、隔离 PG、浏览器的收件→阶段→终态证据。Frontend 仅按需要改测试,不预设 UI 生产代码。 |
|
||
| 5 固定与评审 | Agent-Squad `deps/agent-native`;Backend `deps/agent-native`;Dev `deps/agent-native`、`deps/agent-squad`、`modules/backend`、可选 `modules/frontend`、任务卡与 Documents 指针 | 单一准确组件组合、独立复审、当前 HEAD CI、阶段 6 人工评审材料及资源决策。 |
|
||
|
||
接口顺序:Task 1 的只读终态观察由 Task 3 记录;Task 2 的宿主确认由 Task 4 从真实浏览器观察;Task 1–4 的组件 SHA 进入 Task 5。Task 2 与 Task 3 可在各自包内分别验证,但 Backend 提交和 Dev 固定仍按上述依赖顺序完成。已有登记任务工作区与草稿 PR 继续使用,不从嵌套 detached gitlink 直接交付。
|
||
|
||
### Task 1: Agent-Native 有界终态观察,不放宽工具门禁
|
||
|
||
**Files:** 修改 `runtime/llm/types.go`、`runtime/llm/sse.go`、`runtime/llm/client.go`、`runtime/llm/sse_test.go`;新建 `runtime/llm/client_test.go`。**Interfaces:** 生产现有 `llm.Event` 的 `FinishReason`、`StreamComplete`、`Usage`、`HTTPStatus` 保留;新增只在终态/错误事件上出现的 `*TerminalObservation`,字段为 `ReadOutcome`(`done_marker|clean_eof|read_error|cancelled|invalid_frame|provider_error`)、`LastFrameJSONComplete`、`UsagePresent`、`ToolFragments`、`ToolArgumentBytes`(饱和计数)、`ToolArgumentsValid`。不包含原文、工具名或错误原句。Task 3 只消费这些标量。
|
||
|
||
- [ ] **Step 1: 写 RED。** 以现有 `TestParseSSEToolFinishWithoutDone`、`TestParseSSEReaderErrorAfterUsageKeepsUsage` 为底,新增 `TestTerminalObservationCleanEOFWithoutDone`:SSE 有合法工具 JSON、`finish_reason=tool_calls`、Usage 17,却在正常 EOF 结束;断言终态错误、`ReadOutcome=clean_eof`、`StreamComplete=false`、`ToolArgumentsValid=true`、`ToolFragments=1`、`ToolArgumentBytes>0`、`len(Message.ToolCalls)==0`。`TestTerminalObservationMalformedAndReadError` 表驱动比较半帧 JSON、注入读错误、取消;断言分类及返回 Usage。`client_test.go` 的本地 `httptest` 断言成功 HTTP 200 只作为标量传到终态,不保存 URL/请求头。
|
||
|
||
```go
|
||
if end.Err == nil || end.StreamComplete || len(end.Message.ToolCalls) != 0 ||
|
||
end.TerminalObservation == nil || end.TerminalObservation.ReadOutcome != "clean_eof" ||
|
||
end.Usage.TotalTokens != 17 {
|
||
t.Fatalf("unsafe or unclassified terminal: %#v", end)
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 记录 RED。** `GOWORK=off go test ./runtime/llm -run 'TestTerminalObservationCleanEOFWithoutDone|TestTerminalObservationMalformedAndReadError|TestClientTerminalObservationHTTPStatus' -count=1`;预计因新观察字段/分类缺失而失败,使用 Dev `make evidence` 保存原始 SELF 输出。不得用旧 r0.4 守卫测试的 PASS 代替新诊断测试。
|
||
- [ ] **Step 3: 最小 GREEN。** 在 `sseEventReader`/`emitParsed` 区分 `[DONE]`、扫描器 clean EOF、`sc.Err()`、坏 JSON 和取消;仅维护布尔、枚举与计数,参数字节数饱和到 `4*1024*1024+1`。为诊断先计算已收参数的对象 JSON 有效性,**再**运行原有“缺 `[DONE]` 必拒绝”的工具释放门禁,不能因 `ToolArgumentsValid=true` 绕过它。创建终态 Event 时附观察;`client.run` 对成功响应复制 `resp.StatusCode` 到终态,不读取或记录头/body。现有 `toolFinishExecutable` 与“缺 `[DONE]` 不发 ToolCalls”的顺序一字不放宽;`KindError` 路径也要给出可判定的失败类别,不伪造正常终态。
|
||
|
||
```go
|
||
type TerminalObservation struct {
|
||
ReadOutcome string
|
||
LastFrameJSONComplete bool
|
||
UsagePresent bool
|
||
ToolFragments int
|
||
ToolArgumentBytes int
|
||
ToolArgumentsValid bool
|
||
}
|
||
// 仅 llm.Event 追加:TerminalObservation *TerminalObservation
|
||
```
|
||
|
||
- [ ] **Step 4: 跑 GREEN 与兼容。** 重跑 Step 2;另跑 `GOWORK=off go vet ./... && GOWORK=off go test -count=1 ./...`,重点复看合法多片工具、纯文本缺 `[DONE]` 的既有行为,以及 `runtime.Run` 的 Usage/不重试测试。测试诊断序列化后不含哨兵 prompt、工具名、参数原文。记录若当前 `client.go` 的 HTTP 状态只在错误事件有值,新增正常 200 不影响 fallback 选择。
|
||
- [ ] **Step 5: 提交 Agent-Native。** 仅提交该组件文件;描述 `Event` 的加法式 Go 契约及 Agent-Squad/Backend 固定指针依赖,不宣布供应商行为已修复。推送现有草稿 PR,核对它的准确 HEAD CI。
|
||
|
||
### Task 2: Backend 预启动材料的宿主确认
|
||
|
||
**Files:** `internal/runmgr/material.go`、新建 `internal/runmgr/material_ack.go` 与 `material_ack_test.go`、`internal/runmgr/runmgr.go`、`internal/runmgr/material_test.go`。**Interfaces:** 内部 `materialIntakeDecision{Note string, State materialIntakeState, CardID core.ID}`;`State` 仅为 `none|queued|running|reused_running|completed|failed|unknown`。`materialAckRunner` 实现已有 `runtime.Runner`,只消费可信附件数、工作重点是否存在与卡片状态。`completed`/`none` 继续原 runner;`queued|running|reused_running|failed|unknown` 走宿主确认,不请求前台 provider。
|
||
|
||
- [ ] **Step 1: 写 RED。** 用 `newCardSpawnEnv` 和实际 `Store` 增加 `TestMaterialAckAttachmentOnlyDoesNotCallParentModel`:三附件无文字时先建任务卡,再在同一对话 run 保存一条不含“剩余4项含3项依赖接口”的宿主消息;前台 stream 调用数 **0**,慢任务数 **1**,宿主消息不增加 `checkpointSeq`。`TestMaterialAckQueueAndReuse` 将三个后台槽占满再发送,断言卡为 queued、确认文案不称“正在执行”;同批重发只复用一张卡,后台刚完成但内存活动键尚未释放时以卡片终态为准。`TestMaterialAckPreservesUserFocusAndSmallPath` 断言文字+长材料的原文重点在 `buildMaterialTaskFor`,文字+小材料仍调用原 runner;完成态重发仍可进入普通问答。`TestMaterialAckSkipsSquadEntrypoint` 将 `runtime.entry=squad`,断言系统预启动分支也不调用小队 runner。`TestMaterialAckFailureAndUnknown` 注入注册/读卡错误,断言不称已启动、不追加模型请求。
|
||
|
||
```go
|
||
if parentProviderCalls != 0 || materialChildren != 1 || card.Task.CheckpointSeq != 0 ||
|
||
strings.Contains(visibleReply, "剩余 4 项(含 3 项依赖接口)") ||
|
||
!strings.Contains(visibleReply, "待确认") {
|
||
t.Fatalf("first reply is not a truthful host acknowledgement")
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 记录 RED。** `GOWORK=off go test ./internal/runmgr -run 'TestMaterialAckAttachmentOnlyDoesNotCallParentModel|TestMaterialAckQueueAndReuse|TestMaterialAckPreservesUserFocusAndSmallPath|TestMaterialAckSkipsSquadEntrypoint|TestMaterialAckFailureAndUnknown' -count=1`;旧实现会调用前台 runner/缺状态分流,预计失败。原始 SELF 收据经 Dev `make evidence`,不要手抄输出。
|
||
- [ ] **Step 3: 最小 GREEN。** 把 `materialIntake`、`materialExisting`、`materialSpawn` 的内部返回收敛为 `materialIntakeDecision`,更新同包调用和现有测试;从已注册的、同 owner/project/inputRevision 的卡片读取 queued/running/terminal 状态,读不到就 `unknown`,不靠中文 note 判断分支。`startClaimedRun` 在 `materialIntake` 后按 `State` 将 `runner` 替换为 `materialAckRunner`;该 runner 发一次既有 `EvRunPhase(Responding)`/`EvMessageDelta`,再用 `SetAssistantParts` 交给 `finishRun` 正常持久化。短文案只用可信附件数/状态,并明确未经核对的关系“待确认”;失败态提示可重新提出请求但不自动重试。原始模型候选不调用、不进入事件、转录或后续上下文。`none`/`completed` 保留原模型入口;`ensureMaterial` 的文字+小材料兜底继续原路径。
|
||
|
||
```go
|
||
type materialIntakeState string
|
||
const (
|
||
materialNone materialIntakeState = "none"
|
||
materialQueued materialIntakeState = "queued"
|
||
materialRunning materialIntakeState = "running"
|
||
materialReusedRunning materialIntakeState = "reused_running"
|
||
materialCompleted materialIntakeState = "completed"
|
||
materialFailed materialIntakeState = "failed"
|
||
materialUnknown materialIntakeState = "unknown"
|
||
)
|
||
func (s materialIntakeState) needsAcknowledgement() bool {
|
||
return s == materialQueued || s == materialRunning || s == materialReusedRunning ||
|
||
s == materialFailed || s == materialUnknown
|
||
}
|
||
decision := m.materialIntake(rc)
|
||
rc.materialNote = decision.Note
|
||
if decision.State.needsAcknowledgement() {
|
||
runner = materialAckRunner{decision: decision, attachmentCount: len(rc.attachments), hasUserFocus: messageText(rc.userMessage) != ""}
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 4: 跑 GREEN 与并发。** 重跑 Step 2,再跑 `GOWORK=off go test -count=1 ./internal/runmgr ./internal/runtime ./internal/cardsvc` 与 `GOWORK=off go test -race -count=1 ./internal/runmgr -run 'TestMaterialAckQueueAndReuse|TestMaterialIntakeAttachmentOnlyLifecycle'`。核对授权关闭、无附件、任务注册失败、取消、重启后的活动卡去重、原已有完成结果、任务来源版本变化与后台 notice;宿主确认不能被通知当作最终整理正文或建议列表。
|
||
- [ ] **Step 5: 提交 Backend 快反馈单元。** 只提交此任务相关文件,保留消息/卡片的现有 Core 契约;说明首回合少一次模型请求及其 Usage 可比性变化,不把普通小材料问答标成已整改。
|
||
|
||
### Task 3: 受控评测的无内容记录与停止规则
|
||
|
||
**Files:** `internal/server/work_checkpoint_eval_test.go`、`internal/server/work_checkpoint_eval_budget_test.go`。**Interfaces:** 消费 Task 1 `Event.TerminalObservation`、既有 `FinishReason`/`StreamComplete`/`Usage`/`HTTPStatus`;`provider-records.json` 保留 `runId`、调用号、输出上限、返回 Usage、结束原因及诊断标量,移除以后样本里的 `messages`、`toolDeclarations` 和原样 `terminal`。历史 r0.4 文件哈希不变。
|
||
|
||
- [ ] **Step 1: 写 RED。** `httptest` 分别返回带 `[DONE]` 的合法工具调用、正常 EOF 无 `[DONE]` 且 JSON/Usage 合法、半帧 JSON、读错误、缺 Usage。新增 `TestEvaluationRecordsContentFreeTerminal`:将哨兵 `PRIVATE_PROMPT_R05` 放入请求消息、`PRIVATE_ARGUMENT_R05` 放入工具参数,断言新 `provider-records.json` 不含两串和原始 `messages`/`toolDeclarations`/`terminal` 键,但有 run ID、调用号、`readOutcome`、`usagePresent`、`reportedTokens`。`TestEvaluationStopsAfterUnmarkedToolEOF` 与 `TestEvaluationStopsWhenTerminalRecordUnavailable` 分别断言无 `[DONE]` 和 0600 写入失败后第二次 provider 调用数仍为 **0**;已返回 Usage 必须保留。
|
||
|
||
```go
|
||
raw, err := os.ReadFile(filepath.Join(dir, "provider-records.json"))
|
||
if err != nil { t.Fatal(err) }
|
||
if bytes.Contains(raw, []byte("PRIVATE_PROMPT_R05")) ||
|
||
bytes.Contains(raw, []byte("PRIVATE_ARGUMENT_R05")) ||
|
||
bytes.Contains(raw, []byte(`"toolDeclarations"`)) {
|
||
t.Fatal("evaluation record contains model payload")
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 记录 RED。** `GOWORK=off go test -tags live ./internal/server -run 'TestEvaluationRecordsContentFreeTerminal|TestEvaluationStopsAfterUnmarkedToolEOF|TestEvaluationStopsWhenTerminalRecordUnavailable' -count=1`。只运行这些本地假 provider 测试;**不得**用无 `-run` 的 live-tag 命令误启动 `eval/live/TestWorkCheckpoints`。
|
||
- [ ] **Step 3: 最小 GREEN。** 在 `evalProviderStreamWithProvider` 的“先关流、落 Usage/停止记录、再释放终态”顺序内生成只含标量的记录;`terminal==nil` 时使用有限的 `missing_terminal` 类别,错误信息仅记录枚举,不保存 `Err.Error()` 的可能敏感原文。`terminalFault`、`usageUnknown`、`checkpointRejected`、批次与单 run 上限继续在下次请求前拒绝。记录写入失败则终态不执行工具、下一次请求继续被本地拒绝;原始历史记录不迁移。
|
||
|
||
```go
|
||
type evalCallRecord struct {
|
||
RunID string `json:"runId"`
|
||
Call int `json:"call"`
|
||
OutputCap int `json:"outputCap"`
|
||
ReportedTokens int `json:"reportedTokens"`
|
||
FinishReason string `json:"finishReason"`
|
||
StreamComplete bool `json:"streamComplete"`
|
||
Observation *agentllm.TerminalObservation `json:"terminalObservation,omitempty"`
|
||
FailureClass string `json:"failureClass,omitempty"`
|
||
}
|
||
// 不再包含 messages、toolDeclarations 或整个 terminal Event。
|
||
```
|
||
- [ ] **Step 4: 跑 GREEN 与控制组。** 重跑 Step 2,再跑 `GOWORK=off go test -count=1 ./...`;仅用精确正则执行 `GOWORK=off go test -tags live ./internal/server ./eval/live -run 'TestEvaluationRecordsContentFreeTerminal|TestEvaluationStopsAfterUnmarkedToolEOF|TestEvaluationStopsWhenTerminalRecordUnavailable|TestFollowupA01KeepsItsOwnOneShotResult|TestFollowupA01UsesNewBoundedAuthorization' -count=1`。保存合法 `[DONE]` 工具调用成功、纯文本缺标记仍按旧行为且诊断清楚、坏工具终态失败三种记录,不能把诊断字段出现当作质量 PASS。
|
||
- [ ] **Step 5: 提交 Backend 诊断单元。** 与 Task 2 合并前确认没有 `messages`、原始终态或工具参数在新的评测文件中,且 A01 标签/路由/模型预算没有变化。推送 Backend 草稿 PR 并核对准确 HEAD CI。
|
||
|
||
### Task 4: 用户链零付费组合重放
|
||
|
||
**Files:** Backend `internal/runmgr/material_notice_failure_test.go`、`internal/server/work_checkpoint_eval_test.go`;Frontend `e2e/work-checkpoints.playwright.ts`。**Interfaces:** 只通过现有 HTTP 消息、run、卡片、事件及 `TaskSnapshot` 观察 Task 2/3;不新增前端状态所有者或 API 字段。
|
||
|
||
- [ ] **Step 1: 写 RED。** 在 `e2e/work-checkpoints.playwright.ts` 的 `Scenario` 联合类型中增加 `fast-ack`,并在任务自有 HTTP 假流夹具加入“附件仅发送”“文字+长材料”“三个槽位已满”“同批重发”“启动失败”“已完成复用”“文字+小材料”场景;浏览器先断言宿主确认在原对话可见、任务卡在右侧同一入口、未出现无依据的 `剩余4项含3项依赖接口`、宿主确认不增加 finding/checkpoint。文字+小材料断言仍有原直连答复;后台失败零发现、部分成果、成功建议选择仍按 r0.4 规则。先让新增用例在旧前台模型路径失败,保存 SELF RED。
|
||
|
||
```ts
|
||
test('attachment-only material intake shows a safe first reply', async ({ page, request }) => {
|
||
const seed = await setup(request, 'fast-ack', false)
|
||
await open(page, seed)
|
||
await expect(page.getByText(/已收到.*份材料.*待确认/)).toBeVisible()
|
||
await expect(page.getByText(/剩余\s*4\s*项.*含\s*3\s*项依赖接口/)).toHaveCount(0)
|
||
const card = (await (await request.get(manifest.base + `/api/conversations/${seed.conv.id}/cards`)).json()).cards.find((c: any) => c.task)
|
||
expect(card.task.checkpointSeq).toBe(0)
|
||
})
|
||
```
|
||
- [ ] **Step 2: 完成受控机制夹具。** Backend 夹具在每个场景使用新的隔离 SQLite、固定三份合成文本与人工 SSE;父回合计数器记录 provider 调用数,子回合按场景给合法 `[DONE]`/阶段工具/失败终态。保留实际 HTTP 的 owner/project 与来源哈希核验;不把 fixture 中的 `reading`/`crosscheck` 当成真实模型遵循顺序的证明。
|
||
|
||
```go
|
||
// 每个 scenario 使用新 dataDir;stream 只连 httptest.Server。
|
||
parentCalls := atomic.Int32{}
|
||
parentStream := func(ctx context.Context, model string, messages []agentllm.Message,
|
||
tools []agentllm.ToolDecl, opts agentllm.StreamOptions) <-chan agentllm.Event {
|
||
parentCalls.Add(1)
|
||
out := make(chan agentllm.Event, 1)
|
||
out <- agentllm.Event{Kind: agentllm.KindMessageEnd,
|
||
Message: agentllm.Message{Role: agentllm.RoleAssistant, Content: "本地夹具答复", StopReason: agentllm.StopEnd},
|
||
Usage: agentllm.Usage{PromptTokens: 2, CompletionTokens: 1, TotalTokens: 3}, StreamComplete: true}
|
||
close(out)
|
||
return out
|
||
}
|
||
// 自动材料入口完成后断言 parentCalls.Load()==0;文字+小材料对照为 1。
|
||
```
|
||
- [ ] **Step 3: 跑 GREEN 与视觉回归。** 启动本任务独有的 mechanism 二进制和数据目录,manifest 固定 PID、可执行文件 SHA、Dev/Backend/Agent-Native/Frontend SHA、模式与地址;先用 manifest 的 `pid` 核查对应 `/proc/` 可执行文件和目录归属,再将 manifest 的 `base` 设为 `PLAYWRIGHT_BASE_URL`、manifest 路径设为 `WORK_CHECKPOINT_HTTP_MANIFEST`,运行 `npx playwright test e2e/work-checkpoints.playwright.ts`。至少覆盖 1440px 桌面与 390px 窄屏:默认只见短确认,任务卡阶段发现、来源、失败和选择卡按现有层级展示,刷新和后台更新不抢文件、输入焦点或阅读位置;禁止新增命令/SSE 展示。只停止任务自有 PID,保留 trace/截图及 SELF 收据。
|
||
- [ ] **Step 4: 隔离 PG 与完整 Backend。** 使用本任务独有的 PostgreSQL/Redis 实例和 DSN(不能连接既有服务),先验证实例 PID、数据根和数据库身份;设置 `AGENT_PG_TEST_DSN`、`AGENT_REDIS_TEST_ADDR` 后运行 `make test-backend-pg`,缺变量不得跳过。另运行 Backend `GOWORK=off go vet ./... && GOWORK=off go test -count=1 ./...`,以及纯文档/Frontend 受影响的正常路径。记录失败与修复版本;PG 和 SQLite 都核对 seq、宿主消息、来源卡和失败 Usage。
|
||
- [ ] **Step 5: 提交实际测试增量。** Backend/Frontend 只提交本单元实际新增的夹具与浏览器测试;若需要改 Frontend/UI 生产组件才能表达正确状态,停止该部分,补设计与计划评审,再读 `DESIGN.md`、固定 `@ui` 令牌 PR 和设计守护测试。真实模型质量继续 `NOT_RUN`。
|
||
|
||
### Task 5: 固定跨仓候选、复审与资源出口
|
||
|
||
**Files:** Agent-Squad `deps/agent-native`、Backend `deps/agent-native`、Dev `deps/agent-native`/`deps/agent-squad`/`modules/backend`/可选 `modules/frontend`、`.task/feature-fast-slow-u2-u3-plan.md`、`PROJECT.md`、Documents 的核验报告。**Interfaces:** 公开 Core/HTTP/TaskSnapshot 不变;准确 gitlink 与运行 manifest 是唯一测试对象。
|
||
|
||
- [ ] **Step 1: 按依赖提交并固定。** Agent-Native 先提交并推送准确 SHA;Agent-Squad 在自己分支固定同一 SHA,完整 Go vet/test 后提交;Backend 固定嵌套 Agent-Native(及现有 Core)并完成 Task 2/3/4 的全量测试后推送;Frontend 如只改 e2e 则提交相应用例。Dev 最后暂存全部新 gitlink,先跑 `make test-pins`,再用 `CMD='make test && make build' make evidence` 保存组合;禁止用 detached 嵌套检出的未提交代码冒充可交付组件。
|
||
- [ ] **Step 2: 记录最小重放集。** 任务卡列明 r0.5 需求/设计/用例/结果矩阵:`[DONE]` 与 EOF、半帧、读取错误、缺 Usage、queue/reuse/failed/completed、附件仅发送、文字+长/小材料、0/部分/两批成果、刷新与来源版本。每条标 `SELF`/`REPLAY`/`GATE`,旧 r0.4 A01 继续 FAIL;新真实模型 A01 为 `NOT_RUN`。文档补齐精确 SHA、原件保存路径和已知限制,不写未经验证的供应商终态原因。
|
||
- [ ] **Step 3: 独立复审与当前 HEAD CI。** 请求审核前、验收前、将来合入前各用 `CMD='make task-sync' make evidence` 纳入最新权威 main;同步若改变代码、依赖、配置或 head,按影响重跑 Task 1–4 与组合。独立审阅者重放终态无内容记录、坏工具零执行、前台零 provider、队列/失败真实性、原文归属、Usage 与脚本权限,整改到无 Critical/Important。逐仓核验 Agent-Native、Agent-Squad、Backend、适用 Frontend 及 Dev 的准确 HEAD CI,Dev 的 `test`、`test-pg`、`svc-realcombo` 全部作业/步骤均须 GATE 成功;源外 RECEIPT 留固定后检查。
|
||
- [ ] **Step 4: 提交阶段 6 人工评审。** 交付零付费候选、实际 HTTP/浏览器和 PG 的 SELF、独立 REPLAY、准确 HEAD GATE、未解决风险与八个草稿 PR 链接。用户仅评审 r0.5 实施候选;即使代码机制 PASS,仍不等于真实模型质量、阶段 7 或合入批准。没有人工阶段 6 结论,不进入资源决策。
|
||
- [ ] **Step 5: 单独资源决策,不自动跑 A01。** 先判断无内容终态诊断是否仍不能证明缺 `[DONE]` 的工具流可安全执行。若不能,记录“工具仍保守失败”,向用户提交独立的、受限供应商终态探针或后续设计修订选择;**不得**直接复用 r0.4 A01 额度、放宽门禁或自动再跑。仅在后续必要设计/计划评审和单独资源提案获批、质量候选固定后,才按实际授权的 run/token/时间/输出/停止边界执行新真实模型对照。真实质量 PASS 后仍须阶段 7 人工验收,随后才按 Dev 策略做最终 Review、准确 HEAD CI、串行 PR 合入、合入核验与安全回收;质量 FAIL 则保持全部 PR 草稿。
|
||
|
||
## 计划自检与停止条件
|
||
|
||
- 本计划的终点是**可审阅的零付费实施候选**和阶段 6 材料;当前用户只授权编写计划,Task 1–5 的代码与测试均 `NOT_RUN`。不得将本计划的绿色文档 CI、未来单测或假流当成真实 A01 通过。
|
||
- Task 1 的观察字段只增加无内容诊断,不更改 `toolFinishExecutable`;Task 2 只接管预启动首回合,保持用户重点与既有消息合同;Task 3 先持久 Usage/故障记录再放终态;Task 4 以真实 HTTP/浏览器检验用户看到的内容;Task 5 只固定实际提交的组件与证据。接口消费和责任边界按此顺序复核。
|
||
- 任何需要接受无 `[DONE]` 的工具请求、增加模型调用、变更公开消息/TaskSnapshot 契约、数据库迁移、扩大权限或改变验收标准的发现,立即停止受影响实现,返回书面设计与计划人工评审;未受影响的零付费调查可继续。任何付费请求必须另有明确资源批准,失败不自动重跑。
|