Archived
fix: 配方只认 liveStep;保存不再无端叫醒任务;prune 不在人填的时候撤行
**I6 · 配方只认 `liveStep`**:`current` 有一条兜底(`liveStep ?? 在场的第一个有剧本的项`), 它不过滤 `settledIds`。当初这只影响提示词内容;配方接进来之后它驱动**副作用** —— 再写一遍台账、再 prune 一遍,carded 为假时甚至再发一张卡。兜底继续只服务提示词。 同处 `settledIds` 改取 sweep 之后的 `stateRuntime`(`liveStepOf` 吃的是 sweep 之后的 `stateEnabled`,两边得同一份局面)。 **注**:这一形状(既已收掉又在场)我没能在当前 lifecycle 里构造出可达来路 —— `publishEnabled` 把 Completed/Terminated 挡在 entries 与 inProgress 之外, `reenter` 又是就地改状态、不留旧实例。用例用的是 `live-step.ts` 注释描述的那种局面 (同 id 两份实例),守的是不变式;详见最终报告。 **F1 · `afterSave` 只在有未答的卡时才 `wake`**:`wake` 对 awaiting-event / awaiting-human 一律拨成 running,推进器随即推一拍并**记一步预算**。而现有九个活动包一个 auto 都没有: 人每按一次保存就白烧一步、界面闪一次「AI 正在推进这单」;任务本来 awaiting-human (等人裁决)时被叫醒,那一拍结束还会把它写成 awaiting-event —— 从「等人办事」降级成 「等外部回音」,而等的明明还是人。**这是界面撒谎。**「保存即铺格」的场景恰恰都有卡在场。 用既有的 `humanTool.pendingCards`,不新造。`wake` 失败不再 `.catch(() => false)` 静默 —— 它是「保存即铺格」的唯一触发器,吞掉等于人按了保存、格子没出现、系统一个字不说。 **F3 · `prune` 不在人填的时候撤行**:人在件上「加一行」加出来的行,配方的数据源里当然没有 —— 编辑中途把它悄悄撤掉是最不能接受的一种静默数据丢失。有卡在等人(quiet)那几拍只铺不撤; 同一张表既被 prune 又列在该步 `humanWrites` 里,装包报 warn;规范 §4.6 纪律补两条 (prune 的表不该是人能加行的表 · card 之后那几笔要等人交回之后那一拍才跑)。 I6 2 测(做过变异验证)· F1 5 测 · F3 2+1 测。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -2613,3 +2613,63 @@ describe('auto 步 · 配方办不下去要给人一条路(I1)', () => {
|
||||
expect(h.issued).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* **配方由 `liveStep` 驱动,不由 `current` 的兜底驱动**(2026-08-29 终审 I6)。
|
||||
*
|
||||
* `current` 有一条兜底(`liveStep ?? 在场的第一个有剧本的项`),它**不过滤 settledIds**。
|
||||
* 当初这只影响提示词内容(读错一段无非白费点上下文);配方接进来之后它驱动**副作用**:
|
||||
* 再写一遍台账、再 prune 一遍,carded 为假时甚至再发一张卡。
|
||||
*
|
||||
* 两者要分岔,得有一个项**既在 settledIds 里、又在场**。局面形状取自 `live-step.ts`
|
||||
* 那条注释描述的那一种:同一个 id 留着一份收掉的实例、又有一份重新入场的
|
||||
* (`publishEnabled` 的 `byId` 取后者 → 在场;`settledIds` 见到前者 → 已收掉)。
|
||||
* **注:这一形状在当前 lifecycle 里我没能构造出可达的来路**(`reenter` 是就地改状态、
|
||||
* 不留旧实例),所以这几条守的是**不变式**而不是复现过的事故 —— 见最终报告 I6 那一段。
|
||||
*/
|
||||
describe('auto 步 · 只认 liveStep(I6)', () => {
|
||||
const REPEAT_MODEL: CaseModel = {
|
||||
name: '收了还在场',
|
||||
autoComplete: false,
|
||||
items: [{ id: 'S1', name: '归一', type: 'humanTask', discretionary: false, entryCriteria: [], exitCriteria: [] }],
|
||||
sentries: [],
|
||||
} as never;
|
||||
/** 收掉的那份实例 + 重新入场的那份 —— 「既已收掉又在场」 */
|
||||
const SETTLED_YET_ONSTAGE: CaseRuntime = {
|
||||
items: [
|
||||
{ id: 'S1', state: 'Completed', instance: 1 },
|
||||
{ id: 'S1', state: 'Enabled', instance: 2 },
|
||||
],
|
||||
} as never;
|
||||
const STEP = {
|
||||
item: 'S1',
|
||||
guide: '',
|
||||
writes: [],
|
||||
reads: [],
|
||||
tools: [],
|
||||
auto: {
|
||||
do: [
|
||||
{ kind: 'project', into: 'quote_intake.record', from: { once: true }, fields: { pn: 'A', eau: 1 } },
|
||||
{ kind: 'card', as: 'execution', title: '再来一张', basis: [{ k: 'a', v: 'b' }] },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
it('既已收掉又在场的步:配方不跑 —— 不重写台账、不再发一张卡', async () => {
|
||||
const h = harness({ parsed: REPEAT_MODEL, runtime: SETTLED_YET_ONSTAGE, sfeelCondition: () => false, steps: [STEP] });
|
||||
await h.runner.runTurn(TASK);
|
||||
expect(h.written).toHaveLength(0);
|
||||
expect(h.issued.some((i) => String(i.card['title']) === '再来一张')).toBe(false);
|
||||
});
|
||||
|
||||
it('同一个局面下,没收掉的那一步照跑 —— 这道门拦的是「已收掉」,不是「所有兜底」', async () => {
|
||||
const h = harness({
|
||||
parsed: REPEAT_MODEL,
|
||||
runtime: { items: [{ id: 'S1', state: 'Enabled', instance: 1 }] } as never,
|
||||
sfeelCondition: () => false,
|
||||
steps: [STEP],
|
||||
});
|
||||
await h.runner.runTurn(TASK);
|
||||
expect(h.written).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user