Archived
fix(loop-engine): 配方少写的行要出声,撤掉的行要点名
**M1**:主键算不出来那一行从前是静默 `continue` —— 既不计数也不留痕。 表里少了行,人和判据都查不出为什么。现在这一笔结束时点明少了几行、主键列是哪几个。 **M2**:`prune` 撤行从前只说「撤掉 M 行」。2026-08-02 为人工撤行立过 「撤要说出来、要点名哪几行」的规矩(不说清,下一拍的读者会把人刚删的写回去), 配方这一侧跟上:点名撤了哪几行,封顶 8 个,**并去掉单号那一段** —— 人正在这一单里看,单号是脚下的地面,不是区分行的信息(2026-08-25 同一条口径, 基座那一侧是 `rowLabel`)。为此 `TaskDataGateway.writable` 补一个可选的 `caseKey`。 3 测(少写出声 / 撤行点名 / 名单不带单号)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -54,6 +54,23 @@ export type AutoOutcome = {
|
||||
|
||||
const keyText = (key: Readonly<Record<string, string>>): string => Object.values(key).join('|');
|
||||
|
||||
/**
|
||||
* 摆给人看的行标识 —— **去掉单号那一段**(2026-08-25 柴浩:「只保留斜杠右边」):
|
||||
* 人正在这一单里看,单号是脚下的地面,不是区分行的信息。与基座的 `rowLabel` 同一条口径。
|
||||
* 去完一段都不剩(主键只有单号)就退回整段。
|
||||
*/
|
||||
function rowName(key: Readonly<Record<string, string>>, caseKey: string | undefined): string {
|
||||
const rest = Object.entries(key)
|
||||
.filter(([column]) => column !== caseKey)
|
||||
.map(([, value]) => value);
|
||||
return (rest.length > 0 ? rest : Object.values(key)).join('/');
|
||||
}
|
||||
|
||||
/** 点名封顶 8 个 —— 再多就不是「点名」而是刷屏(拒回主键名单封顶 12 是同一条理由) */
|
||||
function nameList(names: readonly string[]): string {
|
||||
return names.length <= 8 ? names.join('、') : `${names.slice(0, 8).join('、')}…等 ${names.length} 行`;
|
||||
}
|
||||
|
||||
/** 这份配方会不会发卡 —— 「卡答过了就收步」那道门要问它 */
|
||||
export function recipeIssuesCard(recipe: AutoRecipe): boolean {
|
||||
return recipe.do.some((action) => action.kind === 'card');
|
||||
@@ -112,6 +129,10 @@ export function createAutoRunner(deps: AutoRunnerDeps): {
|
||||
let localWrote = 0;
|
||||
/** 「只建不改」的表里已经在库、这一笔不用建的行数 */
|
||||
let localExisting = 0;
|
||||
/** 主键算不出来、这一笔没写下去的行数 —— 不出声的话表里少了行谁也查不出为什么(M1) */
|
||||
let localUnkeyed = 0;
|
||||
/** 撤掉的是哪几行 —— 「撤要说出来、要点名」(2026-08-02 为人工撤行立的规矩,配方这一侧跟上,M2) */
|
||||
const dropNames: string[] = [];
|
||||
for (const { row, outer } of expandSource(action.from, base)) {
|
||||
const computed: Record<string, unknown> = {};
|
||||
for (const [name, expr] of Object.entries(action.fields)) {
|
||||
@@ -119,8 +140,11 @@ export function createAutoRunner(deps: AutoRunnerDeps): {
|
||||
}
|
||||
const key: Record<string, string> = {};
|
||||
for (const column of decl.keys) key[column] = String(computed[column] ?? '');
|
||||
/* 主键算不出来就先不写 —— 空主键写下去是一行永远对不上的垃圾 */
|
||||
if (decl.keys.some((column) => key[column] === '')) continue;
|
||||
/* 主键算不出来就先不写 —— 空主键写下去是一行永远对不上的垃圾。**但要数一笔**(M1) */
|
||||
if (decl.keys.some((column) => key[column] === '')) {
|
||||
localUnkeyed += 1;
|
||||
continue;
|
||||
}
|
||||
const patch: Record<string, unknown> = {};
|
||||
for (const [name, value] of Object.entries(computed)) if (!decl.keys.includes(name)) patch[name] = value;
|
||||
/*
|
||||
@@ -160,10 +184,13 @@ export function createAutoRunner(deps: AutoRunnerDeps): {
|
||||
for (const column of decl.keys) key[column] = String(stale[column] ?? '');
|
||||
if (kept.has(keyText(key))) continue;
|
||||
const { dropped: gone } = await deps.data.draftDrop({ taskId: context.taskId, domain: decl.domain, object: decl.object, key });
|
||||
if (gone) localDropped += 1;
|
||||
if (gone) {
|
||||
localDropped += 1;
|
||||
dropNames.push(rowName(key, decl.caseKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (localWrote > 0 || localDropped > 0 || localExisting > 0) {
|
||||
if (localWrote > 0 || localDropped > 0 || localExisting > 0 || localUnkeyed > 0) {
|
||||
/*
|
||||
* **kind: 'observation' + `internal: true`**(2026-08-29 终审 I5,控制端裁定 F2)。
|
||||
* `kind` 必须是 `observation`:`result` 不在 `task-log.ts` 的 `SKELETON` 里,进不了工作日志(R10)。
|
||||
@@ -178,7 +205,10 @@ export function createAutoRunner(deps: AutoRunnerDeps): {
|
||||
title:
|
||||
`${decl.label}:写下 ${localWrote} 行` +
|
||||
(localExisting === 0 ? '' : `,${localExisting} 行已在库、这一笔不用建`) +
|
||||
(localDropped === 0 ? '' : `,撤掉 ${localDropped} 行`),
|
||||
/* 撤了要点名(M2):不点名的话,下一拍的读者(人或模型)对不上是哪几行没了 */
|
||||
(localDropped === 0 ? '' : `,撤掉 ${localDropped} 行(${nameList(dropNames)})`) +
|
||||
/* 少写了几行要出声(M1):静默 continue 的那几行,表里少了谁也查不出为什么 */
|
||||
(localUnkeyed === 0 ? '' : `,${localUnkeyed} 行因主键算不出来没写(主键列:${decl.keys.join('、')})`),
|
||||
detail: { auto: `${context.stepId}#${index + 1}`, ledger: action.into, internal: true },
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user