Archived
fix(desk): 有活等着人办就不摆「AI 正在推进这单」—— 保存后那一闪盖掉的是刚重推的件
用户:「在中央工作区修改了内容点击保存后,为什么会出现『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
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import { dataEntryCardSchema, decisionCardSchema } from '@contracts';
|
import { dataEntryCardSchema, decisionCardSchema } from '@contracts';
|
||||||
import type { TaskDetail } from '@contracts';
|
import type { TaskDetail } from '@contracts';
|
||||||
import { pendingCardsOf } from '../internal/pending-cards.ts';
|
import { awaitingHuman, pendingCardsOf } from '../internal/pending-cards.ts';
|
||||||
|
|
||||||
const AT = '2026-08-28T00:55:30.054Z';
|
const AT = '2026-08-28T00:55:30.054Z';
|
||||||
|
|
||||||
@@ -66,3 +66,40 @@ describe('pendingCardsOf', () => {
|
|||||||
expect(pendingCardsOf(chat(entry('c-1'), later)).entryLedger).toBe('客户档案');
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import ScrollArea from '@web-shared/ScrollArea.tsx';
|
|||||||
import { EmptyState } from '@web-shared/desk/module-kit.tsx';
|
import { EmptyState } from '@web-shared/desk/module-kit.tsx';
|
||||||
import { ViewBlocks, ViewContainer } from '@ui';
|
import { ViewBlocks, ViewContainer } from '@ui';
|
||||||
import { pagerFace, pagerText } from './pager.ts';
|
import { pagerFace, pagerText } from './pager.ts';
|
||||||
import { pendingCardsOf } from './pending-cards.ts';
|
import { awaitingHuman, pendingCardsOf } from './pending-cards.ts';
|
||||||
import type { ViewEdits, ViewSave } from '@ui';
|
import type { ViewEdits, ViewSave } from '@ui';
|
||||||
import { GuideCard, WorkOrderReport } from './work-order-report.tsx';
|
import { GuideCard, WorkOrderReport } from './work-order-report.tsx';
|
||||||
import { FileViewer } from './FileViewer.tsx';
|
import { FileViewer } from './FileViewer.tsx';
|
||||||
@@ -176,6 +176,8 @@ export function ViewRegion({ detail, onReplyOrder, values, onField, locate }: Vi
|
|||||||
*/
|
*/
|
||||||
const pending = pendingCardsOf(detail.chat);
|
const pending = pendingCardsOf(detail.chat);
|
||||||
const pendingChatCard = pending.decision || pending.entry;
|
const pendingChatCard = pending.decision || pending.entry;
|
||||||
|
/* 这一单还有活等着人办吗 —— 推进场景要不要摆看它(缘由见 `awaitingHuman` 的头注) */
|
||||||
|
const holding = awaitingHuman({ openOrders: openList.length, cards: pending });
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 标记已读 / 未读 —— 乐观更新交给失效:标完服务端广播 task 触点,
|
* 标记已读 / 未读 —— 乐观更新交给失效:标完服务端广播 task 触点,
|
||||||
@@ -763,7 +765,7 @@ export function ViewRegion({ detail, onReplyOrder, values, onField, locate }: Vi
|
|||||||
desc="成果正在并进正式表、按去向发往下游。落完这一屏会换成收尾结果 —— 不用刷新。"
|
desc="成果正在并进正式表、按去向发往下游。落完这一屏会换成收尾结果 —— 不用刷新。"
|
||||||
hint="通常几秒钟"
|
hint="通常几秒钟"
|
||||||
/>
|
/>
|
||||||
) : detail.card.runtime === 'running' ? (
|
) : detail.card.runtime === 'running' && !holding ? (
|
||||||
/*
|
/*
|
||||||
* **AI 正在算这一拍,不是「已经有一份件摆着」**(2026-08-21 用户:
|
* **AI 正在算这一拍,不是「已经有一份件摆着」**(2026-08-21 用户:
|
||||||
* 「AI 持续推进中时中间的页面不变成持续推进的页面了,而是卡在 html
|
* 「AI 持续推进中时中间的页面不变成持续推进的页面了,而是卡在 html
|
||||||
@@ -774,6 +776,14 @@ export function ViewRegion({ detail, onReplyOrder, values, onField, locate }: Vi
|
|||||||
* 空待处理就被这份**陈旧**的件抢先占住,推进中动画再也显示不出来。
|
* 空待处理就被这份**陈旧**的件抢先占住,推进中动画再也显示不出来。
|
||||||
* **正在跑的这一拍,永远比上一拍摆下的旧件优先**:running 判据挪到
|
* **正在跑的这一拍,永远比上一拍摆下的旧件优先**:running 判据挪到
|
||||||
* viewHtml 前面,陈旧的件只在「AI 这会儿不在算」时才配拿来顶上。
|
* viewHtml 前面,陈旧的件只在「AI 这会儿不在算」时才配拿来顶上。
|
||||||
|
*
|
||||||
|
* **`&& !holding` 是 2026-08-31 补的**(用户:「在中央工作区修改了内容点击
|
||||||
|
* 保存后,为什么会出现『AI 正在推进这单』」):上面那段的前提是「viewHtml 是
|
||||||
|
* 上一次推的,可能陈旧」。而件上按保存走的 `afterSave` 是**先重推件、再叫醒
|
||||||
|
* 任务**(「保存即铺格」)—— 那条路上件比 `running` 还新,顶掉它没有道理;
|
||||||
|
* 且那一拍按「叫了人就等人」的闸根本不问模型,这一屏写的「看材料、写台账、
|
||||||
|
* 调工具都由它来」一件没发生。**有活等着人办就不摆推进场景**,判断收在
|
||||||
|
* `awaitingHuman` 里(与服务端 `humanTool.pendingCards` 同一句话)。
|
||||||
*/
|
*/
|
||||||
<WorkingScene doneN={doneList.length} />
|
<WorkingScene doneN={doneList.length} />
|
||||||
) : pending.entry ? (
|
) : pending.entry ? (
|
||||||
|
|||||||
@@ -23,6 +23,32 @@ export type PendingCards = {
|
|||||||
readonly entryLedger?: string;
|
readonly entryLedger?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **这一单还有活等着人办吗** —— 未办结的工单,或未回的决策卡 / 录入卡。
|
||||||
|
*
|
||||||
|
* 与服务端 `humanTool.pendingCards`(没办结工单 + 没作答且没被顶替的对话卡)说的是
|
||||||
|
* 同一句话,两侧各按自己手上的数据算。
|
||||||
|
*
|
||||||
|
* 用处(2026-08-31 用户:「在中央工作区修改了内容点击保存后,为什么会出现『AI 正在
|
||||||
|
* 推进这单』」):件上按保存会走 `afterSave` —— **先重推件、再叫醒任务**
|
||||||
|
* (「保存即铺格」:人选了芯片类型按保存,配方这才有机会把参数行铺出来;不叫醒
|
||||||
|
* 格子永远不出现,2026-08-30 走查在这儿卡死过)。叫醒把 runtime 拨成 `running`,
|
||||||
|
* 而工作区的 `running` 判据排在 `viewHtml` 前面(2026-08-21:正在跑的这一拍要压过
|
||||||
|
* 上一拍摆下的**陈旧**的件)—— 于是人刚填完按下保存,屏上那份**刚重推、含着他刚填
|
||||||
|
* 的值**的件被「AI 正在推进这单」顶掉几秒。
|
||||||
|
* 那条 2026-08-21 的规则前提是「viewHtml 是上一次推的,可能陈旧」,而这条路上件比
|
||||||
|
* `running` 还新,前提不成立;更要紧的是那一拍按「叫了人就等人」的闸
|
||||||
|
* (`turn-runner`,2026-08-27)**根本不问模型**,推进场景写着「看材料、写台账、调工具
|
||||||
|
* 都由它来」——那些事一件没发生,**界面在撒谎**。
|
||||||
|
*
|
||||||
|
* 所以:**有活等着人办时不摆推进场景**(件 / 录入占位页照旧摆,人正要在上面办事);
|
||||||
|
* 没有任何卡在等人、AI 真在埋头算下一拍时,推进场景照旧 —— 那正是 2026-08-21 要的。
|
||||||
|
* 与服务端那条闸是同一条道理的两侧:**有未答的卡,服务端就不问模型,前端就不说 AI 在推进。**
|
||||||
|
*/
|
||||||
|
export function awaitingHuman(input: { readonly openOrders: number; readonly cards: PendingCards }): boolean {
|
||||||
|
return input.openOrders > 0 || input.cards.decision || input.cards.entry;
|
||||||
|
}
|
||||||
|
|
||||||
export function pendingCardsOf(chat: TaskDetail['chat']): PendingCards {
|
export function pendingCardsOf(chat: TaskDetail['chat']): PendingCards {
|
||||||
let decision = false;
|
let decision = false;
|
||||||
let entryLedger: string | undefined;
|
let entryLedger: string | undefined;
|
||||||
|
|||||||
Reference in New Issue
Block a user