feat(core): add Goal v3 state protocol (#7517)

* feat(core): add Goal v3 state protocol

* fix(core): address Goal protocol review feedback

* fix(core): recover paused legacy Goals

* fix(core): reject unsupported Goal controls

* fix(core): cover recoverable Goal states

* fix(core): recover older valid Goal state

Continue past malformed lifecycle records so a newer corrupt write cannot hide the latest valid Goal snapshot.

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
This commit is contained in:
qqqys 2026-07-24 01:39:02 +08:00 committed by GitHub
parent 8bc1cf0a5e
commit e79b538461
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 1834 additions and 0 deletions

View file

@ -17,6 +17,10 @@
"types": "./dist/src/utils/transcript-records.d.ts",
"import": "./dist/src/utils/transcript-records.js"
},
"./goalWire": {
"types": "./dist/src/goals/goal-wire.d.ts",
"import": "./dist/src/goals/goal-wire.js"
},
"./package.json": "./package.json",
"./dist/*": "./dist/*",
"./src/*": "./src/*"

View file

@ -0,0 +1,125 @@
/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, expect, it } from 'vitest';
import type {
GoalRecord,
GoalStateCause,
GoalStateRecordPayloadV2,
} from './goal-protocol.js';
import { projectGoalStateToLegacy } from './goal-legacy-projection.js';
const GOAL: GoalRecord = {
goalId: 'goal-1',
revision: 2,
objective: 'ship it',
status: 'active',
evidenceCursor: { recordId: 'state-1' },
turnCount: 4,
activeTimeMs: 2000,
createdAt: 100,
updatedAt: 200,
lastReason: 'continuing',
};
function payload(
cause: GoalStateCause,
status: GoalRecord['status'] = 'active',
goal: GoalRecord | null = { ...GOAL, status },
): GoalStateRecordPayloadV2 {
return {
v: 2,
cause,
snapshot: { v: 2, activity: 'idle', goal },
};
}
describe('projectGoalStateToLegacy', () => {
it.each(['create', 'replace', 'edit', 'resume', 'migrated'] as const)(
'projects %s as legacy set with an active projection',
(cause) => {
const projected = projectGoalStateToLegacy(payload(cause));
expect(projected.goalStatus.kind).toBe('set');
expect(projected.activeGoal).toMatchObject({
condition: 'ship it',
iterations: 4,
setAt: 100,
lastReason: 'continuing',
});
expect(projected.goalTerminal).toBeNull();
},
);
it('projects completion as achieved and stops active_goal', () => {
const projected = projectGoalStateToLegacy(payload('complete', 'complete'));
expect(projected.goalStatus.kind).toBe('achieved');
expect(projected.activeGoal).toBeNull();
expect(projected.goalTerminal).toMatchObject({
kind: 'achieved',
condition: 'ship it',
iterations: 4,
durationMs: 2000,
});
});
it('projects clear as cleared using the prior goal objective', () => {
const projected = projectGoalStateToLegacy(
payload('clear', 'active', null),
GOAL,
);
expect(projected.goalStatus).toMatchObject({
kind: 'cleared',
condition: 'ship it',
});
expect(projected.activeGoal).toBeNull();
expect(projected.goalTerminal).toBeNull();
});
it('projects pause as a non-terminal legacy paused state', () => {
const projected = projectGoalStateToLegacy(payload('pause', 'paused'));
expect(projected.goalStatus.kind).toBe('paused');
expect(projected.activeGoal).toBeNull();
expect(projected.goalTerminal).toBeNull();
});
it.each(['blocked', 'usage_limited'] as const)(
'projects %s as a legacy stopped state',
(status) => {
const projected = projectGoalStateToLegacy(payload(status, status));
expect(projected.goalStatus.kind).toBe('aborted');
expect(projected.activeGoal).toBeNull();
expect(projected.goalTerminal).toMatchObject({
kind: 'aborted',
condition: 'ship it',
});
},
);
it('uses checking for active runtime progress without widening the union', () => {
const projected = projectGoalStateToLegacy(
payload('turn_finished', 'active'),
);
expect(projected.goalStatus.kind).toBe('checking');
expect(projected.activeGoal).not.toBeNull();
expect(projected.goalTerminal).toBeNull();
});
it('does not repeat an aborted terminal after a paused turn finishes', () => {
const projected = projectGoalStateToLegacy(
payload('turn_finished', 'paused'),
);
expect(projected.goalStatus.kind).toBe('checking');
expect(projected.activeGoal).toBeNull();
expect(projected.goalTerminal).toBeNull();
});
});

View file

@ -0,0 +1,137 @@
/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import type { GoalRecord, GoalStateRecordPayloadV2 } from './goal-protocol.js';
export type LegacyGoalStatusKind =
| 'set'
| 'achieved'
| 'cleared'
| 'failed'
| 'aborted'
| 'paused'
| 'checking';
export interface LegacyGoalStatus {
type: 'goal_status';
kind: LegacyGoalStatusKind;
condition: string;
iterations?: number;
setAt?: number;
durationMs?: number;
lastReason?: string;
}
export interface LegacyActiveGoal {
readonly condition: string;
readonly iterations: number;
readonly setAt: number;
readonly tokensAtStart?: number;
readonly hookId?: string;
readonly lastReason?: string;
}
export interface LegacyGoalTerminal {
kind: 'achieved' | 'failed' | 'aborted';
condition: string;
iterations: number;
durationMs: number;
lastReason?: string;
}
export interface LegacyGoalProjection {
activeGoal: LegacyActiveGoal | null;
goalStatus: LegacyGoalStatus;
goalTerminal: LegacyGoalTerminal | null;
}
export function projectGoalStateToLegacy(
payload: GoalStateRecordPayloadV2,
previousGoal: GoalRecord | null = null,
): LegacyGoalProjection {
const snapshotGoal = payload.snapshot.goal;
const displayGoal = snapshotGoal ?? previousGoal;
const kind = legacyStatusKind(payload);
const goalStatus: LegacyGoalStatus = {
type: 'goal_status',
kind,
condition: displayGoal?.objective ?? '',
...(displayGoal ? { iterations: displayGoal.turnCount } : {}),
...(displayGoal ? { setAt: displayGoal.createdAt } : {}),
...(displayGoal ? { durationMs: displayGoal.activeTimeMs } : {}),
...(displayGoal?.lastReason === undefined
? {}
: { lastReason: displayGoal.lastReason }),
};
const terminalKind =
kind === 'achieved' || kind === 'failed' || kind === 'aborted'
? kind
: undefined;
return {
activeGoal:
snapshotGoal?.status === 'active'
? {
condition: snapshotGoal.objective,
iterations: snapshotGoal.turnCount,
setAt: snapshotGoal.createdAt,
...(snapshotGoal.lastReason === undefined
? {}
: { lastReason: snapshotGoal.lastReason }),
}
: null,
goalStatus,
goalTerminal:
terminalKind && displayGoal
? {
kind: terminalKind,
condition: displayGoal.objective,
iterations: displayGoal.turnCount,
durationMs: displayGoal.activeTimeMs,
...(displayGoal.lastReason === undefined
? {}
: { lastReason: displayGoal.lastReason }),
}
: null,
};
}
function legacyStatusKind(
payload: GoalStateRecordPayloadV2,
): LegacyGoalStatusKind {
switch (payload.cause) {
case 'create':
case 'replace':
case 'edit':
case 'resume':
case 'migrated':
return 'set';
case 'complete':
return 'achieved';
case 'clear':
return 'cleared';
case 'pause':
return 'paused';
case 'blocked':
case 'usage_limited':
return 'aborted';
case 'turn_finished':
case 'verifier_accept':
case 'verifier_reject':
return payload.snapshot.goal?.status === 'complete'
? 'achieved'
: payload.snapshot.goal?.status === 'blocked' ||
payload.snapshot.goal?.status === 'usage_limited'
? 'aborted'
: 'checking';
default:
return assertNever(payload.cause);
}
}
function assertNever(value: never): never {
throw new Error(`Unsupported Goal state cause: ${String(value)}`);
}

View file

@ -0,0 +1,249 @@
/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, expect, it } from 'vitest';
import type { GoalStateRecordPayloadV2 } from './goal-protocol.js';
import type { GoalRecoveryRecord } from './goal-persistence.js';
import {
createMigratedGoalState,
recoverGoalFromRecords,
} from './goal-persistence.js';
const ACTIVE_PAYLOAD: GoalStateRecordPayloadV2 = {
v: 2,
cause: 'create',
snapshot: {
v: 2,
activity: 'idle',
goal: {
goalId: 'goal-1',
revision: 1,
objective: 'ship it',
status: 'active',
evidenceCursor: { recordId: 'state-1' },
turnCount: 3,
activeTimeMs: 1500,
createdAt: 100,
updatedAt: 200,
},
},
};
function record(
uuid: string,
overrides: Partial<GoalRecoveryRecord> = {},
): GoalRecoveryRecord {
return {
uuid,
type: 'system',
...overrides,
};
}
describe('recoverGoalFromRecords', () => {
it('returns the newest valid v2 lifecycle snapshot', () => {
const newer = {
...ACTIVE_PAYLOAD,
cause: 'pause' as const,
snapshot: {
...ACTIVE_PAYLOAD.snapshot,
goal: { ...ACTIVE_PAYLOAD.snapshot.goal!, status: 'paused' as const },
},
};
expect(
recoverGoalFromRecords([
record('state-1', {
subtype: 'goal_state',
systemPayload: ACTIVE_PAYLOAD,
}),
record('state-2', {
subtype: 'goal_state',
systemPayload: newer,
}),
]),
).toEqual({ kind: 'v2', payload: newer });
});
it.each<{
label: string;
overrides: Partial<GoalRecoveryRecord>;
}>([
{
label: 'malformed',
overrides: {
systemPayload: {
v: 3,
snapshot: ACTIVE_PAYLOAD.snapshot,
} as unknown as GoalStateRecordPayloadV2,
},
},
{
label: 'non-system',
overrides: {
type: 'user',
systemPayload: ACTIVE_PAYLOAD,
},
},
])(
'uses the newest valid lifecycle record when a newer record is $label',
({ overrides }) => {
expect(
recoverGoalFromRecords([
record('state-1', {
subtype: 'goal_state',
systemPayload: ACTIVE_PAYLOAD,
}),
record('state-2', {
subtype: 'goal_state',
...overrides,
}),
]),
).toEqual({ kind: 'v2', payload: ACTIVE_PAYLOAD });
},
);
it('rejects a goal_state payload stored on a non-system record', () => {
expect(
recoverGoalFromRecords([
record('state-1', {
type: 'user',
subtype: 'goal_state',
systemPayload: ACTIVE_PAYLOAD,
}),
]),
).toEqual({
kind: 'unsupported',
reason: expect.stringContaining('state-1'),
});
});
it.each(['paused', 'blocked', 'usage_limited', 'complete'] as const)(
'restores %s state for display without making it active',
(status) => {
const payload: GoalStateRecordPayloadV2 = {
...ACTIVE_PAYLOAD,
snapshot: {
...ACTIVE_PAYLOAD.snapshot,
goal: { ...ACTIVE_PAYLOAD.snapshot.goal!, status },
},
};
const recovery = recoverGoalFromRecords([
record('state-1', {
subtype: 'goal_state',
systemPayload: payload,
}),
]);
expect(recovery).toEqual({ kind: 'v2', payload });
if (recovery.kind === 'v2') {
expect(recovery.payload.snapshot.activity).toBe('idle');
expect(recovery.payload.snapshot.goal?.status).toBe(status);
}
},
);
it('uses only the objective from a legacy active Goal', () => {
expect(
recoverGoalFromRecords([
record('legacy', {
subtype: 'slash_command',
systemPayload: {
phase: 'result',
rawCommand: '/goal ship it',
outputHistoryItems: [
{
type: 'goal_status',
kind: 'checking',
condition: 'ship it',
iterations: 19,
setAt: 42,
lastReason: 'old evidence',
},
],
},
}),
]),
).toEqual({ kind: 'legacy', objective: 'ship it' });
});
it('does not revive a stopped legacy Goal', () => {
expect(
recoverGoalFromRecords([
record('legacy', {
subtype: 'slash_command',
systemPayload: {
phase: 'result',
rawCommand: '/goal',
outputHistoryItems: [
{ type: 'goal_status', kind: 'aborted', condition: 'ship it' },
],
},
}),
]),
).toEqual({ kind: 'none' });
});
it('does not revive a paused legacy Goal', () => {
expect(
recoverGoalFromRecords([
record('legacy', {
subtype: 'slash_command',
systemPayload: {
phase: 'result',
rawCommand: '/goal',
outputHistoryItems: [
{ type: 'goal_status', kind: 'paused', condition: 'ship it' },
],
},
}),
]),
).toEqual({ kind: 'none' });
});
});
describe('legacy migration', () => {
it('creates a fresh active payload at the lifecycle record boundary', () => {
expect(
createMigratedGoalState({
objective: 'ship it',
goalId: 'new-goal',
recordUuid: 'migration-record',
now: 1000,
}),
).toEqual({
v: 2,
cause: 'migrated',
snapshot: {
v: 2,
activity: 'idle',
goal: {
goalId: 'new-goal',
revision: 1,
objective: 'ship it',
status: 'active',
evidenceCursor: { recordId: 'migration-record' },
turnCount: 0,
activeTimeMs: 0,
createdAt: 1000,
updatedAt: 1000,
},
},
});
});
it('rejects an empty migrated objective', () => {
expect(() =>
createMigratedGoalState({
objective: ' ',
goalId: 'new-goal',
recordUuid: 'migration-record',
now: 1000,
}),
).toThrow('Migrated Goal objective must not be empty');
});
});

View file

@ -0,0 +1,145 @@
/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import type {
ChatRecord,
SlashCommandRecordPayload,
} from '../services/chatRecordingService.js';
import { parseGoalStateRecordPayloadV2 } from './goal-reducer.js';
import {
GOAL_STATE_VERSION,
type GoalStateRecordPayloadV2,
} from './goal-protocol.js';
export type GoalRecovery =
| { kind: 'v2'; payload: GoalStateRecordPayloadV2 }
| { kind: 'legacy'; objective: string }
| { kind: 'unsupported'; reason: string }
| { kind: 'none' };
export type GoalRecoveryRecord = Pick<ChatRecord, 'uuid' | 'type'> & {
subtype?: string;
systemPayload?: unknown;
};
const LEGACY_ACTIVE_KINDS = new Set(['set', 'checking']);
const LEGACY_STOPPED_KINDS = new Set([
'achieved',
'cleared',
'failed',
'aborted',
'paused',
]);
export function recoverGoalFromRecords(
records: readonly GoalRecoveryRecord[],
): GoalRecovery {
let unsupported: GoalRecovery | undefined;
for (let index = records.length - 1; index >= 0; index -= 1) {
const record = records[index];
if (record?.subtype !== 'goal_state') continue;
const payload =
record.type === 'system'
? parseGoalStateRecordPayloadV2(record.systemPayload)
: undefined;
if (payload) return { kind: 'v2', payload };
unsupported ??= {
kind: 'unsupported',
reason: `Goal lifecycle record ${record.uuid} is malformed or uses an unsupported version`,
};
}
return unsupported ?? recoverLegacyGoal(records);
}
function recoverLegacyGoal(
records: readonly GoalRecoveryRecord[],
): GoalRecovery {
for (
let recordIndex = records.length - 1;
recordIndex >= 0;
recordIndex -= 1
) {
const record = records[recordIndex];
if (record?.type !== 'system' || record.subtype !== 'slash_command') {
continue;
}
const payload = record.systemPayload as
| SlashCommandRecordPayload
| undefined;
if (
payload?.phase !== 'result' ||
!Array.isArray(payload.outputHistoryItems)
) {
continue;
}
for (
let itemIndex = payload.outputHistoryItems.length - 1;
itemIndex >= 0;
itemIndex -= 1
) {
const value: unknown = payload.outputHistoryItems[itemIndex];
if (!isObjectRecord(value) || value['type'] !== 'goal_status') continue;
const kind = value['kind'];
const condition = value['condition'];
if (typeof kind !== 'string' || typeof condition !== 'string') {
return unsupportedLegacy(record.uuid);
}
if (LEGACY_STOPPED_KINDS.has(kind)) return { kind: 'none' };
if (!LEGACY_ACTIVE_KINDS.has(kind) || condition.trim().length === 0) {
return unsupportedLegacy(record.uuid);
}
return { kind: 'legacy', objective: condition.trim() };
}
}
return { kind: 'none' };
}
function unsupportedLegacy(recordUuid: string): GoalRecovery {
return {
kind: 'unsupported',
reason: `Legacy Goal record ${recordUuid} cannot be recovered safely`,
};
}
function isObjectRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
export interface MigratedGoalStateInput {
objective: string;
goalId: string;
recordUuid: string;
now: number;
}
export function createMigratedGoalState(
input: MigratedGoalStateInput,
): GoalStateRecordPayloadV2 {
const objective = input.objective.trim();
if (!objective) {
throw new Error('Migrated Goal objective must not be empty');
}
return {
v: GOAL_STATE_VERSION,
cause: 'migrated',
snapshot: {
v: GOAL_STATE_VERSION,
activity: 'idle',
goal: {
goalId: input.goalId,
revision: 1,
objective,
status: 'active',
evidenceCursor: { recordId: input.recordUuid },
turnCount: 0,
activeTimeMs: 0,
createdAt: input.now,
updatedAt: input.now,
},
},
};
}

View file

@ -0,0 +1,126 @@
/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
export const GOAL_STATE_VERSION = 2 as const;
export const PAUSED_GOAL_SYSTEM_REMINDER =
'<system-reminder>\nThe Goal is paused. Do not continue its objective unless the user resumes it. Treat this message as ordinary conversation.\n</system-reminder>';
export type GoalStatus =
| 'active'
| 'paused'
| 'blocked'
| 'usage_limited'
| 'complete';
export type GoalActivity = 'idle' | 'running' | 'verifying';
export interface TranscriptCursor {
recordId: string | null;
}
export interface GoalExpectedVersion {
goalId: string;
revision: number;
}
export interface GoalTurnPermit extends GoalExpectedVersion {
turnId: string;
}
export interface GoalRecord {
goalId: string;
revision: number;
objective: string;
status: GoalStatus;
evidenceCursor: TranscriptCursor;
turnCount: number;
activeTimeMs: number;
createdAt: number;
updatedAt: number;
lastReason?: string;
}
export interface GoalSnapshotV2 {
v: typeof GOAL_STATE_VERSION;
goal: GoalRecord | null;
activity: GoalActivity;
}
/** True while any new model send must carry the runtime's exact turn permit. */
export function goalRequiresExactPermit(snapshot: GoalSnapshotV2): boolean {
return (
snapshot.goal !== null &&
(snapshot.goal.status === 'active' || snapshot.activity === 'running')
);
}
export type GoalControlRequest =
| { action: 'create'; objective: string }
| {
action: 'replace';
objective: string;
expectedGoalId: string;
expectedRevision: number;
}
| {
action: 'edit';
objective: string;
expectedGoalId: string;
expectedRevision: number;
}
| {
action: 'pause';
expectedGoalId: string;
expectedRevision: number;
}
| {
action: 'resume';
expectedGoalId: string;
expectedRevision: number;
}
| {
action: 'clear';
expectedGoalId: string;
expectedRevision: number;
};
export interface GoalStateResponse {
snapshot: GoalSnapshotV2;
}
export interface GoalTerminalProposal {
status: 'complete' | 'blocked';
reason: string;
evidenceRefs: string[];
blockerKind?: 'authority' | 'external' | 'repeated';
}
export type GoalStateCause =
| 'create'
| 'replace'
| 'edit'
| 'pause'
| 'resume'
| 'turn_finished'
| 'verifier_accept'
| 'verifier_reject'
| 'complete'
| 'blocked'
| 'usage_limited'
| 'clear'
| 'migrated';
export interface GoalStateRecordPayloadV2 {
v: typeof GOAL_STATE_VERSION;
cause: GoalStateCause;
snapshot: GoalSnapshotV2;
blockedAudit?: {
fingerprint: string;
count: number;
turnIds: string[];
};
}

View file

@ -0,0 +1,505 @@
/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, expect, it } from 'vitest';
import {
goalRequiresExactPermit,
type GoalControlRequest,
type GoalRecord,
type GoalSnapshotV2,
} from './goal-protocol.js';
import {
GoalConflictError,
GoalInvalidTransitionError,
elapsedActiveTime,
parseGoalControlRequest,
parseGoalSnapshotV2,
parseGoalStateRecordPayloadV2,
reduceGoalControl,
reduceGoalTurnFinished,
} from './goal-reducer.js';
const goalRecord = (overrides: Partial<GoalRecord> = {}): GoalRecord => ({
goalId: 'g-1',
revision: 1,
objective: 'ship',
status: 'active',
evidenceCursor: { recordId: 'r-100' },
turnCount: 0,
activeTimeMs: 0,
createdAt: 100,
updatedAt: 100,
...overrides,
});
const snapshot = (goal: GoalRecord | null): GoalSnapshotV2 => ({
v: 2,
goal,
activity: 'idle',
});
describe('goal reducer', () => {
it('replaces the same objective with a fresh identity and cursor', () => {
const previous = goalRecord({ goalId: 'g-1', objective: 'ship' });
const next = reduceGoalControl(previous, {
request: {
action: 'replace',
objective: 'ship',
expectedGoalId: 'g-1',
expectedRevision: 1,
},
now: 200,
nextGoalId: 'g-2',
cursor: { recordId: 'r-200' },
});
expect(next).toMatchObject({
goalId: 'g-2',
revision: 1,
objective: 'ship',
status: 'active',
evidenceCursor: { recordId: 'r-200' },
turnCount: 0,
});
});
it('edits in place and rejects evidence from the previous revision', () => {
const previous = goalRecord({ goalId: 'g-1', revision: 4 });
const next = reduceGoalControl(previous, {
request: {
action: 'edit',
objective: 'new objective',
expectedGoalId: 'g-1',
expectedRevision: 4,
},
now: 300,
nextGoalId: 'unused',
cursor: { recordId: 'r-300' },
});
expect(next).toMatchObject({
goalId: 'g-1',
revision: 5,
objective: 'new objective',
evidenceCursor: { recordId: 'r-300' },
});
});
it('creates a trimmed active goal only when no goal exists', () => {
const next = reduceGoalControl(null, {
request: { action: 'create', objective: ' ship ' },
now: 100,
nextGoalId: 'g-1',
cursor: { recordId: 'r-100' },
});
expect(next).toEqual(goalRecord());
expect(() =>
reduceGoalControl(next, {
request: { action: 'create', objective: 'another' },
now: 200,
nextGoalId: 'g-2',
cursor: { recordId: 'r-200' },
}),
).toThrow(GoalConflictError);
});
it('rejects empty objectives', () => {
expect(() =>
reduceGoalControl(null, {
request: { action: 'create', objective: ' \n ' },
now: 100,
nextGoalId: 'g-1',
cursor: { recordId: 'r-100' },
}),
).toThrow(GoalInvalidTransitionError);
});
it('returns the current snapshot for stale identity and revision', () => {
const previous = goalRecord({ revision: 4 });
for (const request of [
{
action: 'pause' as const,
expectedGoalId: 'g-other',
expectedRevision: 4,
},
{
action: 'pause' as const,
expectedGoalId: 'g-1',
expectedRevision: 3,
},
]) {
try {
reduceGoalControl(previous, {
request,
now: 200,
nextGoalId: 'unused',
cursor: { recordId: 'r-200' },
});
throw new Error('expected conflict');
} catch (error) {
expect(error).toBeInstanceOf(GoalConflictError);
expect((error as GoalConflictError).current).toEqual(
snapshot(previous),
);
}
}
});
it('pauses and resumes without changing revision or evidence cursor', () => {
const paused = reduceGoalControl(goalRecord(), {
request: {
action: 'pause',
expectedGoalId: 'g-1',
expectedRevision: 1,
},
now: 150,
nextGoalId: 'unused',
cursor: { recordId: 'r-150' },
});
const resumed = reduceGoalControl(paused, {
request: {
action: 'resume',
expectedGoalId: 'g-1',
expectedRevision: 1,
},
now: 200,
nextGoalId: 'unused',
cursor: { recordId: 'r-200' },
});
expect(paused).toMatchObject({
status: 'paused',
revision: 1,
evidenceCursor: { recordId: 'r-100' },
});
expect(resumed).toMatchObject({
status: 'active',
revision: 1,
evidenceCursor: { recordId: 'r-100' },
});
});
it('rejects resuming an already-active goal', () => {
expect(() =>
reduceGoalControl(goalRecord(), {
request: {
action: 'resume',
expectedGoalId: 'g-1',
expectedRevision: 1,
},
now: 200,
nextGoalId: 'unused',
cursor: { recordId: 'r-200' },
}),
).toThrow(GoalInvalidTransitionError);
});
it.each(['blocked', 'usage_limited'] as const)(
'resumes a %s goal without changing revision or evidence cursor',
(status) => {
const resumed = reduceGoalControl(goalRecord({ status, revision: 4 }), {
request: {
action: 'resume',
expectedGoalId: 'g-1',
expectedRevision: 4,
},
now: 200,
nextGoalId: 'unused',
cursor: { recordId: 'r-200' },
});
expect(resumed).toMatchObject({
status: 'active',
revision: 4,
evidenceCursor: { recordId: 'r-100' },
});
},
);
it('rejects an unsupported control action instead of resuming', () => {
expect(() =>
reduceGoalControl(goalRecord({ status: 'paused' }), {
request: {
action: 'archive',
expectedGoalId: 'g-1',
expectedRevision: 1,
} as unknown as GoalControlRequest,
now: 200,
nextGoalId: 'unused',
cursor: { recordId: 'r-200' },
}),
).toThrow(GoalInvalidTransitionError);
});
it.each(['paused', 'blocked', 'usage_limited'] as const)(
'edits a %s goal without changing its status',
(status) => {
const next = reduceGoalControl(goalRecord({ status, revision: 4 }), {
request: {
action: 'edit',
objective: 'new objective',
expectedGoalId: 'g-1',
expectedRevision: 4,
},
now: 300,
nextGoalId: 'unused',
cursor: { recordId: 'r-300' },
});
expect(next).toMatchObject({ status, revision: 5 });
},
);
it('rejects editing or resuming a completed goal', () => {
const complete = goalRecord({ status: 'complete' });
for (const request of [
{
action: 'edit' as const,
objective: 'new objective',
expectedGoalId: 'g-1',
expectedRevision: 1,
},
{
action: 'resume' as const,
expectedGoalId: 'g-1',
expectedRevision: 1,
},
]) {
expect(() =>
reduceGoalControl(complete, {
request,
now: 200,
nextGoalId: 'unused',
cursor: { recordId: 'r-200' },
}),
).toThrow(GoalInvalidTransitionError);
}
});
it('clears a matching goal', () => {
expect(
reduceGoalControl(goalRecord(), {
request: {
action: 'clear',
expectedGoalId: 'g-1',
expectedRevision: 1,
},
now: 200,
nextGoalId: 'unused',
cursor: { recordId: 'r-200' },
}),
).toBeNull();
});
it('folds active elapsed time before each persisted transition', () => {
const paused = reduceGoalControl(goalRecord(), {
request: {
action: 'pause',
expectedGoalId: 'g-1',
expectedRevision: 1,
},
now: 160,
nextGoalId: 'unused',
cursor: { recordId: 'r-160' },
});
const resumed = reduceGoalControl(paused, {
request: {
action: 'resume',
expectedGoalId: 'g-1',
expectedRevision: 1,
},
now: 250,
nextGoalId: 'unused',
cursor: { recordId: 'r-250' },
});
const pausedAgain = reduceGoalControl(resumed, {
request: {
action: 'pause',
expectedGoalId: 'g-1',
expectedRevision: 1,
},
now: 275,
nextGoalId: 'unused',
cursor: { recordId: 'r-275' },
});
expect(paused?.activeTimeMs).toBe(60);
expect(resumed?.activeTimeMs).toBe(60);
expect(pausedAgain?.activeTimeMs).toBe(85);
expect(elapsedActiveTime(resumed!, 275)).toBe(85);
});
it('never derives a terminal status from turn count or elapsed time', () => {
let goal = goalRecord();
for (let turn = 1; turn <= 150; turn += 1) {
goal = reduceGoalTurnFinished(goal, {
now: 100 + turn,
});
}
expect(goal).toMatchObject({
status: 'active',
revision: 1,
turnCount: 150,
activeTimeMs: 150,
evidenceCursor: { recordId: 'r-100' },
});
});
it('finishes an in-flight turn after pause without resuming active time', () => {
const paused = goalRecord({
revision: 4,
status: 'paused',
turnCount: 2,
activeTimeMs: 60,
updatedAt: 160,
});
const finished = reduceGoalTurnFinished(paused, { now: 225 });
expect(finished).toMatchObject({
status: 'paused',
revision: 4,
evidenceCursor: { recordId: 'r-100' },
turnCount: 3,
activeTimeMs: 60,
updatedAt: 225,
});
});
it.each(['blocked', 'usage_limited', 'complete'] as const)(
'rejects finishing a turn for a %s goal',
(status) => {
expect(() =>
reduceGoalTurnFinished(goalRecord({ status }), { now: 200 }),
).toThrow(GoalInvalidTransitionError);
},
);
it.each([
[null, 'idle', false],
[goalRecord(), 'idle', true],
[goalRecord({ status: 'paused' }), 'idle', false],
[goalRecord({ status: 'paused' }), 'running', true],
] as const)(
'requires an exact permit for the matching goal and activity state',
(goal, activity, expected) => {
expect(goalRequiresExactPermit({ ...snapshot(goal), activity })).toBe(
expected,
);
},
);
it('strictly parses persisted idle goal snapshots and control requests', () => {
const record = goalRecord();
expect(
parseGoalStateRecordPayloadV2({
v: 2,
cause: 'create',
snapshot: snapshot(record),
}),
).toEqual({ v: 2, cause: 'create', snapshot: snapshot(record) });
expect(
parseGoalStateRecordPayloadV2({
v: 2,
cause: 'create',
snapshot: { ...snapshot(record), activity: 'running' },
}),
).toBeUndefined();
expect(
parseGoalControlRequest({ action: 'create', objective: 'ship' }),
).toEqual({
action: 'create',
objective: 'ship',
});
expect(
parseGoalControlRequest({
action: 'edit',
objective: ' ',
expectedGoalId: 'g-1',
expectedRevision: 1,
}),
).toBeUndefined();
expect(
parseGoalControlRequest({
action: 'pause',
expectedGoalId: 'g-1',
}),
).toBeUndefined();
expect(
parseGoalControlRequest({
action: 'pause',
expectedGoalId: 'g-1',
expectedRevision: 0,
}),
).toBeUndefined();
});
it.each(['idle', 'running', 'verifying'] as const)(
'parses %s activity in public wire snapshots',
(activity) => {
const value = { ...snapshot(goalRecord()), activity };
expect(parseGoalSnapshotV2(value)).toEqual(value);
},
);
it.each([
['zero count', { fingerprint: 'same', count: 0, turnIds: [] }],
[
'count above the blocker threshold',
{
fingerprint: 'same',
count: 4,
turnIds: ['turn-1', 'turn-2', 'turn-3', 'turn-4'],
},
],
[
'count and turn ID mismatch',
{ fingerprint: 'same', count: 2, turnIds: ['turn-1'] },
],
['empty fingerprint', { fingerprint: '', count: 1, turnIds: ['turn-1'] }],
['empty turn ID', { fingerprint: 'same', count: 1, turnIds: [''] }],
[
'extra key',
{
fingerprint: 'same',
count: 1,
turnIds: ['turn-1'],
unexpected: true,
},
],
])('rejects a blocked audit with %s', (_label, blockedAudit) => {
expect(
parseGoalStateRecordPayloadV2({
v: 2,
cause: 'turn_finished',
snapshot: snapshot(goalRecord()),
blockedAudit,
}),
).toBeUndefined();
});
it('parses and clones a valid blocked audit', () => {
const blockedAudit = {
fingerprint: 'same',
count: 2,
turnIds: ['turn-1', 'turn-2'],
};
const parsed = parseGoalStateRecordPayloadV2({
v: 2,
cause: 'turn_finished',
snapshot: snapshot(goalRecord()),
blockedAudit,
});
expect(parsed?.blockedAudit).toEqual(blockedAudit);
expect(parsed?.blockedAudit).not.toBe(blockedAudit);
});
});

View file

@ -0,0 +1,485 @@
/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import {
GOAL_STATE_VERSION,
type GoalControlRequest,
type GoalRecord,
type GoalSnapshotV2,
type GoalStateCause,
type GoalStateRecordPayloadV2,
type GoalStatus,
type TranscriptCursor,
} from './goal-protocol.js';
const MAX_BLOCKED_AUDIT_COUNT = 3;
export interface GoalControlTransition {
request: GoalControlRequest;
now: number;
nextGoalId: string;
cursor: TranscriptCursor;
}
export interface GoalTurnFinishedTransition {
now: number;
lastReason?: string;
}
export class GoalConflictError extends Error {
constructor(readonly current: GoalSnapshotV2) {
super('Goal version does not match the current session Goal');
this.name = 'GoalConflictError';
}
}
export class GoalInvalidTransitionError extends Error {
constructor(
message: string,
readonly current: GoalSnapshotV2,
) {
super(message);
this.name = 'GoalInvalidTransitionError';
}
}
export function elapsedActiveTime(goal: GoalRecord, now: number): number {
return (
goal.activeTimeMs +
(goal.status === 'active' ? Math.max(0, now - goal.updatedAt) : 0)
);
}
export function reduceGoalControl(
current: GoalRecord | null,
transition: GoalControlTransition,
): GoalRecord | null {
const { request } = transition;
if (request.action === 'create') {
if (current) throw new GoalConflictError(snapshotOf(current));
return createGoal(
transition.nextGoalId,
normalizeObjective(request.objective, snapshotOf(null)),
transition.now,
transition.cursor,
);
}
assertExpectedVersion(
current,
request.expectedGoalId,
request.expectedRevision,
);
if (request.action === 'clear') return null;
if (request.action === 'replace') {
return createGoal(
transition.nextGoalId,
normalizeObjective(request.objective, snapshotOf(current)),
transition.now,
transition.cursor,
);
}
if (request.action === 'edit') {
if (current.status === 'complete') {
throw new GoalInvalidTransitionError(
'A completed Goal cannot be edited',
snapshotOf(current),
);
}
return transitionGoal(current, transition.now, {
revision: current.revision + 1,
objective: normalizeObjective(request.objective, snapshotOf(current)),
evidenceCursor: copyCursor(transition.cursor),
});
}
if (request.action === 'pause') {
if (current.status !== 'active') {
throw new GoalInvalidTransitionError(
'Only an active Goal can be paused',
snapshotOf(current),
);
}
return transitionGoal(current, transition.now, { status: 'paused' });
}
if (current.status === 'complete') {
throw new GoalInvalidTransitionError(
'A completed Goal cannot be resumed',
snapshotOf(current),
);
}
if (current.status === 'active') {
throw new GoalInvalidTransitionError(
'An active Goal cannot be resumed',
snapshotOf(current),
);
}
if (request.action !== 'resume') {
return assertNever(request, snapshotOf(current));
}
return transitionGoal(current, transition.now, { status: 'active' });
}
export function reduceGoalTurnFinished(
current: GoalRecord,
transition: GoalTurnFinishedTransition,
): GoalRecord {
if (current.status !== 'active' && current.status !== 'paused') {
throw new GoalInvalidTransitionError(
'Only an active or paused Goal can finish a turn',
snapshotOf(current),
);
}
return transitionGoal(current, transition.now, {
turnCount: current.turnCount + 1,
...(transition.lastReason === undefined
? {}
: { lastReason: transition.lastReason }),
});
}
export function parseGoalControlRequest(
value: unknown,
): GoalControlRequest | undefined {
if (!isRecord(value) || typeof value['action'] !== 'string') {
return undefined;
}
switch (value['action']) {
case 'create':
if (!hasOnlyKeys(value, ['action', 'objective'])) return undefined;
return typeof value['objective'] === 'string'
? parseObjectiveRequest(value['action'], value['objective'])
: undefined;
case 'replace':
case 'edit':
if (
!hasOnlyKeys(value, [
'action',
'objective',
'expectedGoalId',
'expectedRevision',
]) ||
typeof value['objective'] !== 'string' ||
!isExpectedVersion(value)
) {
return undefined;
}
return parseObjectiveVersionedRequest(
value['action'],
value['objective'],
value['expectedGoalId'],
value['expectedRevision'],
);
case 'pause':
case 'resume':
case 'clear':
if (
!hasOnlyKeys(value, ['action', 'expectedGoalId', 'expectedRevision']) ||
!isExpectedVersion(value)
) {
return undefined;
}
return {
action: value['action'],
expectedGoalId: value['expectedGoalId'],
expectedRevision: value['expectedRevision'],
};
default:
return undefined;
}
}
export function parseGoalStateRecordPayloadV2(
value: unknown,
): GoalStateRecordPayloadV2 | undefined {
if (
!isRecord(value) ||
!hasOnlyKeys(value, ['v', 'cause', 'snapshot', 'blockedAudit']) ||
value['v'] !== GOAL_STATE_VERSION ||
!isGoalStateCause(value['cause']) ||
!isBlockedAudit(value['blockedAudit'])
) {
return undefined;
}
const parsedSnapshot = parseGoalSnapshotV2(value['snapshot']);
return parsedSnapshot?.activity === 'idle'
? {
v: GOAL_STATE_VERSION,
cause: value['cause'],
snapshot: parsedSnapshot,
...(value['blockedAudit']
? { blockedAudit: structuredClone(value['blockedAudit']) }
: {}),
}
: undefined;
}
export function parseGoalSnapshotV2(
value: unknown,
): GoalSnapshotV2 | undefined {
if (
!isRecord(value) ||
!hasOnlyKeys(value, ['v', 'goal', 'activity']) ||
value['v'] !== GOAL_STATE_VERSION ||
!isGoalActivity(value['activity'])
) {
return undefined;
}
if (value['goal'] === null) {
return {
v: GOAL_STATE_VERSION,
goal: null,
activity: value['activity'],
};
}
const goal = parseGoalRecord(value['goal']);
return goal
? { v: GOAL_STATE_VERSION, goal, activity: value['activity'] }
: undefined;
}
function createGoal(
goalId: string,
objective: string,
now: number,
cursor: TranscriptCursor,
): GoalRecord {
return {
goalId,
revision: 1,
objective,
status: 'active',
evidenceCursor: copyCursor(cursor),
turnCount: 0,
activeTimeMs: 0,
createdAt: now,
updatedAt: now,
};
}
function assertExpectedVersion(
current: GoalRecord | null,
expectedGoalId: string,
expectedRevision: number,
): asserts current is GoalRecord {
if (
!current ||
current.goalId !== expectedGoalId ||
current.revision !== expectedRevision
) {
throw new GoalConflictError(snapshotOf(current));
}
}
function normalizeObjective(
objective: string,
current: GoalSnapshotV2,
): string {
const normalized = objective.trim();
if (!normalized) {
throw new GoalInvalidTransitionError(
'Goal objective must not be empty',
current,
);
}
return normalized;
}
function transitionGoal(
goal: GoalRecord,
now: number,
changes: Partial<GoalRecord>,
): GoalRecord {
return {
...goal,
...changes,
activeTimeMs: elapsedActiveTime(goal, now),
updatedAt: now,
};
}
function snapshotOf(goal: GoalRecord | null): GoalSnapshotV2 {
return { v: GOAL_STATE_VERSION, goal, activity: 'idle' };
}
function copyCursor(cursor: TranscriptCursor): TranscriptCursor {
return { recordId: cursor.recordId };
}
function parseObjectiveRequest(
action: 'create',
objective: string,
): GoalControlRequest | undefined {
const normalized = objective.trim();
return normalized ? { action, objective: normalized } : undefined;
}
function parseObjectiveVersionedRequest(
action: 'replace' | 'edit',
objective: string,
expectedGoalId: string,
expectedRevision: number,
): GoalControlRequest | undefined {
const normalized = objective.trim();
return normalized
? { action, objective: normalized, expectedGoalId, expectedRevision }
: undefined;
}
function parseGoalRecord(value: unknown): GoalRecord | undefined {
if (
!isRecord(value) ||
!hasOnlyKeys(value, [
'goalId',
'revision',
'objective',
'status',
'evidenceCursor',
'turnCount',
'activeTimeMs',
'createdAt',
'updatedAt',
'lastReason',
]) ||
typeof value['goalId'] !== 'string' ||
!value['goalId'] ||
!isNonNegativeInteger(value['revision']) ||
value['revision'] === 0 ||
typeof value['objective'] !== 'string' ||
!value['objective'].trim() ||
!isGoalStatus(value['status']) ||
!isTranscriptCursor(value['evidenceCursor']) ||
!isNonNegativeInteger(value['turnCount']) ||
!isNonNegativeNumber(value['activeTimeMs']) ||
!isFiniteNumber(value['createdAt']) ||
!isFiniteNumber(value['updatedAt']) ||
(value['lastReason'] !== undefined &&
typeof value['lastReason'] !== 'string')
) {
return undefined;
}
return {
goalId: value['goalId'],
revision: value['revision'],
objective: value['objective'],
status: value['status'],
evidenceCursor: copyCursor(value['evidenceCursor']),
turnCount: value['turnCount'],
activeTimeMs: value['activeTimeMs'],
createdAt: value['createdAt'],
updatedAt: value['updatedAt'],
...(value['lastReason'] === undefined
? {}
: { lastReason: value['lastReason'] }),
};
}
function isExpectedVersion(value: Record<string, unknown>): value is Record<
string,
unknown
> & {
expectedGoalId: string;
expectedRevision: number;
} {
return (
typeof value['expectedGoalId'] === 'string' &&
value['expectedGoalId'].length > 0 &&
isNonNegativeInteger(value['expectedRevision']) &&
value['expectedRevision'] > 0
);
}
function isTranscriptCursor(value: unknown): value is TranscriptCursor {
return (
isRecord(value) &&
hasOnlyKeys(value, ['recordId']) &&
(typeof value['recordId'] === 'string' || value['recordId'] === null)
);
}
function isGoalStatus(value: unknown): value is GoalStatus {
return (
value === 'active' ||
value === 'paused' ||
value === 'blocked' ||
value === 'usage_limited' ||
value === 'complete'
);
}
function isGoalActivity(value: unknown): value is GoalSnapshotV2['activity'] {
return value === 'idle' || value === 'running' || value === 'verifying';
}
function isGoalStateCause(value: unknown): value is GoalStateCause {
return (
value === 'create' ||
value === 'replace' ||
value === 'edit' ||
value === 'pause' ||
value === 'resume' ||
value === 'turn_finished' ||
value === 'verifier_accept' ||
value === 'verifier_reject' ||
value === 'complete' ||
value === 'blocked' ||
value === 'usage_limited' ||
value === 'clear' ||
value === 'migrated'
);
}
function isBlockedAudit(
value: unknown,
): value is GoalStateRecordPayloadV2['blockedAudit'] {
return (
value === undefined ||
(isRecord(value) &&
hasOnlyKeys(value, ['fingerprint', 'count', 'turnIds']) &&
typeof value['fingerprint'] === 'string' &&
value['fingerprint'].length > 0 &&
isNonNegativeInteger(value['count']) &&
value['count'] > 0 &&
value['count'] <= MAX_BLOCKED_AUDIT_COUNT &&
Array.isArray(value['turnIds']) &&
value['turnIds'].length === value['count'] &&
value['turnIds'].every(
(turnId) => typeof turnId === 'string' && turnId.length > 0,
))
);
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function hasOnlyKeys(value: Record<string, unknown>, keys: string[]): boolean {
return Object.keys(value).every((key) => keys.includes(key));
}
function isNonNegativeInteger(value: unknown): value is number {
return typeof value === 'number' && Number.isInteger(value) && value >= 0;
}
function isNonNegativeNumber(value: unknown): value is number {
return typeof value === 'number' && Number.isFinite(value) && value >= 0;
}
function isFiniteNumber(value: unknown): value is number {
return typeof value === 'number' && Number.isFinite(value);
}
function assertNever(value: never, snapshot: GoalSnapshotV2): never {
throw new GoalInvalidTransitionError(
`Unsupported Goal control action: ${String(value)}`,
snapshot,
);
}

View file

@ -0,0 +1,34 @@
/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
export {
GOAL_STATE_VERSION,
goalRequiresExactPermit,
type GoalActivity,
type GoalControlRequest,
type GoalExpectedVersion,
type GoalRecord,
type GoalSnapshotV2,
type GoalStateCause,
type GoalStateRecordPayloadV2,
type GoalStateResponse,
type GoalStatus,
type GoalTerminalProposal,
type GoalTurnPermit,
type TranscriptCursor,
} from './goal-protocol.js';
export {
parseGoalSnapshotV2,
parseGoalStateRecordPayloadV2,
} from './goal-reducer.js';
export {
projectGoalStateToLegacy,
type LegacyActiveGoal,
type LegacyGoalProjection,
type LegacyGoalStatus,
type LegacyGoalStatusKind,
type LegacyGoalTerminal,
} from './goal-legacy-projection.js';

View file

@ -35,3 +35,27 @@ export {
} from './goalHook.js';
export { judgeGoal } from './goalJudge.js';
export type { GoalJudgeOutcome, JudgeResult } from './goalJudge.js';
export * from './goal-protocol.js';
export {
GoalConflictError,
GoalInvalidTransitionError,
elapsedActiveTime,
parseGoalControlRequest,
parseGoalSnapshotV2,
parseGoalStateRecordPayloadV2,
reduceGoalControl,
reduceGoalTurnFinished,
} from './goal-reducer.js';
export type {
GoalControlTransition,
GoalTurnFinishedTransition,
} from './goal-reducer.js';
export * from './goal-persistence.js';
export { projectGoalStateToLegacy } from './goal-legacy-projection.js';
export type {
LegacyActiveGoal,
LegacyGoalProjection,
LegacyGoalStatus,
LegacyGoalStatusKind,
LegacyGoalTerminal,
} from './goal-legacy-projection.js';