Skip to content

Commit 0ee25cd

Browse files
committed
test(subagent): fold a due cache ttl on an in-window stall ping
## Summary An in-window empty stall ping whose provider cache TTL is due returns a cache-ttl-recompress compact and a continuation. It does not infer or restart the silence clock. The governor reads the director clock so due-ness and the stall window share one timeline. ## Verification - bun test ./src/subagent/nudge-director.test.ts (49 pass, exit 0) Refs CL-8912
1 parent 85ae977 commit 0ee25cd

2 files changed

Lines changed: 59 additions & 0 deletions

File tree

‎src/subagent/nudge-director.test.ts‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,6 +1665,64 @@ describe("SubAgentDirector idle stall ping", () => {
16651665
expect(folded.some((action) => action.type === "wait")).toBe(false);
16661666
});
16671667

1668+
test("cache-ttl recompress still folds on an in-window empty ping", async () => {
1669+
// test-model takes the 10-minute default TTL. The stall window is longer
1670+
// so the due fold is still an in-window ping, not a stall nudge.
1671+
const activityAt = 11_000_000;
1672+
const cacheTtlMs = 10 * 60_000;
1673+
const stallTimeoutMs = 15 * 60_000;
1674+
let now = activityAt;
1675+
let continuations = 0;
1676+
const director = new SubAgentDirector(
1677+
"system",
1678+
[],
1679+
() => {
1680+
continuations++;
1681+
},
1682+
stallTimeoutMs,
1683+
() => now,
1684+
);
1685+
const caps = createTestCapabilities();
1686+
1687+
await director.decide(inferenceDoneText("working"), longState, caps);
1688+
1689+
now += cacheTtlMs + 1;
1690+
const folded = actions(
1691+
await director.decide(messageReceived(""), longState, caps),
1692+
);
1693+
expect(folded).toEqual([
1694+
{
1695+
type: "compact",
1696+
compactor: "pruning-compactor",
1697+
reason: "cache-ttl-recompress",
1698+
},
1699+
]);
1700+
expect(continuations).toBe(1);
1701+
expect(folded.some((action) => action.type === "infer")).toBe(false);
1702+
expect(folded.some((action) => action.type === "wait")).toBe(false);
1703+
1704+
// Meter-only resume of the empty fold. Same clock: still inside the
1705+
// stall window, and this wait must not count as activity either.
1706+
const resumed = actions(
1707+
await director.decide(messageReceived(""), longState, caps),
1708+
);
1709+
expect(resumed).toEqual([{ type: "wait" }]);
1710+
expect(resumed.some((action) => action.type === "infer")).toBe(false);
1711+
1712+
// The fold must not stamp lastActivityAt or clear stallNudgeAt. One
1713+
// stall timeout from the original activity still nudges, once.
1714+
now = activityAt + stallTimeoutMs;
1715+
const nudge = actions(
1716+
await director.decide(messageReceived(""), longState, caps),
1717+
);
1718+
expect(nudge).toContainEqual({
1719+
type: "checkpoint",
1720+
message: "subagent-stall-nudge",
1721+
});
1722+
expect(ephemeralTexts(inferAction(nudge))).toEqual([STALL_NUDGE_TEXT]);
1723+
expect(nudge.some((action) => action.type === "wait")).toBe(false);
1724+
});
1725+
16681726
test("no stall timeout waits on an unsolicited empty continuation", async () => {
16691727
const director = new SubAgentDirector("system", [], undefined);
16701728
const caps = createTestCapabilities();

‎src/subagent/nudge-director.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ export class SubAgentDirector extends DefaultDirector {
265265
requestContinuation,
266266
composedPrompt,
267267
toolDefinitions,
268+
now,
268269
);
269270
this.stallTimeoutMs = stallTimeoutMs;
270271
this.now = now;

0 commit comments

Comments
 (0)