[{"data":1,"prerenderedAt":1777},["ShallowReactive",2],{"docs-nav":3,"docs-\u002Fdocs\u002Fskills-roles":78},[4,9,13,16,21,26,30,34,38,42,46,50,53,58,62,66,69,74],{"path":5,"title":6,"navTitle":6,"group":7,"order":8},"\u002Fdocs\u002Fcommands","Other commands","Guide",999,{"path":10,"title":11,"navTitle":12,"group":7,"order":8},"\u002Fdocs\u002Fcompaction","Context and compaction","Context & compaction",{"path":14,"title":15,"navTitle":15,"group":7,"order":8},"\u002Fdocs\u002Fconfiguration","Configuration",{"path":17,"title":18,"navTitle":18,"group":19,"order":20},"\u002Fdocs\u002Fenvironment","Environment variables","Reference",11,{"path":22,"title":23,"navTitle":23,"group":24,"order":25},"\u002Fdocs\u002Fflags","Flag reference","CLI",8,{"path":27,"title":28,"navTitle":28,"group":7,"order":29},"\u002Fdocs","Introduction",1,{"path":31,"title":32,"navTitle":32,"group":7,"order":33},"\u002Fdocs\u002Finstallation","Installation",2,{"path":35,"title":36,"navTitle":36,"group":19,"order":37},"\u002Fdocs\u002Fproviders","Providers",10,{"path":39,"title":40,"navTitle":40,"group":7,"order":41},"\u002Fdocs\u002Fquickstart","Quickstart",3,{"path":43,"title":44,"navTitle":45,"group":7,"order":8},"\u002Fdocs\u002Freleases","Releases and upgrading","Releases",{"path":47,"title":48,"navTitle":49,"group":7,"order":8},"\u002Fdocs\u002Frun","The run command","Running an audit",{"path":51,"title":52,"navTitle":52,"group":7,"order":8},"\u002Fdocs\u002Fsecurity","Security model",{"path":54,"title":55,"navTitle":56,"group":24,"order":57},"\u002Fdocs\u002Fsessions","Sessions and forking","Sessions",6,{"path":59,"title":60,"navTitle":61,"group":7,"order":8},"\u002Fdocs\u002Fskills-roles","Skills and roles","Skills & roles",{"path":63,"title":64,"navTitle":64,"group":19,"order":65},"\u002Fdocs\u002Ftools","Tool reference",12,{"path":67,"title":68,"navTitle":68,"group":7,"order":8},"\u002Fdocs\u002Ftroubleshooting","Troubleshooting",{"path":70,"title":71,"navTitle":72,"group":24,"order":73},"\u002Fdocs\u002Ftui","The interactive TUI","Interactive TUI",9,{"path":75,"title":76,"navTitle":77,"group":7,"order":8},"\u002Fdocs\u002Fweb-eval","Dashboard and evaluation","Dashboard & evaluation",{"id":79,"title":60,"body":80,"description":1768,"extension":1769,"group":7,"meta":1770,"navTitle":61,"navigation":174,"order":8,"path":59,"seo":1771,"stem":1775,"__hash__":1776},"docs\u002Fdocs\u002Fskills-roles.md",{"type":81,"value":82,"toc":1750},"minimark",[83,87,100,116,120,127,137,140,191,196,211,238,245,248,252,340,345,365,369,376,394,398,401,461,473,483,498,502,561,572,578,583,587,646,665,668,691,697,710,717,721,730,851,862,890,900,910,916,919,926,998,1001,1006,1023,1124,1129,1159,1182,1193,1250,1253,1334,1363,1367,1373,1551,1567,1570,1683,1690,1696,1700,1706,1709,1715,1732,1737,1741,1746],[84,85,60],"h1",{"id":86},"skills-and-roles",[88,89,90,91,95,96,99],"p",{},"Two mechanisms shape a run without touching the harness. ",[92,93,94],"strong",{},"Skills"," are playbooks the model can\nchoose to open. ",[92,97,98],{},"Roles"," are typed subagents with their own prompt, tools, and report shape.",[88,101,102,103,107,108,111,112,115],{},"Both are read only from ",[104,105,106],"code",{},"LOCAC_HOME"," (",[104,109,110],{},"~\u002F.locac"," by default) or from the binary itself. Neither is\never read from the repository under audit. See the ",[113,114,52],"a",{"href":51},".",[117,118,94],"h2",{"id":119},"skills",[88,121,122,123,126],{},"A skill is a directory containing a ",[104,124,125],{},"SKILL.md",":",[128,129,135],"pre",{"className":130,"code":132,"language":133,"meta":134},[131],"language-text","~\u002F.locac\u002Fskills\u002F\n  archive-extraction\u002F\n    SKILL.md\n  jwt-auth\u002F\n    SKILL.md\n","text","",[104,136,132],{"__ignoreMap":134},[88,138,139],{},"The front matter needs two fields. Without both, the file is not a skill and is skipped silently:",[128,141,145],{"className":142,"code":143,"language":144,"meta":134,"style":134},"language-markdown shiki shiki-themes vitesse-dark","---\nname: archive-extraction\ndescription: Traversal and zip-slip in archive handlers: where entry names reach a path join, and what a correct destination check looks like. Use when auditing tar\u002Fzip extraction.\n---\n\n# archive-extraction\n\nThe body. Written for the agent, not for a human reader.\n","markdown",[104,146,147,154,159,164,169,176,181,186],{"__ignoreMap":134},[148,149,151],"span",{"class":150,"line":29},"line",[148,152,153],{},"---\n",[148,155,156],{"class":150,"line":33},[148,157,158],{},"name: archive-extraction\n",[148,160,161],{"class":150,"line":41},[148,162,163],{},"description: Traversal and zip-slip in archive handlers: where entry names reach a path join, and what a correct destination check looks like. Use when auditing tar\u002Fzip extraction.\n",[148,165,167],{"class":150,"line":166},4,[148,168,153],{},[148,170,172],{"class":150,"line":171},5,[148,173,175],{"emptyLinePlaceholder":174},true,"\n",[148,177,178],{"class":150,"line":57},[148,179,180],{},"# archive-extraction\n",[148,182,184],{"class":150,"line":183},7,[148,185,175],{"emptyLinePlaceholder":174},[148,187,188],{"class":150,"line":25},[148,189,190],{},"The body. Written for the agent, not for a human reader.\n",[192,193,195],"h3",{"id":194},"what-the-model-sees","What the model sees",[88,197,198,199,202,203,206,207,210],{},"Only the ",[104,200,201],{},"name",", the ",[104,204,205],{},"description"," and the ",[104,208,209],{},"path"," go into the system prompt:",[128,212,216],{"className":213,"code":214,"language":215,"meta":134,"style":134},"language-xml shiki shiki-themes vitesse-dark","Load a skill when its description matches what you are about to do: read its `path`, then follow it.\n\u003Cavailable_skills>\n  \u003Cskill name=\"archive-extraction\" path=\"\u002Fhome\u002Fyou\u002F.locac\u002Fskills\u002Farchive-extraction\u002FSKILL.md\">Traversal and zip-slip in archive handlers…\u003C\u002Fskill>\n\u003C\u002Favailable_skills>\n","xml",[104,217,218,223,228,233],{"__ignoreMap":134},[148,219,220],{"class":150,"line":29},[148,221,222],{},"Load a skill when its description matches what you are about to do: read its `path`, then follow it.\n",[148,224,225],{"class":150,"line":33},[148,226,227],{},"\u003Cavailable_skills>\n",[148,229,230],{"class":150,"line":41},[148,231,232],{},"  \u003Cskill name=\"archive-extraction\" path=\"\u002Fhome\u002Fyou\u002F.locac\u002Fskills\u002Farchive-extraction\u002FSKILL.md\">Traversal and zip-slip in archive handlers…\u003C\u002Fskill>\n",[148,234,235],{"class":150,"line":166},[148,236,237],{},"\u003C\u002Favailable_skills>\n",[88,239,240,241,244],{},"The ",[92,242,243],{},"body is not in the prompt",". The agent reads the file when the description matches what it is\nabout to do, which is what keeps a large skill library affordable: the menu costs a line per skill,\nthe playbook costs nothing until it is used.",[88,246,247],{},"Write the description as a trigger, not as a title. \"Traversal and zip-slip in archive handlers …\nUse when auditing tar\u002Fzip extraction\" is a description the model can route on; \"archive stuff\" is\nnot.",[192,249,251],{"id":250},"optional-front-matter","Optional front matter",[253,254,255,268],"table",{},[256,257,258],"thead",{},[259,260,261,265],"tr",{},[262,263,264],"th",{},"Field",[262,266,267],{},"Effect",[269,270,271,293,307,317,326],"tbody",{},[259,272,273,279],{},[274,275,276],"td",{},[104,277,278],{},"disable-model-invocation",[274,280,281,284,285,288,289,292],{},[104,282,283],{},"true"," removes the skill from ",[104,286,287],{},"\u003Cavailable_skills>",". It stays invokable as ",[104,290,291],{},"\u002Fskill:\u003Cname>"," in the TUI",[259,294,295,300],{},[274,296,297],{},[104,298,299],{},"allowed-tools",[274,301,302,303,306],{},"Parsed and stored. ",[92,304,305],{},"Not yet enforced",", experimental",[259,308,309,314],{},[274,310,311],{},[104,312,313],{},"version",[274,315,316],{},"Free-form string, carried through",[259,318,319,324],{},[274,320,321],{},[104,322,323],{},"license",[274,325,316],{},[259,327,328,333],{},[274,329,330],{},[104,331,332],{},"metadata",[274,334,335,336,339],{},"An inline ",[104,337,338],{},"key=value, key2=value2"," map",[88,341,342,344],{},[104,343,278],{}," is the lever for a skill you want available but not advertised: a long\nchecklist you invoke by hand, whose description would otherwise cost prompt tokens on every request\nof every run.",[88,346,347,349,350,353,354,357,358,361,362,364],{},[104,348,332],{}," is an inline map rather than nested YAML because the front-matter parser is hand-rolled:\nlocac ships with no YAML dependency. It handles scalars and ",[104,351,352],{},"key:"," followed by indented ",[104,355,356],{},"- item","\nlists, and it splits on both LF and CRLF. That last detail was a bug: ",[104,359,360],{},"\\r"," is a line terminator to\na JavaScript regex, so a CRLF-authored ",[104,363,125],{}," parsed as having no front matter at all and the\nskill vanished without a message.",[192,366,368],{"id":367},"discovery-order","Discovery order",[88,370,371,372,375],{},"Skills are discovered from the user directory first, then the bundled set. A user skill ",[92,373,374],{},"overrides","\na bundled one of the same name, and the merged list is sorted by name in byte order, not by locale\ncollation, because the rendered block is a model input and must be identical on every host.",[88,377,378,379,382,383,385,386,389,390,393],{},"The glob is exactly ",[104,380,381],{},"*\u002FSKILL.md",", one level deep. It is not recursive, and that is deliberate: a\nskill's description lands in the system prompt, so anything the glob matches is prompt text. At\nunbounded depth, a stray ",[104,384,125],{}," inside something you merely cloned or vendored into\n",[104,387,388],{},"~\u002F.locac\u002Fskills",", such as a ",[104,391,392],{},"node_modules"," tree or a fixtures directory, became a system-prompt entry\nnobody chose to install.",[192,395,397],{"id":396},"the-bundled-skills","The bundled skills",[88,399,400],{},"Three ship inside the binary:",[253,402,403,413],{},[256,404,405],{},[259,406,407,410],{},[262,408,409],{},"Skill",[262,411,412],{},"What it is for",[269,414,415,438,448],{},[259,416,417,422],{},[274,418,419],{},[104,420,421],{},"srcwalk",[274,423,424,425,428,429,428,432,428,435],{},"Source-to-sink tracing: sink-first, source-first, and meet-in-the-middle workflows over ",[104,426,427],{},"outline"," \u002F ",[104,430,431],{},"grok_sink",[104,433,434],{},"search",[104,436,437],{},"read",[259,439,440,445],{},[274,441,442],{},[104,443,444],{},"verify",[274,446,447],{},"Turning a candidate flow into a proven finding: reachability, controllability, sanitisation, default-deployment, PoC reproduction",[259,449,450,455],{},[274,451,452],{},[104,453,454],{},"variant",[274,456,457,458,460],{},"After a bug clears ",[104,459,444],{},", finding its siblings: the same root cause elsewhere, generalised one element at a time",[88,462,463,464,466,467,469,470,472],{},"They are the intended sequence: ",[104,465,421],{}," produces candidates, ",[104,468,444],{}," kills the false positives,\n",[104,471,454],{}," turns one confirmed bug into a family.",[88,474,475,476,478,479,482],{},"In a development checkout they are read from the source tree. In a compiled binary each ",[104,477,125],{}," is\nembedded and materialised on first use under ",[104,480,481],{},"~\u002F.locac\u002Fbundled-skills\u002F",", refreshed only when the\ncontent differs.",[88,484,485,486,488,489,492,493,497],{},"That location is not incidental. It is under ",[104,487,106],{}," rather than the system temp directory\nbecause sandboxed target code can write to ",[104,490,491],{},"tmpdir()"," on POSIX, and a bundled skill's description\ngoes into the system prompt, so a temp-directory extraction would be a persistent prompt-injection\nchannel. It is a ",[494,495,496],"em",{},"sibling"," of the user skills directory rather than a child, because the dashboard\ndecides whether a skill is editable by testing the user-directory prefix, and a bundled skill must\nnot be editable through the browser.",[192,499,501],{"id":500},"managing-them","Managing them",[128,503,507],{"className":504,"code":505,"language":506,"meta":134,"style":134},"language-bash shiki shiki-themes vitesse-dark","locac skills list\nlocac skills add .\u002Fmy-skill            # a SKILL.md file, or a directory containing one\nlocac skills remove archive-extraction\nlocac skills edit archive-extraction\n","bash",[104,508,509,522,538,550],{"__ignoreMap":134},[148,510,511,515,519],{"class":150,"line":29},[148,512,514],{"class":513},"sCK9x","locac",[148,516,518],{"class":517},"s7rlk"," skills",[148,520,521],{"class":517}," list\n",[148,523,524,526,528,531,534],{"class":150,"line":33},[148,525,514],{"class":513},[148,527,518],{"class":517},[148,529,530],{"class":517}," add",[148,532,533],{"class":517}," .\u002Fmy-skill",[148,535,537],{"class":536},"sux-A","            # a SKILL.md file, or a directory containing one\n",[148,539,540,542,544,547],{"class":150,"line":41},[148,541,514],{"class":513},[148,543,518],{"class":517},[148,545,546],{"class":517}," remove",[148,548,549],{"class":517}," archive-extraction\n",[148,551,552,554,556,559],{"class":150,"line":166},[148,553,514],{"class":513},[148,555,518],{"class":517},[148,557,558],{"class":517}," edit",[148,560,549],{"class":517},[88,562,563,564,567,568,571],{},"The install name comes from the front matter and must be a safe slug: letters, digits, ",[104,565,566],{},"_"," and ",[104,569,570],{},"-",",\nstarting with a letter or digit. No separators, no dots, no traversal:",[128,573,576],{"className":574,"code":575,"language":133,"meta":134},[131],"error: Unsafe skill name \"..\u002Fevil\" — use letters, digits, _ or -.\nerror: SKILL.md must have `name` and `description` frontmatter.\n",[104,577,575],{"__ignoreMap":134},[88,579,580,581,115],{},"Installing over an existing skill of the same name replaces it. Full command behaviour is in\n",[113,582,6],{"href":5},[192,584,586],{"id":585},"installing-from-a-url","Installing from a URL",[128,588,590],{"className":504,"code":589,"language":506,"meta":134,"style":134},"locac skills add https:\u002F\u002Fgithub.com\u002Fanthropics\u002Fskills --all        # prints the index, installs nothing\nlocac skills add https:\u002F\u002Fgithub.com\u002Fanthropics\u002Fskills --all --yes  # installs all 18\nlocac skills add https:\u002F\u002Fgithub.com\u002Fanthropics\u002Fskills pdf --yes    # installs one\n",[104,591,592,610,628],{"__ignoreMap":134},[148,593,594,596,598,600,603,607],{"class":150,"line":29},[148,595,514],{"class":513},[148,597,518],{"class":517},[148,599,530],{"class":517},[148,601,602],{"class":517}," https:\u002F\u002Fgithub.com\u002Fanthropics\u002Fskills",[148,604,606],{"class":605},"sXjYR"," --all",[148,608,609],{"class":536},"        # prints the index, installs nothing\n",[148,611,612,614,616,618,620,622,625],{"class":150,"line":33},[148,613,514],{"class":513},[148,615,518],{"class":517},[148,617,530],{"class":517},[148,619,602],{"class":517},[148,621,606],{"class":605},[148,623,624],{"class":605}," --yes",[148,626,627],{"class":536},"  # installs all 18\n",[148,629,630,632,634,636,638,641,643],{"class":150,"line":41},[148,631,514],{"class":513},[148,633,518],{"class":517},[148,635,530],{"class":517},[148,637,602],{"class":517},[148,639,640],{"class":517}," pdf",[148,642,624],{"class":605},[148,644,645],{"class":536},"    # installs one\n",[88,647,648,649,652,653,656,657,660,661,664],{},"A github repo, tree or blob URL, a ",[104,650,651],{},"raw.githubusercontent"," URL, or a bare ",[104,654,655],{},"https"," ",[104,658,659],{},".md",". The repo is\nshallow-cloned into a temp directory, ",[104,662,663],{},".git"," is dropped, and the skill directories found in it become\nthe menu.",[88,666,667],{},"Everything about the path from URL to prompt is adversarial, and it is gated three times.",[88,669,670,673,674,676,677,680,681,684,685,567,687,690],{},[92,671,672],{},"The URL is parsed offline before anything is fetched."," A ref or a subpath is validated component by\ncomponent against a pattern whose leading class excludes ",[104,675,570],{},", so a crafted URL cannot smuggle a git\n",[494,678,679],{},"flag"," into the clone: ",[104,682,683],{},"--upload-pack=…"," is remote code execution with no shell involved. ",[104,686,115],{},[104,688,689],{},"..","\nare rejected, so a percent-encoded traversal cannot escape the clone.",[88,692,693,696],{},[92,694,695],{},"The host is resolved and re-checked before the connection",", by the same guard DAST egress uses -\nso a skill URL is not a path to the cloud metadata endpoint.",[88,698,699,702,703,706,707,709],{},[92,700,701],{},"The search for skills inside the clone is depth-bounded"," to two levels. That is enough for both a\nflat layout and Anthropic's ",[104,704,705],{},"document-skills\u002Fpdf\u002FSKILL.md",", and short of sweeping a vendored\n",[104,708,392],{}," into a menu you are about to approve.",[88,711,712,713,716],{},"Then the operator gate. A description lands verbatim in the system prompt, beside the harness\ninvariants and with no fence around it, so nothing remote installs until you have seen the exact text\nthat will go there. Control characters are ",[92,714,715],{},"stripped rather than escaped"," on the way to your\nterminal: an ANSI sequence from an untrusted repo can repaint the very line asking for approval.",[192,718,720],{"id":719},"provenance-and-updates","Provenance and updates",[88,722,723,724,727,728,126],{},"Each install from a URL writes a ",[104,725,726],{},".source.json"," beside the ",[104,729,125],{},[128,731,735],{"className":732,"code":733,"language":734,"meta":134,"style":134},"language-json shiki shiki-themes vitesse-dark","{\n    \"commit\": \"b29e7cf65e5cb78a5ac33d582270551bc74a14eb\",\n    \"dir\": \"skills\u002Fpdf\",\n    \"kind\": \"git\",\n    \"ref\": null,\n    \"source\": \"https:\u002F\u002Fgithub.com\u002Fanthropics\u002Fskills\"\n}\n","json",[104,736,737,743,770,790,810,827,846],{"__ignoreMap":134},[148,738,739],{"class":150,"line":29},[148,740,742],{"class":741},"s_pn2","{\n",[148,744,745,749,753,756,758,762,765,767],{"class":150,"line":33},[148,746,748],{"class":747},"s6USN","    \"",[148,750,752],{"class":751},"sm68I","commit",[148,754,755],{"class":747},"\"",[148,757,126],{"class":741},[148,759,761],{"class":760},"sNJcY"," \"",[148,763,764],{"class":517},"b29e7cf65e5cb78a5ac33d582270551bc74a14eb",[148,766,755],{"class":760},[148,768,769],{"class":741},",\n",[148,771,772,774,777,779,781,783,786,788],{"class":150,"line":41},[148,773,748],{"class":747},[148,775,776],{"class":751},"dir",[148,778,755],{"class":747},[148,780,126],{"class":741},[148,782,761],{"class":760},[148,784,785],{"class":517},"skills\u002Fpdf",[148,787,755],{"class":760},[148,789,769],{"class":741},[148,791,792,794,797,799,801,803,806,808],{"class":150,"line":166},[148,793,748],{"class":747},[148,795,796],{"class":751},"kind",[148,798,755],{"class":747},[148,800,126],{"class":741},[148,802,761],{"class":760},[148,804,805],{"class":517},"git",[148,807,755],{"class":760},[148,809,769],{"class":741},[148,811,812,814,817,819,821,825],{"class":150,"line":171},[148,813,748],{"class":747},[148,815,816],{"class":751},"ref",[148,818,755],{"class":747},[148,820,126],{"class":741},[148,822,824],{"class":823},"s3QIE"," null",[148,826,769],{"class":741},[148,828,829,831,834,836,838,840,843],{"class":150,"line":57},[148,830,748],{"class":747},[148,832,833],{"class":751},"source",[148,835,755],{"class":747},[148,837,126],{"class":741},[148,839,761],{"class":760},[148,841,842],{"class":517},"https:\u002F\u002Fgithub.com\u002Fanthropics\u002Fskills",[148,844,845],{"class":760},"\"\n",[148,847,848],{"class":150,"line":183},[148,849,850],{"class":741},"}\n",[88,852,853,854,857,858,861],{},"It lives in the skill directory rather than in ",[104,855,856],{},"config.json"," because the directory ",[92,859,860],{},"is"," the install:\ndelete a skill by hand and its record goes with it, instead of leaving a stale entry behind. There is\nno timestamp. The commit is the freshness key, and a clock in a file that gets compared is a\ndeterminism bug waiting to happen.",[128,863,865],{"className":504,"code":864,"language":506,"meta":134,"style":134},"locac skills update pdf\nlocac skills update --all\n",[104,866,867,879],{"__ignoreMap":134},[148,868,869,871,873,876],{"class":150,"line":29},[148,870,514],{"class":513},[148,872,518],{"class":517},[148,874,875],{"class":517}," update",[148,877,878],{"class":517}," pdf\n",[148,880,881,883,885,887],{"class":150,"line":33},[148,882,514],{"class":513},[148,884,518],{"class":517},[148,886,875],{"class":517},[148,888,889],{"class":605}," --all\n",[88,891,892,895,896,899],{},[104,893,894],{},"update"," replays the record rather than re-deriving anything from the URL. Same commit means up to\ndate and nothing is written; a changed name or description stops for ",[104,897,898],{},"--yes","; a rename removes the old\ndirectory, which otherwise leaves two installs with the stale one still loading.",[88,901,902,903,906,907,909],{},"The sidecar is read as ",[92,904,905],{},"remote input, not trusted config",". The installer copies a source tree\nverbatim, so a repo shipping its own ",[104,908,726],{}," would otherwise choose what a later update cloned\nfrom and with which flags. The copier drops any it finds, and the reader independently re-validates\nevery field. Either defence alone would do; neither has to be perfect.",[88,911,912,913,915],{},"A skill installed from a local path has no record and is skipped by ",[104,914,894],{}," - it is yours, and\nlocac has nowhere to re-fetch it from.",[117,917,98],{"id":918},"roles",[88,920,921,922,925],{},"A subagent is not a copy of the main agent with a different prompt. It is spawned ",[92,923,924],{},"as a role",", and\nthe role is a typed contract:",[253,927,928,938],{},[256,929,930],{},[259,931,932,935],{},[262,933,934],{},"Part",[262,936,937],{},"Meaning",[269,939,940,950,960,974,988],{},[259,941,942,947],{},[274,943,944],{},[104,945,946],{},"systemPrompt",[274,948,949],{},"Replaces the default prompt for the child",[259,951,952,957],{},[274,953,954],{},[104,955,956],{},"tools",[274,958,959],{},"The allow-list of tool names the child may call",[259,961,962,967],{},[274,963,964],{},[104,965,966],{},"taskSchema",[274,968,969,970,973],{},"Validates the spawn input ",[92,971,972],{},"before"," the child is created",[259,975,976,981],{},[274,977,978],{},[104,979,980],{},"reportSchema",[274,982,983,984,987],{},"Validates the child's ",[104,985,986],{},"submit_report"," payload",[259,989,990,995],{},[274,991,992],{},[104,993,994],{},"model",[274,996,997],{},"Optional per-role model. Unset means the child inherits the parent's",[88,999,1000],{},"Two roles ship built in.",[192,1002,1004],{"id":1003},"surface-mapper",[104,1005,1003],{},[88,1007,1008,1009,567,1012,1015,1016,1018,1019,1022],{},"Read-only reconnaissance. Its tool list is the eleven read-only tools plus ",[104,1010,1011],{},"scratchpad",[104,1013,1014],{},"memory",".\nNo ",[104,1017,506],{},", no ",[104,1020,1021],{},"execute",", nothing that writes. It enumerates entry points, sinks and trust boundaries\nand reports:",[128,1024,1026],{"className":732,"code":1025,"language":734,"meta":134,"style":134},"{\n  \"surfaces\": [{ \"kind\": \"http-route\", \"location\": \"src\u002Fapi\u002Fupload.ts:24\", \"notes\": \"multipart\" }],\n  \"summary\": \"…\"\n}\n",[104,1027,1028,1032,1102,1120],{"__ignoreMap":134},[148,1029,1030],{"class":150,"line":29},[148,1031,742],{"class":741},[148,1033,1034,1037,1040,1042,1044,1047,1049,1051,1053,1055,1057,1060,1062,1065,1067,1070,1072,1074,1076,1079,1081,1083,1085,1088,1090,1092,1094,1097,1099],{"class":150,"line":33},[148,1035,1036],{"class":747},"  \"",[148,1038,1039],{"class":751},"surfaces",[148,1041,755],{"class":747},[148,1043,126],{"class":741},[148,1045,1046],{"class":741}," [{",[148,1048,761],{"class":747},[148,1050,796],{"class":751},[148,1052,755],{"class":747},[148,1054,126],{"class":741},[148,1056,761],{"class":760},[148,1058,1059],{"class":517},"http-route",[148,1061,755],{"class":760},[148,1063,1064],{"class":741},",",[148,1066,761],{"class":747},[148,1068,1069],{"class":751},"location",[148,1071,755],{"class":747},[148,1073,126],{"class":741},[148,1075,761],{"class":760},[148,1077,1078],{"class":517},"src\u002Fapi\u002Fupload.ts:24",[148,1080,755],{"class":760},[148,1082,1064],{"class":741},[148,1084,761],{"class":747},[148,1086,1087],{"class":751},"notes",[148,1089,755],{"class":747},[148,1091,126],{"class":741},[148,1093,761],{"class":760},[148,1095,1096],{"class":517},"multipart",[148,1098,755],{"class":760},[148,1100,1101],{"class":741}," }],\n",[148,1103,1104,1106,1109,1111,1113,1115,1118],{"class":150,"line":41},[148,1105,1036],{"class":747},[148,1107,1108],{"class":751},"summary",[148,1110,755],{"class":747},[148,1112,126],{"class":741},[148,1114,761],{"class":760},[148,1116,1117],{"class":517},"…",[148,1119,845],{"class":760},[148,1121,1122],{"class":150,"line":166},[148,1123,850],{"class":741},[192,1125,1127],{"id":1126},"exploit-verifier",[104,1128,1126],{},[88,1130,1131,1132,1134,1135,1134,1137,1134,1140,1143,1144,1134,1147,1134,1150,567,1152,1154,1155,1158],{},"The armed role: the read-only tools plus ",[104,1133,506],{},", ",[104,1136,1021],{},[104,1138,1139],{},"reproduce",[104,1141,1142],{},"coverage",", the DAST tools,\n",[104,1145,1146],{},"record_finding",[104,1148,1149],{},"score_finding",[104,1151,1011],{},[104,1153,1014],{},". Its instruction is to reproduce a\ncandidate ",[92,1156,1157],{},"from scratch"," and try hard to refute it.",[88,1160,1161,1162,1165,1166,107,1169,428,1172,428,1174,1177,1178,1181],{},"Its task accepts a ",[104,1163,1164],{},"finding_id",", a ",[104,1167,1168],{},"candidate",[104,1170,1171],{},"file",[104,1173,150],{},[104,1175,1176],{},"vulnClass","), or both, not either\none exclusively, because ",[104,1179,1180],{},"cross_verify"," sends both: the id to vote on, plus the resolved location,\nwhich the child has no tool to look up for itself.",[88,1183,1184,1185,1188,1189,1192],{},"It also accepts a ",[104,1186,1187],{},"lens",", which is what makes the quorum three ",[494,1190,1191],{},"different"," verifiers rather than\nthree identical ones:",[253,1194,1195,1205],{},[256,1196,1197],{},[259,1198,1199,1202],{},[262,1200,1201],{},"Lens",[262,1203,1204],{},"Instruction",[269,1206,1207,1226,1236],{},[259,1208,1209,1213],{},[274,1210,1211],{},[104,1212,1139],{},[274,1214,1215,1216,1218,1219,1222,1223,1225],{},"Build the PoC from scratch, run it through ",[104,1217,1139],{}," with a ",[104,1220,1221],{},"claim"," whose location and ",[104,1224,1164],{}," match the candidate, and promote only if the oracle triggered",[259,1227,1228,1233],{},[274,1229,1230],{},[104,1231,1232],{},"refute",[274,1234,1235],{},"Try your hardest to break the claim: find the sanitiser or guard that makes it a false positive",[259,1237,1238,1243],{},[274,1239,1240],{},[104,1241,1242],{},"boundary",[274,1244,1245,1246,1249],{},"Confirm it fires in the ",[92,1247,1248],{},"default"," deployment - no debug flag, no example credentials, no administrator mistake - and independently mint the same bound proof",[88,1251,1252],{},"Its report is a verdict:",[128,1254,1256],{"className":732,"code":1255,"language":734,"meta":134,"style":134},"{ \"verdict\": \"promoted\", \"finding_id\": \"f_…\", \"artifact_id\": \"ev_…\", \"rationale\": \"…\" }\n",[104,1257,1258],{"__ignoreMap":134},[148,1259,1260,1263,1265,1268,1270,1272,1274,1277,1279,1281,1283,1285,1287,1289,1291,1294,1296,1298,1300,1303,1305,1307,1309,1312,1314,1316,1318,1321,1323,1325,1327,1329,1331],{"class":150,"line":29},[148,1261,1262],{"class":741},"{",[148,1264,761],{"class":747},[148,1266,1267],{"class":751},"verdict",[148,1269,755],{"class":747},[148,1271,126],{"class":741},[148,1273,761],{"class":760},[148,1275,1276],{"class":517},"promoted",[148,1278,755],{"class":760},[148,1280,1064],{"class":741},[148,1282,761],{"class":747},[148,1284,1164],{"class":751},[148,1286,755],{"class":747},[148,1288,126],{"class":741},[148,1290,761],{"class":760},[148,1292,1293],{"class":517},"f_…",[148,1295,755],{"class":760},[148,1297,1064],{"class":741},[148,1299,761],{"class":747},[148,1301,1302],{"class":751},"artifact_id",[148,1304,755],{"class":747},[148,1306,126],{"class":741},[148,1308,761],{"class":760},[148,1310,1311],{"class":517},"ev_…",[148,1313,755],{"class":760},[148,1315,1064],{"class":741},[148,1317,761],{"class":747},[148,1319,1320],{"class":751},"rationale",[148,1322,755],{"class":747},[148,1324,126],{"class":741},[148,1326,761],{"class":760},[148,1328,1117],{"class":517},[148,1330,755],{"class":760},[148,1332,1333],{"class":741}," }\n",[88,1335,1336,1337,1340,1341,1344,1345,656,1347,1349,1350,1352,1353,1356,1357,1359,1360,1362],{},"The role's own prompt carries the quorum's rules, so a verifier is told them rather than discovering\nthem at the gate. It must cite proof ",[92,1338,1339],{},"it"," minted in ",[92,1342,1343],{},"this"," round, bound to ",[92,1346,1343],{},[104,1348,1164],{},"\nand location; re-citing the candidate's existing artifact, or an artifact about some other path, is\nnot independent evidence. Generic ",[104,1351,1021],{}," output and a plain replay are diagnostics, so a replay\ncounts only with a baseline-differential assertion, and re-verifying a DAST finding means calling\n",[104,1354,1355],{},"run_module"," with its ",[104,1358,1164],{}," so only an exact URL-and-class match binds. And when a PoC runs\nwithout crashing, it emits an lcov report and calls ",[104,1361,1142],{}," on the sink line to tell \"not reached\"\n(fix the PoC) apart from \"reached, no crash\" (refine the payload) - while remembering that coverage\nnever satisfies the evidence gate either.",[192,1364,1366],{"id":1365},"defining-and-overriding-roles","Defining and overriding roles",[88,1368,1369,1370,1372],{},"The config's ",[104,1371,918],{}," map can override a built-in's tunable fields, or define a new role entirely:",[128,1374,1376],{"className":732,"code":1375,"language":734,"meta":134,"style":134},"{\n  \"roles\": {\n    \"exploit-verifier\": {\n      \"model\": \"claude-opus-4-8\"\n    },\n    \"docs-reader\": {\n      \"description\": \"Read the vendor's changelog and advisories for the version under test.\",\n      \"systemPrompt\": \"You read documentation only. Never modify files.\",\n      \"tools\": [\"read\", \"search\", \"find\", \"ls\", \"submit_report\"]\n    }\n  }\n}\n",[104,1377,1378,1382,1395,1407,1425,1430,1443,1462,1481,1537,1542,1547],{"__ignoreMap":134},[148,1379,1380],{"class":150,"line":29},[148,1381,742],{"class":741},[148,1383,1384,1386,1388,1390,1392],{"class":150,"line":33},[148,1385,1036],{"class":747},[148,1387,918],{"class":751},[148,1389,755],{"class":747},[148,1391,126],{"class":741},[148,1393,1394],{"class":741}," {\n",[148,1396,1397,1399,1401,1403,1405],{"class":150,"line":41},[148,1398,748],{"class":747},[148,1400,1126],{"class":751},[148,1402,755],{"class":747},[148,1404,126],{"class":741},[148,1406,1394],{"class":741},[148,1408,1409,1412,1414,1416,1418,1420,1423],{"class":150,"line":166},[148,1410,1411],{"class":747},"      \"",[148,1413,994],{"class":751},[148,1415,755],{"class":747},[148,1417,126],{"class":741},[148,1419,761],{"class":760},[148,1421,1422],{"class":517},"claude-opus-4-8",[148,1424,845],{"class":760},[148,1426,1427],{"class":150,"line":171},[148,1428,1429],{"class":741},"    },\n",[148,1431,1432,1434,1437,1439,1441],{"class":150,"line":57},[148,1433,748],{"class":747},[148,1435,1436],{"class":751},"docs-reader",[148,1438,755],{"class":747},[148,1440,126],{"class":741},[148,1442,1394],{"class":741},[148,1444,1445,1447,1449,1451,1453,1455,1458,1460],{"class":150,"line":183},[148,1446,1411],{"class":747},[148,1448,205],{"class":751},[148,1450,755],{"class":747},[148,1452,126],{"class":741},[148,1454,761],{"class":760},[148,1456,1457],{"class":517},"Read the vendor's changelog and advisories for the version under test.",[148,1459,755],{"class":760},[148,1461,769],{"class":741},[148,1463,1464,1466,1468,1470,1472,1474,1477,1479],{"class":150,"line":25},[148,1465,1411],{"class":747},[148,1467,946],{"class":751},[148,1469,755],{"class":747},[148,1471,126],{"class":741},[148,1473,761],{"class":760},[148,1475,1476],{"class":517},"You read documentation only. Never modify files.",[148,1478,755],{"class":760},[148,1480,769],{"class":741},[148,1482,1483,1485,1487,1489,1491,1494,1496,1498,1500,1502,1504,1506,1508,1510,1512,1515,1517,1519,1521,1524,1526,1528,1530,1532,1534],{"class":150,"line":73},[148,1484,1411],{"class":747},[148,1486,956],{"class":751},[148,1488,755],{"class":747},[148,1490,126],{"class":741},[148,1492,1493],{"class":741}," [",[148,1495,755],{"class":760},[148,1497,437],{"class":517},[148,1499,755],{"class":760},[148,1501,1064],{"class":741},[148,1503,761],{"class":760},[148,1505,434],{"class":517},[148,1507,755],{"class":760},[148,1509,1064],{"class":741},[148,1511,761],{"class":760},[148,1513,1514],{"class":517},"find",[148,1516,755],{"class":760},[148,1518,1064],{"class":741},[148,1520,761],{"class":760},[148,1522,1523],{"class":517},"ls",[148,1525,755],{"class":760},[148,1527,1064],{"class":741},[148,1529,761],{"class":760},[148,1531,986],{"class":517},[148,1533,755],{"class":760},[148,1535,1536],{"class":741},"]\n",[148,1538,1539],{"class":150,"line":37},[148,1540,1541],{"class":741},"    }\n",[148,1543,1544],{"class":150,"line":20},[148,1545,1546],{"class":741},"  }\n",[148,1548,1549],{"class":150,"line":65},[148,1550,850],{"class":741},[88,1552,1553,1554,1134,1556,1134,1558,567,1560,1562,1563,1566],{},"Only ",[104,1555,205],{},[104,1557,946],{},[104,1559,956],{},[104,1561,994],{}," are overridable. ",[92,1564,1565],{},"Schemas are code",": a\nbuilt-in's task and report shapes cannot be changed from JSON, because they are what the rest of the\npipeline relies on.",[88,1568,1569],{},"A role defined here under a new name gets generic schemas instead:",[128,1571,1573],{"className":732,"code":1572,"language":734,"meta":134,"style":134},"\u002F\u002F task\n{ \"task\": \"…\", \"context\": { } }\n\u002F\u002F report\n{ \"status\": \"done\", \"summary\": \"…\", \"findingIds\": [\"…\"] }\n",[104,1574,1575,1580,1618,1623],{"__ignoreMap":134},[148,1576,1577],{"class":150,"line":29},[148,1578,1579],{"class":536},"\u002F\u002F task\n",[148,1581,1582,1584,1586,1589,1591,1593,1595,1597,1599,1601,1603,1606,1608,1610,1613,1616],{"class":150,"line":33},[148,1583,1262],{"class":741},[148,1585,761],{"class":747},[148,1587,1588],{"class":751},"task",[148,1590,755],{"class":747},[148,1592,126],{"class":741},[148,1594,761],{"class":760},[148,1596,1117],{"class":517},[148,1598,755],{"class":760},[148,1600,1064],{"class":741},[148,1602,761],{"class":747},[148,1604,1605],{"class":751},"context",[148,1607,755],{"class":747},[148,1609,126],{"class":741},[148,1611,1612],{"class":741}," {",[148,1614,1615],{"class":741}," }",[148,1617,1333],{"class":741},[148,1619,1620],{"class":150,"line":41},[148,1621,1622],{"class":536},"\u002F\u002F report\n",[148,1624,1625,1627,1629,1632,1634,1636,1638,1641,1643,1645,1647,1649,1651,1653,1655,1657,1659,1661,1663,1666,1668,1670,1672,1674,1676,1678,1681],{"class":150,"line":166},[148,1626,1262],{"class":741},[148,1628,761],{"class":747},[148,1630,1631],{"class":751},"status",[148,1633,755],{"class":747},[148,1635,126],{"class":741},[148,1637,761],{"class":760},[148,1639,1640],{"class":517},"done",[148,1642,755],{"class":760},[148,1644,1064],{"class":741},[148,1646,761],{"class":747},[148,1648,1108],{"class":751},[148,1650,755],{"class":747},[148,1652,126],{"class":741},[148,1654,761],{"class":760},[148,1656,1117],{"class":517},[148,1658,755],{"class":760},[148,1660,1064],{"class":741},[148,1662,761],{"class":747},[148,1664,1665],{"class":751},"findingIds",[148,1667,755],{"class":747},[148,1669,126],{"class":741},[148,1671,1493],{"class":741},[148,1673,755],{"class":760},[148,1675,1117],{"class":517},[148,1677,755],{"class":760},[148,1679,1680],{"class":741},"]",[148,1682,1333],{"class":741},[88,1684,1685,1686,1689],{},"Every role's tool list is validated against the tool universe ",[92,1687,1688],{},"at load",", so a typo fails at\nstartup rather than at spawn time, halfway through a run:",[128,1691,1694],{"className":1692,"code":1693,"language":133,"meta":134},[131],"Role \"docs-reader\" names unknown tool \"raed\". Known: bash, code_search, coverage, …\n",[104,1695,1693],{"__ignoreMap":134},[192,1697,1699],{"id":1698},"spawning","Spawning",[128,1701,1704],{"className":1702,"code":1703,"language":133,"meta":134},[131],"spawn_subagent(role: \"exploit-verifier\", input: { finding_id: \"f_…\", lens: \"refute\" })\n→ Spawned exploit-verifier subagent sub_3. Do other work, then wait_subagent id=\"sub_3\".\n",[104,1705,1703],{"__ignoreMap":134},[88,1707,1708],{},"The input is validated against the role's task schema before the child exists, so a malformed spawn\ncosts nothing:",[128,1710,1713],{"className":1711,"code":1712,"language":133,"meta":134},[131],"Unknown role \"verifier\". Available: exploit-verifier, surface-mapper.\nInvalid input for role \"exploit-verifier\": provide finding_id, candidate, or both\n",[104,1714,1712],{"__ignoreMap":134},[88,1716,1717,1718,1720,1721,1724,1725,1728,1729,1731],{},"A child cannot spawn children, because the spawn\u002Fwait\u002Fsteer tools are never in a child's tool set, and\n",[104,1719,986],{}," is always added to it, so it can always answer its parent. ",[104,1722,1723],{},"--tools"," and\n",[104,1726,1727],{},"--exclude-tools"," apply to children too; ",[104,1730,986],{}," is the one name neither flag can remove.",[88,1733,1734,1735,115],{},"The orchestration tools themselves are in the ",[113,1736,64],{"href":63},[117,1738,1740],{"id":1739},"next","Next",[88,1742,1743,1745],{},[113,1744,76],{"href":75}," covers the browser UI over a session database, and the\ntwo eval verbs that measure whether a change to any of the above actually helped.",[1747,1748,1749],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sCK9x, html code.shiki .sCK9x{--shiki-default:#80A665}html pre.shiki code .s7rlk, html code.shiki .s7rlk{--shiki-default:#C98A7D}html pre.shiki code .sux-A, html code.shiki .sux-A{--shiki-default:#758575DD}html pre.shiki code .sXjYR, html code.shiki .sXjYR{--shiki-default:#C99076}html pre.shiki code .s_pn2, html code.shiki .s_pn2{--shiki-default:#666666}html pre.shiki code .s6USN, html code.shiki .s6USN{--shiki-default:#B8A96577}html pre.shiki code .sm68I, html code.shiki .sm68I{--shiki-default:#B8A965}html pre.shiki code .sNJcY, html code.shiki .sNJcY{--shiki-default:#C98A7D77}html pre.shiki code .s3QIE, html code.shiki .s3QIE{--shiki-default:#4D9375}",{"title":134,"searchDepth":41,"depth":41,"links":1751},[1752,1761,1767],{"id":119,"depth":33,"text":94,"children":1753},[1754,1755,1756,1757,1758,1759,1760],{"id":194,"depth":41,"text":195},{"id":250,"depth":41,"text":251},{"id":367,"depth":41,"text":368},{"id":396,"depth":41,"text":397},{"id":500,"depth":41,"text":501},{"id":585,"depth":41,"text":586},{"id":719,"depth":41,"text":720},{"id":918,"depth":33,"text":98,"children":1762},[1763,1764,1765,1766],{"id":1003,"depth":41,"text":1003},{"id":1126,"depth":41,"text":1126},{"id":1365,"depth":41,"text":1366},{"id":1698,"depth":41,"text":1699},{"id":1739,"depth":33,"text":1740},"[object Object]","md",{},{"title":60,"description":1772},{"The two ways to shape what the agent does":1773,"group":19,"order":1774},"markdown skills injected into the system prompt, and typed roles that give a subagent its own prompt, tool allow-list and report schema.",15,"docs\u002Fskills-roles","bNlZc5TcXAV1apaTz4g8YPCZKt7qHFACx7xjstTGJhI",1786794387572]