fix(session): prevent goal mode pause during compaction/switch

This commit is contained in:
usr_bin_roygbiv
2026-06-18 10:30:40 -05:00
parent 2023a6f674
commit c16a41fd95
5 changed files with 105 additions and 12 deletions
@@ -80,6 +80,9 @@ function createHarness(initial: { state?: GoalModeState; usage?: GoalTokenUsage;
return {
runtime: new GoalRuntime(host),
getState: () => cloneState(state),
setState: (next: GoalModeState | undefined) => {
state = cloneState(next);
},
setUsage: (next: Partial<GoalTokenUsage>) => {
usage = createUsage(next);
},
@@ -153,6 +156,65 @@ describe("goal runtime", () => {
expect(harness.persists).toHaveLength(0);
});
it("persists wall-clock-only usage before internal compaction or session-switch aborts", async () => {
const harness = createHarness({
state: { enabled: true, mode: "active", goal: createGoal() },
});
harness.runtime.onTurnStart("turn-1", createUsage());
harness.advance(2_500);
await harness.runtime.onTaskAborted({ reason: "internal" });
expect(harness.getState()?.enabled).toBe(true);
expect(harness.getState()?.goal.status).toBe("active");
expect(harness.getState()?.goal.timeUsedSeconds).toBe(2);
expect(harness.persists).toHaveLength(1);
expect(harness.persists[0]).toMatchObject({
mode: "goal",
state: { goal: { timeUsedSeconds: 2 } },
});
});
it("resets wall-clock baseline when preserving an active goal after a no-goal switch", async () => {
const goal = createGoal();
const harness = createHarness({
state: { enabled: true, mode: "active", goal },
});
harness.runtime.onTurnStart("turn-1", createUsage());
harness.setState(undefined);
harness.advance(10_000);
harness.setState({ enabled: true, mode: "active", goal });
const resumed = await harness.runtime.onThreadResumed({ preserveActiveGoal: true });
harness.advance(1_000);
await harness.runtime.flushUsage("suppressed");
expect(resumed?.goal.status).toBe("active");
expect(harness.getState()?.goal.timeUsedSeconds).toBe(1);
expect(harness.runtime.snapshot.wallClock.lastAccountedAt).toBe(11_000);
});
it("clears stale accounting when reconciling to a no-goal session", async () => {
const goal = createGoal();
const harness = createHarness({
state: { enabled: true, mode: "active", goal },
});
harness.runtime.onTurnStart("turn-1", createUsage());
harness.setState(undefined);
harness.runtime.clearAccounting();
harness.advance(10_000);
harness.setState({ enabled: true, mode: "active", goal });
await harness.runtime.onThreadResumed({ preserveActiveGoal: true });
harness.advance(1_000);
await harness.runtime.flushUsage("suppressed");
expect(harness.getState()?.goal.timeUsedSeconds).toBe(1);
expect(harness.runtime.snapshot.wallClock.lastAccountedAt).toBe(11_000);
});
it("steers only once until a budget mutation resets the cycle", async () => {
const harness = createHarness({
state: {
@@ -219,6 +281,20 @@ describe("goal runtime", () => {
expect(harness.persists.at(-1)?.mode).toBe("goal_paused");
});
it("preserves an active goal during internal session-switch reconciliation", async () => {
const harness = createHarness({
state: { enabled: true, mode: "active", goal: createGoal() },
});
const resumed = await harness.runtime.onThreadResumed({ preserveActiveGoal: true });
expect(resumed?.enabled).toBe(true);
expect(resumed?.goal.status).toBe("active");
expect(harness.getState()?.enabled).toBe(true);
expect(harness.getState()?.goal.status).toBe("active");
expect(harness.persists).toHaveLength(0);
});
it("escapes XML in goal helpers and rendered prompts", () => {
const objective = "Fix <root>&keep>safe";
const goal = createGoal({ objective });