Archived
harness 与工作台同日修:数据工单确认即入库 · 叫了人不再自动推进(两道结构闸)· 审核卡 = 终局验收卡结构 · 验收清单人点达标 · 迟到守卫不丢放行裁决 · 画面跟着这一拍发出去的卡走 · 成果详情只翻页 · 占位居中 · 进度条
human-tool / ledger / loop-engine / release / task / interaction 六模块与 app 层(desk 路由、engines、consumers、 data-order-landing、case-stamp、reset-runs、seed-activities、容器与 server 装配),web 的 desk-task (ViewRegion / chat-bubbles / review-face)与 app.css、ui-kit 图标;契约 card / manifest; 迁移 0034_task_seq 与迁移日志;api-report 快照;CLAUDE.md 快照、debt、活动开发规范、HTML 报告规范同步。 逐条口径见 CLAUDE.md 快照(2026-08-27 下午至晚)。另一会话做的,一直留在工作树没入库;与同批另两条提交互相依赖。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0167qWjeyBwvfdGovd7FWbCu
This commit is contained in:
@@ -43,6 +43,23 @@ export function phaseProgress(
|
||||
if (terminal) return { index: total, total, name: CLOSING_PHASE };
|
||||
|
||||
const settled = new Set(items.filter((i) => i.state === 'Completed' || i.state === 'Terminated').map((i) => i.id));
|
||||
/*
|
||||
* **被跳过的项不许把进度条钉死**(2026-08-27 用户实测:都在库时数据工单两项一直是 Available,
|
||||
* 从前「一段里的项全收完才算过」让条永远停在 1/6,而单子早已办到审核了)。
|
||||
* 现在先看「案子走到了哪一段」—— 最后一个有项**动过**(Active / Completed / Terminated)的阶段;
|
||||
* 那一段全收完了就算过、进下一段,没收完就停在那一段。一段都没动过才退回老规矩(第一段)。
|
||||
*/
|
||||
const touched = new Set(
|
||||
items.filter((i) => i.state === 'Active' || i.state === 'Completed' || i.state === 'Terminated').map((i) => i.id),
|
||||
);
|
||||
let last = -1;
|
||||
for (const [at, phase] of phases.entries()) if (phase.items.some((id) => touched.has(id))) last = at;
|
||||
if (last >= 0) {
|
||||
const passed = phases[last]!.items.every((id) => settled.has(id));
|
||||
const current = passed ? last + 1 : last;
|
||||
const phase = phases[current];
|
||||
return phase === undefined ? { index: total, total, name: CLOSING_PHASE } : { index: current + 1, total, name: phase.name };
|
||||
}
|
||||
for (const [at, phase] of phases.entries()) {
|
||||
if (!phase.items.every((id) => settled.has(id))) return { index: at + 1, total, name: phase.name };
|
||||
}
|
||||
|
||||
@@ -387,6 +387,15 @@ export function handsOff(actions: ReadonlyArray<{ kind: string }>): boolean {
|
||||
|
||||
const HANDOFF_KINDS = new Set(['human', 'decide', 'listen', 'submit']);
|
||||
|
||||
/**
|
||||
* **这一步是不是终局放行那一步**(2026-08-27,W05 实测:模型在 T_release 先发了一张执行工单
|
||||
* 「评审放行与落账确认」,人答完它才 submit —— 同一件事问了人两遍)。
|
||||
* 认法:剧本给它声明了人工验收清单、又不是审核卡那一步 —— 放行卡由 `submit` 发,不该再有别的卡。
|
||||
*/
|
||||
export function isReleaseStep(step: { acceptanceChecklist?: ReadonlyArray<string> | undefined; cardVariant?: string | undefined } | undefined): boolean {
|
||||
return step !== undefined && step.acceptanceChecklist !== undefined && step.acceptanceChecklist.length > 0 && step.cardVariant !== 'review';
|
||||
}
|
||||
|
||||
export function sameGist(a: string, b: string): boolean {
|
||||
const norm = (s: string): string => s.replace(/[\p{P}\p{S}\s]/gu, '');
|
||||
const x = norm(a);
|
||||
@@ -643,6 +652,11 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
* 熔断检测(stalledTurns 数「行动被拒」)靠它兜底,纯空转的单不能悄无声息。
|
||||
*/
|
||||
hasFollowUp = false,
|
||||
/**
|
||||
* **这一步派的卡穿哪副面孔**(剧本 `cardVariant`,2026-08-27):按行动里的项号查,项号没给 / 挂不上就按
|
||||
* 当下在办的那一步 —— 从前只按项号查,模型漏了 itemId,审核卡就退成「需要你定」。
|
||||
*/
|
||||
faceOf?: (itemId: string | undefined) => { variant?: 'review'; checklist?: ReadonlyArray<string>; release?: true } | undefined,
|
||||
): Promise<{
|
||||
runtime: CaseRuntime;
|
||||
status: TurnOutcome['status'];
|
||||
@@ -650,6 +664,8 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
saved?: true;
|
||||
/** 这个行动自己已经把画面摆好了 —— 回合末别再盖一帧上去 */
|
||||
framed?: true;
|
||||
/** 这个行动发了一张卡给人,卡是哪一步的 —— 回合末的画面跟着它走(见 runTurn 推帧那段) */
|
||||
issuedFor?: string;
|
||||
}> {
|
||||
const context = await deps.tasks.of(taskId);
|
||||
// 项号先归一(简写认得出),再往下走 —— 一处归一,所有行动型都受用
|
||||
@@ -1118,6 +1134,17 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
return { runtime, status: 'running' };
|
||||
}
|
||||
}
|
||||
/*
|
||||
* **放行那一步不发工单**(2026-08-27 W05 实测):模型在 T_release 发了一张执行工单
|
||||
* 「评审放行与落账确认」,人答完它才 submit,验收卡又来一张 —— 同一件事问人两遍。
|
||||
* 放行卡由 submit 发(基座预检后发验收卡),这一步没有别的卡可发;拒回把路指清。
|
||||
*/
|
||||
if (faceOf?.(action.itemId)?.release === true) {
|
||||
throw new Error(
|
||||
'这一步是终局放行:放行卡由 submit 发(基座预检后自己发验收卡),不发执行工单 / 数据工单 —— ' +
|
||||
'发了等于把同一件事问人两遍(先一张执行单、再一张验收单)。这一拍改成:judge 该裁的裁量判据,然后 submit。',
|
||||
);
|
||||
}
|
||||
const { parsed: cardModel } = await loaded(taskId);
|
||||
/*
|
||||
* **发卡的授权面是调人预算,不是计划项**(2026-08-23,E04 回归实测撞出来的):
|
||||
@@ -1161,6 +1188,7 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
return {
|
||||
runtime: cardUnattached === undefined ? markWorldTouched(advanced, action.itemId) : advanced,
|
||||
status: 'awaiting',
|
||||
issuedFor: action.itemId,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1204,7 +1232,7 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
keyFilter: { cardId: entryCard.id },
|
||||
deadline: 'P3D',
|
||||
});
|
||||
return { runtime: markWorldTouched(entryReady, action.itemId), status: 'awaiting' };
|
||||
return { runtime: markWorldTouched(entryReady, action.itemId), status: 'awaiting', issuedFor: action.itemId };
|
||||
}
|
||||
|
||||
case 'decide': {
|
||||
@@ -1249,9 +1277,12 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
return { runtime, status: 'running' };
|
||||
}
|
||||
const advanced = readyToWork((await loaded(taskId)).parsed, runtime, action.itemId);
|
||||
const face = faceOf?.(action.itemId);
|
||||
const card = await deps.cards.decide({
|
||||
taskId,
|
||||
itemId: action.itemId,
|
||||
...(face?.variant === undefined ? {} : { variant: face.variant }),
|
||||
...(face?.checklist === undefined ? {} : { checklist: face.checklist }),
|
||||
ownerId: context.ownerId,
|
||||
humanCallCap: context.humanCallCap,
|
||||
ask: action.ask,
|
||||
@@ -1265,7 +1296,7 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
keyFilter: { cardId: card.id },
|
||||
deadline: 'P3D',
|
||||
});
|
||||
return { runtime: markWorldTouched(advanced, action.itemId), status: 'awaiting' };
|
||||
return { runtime: markWorldTouched(advanced, action.itemId), status: 'awaiting', issuedFor: action.itemId };
|
||||
}
|
||||
|
||||
case 'listen': {
|
||||
@@ -1514,13 +1545,20 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
detail: { internal: true },
|
||||
});
|
||||
}
|
||||
/*
|
||||
* 验收卡的题目用放行那一步在案例模型里的名字(如「评审放行与落账确认(ACT-GATE-03)」)——
|
||||
* 「收口验收」是没声明时的兜底,人看见的应该是活动自己给这一步起的名。
|
||||
*/
|
||||
const { model: releaseModel, parsed: releaseParsed } = await loaded(taskId);
|
||||
const gateStep = releaseModel.steps?.find((st) => isReleaseStep(st)) ?? releaseStep;
|
||||
const releaseName = gateStep === undefined ? undefined : releaseParsed.items.find((i) => i.id === gateStep.item)?.name;
|
||||
const card = await deps.cards.issue({
|
||||
taskId,
|
||||
ownerId: context.ownerId,
|
||||
humanCallCap: context.humanCallCap,
|
||||
workOrder: 'acceptance',
|
||||
card: {
|
||||
title: '收口验收',
|
||||
title: releaseName === undefined || releaseName.trim() === '' ? '收口验收' : releaseName,
|
||||
basis: [{ k: '提交理由', v: rationale }],
|
||||
...(fields === undefined || fields.length === 0 ? {} : { fields: fields.map((f) => ({ ...f })) }),
|
||||
},
|
||||
@@ -1610,6 +1648,26 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* **叫了人就等人**(2026-08-27,用户:「你叫了人我都没填你就直接推进了」)。
|
||||
*
|
||||
* 这一单还有卡没交回,这一拍就不问模型 —— 不管是谁叫醒的:迟到的回传事件、另一台后端的
|
||||
* 推进器、手动 wake。从前每一次唤醒都跑一拍,而事实行对模型说「这一步已经发过卡,人已经被
|
||||
* 问过了」,它就照「人交回之后」那一段把步骤收掉 —— 没人答的卡就这样被跳过(实测:S1 的卡
|
||||
* 发出 0.3 秒后,上一张卡回传的事件才投到,再一拍就「完成:评估输入与资料补齐确认」)。
|
||||
* 在途验收(上面那段)先于它:验收裁决本身就是那张卡的回传。
|
||||
*/
|
||||
const waiting = await deps.cards.unanswered(taskId);
|
||||
if (waiting.length > 0) {
|
||||
await deps.memory.append({
|
||||
taskId,
|
||||
kind: 'observation',
|
||||
title: `等人交回:${waiting.map((c) => c.title).join('、')} —— 这一拍不动`,
|
||||
detail: { internal: true, cards: waiting.map((c) => c.id) },
|
||||
});
|
||||
return { runtime, enabled, navigation: { kind: 'converged' }, status: 'awaiting' };
|
||||
}
|
||||
|
||||
/*
|
||||
* 齐了即收:必做项完成且无进行中项 → 提交放行(发验收卡,终局唯放行)。
|
||||
*
|
||||
@@ -1772,22 +1830,37 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
* 只收 Active/Available 的项;已经收了的不重复收。收完重算 enabled(收掉一项
|
||||
* 可能让下一项的前提成立),循环到收不动为止 —— 封顶 8 轮,防剧本写出环。
|
||||
*/
|
||||
let stateRuntime = runtime;
|
||||
let stateEnabled = enabled;
|
||||
if (steps.length > 0) {
|
||||
/**
|
||||
* 在场的项 = 当下可为 ∪ 进行中 —— 两摞都要看:
|
||||
* 成果写完但还没 finish 的那一项正躺在「进行中」里,它才是最该被自动收的。
|
||||
*
|
||||
* **人工激活的项:人答过卡之后照常求 doneWhen**(2026-08-27 晚,用户:「根因修了吗?不要只修表象」)。
|
||||
* 从前一律不收 —— 怕引擎替人签字(T_release 触达世界,收它得人按一下)。可 W05 的两张数据工单实测:
|
||||
* 卡人答了、数据也入库了,「这一步办完了没有」是引擎手里现成的事实,却让模型去记得 finish ——
|
||||
* 剧本两处白纸黑字「先 finish 再发下一张」,它照样忘,两步同时 Active、件停在上一步、人无处可填。
|
||||
* 设计律:**指南让模型判的事,引擎有现成事实的,一律改成读事实。**
|
||||
* 门槛仍在:`worldTouched`(卡真发过)—— 卡没发过的人工激活项 doneWhen 再真也不收,那才是替人签字;
|
||||
* 走到这儿时没有未答的卡(前面 `cards.unanswered` 把住),拍尾那一遍另把这一拍刚发卡的项排除。
|
||||
* 没声明 doneWhen 的人工激活项(T_release / 审核循环 / 交人补缺口)一个字不变。
|
||||
*/
|
||||
const sweepRipe = async (
|
||||
runtimeIn: CaseRuntime,
|
||||
enabledIn: ReturnType<typeof publishEnabled>,
|
||||
scopeIn: typeof scope,
|
||||
exclude?: string,
|
||||
): Promise<{ runtime: CaseRuntime; enabled: ReturnType<typeof publishEnabled> }> => {
|
||||
let runtimeNow = runtimeIn;
|
||||
let enabledNow = enabledIn;
|
||||
if (steps.length === 0) return { runtime: runtimeNow, enabled: enabledNow };
|
||||
for (let round = 0; round < 8; round += 1) {
|
||||
/*
|
||||
* 在场的项 = 当下可为 ∪ 进行中 —— 两摞都要看:
|
||||
* 成果写完但还没 finish 的那一项正躺在「进行中」里,它才是最该被自动收的。
|
||||
* **人工激活的项不自动收**(T_release 触达世界,收它得人按一下)。
|
||||
*/
|
||||
const onStage = [...stateEnabled.entries, ...stateEnabled.inProgress];
|
||||
const onStage = [...enabledNow.entries, ...enabledNow.inProgress];
|
||||
const ripe = onStage.find((e) => {
|
||||
if (e.requiresManualStart) return false;
|
||||
if (e.id === exclude) return false;
|
||||
if (e.requiresManualStart && runtimeNow.items.find((i) => i.id === e.id)?.worldTouched !== true) return false;
|
||||
const step = stepOf(e.id);
|
||||
if (step?.doneWhen === undefined || step.doneWhen.trim() === '') return false;
|
||||
try {
|
||||
return deps.sfeel.condition(step.doneWhen, scope);
|
||||
return deps.sfeel.condition(step.doneWhen, scopeIn);
|
||||
} catch {
|
||||
// 谓词写坏了不该拖垮整拍:当作「没算出来」,照旧交给模型
|
||||
return false;
|
||||
@@ -1796,8 +1869,8 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
if (ripe === undefined) break;
|
||||
const step = stepOf(ripe.id);
|
||||
try {
|
||||
const active = readyToWork(parsed, stateRuntime, ripe.id);
|
||||
stateRuntime = transitionItem(active, ripe.id, 'Completed');
|
||||
const active = readyToWork(parsed, runtimeNow, ripe.id);
|
||||
runtimeNow = transitionItem(active, ripe.id, 'Completed');
|
||||
} catch {
|
||||
break;
|
||||
}
|
||||
@@ -1807,11 +1880,13 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
title: `自动收步:${ripe.id} 的成果已在台账里(${step?.doneWhen ?? ''})—— 不必再问模型`,
|
||||
detail: { internal: true, item: ripe.id, doneWhen: step?.doneWhen },
|
||||
});
|
||||
stateEnabled = publishEnabled(parsed, stateRuntime, scope, changedRefs(scope), deps.sfeel.condition, [
|
||||
...stateEnabled.gaps,
|
||||
]);
|
||||
enabledNow = publishEnabled(parsed, runtimeNow, scopeIn, changedRefs(scopeIn), deps.sfeel.condition, [...enabledNow.gaps]);
|
||||
}
|
||||
}
|
||||
return { runtime: runtimeNow, enabled: enabledNow };
|
||||
};
|
||||
const swept = await sweepRipe(runtime, enabled, scope);
|
||||
const stateRuntime = swept.runtime;
|
||||
const stateEnabled = swept.enabled;
|
||||
|
||||
/*
|
||||
* 当前这一步 = 当下可为里**第一个有剧本的项**(CMMN 的项序即业务次序)。
|
||||
@@ -1850,6 +1925,17 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
const declared = parsed.items.find((i) => i.id === current.item);
|
||||
if (declared?.manualActivation !== true) return '';
|
||||
const touched = stateRuntime.items.find((i) => i.id === current.item)?.worldTouched === true;
|
||||
/*
|
||||
* 终局放行那一步的事实行**只指向 submit**(2026-08-27 W05 实测):下面那句「唯一合法的动作是先发
|
||||
* 那一张卡」本是给人工激活的执行 / 数据 / 决策步写的,放行步读到它就去发了一张执行工单。
|
||||
*/
|
||||
if (isReleaseStep(current)) {
|
||||
return touched
|
||||
? '这一步是终局放行:验收卡已经发出去了,人签字之前不要再动台账、不要再发任何卡;被打回重做照剧本改完再 submit。'
|
||||
: '这一步是终局放行(触达世界):**唯一合法的动作是 submit(提交放行)** —— 基座预检后自己发验收卡。' +
|
||||
'不要发执行工单 / 数据工单 / 决策卡,也不要立监听(发了会被拒,而且等于把同一件事问人两遍)。' +
|
||||
'先 judge 该裁的裁量判据,再 submit,同一拍连做。';
|
||||
}
|
||||
return touched
|
||||
? '这一步标了人工激活(触达世界):这一单里已经真的给它发过卡或立过监听——人已经被问过了,' +
|
||||
'照下面「人交回之后」那一段办。'
|
||||
@@ -2058,6 +2144,8 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
let saved = false;
|
||||
let framed = false;
|
||||
let lastAction: AgentAction | undefined;
|
||||
/** 这一拍发出去的卡是哪一步的 —— 画面跟卡走 */
|
||||
let issuedFor: string | undefined;
|
||||
/*
|
||||
* **finish 的 doneWhen 要按「此刻」的草稿算,不是拍首快照**(2026-08-22,E04 实测):
|
||||
* 「同一串先写后 finish 一拍连做」是提示词鼓励的省拍打法,而 doneWhen 若按拍首的
|
||||
@@ -2077,6 +2165,7 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
reason?: string;
|
||||
saved?: true;
|
||||
framed?: true;
|
||||
issuedFor?: string;
|
||||
};
|
||||
try {
|
||||
// AI 不得产生终局迁移 —— start 一个不存在的项按被拒处理,不掀翻整拍
|
||||
@@ -2103,6 +2192,20 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
}
|
||||
},
|
||||
index < planned.length - 1,
|
||||
// 面孔与人工验收清单按剧本定:行动点名的项声明了就用它的,没声明就按当下在办的那一步
|
||||
(itemId) => {
|
||||
const named = itemId === undefined ? undefined : stepOf(itemId);
|
||||
const step = named?.cardVariant !== undefined || named?.acceptanceChecklist !== undefined ? named : (current ?? named);
|
||||
const variant = step?.cardVariant === 'review' ? ('review' as const) : undefined;
|
||||
const checklist = step?.acceptanceChecklist;
|
||||
const release = isReleaseStep(step);
|
||||
if (variant === undefined && checklist === undefined && !release) return undefined;
|
||||
return {
|
||||
...(variant === undefined ? {} : { variant }),
|
||||
...(checklist === undefined ? {} : { checklist }),
|
||||
...(release ? { release: true as const } : {}),
|
||||
};
|
||||
},
|
||||
);
|
||||
if (action.kind === 'write' || action.kind === 'drop') scopeStale = true;
|
||||
} catch (error) {
|
||||
@@ -2120,6 +2223,7 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
if (applied.reason !== undefined) blockedReason = applied.reason;
|
||||
if (applied.saved === true) saved = true;
|
||||
if (applied.framed === true) framed = true;
|
||||
if (applied.issuedFor !== undefined) issuedFor = applied.issuedFor;
|
||||
// 挂起型落地即收拍:卡已发 / 监听已立 / 验收已提 —— 这一拍到此为止
|
||||
if (applied.status !== 'running') break;
|
||||
// 工具出参要先看见:同拍余下的行动是闭着眼计划的,作废并留痕
|
||||
@@ -2134,6 +2238,21 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* **拍尾再扫一遍**(2026-08-27 晚):人答卡的那一拍里模型写完东西,上一步的 doneWhen 往往这时才真
|
||||
* (值是人在件上填的、旗是这一拍记的);不等下一拍 —— 下一拍可能要等人再答一张卡才来,
|
||||
* 而画面与进度条在这之间都得对(实测:P-08 没收、P-10 的卡发了,人对着 P-08 的件无处可填)。
|
||||
* 提交放行那一拍(saved)不扫:它已连在途验收一起落盘,再存一次会把信封擦掉。
|
||||
* 这一拍刚发卡的项排除(卡还没人答,收它就是替人签字)。
|
||||
*/
|
||||
if (!saved) {
|
||||
if (scopeStale) {
|
||||
liveScope = await deps.data.view(taskId);
|
||||
scopeStale = false;
|
||||
}
|
||||
const enabledTail = publishEnabled(parsed, currentRuntime, liveScope, changedRefs(liveScope), deps.sfeel.condition, [...stateEnabled.gaps]);
|
||||
currentRuntime = (await sweepRipe(currentRuntime, enabledTail, liveScope, issuedFor)).runtime;
|
||||
}
|
||||
// 已经自己落过笔的行动(提交放行连在途验收一起存)不再兜底保存 —— 见 submit 分支的注释
|
||||
if (!saved) await deps.state.save(taskId, model, parsed, currentRuntime);
|
||||
|
||||
@@ -2181,11 +2300,22 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
||||
.filter((e) => !settledNow.has(e.id))
|
||||
.map((e) => stepOf(e.id))
|
||||
.find((x) => x !== undefined);
|
||||
const wantedNow = stepNow?.view !== undefined && stepNow.view !== '' ? stepNow.view : wanted;
|
||||
/*
|
||||
* **画面跟着这一拍发出去的卡走**(2026-08-27 晚,用户送来 3ef3fc3e:「当前这一步不是应该给我推
|
||||
* P-10 的数据工单吗?怎么还是推送的芯片主数据创建,这样我都没法填数据了」)。
|
||||
* 上面那条「按办完之后在办的那一步」在**两步同时 Active** 时仍会挑错:模型没收掉 S0d_p08(剧本白纸黑字
|
||||
* 写了「先 finish 再发下一张」,两处;措辞治不了),直接发了 S0d_p10 的数据工单 —— 「第一步没干完的」
|
||||
* 算出来还是 P-08,人拿着 P-10 的卡对着 P-08 的件,提交闸拦「缺资料」而屏上没有资料表可填。
|
||||
* 卡是发给人的那件事,件必须是那件事的件:这一拍发了哪一步的卡(human / entry / decide),
|
||||
* 那一步声明了画面就推它,压过别的规则;没发卡的拍照旧。
|
||||
*/
|
||||
const issuedStep = issuedFor === undefined ? undefined : stepOf(issuedFor);
|
||||
const issuedView = issuedStep?.view !== undefined && issuedStep.view !== '' ? issuedStep.view : undefined;
|
||||
const wantedNow = issuedView ?? (stepNow?.view !== undefined && stepNow.view !== '' ? stepNow.view : wanted);
|
||||
const frame = wantedNow !== undefined && wantedNow !== '' ? wantedNow : steps.length === 0 ? 'case-overview' : undefined;
|
||||
if (!framed && frame !== undefined && frame !== '') {
|
||||
try {
|
||||
const shown = await showView(taskId, frame, currentRuntime, stepNow?.view ?? liveStep?.view);
|
||||
const shown = await showView(taskId, frame, currentRuntime, issuedView ?? stepNow?.view ?? liveStep?.view);
|
||||
if (!shown.pushed) {
|
||||
await deps.memory.append({
|
||||
taskId,
|
||||
|
||||
Reference in New Issue
Block a user