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:
JCAI Main Agent
2026-07-27 01:51:22 +08:00
co-authored by Claude Opus 5
parent 2850e6d4ef
commit 3c974d2860
55 changed files with 7113 additions and 1 deletions
@@ -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(/非法时长/);
});
});