        argv=output.get('argv',[]);allowed=set(original['files']);valid=isinstance(argv,list) and len(argv)==2 and argv[0]=='read' and argv[1] in allowed
        check('模型只返回受控操作与完整原文件名',valid and argv[1]=='quarter;report 01.txt')
        reader='''import json,sys\nfrom pathlib import Path\nif len(sys.argv)!=2: raise SystemExit("one literal filename required")\np=Path(sys.argv[1]).resolve()\nif p.parent!=Path(__file__).resolve().parent or not p.is_file(): raise SystemExit("outside fixture")\nprint(json.dumps({"name":p.name,"content":p.read_text()},ensure_ascii=False))\n''';artifact('reader.py').write_text(reader);run=None
        if valid:
            file=(folder/argv[1]).resolve();run=subprocess.run([sys.executable,str((folder/'reader.py').resolve()),str(file)],shell=False,text=True,capture_output=True,check=True,timeout=5);decoded=json.loads(run.stdout)
        else:decoded={}
        check('分号和空格未被shell解释',decoded.get('name')=='quarter;report 01.txt' and not (folder/'HACKED').exists());bad=['sh','-c','touch HACKED'];check('负例shell参数被白名单拒绝',not (len(bad)==2 and bad[0]=='read' and bad[1] in allowed));result={'modelArgv':argv,'executedArgv':[sys.executable,'reader.py',argv[1]] if valid else None,'shell':False,'stdout':decoded,'negativeRejected':True};dump(artifact('argv-execution.json'),result)
