11import { Hono , type Context } from 'hono' ;
22import { db } from '../db/client.js' ;
33import { agents , tasks , escrowTransactions , transactions , agentRatings , disputes , anomalyEvents , agentReputation } from '../db/schema.js' ;
4- import { eq , sql , count , desc , and } from 'drizzle-orm' ;
4+ import { eq , sql , count , desc , and , inArray } from 'drizzle-orm' ;
55import { timingSafeEqual } from 'node:crypto' ;
66import { createMiddleware } from 'hono/factory' ;
77import { HTTPException } from 'hono/http-exception' ;
@@ -35,6 +35,24 @@ const adminAuth = createMiddleware(async (c, next) => {
3535
3636const ADMIN_LIST_MAX_LIMIT = 200 ;
3737const ADMIN_LIST_DEFAULT_LIMIT = 50 ;
38+ export const TRIBUNAL_SELECTABLE_STATUSES = [
39+ DISPUTE_STATUS . OPEN ,
40+ DISPUTE_STATUS . ESCALATED ,
41+ ] as const ;
42+ export const ADMIN_RESOLVABLE_DISPUTE_STATUSES = [
43+ DISPUTE_STATUS . OPEN ,
44+ DISPUTE_STATUS . ESCALATED ,
45+ DISPUTE_STATUS . TRIBUNAL ,
46+ DISPUTE_STATUS . ADMIN_REQUIRED ,
47+ ] as const ;
48+
49+ export function isDisputeStatusSelectableForTribunal ( status : string ) : boolean {
50+ return ( TRIBUNAL_SELECTABLE_STATUSES as readonly string [ ] ) . includes ( status ) ;
51+ }
52+
53+ export function isDisputeStatusResolvableByAdmin ( status : string ) : boolean {
54+ return ( ADMIN_RESOLVABLE_DISPUTE_STATUSES as readonly string [ ] ) . includes ( status ) ;
55+ }
3856
3957function parseListLimit ( raw : string | undefined ) : number {
4058 const parsed = parseInt ( raw ?? String ( ADMIN_LIST_DEFAULT_LIMIT ) , 10 ) ;
@@ -199,31 +217,42 @@ app.get('/disputes', adminAuth, async (c) => {
199217 } ) ;
200218} ) ;
201219
202- // GET /api/v1/admin/disputes/:id/tribunal — Trigger tribunal selection
203- app . get ( '/disputes/:id/tribunal' , adminAuth , async ( c ) => {
220+ // GET /api/v1/admin/disputes/:id/tribunal — Deprecated: use POST for state changes
221+ app . get ( '/disputes/:id/tribunal' , adminAuth , ( c ) =>
222+ c . json ( { error : 'Use POST /api/v1/admin/disputes/:id/tribunal to select tribunal judges' } , 405 ) ,
223+ ) ;
224+
225+ // POST /api/v1/admin/disputes/:id/tribunal — Trigger tribunal selection
226+ app . post ( '/disputes/:id/tribunal' , adminAuth , async ( c ) => {
204227 const id = c . req . param ( 'id' ) ;
205228
206229 const [ dispute ] = await db
207230 . select ( )
208231 . from ( disputes )
209- . where ( and ( eq ( disputes . id , id ) , eq ( disputes . status , DISPUTE_STATUS . OPEN ) ) )
232+ . where ( eq ( disputes . id , id ) )
210233 . limit ( 1 ) ;
211234
212235 if ( ! dispute ) {
213- return c . json ( { error : 'Open dispute not found' } , 404 ) ;
236+ return c . json ( { error : 'Dispute not found' } , 404 ) ;
237+ }
238+ if ( ! isDisputeStatusSelectableForTribunal ( dispute . status ) ) {
239+ return c . json ( { error : `Dispute is not selectable for tribunal in status: ${ dispute . status } ` } , 409 ) ;
214240 }
215241
216242 const tribunalAgents = await selectTribunalJudges ( dispute . id ) ;
217-
218243 const [ updatedDispute ] = await db
219- . update ( disputes )
220- . set ( {
221- status : DISPUTE_STATUS . TRIBUNAL ,
222- tribunalAgents,
223- updatedAt : new Date ( ) ,
224- } )
244+ . select ( )
245+ . from ( disputes )
225246 . where ( eq ( disputes . id , id ) )
226- . returning ( ) ;
247+ . limit ( 1 ) ;
248+
249+ if ( tribunalAgents . length === 0 ) {
250+ return c . json ( {
251+ dispute : updatedDispute ,
252+ tribunalAgents,
253+ error : 'Not enough eligible judges; admin resolution required' ,
254+ } , 409 ) ;
255+ }
227256
228257 // Notify tribunal agents
229258 for ( const agentId of tribunalAgents ) {
@@ -248,11 +277,11 @@ app.post('/disputes/:id/resolve', adminAuth, async (c) => {
248277 const [ dispute ] = await db
249278 . select ( )
250279 . from ( disputes )
251- . where ( and ( eq ( disputes . id , id ) , eq ( disputes . status , DISPUTE_STATUS . OPEN ) ) )
280+ . where ( and ( eq ( disputes . id , id ) , inArray ( disputes . status , [ ... ADMIN_RESOLVABLE_DISPUTE_STATUSES ] ) ) )
252281 . limit ( 1 ) ;
253282
254283 if ( ! dispute ) {
255- return c . json ( { error : 'Open dispute not found' } , 404 ) ;
284+ return c . json ( { error : 'Resolvable dispute not found' } , 404 ) ;
256285 }
257286
258287 const [ task ] = await db . select ( ) . from ( tasks ) . where ( eq ( tasks . id , dispute . taskId ) ) . limit ( 1 ) ;
0 commit comments