Archived
fix(loop-engine): 配方留痕改 kind: observation —— result 不在骨架里,永远进不了工作日志
R10:评审查实 Task 12 只测了 actorOf 这个纯函数,而 auto-runner.ts 四类留痕
(say / project / analyze / 出错兜底)一直用 kind: 'result',它不在 task-log.ts 的
SKELETON 里,structuredLog 在骨架过滤这一关就把它们全部滤掉,actorOf 判得再对也没用。
project 与 analyze 两笔还带 detail.internal: true,即使 kind 改对也会被 machinery() 挡下,
一并去掉。
新增回归测试:直接跑真实的 auto-runner.ts,把捕获到的 {kind, title, detail} 喂给
structuredLog 全程,断言它出现在结果里且 who 是 system —— 不是照 spec 手写理想形状。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -86,9 +86,15 @@ export function createAutoRunner(deps: AutoRunnerDeps): {
|
||||
context: AutoContext,
|
||||
): Promise<{ wrote: number; dropped: number; cardId?: string; submit?: { rationale: string }; stop?: boolean }> {
|
||||
if (action.kind === 'say') {
|
||||
/*
|
||||
* **kind: 'observation'**(2026-08-29,R10)—— 从前是 `kind: 'result'`,
|
||||
* 而 `result` 不在 `task-log.ts` 的 `SKELETON` 里,这一笔永远进不了工作日志
|
||||
* (spec §5.9 承诺「配方每一笔照常进 task_memory,工作日志里归系统灰」落空)。
|
||||
* `observation` 在骨架里;不带 `detail.say` 时 `actorOf` 本就归系统。
|
||||
*/
|
||||
await deps.memory.append({
|
||||
taskId: context.taskId,
|
||||
kind: 'result',
|
||||
kind: 'observation',
|
||||
title: evalText(action.text, base),
|
||||
detail: { auto: `${context.stepId}#${index + 1}` },
|
||||
});
|
||||
@@ -130,11 +136,16 @@ export function createAutoRunner(deps: AutoRunnerDeps): {
|
||||
}
|
||||
}
|
||||
if (localWrote > 0 || localDropped > 0) {
|
||||
/*
|
||||
* **kind: 'observation'、不再标 internal**(2026-08-29,R10)—— 从前 `kind: 'result'`
|
||||
* 连骨架都进不了;就算 kind 改对,`internal: true` 也会被 `machinery()` 挡下。
|
||||
* 这一笔是「系统做了什么」,恰是人回看时最想看的东西,不该当排障内部痕藏起来。
|
||||
*/
|
||||
await deps.memory.append({
|
||||
taskId: context.taskId,
|
||||
kind: 'result',
|
||||
kind: 'observation',
|
||||
title: `${decl.label}:写下 ${localWrote} 行${localDropped === 0 ? '' : `,撤掉 ${localDropped} 行`}`,
|
||||
detail: { auto: `${context.stepId}#${index + 1}`, internal: true, ledger: action.into },
|
||||
detail: { auto: `${context.stepId}#${index + 1}`, ledger: action.into },
|
||||
});
|
||||
}
|
||||
return { wrote: localWrote, dropped: localDropped };
|
||||
@@ -159,11 +170,12 @@ export function createAutoRunner(deps: AutoRunnerDeps): {
|
||||
/* `into` 既是出参里那个数组字段的名字,也是拍内作用域的键 —— 一处声明,两处认 */
|
||||
const rows = object[action.into];
|
||||
results[`result:${action.into}`] = Array.isArray(rows) ? rows : [];
|
||||
/* kind: 'observation'、不再标 internal —— 理由同上(project 分支那条注释) */
|
||||
await deps.memory.append({
|
||||
taskId: context.taskId,
|
||||
kind: 'result',
|
||||
kind: 'observation',
|
||||
title: `已把 ${payload.length} 条参数交给模型逐条判定,拿回 ${results[`result:${action.into}`]?.length ?? 0} 条结论`,
|
||||
detail: { auto: `${context.stepId}#${index + 1}`, internal: true, model: action.model },
|
||||
detail: { auto: `${context.stepId}#${index + 1}`, model: action.model },
|
||||
});
|
||||
/* 出参只活在拍内作用域里,不进草稿层 —— 下一笔要能看见它,所以这里也算「脏」 */
|
||||
scope = { ...scope, ...results };
|
||||
@@ -252,11 +264,12 @@ export function createAutoRunner(deps: AutoRunnerDeps): {
|
||||
if (done.stop) break;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
/* kind: 'observation'、去掉那个恒为 false 的 internal 键 —— 理由同上 */
|
||||
await deps.memory.append({
|
||||
taskId: ctx.taskId,
|
||||
kind: 'result',
|
||||
kind: 'observation',
|
||||
title: `配方第 ${index + 1} 笔办不下去:${message} —— 交给你处理`,
|
||||
detail: { auto: `${ctx.stepId}#${index + 1}`, internal: false },
|
||||
detail: { auto: `${ctx.stepId}#${index + 1}` },
|
||||
});
|
||||
return { wrote, dropped, ...(issuedCardId === undefined ? {} : { issuedCardId }), failedAt: { index, message } };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user