import type { ChecklistOf } from "../vocab/index.ts"; import { Draft, factorLines, KIND_TEXT, offText, registeredMetrics, taskPoints } from "./blocks.ts"; import { fenced, json } from "./format.ts"; import { unassignedChanges } from "./locations.ts"; import { clauseKey, factorKey, splitCell, type FrozenProtocol, type LocationTable } from "./shapes.ts"; // 实验员、核验员的 5 种任务(方法.md 4.3 ⑨ ⑯):只看分给他的那个候选或那一格——冻结协议、起点代码、 // 要实现或核验的东西。不给信念本(用户 2026-10-03 决定 3)、不给任何运行成绩、不给别的候选。 // 排法照参考项目 assignedBlocks、ablationBlocks:任务与本轮 → 代码 → 本任务要处理的东西(核验把没有归属的改动放最前)。 type Items = { candidate: string; clauses: string[]; factors: string[]; safeguards: string[] }; /** 每个函数最先调用:一级分组"分给你的材料",冻结协议进查询,交回协议和对号清单的 FrozenItems。 */ function frozen(d: Draft, candidate: string): { p: FrozenProtocol; items: Items } { d.group("分给你的材料", "下面是分给你的这一个对象的全部材料。"); const p = d.board.protocol(candidate); d.record(candidate, `候选 ${candidate} 的冻结协议`, p); return { p, items: { candidate, clauses: p.clauses.map((_, i) => clauseKey(i)), factors: p.factors.map((_, i) => factorKey(i)), safeguards: p.safeguards } }; } function taskSection(d: Draft, role: string, what: string): void { d.section("任务与本轮", [ `你是${role} ${d.req.actor},现在是第 ${d.round} 轮(阶段编号 ${d.req.stageId})。本次任务:${KIND_TEXT[d.req.kind]},${what}。`, "", ...taskPoints(d), ]); } /** 协议要采集的指标:新登记的全文,引用的取原登记(方法.md 第五部分"引用别人登记过的指标时,沿用原登记的选法")。 */ function metricSection(d: Draft, p: FrozenProtocol): void { const registered = registeredMetrics(d.board); const all = [...p.metrics.defined, ...p.metrics.reused.map((id) => registered.find((x) => x.id === id)!)]; d.section("要实现的指标定义", [ all.length === 0 ? "(这份协议没有要实现的假设专用指标)" : json(all), "", "每个指标都由候选代码在正式运行时算出、写进结果文件;测不到的写 null,不写 0。", ]); } function locationTableSection(d: Draft, p: FrozenProtocol): void { d.section("要交的因子位置表", [ "每个因子、每条保障各在哪几行;同一行既属于因子又属于保障时两边都写;不属于任何因子或保障的改动写进 unassigned 并说一句为什么。", "", ...factorLines(p), `保障:${p.safeguards.length === 0 ? "没有(保障位置表交空数组)" : p.safeguards.join("、")}`, ]); } function codeLines(d: Draft): string[] { return [...d.readableLines(), "读这些目录用 read_source(核验员)或 shell(实验员)。"]; } /** ⑨ 实现:冻结协议、指标定义、要交的位置表、起点代码(本批基线)。 */ export function implementation(d: Draft): ChecklistOf<"implementation"> { const candidate = d.req.object!; const { p, items } = frozen(d, candidate); taskSection(d, "实验员", `候选 ${candidate}`); d.read(p.baseline, "起点代码(本批基线)"); d.section("基线代码", [...codeLines(d), "你的工作目录是起点代码的副本,改动都落在工作目录里。"]); d.section("本任务要处理的东西", [`### 候选 ${candidate} 的冻结协议`, "", json(p)]); metricSection(d, p); locationTableSection(d, p); return { kind: "implementation", ...items }; } /** ⑨ 修复:实现的全部材料,加退回意见(最近一次核验里没落实、判越界的几项)、上一次的位置表、上一次提交相对起点的改动。 */ export function repair(d: Draft): ChecklistOf<"repair"> { const candidate = d.req.object!; const { p, items } = frozen(d, candidate); taskSection(d, "实验员", `候选 ${candidate}`); d.read(p.baseline, "起点代码(本批基线)"); d.read(candidate, "你上一次提交的代码"); d.section("基线代码", [...codeLines(d), "你的工作目录是上一次提交的代码的副本。"]); const v = d.board.published("verification").filter((s) => s.target === candidate).at(-1)!.content; d.section("本任务要处理的东西", [ "### 退回意见", "", ...v.clauses.filter((x) => !x.found).map((x) => `- 条款 ${x.clause} 没落实:${x.where}`), ...v.factors.filter((x) => !x.found).map((x) => `- 因子 ${x.factor} 没落实:${x.where}`), ...v.safeguards.filter((x) => !x.found).map((x) => `- 保障 ${x.safeguard} 没落实:${x.where}`), ...v.unassigned.filter((x) => x.outOfScope).map((x) => `- ${x.location} 判为越界:${x.reason}`), "", `核验员的说明:${v.body}`, "", "### 上一次交的位置表", "", json(d.board.code(candidate).locations), "", "### 上一次提交相对起点代码的改动", "", fenced(d.req.changes!.diff, "diff"), "", `### 候选 ${candidate} 的冻结协议`, "", json(p), ]); metricSection(d, p); locationTableSection(d, p); return { kind: "repair", ...items }; } /** * ⑨ 核验:没归到任何因子或保障的改动排第一(程序按位置表现算);冻结协议、位置表、指标定义、改动全文。 * 因子与保障同一行的地方只作提醒。能走到核验的实现已经过了 Jev,Jev 的判断不给核验员看(Jev 是判别条件,不是提示)。 */ export function verification(d: Draft): ChecklistOf<"verification"> { const candidate = d.req.object!; const { p, items } = frozen(d, candidate); const table = d.board.code(candidate).locations as LocationTable; const found = unassignedChanges(d.req.changes!, table); d.section("没归到任何因子或保障的改动", [ "程序按实验员交的位置表,把一行都没落进任何因子或保障的改动列在下面,逐条判断是否越界(location 照这里抄):", ...(found.unassigned.length === 0 ? ["(没有)"] : found.unassigned.map((u) => `- ${u.location}(${u.note})`)), "", "实验员自己对不属于任何因子的改动写的说明(只是说明,不算归属):", ...(table.unassigned.length === 0 ? ["(没有)"] : table.unassigned.map((u) => `- ${u.location}:${u.note}`)), "", "同一行既属于因子又属于保障的地方(只是提醒,不判越界;在说明里写一句):", ...(found.overlaps.length === 0 ? ["(没有)"] : found.overlaps.map((o) => `- ${o.location}:因子 ${o.factor}、保障 ${o.safeguard}`)), ]); taskSection(d, "核验员", `候选 ${candidate}`); d.read(p.baseline, "起点代码(本批基线)"); d.read(candidate, "要核验的候选代码"); d.section("基线代码", codeLines(d)); const impl = d.board.implementation(candidate)!; d.section("本任务要处理的东西", [ `### 候选 ${candidate} 的冻结协议`, "", json(p), "", "### 实验员交的位置表", "", json(table), "", "### 实验员的实现说明", "", impl.content.body, "", "### 改动全文(候选相对起点代码,每段前后各 3 行上下文)", "", fenced(d.req.changes!.diff, "diff"), ]); metricSection(d, p); return { kind: "verification", unassigned: found.unassigned.map((u) => u.location), ...items }; } /** * 消融两种任务共用(方法.md 4.3 ⑯):任务与本轮、完整代码与本批基线、这一格(关哪个因子、换成什么)。 * 交回本任务要处理的东西的公共部分:因子清单、完整代码的位置表、冻结协议。 */ function ablation(d: Draft, role: string, codeNote: string[]): { cell: string; key: string; factors: string[]; lines: string[] } { const cell = d.req.object!; const { candidate, key } = splitCell(cell); const { p, items } = frozen(d, candidate); taskSection(d, role, `消融格 ${cell}(候选 ${candidate} 关掉 ${key})`); d.read(candidate, "完整代码"); d.read(p.baseline, "本批基线(因子没写关掉时换成什么,就回到它在这件事上的做法)"); if (d.req.kind === "ablationVerification") d.read(cell, "要核验的这一格的代码"); d.section("完整代码(消融的起点)", [...codeLines(d), ...codeNote]); const f = p.factors[items.factors.indexOf(key)]!; d.section("这一格", [ `格 ${cell}:关掉 ${key} ${f.name}。关掉时换成:${offText(f)}。`, `其余因子、配套设定、全部保障(${p.safeguards.length === 0 ? "没有保障" : p.safeguards.join("、")})都照完整代码保留。`, ]); const lines = ["### 因子清单", "", ...factorLines(p), "", "### 完整代码那次实现交的位置表", "", json(d.board.code(candidate).locations), "", "### 冻结协议", "", json(p)]; return { cell, key, factors: items.factors, lines }; } /** * ⑯ 消融实现:从完整代码出发只关指定的因子。核验不通过重做时(这一格已有公开的消融核验)另放退回意见: * 最近一次核验里关错了的因子和核验员写的依据,写法同修复的退回意见。 */ export function ablationImplementation(d: Draft): ChecklistOf<"ablationImplementation"> { const a = ablation(d, "实验员", ["你的工作目录是完整代码的副本,改动都落在工作目录里。"]); const verified = d.board.published("ablationVerification").filter((s) => s.target === a.cell).at(-1); const opinion: string[] = []; if (verified !== undefined) { const off = new Set([a.key, ...lastAblation(d, a.cell).alsoOff]); const wrong = verified.content.factors.filter((x) => x.off !== off.has(x.factor)); const text = (x: (typeof wrong)[number]) => `- 因子 ${x.factor} 应当${off.has(x.factor) ? "关着" : "照留"},核验员看到的是${x.off ? "关着" : "照留"}:${x.where}`; opinion.push("### 退回意见", "", "上一次交的这一格没通过消融核验,下面是关错了的因子:", ...wrong.map(text), ""); } d.section("本任务要处理的东西", [...opinion, ...a.lines]); return { kind: "ablationImplementation", cell: a.cell, off: a.key, factors: a.factors }; } /** 这一格最近一次公开的消融实现提交。 */ const lastAblation = (d: Draft, cell: string) => d.board.published("ablationImplementation").filter((s) => s.target === cell).at(-1)!.content; /** ⑯ 消融核验:只查是不是只关了指定的因子(加实验员报的一起关的因子)。不算没有归属的改动。 */ export function ablationVerification(d: Draft): ChecklistOf<"ablationVerification"> { const a = ablation(d, "核验员", []); const also = lastAblation(d, a.cell).alsoOff; d.section("这一格实际要关的因子", [ [a.key, ...also].join("、") + (also.length === 0 ? "(只关指定的因子)" : "(指定的因子加实验员报的一起关的因子)"), ]); d.section("本任务要处理的东西", [ ...a.lines, "", "### 改动全文(这一格的代码相对完整代码,每段前后各 3 行上下文)", "", fenced(d.req.changes!.diff, "diff"), ]); return { kind: "ablationVerification", cell: a.cell, factors: a.factors }; }