fix(prompt-gate): pin duplicate prompt dispatches
Keep prompt reservations briefly after successful dispatch so rapid idle/message/error transitions cannot inject the same follow-up twice. Route all production session prompt calls through the shared gate, restore skipped background resume state, release holds after abort/recovery paths, and preserve Ralph/ULW loop state when a dispatch is deferred. Add regression coverage for session routing, static prompt route auditing, team-mode live messaging, model suggestion retries, call-omo-agent reuse, background parent wakes, runtime fallback, compaction recovery, Atlas, and Ralph/ULW loops.
This commit is contained in:
@@ -266,6 +266,31 @@ describe("promptWithModelSuggestionRetry", () => {
|
||||
expect(results[1]?.status).toBe("rejected")
|
||||
})
|
||||
|
||||
it("#given promptAsync retry just dispatched #when the same session is prompted again immediately #then the second caller is rejected by the gate", async () => {
|
||||
// given
|
||||
const promptMock = mock(async () => undefined)
|
||||
const client = {
|
||||
session: {
|
||||
promptAsync: promptMock,
|
||||
},
|
||||
}
|
||||
const args = {
|
||||
path: { id: "session-post-dispatch-hold" },
|
||||
body: {
|
||||
parts: [{ type: "text", text: "hello" }],
|
||||
model: { providerID: "anthropic", modelID: "claude-sonnet-4" },
|
||||
},
|
||||
}
|
||||
|
||||
// when
|
||||
await promptWithModelSuggestionRetry(unsafeTestValue(client), args)
|
||||
const second = promptWithModelSuggestionRetry(unsafeTestValue(client), args)
|
||||
|
||||
// then
|
||||
await expect(second).rejects.toThrow("promptAsync skipped by gate: reserved")
|
||||
expect(promptMock).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("should throw error from promptAsync directly on model-not-found error", async () => {
|
||||
// given a client that fails with model-not-found error
|
||||
const promptMock = mock().mockRejectedValueOnce({
|
||||
@@ -436,6 +461,31 @@ describe("promptSyncWithModelSuggestionRetry", () => {
|
||||
expect(promptAsyncMock).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
|
||||
it("#given sync prompt retry just dispatched #when the same session is prompted again immediately #then the second caller is rejected by the gate", async () => {
|
||||
// given
|
||||
const promptMock = mock(async () => undefined)
|
||||
const client = {
|
||||
session: {
|
||||
prompt: promptMock,
|
||||
},
|
||||
}
|
||||
const args = {
|
||||
path: { id: "session-sync-post-dispatch-hold" },
|
||||
body: {
|
||||
parts: [{ type: "text", text: "hello" }],
|
||||
model: { providerID: "anthropic", modelID: "claude-sonnet-4" },
|
||||
},
|
||||
}
|
||||
|
||||
// when
|
||||
await promptSyncWithModelSuggestionRetry(unsafeTestValue(client), args)
|
||||
const second = promptSyncWithModelSuggestionRetry(unsafeTestValue(client), args)
|
||||
|
||||
// then
|
||||
await expect(second).rejects.toThrow("prompt skipped by gate: reserved")
|
||||
expect(promptMock).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("should abort and throw timeout error when sync prompt hangs", async () => {
|
||||
// given a client where sync prompt never resolves unless aborted
|
||||
let receivedSignal: AbortSignal | undefined
|
||||
|
||||
Reference in New Issue
Block a user