Archived
用户:「在中央工作区修改了内容点击保存后,为什么会出现『AI 正在推进这单』的提示?」 因果链:件上按保存 → afterSave **先重推件、再叫醒任务**(「保存即铺格」,2026-08-29: 人选了芯片类型按保存,配方这才有机会把参数行铺出来;不叫醒格子永远不出现,08-30 走查在这儿卡死过)→ wake 把 runtime 拨成 running → 工作区的 running 判据排在 viewHtml 前面(2026-08-21:正在跑的这一拍要压过上一拍摆下的**陈旧**的件)→ 人刚填完按下保存, 屏上那份刚重推、含着他刚填的值的件被推进场景顶掉几秒。 08-21 那条规则的前提是「viewHtml 是上一次推的,可能陈旧」,而这条路上件比 running 还新,前提不成立;更要紧的是那一拍按「叫了人就等人」的闸(turn-runner,08-27) **根本不问模型**,而那一屏写着「看材料、写台账、调工具都由它来」——一件没发生, 界面在撒谎。 修:running 那一支加 `&& !holding`。判断收进纯函数 `awaitingHuman`(未办结工单 / 未选决策卡 / 未确认录入卡),与服务端 humanTool.pendingCards 说的是同一句话 —— **有未答的卡,服务端就不问模型,前端就不说 AI 在推进**,两侧同一条道理的两面。 没有任何卡在等人、AI 真在埋头算时,推进场景照旧摆(2026-08-21 要的正是它)。 6 条新测,四项各做变异验证(去掉工单项 / 决策卡项 / 录入卡项各红一条,恒真红两条 —— 恒真等于把推进场景整个废掉);pnpm gates 退出码 0(2493 测)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
106 lines
4.4 KiB
TypeScript
106 lines
4.4 KiB
TypeScript
/**
|
|
* 工作区看对话区的脸色 —— 录入卡在等人时摆占位页,不摆件(2026-08-28 用户定)。
|
|
*/
|
|
import { describe, expect, it } from 'vitest';
|
|
import { dataEntryCardSchema, decisionCardSchema } from '@contracts';
|
|
import type { TaskDetail } from '@contracts';
|
|
import { awaitingHuman, pendingCardsOf } from '../internal/pending-cards.ts';
|
|
|
|
const AT = '2026-08-28T00:55:30.054Z';
|
|
|
|
function entry(id: string, extra: Record<string, unknown> = {}) {
|
|
return dataEntryCardSchema.parse({
|
|
k: 'entry',
|
|
id,
|
|
at: AT,
|
|
ask: '客户 123 不在客户档案里,请核对建档信息后确认',
|
|
ledger: 'customer.profile',
|
|
ledgerLabel: '客户档案',
|
|
key: { customer_code: '123' },
|
|
fields: [],
|
|
...extra,
|
|
});
|
|
}
|
|
|
|
function decision(id: string, extra: Record<string, unknown> = {}) {
|
|
return decisionCardSchema.parse({
|
|
k: 'decision',
|
|
id,
|
|
at: AT,
|
|
ask: '经营计划是否通过?',
|
|
basis: [{ k: '依据', v: '计划表 4 行' }],
|
|
options: [
|
|
{ value: 'pass', label: '通过' },
|
|
{ value: 'reject', label: '驳回' },
|
|
],
|
|
...extra,
|
|
});
|
|
}
|
|
|
|
const chat = (...items: TaskDetail['chat']): TaskDetail['chat'] => items;
|
|
|
|
describe('pendingCardsOf', () => {
|
|
it('未确认的录入卡在场 → entry,并带上那张表的人读名', () => {
|
|
expect(pendingCardsOf(chat(entry('c-1')))).toEqual({ decision: false, entry: true, entryLedger: '客户档案' });
|
|
});
|
|
|
|
it('录入卡已确认(done 落了)→ 不再算在等', () => {
|
|
const done = { at: AT, by: 'S-1', values: { name: '测试公司' } };
|
|
expect(pendingCardsOf(chat(entry('c-1', { done })))).toEqual({ decision: false, entry: false });
|
|
});
|
|
|
|
it('登记里没有人读名时退回对象地址 —— 宁可笼统,不可编', () => {
|
|
expect(pendingCardsOf(chat(entry('c-1', { ledgerLabel: undefined }))).entryLedger).toBe('customer.profile');
|
|
});
|
|
|
|
it('未选的决策卡 → decision;它不触发占位页(人要决定的常常就是件上那份稿)', () => {
|
|
expect(pendingCardsOf(chat(decision('d-1')))).toEqual({ decision: true, entry: false });
|
|
expect(pendingCardsOf(chat(decision('d-1', { chosen: { at: AT, by: 'S-1', value: 'pass' } })))).toEqual({
|
|
decision: false,
|
|
entry: false,
|
|
});
|
|
});
|
|
|
|
it('两张未回的录入卡:点名最早那一张', () => {
|
|
const later = entry('c-2', { at: '2026-08-28T01:00:00.000Z', ledgerLabel: '联系人档案' });
|
|
expect(pendingCardsOf(chat(entry('c-1'), later)).entryLedger).toBe('客户档案');
|
|
});
|
|
});
|
|
|
|
/**
|
|
* **有活等着人办就不摆推进场景**(2026-08-31 用户:「在中央工作区修改了内容点击保存后,
|
|
* 为什么会出现『AI 正在推进这单』」)。
|
|
*
|
|
* 件上按保存走 `afterSave`:**先重推件、再叫醒任务**(「保存即铺格」)。叫醒把 runtime
|
|
* 拨成 `running`,而工作区的 `running` 判据压过 `viewHtml` —— 人刚填完按保存,屏上那份
|
|
* 刚重推的件被推进场景顶掉几秒;而那一拍按「叫了人就等人」的闸根本不问模型,那一屏
|
|
* 写的「看材料、写台账、调工具」一件没发生。这一组钉的就是那道新条件。
|
|
*/
|
|
describe('awaitingHuman:这一单还有活等着人办吗', () => {
|
|
const none = { decision: false, entry: false } as const;
|
|
|
|
it('没工单没卡 —— AI 真在埋头算,推进场景照旧摆(2026-08-21 要的正是它)', () => {
|
|
expect(awaitingHuman({ openOrders: 0, cards: none })).toBe(false);
|
|
});
|
|
|
|
it('有工单没办结 —— 人正要在件上填,不许拿推进场景盖掉件', () => {
|
|
expect(awaitingHuman({ openOrders: 1, cards: none })).toBe(true);
|
|
});
|
|
|
|
it('决策卡还没选 —— 人要决定的对象常常就是件上那份稿,件得留着', () => {
|
|
expect(awaitingHuman({ openOrders: 0, cards: { decision: true, entry: false } })).toBe(true);
|
|
});
|
|
|
|
it('录入卡还没确认 —— 让位给录入占位页那一支(它排在 running 之后)', () => {
|
|
expect(awaitingHuman({ openOrders: 0, cards: { decision: false, entry: true, entryLedger: '客户档案' } })).toBe(true);
|
|
});
|
|
|
|
it('工单全办结、卡全答过 —— 回到「AI 真在算」,推进场景该摆', () => {
|
|
expect(awaitingHuman({ openOrders: 0, cards: none })).toBe(false);
|
|
});
|
|
|
|
it('三样都在 —— 仍是「等人」,不是「AI 在推进」', () => {
|
|
expect(awaitingHuman({ openOrders: 2, cards: { decision: true, entry: true, entryLedger: '客户档案' } })).toBe(true);
|
|
});
|
|
});
|