Archived
M4: 范式引擎与任务运行时
skill-engine(L4):载方法不载权力(白名单外工具即拒)、结晶片段经 ToolInvoker 留痕、 装配入信封、技能目录渐进披露 flow-engine(L5):四种步型、确定性谓词跳步、重试与声明给定的兜底、 异步步即监听(时限必配)、写类工具登记动作意向、步序位置持久 loop-engine(L5):自研 CMMN 子集解析器(表外构件直接报错,子集封顶可机器证明)、 两副标准生命周期与迁移拓扑(引擎不得私改)、Enabled ≠ Active、 语义性前提标待裁量评估(引擎不代裁量)、DMN 确定性求值 + 命中行留痕、 DRD 缺口演绎与拓扑序、双向驱动与交集导航(断点即人环)、三型行动、 终局唯放行与 Harness task(L6):终态五名裁定(released 唯一成功)、Harness 预算环外强制与停滞熔断 (监听中是健康态)、控制三语义(叫停不是终态,记最重档纠正)、改派留痕通知双方、 断点续跑三份持久状态、封卷触发蒸馏钩子 301 个测试通过,门禁五道全绿 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
co-authored by
Claude Opus 5
parent
2850e6d4ef
commit
3c974d2860
@@ -0,0 +1,158 @@
|
||||
/** Harness 与终态五名裁定 —— domain 测试零 mock(OS 04 / 4.3,AM 10) */
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import type { BudgetCaps } from '@contracts';
|
||||
import { budgetBreaches, durationMs, isStalled, judge, terminalOf, ZERO_CONSUMPTION } from '../internal/domain/harness.ts';
|
||||
import {
|
||||
applyControl,
|
||||
decideTerminal,
|
||||
isPassiveOpen,
|
||||
isSuccess,
|
||||
isTerminalControl,
|
||||
landingBlocked,
|
||||
rejectionKeepsRunning,
|
||||
resumeReadiness,
|
||||
triggersFailureDisposition,
|
||||
} from '../internal/domain/lifecycle.ts';
|
||||
|
||||
const caps: BudgetCaps = { steps: 50, tokens: 100_000, activeMinutes: 120, humanCalls: 5 };
|
||||
|
||||
describe('预算执行器 —— 环外强制,活动不自查自守', () => {
|
||||
it('未越限即无 breach', () => {
|
||||
expect(budgetBreaches({ steps: 10, tokens: 100, activeMs: 1000, humanCalls: 1 }, caps)).toEqual([]);
|
||||
});
|
||||
|
||||
it('任一项触顶即熔断', () => {
|
||||
expect(budgetBreaches({ ...ZERO_CONSUMPTION, steps: 50 }, caps).map((b) => b.item)).toEqual(['steps']);
|
||||
expect(budgetBreaches({ ...ZERO_CONSUMPTION, tokens: 100_000 }, caps).map((b) => b.item)).toEqual(['tokens']);
|
||||
expect(budgetBreaches({ ...ZERO_CONSUMPTION, humanCalls: 5 }, caps).map((b) => b.item)).toEqual(['humanCalls']);
|
||||
});
|
||||
|
||||
it('活跃时长按分钟折算', () => {
|
||||
expect(budgetBreaches({ ...ZERO_CONSUMPTION, activeMs: 120 * 60_000 }, caps).map((b) => b.item)).toEqual(['activeMs']);
|
||||
expect(budgetBreaches({ ...ZERO_CONSUMPTION, activeMs: 119 * 60_000 }, caps)).toEqual([]);
|
||||
});
|
||||
|
||||
it('多项同时越限全部报出', () => {
|
||||
expect(budgetBreaches({ steps: 99, tokens: 999_999, activeMs: 0, humanCalls: 0 }, caps)).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('停滞检测 —— 监听中是健康态', () => {
|
||||
const base = { sinceLastProgressMs: 60 * 60_000, stallAfterMs: 30 * 60_000, hasArmedListener: false, awaitingHumanReply: false };
|
||||
|
||||
it('该动未动、超限仍无推进即停滞', () => {
|
||||
expect(isStalled(base)).toBe(true);
|
||||
});
|
||||
|
||||
it('有监听在等一律不算停滞 —— 与停滞分别对待', () => {
|
||||
expect(isStalled({ ...base, hasArmedListener: true })).toBe(false);
|
||||
});
|
||||
|
||||
it('等人回传也不算停滞', () => {
|
||||
expect(isStalled({ ...base, awaitingHumanReply: true })).toBe(false);
|
||||
});
|
||||
|
||||
it('未到阈值不算停滞', () => {
|
||||
expect(isStalled({ ...base, sinceLastProgressMs: 10 * 60_000 })).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('judge —— 预算优先于停滞', () => {
|
||||
const stall = { sinceLastProgressMs: 999_999_999, stallAfterMs: 1000, hasArmedListener: false, awaitingHumanReply: false };
|
||||
|
||||
it('越限即 exhausted', () => {
|
||||
const verdict = judge({ ...ZERO_CONSUMPTION, steps: 50 }, caps, stall);
|
||||
expect(verdict.kind).toBe('exhausted');
|
||||
expect(terminalOf(verdict)).toBe('exhausted');
|
||||
});
|
||||
|
||||
it('未越限但空转即 stalled', () => {
|
||||
const verdict = judge(ZERO_CONSUMPTION, caps, stall);
|
||||
expect(verdict.kind).toBe('stalled');
|
||||
expect(terminalOf(verdict)).toBe('stalled');
|
||||
});
|
||||
|
||||
it('都没事即 ok,不产生终态', () => {
|
||||
const verdict = judge(ZERO_CONSUMPTION, caps, { ...stall, hasArmedListener: true });
|
||||
expect(verdict).toEqual({ kind: 'ok' });
|
||||
expect(terminalOf(verdict)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('控制三语义', () => {
|
||||
it('暂停 = 可恢复冻结', () => {
|
||||
expect(applyControl('pause')).toEqual({ kind: 'suspended', resumable: true });
|
||||
});
|
||||
|
||||
it('叫停 = 挂起 + 最重档纠正信号,恢复归负责人', () => {
|
||||
expect(applyControl('halt')).toEqual({ kind: 'halted', resumable: true, correctionSignal: 'heaviest' });
|
||||
});
|
||||
|
||||
it('终止 = 不可恢复关闭', () => {
|
||||
expect(applyControl('terminate')).toEqual({ kind: 'terminated', resumable: false, terminal: 'terminated' });
|
||||
});
|
||||
|
||||
it('只有终止是终态 —— 叫停不是', () => {
|
||||
expect(isTerminalControl('halt')).toBe(false);
|
||||
expect(isTerminalControl('pause')).toBe(false);
|
||||
expect(isTerminalControl('terminate')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('终态五名 —— 系统命名,封闭枚举', () => {
|
||||
it('放行完成是唯一成功终态', () => {
|
||||
expect(decideTerminal({ kind: 'released' }).terminal).toBe('released');
|
||||
expect(isSuccess('released')).toBe(true);
|
||||
for (const t of ['blocked', 'stalled', 'exhausted', 'terminated'] as const) expect(isSuccess(t)).toBe(false);
|
||||
});
|
||||
|
||||
it('五名各有其因', () => {
|
||||
expect(decideTerminal({ kind: 'blocked', reason: '监听时限走完' }).terminal).toBe('blocked');
|
||||
expect(decideTerminal({ kind: 'stalled', evidence: '空转' }).terminal).toBe('stalled');
|
||||
expect(decideTerminal({ kind: 'exhausted', detail: 'token' }).terminal).toBe('exhausted');
|
||||
expect(decideTerminal({ kind: 'terminated', reason: '治理台终止' }).terminal).toBe('terminated');
|
||||
});
|
||||
|
||||
it('非完成终态触发流程层失败走向', () => {
|
||||
expect(triggersFailureDisposition('released')).toBe(false);
|
||||
expect(triggersFailureDisposition('blocked')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('「不是终态」的三样', () => {
|
||||
it('驳回是环内回转,任务继续', () => {
|
||||
expect(rejectionKeepsRunning()).toEqual({ runtime: 'running' });
|
||||
});
|
||||
it('落地受阻是运行态', () => {
|
||||
expect(landingBlocked('连接超时')).toEqual({ runtime: 'landing-blocked', reason: '连接超时' });
|
||||
});
|
||||
it('打开任务只是打开呈现', () => {
|
||||
expect(isPassiveOpen('running')).toBe(false);
|
||||
expect(isPassiveOpen('awaiting-event')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('断点续跑 —— 三份持久状态共同保证', () => {
|
||||
it('三样齐备才算可续', () => {
|
||||
expect(resumeReadiness({ 'task-memory': true, 'draft-layer': true, 'runtime-state': true })).toEqual({
|
||||
ready: true,
|
||||
missing: [],
|
||||
});
|
||||
});
|
||||
it('缺一即不可续,并指出缺谁', () => {
|
||||
expect(resumeReadiness({ 'task-memory': true, 'draft-layer': false, 'runtime-state': true })).toEqual({
|
||||
ready: false,
|
||||
missing: ['draft-layer'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('durationMs', () => {
|
||||
it('解析 ISO-8601', () => {
|
||||
expect(durationMs('PT30M')).toBe(30 * 60_000);
|
||||
expect(durationMs('P1D')).toBe(86_400_000);
|
||||
});
|
||||
it('非法即报错', () => {
|
||||
expect(() => durationMs('30分钟')).toThrow(/非法时长/);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user