@@ -45,12 +45,15 @@ describe("fingerprintDeniedCall", () => {
4545 expect ( a ) . toBe ( b ) ;
4646 } ) ;
4747
48- test ( "differs across args and tool (cwd binds at precheck, not in the hash) " , ( ) => {
48+ test ( "differs across args, tool, and cwd " , ( ) => {
4949 const base = fingerprintDeniedCall ( "run_shell" , ARGS , CWD ) ;
5050 expect (
5151 fingerprintDeniedCall ( "run_shell" , { command : "npm run evil" } , CWD ) ,
5252 ) . not . toBe ( base ) ;
5353 expect ( fingerprintDeniedCall ( "read_file" , ARGS , CWD ) ) . not . toBe ( base ) ;
54+ expect ( fingerprintDeniedCall ( "run_shell" , ARGS , "/elsewhere" ) ) . not . toBe (
55+ base ,
56+ ) ;
5457 expect (
5558 fingerprintDeniedCall ( "read_file" , { path : "a.md" } , "/repo" ) ===
5659 fingerprintDeniedCall ( "read_file" , { path : "b.md" } , "/repo" ) ,
@@ -283,19 +286,32 @@ describe("WorkerGrantStore lifecycle", () => {
283286 expect ( store . invalidateSession ( "worker-1" , "again" ) ) . toBe ( 0 ) ;
284287 } ) ;
285288
286- test ( "attachToAsk stamps the questionId and keeps pending" , ( ) => {
289+ test ( "attachToAsk binds only an explicit requestId and keeps it pending" , ( ) => {
287290 const store = new WorkerGrantStore ( ) ;
288291 const envelope = store . register ( createDeniedCallEnvelope ( descriptor ( ) ) ) ;
289292 // Reads pin the clock: fixture now predates wall time, so an unpinned
290293 // read would sweep the envelope as expired.
291- expect ( store . attachToAsk ( "worker-1" , "ask-7" , undefined , 1_000_001 ) ) . toBe (
292- envelope ,
293- ) ;
294- expect ( envelope . questionId ) . toBe ( "ask-7" ) ;
294+ expect (
295+ store . attachToAsk ( "worker-1" , "ask-generic" , undefined , 1_000_001 ) ,
296+ ) . toBeUndefined ( ) ;
297+ expect ( envelope . questionId ) . toBeUndefined ( ) ;
295298 expect ( envelope . status ) . toBe ( "pending" ) ;
296299 expect ( store . peek ( envelope . requestId ) ) . toBe ( envelope ) ;
297300 expect (
298- store . attachToAsk ( "worker-9" , "ask-8" , undefined , 1_000_001 ) ,
301+ store . attachToAsk ( "worker-1" , "ask-7" , envelope . requestId , 1_000_001 ) ,
302+ ) . toBe ( envelope ) ;
303+ expect ( envelope . questionId ) . toBe ( "ask-7" ) ;
304+ expect (
305+ store . attachToAsk (
306+ "worker-1" ,
307+ "ask-rebound" ,
308+ envelope . requestId ,
309+ 1_000_001 ,
310+ ) ,
311+ ) . toBeUndefined ( ) ;
312+ expect ( envelope . questionId ) . toBe ( "ask-7" ) ;
313+ expect (
314+ store . attachToAsk ( "worker-9" , "ask-8" , envelope . requestId , 1_000_001 ) ,
299315 ) . toBeUndefined ( ) ;
300316 expect ( envelope . audit . map ( ( event ) => event . event ) ) . toEqual ( [
301317 "denied" ,
@@ -329,12 +345,12 @@ describe("WorkerGrantStore lifecycle", () => {
329345 expect ( store . peek ( envelopeB . requestId ) ?. questionId ) . toBe ( "ask-B" ) ;
330346 } ) ;
331347
332- test ( "unnamed ask keeps the legacy first- pending bind " , ( ) => {
348+ test ( "unnamed ask does not bind a pending denial " , ( ) => {
333349 const { store, envelopeA, envelopeB } = twoDenies ( ) ;
334- expect ( store . attachToAsk ( "worker-1" , "ask-x" , undefined , 1_000_001 ) ) . toBe (
335- envelopeA ,
336- ) ;
337- expect ( envelopeA . questionId ) . toBe ( "ask-x" ) ;
350+ expect (
351+ store . attachToAsk ( "worker-1" , "ask-x" , undefined , 1_000_001 ) ,
352+ ) . toBeUndefined ( ) ;
353+ expect ( envelopeA . questionId ) . toBeUndefined ( ) ;
338354 expect ( envelopeB . questionId ) . toBeUndefined ( ) ;
339355 } ) ;
340356
@@ -370,7 +386,7 @@ describe("WorkerGrantStore lifecycle", () => {
370386 test ( "audit trail orders deny → ask → consume" , ( ) => {
371387 const store = new WorkerGrantStore ( ) ;
372388 const envelope = store . register ( createDeniedCallEnvelope ( descriptor ( ) ) ) ;
373- store . attachToAsk ( "worker-1" , "ask-1" , undefined , 1_000_001 ) ;
389+ store . attachToAsk ( "worker-1" , "ask-1" , envelope . requestId , 1_000_001 ) ;
374390 store . consumeOnAllow ( {
375391 sessionId : "worker-1" ,
376392 canonicalTool : "run_shell" ,
@@ -385,6 +401,25 @@ describe("WorkerGrantStore lifecycle", () => {
385401 ] ) ;
386402 } ) ;
387403
404+ test ( "revokeAuthorizedRetry returns authorized to pending and audits revoked" , ( ) => {
405+ const store = new WorkerGrantStore ( ) ;
406+ const envelope = store . register ( createDeniedCallEnvelope ( descriptor ( ) ) ) ;
407+ expect ( store . authorizeRetry ( envelope . requestId , 1_000_001 ) ) . toBe ( true ) ;
408+ store . revokeAuthorizedRetry ( envelope . requestId ) ;
409+ expect ( envelope . status ) . toBe ( "pending" ) ;
410+ expect ( envelope . audit . map ( ( event ) => event . event ) ) . toEqual ( [
411+ "denied" ,
412+ "authorized" ,
413+ "revoked" ,
414+ ] ) ;
415+ store . revokeAuthorizedRetry ( envelope . requestId ) ;
416+ expect ( envelope . audit . map ( ( event ) => event . event ) ) . toEqual ( [
417+ "denied" ,
418+ "authorized" ,
419+ "revoked" ,
420+ ] ) ;
421+ } ) ;
422+
388423 test ( "no text API: state moves only on typed identities, never prose" , ( ) => {
389424 const store = new WorkerGrantStore ( ) ;
390425 const envelope = store . register ( createDeniedCallEnvelope ( descriptor ( ) ) ) ;
0 commit comments