Files
Agent-System-Documents/docs/proposals/2026-10-13-fast-slow-u3-quality-implementation-plan.md
T

29 KiB
Raw Blame History

快慢通道 U3 质量修订 Implementation Plan r0.2

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking. 本任务沿用已登记的 Dev worktree 和原生逐项实施方式;不再创建同名任务或重复启动 precheck。本文已获阶段 5 人工批准并开始零付费实施,不是新增付费模型授权。

Goal: 让同一任务卡的材料阶段成果忠于已提供原文、清楚标出待核对判断,并在受控预算内尽量形成真实最终答复。

Architecture: Backend 用现有 materialSourceRegistry 把来源编号和实际提供的原文块一次呈现,不改变来源权限或 Core JSON。Backend 在检查点提交前挡住模型生成的肯定批准断言,材料子代理依据已返回 Usage 为最终答复留请求空间;UI 在既有卡片中区分材料记载与待核对判断。旧失败样本、执行偏差和现有三栏仍是验收基线。

Tech Stack: Go、agent-native/runtime 子代理循环、SQLite/PostgreSQL、React/TypeScript、现有 @ui/Vitest/Playwright、Dev make evidence 与 Gitea CI。

Spec: 已批准书面设计 r0.2。原 U2/U3 计划 r0.1 的 U2 契约、执行权及失败恢复不重做。用户已批准本实施计划开始零付费阶段;真实模型样本的资源边界仍待单独批准。

Global Constraints

  • 保留现有三栏、同一任务卡及右栏「工作 / 文件」;不增加通道页、命令详情或新的确认能力。
  • 不改变 Core WorkCheckpointCandidate JSON、Store 表、SSE 事件、owner/project 约束及来源 quote 必须为连续原文的校验。
  • 不新增摘要/审核模型回合;旧样本及其原始 exit=1、57,997 Usage、14,231-token 停止规则偏差不重写。
  • 本轮只做零付费代码与浏览器验证。任何新真实模型样本须另行明确批准 run 数、单 run/总 Usage 目标、在途越额风险和停止条件;未获批准为 NOT_RUN。
  • UI 颜色/圆角/字号只消费 @ui/tokens.css,动效只用 @ui/motion.css,折叠只用 @ui Collapsible;异步状态仍由现有 Frontend store 单一所有者按 seq 处理。
  • 组件先各自提交;Dev gitlink 每次推进后跑 make test && make build。正式验收、请求审核与合入前按 policy/delivery.md 和 AGENTS.md 执行 make task-sync;SELF 原始输出经 make evidence 进入任务卡。新候选的 GATE 只能引用准确 HEAD。

Review Focus

  1. 重复文字/无 Origin/截断末块:模型只得到注册表授权的 sourceId,输入文字仍与实际提供前缀一致;Task 1 的 TestMaterialSourceInlineAnnotationsPreserveProvidedPrefix 和 TestMaterialSourceInlineAnnotationsNeverExposeHiddenBlocks 覆盖。
  2. 否定、疑问与肯定审批表达:“审批尚未完成”“是否已批准”“防止把50万写成已批准”不能被误拒;“预算是已批准决定”必须整批拒绝;Task 2 的表驱动测试覆盖。
  3. 默认摘要把 analysis 当事实:既有材料记载可见,待核对判断带限定且总默认项数不超过三;Task 3 的 UI 组件测试覆盖。
  4. 两个检查点后仍无限继续/只有一个检查点时余额不足:优先真实最终答复或诚实部分结果,不多发 provider 请求;Task 4 的假 provider 用量序列覆盖。
  5. 正常通知、来源导航与停止边界:测试通知不代表生产通知质量;Task 5 的实际 HTTP/browser 用例及 Task 6 的独立真实样本出口分别覆盖,任何工具拒绝后不继续付费。

Task 1:一次呈现原文和可引用来源(Backend)

Files: Modify modules/backend/internal/runmgr/material_sources.go、material.go、material_sources_test.go、material_test.go;update the live-tag, zero-paid mechanism fixture in modules/backend/internal/server/work_checkpoint_eval_test.go and its regression in work_checkpoint_eval_budget_test.go;不改 modules/core/task_snapshot.go。

Interfaces: materialSources(docs, perDoc) 继续产出唯一授权注册表。给内部 materialSource 增加 Start, End int(相对于 strings.TrimSpace(d.PlainText()) 的 byte offset),新增 annotateProvidedMaterial(d core.ParsedDocument, provided string, sources materialSourceRegistry) string。buildMaterialTask 仅渲染各文档提供的前缀与紧邻的 sourceId/blockIndex,删除末尾重复索引;validate 原语义不动。测试专用 mechanismCheckpointSources(text string) [][]string 仍返回正则组 [full,id,blockIndex,providedLine],只适配新内联格式。

  • Step 1: 在既有测试写 RED。 material_sources_test.go 用含重复段落、截断段落、缺 Origin 和表格的文档,断言只对注册表里的块插入 sourceId=... blockIndex=...;移除这些标记后,正文与当前 strings.TrimSpace(d.PlainText()) 的实际提供前缀逐字节相同。material_test.go 断言同一个短事实只出现一次、每个可引用标记紧邻其实际块、超长提示仍出现。

    func TestMaterialSourceInlineAnnotationsPreserveProvidedPrefix(t *testing.T) {
        d := materialDocs()[0]
        d.Meta.Origin = &core.DocumentOrigin{ArtifactID: "art1", ContentHash: strings.Repeat("a", 64), ParserRevision: "docx1"}
        docs := []core.ParsedDocument{d}
        provided := strings.TrimSpace(d.PlainText())
        got := annotateProvidedMaterial(d, provided, materialSources(docs, 48000))
        markers := regexp.MustCompile(`(?m)^sourceId=src_[a-f0-9]+ blockIndex=[0-9]+\n`)
        if !markers.MatchString(got) { t.Fatal("provided block has no source marker") }
        if markers.ReplaceAllString(got, "") != provided {
            t.Fatal("source annotations changed the provided material bytes")
        }
    }
    

    在 live-tag 夹具新增 TestMechanismSourceExtractionAfterSourceOnce:给它 sourceId=src_... blockIndex=0\n上线六月。验收条件需要补齐,断言读取到真实 ID、索引和连续原句。当前夹具只接受尾部“引用定位片段:”行,故此测试须先 RED;它不发真实模型请求。

    func TestMechanismSourceExtractionAfterSourceOnce(t *testing.T) {
        id := "src_" + strings.Repeat("a", 24)
        got := mechanismCheckpointSources("sourceId=" + id + " blockIndex=0\n上线六月。验收条件需要补齐")
        if len(got) != 1 || got[0][1] != id || got[0][2] != "0" || !strings.Contains(got[0][3], "上线六月") {
            t.Fatalf("inline source unavailable to mechanism fixture: %v", got)
        }
    }
    
  • Step 2: 运行 RED。 在 Backend 分别运行 go test ./internal/runmgr -run 'TestMaterialSourceInlineAnnotations|TestBuildMaterialTaskInlineBudget' -count=1 和 go test -tags live ./internal/server -run '^TestMechanismSourceExtractionAfterSourceOnce$' -count=1;期望新增的“一次呈现/标记权限”和夹具内联读取断言各自失败,而旧来源 CAS 测试仍可运行。

  • Step 3: 实现最小改动。 在 materialSources 记录已提供块的 byte 起止;按 Start 排序,在原 provided 字符串里只插入来源标记,保持所有非标记字节、顺序和截断位置不变;缺 Origin 不赋 ID。buildMaterialTask 只构造一次注册表,不再调用末尾的 prompt();materialRevision 的 material-recipe.v7 更新为 v8,旧完成结果不被新输入布局复用。同步更新 evalMechanismStream 的测试专用 source 提取器,使它读取新内联标记后面的已提供原句;它仍只提交来自可信 ID 和字面引用的假 provider 检查点,不替代真实模型验收。

    sources := materialSources(docs, perDoc)
    // 原有每份文档的 cap/left 计算保持不变。
    take := cap
    if runes < take { take = runes }
    provided := string([]rune(text)[:take])
    b.WriteString(annotateProvidedMaterial(d, provided, sources))
    
  • Step 4: 验证 GREEN 与权限。 重跑 Step 2,加 go test ./internal/runmgr -run 'TestMaterialSourceQuoteRequiresActualProvidedBlock|TestMaterialSourceDuplicateTextDoesNotAuthorizeUnprovidedBlock|TestMaterialInputRevisionIncludesProcessingVersion' -count=1,以及 go test -tags live ./internal/server -run '^TestMechanismSourceExtractionAfterSourceOnce$' -count=1。用旧 A01 材料在离线诊断中记录新旧输入字符数、sourceId 集合与 hash;只声明实际测得的长度,不推断新模型 token 使用量。

  • Step 5: 自审并提交 Backend 独立提交。 检查 git diff --check、新增 ID 与注册表一一对应、没有未提供文本或权限放宽;按本仓 Why/Evidence/Impact/Refs 提交。此项可被独立退回而不影响 Task 2 的审批防误判实现。

Task 2:检查点审批断言守卫(Backend)

Files: Create modules/backend/internal/runmgr/checkpoint_claim_guard.go 与 checkpoint_claim_guard_test.go;modify task_checkpoint.go、task_checkpoint_test.go、internal/runtime/work_checkpoint.go 的工具说明。生产拒绝只作用于系统材料整理的受控检查点,不修改一般对话正文。

Interfaces: validateMaterialApprovalClaims(candidate core.WorkCheckpointCandidate) error 在 PublishWorkCheckpoint 的 Core 基础校验后、任何持久写入前调用。检查 analysis/provisional 的 Text/Impact/Assumptions 及默认可见的 Focus/Remaining/NextStep。material_record 继续由宿主重写成原文;needs_user 可询问审批状态。肯定审批表达至少覆盖“已批准/已获批/审批通过/批复同意/已通过审批”,否定与待核对表达单列测试;返回稳定的 checkpoint.claim: approval needs explicit source record 错误,不把它包装成已通过校验。

  • Step 1: 写 RED。 在 task_checkpoint_test.go 复用 checkpointEnvironment 的合法“上线六月”quote,却让 analysis 无依据声称“预算是已批准决定”,断言 PublishWorkCheckpoint 返回 checkpoint.claim 且卡 revision/发现数/事件数均不变。表驱动测试覆盖 Text/Impact/Assumptions/Focus/Remaining/NextStep,并包含“审批尚未完成”“是否已批准”“防止把50万写成已批准”、确有批准原文的 material_record 与 needs_user 问句,断言不误拒。

    func TestTaskCheckpointRejectsUnsupportedApproval(t *testing.T) {
        c := checkpointEnvironment(t)
        cp := validCheckpoint(c, "unsupported-approval")
        cp.Findings[0].Kind = "analysis"
        cp.Findings[0].Text = "需求规定50万元,是已批准决定"
        cp.Findings[0].Assumptions = "把需求条目作为项目基线"
        if err := c.PublishWorkCheckpoint(cp); err == nil || !strings.Contains(err.Error(), "checkpoint.claim") {
            t.Fatalf("unsupported approval was published: %v", err)
        }
    }
    
  • Step 2: 运行 RED。 cd modules/backend && go test ./internal/runmgr -run 'TestTaskCheckpointRejectsUnsupportedApproval|TestMaterialApprovalClaims' -count=1;期望错误分析目前被接受,修复后转为拒绝。

  • Step 3: 实现有限守卫。 新文件按中文句子边界检查明确的肯定审批短语,排除同句的未批准/疑问/防止误写语境;仅以此处理可确定的表达,不宣称通用语义正确。PublishWorkCheckpoint 先运行守卫,再走原有来源、owner、版本、CAS 与事务路径;工具说明要求批准事实只作为明确原文 material_record,推断为待核对。

    if err := core.ValidateWorkCheckpoint(candidate); err != nil { return err }
    if err := validateMaterialApprovalClaims(candidate); err != nil { return err }
    // 原有来源、权限、版本及 CommitTaskSources 路径继续执行。
    
  • Step 4: 跑 GREEN 与邻接回归。 Step 2 加 go test ./internal/runmgr -run '^TestTaskCheckpoint' -count=1 和 go test ./internal/runtime -run '^TestWorkCheckpoint' -count=1。若短语误拒或遗漏已批准变体,补夹具后只调整这个守卫;不借更宽泛的模型分数降低原来源门禁。

  • Step 5: 自审并提交 Backend 独立提交。 复核分析仍保留来源和 assumptions、材料记载的原文不被删除、被拒批次无半落盘;git diff --check 后按四段提交。

Task 3:任务卡把原文与判断分层(UI)

Files: Modify modules/ui/src/ProgressCardBody.tsx、src/activitySummary.ts、test/progressive-cards.test.tsx;仅在现有卡内分组,不修改 Frontend store 或新增 UI token。

Interfaces: TaskProgressBody 保持同一个 ActivityCard.Task。默认最多三条:优先展示至多两条 material_record,其余位置可放带“待核对的判断”标识的 analysis/provisional;needs_user 与更正继续即时可见。activitySummary 给模型判断加限定,不得把 analysis 的原始句子作为无保留的摘要事实;完整依据仍在现有 Collapsible。

  • Step 1: 写 RED。 在 test/progressive-cards.test.tsx 构造两条材料记载、一条 analysis “预算已批准”、一条 needs_user 和第四条普通记录,断言默认三个以内且 analysis 所在区域有“待核对的判断”标签;activitySummary 的摘要不能出现无前缀的“预算已批准”;点击现有“查看依据/全部发现”仍能读原 quote 与历史更正。另用仅 analysis 的卡断言说明“已有待核对判断”,不冒称“已核对”。

    // 在 test/progressive-cards.test.tsx 增加 activitySummary 与 TaskFinding import。
    const subject = card()
    const source = { id: 's', artifactId: 'art', contentHash: 'h', parserRevision: 'txt', blockIndex: 0, label: '需求.txt · 段落1', quote: '预算50万尚未审批', availability: 'available' }
    const finding = (id: string, kind: string, text: string): TaskFinding =>
      ({ id, kind, text, status: 'active', revision: 1, sources: [source] })
    subject.task = { schemaVersion: 1, taskId: 't', attemptId: 'a', cardId: subject.id,
      projectId: subject.projectId, conversationId: subject.conversationId!, ownerInstanceId: 'i',
      revision: 3, sourceSeq: 3, streamScope: 'conversation:' + subject.conversationId,
      title: subject.title, executionState: 'running', outcomeCompleteness: 'partial',
      deliveryState: 'available', phase: { id: 'crosscheck', label: '核对材料' },
      findings: [finding('r1', 'material_record', '材料记载1'), finding('r2', 'material_record', '材料记载2'),
        finding('a', 'analysis', '预算已批准'), finding('q', 'needs_user', '请确认预算'),
        finding('r3', 'material_record', '材料记载3')] }
    render(<ActivityCardView card={subject} />)
    expect(screen.getByText('待核对的判断')).toBeTruthy()
    expect(screen.getByText('预算已批准').closest('.acard-findings')?.textContent).toContain('待核对')
    expect(activitySummary(subject).findings).not.toContain('预算已批准')
    
  • Step 2: 运行 RED。 cd modules/ui && npm test -- test/progressive-cards.test.tsx;预期当前同组“当前发现”会把 analysis 当普通项。

  • Step 3: 实现最小 UI。 沿用 @ui Collapsible 和现有 acard-* 类;按 kind 投影到材料记载/待核对判断/需要留意,默认总数上限三,剩余到“查看全部发现与更正记录”;更新摘要选择,不改变卡片状态或来源导航。若需 CSS,只消费现有 @ui/tokens.css、--fs-ui-* 和 mk-*。

    const records = ordinary.filter(f => f.kind === 'material_record')
    const judgments = ordinary.filter(f => f.kind === 'analysis' || f.kind === 'provisional')
    const visibleRecords = records.slice(0, 2)
    const visibleJudgments = judgments.slice(0, 3 - visibleRecords.length)
    // needs_user/更正仍走既有关键项路径;未展示项保留在原 Collapsible。
    
  • Step 4: 跑 GREEN 与设计守护。 cd modules/ui && npm test -- test/progressive-cards.test.tsx test/design-guard.test.ts test/contrast-guard.test.ts && npm run typecheck;检查浅/深色和窄屏文本层级,没有新裸色、私有 keyframes 或 <details>。

  • Step 5: 提交 UI 独立提交。 自审普通用户能一眼区分原文与模型判断,git diff --check 后按四段提交;它独立于 Task 4 的预算实现。

Task 4:材料子代理的最终答复预留(Backend)

Files: Create modules/backend/internal/runtime/material_final_reserve.go、material_final_reserve_test.go;modify sub_loop.go、usage_n3_test.go;replay the existing run_budget_stream_test.go unchanged。一般子代理、前台、生产模型路由和 budgetedStream 的已返回 Usage 守卫不扩大。

Interfaces: type materialBudgetState struct { Limit, Used, LastPrompt, OutputCap, SavedCheckpoints, Responses int; UsageKnown bool } 与 materialFinalDecision(s materialBudgetState) (finalOnly, stop bool) 只处理 agent.sub.material。Responses=0 是尚未产生调用的初态;已有响应但 Usage 未知则 fail-closed。增长余量 margin=max(1024, LastPrompt/4);最终预留 reserve=LastPrompt+OutputCap+margin,再做一次检查点的估算成本也取同一值。Limit-Used <= LastPrompt+margin 时停止;否则 SavedCheckpoints>=2 或 Limit-Used <= 2*reserve 时进入 final-only。首个响应前不套用该估算。这样保留一次最终请求的空间,且一次成果后发现下一次发布会挤占预留时优先收尾,明确这是部分结论而非第二项。final-only 不再向 provider 声明 publish_work_checkpoint,并添加系统拥有的简短最终答复指令;若剩余额度不容纳预计最终 prompt,则不发新请求,保留已保存卡和真实失败状态。它不保证在途请求不越过总目标。

  • Step 1: 写 RED。 material_final_reserve_test.go 用假 stream 顺序返回用量,断言“初始零响应允许首个请求”“两个合法检查点→下一次仅最终答复”“只有一个检查点且额度临界→真实部分最终答复”“额度不足→0 次额外 provider 请求”“已有响应而全部 Usage 缺失→停止”“非材料子代理保持原行为”。usage_n3_test.go 补一条 TotalTokens=0 但 prompt/completion 有值时按两者之和入账的回归,与现有 budgetedStream 口径一致。故意让假 provider 在 final-only 中仍请求隐藏的检查点,断言 runTool 拒绝、设置本次执行的停止标记,下一轮在 provider 前停止且没有持久发布。测试检查 provider 请求里的工具声明和系统提醒,而非只检查内部布尔值。

    func TestMaterialFinalReserveAfterTwoCheckpoints(t *testing.T) {
        finalOnly, stop := materialFinalDecision(materialBudgetState{Limit: 30000, Used: 16000, LastPrompt: 5000, OutputCap: 4000, SavedCheckpoints: 2, Responses: 2, UsageKnown: true})
        if !finalOnly || stop { t.Fatalf("want one final-only request: final=%v stop=%v", finalOnly, stop) }
        finalOnly, stop = materialFinalDecision(materialBudgetState{Limit: 30000, Used: 27000, LastPrompt: 8000, OutputCap: 4000, SavedCheckpoints: 1, Responses: 2, UsageKnown: true})
        if !stop { t.Fatalf("insufficient final prompt budget started another request: final=%v", finalOnly) }
    }
    
  • Step 2: 运行 RED。 cd modules/backend && go test ./internal/runtime -run 'TestMaterialFinalReserve|TestRunBudgetStreamStopsReportedTokenExhaustion' -count=1;期望当前两检查点之后仍继续暴露发布工具或临界余额耗尽。

  • Step 3: 实现调度。 subRun.runTool 仅在检查点真实成功时增加 savedCheckpoints,在 final-only 阶段拒绝模型凭旧记忆仍提交的检查点并设置停止标记;wrapped 下一次调用先检查此标记,直接返回错误而不接触 provider;onEvent(message_end) 用返回 Usage 更新 Used/LastPrompt/Responses/UsageKnown。TotalTokens=0 时仅在 prompt+completion 大于零才采用其和;三项均为零才标 Usage 未知并阻断后续请求。该回退也修复子 run 的 AddUsage 记账,不修改前台或 budgetedStream。在 SubLLMRunner.Execute 的 wrapped 调用前计算决策;final-only 只改当前请求的工具声明和系统提示,不写回历史、不提高 MaxTokens 或单次输出上限。预算不能证明可用时保留原失败/部分投影,不伪造正文。

    if st.finalOnlyRejected {
        out := make(chan agentllm.Event, 1)
        out <- agentllm.Event{Kind: agentllm.KindError, Err: fmt.Errorf("budget_exceeded: checkpoint requested after final-only transition")}
        close(out)
        return out
    }
    limit := *rc.Profile().Budget.MaxTokens // 仅在 MaxTokens 非 nil 且 >0 的材料分支调用
    outputCap := outputTokenCap(rc.Profile().Budget, rc.Settings(), "models.chat.maxOutputTokens")
    finalOnly, stop := materialFinalDecision(materialBudgetState{Limit: limit,
        Used: st.usageTotal.TotalTokens, LastPrompt: st.usageTotal.LastPromptTokens,
        OutputCap: outputCap, SavedCheckpoints: st.savedCheckpoints,
        Responses: st.responses, UsageKnown: st.usageKnown})
    if stop {
        out := make(chan agentllm.Event, 1)
        out <- agentllm.Event{Kind: agentllm.KindError, Err: fmt.Errorf("budget_exceeded: final-answer reserve unavailable")}
        close(out)
        return out
    }
    requestDecls := append([]agentllm.ToolDecl(nil), decls...)
    requestMessages := append([]agentllm.Message(nil), messages...)
    if finalOnly {
        filtered := make([]agentllm.ToolDecl, 0, len(requestDecls))
        for _, d := range requestDecls { if d.Name != workCheckpointTool { filtered = append(filtered, d) } }
        requestDecls = filtered
        requestMessages = append([]agentllm.Message{{Role: agentllm.RoleSystem, Content: "阶段成果已保存;现在用现有材料写出真实最终结论与未决项,不再发布检查点。"}}, requestMessages...)
    }
    in := stream(ctx, model, requestMessages, requestDecls, opts)
    
  • Step 4: 跑 GREEN 与现有子代理回归。 Step 2 加 go test ./internal/runtime -run 'TestWorkCheckpoint|TestSub|TestRunBudget' -count=1。用 A01 离线 Usage 序列(6,778/10,134/11,343 prompt,整体 35,632)验证决策会在原子预算检查之前转入 final-only 或诚实停止;不能把这个离线判断当新版真实模型通过。

  • Step 5: 自审并提交 Backend 独立提交。 检查无隐藏的附加模型回合、无流式并发数据竞争、无“两个检查点”计数伪造;按四段提交。

Task 5:固定组件组合、零付费回归与独立 Review(Dev)

Files: Modify Dev modules/backend、modules/ui、modules/documents gitlinks、.task/feature-fast-slow-u2-u3-plan.md and Documents 核验报告;Core/Frontend 不变。若测试证明公开契约或 Frontend store 必须改变,先返回方案评审再扩围。

  • Step 1: 确认每个组件工作树干净、提交 SHA 与 PR HEAD 一致。 Backend 完整 go test ./... -count=1,UI npm test && npm run typecheck,Frontend 受影响的现有 store/阅读测试重放;每项保留真实命令、SHA 和输出。

  • Step 2: 在已登记 Dev 任务 worktree 固定 gitlink 和任务卡。 先提交组件,再推进 Dev 指针;不要把旧样本的 SELF 或旧 HEAD CI 当新候选证据。

  • Step 3: 正式组合验收。 在干净的 Dev HEAD 以 RECEIPT=/tmp/fast-slow-u2-u3-r02-sync-20261013.json CMD='make task-sync' make evidence 同步最新主干,导入工具收据;随后以 RECEIPT=/tmp/fast-slow-u2-u3-r02-combination-20261013.json CMD='make test && make build' make evidence 捕获完整 SELF 输出并导入任务卡。执行前检查同名收据不存在;若已占用则用新的任务专用时间后缀,不覆盖。对数据库、来源 CAS 和预算路径执行直接相关 SQLite/真实 PG 测试。

    test ! -e /tmp/fast-slow-u2-u3-r02-sync-20261013.json
    RECEIPT=/tmp/fast-slow-u2-u3-r02-sync-20261013.json CMD='make task-sync' make evidence
    IMPORT_RECEIPT=/tmp/fast-slow-u2-u3-r02-sync-20261013.json make evidence
    # 先提交任务卡中的同步收据,确保组合测试从干净 HEAD 开始。
    test ! -e /tmp/fast-slow-u2-u3-r02-combination-20261013.json
    RECEIPT=/tmp/fast-slow-u2-u3-r02-combination-20261013.json CMD='make test && make build' make evidence
    IMPORT_RECEIPT=/tmp/fast-slow-u2-u3-r02-combination-20261013.json make evidence
    
  • Step 4: 实际浏览器验收。 用任务自有、带 PID/二进制 SHA/数据目录 manifest 的 mechanism 实例运行 e2e/work-checkpoints.playwright.ts;该 manifest 必须由本轮启动过程写入并在执行前验证,不复用旧文件。证明来源跳转、后台不抢阅读、刷新/旧版本、材料记载标签与浅色默认阅读;analysis 的限定标签由 Task 3 组件测试证明。不得使用旧端口或旧服务作为新版本证据。

    mkdir -p /tmp/fast-slow-u2-u3-r02
    test ! -e /tmp/fast-slow-u2-u3-r02/mechanism-data
    test ! -e /tmp/fast-slow-u2-u3-r02/mechanism-manifest.json
    test ! -e /tmp/fast-slow-u2-u3-r02/server-eval.test
    cd modules/backend
    go test -tags live -c ./internal/server -o /tmp/fast-slow-u2-u3-r02/server-eval.test
    # 在保持打开的任务终端中执行前台服务,不用 nohup 后台启动。
    backend_sha="$(git rev-parse HEAD)"
    dev_sha="$(git -C ../.. rev-parse HEAD)"
    WORK_CHECKPOINT_SERVER_MODE=mechanism WORK_CHECKPOINT_SERVER_DATA=/tmp/fast-slow-u2-u3-r02/mechanism-data WORK_CHECKPOINT_SERVER_MANIFEST=/tmp/fast-slow-u2-u3-r02/mechanism-manifest.json WORK_CHECKPOINT_BACKEND_SHA="$backend_sha" WORK_CHECKPOINT_DEV_SHA="$dev_sha" /tmp/fast-slow-u2-u3-r02/server-eval.test -test.run='^TestServeWorkCheckpointEval$' -test.v
    

    在任务终端 B 从 Dev 根目录验证 manifest,启动预览。代理目标必须从本轮 manifest 读取,不能沿用旧端口;4179 若已占用则停止并选择另一个本任务专用空闲端口,不能接管既有预览:

    cd modules/frontend
    npm run build
    python3 -c 'import hashlib,json,os; m=json.load(open("/tmp/fast-slow-u2-u3-r02/mechanism-manifest.json")); assert m["mode"]=="mechanism" and m["dataDir"]=="/tmp/fast-slow-u2-u3-r02/mechanism-data" and m["base"].startswith("http://127.0.0.1:") and os.path.realpath("/proc/%s/exe" % m["pid"])==m["binary"] and hashlib.sha256(open(m["binary"],"rb").read()).hexdigest()==m["binarySHA256"]'
    api_target="$(python3 -c 'import json; print(json.load(open("/tmp/fast-slow-u2-u3-r02/mechanism-manifest.json"))["base"])')"
    PLAYWRIGHT_API_TARGET="$api_target" PLAYWRIGHT_PREVIEW_PORT=4179 npm run preview -- --host 127.0.0.1 --strictPort
    

    在同时保持打开的任务终端 C,从 Frontend 目录执行浏览器用例;结束后只停止这两个任务拥有的前台进程并保存截图/追踪:

    cd modules/frontend
    PLAYWRIGHT_BASE_URL=http://127.0.0.1:4179 WORK_CHECKPOINT_HTTP_MANIFEST=/tmp/fast-slow-u2-u3-r02/mechanism-manifest.json npm run e2e -- e2e/work-checkpoints.playwright.ts
    
  • Step 5: 独立代码审阅与草稿 PR。 审阅准确的 Backend/UI/Docs/Dev SHA,重放来源映射、审批误判、预算、停止边界及浏览器最小集;修正发现后只重跑受影响的检查。推送草稿 PR 并核验当前 HEAD 的 Gitea CI 运行身份、SHA、每个必需 job/step;质量出口仍 NOT_RUN 时不合入。

Task 6:真实模型质量出口与交付决策(单独资源授权)

Files: Backend eval/live/work_checkpoints_test.go 仅在固定候选所需的受控入口/读数缺口确有证据时修改;Dev 任务卡、Documents 核验报告写入结果。旧原始样本不覆盖、不补写。

  • Step 1: 先准备可审查资源提案。 固定 Backend/Dev/Frontend/UI/Docs 精确 SHA、模型路由来源及受保护配置引用、A01 同一合成材料、Run/单 run/returned Usage 目标、输出上限、300 秒、缺 Usage、任何检查点拒绝、越额的停止规则与成本边界。明确在途调用可能越过目标;不给出虚假的账单硬上限。
  • Step 2: 等待用户另行明确批准该一次性付费范围。 未获批准时只记录 NOT_RUN,不以本计划或“继续”推定新样本授权;保持六个 PR 草稿。
  • Step 3: 只在批准范围内运行一次。 启动新的隔离数据目录与一份独立结果文件,启动前验证 PID/二进制哈希、route 文件权限及零既有 run;运行后逐调用对账真实付费 run ID、Usage、检查点持久事件、来源字面与语义、用户可读窗口和最终正文。拒绝工具候选后全实例停止,不自动重试或补写。
  • Step 4: 独立审阅与门禁。 若两个阶段、逐项语义、最终结果或停止规则任一 FAIL,就保留证据与草稿 PR,不合入或部署。若全部 PASS,依 policy/delivery.md 再同步 base/head、复核独立 Review 与当前 HEAD CI,然后由智能体合入、核验实际结果并按任务卡规则安全收口;部署仍不在本计划授权内。