[{"data":1,"prerenderedAt":4391},["ShallowReactive",2],{"navigation":3,"page-\u002Fchapters\u002Fparallelism-shared-state":102,"surround-\u002Fchapters\u002Fparallelism-shared-state":4386},[4,8],{"title":5,"path":6,"stem":7},"Home","\u002F","index",{"title":9,"path":10,"stem":11,"children":12,"page":101},"Chapters","\u002Fchapters","2.chapters",[13,17,21,25,29,33,37,41,45,49,53,57,61,65,69,73,77,81,85,89,93,97],{"title":14,"path":15,"stem":16},"Chapter 1. What an Agent Actually Is","\u002Fchapters\u002Fwhat-an-agent-actually-is","2.chapters\u002F01.what-an-agent-actually-is",{"title":18,"path":19,"stem":20},"Chapter 2. The Minimum Viable Loop","\u002Fchapters\u002Fminimum-viable-loop","2.chapters\u002F02.minimum-viable-loop",{"title":22,"path":23,"stem":24},"Chapter 3. Messages, Turns, and the Transcript","\u002Fchapters\u002Fmessages-turns-transcript","2.chapters\u002F03.messages-turns-transcript",{"title":26,"path":27,"stem":28},"Chapter 4. The Tool Protocol","\u002Fchapters\u002Ftool-protocol","2.chapters\u002F04.tool-protocol",{"title":30,"path":31,"stem":32},"Chapter 5. Streaming, Interruption, and Error Handling","\u002Fchapters\u002Fstreaming-interruption-errors","2.chapters\u002F05.streaming-interruption-errors",{"title":34,"path":35,"stem":36},"Chapter 6. Safe Tool Execution","\u002Fchapters\u002Fsafe-tool-execution","2.chapters\u002F06.safe-tool-execution",{"title":38,"path":39,"stem":40},"Chapter 7. The Context Window Is a Resource","\u002Fchapters\u002Fcontext-window-as-resource","2.chapters\u002F07.context-window-as-resource",{"title":42,"path":43,"stem":44},"Chapter 8. Compaction","\u002Fchapters\u002Fcompaction","2.chapters\u002F08.compaction",{"title":46,"path":47,"stem":48},"Chapter 9. External State: The Scratchpad","\u002Fchapters\u002Fscratchpad","2.chapters\u002F09.scratchpad",{"title":50,"path":51,"stem":52},"Chapter 10. Retrieval","\u002Fchapters\u002Fretrieval","2.chapters\u002F10.retrieval",{"title":54,"path":55,"stem":56},"Chapter 11. Designing Tools Models Can Actually Use","\u002Fchapters\u002Fdesigning-tools","2.chapters\u002F11.designing-tools",{"title":58,"path":59,"stem":60},"Chapter 12. The Tool Cliff and Dynamic Loading","\u002Fchapters\u002Ftool-cliff","2.chapters\u002F12.tool-cliff",{"title":62,"path":63,"stem":64},"Chapter 13. MCP: Tools From the Outside World","\u002Fchapters\u002Fmcp","2.chapters\u002F13.mcp",{"title":66,"path":67,"stem":68},"Chapter 14. Sandboxing and Permissions","\u002Fchapters\u002Fsandboxing-permissions","2.chapters\u002F14.sandboxing-permissions",{"title":70,"path":71,"stem":72},"Chapter 15. Sub-agents","\u002Fchapters\u002Fsub-agents","2.chapters\u002F15.sub-agents",{"title":74,"path":75,"stem":76},"Chapter 16. Structured Plans and Verified Completion","\u002Fchapters\u002Fplans-verified-completion","2.chapters\u002F16.plans-verified-completion",{"title":78,"path":79,"stem":80},"Chapter 17. Parallelism and Shared State","\u002Fchapters\u002Fparallelism-shared-state","2.chapters\u002F17.parallelism-shared-state",{"title":82,"path":83,"stem":84},"Chapter 18. Observability","\u002Fchapters\u002Fobservability","2.chapters\u002F18.observability",{"title":86,"path":87,"stem":88},"Chapter 19. Evals","\u002Fchapters\u002Fevals","2.chapters\u002F19.evals",{"title":90,"path":91,"stem":92},"Chapter 20. Cost Control","\u002Fchapters\u002Fcost-control","2.chapters\u002F20.cost-control",{"title":94,"path":95,"stem":96},"Chapter 21. Resumability and Durable State","\u002Fchapters\u002Fresumability","2.chapters\u002F21.resumability",{"title":98,"path":99,"stem":100},"Chapter 22. What Transfers, Where to Go","\u002Fchapters\u002Fwhat-transfers","2.chapters\u002F22.what-transfers",false,{"id":103,"title":78,"body":104,"description":117,"extension":4381,"meta":4382,"navigation":4383,"path":79,"seo":4384,"stem":80,"__hash__":4385},"content\u002F2.chapters\u002F17.parallelism-shared-state.md",{"type":105,"value":106,"toc":4370},"minimark",[107,111,118,121,128,134,140,143,266,269,274,277,280,283,285,289,1581,1584,1598,1607,1613,1615,1619,1622,1660,2321,2331,2334,2336,2340,2343,2629,2648,3301,3320,3322,3326,3329,3336,3339,3347,3354,3527,3530,3569,3571,3575,4235,4242,4244,4248,4251,4260,4266,4268,4272,4319,4323,4355,4357,4366],[108,109,78],"h1",{"id":110},"chapter-17-parallelism-and-shared-state",[112,113,114],"p",{},[115,116,117],"em",{},"Previously: structured plans with evidence-backed completion. Sub-agents still run sequentially. The payoff for sub-agents comes from running them in parallel — the Anthropic multi-agent finding of 90%+ improvement over single-agent baselines rests on parallelism plus independent context windows, not sub-agents in series.",[112,119,120],{},"Three problems emerge when sub-agents run concurrently.",[112,122,123,127],{},[124,125,126],"strong",{},"Write conflicts."," Two sub-agents both decide to write to the same file at the same moment. One wins, one loses, neither knows. The MAST paper found that coordination breakdowns accounted for 36.9% of multi-agent failures — the largest single category — and shared-state corruption was a recurring mechanism.",[112,129,130,133],{},[124,131,132],{},"Hallucinated consensus."," A sub-agent invents a fact. Another sub-agent, asked to verify, reads the first's output and confirms it. The orchestrator treats \"confirmed\" facts as ground truth. The system produces confident wrong answers because verification closed a loop with no external ground truth at the bottom.",[112,135,136,139],{},[124,137,138],{},"Duplication."," Two sub-agents, given overlapping objectives, do similar work. The parent pays twice. Anthropic's multi-agent research post names this explicitly: \"research the semiconductor shortage\" given to two sub-agents produces two redundant research runs.",[112,141,142],{},"This chapter addresses all three with concrete mechanisms: a lease-based write-ownership system, a verification discipline that grounds claims in external tool calls rather than peer output, and a coordinator that narrows sub-agent objectives to prevent overlap.",[144,145,149,259],"figure",{"className":146},[147,148],"not-prose","my-8",[150,151,158,212,237],"div",{"className":152},[153,154,155,156,157],"grid","grid-cols-1","md:grid-cols-5","gap-3","items-center",[150,159,165,187,201],{"className":160},[161,162,163,164],"md:col-span-2","flex","flex-col","gap-2",[150,166,175,182],{"className":167},[168,169,170,171,172,173,174],"rounded-lg","border-2","border-primary","bg-elevated","px-3","py-2","text-sm",[150,176,181],{"className":177},[178,179,180],"font-mono","text-xs","text-primary","sub-agent A",[150,183,186],{"className":184},[185],"text-default","holds lease ✓",[150,188,192,197],{"className":189},[168,190,191,171,172,173,174],"border","border-default",[150,193,196],{"className":194},[178,179,195],"text-muted","sub-agent B",[150,198,200],{"className":199},[195],"← WAIT (retry)",[150,202,204,208],{"className":203},[168,190,191,171,172,173,174],[150,205,207],{"className":206},[178,179,195],"sub-agent C",[150,209,211],{"className":210},[195],"← CONFLICT",[150,213,217],{"className":214},[215,162,216],"md:col-span-1","justify-center",[150,218,223,227,233],{"className":219,"style":222},[168,190,191,171,172,220,221,174],"py-4","text-center","background:color-mix(in oklab, currentColor 8%, transparent);",[150,224,226],{"className":225},[178,179,195],"LeaseManager",[150,228,232],{"className":229},[185,230,231],"font-semibold","mt-1","broker",[150,234,236],{"className":235},[179,195,231],"1 holder \u002F resource",[150,238,241],{"className":239},[161,162,216,240],"md:justify-start",[150,242,247,251,255],{"className":243,"style":246},[168,190,191,171,244,245,174],"px-4","py-3","max-width:240px;",[150,248,250],{"className":249},[178,179,195],"resource",[150,252,254],{"className":253},[178,185],"scratchpad\u002Fshared.md",[150,256,258],{"className":257},[179,195,231],"exclusive writes only",[260,261,265],"figcaption",{"className":262},[179,195,263,221,264],"mt-3","italic","Three sub-agents, one resource, one lease. The harness serializes writes so concurrent agents can't corrupt shared state.",[267,268],"hr",{},[270,271,273],"h2",{"id":272},"_171-why-llms-dont-have-a-concurrency-model","17.1 Why LLMs Don't Have a Concurrency Model",[112,275,276],{},"LLMs are stateless. Each call is independent; there is no shared memory across calls except what the caller puts in the context. When two sub-agents run in parallel, each is a separate sequence of LLM calls with its own transcript, and they have no native way to know about each other's work.",[112,278,279],{},"Production systems with concurrent state discipline — databases, distributed systems, actor frameworks — solve this with locks, transactions, or immutability. LLMs don't participate in any of that. The agent isn't the thing holding the lock; the agent is running inside something else that holds the lock on its behalf. This is the distributed-systems observation Leslie Lamport formalized in \"Time, Clocks, and the Ordering of Events in a Distributed System\" (Communications of the ACM, 1978): coordination between independent processes is not a problem the processes themselves can solve — it requires a mediator outside the set of participants, and that mediator's job is to impose an ordering the participants can agree on. Substitute \"sub-agent\" for \"process\" and the conclusion is the same.",[112,281,282],{},"So the harness has to be that something else. It brokers access to shared resources; it serializes conflicting writes; it exposes clean, consistent reads. The agents stay stateless at the protocol level; coordination lives in the harness.",[267,284],{},[270,286,288],{"id":287},"_172-the-resource-lease","17.2 The Resource Lease",[290,291,296],"pre",{"className":292,"code":293,"language":294,"meta":295,"style":295},"language-python shiki shiki-themes material-theme-lighter github-light github-dark","# src\u002Fharness\u002Fcoordination\u002Flease.py\nfrom __future__ import annotations\n\nimport asyncio\nfrom dataclasses import dataclass, field\nfrom datetime import datetime, timedelta, timezone\nfrom uuid import uuid4\n\n\n@dataclass\nclass Lease:\n    resource: str\n    holder: str            # agent or sub-agent ID\n    token: str             # unique lease token; required for ops on this resource\n    expires_at: datetime\n\n\nclass LeaseConflict(Exception):\n    pass\n\n\n@dataclass\nclass LeaseManager:\n    \"\"\"Mediates exclusive access to named resources across concurrent agents.\"\"\"\n\n    _leases: dict[str, Lease] = field(default_factory=dict)\n    _lock: asyncio.Lock = field(default_factory=asyncio.Lock)\n\n    async def acquire(\n        self, resource: str, holder: str, ttl: timedelta = timedelta(seconds=60)\n    ) -> Lease:\n        async with self._lock:\n            existing = self._leases.get(resource)\n            if existing is not None:\n                if existing.expires_at > datetime.now(timezone.utc):\n                    raise LeaseConflict(\n                        f\"resource {resource!r} held by {existing.holder!r}\"\n                    )\n                # expired — reap\n                del self._leases[resource]\n            lease = Lease(\n                resource=resource,\n                holder=holder,\n                token=str(uuid4()),\n                expires_at=datetime.now(timezone.utc) + ttl,\n            )\n            self._leases[resource] = lease\n            return lease\n\n    async def release(self, lease: Lease) -> None:\n        async with self._lock:\n            existing = self._leases.get(lease.resource)\n            if existing and existing.token == lease.token:\n                del self._leases[lease.resource]\n\n    async def renew(self, lease: Lease, ttl: timedelta = timedelta(seconds=60)) -> Lease:\n        async with self._lock:\n            existing = self._leases.get(lease.resource)\n            if not existing or existing.token != lease.token:\n                raise LeaseConflict(\"lease no longer valid\")\n            new_lease = Lease(\n                resource=lease.resource, holder=lease.holder, token=lease.token,\n                expires_at=datetime.now(timezone.utc) + ttl,\n            )\n            self._leases[lease.resource] = new_lease\n            return new_lease\n\n    async def check(self, resource: str, token: str) -> bool:\n        async with self._lock:\n            existing = self._leases.get(resource)\n            return (existing is not None\n                    and existing.token == token\n                    and existing.expires_at > datetime.now(timezone.utc))\n","python","",[297,298,299,308,326,333,342,362,385,398,403,408,419,433,446,460,473,484,489,494,511,517,522,527,534,544,558,563,610,647,652,667,720,733,752,778,799,835,845,886,892,898,917,929,942,954,972,1005,1011,1032,1040,1045,1077,1092,1120,1148,1169,1174,1225,1240,1267,1296,1317,1329,1369,1398,1403,1427,1434,1439,1478,1493,1516,1534,1551],"code",{"__ignoreMap":295},[300,301,304],"span",{"class":302,"line":303},"line",1,[300,305,307],{"class":306},"sutJx","# src\u002Fharness\u002Fcoordination\u002Flease.py\n",[300,309,311,315,319,322],{"class":302,"line":310},2,[300,312,314],{"class":313},"sVHd0","from",[300,316,318],{"class":317},"s_hVV"," __future__",[300,320,321],{"class":313}," import",[300,323,325],{"class":324},"su5hD"," annotations\n",[300,327,329],{"class":302,"line":328},3,[300,330,332],{"emptyLinePlaceholder":331},true,"\n",[300,334,336,339],{"class":302,"line":335},4,[300,337,338],{"class":313},"import",[300,340,341],{"class":324}," asyncio\n",[300,343,345,347,350,352,355,359],{"class":302,"line":344},5,[300,346,314],{"class":313},[300,348,349],{"class":324}," dataclasses ",[300,351,338],{"class":313},[300,353,354],{"class":324}," dataclass",[300,356,358],{"class":357},"sP7_E",",",[300,360,361],{"class":324}," field\n",[300,363,365,367,370,372,375,377,380,382],{"class":302,"line":364},6,[300,366,314],{"class":313},[300,368,369],{"class":324}," datetime ",[300,371,338],{"class":313},[300,373,374],{"class":324}," datetime",[300,376,358],{"class":357},[300,378,379],{"class":324}," timedelta",[300,381,358],{"class":357},[300,383,384],{"class":324}," timezone\n",[300,386,388,390,393,395],{"class":302,"line":387},7,[300,389,314],{"class":313},[300,391,392],{"class":324}," uuid ",[300,394,338],{"class":313},[300,396,397],{"class":324}," uuid4\n",[300,399,401],{"class":302,"line":400},8,[300,402,332],{"emptyLinePlaceholder":331},[300,404,406],{"class":302,"line":405},9,[300,407,332],{"emptyLinePlaceholder":331},[300,409,411,415],{"class":302,"line":410},10,[300,412,414],{"class":413},"stp6e","@",[300,416,418],{"class":417},"sGLFI","dataclass\n",[300,420,422,426,430],{"class":302,"line":421},11,[300,423,425],{"class":424},"sbsja","class",[300,427,429],{"class":428},"sbgvK"," Lease",[300,431,432],{"class":357},":\n",[300,434,436,439,442],{"class":302,"line":435},12,[300,437,438],{"class":324},"    resource",[300,440,441],{"class":357},":",[300,443,445],{"class":444},"sZMiF"," str\n",[300,447,449,452,454,457],{"class":302,"line":448},13,[300,450,451],{"class":324},"    holder",[300,453,441],{"class":357},[300,455,456],{"class":444}," str",[300,458,459],{"class":306},"            # agent or sub-agent ID\n",[300,461,463,466,468,470],{"class":302,"line":462},14,[300,464,465],{"class":324},"    token",[300,467,441],{"class":357},[300,469,456],{"class":444},[300,471,472],{"class":306},"             # unique lease token; required for ops on this resource\n",[300,474,476,479,481],{"class":302,"line":475},15,[300,477,478],{"class":324},"    expires_at",[300,480,441],{"class":357},[300,482,483],{"class":324}," datetime\n",[300,485,487],{"class":302,"line":486},16,[300,488,332],{"emptyLinePlaceholder":331},[300,490,492],{"class":302,"line":491},17,[300,493,332],{"emptyLinePlaceholder":331},[300,495,497,499,502,505,508],{"class":302,"line":496},18,[300,498,425],{"class":424},[300,500,501],{"class":428}," LeaseConflict",[300,503,504],{"class":357},"(",[300,506,507],{"class":444},"Exception",[300,509,510],{"class":357},"):\n",[300,512,514],{"class":302,"line":513},19,[300,515,516],{"class":313},"    pass\n",[300,518,520],{"class":302,"line":519},20,[300,521,332],{"emptyLinePlaceholder":331},[300,523,525],{"class":302,"line":524},21,[300,526,332],{"emptyLinePlaceholder":331},[300,528,530,532],{"class":302,"line":529},22,[300,531,414],{"class":413},[300,533,418],{"class":417},[300,535,537,539,542],{"class":302,"line":536},23,[300,538,425],{"class":424},[300,540,541],{"class":428}," LeaseManager",[300,543,432],{"class":357},[300,545,547,551,555],{"class":302,"line":546},24,[300,548,550],{"class":549},"s2W-s","    \"\"\"",[300,552,554],{"class":553},"sithA","Mediates exclusive access to named resources across concurrent agents.",[300,556,557],{"class":549},"\"\"\"\n",[300,559,561],{"class":302,"line":560},25,[300,562,332],{"emptyLinePlaceholder":331},[300,564,566,569,571,574,577,580,582,584,587,591,595,597,601,604,607],{"class":302,"line":565},26,[300,567,568],{"class":324},"    _leases",[300,570,441],{"class":357},[300,572,573],{"class":324}," dict",[300,575,576],{"class":357},"[",[300,578,579],{"class":444},"str",[300,581,358],{"class":357},[300,583,429],{"class":324},[300,585,586],{"class":357},"]",[300,588,590],{"class":589},"smGrS"," =",[300,592,594],{"class":593},"slqww"," field",[300,596,504],{"class":357},[300,598,600],{"class":599},"s99_P","default_factory",[300,602,603],{"class":589},"=",[300,605,606],{"class":444},"dict",[300,608,609],{"class":357},")\n",[300,611,613,616,618,621,624,628,630,632,634,636,638,641,643,645],{"class":302,"line":612},27,[300,614,615],{"class":324},"    _lock",[300,617,441],{"class":357},[300,619,620],{"class":324}," asyncio",[300,622,623],{"class":357},".",[300,625,627],{"class":626},"skxfh","Lock",[300,629,590],{"class":589},[300,631,594],{"class":593},[300,633,504],{"class":357},[300,635,600],{"class":599},[300,637,603],{"class":589},[300,639,640],{"class":593},"asyncio",[300,642,623],{"class":357},[300,644,627],{"class":626},[300,646,609],{"class":357},[300,648,650],{"class":302,"line":649},28,[300,651,332],{"emptyLinePlaceholder":331},[300,653,655,658,661,664],{"class":302,"line":654},29,[300,656,657],{"class":424},"    async",[300,659,660],{"class":424}," def",[300,662,663],{"class":417}," acquire",[300,665,666],{"class":357},"(\n",[300,668,670,674,676,680,682,684,686,689,691,693,695,698,700,703,705,707,709,712,714,718],{"class":302,"line":669},30,[300,671,673],{"class":672},"smCYv","        self",[300,675,358],{"class":357},[300,677,679],{"class":678},"sFwrP"," resource",[300,681,441],{"class":357},[300,683,456],{"class":444},[300,685,358],{"class":357},[300,687,688],{"class":678}," holder",[300,690,441],{"class":357},[300,692,456],{"class":444},[300,694,358],{"class":357},[300,696,697],{"class":678}," ttl",[300,699,441],{"class":357},[300,701,702],{"class":324}," timedelta ",[300,704,603],{"class":589},[300,706,379],{"class":593},[300,708,504],{"class":357},[300,710,711],{"class":599},"seconds",[300,713,603],{"class":589},[300,715,717],{"class":716},"srdBf","60",[300,719,609],{"class":357},[300,721,723,726,729,731],{"class":302,"line":722},31,[300,724,725],{"class":357},"    )",[300,727,728],{"class":357}," ->",[300,730,429],{"class":324},[300,732,432],{"class":357},[300,734,736,739,742,745,747,750],{"class":302,"line":735},32,[300,737,738],{"class":313},"        async",[300,740,741],{"class":313}," with",[300,743,744],{"class":317}," self",[300,746,623],{"class":357},[300,748,749],{"class":626},"_lock",[300,751,432],{"class":357},[300,753,755,758,760,762,764,767,769,772,774,776],{"class":302,"line":754},33,[300,756,757],{"class":324},"            existing ",[300,759,603],{"class":589},[300,761,744],{"class":317},[300,763,623],{"class":357},[300,765,766],{"class":626},"_leases",[300,768,623],{"class":357},[300,770,771],{"class":593},"get",[300,773,504],{"class":357},[300,775,250],{"class":593},[300,777,609],{"class":357},[300,779,781,784,787,790,793,797],{"class":302,"line":780},34,[300,782,783],{"class":313},"            if",[300,785,786],{"class":324}," existing ",[300,788,789],{"class":589},"is",[300,791,792],{"class":589}," not",[300,794,796],{"class":795},"s39Yj"," None",[300,798,432],{"class":357},[300,800,802,805,808,810,813,816,818,820,823,825,828,830,833],{"class":302,"line":801},35,[300,803,804],{"class":313},"                if",[300,806,807],{"class":324}," existing",[300,809,623],{"class":357},[300,811,812],{"class":626},"expires_at",[300,814,815],{"class":589}," >",[300,817,374],{"class":324},[300,819,623],{"class":357},[300,821,822],{"class":593},"now",[300,824,504],{"class":357},[300,826,827],{"class":593},"timezone",[300,829,623],{"class":357},[300,831,832],{"class":626},"utc",[300,834,510],{"class":357},[300,836,838,841,843],{"class":302,"line":837},36,[300,839,840],{"class":313},"                    raise",[300,842,501],{"class":593},[300,844,666],{"class":357},[300,846,848,851,855,858,860,863,866,869,871,874,876,879,881,883],{"class":302,"line":847},37,[300,849,850],{"class":424},"                        f",[300,852,854],{"class":853},"s_sjI","\"resource ",[300,856,857],{"class":716},"{",[300,859,250],{"class":593},[300,861,862],{"class":424},"!r",[300,864,865],{"class":716},"}",[300,867,868],{"class":853}," held by ",[300,870,857],{"class":716},[300,872,873],{"class":593},"existing",[300,875,623],{"class":357},[300,877,878],{"class":626},"holder",[300,880,862],{"class":424},[300,882,865],{"class":716},[300,884,885],{"class":853},"\"\n",[300,887,889],{"class":302,"line":888},38,[300,890,891],{"class":357},"                    )\n",[300,893,895],{"class":302,"line":894},39,[300,896,897],{"class":306},"                # expired — reap\n",[300,899,901,904,906,908,910,912,914],{"class":302,"line":900},40,[300,902,903],{"class":313},"                del",[300,905,744],{"class":317},[300,907,623],{"class":357},[300,909,766],{"class":626},[300,911,576],{"class":357},[300,913,250],{"class":626},[300,915,916],{"class":357},"]\n",[300,918,920,923,925,927],{"class":302,"line":919},41,[300,921,922],{"class":324},"            lease ",[300,924,603],{"class":589},[300,926,429],{"class":593},[300,928,666],{"class":357},[300,930,932,935,937,939],{"class":302,"line":931},42,[300,933,934],{"class":599},"                resource",[300,936,603],{"class":589},[300,938,250],{"class":593},[300,940,941],{"class":357},",\n",[300,943,945,948,950,952],{"class":302,"line":944},43,[300,946,947],{"class":599},"                holder",[300,949,603],{"class":589},[300,951,878],{"class":593},[300,953,941],{"class":357},[300,955,957,960,962,964,966,969],{"class":302,"line":956},44,[300,958,959],{"class":599},"                token",[300,961,603],{"class":589},[300,963,579],{"class":444},[300,965,504],{"class":357},[300,967,968],{"class":593},"uuid4",[300,970,971],{"class":357},"()),\n",[300,973,975,978,980,983,985,987,989,991,993,995,998,1001,1003],{"class":302,"line":974},45,[300,976,977],{"class":599},"                expires_at",[300,979,603],{"class":589},[300,981,982],{"class":593},"datetime",[300,984,623],{"class":357},[300,986,822],{"class":593},[300,988,504],{"class":357},[300,990,827],{"class":593},[300,992,623],{"class":357},[300,994,832],{"class":626},[300,996,997],{"class":357},")",[300,999,1000],{"class":589}," +",[300,1002,697],{"class":593},[300,1004,941],{"class":357},[300,1006,1008],{"class":302,"line":1007},46,[300,1009,1010],{"class":357},"            )\n",[300,1012,1014,1017,1019,1021,1023,1025,1027,1029],{"class":302,"line":1013},47,[300,1015,1016],{"class":317},"            self",[300,1018,623],{"class":357},[300,1020,766],{"class":626},[300,1022,576],{"class":357},[300,1024,250],{"class":626},[300,1026,586],{"class":357},[300,1028,590],{"class":589},[300,1030,1031],{"class":324}," lease\n",[300,1033,1035,1038],{"class":302,"line":1034},48,[300,1036,1037],{"class":313},"            return",[300,1039,1031],{"class":324},[300,1041,1043],{"class":302,"line":1042},49,[300,1044,332],{"emptyLinePlaceholder":331},[300,1046,1048,1050,1052,1055,1057,1060,1062,1065,1067,1069,1071,1073,1075],{"class":302,"line":1047},50,[300,1049,657],{"class":424},[300,1051,660],{"class":424},[300,1053,1054],{"class":417}," release",[300,1056,504],{"class":357},[300,1058,1059],{"class":672},"self",[300,1061,358],{"class":357},[300,1063,1064],{"class":678}," lease",[300,1066,441],{"class":357},[300,1068,429],{"class":324},[300,1070,997],{"class":357},[300,1072,728],{"class":357},[300,1074,796],{"class":795},[300,1076,432],{"class":357},[300,1078,1080,1082,1084,1086,1088,1090],{"class":302,"line":1079},51,[300,1081,738],{"class":313},[300,1083,741],{"class":313},[300,1085,744],{"class":317},[300,1087,623],{"class":357},[300,1089,749],{"class":626},[300,1091,432],{"class":357},[300,1093,1095,1097,1099,1101,1103,1105,1107,1109,1111,1114,1116,1118],{"class":302,"line":1094},52,[300,1096,757],{"class":324},[300,1098,603],{"class":589},[300,1100,744],{"class":317},[300,1102,623],{"class":357},[300,1104,766],{"class":626},[300,1106,623],{"class":357},[300,1108,771],{"class":593},[300,1110,504],{"class":357},[300,1112,1113],{"class":593},"lease",[300,1115,623],{"class":357},[300,1117,250],{"class":626},[300,1119,609],{"class":357},[300,1121,1123,1125,1127,1130,1132,1134,1137,1140,1142,1144,1146],{"class":302,"line":1122},53,[300,1124,783],{"class":313},[300,1126,786],{"class":324},[300,1128,1129],{"class":589},"and",[300,1131,807],{"class":324},[300,1133,623],{"class":357},[300,1135,1136],{"class":626},"token",[300,1138,1139],{"class":589}," ==",[300,1141,1064],{"class":324},[300,1143,623],{"class":357},[300,1145,1136],{"class":626},[300,1147,432],{"class":357},[300,1149,1151,1153,1155,1157,1159,1161,1163,1165,1167],{"class":302,"line":1150},54,[300,1152,903],{"class":313},[300,1154,744],{"class":317},[300,1156,623],{"class":357},[300,1158,766],{"class":626},[300,1160,576],{"class":357},[300,1162,1113],{"class":626},[300,1164,623],{"class":357},[300,1166,250],{"class":626},[300,1168,916],{"class":357},[300,1170,1172],{"class":302,"line":1171},55,[300,1173,332],{"emptyLinePlaceholder":331},[300,1175,1177,1179,1181,1184,1186,1188,1190,1192,1194,1196,1198,1200,1202,1204,1206,1208,1210,1212,1214,1216,1219,1221,1223],{"class":302,"line":1176},56,[300,1178,657],{"class":424},[300,1180,660],{"class":424},[300,1182,1183],{"class":417}," renew",[300,1185,504],{"class":357},[300,1187,1059],{"class":672},[300,1189,358],{"class":357},[300,1191,1064],{"class":678},[300,1193,441],{"class":357},[300,1195,429],{"class":324},[300,1197,358],{"class":357},[300,1199,697],{"class":678},[300,1201,441],{"class":357},[300,1203,702],{"class":324},[300,1205,603],{"class":589},[300,1207,379],{"class":593},[300,1209,504],{"class":357},[300,1211,711],{"class":599},[300,1213,603],{"class":589},[300,1215,717],{"class":716},[300,1217,1218],{"class":357},"))",[300,1220,728],{"class":357},[300,1222,429],{"class":324},[300,1224,432],{"class":357},[300,1226,1228,1230,1232,1234,1236,1238],{"class":302,"line":1227},57,[300,1229,738],{"class":313},[300,1231,741],{"class":313},[300,1233,744],{"class":317},[300,1235,623],{"class":357},[300,1237,749],{"class":626},[300,1239,432],{"class":357},[300,1241,1243,1245,1247,1249,1251,1253,1255,1257,1259,1261,1263,1265],{"class":302,"line":1242},58,[300,1244,757],{"class":324},[300,1246,603],{"class":589},[300,1248,744],{"class":317},[300,1250,623],{"class":357},[300,1252,766],{"class":626},[300,1254,623],{"class":357},[300,1256,771],{"class":593},[300,1258,504],{"class":357},[300,1260,1113],{"class":593},[300,1262,623],{"class":357},[300,1264,250],{"class":626},[300,1266,609],{"class":357},[300,1268,1270,1272,1274,1276,1279,1281,1283,1285,1288,1290,1292,1294],{"class":302,"line":1269},59,[300,1271,783],{"class":313},[300,1273,792],{"class":589},[300,1275,786],{"class":324},[300,1277,1278],{"class":589},"or",[300,1280,807],{"class":324},[300,1282,623],{"class":357},[300,1284,1136],{"class":626},[300,1286,1287],{"class":589}," !=",[300,1289,1064],{"class":324},[300,1291,623],{"class":357},[300,1293,1136],{"class":626},[300,1295,432],{"class":357},[300,1297,1299,1302,1304,1306,1310,1313,1315],{"class":302,"line":1298},60,[300,1300,1301],{"class":313},"                raise",[300,1303,501],{"class":593},[300,1305,504],{"class":357},[300,1307,1309],{"class":1308},"sjJ54","\"",[300,1311,1312],{"class":853},"lease no longer valid",[300,1314,1309],{"class":1308},[300,1316,609],{"class":357},[300,1318,1320,1323,1325,1327],{"class":302,"line":1319},61,[300,1321,1322],{"class":324},"            new_lease ",[300,1324,603],{"class":589},[300,1326,429],{"class":593},[300,1328,666],{"class":357},[300,1330,1332,1334,1336,1338,1340,1342,1344,1346,1348,1350,1352,1354,1356,1359,1361,1363,1365,1367],{"class":302,"line":1331},62,[300,1333,934],{"class":599},[300,1335,603],{"class":589},[300,1337,1113],{"class":593},[300,1339,623],{"class":357},[300,1341,250],{"class":626},[300,1343,358],{"class":357},[300,1345,688],{"class":599},[300,1347,603],{"class":589},[300,1349,1113],{"class":593},[300,1351,623],{"class":357},[300,1353,878],{"class":626},[300,1355,358],{"class":357},[300,1357,1358],{"class":599}," token",[300,1360,603],{"class":589},[300,1362,1113],{"class":593},[300,1364,623],{"class":357},[300,1366,1136],{"class":626},[300,1368,941],{"class":357},[300,1370,1372,1374,1376,1378,1380,1382,1384,1386,1388,1390,1392,1394,1396],{"class":302,"line":1371},63,[300,1373,977],{"class":599},[300,1375,603],{"class":589},[300,1377,982],{"class":593},[300,1379,623],{"class":357},[300,1381,822],{"class":593},[300,1383,504],{"class":357},[300,1385,827],{"class":593},[300,1387,623],{"class":357},[300,1389,832],{"class":626},[300,1391,997],{"class":357},[300,1393,1000],{"class":589},[300,1395,697],{"class":593},[300,1397,941],{"class":357},[300,1399,1401],{"class":302,"line":1400},64,[300,1402,1010],{"class":357},[300,1404,1406,1408,1410,1412,1414,1416,1418,1420,1422,1424],{"class":302,"line":1405},65,[300,1407,1016],{"class":317},[300,1409,623],{"class":357},[300,1411,766],{"class":626},[300,1413,576],{"class":357},[300,1415,1113],{"class":626},[300,1417,623],{"class":357},[300,1419,250],{"class":626},[300,1421,586],{"class":357},[300,1423,590],{"class":589},[300,1425,1426],{"class":324}," new_lease\n",[300,1428,1430,1432],{"class":302,"line":1429},66,[300,1431,1037],{"class":313},[300,1433,1426],{"class":324},[300,1435,1437],{"class":302,"line":1436},67,[300,1438,332],{"emptyLinePlaceholder":331},[300,1440,1442,1444,1446,1449,1451,1453,1455,1457,1459,1461,1463,1465,1467,1469,1471,1473,1476],{"class":302,"line":1441},68,[300,1443,657],{"class":424},[300,1445,660],{"class":424},[300,1447,1448],{"class":417}," check",[300,1450,504],{"class":357},[300,1452,1059],{"class":672},[300,1454,358],{"class":357},[300,1456,679],{"class":678},[300,1458,441],{"class":357},[300,1460,456],{"class":444},[300,1462,358],{"class":357},[300,1464,1358],{"class":678},[300,1466,441],{"class":357},[300,1468,456],{"class":444},[300,1470,997],{"class":357},[300,1472,728],{"class":357},[300,1474,1475],{"class":444}," bool",[300,1477,432],{"class":357},[300,1479,1481,1483,1485,1487,1489,1491],{"class":302,"line":1480},69,[300,1482,738],{"class":313},[300,1484,741],{"class":313},[300,1486,744],{"class":317},[300,1488,623],{"class":357},[300,1490,749],{"class":626},[300,1492,432],{"class":357},[300,1494,1496,1498,1500,1502,1504,1506,1508,1510,1512,1514],{"class":302,"line":1495},70,[300,1497,757],{"class":324},[300,1499,603],{"class":589},[300,1501,744],{"class":317},[300,1503,623],{"class":357},[300,1505,766],{"class":626},[300,1507,623],{"class":357},[300,1509,771],{"class":593},[300,1511,504],{"class":357},[300,1513,250],{"class":593},[300,1515,609],{"class":357},[300,1517,1519,1521,1524,1527,1529,1531],{"class":302,"line":1518},71,[300,1520,1037],{"class":313},[300,1522,1523],{"class":357}," (",[300,1525,1526],{"class":324},"existing ",[300,1528,789],{"class":589},[300,1530,792],{"class":589},[300,1532,1533],{"class":795}," None\n",[300,1535,1537,1540,1542,1544,1546,1548],{"class":302,"line":1536},72,[300,1538,1539],{"class":589},"                    and",[300,1541,807],{"class":324},[300,1543,623],{"class":357},[300,1545,1136],{"class":626},[300,1547,1139],{"class":589},[300,1549,1550],{"class":324}," token\n",[300,1552,1554,1556,1558,1560,1562,1564,1566,1568,1570,1572,1574,1576,1578],{"class":302,"line":1553},73,[300,1555,1539],{"class":589},[300,1557,807],{"class":324},[300,1559,623],{"class":357},[300,1561,812],{"class":626},[300,1563,815],{"class":589},[300,1565,374],{"class":324},[300,1567,623],{"class":357},[300,1569,822],{"class":593},[300,1571,504],{"class":357},[300,1573,827],{"class":593},[300,1575,623],{"class":357},[300,1577,832],{"class":626},[300,1579,1580],{"class":357},"))\n",[112,1582,1583],{},"Three properties earned.",[112,1585,1586,1589,1590,1593,1594,1597],{},[124,1587,1588],{},"In-process and async-safe."," The lease manager uses an ",[297,1591,1592],{},"asyncio.Lock"," around its internal state. Multiple coroutines in the same event loop can call ",[297,1595,1596],{},"acquire",", and they serialize correctly.",[112,1599,1600,1603,1604,1606],{},[124,1601,1602],{},"TTL-bounded."," A crashed sub-agent cannot hold a lease forever. When the TTL expires, the next ",[297,1605,1596],{}," reaps the stale lease. This matches the Postgres advisory-lock pattern — if you want distributed leases, you'd swap the backing store to Postgres or Redis without changing the interface.",[112,1608,1609,1612],{},[124,1610,1611],{},"Token-based."," The lease's token is what future operations on the resource check against. A sub-agent that forgot to release a lease but continued working with stale state gets rejected on the next write attempt.",[267,1614],{},[270,1616,1618],{"id":1617},"_173-write-gated-file-tools","17.3 Write-gated File Tools",[112,1620,1621],{},"File tools now go through the lease manager. The sub-agent must acquire a lease before editing, and the tool checks the lease on every call.",[112,1623,1624,1625,1628,1629,1632,1633,1636,1637,1639,1640,1643,1644,1647,1648,1651,1652,1655,1656,1659],{},"Because ",[297,1626,1627],{},"LeaseManager.acquire"," and ",[297,1630,1631],{},"LeaseManager.check"," are ",[297,1634,1635],{},"async def"," (they wait on an ",[297,1638,1592],{}," internally), the tools that wrap them have to be async too. We use ",[297,1641,1642],{},"@async_tool"," from §13.3 and let the registry's ",[297,1645,1646],{},"adispatch"," path ",[297,1649,1650],{},"await"," them. Dropping an ",[297,1653,1654],{},"asyncio.run(...)"," into a sync tool body would raise ",[297,1657,1658],{},"RuntimeError: asyncio.run() cannot be called from a running event loop"," — the agent loop is already running when the tool is dispatched, as §13.3 spells out.",[290,1661,1663],{"className":292,"code":1662,"language":294,"meta":295,"style":295},"# src\u002Fharness\u002Ftools\u002Ffiles_leased.py\nfrom __future__ import annotations\n\nfrom datetime import timedelta\nfrom pathlib import Path\n\nfrom ..coordination.lease import Lease, LeaseManager\nfrom .base import Tool\nfrom .decorator import async_tool\n\n\ndef leased_file_tools(mgr: LeaseManager, holder: str) -> list[Tool]:\n\n    @async_tool(side_effects={\"write\"})\n    async def acquire_file_lease(path: str, ttl_seconds: int = 60) -> str:\n        \"\"\"Acquire an exclusive write-lease on a file.\n\n        path: the file you intend to modify.\n        ttl_seconds: how long to hold the lease before auto-expiry.\n\n        Returns a lease token to include in subsequent edit calls.\n        If another agent holds a lease on the same file, returns an\n        error; wait and retry, or choose a different approach.\n        \"\"\"\n        try:\n            lease = await mgr.acquire(\n                path, holder, ttl=timedelta(seconds=ttl_seconds)\n            )\n            return f\"token={lease.token} (expires in {ttl_seconds}s)\"\n        except Exception as e:\n            return f\"could not acquire lease: {e}\"\n\n    @async_tool(side_effects={\"write\"})\n    async def edit_lines_leased(\n        path: str, start_line: int, end_line: int,\n        replacement: str, lease_token: str,\n    ) -> str:\n        \"\"\"Replace a line range, verifying a lease token for the file.\n\n        lease_token: obtained from acquire_file_lease. Required.\n        Other args: see edit_lines.\n        \"\"\"\n        ok = await mgr.check(path, lease_token)\n        if not ok:\n            return f\"edit rejected: lease for {path} is invalid or expired\"\n        from .files import edit_lines\n        return edit_lines.run(path=path, start_line=start_line,\n                               end_line=end_line, replacement=replacement)\n\n    return [acquire_file_lease, edit_lines_leased]\n",[297,1664,1665,1670,1680,1684,1695,1707,1711,1735,1750,1764,1768,1772,1812,1816,1843,1884,1892,1896,1901,1906,1910,1915,1920,1925,1930,1937,1955,1984,1988,2020,2036,2054,2058,2080,2091,2120,2140,2150,2157,2161,2166,2171,2175,2201,2213,2231,2246,2278,2300,2304],{"__ignoreMap":295},[300,1666,1667],{"class":302,"line":303},[300,1668,1669],{"class":306},"# src\u002Fharness\u002Ftools\u002Ffiles_leased.py\n",[300,1671,1672,1674,1676,1678],{"class":302,"line":310},[300,1673,314],{"class":313},[300,1675,318],{"class":317},[300,1677,321],{"class":313},[300,1679,325],{"class":324},[300,1681,1682],{"class":302,"line":328},[300,1683,332],{"emptyLinePlaceholder":331},[300,1685,1686,1688,1690,1692],{"class":302,"line":335},[300,1687,314],{"class":313},[300,1689,369],{"class":324},[300,1691,338],{"class":313},[300,1693,1694],{"class":324}," timedelta\n",[300,1696,1697,1699,1702,1704],{"class":302,"line":344},[300,1698,314],{"class":313},[300,1700,1701],{"class":324}," pathlib ",[300,1703,338],{"class":313},[300,1705,1706],{"class":324}," Path\n",[300,1708,1709],{"class":302,"line":364},[300,1710,332],{"emptyLinePlaceholder":331},[300,1712,1713,1715,1718,1721,1723,1726,1728,1730,1732],{"class":302,"line":387},[300,1714,314],{"class":313},[300,1716,1717],{"class":357}," ..",[300,1719,1720],{"class":324},"coordination",[300,1722,623],{"class":357},[300,1724,1725],{"class":324},"lease ",[300,1727,338],{"class":313},[300,1729,429],{"class":324},[300,1731,358],{"class":357},[300,1733,1734],{"class":324}," LeaseManager\n",[300,1736,1737,1739,1742,1745,1747],{"class":302,"line":400},[300,1738,314],{"class":313},[300,1740,1741],{"class":357}," .",[300,1743,1744],{"class":324},"base ",[300,1746,338],{"class":313},[300,1748,1749],{"class":324}," Tool\n",[300,1751,1752,1754,1756,1759,1761],{"class":302,"line":405},[300,1753,314],{"class":313},[300,1755,1741],{"class":357},[300,1757,1758],{"class":324},"decorator ",[300,1760,338],{"class":313},[300,1762,1763],{"class":324}," async_tool\n",[300,1765,1766],{"class":302,"line":410},[300,1767,332],{"emptyLinePlaceholder":331},[300,1769,1770],{"class":302,"line":421},[300,1771,332],{"emptyLinePlaceholder":331},[300,1773,1774,1777,1780,1782,1785,1787,1789,1791,1793,1795,1797,1799,1801,1804,1806,1809],{"class":302,"line":435},[300,1775,1776],{"class":424},"def",[300,1778,1779],{"class":417}," leased_file_tools",[300,1781,504],{"class":357},[300,1783,1784],{"class":678},"mgr",[300,1786,441],{"class":357},[300,1788,541],{"class":324},[300,1790,358],{"class":357},[300,1792,688],{"class":678},[300,1794,441],{"class":357},[300,1796,456],{"class":444},[300,1798,997],{"class":357},[300,1800,728],{"class":357},[300,1802,1803],{"class":324}," list",[300,1805,576],{"class":357},[300,1807,1808],{"class":324},"Tool",[300,1810,1811],{"class":357},"]:\n",[300,1813,1814],{"class":302,"line":448},[300,1815,332],{"emptyLinePlaceholder":331},[300,1817,1818,1821,1824,1826,1829,1831,1833,1835,1838,1840],{"class":302,"line":462},[300,1819,1820],{"class":413},"    @",[300,1822,1823],{"class":417},"async_tool",[300,1825,504],{"class":357},[300,1827,1828],{"class":599},"side_effects",[300,1830,603],{"class":589},[300,1832,857],{"class":357},[300,1834,1309],{"class":1308},[300,1836,1837],{"class":853},"write",[300,1839,1309],{"class":1308},[300,1841,1842],{"class":357},"})\n",[300,1844,1845,1847,1849,1852,1854,1857,1859,1861,1863,1866,1868,1871,1873,1876,1878,1880,1882],{"class":302,"line":475},[300,1846,657],{"class":424},[300,1848,660],{"class":424},[300,1850,1851],{"class":417}," acquire_file_lease",[300,1853,504],{"class":357},[300,1855,1856],{"class":678},"path",[300,1858,441],{"class":357},[300,1860,456],{"class":444},[300,1862,358],{"class":357},[300,1864,1865],{"class":678}," ttl_seconds",[300,1867,441],{"class":357},[300,1869,1870],{"class":444}," int",[300,1872,590],{"class":589},[300,1874,1875],{"class":716}," 60",[300,1877,997],{"class":357},[300,1879,728],{"class":357},[300,1881,456],{"class":444},[300,1883,432],{"class":357},[300,1885,1886,1889],{"class":302,"line":486},[300,1887,1888],{"class":549},"        \"\"\"",[300,1890,1891],{"class":553},"Acquire an exclusive write-lease on a file.\n",[300,1893,1894],{"class":302,"line":491},[300,1895,332],{"emptyLinePlaceholder":331},[300,1897,1898],{"class":302,"line":496},[300,1899,1900],{"class":553},"        path: the file you intend to modify.\n",[300,1902,1903],{"class":302,"line":513},[300,1904,1905],{"class":553},"        ttl_seconds: how long to hold the lease before auto-expiry.\n",[300,1907,1908],{"class":302,"line":519},[300,1909,332],{"emptyLinePlaceholder":331},[300,1911,1912],{"class":302,"line":524},[300,1913,1914],{"class":553},"        Returns a lease token to include in subsequent edit calls.\n",[300,1916,1917],{"class":302,"line":529},[300,1918,1919],{"class":553},"        If another agent holds a lease on the same file, returns an\n",[300,1921,1922],{"class":302,"line":536},[300,1923,1924],{"class":553},"        error; wait and retry, or choose a different approach.\n",[300,1926,1927],{"class":302,"line":546},[300,1928,1929],{"class":549},"        \"\"\"\n",[300,1931,1932,1935],{"class":302,"line":560},[300,1933,1934],{"class":313},"        try",[300,1936,432],{"class":357},[300,1938,1939,1941,1943,1946,1949,1951,1953],{"class":302,"line":565},[300,1940,922],{"class":324},[300,1942,603],{"class":589},[300,1944,1945],{"class":313}," await",[300,1947,1948],{"class":324}," mgr",[300,1950,623],{"class":357},[300,1952,1596],{"class":593},[300,1954,666],{"class":357},[300,1956,1957,1960,1962,1964,1966,1968,1970,1973,1975,1977,1979,1982],{"class":302,"line":612},[300,1958,1959],{"class":593},"                path",[300,1961,358],{"class":357},[300,1963,688],{"class":593},[300,1965,358],{"class":357},[300,1967,697],{"class":599},[300,1969,603],{"class":589},[300,1971,1972],{"class":593},"timedelta",[300,1974,504],{"class":357},[300,1976,711],{"class":599},[300,1978,603],{"class":589},[300,1980,1981],{"class":593},"ttl_seconds",[300,1983,609],{"class":357},[300,1985,1986],{"class":302,"line":649},[300,1987,1010],{"class":357},[300,1989,1990,1992,1995,1998,2000,2002,2004,2006,2008,2011,2013,2015,2017],{"class":302,"line":654},[300,1991,1037],{"class":313},[300,1993,1994],{"class":424}," f",[300,1996,1997],{"class":853},"\"token=",[300,1999,857],{"class":716},[300,2001,1113],{"class":324},[300,2003,623],{"class":357},[300,2005,1136],{"class":626},[300,2007,865],{"class":716},[300,2009,2010],{"class":853}," (expires in ",[300,2012,857],{"class":716},[300,2014,1981],{"class":324},[300,2016,865],{"class":716},[300,2018,2019],{"class":853},"s)\"\n",[300,2021,2022,2025,2028,2031,2034],{"class":302,"line":669},[300,2023,2024],{"class":313},"        except",[300,2026,2027],{"class":444}," Exception",[300,2029,2030],{"class":313}," as",[300,2032,2033],{"class":324}," e",[300,2035,432],{"class":357},[300,2037,2038,2040,2042,2045,2047,2050,2052],{"class":302,"line":722},[300,2039,1037],{"class":313},[300,2041,1994],{"class":424},[300,2043,2044],{"class":853},"\"could not acquire lease: ",[300,2046,857],{"class":716},[300,2048,2049],{"class":324},"e",[300,2051,865],{"class":716},[300,2053,885],{"class":853},[300,2055,2056],{"class":302,"line":735},[300,2057,332],{"emptyLinePlaceholder":331},[300,2059,2060,2062,2064,2066,2068,2070,2072,2074,2076,2078],{"class":302,"line":754},[300,2061,1820],{"class":413},[300,2063,1823],{"class":417},[300,2065,504],{"class":357},[300,2067,1828],{"class":599},[300,2069,603],{"class":589},[300,2071,857],{"class":357},[300,2073,1309],{"class":1308},[300,2075,1837],{"class":853},[300,2077,1309],{"class":1308},[300,2079,1842],{"class":357},[300,2081,2082,2084,2086,2089],{"class":302,"line":780},[300,2083,657],{"class":424},[300,2085,660],{"class":424},[300,2087,2088],{"class":417}," edit_lines_leased",[300,2090,666],{"class":357},[300,2092,2093,2096,2098,2100,2102,2105,2107,2109,2111,2114,2116,2118],{"class":302,"line":801},[300,2094,2095],{"class":678},"        path",[300,2097,441],{"class":357},[300,2099,456],{"class":444},[300,2101,358],{"class":357},[300,2103,2104],{"class":678}," start_line",[300,2106,441],{"class":357},[300,2108,1870],{"class":444},[300,2110,358],{"class":357},[300,2112,2113],{"class":678}," end_line",[300,2115,441],{"class":357},[300,2117,1870],{"class":444},[300,2119,941],{"class":357},[300,2121,2122,2125,2127,2129,2131,2134,2136,2138],{"class":302,"line":837},[300,2123,2124],{"class":678},"        replacement",[300,2126,441],{"class":357},[300,2128,456],{"class":444},[300,2130,358],{"class":357},[300,2132,2133],{"class":678}," lease_token",[300,2135,441],{"class":357},[300,2137,456],{"class":444},[300,2139,941],{"class":357},[300,2141,2142,2144,2146,2148],{"class":302,"line":847},[300,2143,725],{"class":357},[300,2145,728],{"class":357},[300,2147,456],{"class":444},[300,2149,432],{"class":357},[300,2151,2152,2154],{"class":302,"line":888},[300,2153,1888],{"class":549},[300,2155,2156],{"class":553},"Replace a line range, verifying a lease token for the file.\n",[300,2158,2159],{"class":302,"line":894},[300,2160,332],{"emptyLinePlaceholder":331},[300,2162,2163],{"class":302,"line":900},[300,2164,2165],{"class":553},"        lease_token: obtained from acquire_file_lease. Required.\n",[300,2167,2168],{"class":302,"line":919},[300,2169,2170],{"class":553},"        Other args: see edit_lines.\n",[300,2172,2173],{"class":302,"line":931},[300,2174,1929],{"class":549},[300,2176,2177,2180,2182,2184,2186,2188,2191,2193,2195,2197,2199],{"class":302,"line":944},[300,2178,2179],{"class":324},"        ok ",[300,2181,603],{"class":589},[300,2183,1945],{"class":313},[300,2185,1948],{"class":324},[300,2187,623],{"class":357},[300,2189,2190],{"class":593},"check",[300,2192,504],{"class":357},[300,2194,1856],{"class":593},[300,2196,358],{"class":357},[300,2198,2133],{"class":593},[300,2200,609],{"class":357},[300,2202,2203,2206,2208,2211],{"class":302,"line":956},[300,2204,2205],{"class":313},"        if",[300,2207,792],{"class":589},[300,2209,2210],{"class":324}," ok",[300,2212,432],{"class":357},[300,2214,2215,2217,2219,2222,2224,2226,2228],{"class":302,"line":974},[300,2216,1037],{"class":313},[300,2218,1994],{"class":424},[300,2220,2221],{"class":853},"\"edit rejected: lease for ",[300,2223,857],{"class":716},[300,2225,1856],{"class":324},[300,2227,865],{"class":716},[300,2229,2230],{"class":853}," is invalid or expired\"\n",[300,2232,2233,2236,2238,2241,2243],{"class":302,"line":1007},[300,2234,2235],{"class":313},"        from",[300,2237,1741],{"class":357},[300,2239,2240],{"class":324},"files ",[300,2242,338],{"class":313},[300,2244,2245],{"class":324}," edit_lines\n",[300,2247,2248,2251,2254,2256,2259,2261,2263,2265,2267,2269,2271,2273,2276],{"class":302,"line":1013},[300,2249,2250],{"class":313},"        return",[300,2252,2253],{"class":324}," edit_lines",[300,2255,623],{"class":357},[300,2257,2258],{"class":593},"run",[300,2260,504],{"class":357},[300,2262,1856],{"class":599},[300,2264,603],{"class":589},[300,2266,1856],{"class":593},[300,2268,358],{"class":357},[300,2270,2104],{"class":599},[300,2272,603],{"class":589},[300,2274,2275],{"class":593},"start_line",[300,2277,941],{"class":357},[300,2279,2280,2283,2285,2288,2290,2293,2295,2298],{"class":302,"line":1034},[300,2281,2282],{"class":599},"                               end_line",[300,2284,603],{"class":589},[300,2286,2287],{"class":593},"end_line",[300,2289,358],{"class":357},[300,2291,2292],{"class":599}," replacement",[300,2294,603],{"class":589},[300,2296,2297],{"class":593},"replacement",[300,2299,609],{"class":357},[300,2301,2302],{"class":302,"line":1042},[300,2303,332],{"emptyLinePlaceholder":331},[300,2305,2306,2309,2312,2315,2317,2319],{"class":302,"line":1047},[300,2307,2308],{"class":313},"    return",[300,2310,2311],{"class":357}," [",[300,2313,2314],{"class":324},"acquire_file_lease",[300,2316,358],{"class":357},[300,2318,2088],{"class":324},[300,2320,916],{"class":357},[112,2322,2323,2324,2327,2328,2330],{},"Concrete protocol: sub-agent acquires a lease, uses the token on every subsequent edit to the same file, releases when done (or the TTL expires). If two sub-agents both try to edit ",[297,2325,2326],{},"\u002Fworkspace\u002Fschema.sql",", only one gets the lease; the other's ",[297,2329,2314],{}," call returns an error, at which point the agent either waits, works on something else, or coordinates with the parent.",[112,2332,2333],{},"For read-only access we don't need leases. Reading a file while another agent writes to it is not a correctness problem here — at worst, the reader sees a stale version, which is a freshness issue, not a corruption issue.",[267,2335],{},[270,2337,2339],{"id":2338},"_174-parallel-sub-agent-spawning","17.4 Parallel Sub-agent Spawning",[112,2341,2342],{},"The spawner from Chapter 15 gets a parallel version:",[290,2344,2346],{"className":292,"code":2345,"language":294,"meta":295,"style":295},"# src\u002Fharness\u002Fsubagents\u002Fparallel.py\nfrom __future__ import annotations\n\nimport asyncio\nfrom dataclasses import dataclass\n\nfrom .spawner import SubagentSpawner\nfrom .subagent import SubagentResult, SubagentSpec\n\n\n@dataclass\nclass ParallelSpawner:\n    inner: SubagentSpawner\n\n    async def spawn_all(\n        self, specs: list[SubagentSpec], justification: str = \"\",\n    ) -> list[SubagentResult]:\n        \"\"\"Run multiple sub-agents concurrently; wait for all; return results.\"\"\"\n        tasks = [\n            asyncio.create_task(self.inner.spawn(spec, justification=justification))\n            for spec in specs\n        ]\n        return await asyncio.gather(*tasks, return_exceptions=False)\n",[297,2347,2348,2353,2363,2367,2373,2384,2388,2402,2421,2425,2429,2435,2444,2453,2457,2468,2503,2518,2527,2537,2577,2591,2596],{"__ignoreMap":295},[300,2349,2350],{"class":302,"line":303},[300,2351,2352],{"class":306},"# src\u002Fharness\u002Fsubagents\u002Fparallel.py\n",[300,2354,2355,2357,2359,2361],{"class":302,"line":310},[300,2356,314],{"class":313},[300,2358,318],{"class":317},[300,2360,321],{"class":313},[300,2362,325],{"class":324},[300,2364,2365],{"class":302,"line":328},[300,2366,332],{"emptyLinePlaceholder":331},[300,2368,2369,2371],{"class":302,"line":335},[300,2370,338],{"class":313},[300,2372,341],{"class":324},[300,2374,2375,2377,2379,2381],{"class":302,"line":344},[300,2376,314],{"class":313},[300,2378,349],{"class":324},[300,2380,338],{"class":313},[300,2382,2383],{"class":324}," dataclass\n",[300,2385,2386],{"class":302,"line":364},[300,2387,332],{"emptyLinePlaceholder":331},[300,2389,2390,2392,2394,2397,2399],{"class":302,"line":387},[300,2391,314],{"class":313},[300,2393,1741],{"class":357},[300,2395,2396],{"class":324},"spawner ",[300,2398,338],{"class":313},[300,2400,2401],{"class":324}," SubagentSpawner\n",[300,2403,2404,2406,2408,2411,2413,2416,2418],{"class":302,"line":400},[300,2405,314],{"class":313},[300,2407,1741],{"class":357},[300,2409,2410],{"class":324},"subagent ",[300,2412,338],{"class":313},[300,2414,2415],{"class":324}," SubagentResult",[300,2417,358],{"class":357},[300,2419,2420],{"class":324}," SubagentSpec\n",[300,2422,2423],{"class":302,"line":405},[300,2424,332],{"emptyLinePlaceholder":331},[300,2426,2427],{"class":302,"line":410},[300,2428,332],{"emptyLinePlaceholder":331},[300,2430,2431,2433],{"class":302,"line":421},[300,2432,414],{"class":413},[300,2434,418],{"class":417},[300,2436,2437,2439,2442],{"class":302,"line":435},[300,2438,425],{"class":424},[300,2440,2441],{"class":428}," ParallelSpawner",[300,2443,432],{"class":357},[300,2445,2446,2449,2451],{"class":302,"line":448},[300,2447,2448],{"class":324},"    inner",[300,2450,441],{"class":357},[300,2452,2401],{"class":324},[300,2454,2455],{"class":302,"line":462},[300,2456,332],{"emptyLinePlaceholder":331},[300,2458,2459,2461,2463,2466],{"class":302,"line":475},[300,2460,657],{"class":424},[300,2462,660],{"class":424},[300,2464,2465],{"class":417}," spawn_all",[300,2467,666],{"class":357},[300,2469,2470,2472,2474,2477,2479,2481,2483,2486,2489,2492,2494,2496,2498,2501],{"class":302,"line":486},[300,2471,673],{"class":672},[300,2473,358],{"class":357},[300,2475,2476],{"class":678}," specs",[300,2478,441],{"class":357},[300,2480,1803],{"class":324},[300,2482,576],{"class":357},[300,2484,2485],{"class":324},"SubagentSpec",[300,2487,2488],{"class":357},"],",[300,2490,2491],{"class":678}," justification",[300,2493,441],{"class":357},[300,2495,456],{"class":444},[300,2497,590],{"class":589},[300,2499,2500],{"class":1308}," \"\"",[300,2502,941],{"class":357},[300,2504,2505,2507,2509,2511,2513,2516],{"class":302,"line":491},[300,2506,725],{"class":357},[300,2508,728],{"class":357},[300,2510,1803],{"class":324},[300,2512,576],{"class":357},[300,2514,2515],{"class":324},"SubagentResult",[300,2517,1811],{"class":357},[300,2519,2520,2522,2525],{"class":302,"line":496},[300,2521,1888],{"class":549},[300,2523,2524],{"class":553},"Run multiple sub-agents concurrently; wait for all; return results.",[300,2526,557],{"class":549},[300,2528,2529,2532,2534],{"class":302,"line":513},[300,2530,2531],{"class":324},"        tasks ",[300,2533,603],{"class":589},[300,2535,2536],{"class":357}," [\n",[300,2538,2539,2542,2544,2547,2549,2551,2553,2556,2558,2561,2563,2566,2568,2570,2572,2575],{"class":302,"line":519},[300,2540,2541],{"class":324},"            asyncio",[300,2543,623],{"class":357},[300,2545,2546],{"class":593},"create_task",[300,2548,504],{"class":357},[300,2550,1059],{"class":317},[300,2552,623],{"class":357},[300,2554,2555],{"class":626},"inner",[300,2557,623],{"class":357},[300,2559,2560],{"class":593},"spawn",[300,2562,504],{"class":357},[300,2564,2565],{"class":593},"spec",[300,2567,358],{"class":357},[300,2569,2491],{"class":599},[300,2571,603],{"class":589},[300,2573,2574],{"class":593},"justification",[300,2576,1580],{"class":357},[300,2578,2579,2582,2585,2588],{"class":302,"line":524},[300,2580,2581],{"class":313},"            for",[300,2583,2584],{"class":324}," spec ",[300,2586,2587],{"class":313},"in",[300,2589,2590],{"class":324}," specs\n",[300,2592,2593],{"class":302,"line":529},[300,2594,2595],{"class":357},"        ]\n",[300,2597,2598,2600,2602,2604,2606,2609,2611,2614,2617,2619,2622,2624,2627],{"class":302,"line":536},[300,2599,2250],{"class":313},[300,2601,1945],{"class":313},[300,2603,620],{"class":324},[300,2605,623],{"class":357},[300,2607,2608],{"class":593},"gather",[300,2610,504],{"class":357},[300,2612,2613],{"class":589},"*",[300,2615,2616],{"class":593},"tasks",[300,2618,358],{"class":357},[300,2620,2621],{"class":599}," return_exceptions",[300,2623,603],{"class":589},[300,2625,2626],{"class":795},"False",[300,2628,609],{"class":357},[112,2630,2631,2632,2635,2636,2638,2639,2641,2642,2644,2645,2647],{},"And a parent-facing tool. Same reasoning as §17.3: ",[297,2633,2634],{},"ParallelSpawner.spawn_all"," is ",[297,2637,1635],{},", so the tool wrapping it has to be ",[297,2640,1642],{}," + ",[297,2643,1635],{}," — a sync tool calling ",[297,2646,1654],{}," from inside the agent loop would crash.",[290,2649,2651],{"className":292,"code":2650,"language":294,"meta":295,"style":295},"# src\u002Fharness\u002Fsubagents\u002Fparallel_tool.py\nfrom ..tools.base import Tool\nfrom ..tools.decorator import async_tool\nfrom .parallel import ParallelSpawner\nfrom .subagent import SubagentSpec\n\n\ndef spawn_parallel_tool(spawner: ParallelSpawner) -> Tool:\n\n    @async_tool(side_effects={\"mutate\"})\n    async def spawn_parallel_subagents(\n        objectives: list[str],\n        output_format: str,\n        tools_allowed: list[str],\n        justification: str,\n    ) -> str:\n        \"\"\"Spawn multiple sub-agents concurrently.\n\n        objectives: list of distinct, non-overlapping objectives. Each\n                    sub-agent handles one. Do not pass the same objective\n                    twice.\n        output_format: format ALL sub-agents use; the parent synthesizes\n                       across parallel results, so they must be comparable.\n        tools_allowed: same list for all sub-agents.\n        justification: why parallel is better than sequential here.\n                       Required.\n\n        Returns a newline-separated, indexed list of sub-agent summaries.\n        Do not use this for tasks where one sub-agent's output is input\n        to the next — those need sequential spawn_subagent.\n        \"\"\"\n        if not justification:\n            return \"error: justification required\"\n        if len(objectives) \u003C 2:\n            return \"error: use spawn_subagent for a single objective\"\n        if len(set(objectives)) != len(objectives):\n            return \"error: objectives must be distinct (no duplicates)\"\n\n        specs = [\n            SubagentSpec(\n                objective=obj, output_format=output_format,\n                tools_allowed=tools_allowed,\n            )\n            for obj in objectives\n        ]\n        results = await spawner.spawn_all(specs, justification)\n        lines = []\n        for i, r in enumerate(results, start=1):\n            lines.append(f\"--- sub-agent {i} ---\")\n            if r.error:\n                lines.append(f\"error: {r.error}\")\n            else:\n                lines.append(r.summary)\n        return \"\\n\".join(lines)\n\n    return spawn_parallel_subagents\n",[297,2652,2653,2658,2675,2691,2705,2717,2721,2725,2750,2754,2777,2788,2804,2815,2830,2841,2851,2858,2862,2867,2872,2877,2882,2887,2892,2897,2902,2906,2911,2916,2921,2925,2935,2947,2970,2981,3008,3019,3023,3032,3039,3061,3073,3077,3089,3093,3121,3131,3166,3196,3210,3241,3248,3267,3290,3294],{"__ignoreMap":295},[300,2654,2655],{"class":302,"line":303},[300,2656,2657],{"class":306},"# src\u002Fharness\u002Fsubagents\u002Fparallel_tool.py\n",[300,2659,2660,2662,2664,2667,2669,2671,2673],{"class":302,"line":310},[300,2661,314],{"class":313},[300,2663,1717],{"class":357},[300,2665,2666],{"class":324},"tools",[300,2668,623],{"class":357},[300,2670,1744],{"class":324},[300,2672,338],{"class":313},[300,2674,1749],{"class":324},[300,2676,2677,2679,2681,2683,2685,2687,2689],{"class":302,"line":328},[300,2678,314],{"class":313},[300,2680,1717],{"class":357},[300,2682,2666],{"class":324},[300,2684,623],{"class":357},[300,2686,1758],{"class":324},[300,2688,338],{"class":313},[300,2690,1763],{"class":324},[300,2692,2693,2695,2697,2700,2702],{"class":302,"line":335},[300,2694,314],{"class":313},[300,2696,1741],{"class":357},[300,2698,2699],{"class":324},"parallel ",[300,2701,338],{"class":313},[300,2703,2704],{"class":324}," ParallelSpawner\n",[300,2706,2707,2709,2711,2713,2715],{"class":302,"line":344},[300,2708,314],{"class":313},[300,2710,1741],{"class":357},[300,2712,2410],{"class":324},[300,2714,338],{"class":313},[300,2716,2420],{"class":324},[300,2718,2719],{"class":302,"line":364},[300,2720,332],{"emptyLinePlaceholder":331},[300,2722,2723],{"class":302,"line":387},[300,2724,332],{"emptyLinePlaceholder":331},[300,2726,2727,2729,2732,2734,2737,2739,2741,2743,2745,2748],{"class":302,"line":400},[300,2728,1776],{"class":424},[300,2730,2731],{"class":417}," spawn_parallel_tool",[300,2733,504],{"class":357},[300,2735,2736],{"class":678},"spawner",[300,2738,441],{"class":357},[300,2740,2441],{"class":324},[300,2742,997],{"class":357},[300,2744,728],{"class":357},[300,2746,2747],{"class":324}," Tool",[300,2749,432],{"class":357},[300,2751,2752],{"class":302,"line":405},[300,2753,332],{"emptyLinePlaceholder":331},[300,2755,2756,2758,2760,2762,2764,2766,2768,2770,2773,2775],{"class":302,"line":410},[300,2757,1820],{"class":413},[300,2759,1823],{"class":417},[300,2761,504],{"class":357},[300,2763,1828],{"class":599},[300,2765,603],{"class":589},[300,2767,857],{"class":357},[300,2769,1309],{"class":1308},[300,2771,2772],{"class":853},"mutate",[300,2774,1309],{"class":1308},[300,2776,1842],{"class":357},[300,2778,2779,2781,2783,2786],{"class":302,"line":421},[300,2780,657],{"class":424},[300,2782,660],{"class":424},[300,2784,2785],{"class":417}," spawn_parallel_subagents",[300,2787,666],{"class":357},[300,2789,2790,2793,2795,2797,2799,2801],{"class":302,"line":435},[300,2791,2792],{"class":678},"        objectives",[300,2794,441],{"class":357},[300,2796,1803],{"class":324},[300,2798,576],{"class":357},[300,2800,579],{"class":444},[300,2802,2803],{"class":357},"],\n",[300,2805,2806,2809,2811,2813],{"class":302,"line":448},[300,2807,2808],{"class":678},"        output_format",[300,2810,441],{"class":357},[300,2812,456],{"class":444},[300,2814,941],{"class":357},[300,2816,2817,2820,2822,2824,2826,2828],{"class":302,"line":462},[300,2818,2819],{"class":678},"        tools_allowed",[300,2821,441],{"class":357},[300,2823,1803],{"class":324},[300,2825,576],{"class":357},[300,2827,579],{"class":444},[300,2829,2803],{"class":357},[300,2831,2832,2835,2837,2839],{"class":302,"line":475},[300,2833,2834],{"class":678},"        justification",[300,2836,441],{"class":357},[300,2838,456],{"class":444},[300,2840,941],{"class":357},[300,2842,2843,2845,2847,2849],{"class":302,"line":486},[300,2844,725],{"class":357},[300,2846,728],{"class":357},[300,2848,456],{"class":444},[300,2850,432],{"class":357},[300,2852,2853,2855],{"class":302,"line":491},[300,2854,1888],{"class":549},[300,2856,2857],{"class":553},"Spawn multiple sub-agents concurrently.\n",[300,2859,2860],{"class":302,"line":496},[300,2861,332],{"emptyLinePlaceholder":331},[300,2863,2864],{"class":302,"line":513},[300,2865,2866],{"class":553},"        objectives: list of distinct, non-overlapping objectives. Each\n",[300,2868,2869],{"class":302,"line":519},[300,2870,2871],{"class":553},"                    sub-agent handles one. Do not pass the same objective\n",[300,2873,2874],{"class":302,"line":524},[300,2875,2876],{"class":553},"                    twice.\n",[300,2878,2879],{"class":302,"line":529},[300,2880,2881],{"class":553},"        output_format: format ALL sub-agents use; the parent synthesizes\n",[300,2883,2884],{"class":302,"line":536},[300,2885,2886],{"class":553},"                       across parallel results, so they must be comparable.\n",[300,2888,2889],{"class":302,"line":546},[300,2890,2891],{"class":553},"        tools_allowed: same list for all sub-agents.\n",[300,2893,2894],{"class":302,"line":560},[300,2895,2896],{"class":553},"        justification: why parallel is better than sequential here.\n",[300,2898,2899],{"class":302,"line":565},[300,2900,2901],{"class":553},"                       Required.\n",[300,2903,2904],{"class":302,"line":612},[300,2905,332],{"emptyLinePlaceholder":331},[300,2907,2908],{"class":302,"line":649},[300,2909,2910],{"class":553},"        Returns a newline-separated, indexed list of sub-agent summaries.\n",[300,2912,2913],{"class":302,"line":654},[300,2914,2915],{"class":553},"        Do not use this for tasks where one sub-agent's output is input\n",[300,2917,2918],{"class":302,"line":669},[300,2919,2920],{"class":553},"        to the next — those need sequential spawn_subagent.\n",[300,2922,2923],{"class":302,"line":722},[300,2924,1929],{"class":549},[300,2926,2927,2929,2931,2933],{"class":302,"line":735},[300,2928,2205],{"class":313},[300,2930,792],{"class":589},[300,2932,2491],{"class":324},[300,2934,432],{"class":357},[300,2936,2937,2939,2942,2945],{"class":302,"line":754},[300,2938,1037],{"class":313},[300,2940,2941],{"class":1308}," \"",[300,2943,2944],{"class":853},"error: justification required",[300,2946,885],{"class":1308},[300,2948,2949,2951,2955,2957,2960,2962,2965,2968],{"class":302,"line":780},[300,2950,2205],{"class":313},[300,2952,2954],{"class":2953},"sptTA"," len",[300,2956,504],{"class":357},[300,2958,2959],{"class":593},"objectives",[300,2961,997],{"class":357},[300,2963,2964],{"class":589}," \u003C",[300,2966,2967],{"class":716}," 2",[300,2969,432],{"class":357},[300,2971,2972,2974,2976,2979],{"class":302,"line":801},[300,2973,1037],{"class":313},[300,2975,2941],{"class":1308},[300,2977,2978],{"class":853},"error: use spawn_subagent for a single objective",[300,2980,885],{"class":1308},[300,2982,2983,2985,2987,2989,2992,2994,2996,2998,3000,3002,3004,3006],{"class":302,"line":837},[300,2984,2205],{"class":313},[300,2986,2954],{"class":2953},[300,2988,504],{"class":357},[300,2990,2991],{"class":444},"set",[300,2993,504],{"class":357},[300,2995,2959],{"class":593},[300,2997,1218],{"class":357},[300,2999,1287],{"class":589},[300,3001,2954],{"class":2953},[300,3003,504],{"class":357},[300,3005,2959],{"class":593},[300,3007,510],{"class":357},[300,3009,3010,3012,3014,3017],{"class":302,"line":847},[300,3011,1037],{"class":313},[300,3013,2941],{"class":1308},[300,3015,3016],{"class":853},"error: objectives must be distinct (no duplicates)",[300,3018,885],{"class":1308},[300,3020,3021],{"class":302,"line":888},[300,3022,332],{"emptyLinePlaceholder":331},[300,3024,3025,3028,3030],{"class":302,"line":894},[300,3026,3027],{"class":324},"        specs ",[300,3029,603],{"class":589},[300,3031,2536],{"class":357},[300,3033,3034,3037],{"class":302,"line":900},[300,3035,3036],{"class":593},"            SubagentSpec",[300,3038,666],{"class":357},[300,3040,3041,3044,3046,3049,3051,3054,3056,3059],{"class":302,"line":919},[300,3042,3043],{"class":599},"                objective",[300,3045,603],{"class":589},[300,3047,3048],{"class":593},"obj",[300,3050,358],{"class":357},[300,3052,3053],{"class":599}," output_format",[300,3055,603],{"class":589},[300,3057,3058],{"class":593},"output_format",[300,3060,941],{"class":357},[300,3062,3063,3066,3068,3071],{"class":302,"line":931},[300,3064,3065],{"class":599},"                tools_allowed",[300,3067,603],{"class":589},[300,3069,3070],{"class":593},"tools_allowed",[300,3072,941],{"class":357},[300,3074,3075],{"class":302,"line":944},[300,3076,1010],{"class":357},[300,3078,3079,3081,3084,3086],{"class":302,"line":956},[300,3080,2581],{"class":313},[300,3082,3083],{"class":324}," obj ",[300,3085,2587],{"class":313},[300,3087,3088],{"class":324}," objectives\n",[300,3090,3091],{"class":302,"line":974},[300,3092,2595],{"class":357},[300,3094,3095,3098,3100,3102,3105,3107,3110,3112,3115,3117,3119],{"class":302,"line":1007},[300,3096,3097],{"class":324},"        results ",[300,3099,603],{"class":589},[300,3101,1945],{"class":313},[300,3103,3104],{"class":324}," spawner",[300,3106,623],{"class":357},[300,3108,3109],{"class":593},"spawn_all",[300,3111,504],{"class":357},[300,3113,3114],{"class":593},"specs",[300,3116,358],{"class":357},[300,3118,2491],{"class":593},[300,3120,609],{"class":357},[300,3122,3123,3126,3128],{"class":302,"line":1013},[300,3124,3125],{"class":324},"        lines ",[300,3127,603],{"class":589},[300,3129,3130],{"class":357}," []\n",[300,3132,3133,3136,3139,3141,3144,3146,3149,3151,3154,3156,3159,3161,3164],{"class":302,"line":1034},[300,3134,3135],{"class":313},"        for",[300,3137,3138],{"class":324}," i",[300,3140,358],{"class":357},[300,3142,3143],{"class":324}," r ",[300,3145,2587],{"class":313},[300,3147,3148],{"class":2953}," enumerate",[300,3150,504],{"class":357},[300,3152,3153],{"class":593},"results",[300,3155,358],{"class":357},[300,3157,3158],{"class":599}," start",[300,3160,603],{"class":589},[300,3162,3163],{"class":716},"1",[300,3165,510],{"class":357},[300,3167,3168,3171,3173,3176,3178,3181,3184,3186,3189,3191,3194],{"class":302,"line":1042},[300,3169,3170],{"class":324},"            lines",[300,3172,623],{"class":357},[300,3174,3175],{"class":593},"append",[300,3177,504],{"class":357},[300,3179,3180],{"class":424},"f",[300,3182,3183],{"class":853},"\"--- sub-agent ",[300,3185,857],{"class":716},[300,3187,3188],{"class":593},"i",[300,3190,865],{"class":716},[300,3192,3193],{"class":853}," ---\"",[300,3195,609],{"class":357},[300,3197,3198,3200,3203,3205,3208],{"class":302,"line":1047},[300,3199,783],{"class":313},[300,3201,3202],{"class":324}," r",[300,3204,623],{"class":357},[300,3206,3207],{"class":626},"error",[300,3209,432],{"class":357},[300,3211,3212,3215,3217,3219,3221,3223,3226,3228,3231,3233,3235,3237,3239],{"class":302,"line":1079},[300,3213,3214],{"class":324},"                lines",[300,3216,623],{"class":357},[300,3218,3175],{"class":593},[300,3220,504],{"class":357},[300,3222,3180],{"class":424},[300,3224,3225],{"class":853},"\"error: ",[300,3227,857],{"class":716},[300,3229,3230],{"class":593},"r",[300,3232,623],{"class":357},[300,3234,3207],{"class":626},[300,3236,865],{"class":716},[300,3238,1309],{"class":853},[300,3240,609],{"class":357},[300,3242,3243,3246],{"class":302,"line":1094},[300,3244,3245],{"class":313},"            else",[300,3247,432],{"class":357},[300,3249,3250,3252,3254,3256,3258,3260,3262,3265],{"class":302,"line":1122},[300,3251,3214],{"class":324},[300,3253,623],{"class":357},[300,3255,3175],{"class":593},[300,3257,504],{"class":357},[300,3259,3230],{"class":593},[300,3261,623],{"class":357},[300,3263,3264],{"class":626},"summary",[300,3266,609],{"class":357},[300,3268,3269,3271,3273,3276,3278,3280,3283,3285,3288],{"class":302,"line":1150},[300,3270,2250],{"class":313},[300,3272,2941],{"class":1308},[300,3274,3275],{"class":317},"\\n",[300,3277,1309],{"class":1308},[300,3279,623],{"class":357},[300,3281,3282],{"class":593},"join",[300,3284,504],{"class":357},[300,3286,3287],{"class":593},"lines",[300,3289,609],{"class":357},[300,3291,3292],{"class":302,"line":1171},[300,3293,332],{"emptyLinePlaceholder":331},[300,3295,3296,3298],{"class":302,"line":1176},[300,3297,2308],{"class":313},[300,3299,3300],{"class":324}," spawn_parallel_subagents\n",[112,3302,3303,3304,3311,3312,3315,3316,3319],{},"The tool enforces the things ",[3305,3306,3310],"a",{"href":3307,"rel":3308},"https:\u002F\u002Fwww.anthropic.com\u002Fengineering\u002Fmulti-agent-research-system",[3309],"nofollow","Anthropic's multi-agent writeup"," warned about: non-overlapping objectives (distinct strings), same output format for comparability, justification required, minimum count to avoid misuse. The parent that wants to run one sub-agent uses ",[297,3313,3314],{},"spawn_subagent","; the parent that wants parallelism uses ",[297,3317,3318],{},"spawn_parallel_subagents",". Two different tools, two different default behaviors, one clean contract each.",[267,3321],{},[270,3323,3325],{"id":3324},"_175-grounding-verification-in-external-truth","17.5 Grounding Verification in External Truth",[112,3327,3328],{},"The hallucinated-consensus problem: if two sub-agents verify each other's claims, neither grounds anything against reality.",[112,3330,3331,3332,3335],{},"The fix is a verification discipline, not a new primitive: ",[124,3333,3334],{},"verification postconditions require an external tool call as evidence",". A sub-agent can't claim \"I checked and the file exists\" unless its evidence string references a concrete tool result. The orchestrator's synthesis can't claim consensus unless it records which external source confirmed each fact.",[112,3337,3338],{},"We encode this in the system prompt for sub-agents:",[290,3340,3345],{"className":3341,"code":3343,"language":3344,"meta":295},[3342],"language-text","When you claim a fact as true, your evidence must reference an external\nsource: a tool call you just made, a file you just read, an API response\nyou just received. Do not cite another sub-agent's summary as evidence —\nsub-agents can be wrong. If you cannot ground a fact externally, say\n\"unconfirmed\" in your output rather than asserting the fact.\n","text",[297,3346,3343],{"__ignoreMap":295},[112,3348,3349,3350,3353],{},"And we add a ",[297,3351,3352],{},"verify_fact_externally"," tool that the agent calls when it wants to assert a fact confidently:",[290,3355,3357],{"className":292,"code":3356,"language":294,"meta":295,"style":295},"@tool(side_effects={\"read\"})\ndef verify_fact_externally(claim: str, tool_used: str, tool_result: str) -> str:\n    \"\"\"Record that a claim has been externally verified.\n\n    claim: what you are asserting.\n    tool_used: name of the tool whose output grounds this claim.\n    tool_result: quoted excerpt from the tool's output that supports the\n                 claim. Must be a direct quote, not a paraphrase.\n\n    Returns a verification receipt you include in your final summary.\n    \"\"\"\n    return (f\"[verified: {claim}; grounded in {tool_used} output: \"\n            f\"{tool_result[:200]}]\")\n",[297,3358,3359,3383,3425,3432,3436,3441,3446,3451,3456,3460,3465,3470,3500],{"__ignoreMap":295},[300,3360,3361,3363,3366,3368,3370,3372,3374,3376,3379,3381],{"class":302,"line":303},[300,3362,414],{"class":413},[300,3364,3365],{"class":417},"tool",[300,3367,504],{"class":357},[300,3369,1828],{"class":599},[300,3371,603],{"class":589},[300,3373,857],{"class":357},[300,3375,1309],{"class":1308},[300,3377,3378],{"class":853},"read",[300,3380,1309],{"class":1308},[300,3382,1842],{"class":357},[300,3384,3385,3387,3390,3392,3395,3397,3399,3401,3404,3406,3408,3410,3413,3415,3417,3419,3421,3423],{"class":302,"line":310},[300,3386,1776],{"class":424},[300,3388,3389],{"class":417}," verify_fact_externally",[300,3391,504],{"class":357},[300,3393,3394],{"class":678},"claim",[300,3396,441],{"class":357},[300,3398,456],{"class":444},[300,3400,358],{"class":357},[300,3402,3403],{"class":678}," tool_used",[300,3405,441],{"class":357},[300,3407,456],{"class":444},[300,3409,358],{"class":357},[300,3411,3412],{"class":678}," tool_result",[300,3414,441],{"class":357},[300,3416,456],{"class":444},[300,3418,997],{"class":357},[300,3420,728],{"class":357},[300,3422,456],{"class":444},[300,3424,432],{"class":357},[300,3426,3427,3429],{"class":302,"line":328},[300,3428,550],{"class":549},[300,3430,3431],{"class":553},"Record that a claim has been externally verified.\n",[300,3433,3434],{"class":302,"line":335},[300,3435,332],{"emptyLinePlaceholder":331},[300,3437,3438],{"class":302,"line":344},[300,3439,3440],{"class":553},"    claim: what you are asserting.\n",[300,3442,3443],{"class":302,"line":364},[300,3444,3445],{"class":553},"    tool_used: name of the tool whose output grounds this claim.\n",[300,3447,3448],{"class":302,"line":387},[300,3449,3450],{"class":553},"    tool_result: quoted excerpt from the tool's output that supports the\n",[300,3452,3453],{"class":302,"line":400},[300,3454,3455],{"class":553},"                 claim. Must be a direct quote, not a paraphrase.\n",[300,3457,3458],{"class":302,"line":405},[300,3459,332],{"emptyLinePlaceholder":331},[300,3461,3462],{"class":302,"line":410},[300,3463,3464],{"class":553},"    Returns a verification receipt you include in your final summary.\n",[300,3466,3467],{"class":302,"line":421},[300,3468,3469],{"class":549},"    \"\"\"\n",[300,3471,3472,3474,3476,3478,3481,3483,3485,3487,3490,3492,3495,3497],{"class":302,"line":435},[300,3473,2308],{"class":313},[300,3475,1523],{"class":357},[300,3477,3180],{"class":424},[300,3479,3480],{"class":853},"\"[verified: ",[300,3482,857],{"class":716},[300,3484,3394],{"class":324},[300,3486,865],{"class":716},[300,3488,3489],{"class":853},"; grounded in ",[300,3491,857],{"class":716},[300,3493,3494],{"class":324},"tool_used",[300,3496,865],{"class":716},[300,3498,3499],{"class":853}," output: \"\n",[300,3501,3502,3505,3507,3509,3512,3515,3518,3520,3522,3525],{"class":302,"line":448},[300,3503,3504],{"class":424},"            f",[300,3506,1309],{"class":853},[300,3508,857],{"class":716},[300,3510,3511],{"class":324},"tool_result",[300,3513,3514],{"class":357},"[:",[300,3516,3517],{"class":716},"200",[300,3519,586],{"class":357},[300,3521,865],{"class":716},[300,3523,3524],{"class":853},"]\"",[300,3526,609],{"class":357},[112,3528,3529],{},"This looks bureaucratic. It is. The trick is that the friction is what stops the agent from rubber-stamping. In practice, agents that use this tool produce visibly different — and more accurate — outputs than agents that don't.",[112,3531,3532,3535,3536,3539,3540,3542,3543,3546,3547,3550,3551,3554,3555,3558,3559,3561,3562,3565,3566,3568],{},[124,3533,3534],{},"A model-capability floor."," There's a failure mode below this whole discipline that system-prompt phrasing alone cannot fix: a sub-agent told to run ",[297,3537,3538],{},"uptime"," will emit a textual narrative of what ",[297,3541,3538],{}," would produce, without ever dispatching the bash tool. ",[297,3544,3545],{},"iterations_used=1",", zero tool calls in the transcript, a confident-sounding summary containing Linux-flavored output even on macOS, or ",[297,3548,3549],{},"\u002Fproc\u002Fmeminfo"," fields in response to a ",[297,3552,3553],{},"vm_stat"," objective. The coordinator then synthesizes across these fabricated outputs and produces a \"machine state report\" that is entirely fiction, presented confidently. This happens consistently with open models in the 7–12B parameter range (Gemma, Llama-class) and occasionally with larger models under ambiguous prompts. Frontier models (Opus, Sonnet 4.5-class) comply with implicit \"use your tools\" expectations; local models need the expectation made explicit. Two defenses that work: (a) §15.2's ",[297,3556,3557],{},"_default_subagent_system"," already injects a hard imperative — \"You MUST call at least one tool from your allowed list before producing your final answer. Do not describe what you would do — do it\" — whenever ",[297,3560,3070],{}," is non-empty; (b) in your eval harness (Chapter 19), add an assertion that fails any sub-agent run where ",[297,3563,3564],{},"iterations_used == 1"," and the transcript contains zero tool calls. Combined, these shift the symptom from silent hallucinated output to a visible missing tool call, which is debuggable. The ",[297,3567,3352],{}," tool above raises the ceiling on what a competent model will do; the capability floor is about whether the model is competent enough in the first place.",[267,3570],{},[270,3572,3574],{"id":3573},"_176-a-parallel-research-scenario","17.6 A Parallel Research Scenario",[290,3576,3578],{"className":292,"code":3577,"language":294,"meta":295,"style":295},"# examples\u002Fch17_parallel.py\nimport asyncio\nfrom pathlib import Path\n\nfrom harness.agent import arun\nfrom harness.coordination.lease import LeaseManager\nfrom harness.providers.anthropic import AnthropicProvider\nfrom harness.subagents.parallel import ParallelSpawner\nfrom harness.subagents.parallel_tool import spawn_parallel_tool\nfrom harness.subagents.spawn_tool import spawn_tool\nfrom harness.subagents.spawner import SubagentSpawner\nfrom harness.tools.scratchpad import Scratchpad\nfrom harness.tools.selector import ToolCatalog\nfrom harness.tools.std import STANDARD_TOOLS\n\n\nSYSTEM = \"\"\"\\\nYou are a research coordinator. When a user question decomposes into\ndistinct, non-overlapping sub-questions, use spawn_parallel_subagents to\nrun them concurrently. When steps must be sequential, use spawn_subagent.\n\nAlways require concrete external grounding: each sub-agent should ground\nits claims in specific tool outputs, not in other sub-agents' output.\n\"\"\"\n\n\nasync def main() -> None:\n    provider = AnthropicProvider()\n    lease_mgr = LeaseManager()\n    pad = Scratchpad(root=Path(\".scratchpad\"))\n\n    catalog = ToolCatalog(tools=STANDARD_TOOLS + pad.as_tools())\n    sub_spawner = SubagentSpawner(provider=provider, catalog=catalog)\n    par_spawner = ParallelSpawner(inner=sub_spawner)\n\n    coordinator_catalog = ToolCatalog(tools=(\n        STANDARD_TOOLS + pad.as_tools() +\n        [spawn_tool(sub_spawner), spawn_parallel_tool(par_spawner)]\n    ))\n\n    await arun(\n        provider=provider,\n        catalog=coordinator_catalog,\n        system=SYSTEM,\n        user_message=(\n            \"Investigate this machine's resource state, concurrently. \"\n            \"I need four things: CPU load average, memory utilization, \"\n            \"disk utilization on \u002F, and the three largest directories \"\n            \"under \u002Fvar. Each sub-agent should use bash; each should \"\n            \"ground its answer in a specific bash command's output. \"\n            \"Synthesize the four answers into one paragraph.\"\n        ),\n    )\n\n\nasyncio.run(main())\n",[297,3579,3580,3585,3591,3601,3605,3622,3640,3661,3680,3700,3720,3738,3758,3778,3798,3802,3806,3819,3824,3829,3834,3838,3843,3848,3852,3856,3860,3879,3892,3903,3934,3938,3970,4001,4021,4025,4042,4060,4085,4090,4094,4104,4115,4127,4138,4147,4157,4166,4175,4184,4193,4202,4207,4212,4216,4220],{"__ignoreMap":295},[300,3581,3582],{"class":302,"line":303},[300,3583,3584],{"class":306},"# examples\u002Fch17_parallel.py\n",[300,3586,3587,3589],{"class":302,"line":310},[300,3588,338],{"class":313},[300,3590,341],{"class":324},[300,3592,3593,3595,3597,3599],{"class":302,"line":328},[300,3594,314],{"class":313},[300,3596,1701],{"class":324},[300,3598,338],{"class":313},[300,3600,1706],{"class":324},[300,3602,3603],{"class":302,"line":335},[300,3604,332],{"emptyLinePlaceholder":331},[300,3606,3607,3609,3612,3614,3617,3619],{"class":302,"line":344},[300,3608,314],{"class":313},[300,3610,3611],{"class":324}," harness",[300,3613,623],{"class":357},[300,3615,3616],{"class":324},"agent ",[300,3618,338],{"class":313},[300,3620,3621],{"class":324}," arun\n",[300,3623,3624,3626,3628,3630,3632,3634,3636,3638],{"class":302,"line":364},[300,3625,314],{"class":313},[300,3627,3611],{"class":324},[300,3629,623],{"class":357},[300,3631,1720],{"class":324},[300,3633,623],{"class":357},[300,3635,1725],{"class":324},[300,3637,338],{"class":313},[300,3639,1734],{"class":324},[300,3641,3642,3644,3646,3648,3651,3653,3656,3658],{"class":302,"line":387},[300,3643,314],{"class":313},[300,3645,3611],{"class":324},[300,3647,623],{"class":357},[300,3649,3650],{"class":324},"providers",[300,3652,623],{"class":357},[300,3654,3655],{"class":324},"anthropic ",[300,3657,338],{"class":313},[300,3659,3660],{"class":324}," AnthropicProvider\n",[300,3662,3663,3665,3667,3669,3672,3674,3676,3678],{"class":302,"line":400},[300,3664,314],{"class":313},[300,3666,3611],{"class":324},[300,3668,623],{"class":357},[300,3670,3671],{"class":324},"subagents",[300,3673,623],{"class":357},[300,3675,2699],{"class":324},[300,3677,338],{"class":313},[300,3679,2704],{"class":324},[300,3681,3682,3684,3686,3688,3690,3692,3695,3697],{"class":302,"line":405},[300,3683,314],{"class":313},[300,3685,3611],{"class":324},[300,3687,623],{"class":357},[300,3689,3671],{"class":324},[300,3691,623],{"class":357},[300,3693,3694],{"class":324},"parallel_tool ",[300,3696,338],{"class":313},[300,3698,3699],{"class":324}," spawn_parallel_tool\n",[300,3701,3702,3704,3706,3708,3710,3712,3715,3717],{"class":302,"line":410},[300,3703,314],{"class":313},[300,3705,3611],{"class":324},[300,3707,623],{"class":357},[300,3709,3671],{"class":324},[300,3711,623],{"class":357},[300,3713,3714],{"class":324},"spawn_tool ",[300,3716,338],{"class":313},[300,3718,3719],{"class":324}," spawn_tool\n",[300,3721,3722,3724,3726,3728,3730,3732,3734,3736],{"class":302,"line":421},[300,3723,314],{"class":313},[300,3725,3611],{"class":324},[300,3727,623],{"class":357},[300,3729,3671],{"class":324},[300,3731,623],{"class":357},[300,3733,2396],{"class":324},[300,3735,338],{"class":313},[300,3737,2401],{"class":324},[300,3739,3740,3742,3744,3746,3748,3750,3753,3755],{"class":302,"line":435},[300,3741,314],{"class":313},[300,3743,3611],{"class":324},[300,3745,623],{"class":357},[300,3747,2666],{"class":324},[300,3749,623],{"class":357},[300,3751,3752],{"class":324},"scratchpad ",[300,3754,338],{"class":313},[300,3756,3757],{"class":324}," Scratchpad\n",[300,3759,3760,3762,3764,3766,3768,3770,3773,3775],{"class":302,"line":448},[300,3761,314],{"class":313},[300,3763,3611],{"class":324},[300,3765,623],{"class":357},[300,3767,2666],{"class":324},[300,3769,623],{"class":357},[300,3771,3772],{"class":324},"selector ",[300,3774,338],{"class":313},[300,3776,3777],{"class":324}," ToolCatalog\n",[300,3779,3780,3782,3784,3786,3788,3790,3793,3795],{"class":302,"line":462},[300,3781,314],{"class":313},[300,3783,3611],{"class":324},[300,3785,623],{"class":357},[300,3787,2666],{"class":324},[300,3789,623],{"class":357},[300,3791,3792],{"class":324},"std ",[300,3794,338],{"class":313},[300,3796,3797],{"class":317}," STANDARD_TOOLS\n",[300,3799,3800],{"class":302,"line":475},[300,3801,332],{"emptyLinePlaceholder":331},[300,3803,3804],{"class":302,"line":486},[300,3805,332],{"emptyLinePlaceholder":331},[300,3807,3808,3811,3813,3816],{"class":302,"line":491},[300,3809,3810],{"class":317},"SYSTEM",[300,3812,590],{"class":589},[300,3814,3815],{"class":1308}," \"\"\"",[300,3817,3818],{"class":795},"\\\n",[300,3820,3821],{"class":302,"line":496},[300,3822,3823],{"class":853},"You are a research coordinator. When a user question decomposes into\n",[300,3825,3826],{"class":302,"line":513},[300,3827,3828],{"class":853},"distinct, non-overlapping sub-questions, use spawn_parallel_subagents to\n",[300,3830,3831],{"class":302,"line":519},[300,3832,3833],{"class":853},"run them concurrently. When steps must be sequential, use spawn_subagent.\n",[300,3835,3836],{"class":302,"line":524},[300,3837,332],{"emptyLinePlaceholder":331},[300,3839,3840],{"class":302,"line":529},[300,3841,3842],{"class":853},"Always require concrete external grounding: each sub-agent should ground\n",[300,3844,3845],{"class":302,"line":536},[300,3846,3847],{"class":853},"its claims in specific tool outputs, not in other sub-agents' output.\n",[300,3849,3850],{"class":302,"line":546},[300,3851,557],{"class":1308},[300,3853,3854],{"class":302,"line":560},[300,3855,332],{"emptyLinePlaceholder":331},[300,3857,3858],{"class":302,"line":565},[300,3859,332],{"emptyLinePlaceholder":331},[300,3861,3862,3865,3867,3870,3873,3875,3877],{"class":302,"line":612},[300,3863,3864],{"class":424},"async",[300,3866,660],{"class":424},[300,3868,3869],{"class":417}," main",[300,3871,3872],{"class":357},"()",[300,3874,728],{"class":357},[300,3876,796],{"class":795},[300,3878,432],{"class":357},[300,3880,3881,3884,3886,3889],{"class":302,"line":649},[300,3882,3883],{"class":324},"    provider ",[300,3885,603],{"class":589},[300,3887,3888],{"class":593}," AnthropicProvider",[300,3890,3891],{"class":357},"()\n",[300,3893,3894,3897,3899,3901],{"class":302,"line":654},[300,3895,3896],{"class":324},"    lease_mgr ",[300,3898,603],{"class":589},[300,3900,541],{"class":593},[300,3902,3891],{"class":357},[300,3904,3905,3908,3910,3913,3915,3918,3920,3923,3925,3927,3930,3932],{"class":302,"line":669},[300,3906,3907],{"class":324},"    pad ",[300,3909,603],{"class":589},[300,3911,3912],{"class":593}," Scratchpad",[300,3914,504],{"class":357},[300,3916,3917],{"class":599},"root",[300,3919,603],{"class":589},[300,3921,3922],{"class":593},"Path",[300,3924,504],{"class":357},[300,3926,1309],{"class":1308},[300,3928,3929],{"class":853},".scratchpad",[300,3931,1309],{"class":1308},[300,3933,1580],{"class":357},[300,3935,3936],{"class":302,"line":722},[300,3937,332],{"emptyLinePlaceholder":331},[300,3939,3940,3943,3945,3948,3950,3952,3954,3957,3959,3962,3964,3967],{"class":302,"line":735},[300,3941,3942],{"class":324},"    catalog ",[300,3944,603],{"class":589},[300,3946,3947],{"class":593}," ToolCatalog",[300,3949,504],{"class":357},[300,3951,2666],{"class":599},[300,3953,603],{"class":589},[300,3955,3956],{"class":2953},"STANDARD_TOOLS",[300,3958,1000],{"class":589},[300,3960,3961],{"class":593}," pad",[300,3963,623],{"class":357},[300,3965,3966],{"class":593},"as_tools",[300,3968,3969],{"class":357},"())\n",[300,3971,3972,3975,3977,3980,3982,3985,3987,3989,3991,3994,3996,3999],{"class":302,"line":754},[300,3973,3974],{"class":324},"    sub_spawner ",[300,3976,603],{"class":589},[300,3978,3979],{"class":593}," SubagentSpawner",[300,3981,504],{"class":357},[300,3983,3984],{"class":599},"provider",[300,3986,603],{"class":589},[300,3988,3984],{"class":593},[300,3990,358],{"class":357},[300,3992,3993],{"class":599}," catalog",[300,3995,603],{"class":589},[300,3997,3998],{"class":593},"catalog",[300,4000,609],{"class":357},[300,4002,4003,4006,4008,4010,4012,4014,4016,4019],{"class":302,"line":780},[300,4004,4005],{"class":324},"    par_spawner ",[300,4007,603],{"class":589},[300,4009,2441],{"class":593},[300,4011,504],{"class":357},[300,4013,2555],{"class":599},[300,4015,603],{"class":589},[300,4017,4018],{"class":593},"sub_spawner",[300,4020,609],{"class":357},[300,4022,4023],{"class":302,"line":801},[300,4024,332],{"emptyLinePlaceholder":331},[300,4026,4027,4030,4032,4034,4036,4038,4040],{"class":302,"line":837},[300,4028,4029],{"class":324},"    coordinator_catalog ",[300,4031,603],{"class":589},[300,4033,3947],{"class":593},[300,4035,504],{"class":357},[300,4037,2666],{"class":599},[300,4039,603],{"class":589},[300,4041,666],{"class":357},[300,4043,4044,4047,4049,4051,4053,4055,4057],{"class":302,"line":847},[300,4045,4046],{"class":2953},"        STANDARD_TOOLS",[300,4048,1000],{"class":589},[300,4050,3961],{"class":593},[300,4052,623],{"class":357},[300,4054,3966],{"class":593},[300,4056,3872],{"class":357},[300,4058,4059],{"class":589}," +\n",[300,4061,4062,4065,4068,4070,4072,4075,4077,4079,4082],{"class":302,"line":888},[300,4063,4064],{"class":357},"        [",[300,4066,4067],{"class":593},"spawn_tool",[300,4069,504],{"class":357},[300,4071,4018],{"class":593},[300,4073,4074],{"class":357},"),",[300,4076,2731],{"class":593},[300,4078,504],{"class":357},[300,4080,4081],{"class":593},"par_spawner",[300,4083,4084],{"class":357},")]\n",[300,4086,4087],{"class":302,"line":894},[300,4088,4089],{"class":357},"    ))\n",[300,4091,4092],{"class":302,"line":900},[300,4093,332],{"emptyLinePlaceholder":331},[300,4095,4096,4099,4102],{"class":302,"line":919},[300,4097,4098],{"class":313},"    await",[300,4100,4101],{"class":593}," arun",[300,4103,666],{"class":357},[300,4105,4106,4109,4111,4113],{"class":302,"line":931},[300,4107,4108],{"class":599},"        provider",[300,4110,603],{"class":589},[300,4112,3984],{"class":593},[300,4114,941],{"class":357},[300,4116,4117,4120,4122,4125],{"class":302,"line":944},[300,4118,4119],{"class":599},"        catalog",[300,4121,603],{"class":589},[300,4123,4124],{"class":593},"coordinator_catalog",[300,4126,941],{"class":357},[300,4128,4129,4132,4134,4136],{"class":302,"line":956},[300,4130,4131],{"class":599},"        system",[300,4133,603],{"class":589},[300,4135,3810],{"class":2953},[300,4137,941],{"class":357},[300,4139,4140,4143,4145],{"class":302,"line":974},[300,4141,4142],{"class":599},"        user_message",[300,4144,603],{"class":589},[300,4146,666],{"class":357},[300,4148,4149,4152,4155],{"class":302,"line":1007},[300,4150,4151],{"class":1308},"            \"",[300,4153,4154],{"class":853},"Investigate this machine's resource state, concurrently. ",[300,4156,885],{"class":1308},[300,4158,4159,4161,4164],{"class":302,"line":1013},[300,4160,4151],{"class":1308},[300,4162,4163],{"class":853},"I need four things: CPU load average, memory utilization, ",[300,4165,885],{"class":1308},[300,4167,4168,4170,4173],{"class":302,"line":1034},[300,4169,4151],{"class":1308},[300,4171,4172],{"class":853},"disk utilization on \u002F, and the three largest directories ",[300,4174,885],{"class":1308},[300,4176,4177,4179,4182],{"class":302,"line":1042},[300,4178,4151],{"class":1308},[300,4180,4181],{"class":853},"under \u002Fvar. Each sub-agent should use bash; each should ",[300,4183,885],{"class":1308},[300,4185,4186,4188,4191],{"class":302,"line":1047},[300,4187,4151],{"class":1308},[300,4189,4190],{"class":853},"ground its answer in a specific bash command's output. ",[300,4192,885],{"class":1308},[300,4194,4195,4197,4200],{"class":302,"line":1079},[300,4196,4151],{"class":1308},[300,4198,4199],{"class":853},"Synthesize the four answers into one paragraph.",[300,4201,885],{"class":1308},[300,4203,4204],{"class":302,"line":1094},[300,4205,4206],{"class":357},"        ),\n",[300,4208,4209],{"class":302,"line":1122},[300,4210,4211],{"class":357},"    )\n",[300,4213,4214],{"class":302,"line":1150},[300,4215,332],{"emptyLinePlaceholder":331},[300,4217,4218],{"class":302,"line":1171},[300,4219,332],{"emptyLinePlaceholder":331},[300,4221,4222,4224,4226,4228,4230,4233],{"class":302,"line":1176},[300,4223,640],{"class":324},[300,4225,623],{"class":357},[300,4227,2258],{"class":593},[300,4229,504],{"class":357},[300,4231,4232],{"class":593},"main",[300,4234,3969],{"class":357},[112,4236,4237,4238,4241],{},"Run it. Four sub-agents start in parallel. Each is constrained to ",[297,4239,4240],{},"bash",", each runs a specific small command, each returns a structured summary with tool-output grounding. Total wall-clock time: roughly the time of the slowest sub-agent, not the sum of all four. Total context in the parent: four compact summaries.",[267,4243],{},[270,4245,4247],{"id":4246},"_177-what-we-havent-solved","17.7 What We Haven't Solved",[112,4249,4250],{},"Two limitations worth naming.",[112,4252,4253,4256,4257,623],{},[124,4254,4255],{},"Lease starvation."," A greedy sub-agent holds a lease for its full 60-second TTL even when it's waiting on a slow tool. Other agents block. Fix: shorter TTLs with automatic renewal when progress is being made. We didn't build it; the interface supports it via ",[297,4258,4259],{},"renew",[112,4261,4262,4265],{},[124,4263,4264],{},"Cross-resource deadlock."," Agent A holds lease on X, waits for lease on Y. Agent B holds lease on Y, waits for lease on X. Classic deadlock. The lease manager has no deadlock detection. For the book's scenarios — small number of concurrent agents, clear resource hierarchies — this doesn't bite. Production systems with richer coordination need either ordered acquisition (always acquire leases in alphabetical order) or a full deadlock detector. That's out of scope here.",[267,4267],{},[270,4269,4271],{"id":4270},"_178-commit","17.8 Commit",[290,4273,4276],{"className":4274,"code":4275,"language":4240,"meta":295,"style":295},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","git add -A && git commit -m \"ch17: lease manager, parallel spawning, grounded verification\"\ngit tag ch17-parallelism\n",[297,4277,4278,4309],{"__ignoreMap":295},[300,4279,4280,4283,4286,4290,4293,4296,4299,4302,4304,4307],{"class":302,"line":303},[300,4281,4282],{"class":428},"git",[300,4284,4285],{"class":853}," add",[300,4287,4289],{"class":4288},"stzsN"," -A",[300,4291,4292],{"class":357}," &&",[300,4294,4295],{"class":428}," git",[300,4297,4298],{"class":853}," commit",[300,4300,4301],{"class":4288}," -m",[300,4303,2941],{"class":1308},[300,4305,4306],{"class":853},"ch17: lease manager, parallel spawning, grounded verification",[300,4308,885],{"class":1308},[300,4310,4311,4313,4316],{"class":302,"line":310},[300,4312,4282],{"class":428},[300,4314,4315],{"class":853}," tag",[300,4317,4318],{"class":853}," ch17-parallelism\n",[270,4320,4322],{"id":4321},"_179-try-it-yourself","17.9 Try It Yourself",[4324,4325,4326,4340,4346],"ol",{},[4327,4328,4329,4332,4333,4336,4337,4339],"li",{},[124,4330,4331],{},"Cause a conflict."," Write two sub-agents that both try to write to ",[297,4334,4335],{},"\u002Fworkspace\u002Fshared.txt",". Run them with ",[297,4338,3318],{},". Observe the lease contention. Does the losing sub-agent retry? If it doesn't, what would you add?",[4327,4341,4342,4345],{},[124,4343,4344],{},"Induce a hallucinated consensus."," Run two sub-agents, one with instructions to invent a plausible fact, the other with instructions to verify claims. See what happens without the external-grounding requirement. Re-run with the grounding requirement enforced. Does the second sub-agent catch the invention?",[4327,4347,4348,4351,4352,4354],{},[124,4349,4350],{},"Time the parallelism."," Measure wall-clock for the parallel scenario above vs. a sequential rewrite that uses four sequential ",[297,4353,3314],{}," calls. How much faster is parallel? Is it worth the coordination complexity?",[267,4356],{},[4358,4359,4360,4363],"what-you-understand",{},[112,4361,4362],{},"Sub-agents can run in parallel. Shared resources are gated by leases that prevent write conflicts. Parallel spawning is a distinct tool from sequential, with guardrails that force non-overlapping objectives. Verification is grounded in external tool output, not peer output — a discipline in the system prompt and a tool that makes the discipline visible. The harness now supports the full Anthropic-style multi-agent pattern: orchestrator over parallel specialists with independent contexts.",[112,4364,4365],{},"What's still missing: nothing user-facing, and that's the problem. The harness does a lot; we have very little visibility into what it's doing. A failed run tells you the final error but not which sub-agent burned tokens, which tool took 12 seconds, which compaction dropped the file the final agent wanted. Chapter 18 makes the harness observable end-to-end via OpenTelemetry GenAI semantic conventions.",[4367,4368,4369],"style",{},"html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .stp6e, html code.shiki .stp6e{--shiki-light:#39ADB5;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s2W-s, html code.shiki .s2W-s{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#032F62;--shiki-default-font-style:inherit;--shiki-dark:#9ECBFF;--shiki-dark-font-style:inherit}html pre.shiki code .sithA, html code.shiki .sithA{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#032F62;--shiki-default-font-style:inherit;--shiki-dark:#9ECBFF;--shiki-dark-font-style:inherit}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .slqww, html code.shiki .slqww{--shiki-light:#6182B8;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smCYv, html code.shiki .smCYv{--shiki-light:#E53935;--shiki-light-font-style:italic;--shiki-default:#24292E;--shiki-default-font-style:inherit;--shiki-dark:#E1E4E8;--shiki-dark-font-style:inherit}html pre.shiki code .sFwrP, html code.shiki .sFwrP{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#24292E;--shiki-default-font-style:inherit;--shiki-dark:#E1E4E8;--shiki-dark-font-style:inherit}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":295,"searchDepth":310,"depth":310,"links":4371},[4372,4373,4374,4375,4376,4377,4378,4379,4380],{"id":272,"depth":310,"text":273},{"id":287,"depth":310,"text":288},{"id":1617,"depth":310,"text":1618},{"id":2338,"depth":310,"text":2339},{"id":3324,"depth":310,"text":3325},{"id":3573,"depth":310,"text":3574},{"id":4246,"depth":310,"text":4247},{"id":4270,"depth":310,"text":4271},{"id":4321,"depth":310,"text":4322},"md",{},null,{"title":78,"description":117},"J8acDgTzU9sOsPYcbj754RSc90AVHZHPBBNb-Tuvb5c",[4387,4389],{"title":74,"path":75,"stem":76,"description":4388,"children":-1},"Previously: sub-agents with bounded delegation. A coordinator can now split work across sub-agents and synthesize. What it cannot yet do — and what single-agent runs also cannot — is verify that what it claims to have done, it actually did.",{"title":82,"path":83,"stem":84,"description":4390,"children":-1},"Previously: parallel sub-agents, leases, grounded verification. The harness is capable but opaque. A failed run tells you the final error but nothing about which sub-agent burned tokens, which tool call took 12 seconds, which compaction event dropped what the final agent wanted.",1776848986824]