$GLOBALS['s26Mode']!=='config_disabled', 'base_url'=>in_array($GLOBALS['s26Mode'],['endpoint_reject','overflow_endpoint'],true)?'https://fixture.invalid/v1':'https://fixture.invalid/v1/chat-messages', 'timeout'=>90,'models'=>['qwen'=>['name'=>'fixture','api_key'=>'fixture-only']]];} function curl_init():\stdClass|false{return $GLOBALS['s26Mode']==='init_fail'?false:(object)['errno'=>0,'code'=>200];} function curl_setopt_array(\stdClass $h,array $o):bool{$h->options=$o;return $GLOBALS['s26Mode']!=='setup_fail';} function curl_exec(\stdClass $h):bool|string{ $GLOBALS['s26InCurl']=true;$GLOBALS['s26Closed']=false;$n=++$GLOBALS['s26Posts'];$mode=$GLOBALS['s26Mode']; $p=$GLOBALS['s26Control']->publicState(); \s26Expect($p['submitted_attempt_count']===$n&&$p['submitted_pending_attempt_count']===1,'Owned pending attempt is durable BEFORE each actual POST.'); \s26Expect(!$GLOBALS['s26Control']->canStartAttempt(),'Concurrent successor is gated during POST.'); $payload=json_decode($h->options[CURLOPT_POSTFIELDS],true);$GLOBALS['s26Payloads'][]=$payload; try{ $header=$h->options[CURLOPT_HEADERFUNCTION]??static fn()=>0;$write=$h->options[CURLOPT_WRITEFUNCTION]??null; if($GLOBALS['s26Blocking'])return '{"answer":"原始阻塞字节。"}'; if($n===1&&in_array($mode,['input_reject','file_reject','endpoint_reject','generic_400','answer_in_error','overflow_endpoint','run_in_error'],true)){ $h->code=in_array($mode,['endpoint_reject','overflow_endpoint'],true)?404:400;$header($h,'HTTP/1.1 '.$h->code." Rejected\r\n"); $body=['code'=>$mode==='file_reject'?'unsupported_file_type':($mode==='endpoint_reject'?'not_found':($mode==='generic_400'?'invalid_param':'invalid_inputs')),'message'=>'fixture rejection']; if($mode==='answer_in_error')$body['answer']='provider execution evidence'; if($mode==='run_in_error')$body['data']=['id'=>'unbound-run','status'=>'running']; if($mode==='overflow_endpoint'){$write($h,str_repeat('x',16385));return true;} $write($h,json_encode($body));return true; } $header($h,"HTTP/1.1 200 OK\r\n"); if($n===1&&in_array($mode,['sse_input_reject','node_then_input_error','terminal_then_input_error','silent_200'],true)){ $frames=[]; if($mode==='node_then_input_error')$frames[]=['event'=>'node_started']; if($mode==='terminal_then_input_error')$frames[]=['event'=>'workflow_finished','task_id'=>'unbound-task','workflow_run_id'=>'unbound-run','data'=>['status'=>'stopped']]; $frames[]=$mode==='silent_200'?['event'=>'message_end']:['event'=>'error','code'=>'invalid_inputs','message'=>'fixture input rejection']; }elseif(str_ends_with($h->options[CURLOPT_URL],'chat/completions')){ $write($h,"data: {\"choices\":[{\"delta\":{\"content\":\"原始临床字节。\"}}]}\n\ndata: [DONE]\n\n");return true; }else $frames=[['event'=>'workflow_started','task_id'=>'own-task-'.$n,'workflow_run_id'=>'own-run-'.$n], ['event'=>'message','task_id'=>'own-task-'.$n,'answer'=>"原始临床\r\n字节。"], ['event'=>'workflow_finished','task_id'=>'own-task-'.$n,'workflow_run_id'=>'own-run-'.$n,'data'=>['status'=>'succeeded']],['event'=>'message_end']]; foreach($frames as$f){$GLOBALS['s26Ns']+=1000000;$write($h,'data: '.json_encode($f,JSON_UNESCAPED_UNICODE)."\n\n");} return true; }finally{$GLOBALS['s26InCurl']=false;} } function curl_errno(\stdClass $h):int{return $h->errno;} function curl_getinfo(\stdClass $h,int $option):int|float{return $option===CURLINFO_HTTP_CODE?$h->code:0.001;} function curl_close(\stdClass $h):void{$GLOBALS['s26Closed']=true;} class DifyWorkflowRunStatus{ public static function lookup(array $id,int $ms):array{ ++$GLOBALS['s26Gets'];\s26Expect(!$GLOBALS['s26InCurl']&&$GLOBALS['s26Closed'],'Only bound own GET after actual closure.'); \s26Expect(count($GLOBALS['s26Timing'])===$GLOBALS['s26Posts'],'Transport clock published before verifier I/O.'); \s26Expect($id['task_id']==='own-task-'.$GLOBALS['s26Posts']&&$id['workflow_run_id']==='own-run-'.$GLOBALS['s26Posts'],'No borrowed rejection/neighbor identity.'); \s26Expect($ms>0&&$ms<=1000,'Original I/O cap preserved.');$GLOBALS['s26Ns']+=10000000; return ['ok'=>true,'workflow_run_id'=>$id['workflow_run_id'],'status'=>'succeeded']; } } } namespace { require dirname(__DIR__).'/vendor/autoload.php'; use app\adminapi\service\AssistantRequestControl as Control; use app\common\service\DifyChatService; $checks=0;$number=0; function s26Expect(bool $ok,string $message):void{++$GLOBALS['checks'];if(!$ok)throw new \RuntimeException($message);} $directory=sys_get_temp_dir().'/r26b-submission-rejection-'.bin2hex(random_bytes(8)); $source=file_get_contents(dirname(__DIR__).'/app/adminapi/controller/tcm/DiagnosisController.php');$literals=[]; foreach(['on_submission','on_task','on_terminal','on_transfer_end','can_start_attempt','on_timeout']as$key){ $pattern=in_array($key,['on_submission','on_task'],true)?'/\$options\[\''.preg_quote($key,'/').'\'\] = (static function.*?\n });/s':'/\''.preg_quote($key,'/').'\' => (static function.*?\n }),/s'; s26Expect(preg_match_all($pattern,$source,$m)===1,'Unique actual canonical callback '.$key);$literals[$key]=$m[1][0]; } $make=static function(string $mode)use($directory,&$number,$literals):array{ $GLOBALS['s26Ns']=100000000000;foreach(['s26Posts','s26Gets']as$key)$GLOBALS[$key]=0; $GLOBALS['s26Timing']=$GLOBALS['s26Payloads']=[];$GLOBALS['s26Mode']=$mode;$GLOBALS['s26InCurl']=$GLOBALS['s26Closed']=$GLOBALS['s26Blocking']=false; $uuid=sprintf('a5678901-1234-4234-8234-%012d',++$number);$control=new Control(7,11,$uuid,$directory);s26Expect($control->begin(),'Synthetic owned request begins.'); $GLOBALS['s26Control']=$control;$profile='qwen';$upstreamUser='own-user';$options=[]; foreach($literals as$key=>$literal)$options[$key]=eval('namespace app\\adminapi\\controller\\tcm; use app\\common\\service\\DifyWorkflowRunStatus; return '.$literal.';'); $options['on_upstream_timing']=static function($timing):void{$GLOBALS['s26Timing'][]=$timing->snapshot();}; $options['knowledge_mode']='evidence';$options['timeout_ms']=90000;$options['request_deadline_ns']=190000000000; return [$control,$options,$directory.'/'.hash('sha256','7:11:'.$uuid).'.json',$uuid]; }; $call=static function(array $options,array $files=[]):array{return DifyChatService::streamChat('qwen',['source'=>'unchanged-input'],'unchanged-query','own-user',static fn():bool=>true,static fn():bool=>false,$files,$options);}; try{ foreach(['input_reject','sse_input_reject','file_reject','endpoint_reject']as$mode){ [$c,$options,$path]=$make($mode);$files=$mode==='file_reject'?[['type'=>'image','url'=>'https://fixture.invalid/photo.png']]:[]; $out=$call($options,$files);$p=$c->publicState();$state=json_decode(file_get_contents($path),true);$attempts=array_values($state['submitted_attempts']); s26Expect($GLOBALS['s26Posts']===2&&!empty($out['ok']),'Explicit pre-execution rejection preserves existing finite compatibility: '.$mode); s26Expect($attempts[0]['disposition']==='rejected_before_execution'&&$attempts[0]['transfer_closed']&&$attempts[0]['task_id']==='','Rejection is a closed own attempt without guessed task/terminal.'); s26Expect($p['pre_execution_closed_attempt_count']===1&&$p['rejected_before_execution_attempt_count']===1&&$p['not_submitted_attempt_count']===0&&$p['submitted_attempt_count']===2&&$p['submitted_transfer_closed_count']===2,'No-task rejection remains distinct from actual next submission.'); if($mode==='endpoint_reject')s26Expect($GLOBALS['s26Gets']===0&&!$c->canStartAttempt()&&$p['submitted_without_identity_count']===1,'A legitimate protocol fallback can deliver bytes but its unidentified execution still cannot authorize another generation.'); else s26Expect($GLOBALS['s26Gets']===1&&$c->canStartAttempt()&&$p['upstream_pending_task_count']===0,'Only actual successor original pair can be independently terminal.'); if(in_array($mode,['input_reject','sse_input_reject'],true))s26Expect($GLOBALS['s26Payloads'][0]['query']===$GLOBALS['s26Payloads'][1]['query']&&$GLOBALS['s26Payloads'][1]['inputs']===[],'Existing exact-query / empty-input fallback unchanged.'); } foreach(['node_then_input_error','terminal_then_input_error','generic_400','answer_in_error','silent_200','overflow_endpoint','run_in_error']as$mode){ [$c,$options,$path,$uuid]=$make($mode);$call($options);$p=$c->publicState(); s26Expect($GLOBALS['s26Posts']===1&&$GLOBALS['s26Gets']===0&&!$c->canStartAttempt(),'Execution/ambiguous no-task result cannot be laundered as input rejection: '.$mode); s26Expect($p['upstream_pending_task_count']===1&&$p['submitted_without_identity_count']===1&&$p['generation_transfer_closed'],'Unknown is pending and independently closed, never absence.'); $stops=0;$c->requestCancel(static function()use(&$stops):bool{++$stops;return true;});$c->finish('error'); $c->reconcileClosedTasks(static function():array{throw new \RuntimeException('No guessed GET');}); s26Expect($stops===0&&$c->publicState()['upstream_pending_task_count']===1,'No unbound stop, guessed GET or finish-based cleanup.'); $state=json_decode(file_get_contents($path),true);$state['expires_at']=time()-1000;file_put_contents($path,json_encode($state));touch($path,time()-1000); $peer=new Control(7,11,$uuid,$directory);s26Expect(is_file($path)&&!$peer->begin(),'Unknown submission survives TTL/GC and same-UUID replay.'); $other=new Control(7,11,sprintf('b5678901-1234-4234-8234-%012d',++$number),$directory);s26Expect($other->begin()&&$other->canStartAttempt(),'No implicit global gate for unrelated UUID.'); } foreach(['config_disabled','init_fail','setup_fail']as$mode){ [$c,$options]=$make($mode);$call($options);s26Expect($GLOBALS['s26Posts']===0&&$c->publicState()['submitted_attempt_count']===0&&$c->canStartAttempt(),'Never-submitted preflight remains distinct: '.$mode); $GLOBALS['s26Mode']='success';s26Expect(!empty($call($options)['ok'])&&$GLOBALS['s26Posts']===1,'A later legitimate first submission still works after preflight failure.'); } foreach(['throw','false','invalid']as$mode){ [$c,$options]=$make('success');$options['on_submission']=static function()use($mode){if($mode==='throw')throw new \RuntimeException('private lifecycle error');return $mode==='false'?false:'foreign-token';}; $out=$call($options);s26Expect($GLOBALS['s26Posts']===0&&empty($out['ok']),'Lifecycle hook failure fails closed before actual POST: '.$mode); s26Expect(!str_contains(json_encode($out),'private lifecycle error'),'Hook failure prose never escapes.'); } foreach(['cancel','deadline']as$interruption){ [$c,$options]=$make('success');$admit=$options['on_submission']; $options['on_submission']=static function()use($admit,$c,$interruption):string{ $token=$admit();if($interruption==='cancel')$c->signalCancel();else$GLOBALS['s26Ns']+=91000000000;return $token; }; $out=DifyChatService::streamChat('qwen',[],'unchanged-query','own-user',static fn():bool=>true,static fn():bool=>$c->shouldAbort(),[],$options); $p=$c->publicState(); s26Expect($GLOBALS['s26Posts']===0&&$GLOBALS['s26Gets']===0&&empty($out['ok']),'Cancellation/deadline after admission still prevents actual POST.'); s26Expect($p['submitted_attempt_count']===1&&$p['submitted_transfer_closed_count']===1&&$p['pre_execution_closed_attempt_count']===1&&$p['not_submitted_attempt_count']===1&&$p['rejected_before_execution_attempt_count']===0&&$p['upstream_pending_task_count']===0,'Admitted but locally unexecuted differs from unknown submitted work.'); s26Expect($p['cancel_requested']===($interruption==='cancel')&&$p['timeout_reached']===($interruption==='deadline'),'New ownership hook cannot change original cancel versus ordinary deadline intent.'); } [$c,$options]=$make('success');$GLOBALS['s26Blocking']=true; $out=DifyChatService::chat('qwen',[],'unchanged-query','own-user',[],$options+['should_abort'=>static fn():bool=>false,'blocking_compatibility'=>true]); s26Expect(!empty($out['ok'])&&$GLOBALS['s26Posts']===1&&$GLOBALS['s26Gets']===0&&!$c->canStartAttempt(),'Actual blocking compatibility also retains successful submitted-without-original-identity as pending.'); s26Expect($c->publicState()['generation_transfer_closed']&&$c->publicState()['submitted_pending_attempt_count']===1,'Blocking EOF has its own opaque attempt closure.'); echo "R26b explicit rejection / unknown / preflight / hook failure: {$checks} assertions passed; pure transport stubs only.\n"; }finally{ $real=realpath($directory);$temp=realpath(sys_get_temp_dir());if($real!==false){if($temp===false||!str_starts_with(str_replace('\\','/',$real),rtrim(str_replace('\\','/',$temp),'/').'/r26b-submission-rejection-'))throw new \RuntimeException('Cleanup boundary'); foreach(new \DirectoryIterator($real)as$f){if($f->isDot())continue;if($f->isLink()||!$f->isFile())throw new \RuntimeException('Unexpected temp entry');unlink($f->getPathname());}rmdir($real);} } }