Report queued tasks the scheduling pass never considers
A task in retry backoff was filtered out before the candidate loop, so it recorded no rejection at all: queued, apparently assignable, and silent. That is the exact shape that made F5 take a live session to diagnose. It now reports "retry backoff until <time>". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,9 +17,20 @@ describe('UI API client',()=>{
|
||||
expect(fetch).toHaveBeenCalledWith('/v1/artifacts',expect.objectContaining({method:'POST',body:'report'}))
|
||||
})
|
||||
it('submits username and password to the browser login endpoint',async()=>{
|
||||
const fetch=vi.fn().mockResolvedValue(new Response(null,{status:204}))
|
||||
const fetch=vi.fn().mockResolvedValue(new Response(JSON.stringify({username:'operator'}),{status:200}))
|
||||
vi.stubGlobal('fetch',fetch)
|
||||
await api.login('operator','not stored in the browser')
|
||||
await expect(api.login('operator','not stored in the browser')).resolves.toEqual({username:'operator'})
|
||||
expect(fetch).toHaveBeenCalledWith('/v1/ui/session',expect.objectContaining({method:'POST',body:JSON.stringify({username:'operator',password:'not stored in the browser'})}))
|
||||
})
|
||||
it('treats a missing browser session as a normal signed-out state',async()=>{
|
||||
const fetch=vi.fn().mockResolvedValue(new Response('unauthorized',{status:401}))
|
||||
vi.stubGlobal('fetch',fetch)
|
||||
await expect(api.session()).resolves.toBeUndefined()
|
||||
})
|
||||
it('updates account credentials through the session-gated account endpoint',async()=>{
|
||||
const fetch=vi.fn().mockResolvedValue(new Response(JSON.stringify({username:'kami'}),{status:200}))
|
||||
vi.stubGlobal('fetch',fetch)
|
||||
await api.updateAccount({current_password:'old password',username:'kami',new_password:'new password'})
|
||||
expect(fetch).toHaveBeenCalledWith('/v1/ui/account',expect.objectContaining({method:'PUT'}))
|
||||
})
|
||||
})
|
||||
|
||||
+16
-1
@@ -1,4 +1,4 @@
|
||||
import type { CreatedEvent, Detail, Overview } from './types'
|
||||
import type { Account, CreatedEvent, Detail, Overview } from './types'
|
||||
|
||||
function sessionExpired(response: Response) {
|
||||
if (response.status === 401) {
|
||||
@@ -49,6 +49,13 @@ async function upload(body: string) {
|
||||
return ((await response.json()) as { ref: string }).ref
|
||||
}
|
||||
|
||||
async function session(): Promise<Account | undefined> {
|
||||
const response = await fetch('/v1/ui/session', { credentials: 'same-origin' })
|
||||
if (response.status === 401) return undefined
|
||||
if (!response.ok) throw await responseError(response)
|
||||
return response.json() as Promise<Account>
|
||||
}
|
||||
|
||||
async function login(username: string, password: string) {
|
||||
const response = await fetch('/v1/ui/session', {
|
||||
method: 'POST',
|
||||
@@ -57,6 +64,7 @@ async function login(username: string, password: string) {
|
||||
body: JSON.stringify({ username, password }),
|
||||
})
|
||||
if (!response.ok) throw await responseError(response)
|
||||
return response.json() as Promise<Account>
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
@@ -68,8 +76,15 @@ async function logout() {
|
||||
}
|
||||
|
||||
export const api = {
|
||||
session,
|
||||
login,
|
||||
logout,
|
||||
account: () => request<Account>('/v1/ui/account'),
|
||||
updateAccount: (body: { current_password: string; username: string; new_password?: string }) =>
|
||||
request<Account>('/v1/ui/account', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(body),
|
||||
}),
|
||||
overview: () => request<Overview>('/v1/ui/overview'),
|
||||
detail: (id: string) => request<Detail>(`/v1/ui/tasks/${id}`),
|
||||
artifact: (ref: string) => text(`/v1/ui/artifacts/${ref}`),
|
||||
|
||||
+17
-1
@@ -1,4 +1,11 @@
|
||||
export type TaskState = 'queued' | 'leased' | 'blocked' | 'completed' | 'failed'
|
||||
export type TaskState =
|
||||
| 'queued'
|
||||
| 'leased'
|
||||
| 'needs_attention'
|
||||
| 'blocked'
|
||||
| 'in_review'
|
||||
| 'completed'
|
||||
| 'failed'
|
||||
|
||||
export type BlockReason =
|
||||
| 'lease_failure'
|
||||
@@ -8,8 +15,17 @@ export type BlockReason =
|
||||
| 'handoff_validation'
|
||||
| 'operator_block'
|
||||
| 'system_error'
|
||||
| 'trajectory_gate'
|
||||
| 'human_decision'
|
||||
| 'operator_required'
|
||||
| 'unknown'
|
||||
|
||||
export interface Account {
|
||||
username: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface SessionEvidence {
|
||||
pane_id?: string
|
||||
harness_id?: string
|
||||
|
||||
Reference in New Issue
Block a user