fix(todo-continuation-enforcer): track continuation stagnation state
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,49 @@
|
|||||||
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test"
|
||||||
|
|
||||||
|
import { createSessionStateStore, type SessionStateStore } from "./session-state"
|
||||||
|
|
||||||
|
describe("createSessionStateStore", () => {
|
||||||
|
let sessionStateStore: SessionStateStore
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
sessionStateStore = createSessionStateStore()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
sessionStateStore.shutdown()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("given repeated incomplete counts after a continuation, tracks stagnation", () => {
|
||||||
|
// given
|
||||||
|
const sessionID = "ses-stagnation"
|
||||||
|
const state = sessionStateStore.getState(sessionID)
|
||||||
|
state.lastInjectedAt = Date.now()
|
||||||
|
|
||||||
|
// when
|
||||||
|
const firstUpdate = sessionStateStore.trackContinuationProgress(sessionID, 2)
|
||||||
|
const secondUpdate = sessionStateStore.trackContinuationProgress(sessionID, 2)
|
||||||
|
const thirdUpdate = sessionStateStore.trackContinuationProgress(sessionID, 2)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(firstUpdate.stagnationCount).toBe(0)
|
||||||
|
expect(secondUpdate.stagnationCount).toBe(1)
|
||||||
|
expect(thirdUpdate.stagnationCount).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("given incomplete count decreases, resets stagnation tracking", () => {
|
||||||
|
// given
|
||||||
|
const sessionID = "ses-progress-reset"
|
||||||
|
const state = sessionStateStore.getState(sessionID)
|
||||||
|
state.lastInjectedAt = Date.now()
|
||||||
|
sessionStateStore.trackContinuationProgress(sessionID, 3)
|
||||||
|
sessionStateStore.trackContinuationProgress(sessionID, 3)
|
||||||
|
|
||||||
|
// when
|
||||||
|
const progressUpdate = sessionStateStore.trackContinuationProgress(sessionID, 2)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(progressUpdate.hasProgressed).toBe(true)
|
||||||
|
expect(progressUpdate.stagnationCount).toBe(0)
|
||||||
|
expect(sessionStateStore.getState(sessionID).lastIncompleteCount).toBe(2)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -10,9 +10,17 @@ interface TrackedSessionState {
|
|||||||
lastAccessedAt: number
|
lastAccessedAt: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ContinuationProgressUpdate {
|
||||||
|
previousIncompleteCount?: number
|
||||||
|
stagnationCount: number
|
||||||
|
hasProgressed: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export interface SessionStateStore {
|
export interface SessionStateStore {
|
||||||
getState: (sessionID: string) => SessionState
|
getState: (sessionID: string) => SessionState
|
||||||
getExistingState: (sessionID: string) => SessionState | undefined
|
getExistingState: (sessionID: string) => SessionState | undefined
|
||||||
|
trackContinuationProgress: (sessionID: string, incompleteCount: number) => ContinuationProgressUpdate
|
||||||
|
resetContinuationProgress: (sessionID: string) => void
|
||||||
cancelCountdown: (sessionID: string) => void
|
cancelCountdown: (sessionID: string) => void
|
||||||
cleanup: (sessionID: string) => void
|
cleanup: (sessionID: string) => void
|
||||||
cancelAllCountdowns: () => void
|
cancelAllCountdowns: () => void
|
||||||
@@ -46,6 +54,7 @@ export function createSessionStateStore(): SessionStateStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const state: SessionState = {
|
const state: SessionState = {
|
||||||
|
stagnationCount: 0,
|
||||||
consecutiveFailures: 0,
|
consecutiveFailures: 0,
|
||||||
}
|
}
|
||||||
sessions.set(sessionID, { state, lastAccessedAt: Date.now() })
|
sessions.set(sessionID, { state, lastAccessedAt: Date.now() })
|
||||||
@@ -61,6 +70,57 @@ export function createSessionStateStore(): SessionStateStore {
|
|||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function trackContinuationProgress(
|
||||||
|
sessionID: string,
|
||||||
|
incompleteCount: number
|
||||||
|
): ContinuationProgressUpdate {
|
||||||
|
const state = getState(sessionID)
|
||||||
|
const previousIncompleteCount = state.lastIncompleteCount
|
||||||
|
|
||||||
|
state.lastIncompleteCount = incompleteCount
|
||||||
|
|
||||||
|
if (previousIncompleteCount === undefined) {
|
||||||
|
state.stagnationCount = 0
|
||||||
|
return {
|
||||||
|
previousIncompleteCount,
|
||||||
|
stagnationCount: state.stagnationCount,
|
||||||
|
hasProgressed: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (incompleteCount < previousIncompleteCount) {
|
||||||
|
state.stagnationCount = 0
|
||||||
|
return {
|
||||||
|
previousIncompleteCount,
|
||||||
|
stagnationCount: state.stagnationCount,
|
||||||
|
hasProgressed: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.lastInjectedAt === undefined) {
|
||||||
|
return {
|
||||||
|
previousIncompleteCount,
|
||||||
|
stagnationCount: state.stagnationCount,
|
||||||
|
hasProgressed: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.stagnationCount += 1
|
||||||
|
return {
|
||||||
|
previousIncompleteCount,
|
||||||
|
stagnationCount: state.stagnationCount,
|
||||||
|
hasProgressed: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetContinuationProgress(sessionID: string): void {
|
||||||
|
const state = getExistingState(sessionID)
|
||||||
|
if (!state) return
|
||||||
|
|
||||||
|
state.lastIncompleteCount = undefined
|
||||||
|
state.stagnationCount = 0
|
||||||
|
}
|
||||||
|
|
||||||
function cancelCountdown(sessionID: string): void {
|
function cancelCountdown(sessionID: string): void {
|
||||||
const tracked = sessions.get(sessionID)
|
const tracked = sessions.get(sessionID)
|
||||||
if (!tracked) return
|
if (!tracked) return
|
||||||
@@ -100,6 +160,8 @@ export function createSessionStateStore(): SessionStateStore {
|
|||||||
return {
|
return {
|
||||||
getState,
|
getState,
|
||||||
getExistingState,
|
getExistingState,
|
||||||
|
trackContinuationProgress,
|
||||||
|
resetContinuationProgress,
|
||||||
cancelCountdown,
|
cancelCountdown,
|
||||||
cleanup,
|
cleanup,
|
||||||
cancelAllCountdowns,
|
cancelAllCountdowns,
|
||||||
|
|||||||
Reference in New Issue
Block a user