grooph 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -31
- package/THIRD-PARTY-NOTICES.md +5 -1
- package/dist/app/assets/App-CT9KxzfQ.css +1 -0
- package/dist/app/assets/App-D1pDFbEY.js +5 -0
- package/dist/app/assets/EmbedApp-CTausQWY.js +1 -0
- package/dist/app/assets/ExportPanel-CbRH4C69.js +8 -0
- package/dist/app/assets/Import-DF0XLxRI.js +1 -0
- package/dist/app/assets/SketchView-BjeUHG4X.js +143 -0
- package/dist/app/assets/brakes-B9b3hDX8.js +1 -0
- package/dist/app/assets/{builtins-Cbs5iQtH.js → builtins-DzyPaYdd.js} +1 -1
- package/dist/app/assets/{compile-BAvi-rd0.js → compile-DL2budwP.js} +8 -8
- package/dist/app/assets/{front-BRl7rl-u.js → front-OxfL-OaD.js} +1 -1
- package/dist/app/assets/full-C0asYORc.js +1 -0
- package/dist/app/assets/graph-more-Dn_hNa20.js +2 -0
- package/dist/app/assets/graph-stage-CwNePnN4.js +4 -0
- package/dist/app/assets/graph-views-CmkQ41zC.js +2 -0
- package/dist/app/assets/{index-BoSi6XYh.js → index-B5ZkSF4L.js} +2 -2
- package/dist/app/assets/mermaid-read-cyqJ32v-.js +17 -0
- package/dist/app/assets/{plan-templates-BQu8G2sX.js → plan-templates-u2fF3GnS.js} +1 -1
- package/dist/app/assets/runs-BA5HKtsx.js +1 -0
- package/dist/app/assets/screens-CMMuexid.js +16 -0
- package/dist/app/assets/share-DV3wsAsJ.js +8 -0
- package/dist/app/assets/site-looks.v1.css +223 -0
- package/dist/app/assets/space-Cuy4YqyZ.js +1 -0
- package/dist/app/assets/styles-Dlp-yYlE.css +1 -0
- package/dist/app/assets/templates-DbAhWvP6.js +2 -0
- package/dist/app/assets/{themes-DtOBrLk0.js → themes-C34TrvOJ.js} +1 -1
- package/dist/app/assets/{units-DRaa5U2m.js → units-DlniUjfH.js} +1 -1
- package/dist/app/assets/{views-DCEDX6Qx.js → views-CFqRShXD.js} +2 -1
- package/dist/app/grooph-app.json +1 -1
- package/dist/app/index.html +14 -11
- package/dist/app/llms.txt +6 -7
- package/dist/app/manifest.webmanifest +1 -1
- package/dist/app/og.png +0 -0
- package/dist/app/routes.json +42 -33
- package/dist/app/sw.js +7 -2
- package/dist/browser/grooph.d.ts +44 -0
- package/dist/browser/grooph.global.js +7 -0
- package/dist/browser/grooph.js +7 -0
- package/dist/bundle/grooph.js +7082 -2606
- package/hooks/grooph-event.mjs +163 -14
- package/package.json +14 -6
- package/dist/app/assets/App-CFO6bPna.js +0 -5
- package/dist/app/assets/App-Cv1auMeh.css +0 -1
- package/dist/app/assets/EmbedApp-B5LWao3x.js +0 -1
- package/dist/app/assets/ExportPanel-VWus7GM3.js +0 -8
- package/dist/app/assets/Import-YEKofx4_.js +0 -1
- package/dist/app/assets/brakes-9E9PsV_Z.js +0 -1
- package/dist/app/assets/graph-more-BKW9C4-h.js +0 -2
- package/dist/app/assets/graph-stage-CWh3OWga.js +0 -4
- package/dist/app/assets/graph-views-tFUJK-be.js +0 -2
- package/dist/app/assets/runs-DcjRi7S6.js +0 -1
- package/dist/app/assets/screens-YUd2HaLm.js +0 -16
- package/dist/app/assets/share-BzIzB4EY.js +0 -8
- package/dist/app/assets/space-DWgrlnfB.js +0 -1
- package/dist/app/assets/styles-BA1aVO9R.css +0 -1
- package/dist/app/assets/templates-DvzLa5sE.js +0 -2
- package/dist/app/demo/run.txt +0 -1
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import{$t as e,
|
|
1
|
+
import{$t as e,Jt as t,Qt as n,Xt as r,Yt as i,Zt as a,_n as o,an as s,cn as c,dn as l,en as u,fn as d,hn as ee,in as f,ln as p,nn as m,ot as te,pn as ne,qt as h,rn as g,rt as _,sn as v,vn as y,yn as re}from"./share-DV3wsAsJ.js";import{n as b,t as ie}from"./groups-CjEPiwfv.js";var x={"claude-code":{harness:`claude-code`,title:`Claude Code`,verifiedAgainst:`2.1.268`,verifiedOn:`2026-09-17`,models:{frontier:`opus`,strong:`sonnet`,fast:`sonnet`},defaultTier:`strong`,effort:{low:`low`,medium:`medium`,high:`high`,max:`max`},capabilityTools:{"read-files":[`Read`,`Glob`,`Grep`],"edit-files":[`Read`,`Edit`,`Write`,`Glob`,`Grep`],"write-outputs":[`Write`],"run-commands":[`Bash`],"run-tests":[`Bash`],web:[`WebFetch`,`WebSearch`],"spawn-agents":[`Agent`]},defaultCapabilities:[`read-files`],toolOrder:[`Read`,`Edit`,`Write`,`Glob`,`Grep`,`Bash`,`WebFetch`,`WebSearch`,`Agent`],advisoryBudgetMeasures:[`usd`,`turns`,`tokens`],runIdFormat:`<yyyymmdd-hhmmss>`},codex:{harness:`codex`,title:`Codex`,verifiedAgainst:`0.160.0`,verifiedOn:`2026-10-04`,models:{frontier:`gpt-6.1-sol`,strong:`gpt-6-luna`,fast:`gpt-6-luna`},defaultTier:`strong`,effort:{low:`low`,medium:`medium`,high:`high`,max:`max`},capabilityTools:{"read-files":[],"edit-files":[],"write-outputs":[],"run-commands":[],"run-tests":[],web:[],"spawn-agents":[]},defaultCapabilities:[`read-files`],toolOrder:[],advisoryBudgetMeasures:[`usd`,`turns`,`tokens`],runIdFormat:`<yyyymmdd-hhmmss>`}};function S(e){let t=Object.prototype.hasOwnProperty.call(x,e)?x[e]:void 0;if(!t)throw Error(`no compile profile for target harness "${e}"; known targets: ${c.join(`, `)}`);return t}var C=(...e)=>e.filter(e=>typeof e==`string`).join(`
|
|
2
2
|
`),w=(...e)=>`${e.filter(e=>typeof e==`string`&&e!==``).join(`
|
|
3
3
|
|
|
4
4
|
`)}\n`,T=e=>`- ${e}`,E=(e,t=``)=>"```"+t+`
|
|
5
|
-
`+e+"\n```",D=(e,t)=>C(`| ${e.join(` | `)} |`,`|${e.map(()=>`---`).join(`|`)}|`,...t.map(e=>`| ${e.join(` | `)} |`)),O=e=>`\`${e.replace(/\|/g,`\\|`)}\``,k=e=>e.replace(/\|/g,`\\|`).replace(/\n/g,` `);function A(e){let t=e.trim(),n=/^.*?[.!?](?=\s|$)/s.exec(t);return(n?n[0]:t).replace(/\s+/g,` `).trim()}var j=/[\s\p{Cc}\p{Cs}\u2028\u2029\ufffe\uffff]+/gu,M=e=>{let t=e.replace(j,` `).trim();return/^[A-Za-z0-9][A-Za-z0-9 ,.'·/()-]*$/.test(t)?t:JSON.stringify(t)};function ae(e,t){return w(se(e,t),...ce(e,t))}var oe=/^[A-Za-z0-9][A-Za-z0-9._:/[\]-]*$/;function N(e){return e.every(e=>oe.test(e))?e.join(`, `):JSON.stringify(e.map(e=>e.replace(j,` `).trim()).join(`, `))}function se(e,t){let n=`${t.role} for graph ${e.graphId}. ${A(t.node.brief)}`;return C(`---`,`name: ${N([t.agentName])}`,`description: ${M(n)}`,...t.model?[`model: ${N([t.model])}`]:[],...t.effort?[`effort: ${N([t.effort])}`]:[],...t.tools.length>0?[`tools: ${N(t.tools)}`]:[],...t.disallowedTools.length>0?[`disallowedTools: ${N(t.disallowedTools)}`]:[],...(t.node.skills??[]).length>0?[`skills: ${N(t.node.skills)}`]:[],`---`)}function ce(e,t){let n=t.node,r=e.index.incoming.get(n.id)??[],i=e.index.outgoing.get(n.id)??[],a=[...new Set(r.flatMap(e=>e.evidence??[]))];return[C(`# ${n.name}`,``,`Node ${O(n.id)} in the grooph graph ${O(e.graphId)} (${e.doc.name}). The lead dispatches you and you report back to the lead — you do not dispatch anyone else, and you do not update the run's progress log.`),C(`## Brief`,``,n.brief),...n.description?[C(`## Context`,``,n.description)]:[],C(`## Inputs`,``,...n.inputs&&n.inputs.length>0?n.inputs.map(T):[T(`None declared: work from the prompt the lead gives you.`)]),C(`## Outputs`,``,`Leave all of these behind before you report:`,``,...n.outputs.map(T),...P(e,t)?[``,`Write them yourself. With ${O(`write-outputs`)} you may create or overwrite only the files you declare in these outputs, and no other file.`]:[]),le(t),ue(e,t,a),de(e,t),fe(t,i)]}function P(e,t){let n=t.node.allow??e.profile.defaultCapabilities;return n.includes(`write-outputs`)&&!n.includes(`edit-files`)}function le(e){let t=e.node.owns??[];return C(`## Ownership`,``,...t.length>0?[`You are the only node in this run that writes ${t.map(O).join(`, `)}. Nothing else touches them while you work; if you need a change elsewhere, say so in your report instead of making it.`]:[`You own no artifact in this graph. Do not write over another node's files: report what should change and let the lead route it.`])}function ue(t,n,r){let i=n.node.inputs&&n.node.inputs.length>0?`your declared inputs (Inputs above)`:`your declared inputs`,a=n.isWriter?`, which for you includes the project you are changing`:``,o=e(t.index,n.node.id).some(e=>(e.stops??[]).some(e=>e.kind===`evidence-invalid`));return C(`## Evidence rules`,``,...r.length>0?[`You may inspect what the lead hands you — the evidence below — plus ${i}${a}, and nothing else:`,``,...r.map(T),``,n.isCritic?`If any of it is missing or unreadable, do not guess and do not substitute your own reading of the repository: report ${O(`invalid-evidence`)} and say which item you could not read.${o?` That round counts toward the loop's evidence stop.`:``}`:`If any of it is missing or unreadable, say so in your report rather than guessing.`]:[`No inbound edge lists evidence for you. Work from ${i}${n.isWriter?`, the project you are changing`:``} and the lead's prompt, and nothing else; say so in your report if something you need is missing.`],...n.isCritic?[``,`You judge; you do not fix. ${n.disallowedTools.length>0?`Editing tools are withheld from you on purpose (${n.disallowedTools.join(`, `)}).`:`Report findings instead of editing.`}${P(t,n)?` Your own outputs are the only files you write.`:``} Cite a file and a line for every claim you make: an adjective is not a finding.`]:[])}function de(e,t){let n=t.node.allow??e.profile.defaultCapabilities,r=t.node.deny??[];return C(`## Capabilities`,``,T(`Allowed: ${n.map(O).join(`, `)} → tools ${t.tools.join(`, `)||`(none)`}`),...r.length>0?[T(`Denied: ${r.map(O).join(`, `)} → withheld tools ${t.disallowedTools.join(`, `)||`(none)`}`)]:[],...t.unmappedAllow.length>0||t.unmappedDeny.length>0?[``,`Capabilities with no tool in this harness, stated here so a human can wire them by hand: ${[...t.unmappedAllow.map(e=>`allow ${e}`),...t.unmappedDeny.map(e=>`deny ${e}`)].join(`, `)}.`]:[],...t.modelSource===`unset`?[``,`The graph pins no model or effort for this node, so you run at the session's defaults. Set ${O(`model`)} in the graph document if that matters.`]:[])}function fe(e,t){let n=[];for(let e of t){let t=c(e),r=typeof t==`string`?t===`always`?`done`:t:t.verdict;n.includes(r)||n.push(r)}return n.length===0&&n.push(`done`),e.isCritic&&!n.includes(`invalid-evidence`)&&n.push(`invalid-evidence`),C(`## Report format`,``,`End your reply with exactly this block, and keep it short — the lead routes on the verdict line:`,``,E(C(`verdict: ${n.join(` | `)}`,`outputs:`,...e.node.outputs.map(e=>` - ${e} — <where you left it>`),...e.isCritic?[`findings:`,` - <file>:<line> — <what is wrong, or the item it satisfies>`]:[`changes:`,` - <what you changed this round, and why>`],`summary: <at most five lines>`),`text`),``,...n.includes(`pass`)||n.includes(`fail`)?[`The lead takes the edge that matches your verdict, so do not hedge: ${O(`pass`)} means every acceptance condition is met, ${O(`fail`)} means at least one is not.`]:[`The lead continues on ${O(n[0])} whatever the content of your report.`])}function pe(e,t={}){let n=g(e),r=S(`claude-code`),a=Object.entries(t.models??{}).filter(([,e])=>typeof e==`string`&&e!==``),o=a.length>0?{...r,models:{...r.models,...Object.fromEntries(a)}}:r,s=e.id,c=`.grooph/${s}`,l=(e.nodes??[]).find(e=>e.kind===`agent`&&e.role===`lead`),u=(e.nodes??[]).filter(e=>e.kind===`agent`).filter(e=>e.id!==l?.id).map(e=>me(e,o,s));return{doc:e,index:n,profile:o,graphId:s,root:c,paths:{graph:`${c}/graph.grooph.json`,lead:`${c}/LEAD.md`,mapping:`${c}/MAPPING.md`,kickoff:`${c}/KICKOFF.md`,skill:`.claude/skills/${s}/SKILL.md`,runs:`${c}/runs`,progress:`${c}/runs/<run-id>/PROGRESS.md`,notes:`${c}/runs/<run-id>/notes.jsonl`,workingCopy:`${c}/runs/<run-id>/graph.grooph.json`},adaptation:i(e),tiersNamed:a.map(([e])=>e),agents:u,agentByNode:new Map(u.map(e=>[e.node.id,e])),...l?{leadNode:l}:{}}}function me(e,t,n){let r=`${n}--${e.id}`,i=e.model?.pin?.[t.harness],a=i??(e.model?t.models[e.model.tier]:void 0),o=i?`pin`:e.model?`tier`:`unset`,s=e.allow??t.defaultCapabilities,c=F(s,t),d=F(e.deny??[],t).filter(e=>!c.includes(e));return{node:e,agentName:r,file:`.claude/agents/${r}.md`,role:f(e),isCritic:l(e),isWriter:u(e),...a?{model:a}:{},modelSource:o,...e.effort?{effort:t.effort[e.effort]}:{},tools:c,disallowedTools:d,unmappedAllow:I(s,t),unmappedDeny:I(e.deny??[],t)}}function F(e,t){let n=new Set;for(let r of e)for(let e of Object.hasOwn(t.capabilityTools,r)?t.capabilityTools[r]:[])n.add(e);return t.toolOrder.filter(e=>n.has(e))}var I=(e,t)=>e.filter(e=>!Object.hasOwn(t.capabilityTools,e));function L(e){switch(e.adaptation){case`adaptive`:return`- When the work shows the graph is wrong, amend the working copy as ${O(`LEAD.md`)} § "Adapting the graph" says: visibly, with a note, and never loosening a brake. Never write the source document.`;case`propose`:return`- When the graph looks wrong, record a proposal note (${O(`LEAD.md`)} § "Adapting the graph") and change nothing.`;case`fixed`:return`- Follow the graph exactly; when you cannot, halt and ask (${O(`LEAD.md`)} § "Adapting the graph").`}}function R(e){let t=_(e.index),n=e.agents.map(e=>O(e.agentName)).join(`, `);return w(C(`Run the grooph graph ${O(e.graphId)} (${e.doc.name}) in this project. You are the lead.`,``,`Read ${O(e.paths.lead)} first and follow it${e.adaptation===`fixed`?` exactly`:``}. It is the brief for this run; this prompt is only the trigger.`),C(`**Goal.**`,``,e.doc.goal??`(the graph states no goal)`),C(`**Before you touch anything:**`,``,`1. Read a run id from the clock (${O(`date -u +%Y%m%d-%H%M%S`)}, the form ${O(e.profile.runIdFormat)}), create ${O(`${e.paths.runs}/<run-id>/`)}, and copy ${O(e.paths.graph)} into it: that copy is the run's working copy.`,`2. Write ${O(`PROGRESS.md`)} and start ${O(`notes.jsonl`)} there, as ${O(`LEAD.md`)} § "Run setup" and § "Progress and notes" describe. Append a note for every node run and every loop round — that file is the record of the run.`,`3. Start at ${t.map(O).join(`, `)||`the first node`}.`),C(`**While you run:**`,``,`- Dispatch each agent node as its own subagent with the ${O(`Agent`)} tool: ${n||`(this graph has no agent nodes)`}. Do not do their work yourself, and do not grade work a critic node is there to grade.`,`- Give a fresh worker only its task, its declared inputs and the evidence its edge lists. Never paste a transcript into one.`,`- Run commands bare, from the project root, and tell each worker to do the same: under a narrow allowlist a compound form (${O(`cd … && …`)}) or ${O(`git -C <path>`)} is refused, and every refusal costs a turn.`,`- Evaluate the loop stops before every round, in the order ${O(`LEAD.md`)} lists them, and record the round.`,`- At a human gate: append the halt note first, then ask, then end your turn (${O(`LEAD.md`)} § "Human gates"). A run nobody answers ends there, and the same run id resumes it.`,L(e)),`**When the run ends** — a stop fires, you reach a stop node, or no edge is left to take — append the final note, write the last ${O(`PROGRESS.md`)}, and reply with the run id, the number of rounds, which stop ended the run, and anything left over.`)}function z(e){let t=b(e).filter(e=>e.from!==void 0);if(t.length===0)return!1;let n=e=>O(e),r=new Map((e.groups??[]).map(e=>[e.id,e.from??``])),i=t.map(t=>[k(t.name),O(t.id),re.test(r.get(t.id))?O(r.get(t.id)):`a template`,ie(e,t.id).nodes.map(n).join(`, `),t.entries.length>0?[...new Set(t.entries.map(e=>n(e.to)))].join(`, `):`nothing leads in`,t.exits.length>0?[...new Set(t.exits.map(e=>n(e.to)))].join(`, `):`its own stop`]);return C(``,`### Units`,``,`Some of these nodes were placed together, each set from one template: a **subgrooph**. A person reading the graph sees each as one box under its name. Its nodes are ordinary nodes: run them as you run any others, by the edges, loops and stops below. When ${O(`PROGRESS.md`)} or a note says where the run is, name the unit as well as the node.`,``,D([`unit`,`id`,`placed from`,`its nodes`,`entered at`,`leads on to`],i))}function he(e,t){return w(ge(e),_e(e),ve(e),be(e),Se(e),Ee(e),ke(e),Fe(e),Ie(e),Ve(e),He(t),Ue(e))}var ge=e=>C(`# Lead brief · ${e.doc.name}`,``,`Graph ${O(e.graphId)} v${e.doc.version} · target ${O(`claude-code`)} · compiled by grooph from ${O(e.paths.graph)}.`,``,`This file is generated. Edit the graph document and export again, or hand-edit and record what you changed — the next export overwrites it.`);function _e(e){let t=e.leadNode?.brief.trim();return C(`## 1. You are the lead`,``,t??C(`You run this graph. The main session is the executive: you dispatch nodes, follow edges, count rounds, evaluate stops, keep the progress log, and stop for the human when the graph says to.`,``,"You do not do the workers' jobs. Every agent node below runs as its own subagent through the `Agent` tool, and sees only its brief, its declared inputs and the evidence its inbound edge allows. You never grade your own work while a critic node exists: the critic's verdict is the one that counts, and you do not overrule it by re-reading the diff yourself."))}function ve(e){let t=e.doc.constraints??{},n=[];return t.budget&&n.push(T(`**Budget.** ${t.budget}`)),t.time&&n.push(T(`**Time.** ${t.time}`)),t.other&&n.push(T(`**Other.** ${t.other}`)),C(`## 2. Goal and constraints`,``,`**Goal.**`,``,e.doc.goal??`(none stated)`,...n.length>0?[``,`**Constraints.**`,``,...n]:[],...e.doc.description?[``,`**What this graph does.**`,``,e.doc.description]:[])}var B=`20260917-093002`,ye=`2026-09-17T09:30:02Z`;function be(e){let t=JSON.stringify({id:`n-0001`,run:B,at:`graph`,started:ye,text:`run started`}),n=V(e);return C(`## 3. Run setup`,``,`1. Read the run id from the clock, in the form ${O(e.profile.runIdFormat)} (UTC): ${O(`date -u +%Y%m%d-%H%M%S`)}, for example ${O(B)}. If ${O(`${e.paths.runs}/<that id>/`)} already exists, append ${O(`-2`)}, then ${O(`-3`)}, and so on. Never make an id up.`,`2. Create ${O(`${e.paths.runs}/<run-id>/`)}.`,`3. Copy the source document ${O(e.paths.graph)} into it as ${O(e.paths.workingCopy)}. That copy is the run's working copy: the graph this run follows, ${e.adaptation===`adaptive`?`and the only copy you may amend (§9)`:`left as copied (§9)`}. Never write the source document.`,`4. Write ${O(`PROGRESS.md`)} there before dispatching anything: the run id, the goal, every node with status ${O(`pending`)}, and the round counter at 0${n.length>0?` (with the dispatch counter of ${n.map(e=>O(e.id)).join(`, `)} at 0, §6)`:``}.`,`5. Create ${O(`notes.jsonl`)} beside it and append the first line, its ${O(`started`)} read from ${O(`date -u +%Y-%m-%dT%H:%M:%SZ`)}:`,``,E(t,`json`),``,`6. If you were given a run id to resume, do not start a second run: read that folder's ${O(`PROGRESS.md`)} and working copy, continue from the last recorded position, and keep appending to the same ${O(`notes.jsonl`)}. Do not copy the source over the working copy again.`,``,`Entry nodes (start here): ${_(e.index).map(O).join(`, `)||`none — the graph has no entry node`}.`)}var V=e=>(e.doc.loops??[]).filter(e=>(e.stops??[]).some(e=>e.kind===`budget`&&e.measure===`dispatches`)),xe=e=>(e.doc.loops??[]).filter(e=>(e.stops??[]).some(e=>e.kind===`evidence-invalid`));function Se(e){return C(`## 4. Nodes`,``,D([`node`,`name`,`how you run it`,`role`,`what it returns`],(e.doc.nodes??[]).map(t=>[O(t.id),k(t.name),Ce(e,t),k(we(t)),k(Te(t))])),``,`Dispatch an agent node with the ${O(`Agent`)} tool and the ${O(`subagent_type`)} named above; its file under ${O(`.claude/agents/`)} carries the full brief, so your prompt carries only the task, the declared inputs and the edge's evidence. Never paste a transcript into a fresh worker.`,z(e.doc))}function Ce(e,t){let n=e.agentByNode.get(t.id);if(n)return`${O(`Agent`)} · ${O(n.agentName)}`;switch(t.kind){case`agent`:return`you — this is the lead node`;case`human-gate`:return`you ask the human`;case`check`:return t.check.run?`you run ${O(t.check.run)}`:`you judge the stated condition`;case`merge`:return`you merge the listed artifacts`;case`stop`:return`you end the run`;default:return`you`}}var we=e=>e.kind===`agent`?typeof e.role==`string`?e.role:`custom: ${e.role.custom}`:e.kind;function Te(e){switch(e.kind){case`agent`:return e.outputs.join(`; `);case`human-gate`:return e.options&&e.options.length>0?e.options.join(` | `):`a human answer`;case`check`:return`pass when: ${e.check.pass}`;case`merge`:return`merged: ${e.merges.join(`, `)}`;case`stop`:return`run ends with outcome ${e.outcome??`success`}`;default:return``}}function Ee(e){let n=e.doc.edges??[],r=xe(e),i=n.some(e=>(e.evidence??[]).some(e=>/\bdiff\b/i.test(e)));return C(`## 5. Edges`,``,D([`edge`,`route`,`taken when`,`isolation`,`evidence and gates`],n.map(e=>[O(e.id),`${O(e.from)} → ${O(e.to)}`,k(t(e)),s(e),k(Oe(e))])),``,T(`When a node finishes, take every outgoing edge whose condition matches its result. Several matching edges run in parallel, capped by any ${O(`concurrency`)} on the edge.`),T(`${O(`fresh`)} isolation: the worker starts with no context except its brief, its declared inputs and the evidence listed above. ${O(`shared`)}: continue the same worker if the build lets you, otherwise do that step yourself rather than faking a continuation.`),T(`A worker may inspect what its inbound edge lists plus its own declared inputs; for a writer that includes the project it is changing. A critic that cannot read its evidence reports ${O(`invalid-evidence`)} instead of guessing.`),T(`When an edge routes ${O(`invalid-evidence`)}, take it. Otherwise repair the evidence and dispatch the same node once more in the same round; a second ${O(`invalid-evidence`)} routes as ${O(`fail`)}.${r.length>0?` Such rounds count toward the ${O(`evidence-invalid`)} stop of loop ${r.map(e=>O(e.id)).join(`, `)}.`:``}`),...i?[T(De)]:[])}var De=`A diff of the change is ${O(`git diff`)} plus, for each file the change added, ${O(`git diff --no-index /dev/null <file>`)} (${O(`git diff`)} omits untracked files; ${O(`--no-index`)} exits 1 whenever the two differ, which is not an error). Run each bare from the project root, one command at a time; no brace group, no ${O(`cd`)}. ${O(`git add -N <file>`)} also works where it is allowed, and stages nothing.`;function Oe(e){let t=[];return e.evidence&&e.evidence.length>0?t.push(`evidence: ${e.evidence.join(`; `)}`):t.push(`evidence: none listed`),e.approval&&t.push(`**human approval required before traversal**`),e.concurrency&&t.push(`concurrency max ${e.concurrency.max}`),e.retry&&t.push(`retry max ${e.retry.max}`),e.label&&t.push(`label: ${e.label}`),t.join(` · `)}function ke(e){let t=e.doc.loops??[];return t.length===0?C(`## 6. Loops`,``,`This graph has no loops. Follow the edges once and stop.`):C(`## 6. Loops`,``,t.map(t=>Ae(e,t).replace(/\n+$/,``)).join(`
|
|
5
|
+
`+e+"\n```",D=(e,t)=>C(`| ${e.join(` | `)} |`,`|${e.map(()=>`---`).join(`|`)}|`,...t.map(e=>`| ${e.join(` | `)} |`)),O=e=>`\`${e.replace(/\|/g,`\\|`)}\``,k=e=>e.replace(/\|/g,`\\|`).replace(/\n/g,` `);function A(e){let t=e.trim(),n=/^.*?[.!?](?=\s|$)/s.exec(t);return(n?n[0]:t).replace(/\s+/g,` `).trim()}var j=/[\s\p{Cc}\p{Cs}\u2028\u2029\ufffe\uffff]+/gu,M=e=>{let t=e.replace(j,` `).trim();return/^[A-Za-z0-9][A-Za-z0-9 ,.'·/()-]*$/.test(t)?t:JSON.stringify(t)};function ae(e,t){return w(se(e,t),...ce(e,t))}var oe=/^[A-Za-z0-9][A-Za-z0-9._:/[\]-]*$/;function N(e){return e.every(e=>oe.test(e))?e.join(`, `):JSON.stringify(e.map(e=>e.replace(j,` `).trim()).join(`, `))}function se(e,t){let n=`${t.role} for graph ${e.graphId}. ${A(t.node.brief)}`;return C(`---`,`name: ${N([t.agentName])}`,`description: ${M(n)}`,...t.model?[`model: ${N([t.model])}`]:[],...t.effort?[`effort: ${N([t.effort])}`]:[],...t.tools.length>0?[`tools: ${N(t.tools)}`]:[],...t.disallowedTools.length>0?[`disallowedTools: ${N(t.disallowedTools)}`]:[],...(t.node.skills??[]).length>0?[`skills: ${N(t.node.skills)}`]:[],`---`)}function ce(e,t){let n=t.node,r=e.index.incoming.get(n.id)??[],i=e.index.outgoing.get(n.id)??[],a=[...new Set(r.flatMap(e=>e.evidence??[]))];return[C(`# ${n.name}`,``,`Node ${O(n.id)} in the grooph graph ${O(e.graphId)} (${e.doc.name}). The lead dispatches you and you report back to the lead — you do not dispatch anyone else, and you do not update the run's progress log.`),C(`## Brief`,``,n.brief),...n.description?[C(`## Context`,``,n.description)]:[],C(`## Inputs`,``,...n.inputs&&n.inputs.length>0?n.inputs.map(T):[T(`None declared: work from the prompt the lead gives you.`)]),C(`## Outputs`,``,`Leave all of these behind before you report:`,``,...n.outputs.map(T),...P(e,t)?[``,`Write them yourself. With ${O(`write-outputs`)} you may create or overwrite only the files you declare in these outputs, and no other file.`]:[]),le(t),ue(e,t,a),de(e,t),fe(t,i)]}function P(e,t){let n=t.node.allow??e.profile.defaultCapabilities;return n.includes(`write-outputs`)&&!n.includes(`edit-files`)}function le(e){let t=e.node.owns??[];return C(`## Ownership`,``,...t.length>0?[`You are the only node in this run that writes ${t.map(O).join(`, `)}. Nothing else touches them while you work; if you need a change elsewhere, say so in your report instead of making it.`]:[`You own no artifact in this graph. Do not write over another node's files: report what should change and let the lead route it.`])}function ue(e,t,n){let r=t.node.inputs&&t.node.inputs.length>0?`your declared inputs (Inputs above)`:`your declared inputs`,i=t.isWriter?`, which for you includes the project you are changing`:``,a=f(e.index,t.node.id).some(e=>(e.stops??[]).some(e=>e.kind===`evidence-invalid`));return C(`## Evidence rules`,``,...n.length>0?[`You may inspect what the lead hands you — the evidence below — plus ${r}${i}, and nothing else:`,``,...n.map(T),``,t.isCritic?`If any of it is missing or unreadable, do not guess and do not substitute your own reading of the repository: report ${O(`invalid-evidence`)} and say which item you could not read.${a?` That round counts toward the loop's evidence stop.`:``}`:`If any of it is missing or unreadable, say so in your report rather than guessing.`]:[`No inbound edge lists evidence for you. Work from ${r}${t.isWriter?`, the project you are changing`:``} and the lead's prompt, and nothing else; say so in your report if something you need is missing.`],...t.isCritic?[``,`You judge; you do not fix. ${t.disallowedTools.length>0?`Editing tools are withheld from you on purpose (${t.disallowedTools.join(`, `)}).`:`Report findings instead of editing.`}${P(e,t)?` Your own outputs are the only files you write.`:``} Cite a file and a line for every claim you make: an adjective is not a finding.`]:[])}function de(e,t){let n=t.node.allow??e.profile.defaultCapabilities,r=t.node.deny??[];return C(`## Capabilities`,``,T(`Allowed: ${n.map(O).join(`, `)} → tools ${t.tools.join(`, `)||`(none)`}`),...r.length>0?[T(`Denied: ${r.map(O).join(`, `)} → withheld tools ${t.disallowedTools.join(`, `)||`(none)`}`)]:[],...t.unmappedAllow.length>0||t.unmappedDeny.length>0?[``,`Capabilities with no tool in this harness, stated here so a human can wire them by hand: ${[...t.unmappedAllow.map(e=>`allow ${e}`),...t.unmappedDeny.map(e=>`deny ${e}`)].join(`, `)}.`]:[],...t.modelSource===`unset`?[``,`The graph pins no model or effort for this node, so you run at the session's defaults. Set ${O(`model`)} in the graph document if that matters.`]:[])}function fe(e,t){let n=[];for(let e of t){let t=i(e),r=typeof t==`string`?t===`always`?`done`:t:t.verdict;n.includes(r)||n.push(r)}return n.length===0&&n.push(`done`),e.isCritic&&!n.includes(`invalid-evidence`)&&n.push(`invalid-evidence`),C(`## Report format`,``,`End your reply with exactly this block, and keep it short — the lead routes on the verdict line:`,``,E(C(`verdict: ${n.join(` | `)}`,`outputs:`,...e.node.outputs.map(e=>` - ${e} — <where you left it>`),...e.isCritic?[`findings:`,` - <file>:<line> — <what is wrong, or the item it satisfies>`]:[`changes:`,` - <what you changed this round, and why>`],`summary: <at most five lines>`),`text`),``,...n.includes(`pass`)||n.includes(`fail`)?[`The lead takes the edge that matches your verdict, so do not hedge: ${O(`pass`)} means every acceptance condition is met, ${O(`fail`)} means at least one is not.`]:[`The lead continues on ${O(n[0])} whatever the content of your report.`])}function pe(e,t={}){let n=l(e),r=S(`claude-code`),i=Object.entries(t.models??{}).filter(([,e])=>typeof e==`string`&&e!==``),o=i.length>0?{...r,models:{...r.models,...Object.fromEntries(i)}}:r,s=e.id,c=`.grooph/${s}`,u=(e.nodes??[]).find(e=>e.kind===`agent`&&e.role===`lead`),d=(e.nodes??[]).filter(e=>e.kind===`agent`).filter(e=>e.id!==u?.id).map(e=>me(e,o,s));return{doc:e,index:n,profile:o,graphId:s,root:c,paths:{graph:`${c}/graph.grooph.json`,lead:`${c}/LEAD.md`,mapping:`${c}/MAPPING.md`,kickoff:`${c}/KICKOFF.md`,skill:`.claude/skills/${s}/SKILL.md`,runs:`${c}/runs`,progress:`${c}/runs/<run-id>/PROGRESS.md`,notes:`${c}/runs/<run-id>/notes.jsonl`,workingCopy:`${c}/runs/<run-id>/graph.grooph.json`},adaptation:a(e),tiersNamed:i.map(([e])=>e),agents:d,agentByNode:new Map(d.map(e=>[e.node.id,e])),...u?{leadNode:u}:{}}}function me(e,t,n){let r=`${n}--${e.id}`,i=e.model?.pin?.[t.harness],a=i??(e.model?t.models[e.model.tier]:void 0),o=i?`pin`:e.model?`tier`:`unset`,c=e.allow??t.defaultCapabilities,l=F(c,t),d=F(e.deny??[],t).filter(e=>!l.includes(e));return{node:e,agentName:r,file:`.claude/agents/${r}.md`,role:s(e),isCritic:u(e),isWriter:m(e),...a?{model:a}:{},modelSource:o,...e.effort?{effort:t.effort[e.effort]}:{},tools:l,disallowedTools:d,unmappedAllow:I(c,t),unmappedDeny:I(e.deny??[],t)}}function F(e,t){let n=new Set;for(let r of e)for(let e of Object.hasOwn(t.capabilityTools,r)?t.capabilityTools[r]:[])n.add(e);return t.toolOrder.filter(e=>n.has(e))}var I=(e,t)=>e.filter(e=>!Object.hasOwn(t.capabilityTools,e));function L(e){switch(e.adaptation){case`adaptive`:return`- When the work shows the graph is wrong, amend the working copy as ${O(`LEAD.md`)} § "Adapting the graph" says: visibly, with a note, and never loosening a brake. Never write the source document.`;case`propose`:return`- When the graph looks wrong, record a proposal note (${O(`LEAD.md`)} § "Adapting the graph") and change nothing.`;case`fixed`:return`- Follow the graph exactly; when you cannot, halt and ask (${O(`LEAD.md`)} § "Adapting the graph").`}}function R(e){let t=n(e.index),r=e.agents.map(e=>O(e.agentName)).join(`, `);return w(C(`Run the grooph graph ${O(e.graphId)} (${e.doc.name}) in this project. You are the lead.`,``,`Read ${O(e.paths.lead)} first and follow it${e.adaptation===`fixed`?` exactly`:``}. It is the brief for this run; this prompt is only the trigger.`),C(`**Goal.**`,``,e.doc.goal??`(the graph states no goal)`),C(`**Before you touch anything:**`,``,`1. Read a run id from the clock (${O(`date -u +%Y%m%d-%H%M%S`)}, the form ${O(e.profile.runIdFormat)}), create ${O(`${e.paths.runs}/<run-id>/`)}, and copy ${O(e.paths.graph)} into it: that copy is the run's working copy.`,`2. Write ${O(`PROGRESS.md`)} and start ${O(`notes.jsonl`)} there, as ${O(`LEAD.md`)} § "Run setup" and § "Progress and notes" describe. Append a note for every node run and every loop round — that file is the record of the run.`,`3. Start at ${t.map(O).join(`, `)||`the first node`}.`),C(`**While you run:**`,``,`- Dispatch each agent node as its own subagent with the ${O(`Agent`)} tool: ${r||`(this graph has no agent nodes)`}. Do not do their work yourself, and do not grade work a critic node is there to grade.`,`- Give a fresh worker only its task, its declared inputs and the evidence its edge lists. Never paste a transcript into one.`,`- Run commands bare, from the project root, and tell each worker to do the same: under a narrow allowlist a compound form (${O(`cd … && …`)}) or ${O(`git -C <path>`)} is refused, and every refusal costs a turn.`,`- Evaluate the loop stops before every round, in the order ${O(`LEAD.md`)} lists them, and record the round.`,`- At a human gate: append the halt note first, then ask, then end your turn (${O(`LEAD.md`)} § "Human gates"). A run nobody answers ends there, and the same run id resumes it.`,L(e)),`**When the run ends** — a stop fires, you reach a stop node, or no edge is left to take — append the final note, write the last ${O(`PROGRESS.md`)}, and reply with the run id, the number of rounds, which stop ended the run, and anything left over.`)}function z(e){let t=b(e).filter(e=>e.from!==void 0);if(t.length===0)return!1;let n=e=>O(e),r=new Map((e.groups??[]).map(e=>[e.id,e.from??``])),i=t.map(t=>[k(t.name),O(t.id),ee.test(r.get(t.id))?O(r.get(t.id)):`a template`,ie(e,t.id).nodes.map(n).join(`, `),t.entries.length>0?[...new Set(t.entries.map(e=>n(e.to)))].join(`, `):`nothing leads in`,t.exits.length>0?[...new Set(t.exits.map(e=>n(e.to)))].join(`, `):`its own stop`]);return C(``,`### Units`,``,`Some of these nodes were placed together, each set from one template: a **subgrooph**. A person reading the graph sees each as one box under its name. Its nodes are ordinary nodes: run them as you run any others, by the edges, loops and stops below. When ${O(`PROGRESS.md`)} or a note says where the run is, name the unit as well as the node.`,``,D([`unit`,`id`,`placed from`,`its nodes`,`entered at`,`leads on to`],i))}function he(e,t){return w(ge(e),_e(e),ve(e),be(e),Se(e),Ee(e),ke(e),Fe(e),Ie(e),Ve(e),He(t),Ue(e))}var ge=e=>C(`# Lead brief · ${e.doc.name}`,``,`Graph ${O(e.graphId)} v${e.doc.version} · target ${O(`claude-code`)} · compiled by grooph from ${O(e.paths.graph)}.`,``,`This file is generated. Edit the graph document and export again, or hand-edit and record what you changed — the next export overwrites it.`);function _e(e){let t=e.leadNode?.brief.trim();return C(`## 1. You are the lead`,``,t??C(`You run this graph. The main session is the executive: you dispatch nodes, follow edges, count rounds, evaluate stops, keep the progress log, and stop for the human when the graph says to.`,``,"You do not do the workers' jobs. Every agent node below runs as its own subagent through the `Agent` tool, and sees only its brief, its declared inputs and the evidence its inbound edge allows. You never grade your own work while a critic node exists: the critic's verdict is the one that counts, and you do not overrule it by re-reading the diff yourself."))}function ve(e){let t=e.doc.constraints??{},n=[];return t.budget&&n.push(T(`**Budget.** ${t.budget}`)),t.time&&n.push(T(`**Time.** ${t.time}`)),t.other&&n.push(T(`**Other.** ${t.other}`)),C(`## 2. Goal and constraints`,``,`**Goal.**`,``,e.doc.goal??`(none stated)`,...n.length>0?[``,`**Constraints.**`,``,...n]:[],...e.doc.description?[``,`**What this graph does.**`,``,e.doc.description]:[])}var B=`20260917-093002`,ye=`2026-09-17T09:30:02Z`;function be(e){let t=JSON.stringify({id:`n-0001`,run:B,at:`graph`,started:ye,text:`run started`}),r=V(e);return C(`## 3. Run setup`,``,`1. Read the run id from the clock, in the form ${O(e.profile.runIdFormat)} (UTC): ${O(`date -u +%Y%m%d-%H%M%S`)}, for example ${O(B)}. If ${O(`${e.paths.runs}/<that id>/`)} already exists, append ${O(`-2`)}, then ${O(`-3`)}, and so on. Never make an id up.`,`2. Create ${O(`${e.paths.runs}/<run-id>/`)}.`,`3. Copy the source document ${O(e.paths.graph)} into it as ${O(e.paths.workingCopy)}. That copy is the run's working copy: the graph this run follows, ${e.adaptation===`adaptive`?`and the only copy you may amend (§9)`:`left as copied (§9)`}. Never write the source document.`,`4. Write ${O(`PROGRESS.md`)} there before dispatching anything: the run id, the goal, every node with status ${O(`pending`)}, and the round counter at 0${r.length>0?` (with the dispatch counter of ${r.map(e=>O(e.id)).join(`, `)} at 0, §6)`:``}.`,`5. Create ${O(`notes.jsonl`)} beside it and append the first line, its ${O(`started`)} read from ${O(`date -u +%Y-%m-%dT%H:%M:%SZ`)}:`,``,E(t,`json`),``,`6. If you were given a run id to resume, do not start a second run: read that folder's ${O(`PROGRESS.md`)} and working copy, continue from the last recorded position, and keep appending to the same ${O(`notes.jsonl`)}. Do not copy the source over the working copy again.`,``,`Entry nodes (start here): ${n(e.index).map(O).join(`, `)||`none — the graph has no entry node`}.`)}var V=e=>(e.doc.loops??[]).filter(e=>(e.stops??[]).some(e=>e.kind===`budget`&&e.measure===`dispatches`)),xe=e=>(e.doc.loops??[]).filter(e=>(e.stops??[]).some(e=>e.kind===`evidence-invalid`));function Se(e){return C(`## 4. Nodes`,``,D([`node`,`name`,`how you run it`,`role`,`what it returns`],(e.doc.nodes??[]).map(t=>[O(t.id),k(t.name),Ce(e,t),k(we(t)),k(Te(t))])),``,`Dispatch an agent node with the ${O(`Agent`)} tool and the ${O(`subagent_type`)} named above; its file under ${O(`.claude/agents/`)} carries the full brief, so your prompt carries only the task, the declared inputs and the edge's evidence. Never paste a transcript into a fresh worker.`,z(e.doc))}function Ce(e,t){let n=e.agentByNode.get(t.id);if(n)return`${O(`Agent`)} · ${O(n.agentName)}`;switch(t.kind){case`agent`:return`you — this is the lead node`;case`human-gate`:return`you ask the human`;case`check`:return t.check.run?`you run ${O(t.check.run)}`:`you judge the stated condition`;case`merge`:return`you merge the listed artifacts`;case`stop`:return`you end the run`;default:return`you`}}var we=e=>e.kind===`agent`?typeof e.role==`string`?e.role:`custom: ${e.role.custom}`:e.kind;function Te(e){switch(e.kind){case`agent`:return e.outputs.join(`; `);case`human-gate`:return e.options&&e.options.length>0?e.options.join(` | `):`a human answer`;case`check`:return`pass when: ${e.check.pass}`;case`merge`:return`merged: ${e.merges.join(`, `)}`;case`stop`:return`run ends with outcome ${e.outcome??`success`}`;default:return``}}function Ee(e){let n=e.doc.edges??[],i=xe(e),a=n.some(e=>(e.evidence??[]).some(e=>/\bdiff\b/i.test(e)));return C(`## 5. Edges`,``,D([`edge`,`route`,`taken when`,`isolation`,`evidence and gates`],n.map(e=>[O(e.id),`${O(e.from)} → ${O(e.to)}`,k(r(e)),t(e),k(Oe(e))])),``,T(`When a node finishes, take every outgoing edge whose condition matches its result. Several matching edges run in parallel, capped by any ${O(`concurrency`)} on the edge.`),T(`${O(`fresh`)} isolation: the worker starts with no context except its brief, its declared inputs and the evidence listed above. ${O(`shared`)}: continue the same worker if the build lets you, otherwise do that step yourself rather than faking a continuation.`),T(`A worker may inspect what its inbound edge lists plus its own declared inputs; for a writer that includes the project it is changing. A critic that cannot read its evidence reports ${O(`invalid-evidence`)} instead of guessing.`),T(`When an edge routes ${O(`invalid-evidence`)}, take it. Otherwise repair the evidence and dispatch the same node once more in the same round; a second ${O(`invalid-evidence`)} routes as ${O(`fail`)}.${i.length>0?` Such rounds count toward the ${O(`evidence-invalid`)} stop of loop ${i.map(e=>O(e.id)).join(`, `)}.`:``}`),...a?[T(De)]:[])}var De=`A diff of the change is ${O(`git diff`)} plus, for each file the change added, ${O(`git diff --no-index /dev/null <file>`)} (${O(`git diff`)} omits untracked files; ${O(`--no-index`)} exits 1 whenever the two differ, which is not an error). Run each bare from the project root, one command at a time; no brace group, no ${O(`cd`)}. ${O(`git add -N <file>`)} also works where it is allowed, and stages nothing.`;function Oe(e){let t=[];return e.evidence&&e.evidence.length>0?t.push(`evidence: ${e.evidence.join(`; `)}`):t.push(`evidence: none listed`),e.approval&&t.push(`**human approval required before traversal**`),e.concurrency&&t.push(`concurrency max ${e.concurrency.max}`),e.retry&&t.push(`retry max ${e.retry.max}`),e.label&&t.push(`label: ${e.label}`),t.join(` · `)}function ke(e){let t=e.doc.loops??[];return t.length===0?C(`## 6. Loops`,``,`This graph has no loops. Follow the edges once and stop.`):C(`## 6. Loops`,``,t.map(t=>Ae(e,t).replace(/\n+$/,``)).join(`
|
|
6
6
|
|
|
7
|
-
`))}function Ae(e,t){let i=o(e.index,t),a=(t.back??[]).map(t=>e.index.edges.get(t)).filter(e=>e!==void 0),s=r(e.index,t.bar),c=(t.stops??[]).filter(t=>t.kind===`budget`&&e.profile.advisoryBudgetMeasures.includes(t.measure));return C(`### Loop ${O(t.id)} · ${t.name}`,``,T(`**Mode.** ${i}${t.mode?``:` (inferred: not every back edge starts at a check node)`}`),T(`**Members.** ${(t.members??[]).map(O).join(`, `)}`),T(`**A round is** one traversal of a back edge: ${a.map(e=>`${O(e.id)} (${e.from} → ${e.to})`).join(`, `)||`none declared`}. The first pass through the members is round 0, because no back edge has been taken yet; each traversal after that adds one. Record the round in ${O(`PROGRESS.md`)} and in a loop note every time you finish a pass.`),``,...t.bar?[`**Bar — ${t.bar.name}.** Stop when: ${t.bar.acceptance}`,``,...t.bar.aspiration?[`Aspiration (direction, never the stop condition): ${t.bar.aspiration}`,``]:[],...t.bar.answerKeyFrom?[`The answer key is the output of node ${O(t.bar.answerKeyFrom)}.`,``]:[],`The critic inspects exactly these:`,``,...s.map(e=>T(`${e.kind}: ${O(e.ref)}${e.note?` — ${e.note}`:``}`)),``]:[`**Bar.** none declared.`,``],`**Stops, evaluated in this order before every round; the first that fires wins:**`,``,D([`#`,`stop`,`what you do`],(t.stops??[]).map((e,t)=>[String(t+1),k(n(e)),k(h(e))])),...Ne(e,t,c),``)}function je(e,t){return(t.members??[]).map(t=>e.index.nodes.get(t)).filter(e=>e!==void 0).filter(t=>t.kind===`agent`&&t.id!==e.leadNode?.id||t.kind===`check`)}var Me=(e,t)=>{let n=new Set(t.members??[]);return(e.doc.loops??[]).filter(e=>e.id!==t.id&&(e.members??[]).length<n.size&&(e.members??[]).every(e=>n.has(e)))};function Ne(e,t,n){let r=[],i=new Set((t.stops??[]).flatMap(e=>e.kind===`budget`?[e.measure]:[]));if(i.has(`dispatches`)&&(r.push(`> A dispatch is one node run inside this loop's members — an agent you dispatch, or a check you run — counted from the loop's first pass; a nested loop's count restarts when the outer loop re-enters it. Keep the count in ${O(`PROGRESS.md`)} and evaluate the stop against it.`),r.push(`> ${Pe(e,t)}`)),i.has(`minutes`)&&r.push(`> Minutes are wall clock from the run's first note.`),n.length>0){let e=[...new Set(n.flatMap(e=>e.kind===`budget`?[e.measure]:[]))];r.push(`> ${e.map(O).join(`, `)} budgets are **advisory**: nothing in Claude Code enforces them inside a session${e.includes(`turns`)?`, and leads count turns inconsistently`:``}. Track the figure in ${O(`PROGRESS.md`)} and halt when you pass it${e.includes(`usd`)?`; a ${O(`usd`)} budget is enforced only from outside, by starting a headless run with ${O(`--max-budget-usd`)}`:``}.`)}return r.length>0?[``,...r]:[]}function Pe(e,t){let n=je(e,t),r=(t.members??[]).filter(e=>!n.some(t=>t.id===e)),i=(t.stops??[]).flatMap(e=>e.kind===`budget`&&e.measure===`dispatches`?[e.limit]:[]),a=i.length>0?Math.min(...i):void 0,o=Me(e,t);if(n.length===0)return`One full round of this loop dispatches nothing: none of its members is an agent or a check.`;let s=a===void 0?``:` The budget of ${a} covers ${Math.floor(a/n.length)} full round${Math.floor(a/n.length)===1?``:`s`}${a%n.length===0?``:` and ${a%n.length} more dispatch${a%n.length===1?``:`es`}`}.`;return`One full round of this loop costs **${n.length} dispatch${n.length===1?``:`es`}**: ${n.map(e=>O(e.id)).join(`, `)}${r.length>0?` (${r.map(O).join(`, `)} ${r.length===1?`is`:`are`} not a dispatch)`:``}.${s}${o.length>0?` Every extra round of the inner loop ${o.map(e=>O(e.id)).join(`, `)} adds its own dispatches on top.`:``} A node dispatched twice in one round (§5, invalid evidence) counts twice.`}function Fe(e){let t=(e.doc.nodes??[]).filter(e=>e.kind===`human-gate`),n=(e.doc.edges??[]).filter(e=>e.approval===!0),r=[...t.map(e=>T(`${O(e.id)} — ${e.kind===`human-gate`?e.prompt:``}${e.kind===`human-gate`&&e.options&&e.options.length>0?` (options: ${e.options.join(` | `)})`:``}`)),...n.map(e=>T(`${O(e.id)} — approval required before ${e.from} → ${e.to} is traversed`))];return C(`## 7. Human gates`,``,...r.length>0?r:[T(`None in this graph.`)],``,`One rule, in every kind of session. On reaching a gate: first append a note at the gate (${O(`at`)} = ${O(`node:<gate-id>`)}, or ${O(`edge:<edge-id>`)} for an approval edge) with ${O(`"outcome":"halt"`)} and a ${O(`text`)} naming it, and write ${O(`PROGRESS.md`)}; then ask, with ${O(`AskUserQuestion`)} when it is available, otherwise in plain text; then end your turn. Do not simulate an answer, do not batch two gates into one question, and do not proceed on silence.`,``,`When the human answers, append a note at the same place with their decision and continue along the matching edge. A run nobody answers ends on that halt note, and the same run id resumes it (§3, step 6).`)}function Ie(e){let t=e.agents.find(e=>e.isCritic)??e.agents[0],r=JSON.stringify({id:`n-0007`,run:B,at:`node:${t?.node.id??`some-node`}`,started:`2026-09-17T09:34:02Z`,ended:`2026-09-17T09:38:41Z`,outcome:`fail`,verdict:`fail`,round:2,evidence:[`docs/REVIEW-CHECKLIST.md`,`test command output`],gaps:[`no test covers the empty-input case`],text:`3 of 5 checklist items cited; two unmet`}),i=(e.doc.loops??[])[0],a=i?.stops??[],o=a.find(e=>e.kind===`bar-passed`)??a.find(e=>e.kind===`budget`)??a[0],s=i&&o?JSON.stringify({id:`n-0012`,run:B,at:`loop:${i.id}`,ended:`2026-09-17T09:51:10Z`,outcome:o.kind===`bar-passed`?`pass`:`halt`,round:3,stop:o.kind,text:o.kind===`bar-passed`?`bar passed at round 3; taking the pass edges`:`${n(o)} fired at round 3`}):void 0,c=V(e);return C(`## 8. Progress and notes`,``,T(`${O(e.paths.progress)} — human-readable. Rewrite it **after every node completes** and whenever the round counter moves: run id, goal, round, ${c.length>0?`the dispatch count of ${c.map(e=>O(e.id)).join(`, `)}, `:``}each node's status, what is waiting, and the stop check you last evaluated.`),T(`${O(e.paths.notes)} — one JSON object per line, appended, never rewritten. Append a line at the start of the run, one **per node run** (${O(`at`)} = ${O(`node:<node-id>`)}), one **per pass through a loop** (${O(`at`)} = ${O(`loop:<loop-id>`)}, carrying the round you just finished and the stop you evaluated — so even a loop that passes on its first pass leaves a line — and ${O(`stop`)} with the kind of the stop when one fires), and one when the run ends.`),T(`When you dispatch a node, append one short line first: ${O(`"outcome":"started"`)}, ${O(`at`)} = ${O(`node:<node-id>`)}, and ${O(`round`)} when the node is inside a loop. The usual line follows when the node completes, so a monitor can show what is running.`),T(`${O(`started`)} and ${O(`ended`)} are read from the clock, ${O(`date -u +%Y-%m-%dT%H:%M:%SZ`)}, or left out. Never estimate one.`),...Le(e),``,"Line shape (graph-ir §6). `id`, `run` and `at` are required; the rest are filled when they apply:",``,E(C("id kebab-case, unique in the file: `n-0001`, `n-0002`, … in append order",`run the run id`,`at graph | node:<node-id> | edge:<edge-id> | loop:<loop-id>`,`started ISO timestamp from the clock, or omitted ended the same`,`outcome pass | fail | halt | invalid-evidence; started on a dispatch line, ending on the line before the final note (§11)`,`verdict the critic's verdict label, when there is one`,`round the loop round this belongs to`,`stop on the loop note that ends the loop: the kind of the stop that fired`,`evidence what was actually inspected`,`cost { measure: dispatches | minutes | usd | turns | tokens, amount }`,`gaps repeated gaps you noticed`,`proposal { summary, patch? } — a graph change for the human to decide; you do not make it`,...e.adaptation===`adaptive`?[`amendment { summary, reason, patch? } — a change you made to the working copy (§9)`]:[],`text short commentary`),`text`),``,s?`Two filled lines, a node run and the loop pass on which a stop fired:`:`One filled line:`,``,E(s?C(r,s):r,`json`),``,`A run never writes the source document ${O(e.paths.graph)}. When the graph itself looks wrong, §9 says what to do.`)}function Le(t){let n=t.agents.filter(n=>n.isCritic&&e(t.index,n.node.id).length>0);if(n.length===0)return[];let r=n.flatMap(e=>e.node.outputs).find(e=>/\.md$/i.test(e.trim()))??`REVIEW.md`,i=r.trim().replace(/\.md$/i,``);return[T(`Before you re-dispatch a builder after a critic's ${O(`fail`)}, copy each report the critic wrote that round into the run folder as ${O(`<report>-round-<n>.md`)} — ${O(r.trim())} from round 0 becomes ${O(`${t.paths.runs}/<run-id>/${i}-round-0.md`)} — so the round's findings survive the next round's rewrite. Copy; the builder still reads the report where its edge says.`)]}var Re=[`a human gate`,`an edge ${O(`approval`)}`,`an ${O(`irreversible`)} marker`,`a ${O(`budget`)} or ${O(`max-iterations`)} stop`,`a bar's ${O(`acceptance`)}`,`critic isolation`,`the ${O(`adaptation`)} level itself`],ze=`A ${O(`patch`)} is preferably a list of grooph ops, the JSON ${O(`grooph apply --ops`)} takes (${O(`[{"op":"updateNode","id":"<node-id>","set":{"effort":"high"}}]`)}): ops name objects by id, so the human can replay them with ${O(`grooph apply`)}.`,Be={setGraphName:`name — the graph id follows while it still matches`,setGraphField:`key (name | goal | description | adaptation | lineage), value — null removes`,setTarget:`harness — null removes`,setConstraint:`key (budget | time | other), value — null removes`,addNode:`kind (agent | human-gate | check | merge | stop), name?, id?, at?, set? — the node's fields (role, brief, outputs, allow, …) go in set`,setNodeName:`id, name`,updateNode:`id, set — a shallow patch: each key replaces the field, null removes it; never id or kind`,removeNode:`id — with its edges, loop memberships and scoped policies`,connect:`from, to, id?, set? — adds an edge; when, isolation, evidence, approval go in set`,updateEdge:`id, set — re-routing (from, to) included`,removeEdge:`id`,addLoop:`members?, name?, id?, set? — no back edge and no stop until set or addStop gives them`,setLoopName:`id, name`,updateLoop:`id, set — mode, members, back, bar, stops`,removeLoop:`id`,toggleLoopMember:`loop, node, on?`,toggleLoopBack:`loop, edge, on?`,setBar:`loop, bar — null removes`,addStop:`loop, kind (human | budget | bar-passed | diminishing-returns | evidence-invalid | max-iterations), set?`,setStop:`loop, index, stop — replaces the stop at index (from 0)`,removeStop:`loop, index`,moveStop:`loop, index, delta (-1 | 1)`,addPolicy:`kind, scope (graph | loop:<id> | node:<id> | edge:<id>), params?, id?`,removePolicy:`id`,setPositions:`positions — layout only`,renameId:`from, to — every reference follows`};function H(){let e=Object.keys(a),t=Math.max(...e.map(e=>e.length)),n=[JSON.stringify({op:`addNode`,kind:`agent`,id:`reviewer`,set:{role:`critic`,brief:`<purpose, limits, outputs>`,outputs:[`REVIEW.md`],allow:[`read-files`,`write-outputs`]}}),JSON.stringify({op:`connect`,from:`builder`,to:`reviewer`,set:{when:`pass`,evidence:[`diff of the change`]}})];return[`These are the ops, and the only ops, ${O(`grooph apply`)} accepts; an op is ${O(`{"op":"<name>", ...arguments}`)}, and a ${O(`?`)} marks an optional argument. There is no ${O(`addEdge`)} and no ${O(`node`)} object: an edge is ${O(`connect`)}, and a node's fields go in ${O(`set`)}.`,``,E(e.map(e=>`${e.padEnd(t)} ${Be[e]}`).join(`
|
|
7
|
+
`))}function Ae(t,n){let r=g(t.index,n),i=(n.back??[]).map(e=>t.index.edges.get(e)).filter(e=>e!==void 0),a=e(t.index,n.bar),o=(n.stops??[]).filter(e=>e.kind===`budget`&&t.profile.advisoryBudgetMeasures.includes(e.measure));return C(`### Loop ${O(n.id)} · ${n.name}`,``,T(`**Mode.** ${r}${n.mode?``:` (inferred: not every back edge starts at a check node)`}`),T(`**Members.** ${(n.members??[]).map(O).join(`, `)}`),T(`**A round is** one traversal of a back edge: ${i.map(e=>`${O(e.id)} (${e.from} → ${e.to})`).join(`, `)||`none declared`}. The first pass through the members is round 0, because no back edge has been taken yet; each traversal after that adds one. Record the round in ${O(`PROGRESS.md`)} and in a loop note every time you finish a pass.`),``,...n.bar?[`**Bar — ${n.bar.name}.** Stop when: ${n.bar.acceptance}`,``,...n.bar.aspiration?[`Aspiration (direction, never the stop condition): ${n.bar.aspiration}`,``]:[],...n.bar.answerKeyFrom?[`The answer key is the output of node ${O(n.bar.answerKeyFrom)}.`,``]:[],`The critic inspects exactly these:`,``,...a.map(e=>T(`${e.kind}: ${O(e.ref)}${e.note?` — ${e.note}`:``}`)),``]:[`**Bar.** none declared.`,``],`**Stops, evaluated in this order before every round; the first that fires wins:**`,``,D([`#`,`stop`,`what you do`],(n.stops??[]).map((e,t)=>[String(t+1),k(h(e)),k(v(e))])),...Ne(t,n,o),``)}function je(e,t){return(t.members??[]).map(t=>e.index.nodes.get(t)).filter(e=>e!==void 0).filter(t=>t.kind===`agent`&&t.id!==e.leadNode?.id||t.kind===`check`)}var Me=(e,t)=>{let n=new Set(t.members??[]);return(e.doc.loops??[]).filter(e=>e.id!==t.id&&(e.members??[]).length<n.size&&(e.members??[]).every(e=>n.has(e)))};function Ne(e,t,n){let r=[],i=new Set((t.stops??[]).flatMap(e=>e.kind===`budget`?[e.measure]:[]));if(i.has(`dispatches`)&&(r.push(`> A dispatch is one node run inside this loop's members — an agent you dispatch, or a check you run — counted from the loop's first pass; a nested loop's count restarts when the outer loop re-enters it. Keep the count in ${O(`PROGRESS.md`)} and evaluate the stop against it.`),r.push(`> ${Pe(e,t)}`)),i.has(`minutes`)&&r.push(`> Minutes are wall clock from the run's first note.`),n.length>0){let e=[...new Set(n.flatMap(e=>e.kind===`budget`?[e.measure]:[]))];r.push(`> ${e.map(O).join(`, `)} budgets are **advisory**: nothing in Claude Code enforces them inside a session${e.includes(`turns`)?`, and leads count turns inconsistently`:``}. Track the figure in ${O(`PROGRESS.md`)} and halt when you pass it${e.includes(`usd`)?`; a ${O(`usd`)} budget is enforced only from outside, by starting a headless run with ${O(`--max-budget-usd`)}`:``}.`)}return r.length>0?[``,...r]:[]}function Pe(e,t){let n=je(e,t),r=(t.members??[]).filter(e=>!n.some(t=>t.id===e)),i=(t.stops??[]).flatMap(e=>e.kind===`budget`&&e.measure===`dispatches`?[e.limit]:[]),a=i.length>0?Math.min(...i):void 0,o=Me(e,t);if(n.length===0)return`One full round of this loop dispatches nothing: none of its members is an agent or a check.`;let s=a===void 0?``:` The budget of ${a} covers ${Math.floor(a/n.length)} full round${Math.floor(a/n.length)===1?``:`s`}${a%n.length===0?``:` and ${a%n.length} more dispatch${a%n.length===1?``:`es`}`}.`;return`One full round of this loop costs **${n.length} dispatch${n.length===1?``:`es`}**: ${n.map(e=>O(e.id)).join(`, `)}${r.length>0?` (${r.map(O).join(`, `)} ${r.length===1?`is`:`are`} not a dispatch)`:``}.${s}${o.length>0?` Every extra round of the inner loop ${o.map(e=>O(e.id)).join(`, `)} adds its own dispatches on top.`:``} A node dispatched twice in one round (§5, invalid evidence) counts twice.`}function Fe(e){let t=(e.doc.nodes??[]).filter(e=>e.kind===`human-gate`),n=(e.doc.edges??[]).filter(e=>e.approval===!0),r=[...t.map(e=>T(`${O(e.id)} — ${e.kind===`human-gate`?e.prompt:``}${e.kind===`human-gate`&&e.options&&e.options.length>0?` (options: ${e.options.join(` | `)})`:``}`)),...n.map(e=>T(`${O(e.id)} — approval required before ${e.from} → ${e.to} is traversed`))];return C(`## 7. Human gates`,``,...r.length>0?r:[T(`None in this graph.`)],``,`One rule, in every kind of session. On reaching a gate: first append a note at the gate (${O(`at`)} = ${O(`node:<gate-id>`)}, or ${O(`edge:<edge-id>`)} for an approval edge) with ${O(`"outcome":"halt"`)} and a ${O(`text`)} naming it, and write ${O(`PROGRESS.md`)}; then ask, with ${O(`AskUserQuestion`)} when it is available, otherwise in plain text; then end your turn. Do not simulate an answer, do not batch two gates into one question, and do not proceed on silence.`,``,`When the human answers, append a note at the same place with their decision and continue along the matching edge. A run nobody answers ends on that halt note, and the same run id resumes it (§3, step 6).`)}function Ie(e){let t=e.agents.find(e=>e.isCritic)??e.agents[0],n=JSON.stringify({id:`n-0007`,run:B,at:`node:${t?.node.id??`some-node`}`,started:`2026-09-17T09:34:02Z`,ended:`2026-09-17T09:38:41Z`,outcome:`fail`,verdict:`fail`,round:2,evidence:[`docs/REVIEW-CHECKLIST.md`,`test command output`],gaps:[`no test covers the empty-input case`],text:`3 of 5 checklist items cited; two unmet`}),r=(e.doc.loops??[])[0],i=r?.stops??[],a=i.find(e=>e.kind===`bar-passed`)??i.find(e=>e.kind===`budget`)??i[0],o=r&&a?JSON.stringify({id:`n-0012`,run:B,at:`loop:${r.id}`,ended:`2026-09-17T09:51:10Z`,outcome:a.kind===`bar-passed`?`pass`:`halt`,round:3,stop:a.kind,text:a.kind===`bar-passed`?`bar passed at round 3; taking the pass edges`:`${h(a)} fired at round 3`}):void 0,s=V(e);return C(`## 8. Progress and notes`,``,T(`${O(e.paths.progress)} — human-readable. Rewrite it **after every node completes** and whenever the round counter moves: run id, goal, round, ${s.length>0?`the dispatch count of ${s.map(e=>O(e.id)).join(`, `)}, `:``}each node's status, what is waiting, and the stop check you last evaluated.`),T(`${O(e.paths.notes)} — one JSON object per line, appended, never rewritten. Append a line at the start of the run, one **per node run** (${O(`at`)} = ${O(`node:<node-id>`)}), one **per pass through a loop** (${O(`at`)} = ${O(`loop:<loop-id>`)}, carrying the round you just finished and the stop you evaluated — so even a loop that passes on its first pass leaves a line — and ${O(`stop`)} with the kind of the stop when one fires), and one when the run ends.`),T(`When you dispatch a node, append one short line first: ${O(`"outcome":"started"`)}, ${O(`at`)} = ${O(`node:<node-id>`)}, and ${O(`round`)} when the node is inside a loop. The usual line follows when the node completes, so a monitor can show what is running.`),T(`${O(`started`)} and ${O(`ended`)} are read from the clock, ${O(`date -u +%Y-%m-%dT%H:%M:%SZ`)}, or left out. Never estimate one.`),...Le(e),``,"Line shape (graph-ir §6). `id`, `run` and `at` are required; the rest are filled when they apply:",``,E(C("id kebab-case, unique in the file: `n-0001`, `n-0002`, … in append order",`run the run id`,`at graph | node:<node-id> | edge:<edge-id> | loop:<loop-id>`,`started ISO timestamp from the clock, or omitted ended the same`,`outcome pass | fail | halt | invalid-evidence; started on a dispatch line, ending on the line before the final note (§11)`,`verdict the critic's verdict label, when there is one`,`round the loop round this belongs to`,`stop on the loop note that ends the loop: the kind of the stop that fired`,`evidence what was actually inspected`,`cost { measure: dispatches | minutes | usd | turns | tokens, amount }`,`gaps repeated gaps you noticed`,`proposal { summary, patch? } — a graph change for the human to decide; you do not make it`,...e.adaptation===`adaptive`?[`amendment { summary, reason, patch? } — a change you made to the working copy (§9)`]:[],`text short commentary`),`text`),``,o?`Two filled lines, a node run and the loop pass on which a stop fired:`:`One filled line:`,``,E(o?C(n,o):n,`json`),``,`A run never writes the source document ${O(e.paths.graph)}. When the graph itself looks wrong, §9 says what to do.`)}function Le(e){let t=e.agents.filter(t=>t.isCritic&&f(e.index,t.node.id).length>0);if(t.length===0)return[];let n=t.flatMap(e=>e.node.outputs).find(e=>/\.md$/i.test(e.trim()))??`REVIEW.md`,r=n.trim().replace(/\.md$/i,``);return[T(`Before you re-dispatch a builder after a critic's ${O(`fail`)}, copy each report the critic wrote that round into the run folder as ${O(`<report>-round-<n>.md`)} — ${O(n.trim())} from round 0 becomes ${O(`${e.paths.runs}/<run-id>/${r}-round-0.md`)} — so the round's findings survive the next round's rewrite. Copy; the builder still reads the report where its edge says.`)]}var Re=[`a human gate`,`an edge ${O(`approval`)}`,`an ${O(`irreversible`)} marker`,`a ${O(`budget`)} or ${O(`max-iterations`)} stop`,`a bar's ${O(`acceptance`)}`,`critic isolation`,`the ${O(`adaptation`)} level itself`],ze=`A ${O(`patch`)} is preferably a list of grooph ops, the JSON ${O(`grooph apply --ops`)} takes (${O(`[{"op":"updateNode","id":"<node-id>","set":{"effort":"high"}}]`)}): ops name objects by id, so the human can replay them with ${O(`grooph apply`)}.`,Be={setGraphName:`name — the graph id follows while it still matches`,setGraphField:`key (name | goal | description | adaptation | lineage), value — null removes`,setTarget:`harness — null removes`,setConstraint:`key (budget | time | other), value — null removes`,addNode:`kind (agent | human-gate | check | merge | stop), name?, id?, at?, set? — the node's fields (role, brief, outputs, allow, …) go in set`,setNodeName:`id, name`,updateNode:`id, set — a shallow patch: each key replaces the field, null removes it; never id or kind`,removeNode:`id — with its edges, loop memberships and scoped policies`,connect:`from, to, id?, set? — adds an edge; when, isolation, evidence, approval go in set`,updateEdge:`id, set — re-routing (from, to) included`,removeEdge:`id`,addLoop:`members?, name?, id?, set? — no back edge and no stop until set or addStop gives them`,setLoopName:`id, name`,updateLoop:`id, set — mode, members, back, bar, stops`,removeLoop:`id`,toggleLoopMember:`loop, node, on?`,toggleLoopBack:`loop, edge, on?`,setBar:`loop, bar — null removes`,addStop:`loop, kind (human | budget | bar-passed | diminishing-returns | evidence-invalid | max-iterations), set?`,setStop:`loop, index, stop — replaces the stop at index (from 0)`,removeStop:`loop, index`,moveStop:`loop, index, delta (-1 | 1)`,addPolicy:`kind, scope (graph | loop:<id> | node:<id> | edge:<id>), params?, id?`,removePolicy:`id`,setPositions:`positions — layout only`,renameId:`from, to — every reference follows`};function H(){let e=Object.keys(_),t=Math.max(...e.map(e=>e.length)),n=[JSON.stringify({op:`addNode`,kind:`agent`,id:`reviewer`,set:{role:`critic`,brief:`<purpose, limits, outputs>`,outputs:[`REVIEW.md`],allow:[`read-files`,`write-outputs`]}}),JSON.stringify({op:`connect`,from:`builder`,to:`reviewer`,set:{when:`pass`,evidence:[`diff of the change`]}})];return[`These are the ops, and the only ops, ${O(`grooph apply`)} accepts; an op is ${O(`{"op":"<name>", ...arguments}`)}, and a ${O(`?`)} marks an optional argument. There is no ${O(`addEdge`)} and no ${O(`node`)} object: an edge is ${O(`connect`)}, and a node's fields go in ${O(`set`)}.`,``,E(e.map(e=>`${e.padEnd(t)} ${Be[e]}`).join(`
|
|
8
8
|
`),`text`),``,`A new critic and the edge into it, as one patch of two ops:`,``,E(`[${n.join(`,
|
|
9
|
-
`)}]`,`json`)]}function Ve(e){let t=e.adaptation,n=(e.doc.policies??[]).filter(e=>e.kind===`no-live-graph-rewrite`),r=n.find(e=>e.scope===`graph`),i=r&&e.doc.adaptation!==t?` (its policy ${O(r.id)}, ${O(`no-live-graph-rewrite`)}, is stricter than ${O(`adaptation: ${e.doc.adaptation??`adaptive`}`)})`:e.doc.adaptation===void 0?` (the default)`:``,a=`## 9. Adapting the graph`,o=`The working copy ${O(e.paths.workingCopy)} stays identical to the source document ${O(e.paths.graph)}: neither is written during this run.`;if(t===`fixed`)return C(a,``,`This graph is ${O(`fixed`)}${i}: follow it exactly. Do not add, remove or re-brief nodes, re-route edges, or change loops, tiers or effort — not even to tighten a brake.`,``,`When the work cannot go on within the graph as written, halt and ask: append a note with ${O(`"outcome":"halt"`)} that says what the graph is missing, write the final ${O(`PROGRESS.md`)}, and tell the human. A ${O(`proposal`)} note alongside is welcome; the run does not continue on it.`,``,o);if(t===`propose`)return C(a,``,`This graph is ${O(`propose`)}${i}: you change nothing in it during the run, not even to tighten a brake. Follow it as written.`,``,`When the work shows the graph is wrong — a missing node, a loop that should exist, a brief that no longer fits — append a note with a ${O(`proposal`)} (${O(`summary`)}, and a ${O(`patch`)} when one helps) and carry on with the graph as it is; the human decides after the run. If you cannot carry on without the change, halt and say why.`,``,ze,``,...H(),``,o);let s=JSON.stringify({id:`n-0009`,run:`<run-id>`,at:`graph`,amendment:{summary:`<what you changed>`,reason:`<what the work showed>`,patch:[{op:`updateNode`,id:`<node-id>`,set:{owns:[`<artifact>`]}}]}}),c=n.filter(e=>e.scope!==`graph`);return C(a,``,`This graph is ${O(`adaptive`)}${i}. It is the plan to start from, not a script: when the work shows it is wrong — a missing node, a loop that should exist, a brief that no longer fits — change the run's working copy rather than work around it. When the graph fits, follow it. Work that fits an existing node's brief and outputs needs no amendment, and the smallest change that closes a real gap is the right one.`,``,`Amending at kickoff is fine when reading the task already shows a gap, such as a file a node must write that its ${O(`owns`)} does not list. Redesigning the graph up front is not: a change to its overall shape before any node has run is a ${O(`proposal`)} for the human.`,``,`You may add, remove or re-brief nodes, add or re-route edges, add loops, and change tiers or effort. For each amendment, when you make it:`,``,`1. Edit the working copy, ${O(e.paths.workingCopy)}. The source document ${O(e.paths.graph)} is never written by a run; after the run the human adopts your working copy as a new version or discards it.`,`2. Append a note with an ${O(`amendment`)} — ${O(`summary`)}, ${O(`reason`)}, and a ${O(`patch`)} when one helps. A patch is preferably a list of grooph ops, the JSON ${O(`grooph apply --ops`)} takes: ops name objects by id, so they survive reordering and can be replayed. The working copy is the record either way.`,``,E(s,`json`),``,`3. Record it in ${O(`PROGRESS.md`)} under **Amendments**, so the human can see the graph the run is actually following.`,`4. Check that the working copy still validates: run ${O(`grooph validate --for-export ${e.paths.workingCopy}`)} when ${O(`grooph`)} is on your PATH; otherwise check the brakes below by hand.`,`5. When the amendment changes a node's ${O(`allow`)} or ${O(`deny`)}, also edit that node's file under ${O(`.claude/agents/`)} before you dispatch it: its ${O(`tools:`)} line (and ${O(`disallowedTools:`)}), with the capability-to-tools table in ${O(e.paths.mapping)}. Claude Code reads the edited file at the next dispatch; no restart is needed. Say in the amendment note that you edited it. If the file cannot be edited, the change is a ${O(`proposal`)}: record it as one and dispatch the node as compiled, never a stand-in.`,``,...H(),``,`At every adaptation level you may not remove or loosen:`,``,...Re.map(T),``,`You may tighten any of them. Loosening one is a ${O(`proposal`)} note for the human, never an amendment. A loop you add needs a stop, and a bar if it is a judgment loop, like any other.${c.length>0?` Policy ${c.map(e=>`${O(e.id)} (scope ${O(e.scope)})`).join(`, `)} forbids live rewrites where it applies: for anything it covers, record a proposal instead.`:``}`,``,`A node you add mid-run has no compiled file under ${O(`.claude/agents/`)}. Write one beside the others, in the shape of an existing one, and dispatch it by that name (the file is read at the next dispatch), or dispatch it as a general-purpose subagent with its brief inline. Either way it works under the same isolation and evidence rules as every other node.`)}function He(e){return C(`## 10. Validation warnings`,``,...e.length===0?[`The document validated clean for export: no warnings.`]:[`grooph raised these when compiling this package. They are not errors, and the human running this graph should see them:`,``,E(e.map(
|
|
10
|
-
`),`text`)])}function Ue(e){let t=(e.doc.nodes??[]).filter(e=>e.kind===`stop`),n=e.adaptation===`adaptive`;return C(`## 11. Ending`,``,`The run ends when you reach a stop node, when a stop fires and its action is to halt, or when no edge is left to take. A gate is different: the halt note of §7 stands as the final note until the human answers, and the run continues from it.`,``,...t.length>0?[`Stop nodes: ${t.map(e=>`${O(e.id)} (${e.kind===`stop`?e.outcome??`success`:``})`).join(`, `)}.`,``]:[`This graph has no stop node: the run ends when no edge is left to take.`,``],`Whichever way it ends, do all three:`,``,`1. Append one short line first, ${O(`{"at":"graph","outcome":"ending"}`)} with a ${O(`text`)} naming how the run ends, then the final note: ${O(`"at":"graph"`)} with the outcome and a ${O(`text`)} that names the stop that fired or the stop node reached. The ${O(`ending`)} line is how a monitor tells a run that finished from one that was cut off while finishing.`,`2. Write the last ${O(`PROGRESS.md`)}: which nodes ran, how many rounds, ${n?`every amendment to the working copy, `:``}and why the run ended.`,`3. Tell the human, in your reply, the run id, the rounds, the stop that ended the run, ${n?`whether the working copy was amended (so they can adopt or discard it), `:``}and what is left over.`,``,`The final note and ${O(`PROGRESS.md`)} are the record. Your last reply is the report: it summarizes them for whoever started this session and points at the run folder, ${O(`${e.paths.runs}/<run-id>/`)}.`)}function We(e){let t=[[`graph ${O(e.graphId)}`,O(e.paths.graph),`the source document in canonical form; the only thing grooph reads back`],[`lead brief`,O(e.paths.lead),`goal, nodes, edges, loops, gates, progress contract, warnings`],[`kickoff`,O(e.paths.kickoff),`the prompt to paste when the skill is not loaded`],[`kickoff (skill)`,O(e.paths.skill),`${O(`/${e.graphId}`)} starts or resumes a run`],[`mapping notes`,O(e.paths.mapping),`this file`],[`progress`,O(`${e.paths.runs}/<run-id>/PROGRESS.md`),`written at run time, after every node`],[`run notes`,O(`${e.paths.runs}/<run-id>/notes.jsonl`),`written at run time, one JSON object per line (graph-ir §6)`],[`working copy`,O(e.paths.workingCopy),e.adaptation===`adaptive`?`copied from the source at run setup; the lead amends it, with a note per amendment, when the work shows the graph is wrong`:`copied from the source at run setup; left unchanged (${e.adaptation})`],...e.agents.map(e=>[`node ${O(e.node.id)}`,O(e.file),`subagent ${O(e.agentName)} · ${e.role} · model ${e.model??`session default`} · effort ${e.effort??`session default`}`])],n=(e.doc.nodes??[]).filter(e=>e.kind!==`agent`),r=e.agents.filter(e=>(e.node.skills??[]).length>0);return w(C(`# Mapping notes · ${e.doc.name}`,``,`How this package's files correspond to the graph document, so a human can hand-adjust without re-running grooph. Target profile: Claude Code, verified against ${O(e.profile.verifiedAgainst)} on ${e.profile.verifiedOn}.`),C(`## Graph piece → file`,``,D([`graph piece`,`file`,`what it carries`],t)),C(`## Pieces with no file of their own`,``,...n.length>0?["These live inside `LEAD.md`, because the lead performs them itself:",``,...n.map(e=>`- ${O(e.id)} (${e.kind}) — ${e.kind===`check`?`the lead runs the check and judges the stated condition`:e.kind===`human-gate`?`the lead asks the human and waits`:e.kind===`merge`?`the lead merges the listed artifacts`:`the lead ends the run here`}`)]:[`Every node in this graph is an agent node with its own file.`],``,`Edges, loops and policies have no file: they are the routing, round and stop rules in ${O(`LEAD.md`)} §5–§7. ${(e.doc.policies??[]).length>0?`Policies in force: ${(e.doc.policies??[]).map(e=>`${O(e.id)} (${typeof e.kind==`string`?e.kind:e.kind.custom}, scope ${e.scope})`).join(`, `)}.`:`This graph declares no policies.`}`),C(`## The three things people hand-edit`,``,`**A node's model or effort.** Change the frontmatter of its agent file:`,``,E(C(`# ${e.agents[0]?.file??`.claude/agents/<graph-id>--<node-id>.md`}`,`model: ${e.agents[0]?.model??`opus`} # ${e.tiersNamed.length>0?`this export`:`profile`}: frontier → ${e.profile.models.frontier}, strong → ${e.profile.models.strong}, fast → ${e.profile.models.fast}`,`effort: ${e.agents[0]?.effort??`high`} # low | medium | high | max`),`yaml`),``,`The durable place for that change is ${O(`model.tier`)} or ${O(`effort`)} on the node in the graph document; edit the file only for a one-off run, because the next export overwrites it.`,``,`**A node's tools.** The ${O(`tools:`)} line of the same frontmatter (and ${O(`disallowedTools:`)}), which is the node's ${O(`allow`)} (and ${O(`deny`)}) through this table:`,``,D([`capability`,`tools`],Object.entries(e.profile.capabilityTools).map(([e,t])=>[O(e),t.map(O).join(`, `)])),``,`The durable place is ${O(`allow`)} or ${O(`deny`)} on the node in the graph document.${e.adaptation===`adaptive`?` A running lead edits ${O(`tools:`)} itself when it amends a node's capabilities (${O(e.paths.lead)} §9); Claude Code reads the edited file at the node's next dispatch, without a restart.`:``}`,``,`The ${O(`skills:`)} line of the same frontmatter is hand-editable the same way: harness skill names, preloaded at the node's dispatch, from ${O(`skills`)} on the node in the graph document. An unknown name is refused by Claude Code, not by grooph.${r.length>0?` In this package: ${r.map(e=>`${O(e.file)} (${e.node.skills.join(`, `)})`).join(`, `)}.`:` No node in this graph names one.`}`,``,`**A loop's stop values.** The numbers a run actually bumps into:`,``,...(e.doc.loops??[]).map(t=>C(`- ${O(t.id)} (${o(e.index,t)}): ${(t.stops??[]).map(e=>e.kind===`max-iterations`?`${O(`max-iterations n=${e.n}`)}`:e.kind===`budget`?`${O(`budget ${e.limit} ${e.measure}`)}`:O(e.kind)).join(`, `)||`no stops declared`} — edit them in ${O(`${e.paths.lead}`)} §6 for this run, or in the graph document to keep them.`))),C(`## Rules this package relies on`,``,`- The subagent files must sit in ${O(`.claude/agents/`)} of the project the session runs in; the package is discovered from the project directory, not from a flag.`,`- Subagent names cannot contain a colon, which is why they read ${O(`<graph-id>--<node-id>`)}.`,`- ${k("A `dispatches` budget is exact: the lead counts node dispatches in `PROGRESS.md`. `usd`, `turns` and `tokens` budgets are advisory inside a session; a `usd` budget is enforced only from outside, by starting a headless run with `--max-budget-usd`.")}`,`- ${k("Commands run bare from the project root: an allowlist matches a command's prefix, so a compound form (`cd … && …`) or `git -C <path>` is refused under a narrow allowlist and costs a turn each time.")}`,`- A run never writes ${O(e.paths.graph)}. Adopt a run's working copy as the next version of the graph, or discard it; either way that is a human decision after the run.`,`- Nothing here executes the graph. grooph compiles; the session is the runtime.`))}function Ge(e){return w(C(`---`,`name: ${N([e.graphId])}`,`description: ${M(`Start or resume a grooph run of the ${e.graphId} graph (${e.doc.name}). Invoked by the human, never on its own.`)}`,`disable-model-invocation: true`,`argument-hint: [run-id to resume]`,`---`),C(`# ${e.doc.name}`,``,`Read ${O(e.paths.lead)} now and follow it. You are the lead for this run: dispatch the graph's nodes, follow its edges, count rounds, evaluate stops, and keep ${O(`${e.paths.runs}/<run-id>/`)} current.`),C(`- No argument: start a new run and choose a run id.`,`- An argument: resume that run id — read its ${O(`PROGRESS.md`)}, continue where it stopped, keep appending to the same ${O(`notes.jsonl`)}.`),C(`The source graph document is ${O(e.paths.graph)} and the mapping from graph to files is ${O(e.paths.mapping)}; a run never edits either. The run follows its working copy, ${O(e.paths.workingCopy)}, ${e.adaptation===`adaptive`?`which changes only through amendments made as ${O(`LEAD.md`)} § "Adapting the graph" describes`:`which stays as it was copied`}.`))}function Ke(e,t,n={}){let r=pe(e,n),i=R(r),a={[r.paths.graph]:y(e),[r.paths.lead]:he(r,t),[r.paths.mapping]:We(r),[r.paths.kickoff]:i,[r.paths.skill]:Ge(r)};for(let e of r.agents)a[e.file]=ae(r,e);let o={};for(let e of Object.keys(a).sort())o[e]=a[e];return{files:o,kickoff:i}}function U(e){let t=``;for(let n=0;n<e.length;n+=1){let r=e.charCodeAt(n);if(r>=55296&&r<=56319){let r=e.charCodeAt(n+1);t+=r>=56320&&r<=57343?e[n]+e[++n]:`�`}else t+=r>=56320&&r<=57343?`�`:e[n]}return JSON.stringify(t).replace(/\u007f/g,`\\u007f`)}function qe(e,t){let n=w(...Je(e,t));return C(`name = ${U(t.agentName)}`,`description = ${U(`${t.role} for graph ${e.graphId}. ${A(t.node.brief)}`)}`,...t.model?[`model = ${U(t.model)}`]:[],...t.effort?[`model_reasoning_effort = ${U(t.effort)}`]:[],`sandbox_mode = ${U(t.sandbox)}`,`web_search = ${U(t.web)}`,`developer_instructions = ${U(n)}`,``)}function Je(e,t){let n=t.node,r=e.index.incoming.get(n.id)??[],i=e.index.outgoing.get(n.id)??[],a=[...new Set(r.flatMap(e=>e.evidence??[]))];return[C(`# ${n.name}`,``,`Node ${O(n.id)} in the grooph graph ${O(e.graphId)} (${e.doc.name}). The lead dispatches you and you report back to the lead — you do not dispatch graph nodes yourself, and you do not update the run's progress log.`),C(`## Brief`,``,n.brief),...n.description?[C(`## Context`,``,n.description)]:[],C(`## Inputs`,``,...n.inputs&&n.inputs.length>0?n.inputs.map(T):[T(`None declared: work from the prompt the lead gives you.`)]),C(`## Outputs`,``,`Leave all of these behind before you report:`,``,...n.outputs.map(T),...W(e,t)?[``,`Write them yourself. With ${O(`write-outputs`)} you may create or overwrite only the files you declare in these outputs, and no other file.`]:[]),Ye(t),Xe(e,t,a),Ze(e,t),Qe(t,i)]}function W(e,t){let n=t.node.allow??e.profile.defaultCapabilities;return n.includes(`write-outputs`)&&!n.includes(`edit-files`)}function Ye(e){let t=e.node.owns??[];return C(`## Ownership`,``,...t.length>0?[`You are the only node in this run that writes ${t.map(O).join(`, `)}. Nothing else touches them while you work; if you need a change elsewhere, say so in your report instead of making it.`]:[`You own no artifact in this graph. Do not write over another node's files: report what should change and let the lead route it.`])}function Xe(t,n,r){let i=n.node.inputs&&n.node.inputs.length>0?`your declared inputs (Inputs above)`:`your declared inputs`,a=n.isWriter?`, which for you includes the project you are changing`:``,o=e(t.index,n.node.id).some(e=>(e.stops??[]).some(e=>e.kind===`evidence-invalid`));return C(`## Evidence rules`,``,...r.length>0?[`You may inspect what the lead hands you — the evidence below — plus ${i}${a}, and nothing else:`,``,...r.map(T),``,n.isCritic?`If any of it is missing or unreadable, do not guess and do not substitute your own reading of the repository: report ${O(`invalid-evidence`)} and say which item you could not read.${o?` That round counts toward the loop's evidence stop.`:``}`:`If any of it is missing or unreadable, say so in your report rather than guessing.`]:[`No inbound edge lists evidence for you. Work from ${i}${n.isWriter?`, the project you are changing`:``} and the lead's prompt, and nothing else; say so in your report if something you need is missing.`],...n.isCritic?[``,`You judge; you do not fix. Report findings instead of editing.${W(t,n)?` Your own outputs are the only files you write.`:``} Cite a file and a line for every claim you make: an adjective is not a finding.`]:[])}function Ze(e,t){let n=t.node.allow??e.profile.defaultCapabilities,r=t.node.deny??[];return C(`## Capabilities and sandbox`,``,T(`Allowed: ${n.map(O).join(`, `)||`none`}. Use only these capabilities; the presence of a tool does not grant permission to use it.`),T(`Denied: ${r.map(O).join(`, `)||`none`}. A denied capability stays denied; write-outputs, when explicitly allowed, permits only your declared output files even when edit-files is denied.`),T(`Requested sandbox: ${O(t.sandbox)}; web search: ${O(t.web)}. This file sets no approval policy, and this package does not know which one applies. Assume no person is asked before a command of yours runs: run only what your capabilities allow. Parent sandbox settings may supersede this agent's settings. A refused command returns a failure to the model, not a native graph halt: report the refusal and stop if required approval is unavailable. These coarse controls do not enforce file-by-file ownership, declared-output scope, shell command scope, or no delegation: those remain instructions.`),T(`Do not use commands to bypass a capability limit. read-files permits non-mutating read, list and search commands when Codex exposes file reading through a shell; use them only for declared inputs and evidence. write-outputs permits writing only the declared output files. run-tests permits only the declared test command. All other command execution requires run-commands. Do not execute commands outside those allowed purposes. Do not spawn other agents unless spawn-agents is expressly allowed and the lead has delegated that decomposition; report every child dispatch to the lead so it can count it.`),...(t.node.skills??[]).length>0?[``,`Skills required by the graph: ${t.node.skills.map(O).join(`, `)}. Explicitly invoke each applicable installed skill and read it before using it. There is no emitted skills preload field. If a required skill is unavailable, report the missing skill and stop; never pretend it was loaded.`]:[],...t.unmappedAllow.length>0||t.unmappedDeny.length>0?[``,`Custom capabilities have no native mapping: ${[...t.unmappedAllow.map(e=>`allow ${e}`),...t.unmappedDeny.map(e=>`deny ${e}`)].join(`, `)}. If the task depends on one whose meaning or enforcement is unavailable, report that limit and stop.`]:[],...t.modelSource===`unset`?[``,`No model is set for this node; use the session's subagent defaults. An explicit effort, when present, still applies.`]:[])}function Qe(e,t){let n=[];for(let e of t){let t=c(e),r=typeof t==`string`?t===`always`?`done`:t:t.verdict;n.includes(r)||n.push(r)}return n.length===0&&n.push(`done`),e.isCritic&&!n.includes(`invalid-evidence`)&&n.push(`invalid-evidence`),C(`## Report format`,``,`End your reply with exactly this block, and keep it short — the lead routes on the verdict line:`,``,E(C(`verdict: ${n.join(` | `)}`,`outputs:`,...e.node.outputs.map(e=>` - ${e} — <where you left it>`),...e.isCritic?[`findings:`,` - <file>:<line> — <what is wrong, or the item it satisfies>`]:[`changes:`,` - <what you changed this round, and why>`],`summary: <at most five lines>`),`text`),``,...n.includes(`pass`)||n.includes(`fail`)?[`The lead takes the edge that matches your verdict, so do not hedge: ${O(`pass`)} means every acceptance condition is met, ${O(`fail`)} means at least one is not.`]:[`The lead continues on ${O(n[0])} whatever the content of your report.`])}function $e(e,t={}){let n=g(e),r=S(`codex`),a=Object.entries(t.models??{}).filter(([,e])=>typeof e==`string`&&e!==``),o=a.length>0?{...r,models:{...r.models,...Object.fromEntries(a)}}:r,s=e.id,c=`.grooph/${s}`,l=(e.nodes??[]).find(e=>e.kind===`agent`&&e.role===`lead`),u=(e.nodes??[]).filter(e=>e.kind===`agent`).filter(e=>e.id!==l?.id).map(e=>et(e,o,s));return{doc:e,index:n,profile:o,graphId:s,root:c,paths:{graph:`${c}/graph.grooph.json`,lead:`${c}/LEAD.md`,mapping:`${c}/MAPPING.md`,kickoff:`${c}/KICKOFF.md`,runs:`${c}/runs`,progress:`${c}/runs/<run-id>/PROGRESS.md`,notes:`${c}/runs/<run-id>/notes.jsonl`,workingCopy:`${c}/runs/<run-id>/graph.grooph.json`},adaptation:i(e),tiersNamed:a.map(([e])=>e),agents:u,agentByNode:new Map(u.map(e=>[e.node.id,e])),...l?{leadNode:l}:{}}}function et(e,t,n){let r=`${n}--${e.id}`,i=e.model?.pin?.[t.harness],a=i??(e.model?t.models[e.model.tier]:void 0),o=i?`pin`:e.model?`tier`:`unset`,s=e.allow??t.defaultCapabilities;return{node:e,agentName:r,file:`.codex/agents/${r}.toml`,role:f(e),isCritic:l(e),isWriter:u(e),...a?{model:a}:{},modelSource:o,...e.effort?{effort:t.effort[e.effort]}:{},sandbox:s.includes(`edit-files`)&&!(e.deny??[]).includes(`edit-files`)||s.includes(`write-outputs`)&&!(e.deny??[]).includes(`write-outputs`)?`workspace-write`:`read-only`,web:s.includes(`web`)&&!(e.deny??[]).includes(`web`)?`live`:`disabled`,unmappedAllow:G(s,t),unmappedDeny:G(e.deny??[],t)}}var G=(e,t)=>e.filter(e=>!Object.hasOwn(t.capabilityTools,e));function tt(e){switch(e.adaptation){case`adaptive`:return`- When the work shows the graph is wrong, amend the working copy as ${O(`LEAD.md`)} § "Adapting the graph" says: visibly, with a note, and never loosening a brake. Never write the source document.`;case`propose`:return`- When the graph looks wrong, record a proposal note (${O(`LEAD.md`)} § "Adapting the graph") and change nothing.`;case`fixed`:return`- Follow the graph exactly; when you cannot, halt and ask (${O(`LEAD.md`)} § "Adapting the graph").`}}function nt(e){let t=_(e.index),n=e.agents.map(e=>O(e.agentName)).join(`, `);return w(C(`Run the grooph graph ${O(e.graphId)} (${e.doc.name}) in this Codex project. You are the lead.`,``,`Read ${O(e.paths.lead)} first and follow it${e.adaptation===`fixed`?` exactly`:``}. It is the brief for this run; this prompt is only the trigger.`),C(`**Goal.**`,``,e.doc.goal??`(the graph states no goal)`),C(`**Before you touch anything:**`,``,`1. If an explicit run id and human gate answer were supplied, resume that run as LEAD.md §3 says instead of creating or copying a second run. Otherwise read a run id from the clock (${O(`date -u +%Y%m%d-%H%M%S`)}, the form ${O(e.profile.runIdFormat)}), create ${O(`${e.paths.runs}/<run-id>/`)}, and copy ${O(e.paths.graph)} into it: that copy is the run's working copy.`,`2. Write ${O(`PROGRESS.md`)} and start ${O(`notes.jsonl`)} there, as ${O(`LEAD.md`)} § "Run setup" and § "Progress and notes" describe. Append a note for every node run and every loop round — that file is the record of the run.`,`3. Start at ${t.map(O).join(`, `)||`the first node`}.`),C(`**While you run:**`,``,`- Dispatch each agent node as its own subagent with the ${O(`spawn_agent`)} tool with ${O(`fork_turns: "none"`)} and the named ${O(`agent_type`)}: ${n||`(this graph has no agent nodes)`}. Do not do their work yourself, and do not grade work a critic node is there to grade.`,`- Give a fresh worker only its task, its declared inputs and the evidence its edge lists. Never paste a transcript into one. If fresh-history exclusion cannot be requested or confirmed by this harness, halt before dispatching; do not silently omit it.`,`- If a custom role or the subagent tools are unavailable, append a halt note and report the missing setup; never substitute yourself for a worker. Close completed fresh workers before dispatching another round to free the concurrency slots.`,`- Evaluate loop stops at the end of every pass before taking a back edge, in the order ${O(`LEAD.md`)} lists them, and record the round.`,`- At a human gate: append the halt note first, then ask, then end your turn (${O(`LEAD.md`)} § "Human gates"). A run nobody answers ends there, and the same run id resumes it.`,tt(e)),`**When the run ends** — a stop fires, you reach a stop node, or no edge is left to take — append the final note, write the last ${O(`PROGRESS.md`)}, and reply with the run id, the number of rounds, which stop ended the run, and anything left over.`)}function rt(e,t){return w(it(e),at(e),ot(e),ct(e),ut(e),mt(e),_t(e),Ct(e),wt(e),kt(e),At(t),Y(e))}var it=e=>C(`# Lead brief · ${e.doc.name}`,``,`Graph ${O(e.graphId)} v${e.doc.version} · target ${O(`codex`)} · compiled by grooph from ${O(e.paths.graph)}.`,``,`This file is generated. Edit the graph document and export again, or hand-edit and record what you changed — the next export overwrites it.`);function at(e){let t=e.leadNode?.brief.trim();return C(`## 1. You are the lead`,``,...e.leadNode?[`Lead node ${O(e.leadNode.id)}: model ${O(e.leadNode.model?.pin?.codex??(e.leadNode.model?e.profile.models[e.leadNode.model.tier]:`session default`))}, effort ${O(e.leadNode.effort?e.profile.effort[e.leadNode.effort]:`session default`)}. These are startup settings: if this session does not match, halt and ask to restart with them. Its inputs: ${(e.leadNode.inputs??[]).map(O).join(`, `)||`none`}; outputs: ${e.leadNode.outputs.map(O).join(`, `)}; owns: ${(e.leadNode.owns??[]).map(O).join(`, `)||`none`}.`,``]:[],...e.leadNode?[`Its allowed capabilities: ${(e.leadNode.allow??e.profile.defaultCapabilities).map(O).join(`, `)||`none`}; denied: ${(e.leadNode.deny??[]).map(O).join(`, `)||`none`}; required skills: ${(e.leadNode.skills??[]).map(O).join(`, `)||`none`}. Respect these in the lead node's own work; run coordination and notes still follow the contract below. Load its applicable installed skills before using them; if a required skill is missing, halt.`,``]:[],...t?[t,``]:[],C(`You run this graph. The main session is the executive: you dispatch nodes, follow edges, count rounds, evaluate stops, keep the progress log, and stop for the human when the graph says to.`,``,"You do not do the workers' jobs. Every agent node below runs as its own subagent through the `spawn_agent` tool, with `fork_turns: none`, and sees only its brief, its declared inputs and the evidence its inbound edge allows. You never grade your own work while a critic node exists: the critic's verdict is the one that counts, and you do not overrule it by re-reading the diff yourself."))}function ot(e){let t=e.doc.constraints??{},n=[];return t.budget&&n.push(T(`**Budget.** ${t.budget}`)),t.time&&n.push(T(`**Time.** ${t.time}`)),t.other&&n.push(T(`**Other.** ${t.other}`)),C(`## 2. Goal and constraints`,``,`**Goal.**`,``,e.doc.goal??`(none stated)`,...n.length>0?[``,`**Constraints.**`,``,...n]:[],...e.doc.description?[``,`**What this graph does.**`,``,e.doc.description]:[])}var K=`20260917-093002`,st=`2026-09-17T09:30:02Z`;function ct(e){let t=JSON.stringify({id:`n-0001`,run:K,at:`graph`,started:st,text:`run started`}),n=q(e);return C(`## 3. Run setup`,``,`1. Read the run id from the clock, in the form ${O(e.profile.runIdFormat)} (UTC): ${O(`date -u +%Y%m%d-%H%M%S`)}, for example ${O(K)}. If ${O(`${e.paths.runs}/<that id>/`)} already exists, append ${O(`-2`)}, then ${O(`-3`)}, and so on. Never make an id up.`,`2. Create ${O(`${e.paths.runs}/<run-id>/`)}.`,`3. Copy the source document ${O(e.paths.graph)} into it as ${O(e.paths.workingCopy)}. That copy is the run's working copy: the graph this run follows, ${e.adaptation===`adaptive`?`and the only copy you may amend (§9)`:`left as copied (§9)`}. Never write the source document.`,`4. Write ${O(`PROGRESS.md`)} there before dispatching anything: the run id, the goal, every node with status ${O(`pending`)}, and the round counter at 0${n.length>0?` (with the dispatch counter of ${n.map(e=>O(e.id)).join(`, `)} at 0, §6)`:``}.`,`5. Create ${O(`notes.jsonl`)} beside it and append the first line, its ${O(`started`)} read from ${O(`date -u +%Y-%m-%dT%H:%M:%SZ`)}:`,``,E(t,`json`),``,`6. If you were given a run id to resume, do not start a second run: read that folder's ${O(`PROGRESS.md`)} and working copy, continue from the last recorded position, and keep appending to the same ${O(`notes.jsonl`)}. Do not copy the source over the working copy again.`,``,`Entry nodes (start here): ${_(e.index).map(O).join(`, `)||`none — the graph has no entry node`}.`)}var q=e=>(e.doc.loops??[]).filter(e=>(e.stops??[]).some(e=>e.kind===`budget`&&e.measure===`dispatches`)),lt=e=>(e.doc.loops??[]).filter(e=>(e.stops??[]).some(e=>e.kind===`evidence-invalid`));function ut(e){return C(`## 4. Nodes`,``,D([`node`,`name`,`how you run it`,`role`,`what it returns`],(e.doc.nodes??[]).map(t=>[O(t.id),k(t.name),dt(e,t),k(ft(t)),k(pt(t))])),``,`Dispatch an agent node with the ${O(`spawn_agent`)} tool and the ${O(`agent_type`)} named above; its TOML file under ${O(`.codex/agents/`)} carries the full brief. Set ${O(`fork_turns: "none"`)} for fresh edges; wait for its result before routing. If fresh-history exclusion cannot be requested or confirmed by this harness, halt before dispatching; do not silently omit it. If the named custom role or subagent tools are unavailable, append a halt note and ask for a correctly configured fresh session, never do the worker job yourself. Your prompt carries only the task, the declared inputs and the edge's evidence. Never paste a transcript into a fresh worker.`,z(e.doc))}function dt(e,t){let n=e.agentByNode.get(t.id);if(n)return`${O(`spawn_agent`)} · ${O(n.agentName)}`;switch(t.kind){case`agent`:return`you — this is the lead node`;case`human-gate`:return`you ask the human`;case`check`:return t.check.run?`you run ${O(t.check.run)}`:`you judge the stated condition`;case`merge`:return`you merge the listed artifacts`;case`stop`:return`you end the run`;default:return`you`}}var ft=e=>e.kind===`agent`?typeof e.role==`string`?e.role:`custom: ${e.role.custom}`:e.kind;function pt(e){switch(e.kind){case`agent`:return e.outputs.join(`; `);case`human-gate`:return e.options&&e.options.length>0?e.options.join(` | `):`a human answer`;case`check`:return`pass when: ${e.check.pass}`;case`merge`:return`merged: ${e.merges.join(`, `)}`;case`stop`:return`run ends with outcome ${e.outcome??`success`}`;default:return``}}function mt(e){let n=e.doc.edges??[],r=lt(e),i=n.some(e=>(e.evidence??[]).some(e=>/\bdiff\b/i.test(e)));return C(`## 5. Edges`,``,D([`edge`,`route`,`taken when`,`isolation`,`evidence and gates`],n.map(e=>[O(e.id),`${O(e.from)} → ${O(e.to)}`,k(t(e)),s(e),k(gt(e))])),``,T(`Respect every scoped policy and edge retry limit. A concurrency-cap policy and every applicable edge cap limit the active workers together; choose the smallest applicable cap. Count retries and never exceed retry.max.`),T(`When a node finishes, take every outgoing edge whose condition matches its result. Several matching edges run in parallel, capped by any ${O(`concurrency`)} on the edge.`),T(`${O(`fresh`)} isolation: the worker starts with no context except its brief, its declared inputs and the evidence listed above. ${O(`shared`)}: continue the same worker if the build lets you, otherwise halt and report the unavailable continuation. Never perform a critic's step in the lead context.`),T(`A worker may inspect what its inbound edge lists plus its own declared inputs; for a writer that includes the project it is changing. A critic that cannot read its evidence reports ${O(`invalid-evidence`)} instead of guessing.`),T(`When an edge routes ${O(`invalid-evidence`)}, take it. Otherwise repair the evidence and dispatch the same node once more in the same round; a second ${O(`invalid-evidence`)} routes as ${O(`fail`)}.${r.length>0?` Such rounds count toward the ${O(`evidence-invalid`)} stop of loop ${r.map(e=>O(e.id)).join(`, `)}.`:``}`),...i?[T(ht)]:[])}var ht=`A diff of the change is ${O(`git diff`)} plus, for each file the change added, ${O(`git diff --no-index /dev/null <file>`)} (${O(`git diff`)} omits untracked files; ${O(`--no-index`)} exits 1 whenever the two differ, which is not an error). Run each bare from the project root, one command at a time; no brace group, no ${O(`cd`)}. ${O(`git add -N <file>`)} also works where it is allowed, and stages nothing.`;function gt(e){let t=[];return e.evidence&&e.evidence.length>0?t.push(`evidence: ${e.evidence.join(`; `)}`):t.push(`evidence: none listed`),e.approval&&t.push(`**human approval required before traversal**`),e.concurrency&&t.push(`concurrency max ${e.concurrency.max}`),e.retry&&t.push(`retry max ${e.retry.max}`),e.label&&t.push(`label: ${e.label}`),t.join(` · `)}function _t(e){let t=e.doc.loops??[];return t.length===0?C(`## 6. Loops`,``,`This graph has no loops. Follow the edges once and stop.`):C(`## 6. Loops`,``,t.map(t=>vt(e,t).replace(/\n+$/,``)).join(`
|
|
9
|
+
`)}]`,`json`)]}function Ve(e){let t=e.adaptation,n=(e.doc.policies??[]).filter(e=>e.kind===`no-live-graph-rewrite`),r=n.find(e=>e.scope===`graph`),i=r&&e.doc.adaptation!==t?` (its policy ${O(r.id)}, ${O(`no-live-graph-rewrite`)}, is stricter than ${O(`adaptation: ${e.doc.adaptation??`adaptive`}`)})`:e.doc.adaptation===void 0?` (the default)`:``,a=`## 9. Adapting the graph`,o=`The working copy ${O(e.paths.workingCopy)} stays identical to the source document ${O(e.paths.graph)}: neither is written during this run.`;if(t===`fixed`)return C(a,``,`This graph is ${O(`fixed`)}${i}: follow it exactly. Do not add, remove or re-brief nodes, re-route edges, or change loops, tiers or effort — not even to tighten a brake.`,``,`When the work cannot go on within the graph as written, halt and ask: append a note with ${O(`"outcome":"halt"`)} that says what the graph is missing, write the final ${O(`PROGRESS.md`)}, and tell the human. A ${O(`proposal`)} note alongside is welcome; the run does not continue on it.`,``,o);if(t===`propose`)return C(a,``,`This graph is ${O(`propose`)}${i}: you change nothing in it during the run, not even to tighten a brake. Follow it as written.`,``,`When the work shows the graph is wrong — a missing node, a loop that should exist, a brief that no longer fits — append a note with a ${O(`proposal`)} (${O(`summary`)}, and a ${O(`patch`)} when one helps) and carry on with the graph as it is; the human decides after the run. If you cannot carry on without the change, halt and say why.`,``,ze,``,...H(),``,o);let s=JSON.stringify({id:`n-0009`,run:`<run-id>`,at:`graph`,amendment:{summary:`<what you changed>`,reason:`<what the work showed>`,patch:[{op:`updateNode`,id:`<node-id>`,set:{owns:[`<artifact>`]}}]}}),c=n.filter(e=>e.scope!==`graph`);return C(a,``,`This graph is ${O(`adaptive`)}${i}. It is the plan to start from, not a script: when the work shows it is wrong — a missing node, a loop that should exist, a brief that no longer fits — change the run's working copy rather than work around it. When the graph fits, follow it. Work that fits an existing node's brief and outputs needs no amendment, and the smallest change that closes a real gap is the right one.`,``,`Amending at kickoff is fine when reading the task already shows a gap, such as a file a node must write that its ${O(`owns`)} does not list. Redesigning the graph up front is not: a change to its overall shape before any node has run is a ${O(`proposal`)} for the human.`,``,`You may add, remove or re-brief nodes, add or re-route edges, add loops, and change tiers or effort. For each amendment, when you make it:`,``,`1. Edit the working copy, ${O(e.paths.workingCopy)}. The source document ${O(e.paths.graph)} is never written by a run; after the run the human adopts your working copy as a new version or discards it.`,`2. Append a note with an ${O(`amendment`)} — ${O(`summary`)}, ${O(`reason`)}, and a ${O(`patch`)} when one helps. A patch is preferably a list of grooph ops, the JSON ${O(`grooph apply --ops`)} takes: ops name objects by id, so they survive reordering and can be replayed. The working copy is the record either way.`,``,E(s,`json`),``,`3. Record it in ${O(`PROGRESS.md`)} under **Amendments**, so the human can see the graph the run is actually following.`,`4. Check that the working copy still validates: run ${O(`grooph validate --for-export ${e.paths.workingCopy}`)} when ${O(`grooph`)} is on your PATH; otherwise check the brakes below by hand.`,`5. When the amendment changes a node's ${O(`allow`)} or ${O(`deny`)}, also edit that node's file under ${O(`.claude/agents/`)} before you dispatch it: its ${O(`tools:`)} line (and ${O(`disallowedTools:`)}), with the capability-to-tools table in ${O(e.paths.mapping)}. Claude Code reads the edited file at the next dispatch; no restart is needed. Say in the amendment note that you edited it. If the file cannot be edited, the change is a ${O(`proposal`)}: record it as one and dispatch the node as compiled, never a stand-in.`,``,...H(),``,`At every adaptation level you may not remove or loosen:`,``,...Re.map(T),``,`You may tighten any of them. Loosening one is a ${O(`proposal`)} note for the human, never an amendment. A loop you add needs a stop, and a bar if it is a judgment loop, like any other.${c.length>0?` Policy ${c.map(e=>`${O(e.id)} (scope ${O(e.scope)})`).join(`, `)} forbids live rewrites where it applies: for anything it covers, record a proposal instead.`:``}`,``,`A node you add mid-run has no compiled file under ${O(`.claude/agents/`)}. Write one beside the others, in the shape of an existing one, and dispatch it by that name (the file is read at the next dispatch), or dispatch it as a general-purpose subagent with its brief inline. Either way it works under the same isolation and evidence rules as every other node.`)}function He(e){return C(`## 10. Validation warnings`,``,...e.length===0?[`The document validated clean for export: no warnings.`]:[`grooph raised these when compiling this package. They are not errors, and the human running this graph should see them:`,``,E(e.map(y).join(`
|
|
10
|
+
`),`text`)])}function Ue(e){let t=(e.doc.nodes??[]).filter(e=>e.kind===`stop`),n=e.adaptation===`adaptive`;return C(`## 11. Ending`,``,`The run ends when you reach a stop node, when a stop fires and its action is to halt, or when no edge is left to take. A gate is different: the halt note of §7 stands as the final note until the human answers, and the run continues from it.`,``,...t.length>0?[`Stop nodes: ${t.map(e=>`${O(e.id)} (${e.kind===`stop`?e.outcome??`success`:``})`).join(`, `)}.`,``]:[`This graph has no stop node: the run ends when no edge is left to take.`,``],`Whichever way it ends, do all three:`,``,`1. Append one short line first, ${O(`{"at":"graph","outcome":"ending"}`)} with a ${O(`text`)} naming how the run ends, then the final note: ${O(`"at":"graph"`)} with the outcome and a ${O(`text`)} that names the stop that fired or the stop node reached. The ${O(`ending`)} line is how a monitor tells a run that finished from one that was cut off while finishing.`,`2. Write the last ${O(`PROGRESS.md`)}: which nodes ran, how many rounds, ${n?`every amendment to the working copy, `:``}and why the run ended.`,`3. Tell the human, in your reply, the run id, the rounds, the stop that ended the run, ${n?`whether the working copy was amended (so they can adopt or discard it), `:``}and what is left over.`,``,`The final note and ${O(`PROGRESS.md`)} are the record. Your last reply is the report: it summarizes them for whoever started this session and points at the run folder, ${O(`${e.paths.runs}/<run-id>/`)}.`)}function We(e){let t=[[`graph ${O(e.graphId)}`,O(e.paths.graph),`the source document in canonical form; the only thing grooph reads back`],[`lead brief`,O(e.paths.lead),`goal, nodes, edges, loops, gates, progress contract, warnings`],[`kickoff`,O(e.paths.kickoff),`the prompt to paste when the skill is not loaded`],[`kickoff (skill)`,O(e.paths.skill),`${O(`/${e.graphId}`)} starts or resumes a run`],[`mapping notes`,O(e.paths.mapping),`this file`],[`progress`,O(`${e.paths.runs}/<run-id>/PROGRESS.md`),`written at run time, after every node`],[`run notes`,O(`${e.paths.runs}/<run-id>/notes.jsonl`),`written at run time, one JSON object per line (graph-ir §6)`],[`working copy`,O(e.paths.workingCopy),e.adaptation===`adaptive`?`copied from the source at run setup; the lead amends it, with a note per amendment, when the work shows the graph is wrong`:`copied from the source at run setup; left unchanged (${e.adaptation})`],...e.agents.map(e=>[`node ${O(e.node.id)}`,O(e.file),`subagent ${O(e.agentName)} · ${e.role} · model ${e.model??`session default`} · effort ${e.effort??`session default`}`])],n=(e.doc.nodes??[]).filter(e=>e.kind!==`agent`),r=e.agents.filter(e=>(e.node.skills??[]).length>0);return w(C(`# Mapping notes · ${e.doc.name}`,``,`How this package's files correspond to the graph document, so a human can hand-adjust without re-running grooph. Target profile: Claude Code, verified against ${O(e.profile.verifiedAgainst)} on ${e.profile.verifiedOn}.`),C(`## Graph piece → file`,``,D([`graph piece`,`file`,`what it carries`],t)),C(`## Pieces with no file of their own`,``,...n.length>0?["These live inside `LEAD.md`, because the lead performs them itself:",``,...n.map(e=>`- ${O(e.id)} (${e.kind}) — ${e.kind===`check`?`the lead runs the check and judges the stated condition`:e.kind===`human-gate`?`the lead asks the human and waits`:e.kind===`merge`?`the lead merges the listed artifacts`:`the lead ends the run here`}`)]:[`Every node in this graph is an agent node with its own file.`],``,`Edges, loops and policies have no file: they are the routing, round and stop rules in ${O(`LEAD.md`)} §5–§7. ${(e.doc.policies??[]).length>0?`Policies in force: ${(e.doc.policies??[]).map(e=>`${O(e.id)} (${typeof e.kind==`string`?e.kind:e.kind.custom}, scope ${e.scope})`).join(`, `)}.`:`This graph declares no policies.`}`),C(`## The three things people hand-edit`,``,`**A node's model or effort.** Change the frontmatter of its agent file:`,``,E(C(`# ${e.agents[0]?.file??`.claude/agents/<graph-id>--<node-id>.md`}`,`model: ${e.agents[0]?.model??`opus`} # ${e.tiersNamed.length>0?`this export`:`profile`}: frontier → ${e.profile.models.frontier}, strong → ${e.profile.models.strong}, fast → ${e.profile.models.fast}`,`effort: ${e.agents[0]?.effort??`high`} # low | medium | high | max`),`yaml`),``,`The durable place for that change is ${O(`model.tier`)} or ${O(`effort`)} on the node in the graph document; edit the file only for a one-off run, because the next export overwrites it.`,``,`**A node's tools.** The ${O(`tools:`)} line of the same frontmatter (and ${O(`disallowedTools:`)}), which is the node's ${O(`allow`)} (and ${O(`deny`)}) through this table:`,``,D([`capability`,`tools`],Object.entries(e.profile.capabilityTools).map(([e,t])=>[O(e),t.map(O).join(`, `)])),``,`The durable place is ${O(`allow`)} or ${O(`deny`)} on the node in the graph document.${e.adaptation===`adaptive`?` A running lead edits ${O(`tools:`)} itself when it amends a node's capabilities (${O(e.paths.lead)} §9); Claude Code reads the edited file at the node's next dispatch, without a restart.`:``}`,``,`The ${O(`skills:`)} line of the same frontmatter is hand-editable the same way: harness skill names, preloaded at the node's dispatch, from ${O(`skills`)} on the node in the graph document. An unknown name is refused by Claude Code, not by grooph.${r.length>0?` In this package: ${r.map(e=>`${O(e.file)} (${e.node.skills.join(`, `)})`).join(`, `)}.`:` No node in this graph names one.`}`,``,`**A loop's stop values.** The numbers a run actually bumps into:`,``,...(e.doc.loops??[]).map(t=>C(`- ${O(t.id)} (${g(e.index,t)}): ${(t.stops??[]).map(e=>e.kind===`max-iterations`?`${O(`max-iterations n=${e.n}`)}`:e.kind===`budget`?`${O(`budget ${e.limit} ${e.measure}`)}`:O(e.kind)).join(`, `)||`no stops declared`} — edit them in ${O(`${e.paths.lead}`)} §6 for this run, or in the graph document to keep them.`))),C(`## Rules this package relies on`,``,`- The subagent files must sit in ${O(`.claude/agents/`)} of the project the session runs in; the package is discovered from the project directory, not from a flag.`,`- Subagent names cannot contain a colon, which is why they read ${O(`<graph-id>--<node-id>`)}.`,`- ${k("A `dispatches` budget is exact: the lead counts node dispatches in `PROGRESS.md`. `usd`, `turns` and `tokens` budgets are advisory inside a session; a `usd` budget is enforced only from outside, by starting a headless run with `--max-budget-usd`.")}`,`- ${k("Commands run bare from the project root: an allowlist matches a command's prefix, so a compound form (`cd … && …`) or `git -C <path>` is refused under a narrow allowlist and costs a turn each time.")}`,`- A run never writes ${O(e.paths.graph)}. Adopt a run's working copy as the next version of the graph, or discard it; either way that is a human decision after the run.`,`- Nothing here executes the graph. grooph compiles; the session is the runtime.`))}function Ge(e){return w(C(`---`,`name: ${N([e.graphId])}`,`description: ${M(`Start or resume a grooph run of the ${e.graphId} graph (${e.doc.name}). Invoked by the human, never on its own.`)}`,`disable-model-invocation: true`,`argument-hint: [run-id to resume]`,`---`),C(`# ${e.doc.name}`,``,`Read ${O(e.paths.lead)} now and follow it. You are the lead for this run: dispatch the graph's nodes, follow its edges, count rounds, evaluate stops, and keep ${O(`${e.paths.runs}/<run-id>/`)} current.`),C(`- No argument: start a new run and choose a run id.`,`- An argument: resume that run id — read its ${O(`PROGRESS.md`)}, continue where it stopped, keep appending to the same ${O(`notes.jsonl`)}.`),C(`The source graph document is ${O(e.paths.graph)} and the mapping from graph to files is ${O(e.paths.mapping)}; a run never edits either. The run follows its working copy, ${O(e.paths.workingCopy)}, ${e.adaptation===`adaptive`?`which changes only through amendments made as ${O(`LEAD.md`)} § "Adapting the graph" describes`:`which stays as it was copied`}.`))}function Ke(e,t,n={}){let r=pe(e,n),i=R(r),a={[r.paths.graph]:d(e),[r.paths.lead]:he(r,t),[r.paths.mapping]:We(r),[r.paths.kickoff]:i,[r.paths.skill]:Ge(r)};for(let e of r.agents)a[e.file]=ae(r,e);let o={};for(let e of Object.keys(a).sort())o[e]=a[e];return{files:o,kickoff:i}}function U(e){let t=``;for(let n=0;n<e.length;n+=1){let r=e.charCodeAt(n);if(r>=55296&&r<=56319){let r=e.charCodeAt(n+1);t+=r>=56320&&r<=57343?e[n]+e[++n]:`�`}else t+=r>=56320&&r<=57343?`�`:e[n]}return JSON.stringify(t).replace(/\u007f/g,`\\u007f`)}function qe(e,t){let n=w(...Je(e,t));return C(`name = ${U(t.agentName)}`,`description = ${U(`${t.role} for graph ${e.graphId}. ${A(t.node.brief)}`)}`,...t.model?[`model = ${U(t.model)}`]:[],...t.effort?[`model_reasoning_effort = ${U(t.effort)}`]:[],`sandbox_mode = ${U(t.sandbox)}`,`web_search = ${U(t.web)}`,`developer_instructions = ${U(n)}`,``)}function Je(e,t){let n=t.node,r=e.index.incoming.get(n.id)??[],i=e.index.outgoing.get(n.id)??[],a=[...new Set(r.flatMap(e=>e.evidence??[]))];return[C(`# ${n.name}`,``,`Node ${O(n.id)} in the grooph graph ${O(e.graphId)} (${e.doc.name}). The lead dispatches you and you report back to the lead — you do not dispatch graph nodes yourself, and you do not update the run's progress log.`),C(`## Brief`,``,n.brief),...n.description?[C(`## Context`,``,n.description)]:[],C(`## Inputs`,``,...n.inputs&&n.inputs.length>0?n.inputs.map(T):[T(`None declared: work from the prompt the lead gives you.`)]),C(`## Outputs`,``,`Leave all of these behind before you report:`,``,...n.outputs.map(T),...W(e,t)?[``,`Write them yourself. With ${O(`write-outputs`)} you may create or overwrite only the files you declare in these outputs, and no other file.`]:[]),Ye(t),Xe(e,t,a),Ze(e,t),Qe(t,i)]}function W(e,t){let n=t.node.allow??e.profile.defaultCapabilities;return n.includes(`write-outputs`)&&!n.includes(`edit-files`)}function Ye(e){let t=e.node.owns??[];return C(`## Ownership`,``,...t.length>0?[`You are the only node in this run that writes ${t.map(O).join(`, `)}. Nothing else touches them while you work; if you need a change elsewhere, say so in your report instead of making it.`]:[`You own no artifact in this graph. Do not write over another node's files: report what should change and let the lead route it.`])}function Xe(e,t,n){let r=t.node.inputs&&t.node.inputs.length>0?`your declared inputs (Inputs above)`:`your declared inputs`,i=t.isWriter?`, which for you includes the project you are changing`:``,a=f(e.index,t.node.id).some(e=>(e.stops??[]).some(e=>e.kind===`evidence-invalid`));return C(`## Evidence rules`,``,...n.length>0?[`You may inspect what the lead hands you — the evidence below — plus ${r}${i}, and nothing else:`,``,...n.map(T),``,t.isCritic?`If any of it is missing or unreadable, do not guess and do not substitute your own reading of the repository: report ${O(`invalid-evidence`)} and say which item you could not read.${a?` That round counts toward the loop's evidence stop.`:``}`:`If any of it is missing or unreadable, say so in your report rather than guessing.`]:[`No inbound edge lists evidence for you. Work from ${r}${t.isWriter?`, the project you are changing`:``} and the lead's prompt, and nothing else; say so in your report if something you need is missing.`],...t.isCritic?[``,`You judge; you do not fix. Report findings instead of editing.${W(e,t)?` Your own outputs are the only files you write.`:``} Cite a file and a line for every claim you make: an adjective is not a finding.`]:[])}function Ze(e,t){let n=t.node.allow??e.profile.defaultCapabilities,r=t.node.deny??[];return C(`## Capabilities and sandbox`,``,T(`Allowed: ${n.map(O).join(`, `)||`none`}. Use only these capabilities; the presence of a tool does not grant permission to use it.`),T(`Denied: ${r.map(O).join(`, `)||`none`}. A denied capability stays denied; write-outputs, when explicitly allowed, permits only your declared output files even when edit-files is denied.`),T(`Requested sandbox: ${O(t.sandbox)}; web search: ${O(t.web)}. This file sets no approval policy, and this package does not know which one applies. Assume no person is asked before a command of yours runs: run only what your capabilities allow. Parent sandbox settings may supersede this agent's settings. A refused command returns a failure to the model, not a native graph halt: report the refusal and stop if required approval is unavailable. These coarse controls do not enforce file-by-file ownership, declared-output scope, shell command scope, or no delegation: those remain instructions.`),T(`Do not use commands to bypass a capability limit. read-files permits non-mutating read, list and search commands when Codex exposes file reading through a shell; use them only for declared inputs and evidence. write-outputs permits writing only the declared output files. run-tests permits only the declared test command. All other command execution requires run-commands. Do not execute commands outside those allowed purposes. Do not spawn other agents unless spawn-agents is expressly allowed and the lead has delegated that decomposition; report every child dispatch to the lead so it can count it.`),...(t.node.skills??[]).length>0?[``,`Skills required by the graph: ${t.node.skills.map(O).join(`, `)}. Explicitly invoke each applicable installed skill and read it before using it. There is no emitted skills preload field. If a required skill is unavailable, report the missing skill and stop; never pretend it was loaded.`]:[],...t.unmappedAllow.length>0||t.unmappedDeny.length>0?[``,`Custom capabilities have no native mapping: ${[...t.unmappedAllow.map(e=>`allow ${e}`),...t.unmappedDeny.map(e=>`deny ${e}`)].join(`, `)}. If the task depends on one whose meaning or enforcement is unavailable, report that limit and stop.`]:[],...t.modelSource===`unset`?[``,`No model is set for this node; use the session's subagent defaults. An explicit effort, when present, still applies.`]:[])}function Qe(e,t){let n=[];for(let e of t){let t=i(e),r=typeof t==`string`?t===`always`?`done`:t:t.verdict;n.includes(r)||n.push(r)}return n.length===0&&n.push(`done`),e.isCritic&&!n.includes(`invalid-evidence`)&&n.push(`invalid-evidence`),C(`## Report format`,``,`End your reply with exactly this block, and keep it short — the lead routes on the verdict line:`,``,E(C(`verdict: ${n.join(` | `)}`,`outputs:`,...e.node.outputs.map(e=>` - ${e} — <where you left it>`),...e.isCritic?[`findings:`,` - <file>:<line> — <what is wrong, or the item it satisfies>`]:[`changes:`,` - <what you changed this round, and why>`],`summary: <at most five lines>`),`text`),``,...n.includes(`pass`)||n.includes(`fail`)?[`The lead takes the edge that matches your verdict, so do not hedge: ${O(`pass`)} means every acceptance condition is met, ${O(`fail`)} means at least one is not.`]:[`The lead continues on ${O(n[0])} whatever the content of your report.`])}function $e(e,t={}){let n=l(e),r=S(`codex`),i=Object.entries(t.models??{}).filter(([,e])=>typeof e==`string`&&e!==``),o=i.length>0?{...r,models:{...r.models,...Object.fromEntries(i)}}:r,s=e.id,c=`.grooph/${s}`,u=(e.nodes??[]).find(e=>e.kind===`agent`&&e.role===`lead`),d=(e.nodes??[]).filter(e=>e.kind===`agent`).filter(e=>e.id!==u?.id).map(e=>et(e,o,s));return{doc:e,index:n,profile:o,graphId:s,root:c,paths:{graph:`${c}/graph.grooph.json`,lead:`${c}/LEAD.md`,mapping:`${c}/MAPPING.md`,kickoff:`${c}/KICKOFF.md`,runs:`${c}/runs`,progress:`${c}/runs/<run-id>/PROGRESS.md`,notes:`${c}/runs/<run-id>/notes.jsonl`,workingCopy:`${c}/runs/<run-id>/graph.grooph.json`},adaptation:a(e),tiersNamed:i.map(([e])=>e),agents:d,agentByNode:new Map(d.map(e=>[e.node.id,e])),...u?{leadNode:u}:{}}}function et(e,t,n){let r=`${n}--${e.id}`,i=e.model?.pin?.[t.harness],a=i??(e.model?t.models[e.model.tier]:void 0),o=i?`pin`:e.model?`tier`:`unset`,c=e.allow??t.defaultCapabilities;return{node:e,agentName:r,file:`.codex/agents/${r}.toml`,role:s(e),isCritic:u(e),isWriter:m(e),...a?{model:a}:{},modelSource:o,...e.effort?{effort:t.effort[e.effort]}:{},sandbox:c.includes(`edit-files`)&&!(e.deny??[]).includes(`edit-files`)||c.includes(`write-outputs`)&&!(e.deny??[]).includes(`write-outputs`)?`workspace-write`:`read-only`,web:c.includes(`web`)&&!(e.deny??[]).includes(`web`)?`live`:`disabled`,unmappedAllow:G(c,t),unmappedDeny:G(e.deny??[],t)}}var G=(e,t)=>e.filter(e=>!Object.hasOwn(t.capabilityTools,e));function tt(e){switch(e.adaptation){case`adaptive`:return`- When the work shows the graph is wrong, amend the working copy as ${O(`LEAD.md`)} § "Adapting the graph" says: visibly, with a note, and never loosening a brake. Never write the source document.`;case`propose`:return`- When the graph looks wrong, record a proposal note (${O(`LEAD.md`)} § "Adapting the graph") and change nothing.`;case`fixed`:return`- Follow the graph exactly; when you cannot, halt and ask (${O(`LEAD.md`)} § "Adapting the graph").`}}function nt(e){let t=n(e.index),r=e.agents.map(e=>O(e.agentName)).join(`, `);return w(C(`Run the grooph graph ${O(e.graphId)} (${e.doc.name}) in this Codex project. You are the lead.`,``,`Read ${O(e.paths.lead)} first and follow it${e.adaptation===`fixed`?` exactly`:``}. It is the brief for this run; this prompt is only the trigger.`),C(`**Goal.**`,``,e.doc.goal??`(the graph states no goal)`),C(`**Before you touch anything:**`,``,`1. If an explicit run id and human gate answer were supplied, resume that run as LEAD.md §3 says instead of creating or copying a second run. Otherwise read a run id from the clock (${O(`date -u +%Y%m%d-%H%M%S`)}, the form ${O(e.profile.runIdFormat)}), create ${O(`${e.paths.runs}/<run-id>/`)}, and copy ${O(e.paths.graph)} into it: that copy is the run's working copy.`,`2. Write ${O(`PROGRESS.md`)} and start ${O(`notes.jsonl`)} there, as ${O(`LEAD.md`)} § "Run setup" and § "Progress and notes" describe. Append a note for every node run and every loop round — that file is the record of the run.`,`3. Start at ${t.map(O).join(`, `)||`the first node`}.`),C(`**While you run:**`,``,`- Dispatch each agent node as its own subagent with the ${O(`spawn_agent`)} tool with ${O(`fork_turns: "none"`)} and the named ${O(`agent_type`)}: ${r||`(this graph has no agent nodes)`}. Do not do their work yourself, and do not grade work a critic node is there to grade.`,`- Give a fresh worker only its task, its declared inputs and the evidence its edge lists. Never paste a transcript into one. If fresh-history exclusion cannot be requested or confirmed by this harness, halt before dispatching; do not silently omit it.`,`- If a custom role or the subagent tools are unavailable, append a halt note and report the missing setup; never substitute yourself for a worker. Close completed fresh workers before dispatching another round to free the concurrency slots.`,`- Evaluate loop stops at the end of every pass before taking a back edge, in the order ${O(`LEAD.md`)} lists them, and record the round.`,`- At a human gate: append the halt note first, then ask, then end your turn (${O(`LEAD.md`)} § "Human gates"). A run nobody answers ends there, and the same run id resumes it.`,tt(e)),`**When the run ends** — a stop fires, you reach a stop node, or no edge is left to take — append the final note, write the last ${O(`PROGRESS.md`)}, and reply with the run id, the number of rounds, which stop ended the run, and anything left over.`)}function rt(e,t){return w(it(e),at(e),ot(e),ct(e),ut(e),mt(e),_t(e),Ct(e),wt(e),kt(e),At(t),Y(e))}var it=e=>C(`# Lead brief · ${e.doc.name}`,``,`Graph ${O(e.graphId)} v${e.doc.version} · target ${O(`codex`)} · compiled by grooph from ${O(e.paths.graph)}.`,``,`This file is generated. Edit the graph document and export again, or hand-edit and record what you changed — the next export overwrites it.`);function at(e){let t=e.leadNode?.brief.trim();return C(`## 1. You are the lead`,``,...e.leadNode?[`Lead node ${O(e.leadNode.id)}: model ${O(e.leadNode.model?.pin?.codex??(e.leadNode.model?e.profile.models[e.leadNode.model.tier]:`session default`))}, effort ${O(e.leadNode.effort?e.profile.effort[e.leadNode.effort]:`session default`)}. These are startup settings: if this session does not match, halt and ask to restart with them. Its inputs: ${(e.leadNode.inputs??[]).map(O).join(`, `)||`none`}; outputs: ${e.leadNode.outputs.map(O).join(`, `)}; owns: ${(e.leadNode.owns??[]).map(O).join(`, `)||`none`}.`,``]:[],...e.leadNode?[`Its allowed capabilities: ${(e.leadNode.allow??e.profile.defaultCapabilities).map(O).join(`, `)||`none`}; denied: ${(e.leadNode.deny??[]).map(O).join(`, `)||`none`}; required skills: ${(e.leadNode.skills??[]).map(O).join(`, `)||`none`}. Respect these in the lead node's own work; run coordination and notes still follow the contract below. Load its applicable installed skills before using them; if a required skill is missing, halt.`,``]:[],...t?[t,``]:[],C(`You run this graph. The main session is the executive: you dispatch nodes, follow edges, count rounds, evaluate stops, keep the progress log, and stop for the human when the graph says to.`,``,"You do not do the workers' jobs. Every agent node below runs as its own subagent through the `spawn_agent` tool, with `fork_turns: none`, and sees only its brief, its declared inputs and the evidence its inbound edge allows. You never grade your own work while a critic node exists: the critic's verdict is the one that counts, and you do not overrule it by re-reading the diff yourself."))}function ot(e){let t=e.doc.constraints??{},n=[];return t.budget&&n.push(T(`**Budget.** ${t.budget}`)),t.time&&n.push(T(`**Time.** ${t.time}`)),t.other&&n.push(T(`**Other.** ${t.other}`)),C(`## 2. Goal and constraints`,``,`**Goal.**`,``,e.doc.goal??`(none stated)`,...n.length>0?[``,`**Constraints.**`,``,...n]:[],...e.doc.description?[``,`**What this graph does.**`,``,e.doc.description]:[])}var K=`20260917-093002`,st=`2026-09-17T09:30:02Z`;function ct(e){let t=JSON.stringify({id:`n-0001`,run:K,at:`graph`,started:st,text:`run started`}),r=q(e);return C(`## 3. Run setup`,``,`1. Read the run id from the clock, in the form ${O(e.profile.runIdFormat)} (UTC): ${O(`date -u +%Y%m%d-%H%M%S`)}, for example ${O(K)}. If ${O(`${e.paths.runs}/<that id>/`)} already exists, append ${O(`-2`)}, then ${O(`-3`)}, and so on. Never make an id up.`,`2. Create ${O(`${e.paths.runs}/<run-id>/`)}.`,`3. Copy the source document ${O(e.paths.graph)} into it as ${O(e.paths.workingCopy)}. That copy is the run's working copy: the graph this run follows, ${e.adaptation===`adaptive`?`and the only copy you may amend (§9)`:`left as copied (§9)`}. Never write the source document.`,`4. Write ${O(`PROGRESS.md`)} there before dispatching anything: the run id, the goal, every node with status ${O(`pending`)}, and the round counter at 0${r.length>0?` (with the dispatch counter of ${r.map(e=>O(e.id)).join(`, `)} at 0, §6)`:``}.`,`5. Create ${O(`notes.jsonl`)} beside it and append the first line, its ${O(`started`)} read from ${O(`date -u +%Y-%m-%dT%H:%M:%SZ`)}:`,``,E(t,`json`),``,`6. If you were given a run id to resume, do not start a second run: read that folder's ${O(`PROGRESS.md`)} and working copy, continue from the last recorded position, and keep appending to the same ${O(`notes.jsonl`)}. Do not copy the source over the working copy again.`,``,`Entry nodes (start here): ${n(e.index).map(O).join(`, `)||`none — the graph has no entry node`}.`)}var q=e=>(e.doc.loops??[]).filter(e=>(e.stops??[]).some(e=>e.kind===`budget`&&e.measure===`dispatches`)),lt=e=>(e.doc.loops??[]).filter(e=>(e.stops??[]).some(e=>e.kind===`evidence-invalid`));function ut(e){return C(`## 4. Nodes`,``,D([`node`,`name`,`how you run it`,`role`,`what it returns`],(e.doc.nodes??[]).map(t=>[O(t.id),k(t.name),dt(e,t),k(ft(t)),k(pt(t))])),``,`Dispatch an agent node with the ${O(`spawn_agent`)} tool and the ${O(`agent_type`)} named above; its TOML file under ${O(`.codex/agents/`)} carries the full brief. Set ${O(`fork_turns: "none"`)} for fresh edges; wait for its result before routing. If fresh-history exclusion cannot be requested or confirmed by this harness, halt before dispatching; do not silently omit it. If the named custom role or subagent tools are unavailable, append a halt note and ask for a correctly configured fresh session, never do the worker job yourself. Your prompt carries only the task, the declared inputs and the edge's evidence. Never paste a transcript into a fresh worker.`,z(e.doc))}function dt(e,t){let n=e.agentByNode.get(t.id);if(n)return`${O(`spawn_agent`)} · ${O(n.agentName)}`;switch(t.kind){case`agent`:return`you — this is the lead node`;case`human-gate`:return`you ask the human`;case`check`:return t.check.run?`you run ${O(t.check.run)}`:`you judge the stated condition`;case`merge`:return`you merge the listed artifacts`;case`stop`:return`you end the run`;default:return`you`}}var ft=e=>e.kind===`agent`?typeof e.role==`string`?e.role:`custom: ${e.role.custom}`:e.kind;function pt(e){switch(e.kind){case`agent`:return e.outputs.join(`; `);case`human-gate`:return e.options&&e.options.length>0?e.options.join(` | `):`a human answer`;case`check`:return`pass when: ${e.check.pass}`;case`merge`:return`merged: ${e.merges.join(`, `)}`;case`stop`:return`run ends with outcome ${e.outcome??`success`}`;default:return``}}function mt(e){let n=e.doc.edges??[],i=lt(e),a=n.some(e=>(e.evidence??[]).some(e=>/\bdiff\b/i.test(e)));return C(`## 5. Edges`,``,D([`edge`,`route`,`taken when`,`isolation`,`evidence and gates`],n.map(e=>[O(e.id),`${O(e.from)} → ${O(e.to)}`,k(r(e)),t(e),k(gt(e))])),``,T(`Respect every scoped policy and edge retry limit. A concurrency-cap policy and every applicable edge cap limit the active workers together; choose the smallest applicable cap. Count retries and never exceed retry.max.`),T(`When a node finishes, take every outgoing edge whose condition matches its result. Several matching edges run in parallel, capped by any ${O(`concurrency`)} on the edge.`),T(`${O(`fresh`)} isolation: the worker starts with no context except its brief, its declared inputs and the evidence listed above. ${O(`shared`)}: continue the same worker if the build lets you, otherwise halt and report the unavailable continuation. Never perform a critic's step in the lead context.`),T(`A worker may inspect what its inbound edge lists plus its own declared inputs; for a writer that includes the project it is changing. A critic that cannot read its evidence reports ${O(`invalid-evidence`)} instead of guessing.`),T(`When an edge routes ${O(`invalid-evidence`)}, take it. Otherwise repair the evidence and dispatch the same node once more in the same round; a second ${O(`invalid-evidence`)} routes as ${O(`fail`)}.${i.length>0?` Such rounds count toward the ${O(`evidence-invalid`)} stop of loop ${i.map(e=>O(e.id)).join(`, `)}.`:``}`),...a?[T(ht)]:[])}var ht=`A diff of the change is ${O(`git diff`)} plus, for each file the change added, ${O(`git diff --no-index /dev/null <file>`)} (${O(`git diff`)} omits untracked files; ${O(`--no-index`)} exits 1 whenever the two differ, which is not an error). Run each bare from the project root, one command at a time; no brace group, no ${O(`cd`)}. ${O(`git add -N <file>`)} also works where it is allowed, and stages nothing.`;function gt(e){let t=[];return e.evidence&&e.evidence.length>0?t.push(`evidence: ${e.evidence.join(`; `)}`):t.push(`evidence: none listed`),e.approval&&t.push(`**human approval required before traversal**`),e.concurrency&&t.push(`concurrency max ${e.concurrency.max}`),e.retry&&t.push(`retry max ${e.retry.max}`),e.label&&t.push(`label: ${e.label}`),t.join(` · `)}function _t(e){let t=e.doc.loops??[];return t.length===0?C(`## 6. Loops`,``,`This graph has no loops. Follow the edges once and stop.`):C(`## 6. Loops`,``,t.map(t=>vt(e,t).replace(/\n+$/,``)).join(`
|
|
11
11
|
|
|
12
|
-
`))}function vt(e,t){let i=o(e.index,t),a=(t.back??[]).map(t=>e.index.edges.get(t)).filter(e=>e!==void 0),s=r(e.index,t.bar),c=(t.stops??[]).filter(t=>t.kind===`budget`&&e.profile.advisoryBudgetMeasures.includes(t.measure));return C(`### Loop ${O(t.id)} · ${t.name}`,``,T(`**Mode.** ${i}${t.mode?``:` (inferred: not every back edge starts at a check node)`}`),T(`**Members.** ${(t.members??[]).map(O).join(`, `)}`),T(`**A round is** one traversal of a back edge: ${a.map(e=>`${O(e.id)} (${e.from} → ${e.to})`).join(`, `)||`none declared`}. The first pass through the members is round 0, because no back edge has been taken yet; each traversal after that adds one. Record the round in ${O(`PROGRESS.md`)} and in a loop note every time you finish a pass.`),``,...t.bar?[`**Bar — ${t.bar.name}.** Stop when: ${t.bar.acceptance}`,``,...t.bar.aspiration?[`Aspiration (direction, never the stop condition): ${t.bar.aspiration}`,``]:[],...t.bar.answerKeyFrom?[`The answer key is the output of node ${O(t.bar.answerKeyFrom)}.`,``]:[],`The critic inspects exactly these:`,``,...s.map(e=>T(`${e.kind}: ${O(e.ref)}${e.note?` — ${e.note}`:``}`)),``]:[`**Bar.** none declared.`,``],`**Stops, evaluated in this order at the end of every pass, before a back edge; the first that fires wins:**`,``,D([`#`,`stop`,`what you do`],(t.stops??[]).map((e,t)=>[String(t+1),k(n(e)),k(h(e))])),...xt(e,t,c),``)}function yt(e,t){return(t.members??[]).map(t=>e.index.nodes.get(t)).filter(e=>e!==void 0).filter(t=>t.kind===`agent`&&t.id!==e.leadNode?.id||t.kind===`check`)}var bt=(e,t)=>{let n=new Set(t.members??[]);return(e.doc.loops??[]).filter(e=>e.id!==t.id&&(e.members??[]).length<n.size&&(e.members??[]).every(e=>n.has(e)))};function xt(e,t,n){let r=[],i=new Set((t.stops??[]).flatMap(e=>e.kind===`budget`?[e.measure]:[]));if(i.has(`dispatches`)&&(r.push(`> A dispatch is one node run inside this loop's members — an agent you dispatch, or a check you run — counted from the loop's first pass; a nested loop's count restarts when the outer loop re-enters it. Keep the count in ${O(`PROGRESS.md`)} and evaluate the stop against it.`),r.push(`> ${St(e,t)}`)),i.has(`minutes`)&&r.push(`> Minutes are wall clock from the run's first note.`),n.length>0){let e=[...new Set(n.flatMap(e=>e.kind===`budget`?[e.measure]:[]))];r.push(`> ${e.map(O).join(`, `)} budgets are **advisory**: nothing in Codex enforces them inside a session${e.includes(`turns`)?`, and leads count turns inconsistently`:``}. Track the figure in ${O(`PROGRESS.md`)} and halt when you pass it${e.includes(`usd`)?`; Codex has no documented dollar-cap flag, so do not claim this is enforced by the harness`:``}.`)}return r.length>0?[``,...r]:[]}function St(e,t){let n=yt(e,t),r=(t.members??[]).filter(e=>!n.some(t=>t.id===e)),i=(t.stops??[]).flatMap(e=>e.kind===`budget`&&e.measure===`dispatches`?[e.limit]:[]),a=i.length>0?Math.min(...i):void 0,o=bt(e,t);if(n.length===0)return`One full round of this loop dispatches nothing: none of its members is an agent or a check.`;let s=a===void 0?``:` The budget of ${a} covers ${Math.floor(a/n.length)} full round${Math.floor(a/n.length)===1?``:`s`}${a%n.length===0?``:` and ${a%n.length} more dispatch${a%n.length===1?``:`es`}`}.`;return`One full round of this loop costs **${n.length} dispatch${n.length===1?``:`es`}**: ${n.map(e=>O(e.id)).join(`, `)}${r.length>0?` (${r.map(O).join(`, `)} ${r.length===1?`is`:`are`} not a dispatch)`:``}.${s}${o.length>0?` Every extra round of the inner loop ${o.map(e=>O(e.id)).join(`, `)} adds its own dispatches on top.`:``} A node dispatched twice in one round (§5, invalid evidence) counts twice.`}function Ct(e){let t=(e.doc.nodes??[]).filter(e=>e.kind===`human-gate`),n=(e.doc.edges??[]).filter(e=>e.approval===!0),r=[...t.map(e=>T(`${O(e.id)} — ${e.kind===`human-gate`?e.prompt:``}${e.kind===`human-gate`&&e.options&&e.options.length>0?` (options: ${e.options.join(` | `)})`:``}`)),...n.map(e=>T(`${O(e.id)} — approval required before ${e.from} → ${e.to} is traversed`))];return C(`## 7. Human gates`,``,...r.length>0?r:[T(`None in this graph.`)],``,`One rule, in every kind of session. On reaching a gate: first append a note at the gate (${O(`at`)} = ${O(`node:<gate-id>`)}, or ${O(`edge:<edge-id>`)} for an approval edge) with ${O(`"outcome":"halt"`)} and a ${O(`text`)} naming it, and write ${O(`PROGRESS.md`)}; then ask, with ${O(`request_user_input`)} when it is available, otherwise in plain text; then end your turn. Do not simulate an answer, do not batch two gates into one question, and do not proceed on silence.`,``,`When the human answers, append a note at the same place with their decision and continue along the matching edge. A run nobody answers ends on that halt note, and the same run id resumes it (§3, step 6).`)}function wt(e){let t=e.agents.find(e=>e.isCritic)??e.agents[0],r=JSON.stringify({id:`n-0007`,run:K,at:`node:${t?.node.id??`some-node`}`,started:`2026-09-17T09:34:02Z`,ended:`2026-09-17T09:38:41Z`,outcome:`fail`,verdict:`fail`,round:2,evidence:[`docs/REVIEW-CHECKLIST.md`,`test command output`],gaps:[`no test covers the empty-input case`],text:`3 of 5 checklist items cited; two unmet`}),i=(e.doc.loops??[])[0],a=i?.stops??[],o=a.find(e=>e.kind===`bar-passed`)??a.find(e=>e.kind===`budget`)??a[0],s=i&&o?JSON.stringify({id:`n-0012`,run:K,at:`loop:${i.id}`,ended:`2026-09-17T09:51:10Z`,outcome:o.kind===`bar-passed`?`pass`:`halt`,round:3,stop:o.kind,text:o.kind===`bar-passed`?`bar passed at round 3; taking the pass edges`:`${n(o)} fired at round 3`}):void 0,c=q(e);return C(`## 8. Progress and notes`,``,T(`${O(e.paths.progress)} — human-readable. Rewrite it **after every node completes** and whenever the round counter moves: run id, goal, round, ${c.length>0?`the dispatch count of ${c.map(e=>O(e.id)).join(`, `)}, `:``}each node's status, what is waiting, and the stop check you last evaluated.`),T(`${O(e.paths.notes)} — one JSON object per line, appended, never rewritten. Append a line at the start of the run, one **per node run** (${O(`at`)} = ${O(`node:<node-id>`)}), one **per pass through a loop** (${O(`at`)} = ${O(`loop:<loop-id>`)}, carrying the round you just finished and the stop you evaluated — so even a loop that passes on its first pass leaves a line — and ${O(`stop`)} with the kind of the stop when one fires), and one when the run ends.`),T(`When you dispatch a node, append one short line first: ${O(`"outcome":"started"`)}, ${O(`at`)} = ${O(`node:<node-id>`)}, and ${O(`round`)} when the node is inside a loop. The usual line follows when the node completes, so a monitor can show what is running.`),T(`${O(`started`)} and ${O(`ended`)} are read from the clock, ${O(`date -u +%Y-%m-%dT%H:%M:%SZ`)}, or left out. Never estimate one.`),...Tt(e),``,"Line shape (graph-ir §6). `id`, `run` and `at` are required; the rest are filled when they apply:",``,E(C("id kebab-case, unique in the file: `n-0001`, `n-0002`, … in append order",`run the run id`,`at graph | node:<node-id> | edge:<edge-id> | loop:<loop-id>`,`started ISO timestamp from the clock, or omitted ended the same`,`outcome pass | fail | halt | invalid-evidence; started on a dispatch line, ending on the line before the final note (§11)`,`verdict the critic's verdict label, when there is one`,`round the loop round this belongs to`,`stop on the loop note that ends the loop: the kind of the stop that fired`,`evidence what was actually inspected`,`cost { measure: dispatches | minutes | usd | turns | tokens, amount }`,`gaps repeated gaps you noticed`,`proposal { summary, patch? } — a graph change for the human to decide; you do not make it`,...e.adaptation===`adaptive`?[`amendment { summary, reason, patch? } — a change you made to the working copy (§9)`]:[],`text short commentary`),`text`),``,s?`Two filled lines, a node run and the loop pass on which a stop fired:`:`One filled line:`,``,E(s?C(r,s):r,`json`),``,`A run never writes the source document ${O(e.paths.graph)}. When the graph itself looks wrong, §9 says what to do.`)}function Tt(t){let n=t.agents.filter(n=>n.isCritic&&e(t.index,n.node.id).length>0);if(n.length===0)return[];let r=n.flatMap(e=>e.node.outputs).find(e=>/\.md$/i.test(e.trim()))??`REVIEW.md`,i=r.trim().replace(/\.md$/i,``);return[T(`Before you re-dispatch a builder after a critic's ${O(`fail`)}, copy each report the critic wrote that round into the run folder as ${O(`<report>-round-<n>.md`)} — ${O(r.trim())} from round 0 becomes ${O(`${t.paths.runs}/<run-id>/${i}-round-0.md`)} — so the round's findings survive the next round's rewrite. Copy; the builder still reads the report where its edge says.`)]}var Et=[`a human gate`,`an edge ${O(`approval`)}`,`an ${O(`irreversible`)} marker`,`a ${O(`budget`)} or ${O(`max-iterations`)} stop`,`a bar's ${O(`acceptance`)}`,`critic isolation`,`the ${O(`adaptation`)} level itself`],Dt=`A ${O(`patch`)} is preferably a list of grooph ops, the JSON ${O(`grooph apply --ops`)} takes (${O(`[{"op":"updateNode","id":"<node-id>","set":{"effort":"high"}}]`)}): ops name objects by id, so the human can replay them with ${O(`grooph apply`)}.`,Ot={setGraphName:`name — the graph id follows while it still matches`,setGraphField:`key (name | goal | description | adaptation | lineage), value — null removes`,setTarget:`harness — null removes`,setConstraint:`key (budget | time | other), value — null removes`,addNode:`kind (agent | human-gate | check | merge | stop), name?, id?, at?, set? — the node's fields (role, brief, outputs, allow, …) go in set`,setNodeName:`id, name`,updateNode:`id, set — a shallow patch: each key replaces the field, null removes it; never id or kind`,removeNode:`id — with its edges, loop memberships and scoped policies`,connect:`from, to, id?, set? — adds an edge; when, isolation, evidence, approval go in set`,updateEdge:`id, set — re-routing (from, to) included`,removeEdge:`id`,addLoop:`members?, name?, id?, set? — no back edge and no stop until set or addStop gives them`,setLoopName:`id, name`,updateLoop:`id, set — mode, members, back, bar, stops`,removeLoop:`id`,toggleLoopMember:`loop, node, on?`,toggleLoopBack:`loop, edge, on?`,setBar:`loop, bar — null removes`,addStop:`loop, kind (human | budget | bar-passed | diminishing-returns | evidence-invalid | max-iterations), set?`,setStop:`loop, index, stop — replaces the stop at index (from 0)`,removeStop:`loop, index`,moveStop:`loop, index, delta (-1 | 1)`,addPolicy:`kind, scope (graph | loop:<id> | node:<id> | edge:<id>), params?, id?`,removePolicy:`id`,setPositions:`positions — layout only`,renameId:`from, to — every reference follows`};function J(){let e=Object.keys(a),t=Math.max(...e.map(e=>e.length)),n=[JSON.stringify({op:`addNode`,kind:`agent`,id:`reviewer`,set:{role:`critic`,brief:`<purpose, limits, outputs>`,outputs:[`REVIEW.md`],allow:[`read-files`,`write-outputs`]}}),JSON.stringify({op:`connect`,from:`builder`,to:`reviewer`,set:{when:`pass`,evidence:[`diff of the change`]}})];return[`These are the ops, and the only ops, ${O(`grooph apply`)} accepts; an op is ${O(`{"op":"<name>", ...arguments}`)}, and a ${O(`?`)} marks an optional argument. There is no ${O(`addEdge`)} and no ${O(`node`)} object: an edge is ${O(`connect`)}, and a node's fields go in ${O(`set`)}.`,``,E(e.map(e=>`${e.padEnd(t)} ${Ot[e]}`).join(`
|
|
12
|
+
`))}function vt(t,n){let r=g(t.index,n),i=(n.back??[]).map(e=>t.index.edges.get(e)).filter(e=>e!==void 0),a=e(t.index,n.bar),o=(n.stops??[]).filter(e=>e.kind===`budget`&&t.profile.advisoryBudgetMeasures.includes(e.measure));return C(`### Loop ${O(n.id)} · ${n.name}`,``,T(`**Mode.** ${r}${n.mode?``:` (inferred: not every back edge starts at a check node)`}`),T(`**Members.** ${(n.members??[]).map(O).join(`, `)}`),T(`**A round is** one traversal of a back edge: ${i.map(e=>`${O(e.id)} (${e.from} → ${e.to})`).join(`, `)||`none declared`}. The first pass through the members is round 0, because no back edge has been taken yet; each traversal after that adds one. Record the round in ${O(`PROGRESS.md`)} and in a loop note every time you finish a pass.`),``,...n.bar?[`**Bar — ${n.bar.name}.** Stop when: ${n.bar.acceptance}`,``,...n.bar.aspiration?[`Aspiration (direction, never the stop condition): ${n.bar.aspiration}`,``]:[],...n.bar.answerKeyFrom?[`The answer key is the output of node ${O(n.bar.answerKeyFrom)}.`,``]:[],`The critic inspects exactly these:`,``,...a.map(e=>T(`${e.kind}: ${O(e.ref)}${e.note?` — ${e.note}`:``}`)),``]:[`**Bar.** none declared.`,``],`**Stops, evaluated in this order at the end of every pass, before a back edge; the first that fires wins:**`,``,D([`#`,`stop`,`what you do`],(n.stops??[]).map((e,t)=>[String(t+1),k(h(e)),k(v(e))])),...xt(t,n,o),``)}function yt(e,t){return(t.members??[]).map(t=>e.index.nodes.get(t)).filter(e=>e!==void 0).filter(t=>t.kind===`agent`&&t.id!==e.leadNode?.id||t.kind===`check`)}var bt=(e,t)=>{let n=new Set(t.members??[]);return(e.doc.loops??[]).filter(e=>e.id!==t.id&&(e.members??[]).length<n.size&&(e.members??[]).every(e=>n.has(e)))};function xt(e,t,n){let r=[],i=new Set((t.stops??[]).flatMap(e=>e.kind===`budget`?[e.measure]:[]));if(i.has(`dispatches`)&&(r.push(`> A dispatch is one node run inside this loop's members — an agent you dispatch, or a check you run — counted from the loop's first pass; a nested loop's count restarts when the outer loop re-enters it. Keep the count in ${O(`PROGRESS.md`)} and evaluate the stop against it.`),r.push(`> ${St(e,t)}`)),i.has(`minutes`)&&r.push(`> Minutes are wall clock from the run's first note.`),n.length>0){let e=[...new Set(n.flatMap(e=>e.kind===`budget`?[e.measure]:[]))];r.push(`> ${e.map(O).join(`, `)} budgets are **advisory**: nothing in Codex enforces them inside a session${e.includes(`turns`)?`, and leads count turns inconsistently`:``}. Track the figure in ${O(`PROGRESS.md`)} and halt when you pass it${e.includes(`usd`)?`; Codex has no documented dollar-cap flag, so do not claim this is enforced by the harness`:``}.`)}return r.length>0?[``,...r]:[]}function St(e,t){let n=yt(e,t),r=(t.members??[]).filter(e=>!n.some(t=>t.id===e)),i=(t.stops??[]).flatMap(e=>e.kind===`budget`&&e.measure===`dispatches`?[e.limit]:[]),a=i.length>0?Math.min(...i):void 0,o=bt(e,t);if(n.length===0)return`One full round of this loop dispatches nothing: none of its members is an agent or a check.`;let s=a===void 0?``:` The budget of ${a} covers ${Math.floor(a/n.length)} full round${Math.floor(a/n.length)===1?``:`s`}${a%n.length===0?``:` and ${a%n.length} more dispatch${a%n.length===1?``:`es`}`}.`;return`One full round of this loop costs **${n.length} dispatch${n.length===1?``:`es`}**: ${n.map(e=>O(e.id)).join(`, `)}${r.length>0?` (${r.map(O).join(`, `)} ${r.length===1?`is`:`are`} not a dispatch)`:``}.${s}${o.length>0?` Every extra round of the inner loop ${o.map(e=>O(e.id)).join(`, `)} adds its own dispatches on top.`:``} A node dispatched twice in one round (§5, invalid evidence) counts twice.`}function Ct(e){let t=(e.doc.nodes??[]).filter(e=>e.kind===`human-gate`),n=(e.doc.edges??[]).filter(e=>e.approval===!0),r=[...t.map(e=>T(`${O(e.id)} — ${e.kind===`human-gate`?e.prompt:``}${e.kind===`human-gate`&&e.options&&e.options.length>0?` (options: ${e.options.join(` | `)})`:``}`)),...n.map(e=>T(`${O(e.id)} — approval required before ${e.from} → ${e.to} is traversed`))];return C(`## 7. Human gates`,``,...r.length>0?r:[T(`None in this graph.`)],``,`One rule, in every kind of session. On reaching a gate: first append a note at the gate (${O(`at`)} = ${O(`node:<gate-id>`)}, or ${O(`edge:<edge-id>`)} for an approval edge) with ${O(`"outcome":"halt"`)} and a ${O(`text`)} naming it, and write ${O(`PROGRESS.md`)}; then ask, with ${O(`request_user_input`)} when it is available, otherwise in plain text; then end your turn. Do not simulate an answer, do not batch two gates into one question, and do not proceed on silence.`,``,`When the human answers, append a note at the same place with their decision and continue along the matching edge. A run nobody answers ends on that halt note, and the same run id resumes it (§3, step 6).`)}function wt(e){let t=e.agents.find(e=>e.isCritic)??e.agents[0],n=JSON.stringify({id:`n-0007`,run:K,at:`node:${t?.node.id??`some-node`}`,started:`2026-09-17T09:34:02Z`,ended:`2026-09-17T09:38:41Z`,outcome:`fail`,verdict:`fail`,round:2,evidence:[`docs/REVIEW-CHECKLIST.md`,`test command output`],gaps:[`no test covers the empty-input case`],text:`3 of 5 checklist items cited; two unmet`}),r=(e.doc.loops??[])[0],i=r?.stops??[],a=i.find(e=>e.kind===`bar-passed`)??i.find(e=>e.kind===`budget`)??i[0],o=r&&a?JSON.stringify({id:`n-0012`,run:K,at:`loop:${r.id}`,ended:`2026-09-17T09:51:10Z`,outcome:a.kind===`bar-passed`?`pass`:`halt`,round:3,stop:a.kind,text:a.kind===`bar-passed`?`bar passed at round 3; taking the pass edges`:`${h(a)} fired at round 3`}):void 0,s=q(e);return C(`## 8. Progress and notes`,``,T(`${O(e.paths.progress)} — human-readable. Rewrite it **after every node completes** and whenever the round counter moves: run id, goal, round, ${s.length>0?`the dispatch count of ${s.map(e=>O(e.id)).join(`, `)}, `:``}each node's status, what is waiting, and the stop check you last evaluated.`),T(`${O(e.paths.notes)} — one JSON object per line, appended, never rewritten. Append a line at the start of the run, one **per node run** (${O(`at`)} = ${O(`node:<node-id>`)}), one **per pass through a loop** (${O(`at`)} = ${O(`loop:<loop-id>`)}, carrying the round you just finished and the stop you evaluated — so even a loop that passes on its first pass leaves a line — and ${O(`stop`)} with the kind of the stop when one fires), and one when the run ends.`),T(`When you dispatch a node, append one short line first: ${O(`"outcome":"started"`)}, ${O(`at`)} = ${O(`node:<node-id>`)}, and ${O(`round`)} when the node is inside a loop. The usual line follows when the node completes, so a monitor can show what is running.`),T(`${O(`started`)} and ${O(`ended`)} are read from the clock, ${O(`date -u +%Y-%m-%dT%H:%M:%SZ`)}, or left out. Never estimate one.`),...Tt(e),``,"Line shape (graph-ir §6). `id`, `run` and `at` are required; the rest are filled when they apply:",``,E(C("id kebab-case, unique in the file: `n-0001`, `n-0002`, … in append order",`run the run id`,`at graph | node:<node-id> | edge:<edge-id> | loop:<loop-id>`,`started ISO timestamp from the clock, or omitted ended the same`,`outcome pass | fail | halt | invalid-evidence; started on a dispatch line, ending on the line before the final note (§11)`,`verdict the critic's verdict label, when there is one`,`round the loop round this belongs to`,`stop on the loop note that ends the loop: the kind of the stop that fired`,`evidence what was actually inspected`,`cost { measure: dispatches | minutes | usd | turns | tokens, amount }`,`gaps repeated gaps you noticed`,`proposal { summary, patch? } — a graph change for the human to decide; you do not make it`,...e.adaptation===`adaptive`?[`amendment { summary, reason, patch? } — a change you made to the working copy (§9)`]:[],`text short commentary`),`text`),``,o?`Two filled lines, a node run and the loop pass on which a stop fired:`:`One filled line:`,``,E(o?C(n,o):n,`json`),``,`A run never writes the source document ${O(e.paths.graph)}. When the graph itself looks wrong, §9 says what to do.`)}function Tt(e){let t=e.agents.filter(t=>t.isCritic&&f(e.index,t.node.id).length>0);if(t.length===0)return[];let n=t.flatMap(e=>e.node.outputs).find(e=>/\.md$/i.test(e.trim()))??`REVIEW.md`,r=n.trim().replace(/\.md$/i,``);return[T(`Before you re-dispatch a builder after a critic's ${O(`fail`)}, copy each report the critic wrote that round into the run folder as ${O(`<report>-round-<n>.md`)} — ${O(n.trim())} from round 0 becomes ${O(`${e.paths.runs}/<run-id>/${r}-round-0.md`)} — so the round's findings survive the next round's rewrite. Copy; the builder still reads the report where its edge says.`)]}var Et=[`a human gate`,`an edge ${O(`approval`)}`,`an ${O(`irreversible`)} marker`,`a ${O(`budget`)} or ${O(`max-iterations`)} stop`,`a bar's ${O(`acceptance`)}`,`critic isolation`,`the ${O(`adaptation`)} level itself`],Dt=`A ${O(`patch`)} is preferably a list of grooph ops, the JSON ${O(`grooph apply --ops`)} takes (${O(`[{"op":"updateNode","id":"<node-id>","set":{"effort":"high"}}]`)}): ops name objects by id, so the human can replay them with ${O(`grooph apply`)}.`,Ot={setGraphName:`name — the graph id follows while it still matches`,setGraphField:`key (name | goal | description | adaptation | lineage), value — null removes`,setTarget:`harness — null removes`,setConstraint:`key (budget | time | other), value — null removes`,addNode:`kind (agent | human-gate | check | merge | stop), name?, id?, at?, set? — the node's fields (role, brief, outputs, allow, …) go in set`,setNodeName:`id, name`,updateNode:`id, set — a shallow patch: each key replaces the field, null removes it; never id or kind`,removeNode:`id — with its edges, loop memberships and scoped policies`,connect:`from, to, id?, set? — adds an edge; when, isolation, evidence, approval go in set`,updateEdge:`id, set — re-routing (from, to) included`,removeEdge:`id`,addLoop:`members?, name?, id?, set? — no back edge and no stop until set or addStop gives them`,setLoopName:`id, name`,updateLoop:`id, set — mode, members, back, bar, stops`,removeLoop:`id`,toggleLoopMember:`loop, node, on?`,toggleLoopBack:`loop, edge, on?`,setBar:`loop, bar — null removes`,addStop:`loop, kind (human | budget | bar-passed | diminishing-returns | evidence-invalid | max-iterations), set?`,setStop:`loop, index, stop — replaces the stop at index (from 0)`,removeStop:`loop, index`,moveStop:`loop, index, delta (-1 | 1)`,addPolicy:`kind, scope (graph | loop:<id> | node:<id> | edge:<id>), params?, id?`,removePolicy:`id`,setPositions:`positions — layout only`,renameId:`from, to — every reference follows`};function J(){let e=Object.keys(_),t=Math.max(...e.map(e=>e.length)),n=[JSON.stringify({op:`addNode`,kind:`agent`,id:`reviewer`,set:{role:`critic`,brief:`<purpose, limits, outputs>`,outputs:[`REVIEW.md`],allow:[`read-files`,`write-outputs`]}}),JSON.stringify({op:`connect`,from:`builder`,to:`reviewer`,set:{when:`pass`,evidence:[`diff of the change`]}})];return[`These are the ops, and the only ops, ${O(`grooph apply`)} accepts; an op is ${O(`{"op":"<name>", ...arguments}`)}, and a ${O(`?`)} marks an optional argument. There is no ${O(`addEdge`)} and no ${O(`node`)} object: an edge is ${O(`connect`)}, and a node's fields go in ${O(`set`)}.`,``,E(e.map(e=>`${e.padEnd(t)} ${Ot[e]}`).join(`
|
|
13
13
|
`),`text`),``,`A new critic and the edge into it, as one patch of two ops:`,``,E(`[${n.join(`,
|
|
14
|
-
`)}]`,`json`)]}function kt(e){let t=e.adaptation,n=(e.doc.policies??[]).filter(e=>e.kind===`no-live-graph-rewrite`),r=n.find(e=>e.scope===`graph`),i=r&&e.doc.adaptation!==t?` (its policy ${O(r.id)}, ${O(`no-live-graph-rewrite`)}, is stricter than ${O(`adaptation: ${e.doc.adaptation??`adaptive`}`)})`:e.doc.adaptation===void 0?` (the default)`:``,a=`## 9. Adapting the graph`,o=`The working copy ${O(e.paths.workingCopy)} stays identical to the source document ${O(e.paths.graph)}: neither is written during this run.`;if(t===`fixed`)return C(a,``,`This graph is ${O(`fixed`)}${i}: follow it exactly. Do not add, remove or re-brief nodes, re-route edges, or change loops, tiers or effort — not even to tighten a brake.`,``,`When the work cannot go on within the graph as written, halt and ask: append a note with ${O(`"outcome":"halt"`)} that says what the graph is missing, write the final ${O(`PROGRESS.md`)}, and tell the human. A ${O(`proposal`)} note alongside is welcome; the run does not continue on it.`,``,o);if(t===`propose`)return C(a,``,`This graph is ${O(`propose`)}${i}: you change nothing in it during the run, not even to tighten a brake. Follow it as written.`,``,`When the work shows the graph is wrong — a missing node, a loop that should exist, a brief that no longer fits — append a note with a ${O(`proposal`)} (${O(`summary`)}, and a ${O(`patch`)} when one helps) and carry on with the graph as it is; the human decides after the run. If you cannot carry on without the change, halt and say why.`,``,Dt,``,...J(),``,o);let s=JSON.stringify({id:`n-0009`,run:`<run-id>`,at:`graph`,amendment:{summary:`<what you changed>`,reason:`<what the work showed>`,patch:[{op:`updateNode`,id:`<node-id>`,set:{owns:[`<artifact>`]}}]}}),c=n.filter(e=>e.scope!==`graph`);return C(a,``,`This graph is ${O(`adaptive`)}${i}. It is the plan to start from, not a script: when the work shows it is wrong — a missing node, a loop that should exist, a brief that no longer fits — change the run's working copy rather than work around it. When the graph fits, follow it. Work that fits an existing node's brief and outputs needs no amendment, and the smallest change that closes a real gap is the right one.`,``,`Amending at kickoff is fine when reading the task already shows a gap, such as a file a node must write that its ${O(`owns`)} does not list. Redesigning the graph up front is not: a change to its overall shape before any node has run is a ${O(`proposal`)} for the human.`,``,`You may add, remove or re-brief nodes, add or re-route edges, add loops, and change tiers or effort. For each amendment, when you make it:`,``,`1. Edit the working copy, ${O(e.paths.workingCopy)}. The source document ${O(e.paths.graph)} is never written by a run; after the run the human adopts your working copy as a new version or discards it.`,`2. Append a note with an ${O(`amendment`)} — ${O(`summary`)}, ${O(`reason`)}, and a ${O(`patch`)} when one helps. A patch is preferably a list of grooph ops, the JSON ${O(`grooph apply --ops`)} takes: ops name objects by id, so they survive reordering and can be replayed. The working copy is the record either way.`,``,E(s,`json`),``,`3. Record it in ${O(`PROGRESS.md`)} under **Amendments**, so the human can see the graph the run is actually following.`,`4. Check that the working copy still validates: run ${O(`grooph validate --for-export ${e.paths.workingCopy}`)} when ${O(`grooph`)} is on your PATH; otherwise check the brakes below by hand.`,`5. When an amendment changes a node's brief, model, effort, skills or capabilities, update its TOML file under ${O(`.codex/agents/`)} using ${O(e.paths.mapping)}. Do not assume this running session reloads it. Use the changed definition only when the harness confirms the role and its settings are available; otherwise record a proposal and halt for a fresh session with the same run id. Never replace a restricted critic with a less restricted stand-in.`,``,...J(),``,`At every adaptation level you may not remove or loosen:`,``,...Et.map(T),``,`You may tighten any of them. Loosening one is a ${O(`proposal`)} note for the human, never an amendment. A loop you add needs a stop, and a bar if it is a judgment loop, like any other.${c.length>0?` Policy ${c.map(e=>`${O(e.id)} (scope ${O(e.scope)})`).join(`, `)} forbids live rewrites where it applies: for anything it covers, record a proposal instead.`:``}`,``,`A node added mid-run needs its own TOML definition beside the others. Do not assume new custom agents are discovered without restarting; if the role is not available, record a proposal and halt. A fresh session may resume the same run id after loading the definition, keeping the same isolation and evidence rules.`)}function At(e){return C(`## 10. Validation warnings`,``,...e.length===0?[`The document validated clean for export: no warnings.`]:[`grooph raised these when compiling this package. They are not errors, and the human running this graph should see them:`,``,E(e.map(
|
|
15
|
-
`),`text`)])}function Y(e){let t=(e.doc.nodes??[]).filter(e=>e.kind===`stop`),n=e.adaptation===`adaptive`;return C(`## 11. Ending`,``,`The run ends when you reach a stop node, when a stop fires and its action is to halt, or when no edge is left to take. A gate is different: the halt note of §7 stands as the final note until the human answers, and the run continues from it.`,``,...t.length>0?[`Stop nodes: ${t.map(e=>`${O(e.id)} (${e.kind===`stop`?e.outcome??`success`:``})`).join(`, `)}.`,``]:[`This graph has no stop node: the run ends when no edge is left to take.`,``],`Whichever way it ends, do all three:`,``,`1. Append one short line first, ${O(`{"at":"graph","outcome":"ending"}`)} with a ${O(`text`)} naming how the run ends, then the final note: ${O(`"at":"graph"`)} with the outcome and a ${O(`text`)} that names the stop that fired or the stop node reached. The ${O(`ending`)} line is how a monitor tells a run that finished from one that was cut off while finishing.`,`2. Write the last ${O(`PROGRESS.md`)}: which nodes ran, how many rounds, ${n?`every amendment to the working copy, `:``}and why the run ended.`,`3. Tell the human, in your reply, the run id, the rounds, the stop that ended the run, ${n?`whether the working copy was amended (so they can adopt or discard it), `:``}and what is left over.`,``,`The final note and ${O(`PROGRESS.md`)} are the record. Your last reply is the report: it summarizes them for whoever started this session and points at the run folder, ${O(`${e.paths.runs}/<run-id>/`)}.`)}var X=e=>`'`+e.replaceAll(`'`,`'"'"'`)+`'`;function jt(e){let t=(e.leadNode?.allow??e.profile.defaultCapabilities).includes(`web`)&&!(e.leadNode?.deny??[]).includes(`web`)?`live`:`disabled`,n=e.leadNode?e.leadNode.model?.pin?.codex??(e.leadNode.model?e.profile.models[e.leadNode.model.tier]:void 0):e.profile.models.frontier,r=n?` -m ${X(n)}`:``,i=e.profile.models[e.profile.defaultTier],a=new Map;for(let[t,n]of Object.entries(e.profile.models))a.set(n,[...a.get(n)??[],t]);let o=[...a].filter(([,e])=>e.length>1).map(([e,t])=>`${t.join(` and `)} resolve to ${e}`),s=o.length>0?`Model collapse: ${o.join(`; `)}; tier names do not guarantee distinct underlying models.`:`No two tiers resolve to the same model.`;return w(C(`# Mapping notes · ${e.doc.name}`,``,`CLI ${O(e.profile.verifiedAgainst)} help was read on ${e.profile.verifiedOn}; the package has not been run in Codex. The graph is the source of truth; re-exporting overwrites these generated files.`),C(`## Graph piece → file`,``,D([`piece`,`file`,`meaning`],[[`source`,O(e.paths.graph),`canonical graph; never written by a run`],[`lead`,O(e.paths.lead),`main session's brief, routing, loops, gates, adaptation and notes`],[`kickoff`,O(e.paths.kickoff),`paste this prompt into a fresh Codex session`],[`mapping`,O(e.paths.mapping),`this file`],[`working copy`,O(e.paths.workingCopy),`created at kickoff; adaptation: ${e.adaptation}`],[`progress`,O(e.paths.progress),`rewritten after every node and halt`],[`notes`,O(e.paths.notes),`append-only JSONL, unchanged run contract for grooph watch`],...e.agents.map(e=>[O(e.node.id),O(e.file),`${e.role}; model ${e.model??`session default`}; effort ${e.effort??`session default`}; sandbox ${e.sandbox}`])])),C(`## Start or resume`,``,`Place the package in the project before starting Codex. Custom agent names are the agent_type values given to spawn_agent; use fork_turns: none on fresh edges. This kickoff explicitly requests subagents. It sets the lead model when the graph supplies one (or when the graph has no lead node) and the default spawned-agent model for this invocation only. It names no approval option and writes nothing to the owner's configuration, hooks or skills.`,``,E(`codex exec --sandbox workspace-write -c agents.default_subagent_model=${X(U(i))} -c web_search=${X(U(t))}${r} -c model_reasoning_effort=${X(U(e.leadNode?.effort?e.profile.effort[e.leadNode.effort]:`high`))} - < ${X(e.paths.kickoff)}`,`sh`),``,`The owner chooses project trust and hook approvals. The package sets no approval policy: no agent file has an approval_policy key and this command has no approval option. Which approval policy then applies, to the lead under codex exec and to the agents it spawns, is unknown to this package: grooph has no run on record that shows it. Do not read the absence of a setting as the owner's configured policy applying. An unattended operation requiring owner approval may be refused when nobody can respond; that refusal is not a native graph halt. Graph gates and stops remain instructions the lead must follow. In an interactive session, paste KICKOFF.md. At a human gate the lead records outcome: halt before asking, then ends its turn. A headless run ends there. Resume with codex exec resume <session-id> and an explicit prompt naming the same grooph run id and the human's actual answer, or start a fresh session with that run id and answer. A session id is separate from the grooph run id. Silence never approves a gate.`),C(`## Hand-adjusting the package`,``,`# ${e.tiersNamed.length>0?`this export`:`profile`}: frontier → ${e.profile.models.frontier}, strong → ${e.profile.models.strong}, fast → ${e.profile.models.fast}`,``,s,``,`A node's TOML model and model_reasoning_effort are its tier or harness-specific pin and effort. A pin wins over export model overrides. Nodes with no model inherit the invocation's spawned-agent default, ${e.profile.models[e.profile.defaultTier]} (${e.profile.defaultTier}); explicit node models take precedence. The lead's settings belong to the main session at startup. Model and effort availability depends on the account and client; halt on an unavailable selection.`,``,`sandbox_mode is read-only unless edit-files or write-outputs is allowed and not denied. Then it is workspace-write. The package does not set approval_policy; which approval policy applies is unknown to this package (Start or resume, above). An unattended operation requiring owner approval may be refused when nobody can respond; that refusal is not a native graph halt. Graph gates and stops remain instructions the lead must follow. web_search is disabled unless web is explicitly allowed and not denied. By Codex's documentation a parent session's live sandbox and approval settings can supersede an agent file's; grooph has no run on record that shows it. There is no per-node native tool allowlist in this package: declared output paths, ownership, test-only commands, evidence limits and delegation limits are instructions, not filesystem or tool guarantees. read-files includes non-mutating shell reads where there is no native file-read tool; this does not grant general command execution. write-outputs can write its own named outputs while edit-files stays denied.`,``,`Node skills are explicitly requested in developer_instructions; Codex has no Claude-style skills preload field. The session must load applicable installed skills; a missing required skill halts the node. Custom capabilities are stated in the brief and require an available meaning and implementation.`,``,`Loops, edges, checks, gates, merge and stop nodes live in LEAD.md. Change stop numbers in the graph and re-export. During adaptive runs change only the run-local working copy with an amendment note. A changed or added custom-agent definition is used only after the harness confirms it is available; hot reload is unknown, so otherwise halt for a fresh session or record a proposal. Never loosen a brake or replace a critic with the lead.`),C(`## Limits and optional observation`,``,`dispatches and minutes are counted by the lead. usd, turns and tokens are advisory; Codex has no documented dollar-cap flag. A max-iterations backstop is never the acceptance bar. Concurrency and retry caps, stops and gates are lead instructions; grooph is a compiler, not a runtime or a hard budget controller.`,``,`grooph hooks install can separately add observation-only hooks; they need owner trust and review and this package neither installs nor depends on them. The lead still writes started and completed node notes and every loop pass, so grooph watch reads a Codex run without changes. Filesystem sandboxing does not provide evidence secrecy: fresh context means no inherited conversation, and the worker reads only its declared inputs and dispatched edge evidence.`))}function Mt(e,t,n={}){let r=$e(e,n),i=nt(r),a={[r.paths.graph]:y(e),[r.paths.lead]:rt(r,t),[r.paths.mapping]:jt(r),[r.paths.kickoff]:i};for(let e of r.agents)a[e.file]=qe(r,e);return{files:Object.fromEntries(Object.keys(a).sort().map(e=>[e,a[e]])),kickoff:i}}var Z=class extends Error{issues;constructor(e){let t=e.filter(e=>e.severity===`error`);super(`cannot export: ${t.length} validation error${t.length===1?``:`s`} — ${t.map(e=>e.code).join(`, `)}`),this.name=`CompileError`,this.issues=e}},Q={graphs:`saved graphs`,proposals:`proposal sets`,templates:`the project's templates`,events:`what the event hook records`,hooks:`the event hook`};function Nt(e){return Object.hasOwn(Q,e)?`A graph with the id "${e}" is not exported: its package would be placed in .grooph/${e}/, the folder grooph keeps ${Q[e]} in.`:void 0}function $(e,t,n={}){let r=d(e);if(!r.doc)throw new Z(r.issues);let i=[...ne(r.doc,{forExport:!0}),...Pt(r.doc,t)];if(te(i))throw new Z(i);let a=i.filter(e=>e.severity===`warning`);switch(t){case`codex`:{let e=Mt(r.doc,a,n);return{files:e.files,kickoff:e.kickoff,warnings:a}}case`claude-code`:{let e=Ke(r.doc,a,n);return{files:e.files,kickoff:e.kickoff,warnings:a}}default:throw Error(`unknown compile target "${String(t)}"; known targets: ${v.join(`, `)}`)}}function Pt(e,t){let n=e.target?.harness;return n===void 0||n===t||!m(n)||!m(t)?[]:[ee(`E_NO_TARGET`,`the document names the harness "${n}" and the export is for "${t}": export it for ${n}, or name ${t} in the document first (grooph apply <file> --ops - --write, given [{"op":"setTarget","harness":"${t}"}]) and export into a project that does not hold this graph's ${n} package`,[e.id])]}function Ft(e,t,n={}){try{return{ok:!0,result:$(e,t,n)}}catch(e){if(e instanceof Z)return{ok:!1,issues:e.issues};throw e}}export{Z as CompileError,$ as compile,Nt as keptFolder,Ft as tryCompile};
|
|
14
|
+
`)}]`,`json`)]}function kt(e){let t=e.adaptation,n=(e.doc.policies??[]).filter(e=>e.kind===`no-live-graph-rewrite`),r=n.find(e=>e.scope===`graph`),i=r&&e.doc.adaptation!==t?` (its policy ${O(r.id)}, ${O(`no-live-graph-rewrite`)}, is stricter than ${O(`adaptation: ${e.doc.adaptation??`adaptive`}`)})`:e.doc.adaptation===void 0?` (the default)`:``,a=`## 9. Adapting the graph`,o=`The working copy ${O(e.paths.workingCopy)} stays identical to the source document ${O(e.paths.graph)}: neither is written during this run.`;if(t===`fixed`)return C(a,``,`This graph is ${O(`fixed`)}${i}: follow it exactly. Do not add, remove or re-brief nodes, re-route edges, or change loops, tiers or effort — not even to tighten a brake.`,``,`When the work cannot go on within the graph as written, halt and ask: append a note with ${O(`"outcome":"halt"`)} that says what the graph is missing, write the final ${O(`PROGRESS.md`)}, and tell the human. A ${O(`proposal`)} note alongside is welcome; the run does not continue on it.`,``,o);if(t===`propose`)return C(a,``,`This graph is ${O(`propose`)}${i}: you change nothing in it during the run, not even to tighten a brake. Follow it as written.`,``,`When the work shows the graph is wrong — a missing node, a loop that should exist, a brief that no longer fits — append a note with a ${O(`proposal`)} (${O(`summary`)}, and a ${O(`patch`)} when one helps) and carry on with the graph as it is; the human decides after the run. If you cannot carry on without the change, halt and say why.`,``,Dt,``,...J(),``,o);let s=JSON.stringify({id:`n-0009`,run:`<run-id>`,at:`graph`,amendment:{summary:`<what you changed>`,reason:`<what the work showed>`,patch:[{op:`updateNode`,id:`<node-id>`,set:{owns:[`<artifact>`]}}]}}),c=n.filter(e=>e.scope!==`graph`);return C(a,``,`This graph is ${O(`adaptive`)}${i}. It is the plan to start from, not a script: when the work shows it is wrong — a missing node, a loop that should exist, a brief that no longer fits — change the run's working copy rather than work around it. When the graph fits, follow it. Work that fits an existing node's brief and outputs needs no amendment, and the smallest change that closes a real gap is the right one.`,``,`Amending at kickoff is fine when reading the task already shows a gap, such as a file a node must write that its ${O(`owns`)} does not list. Redesigning the graph up front is not: a change to its overall shape before any node has run is a ${O(`proposal`)} for the human.`,``,`You may add, remove or re-brief nodes, add or re-route edges, add loops, and change tiers or effort. For each amendment, when you make it:`,``,`1. Edit the working copy, ${O(e.paths.workingCopy)}. The source document ${O(e.paths.graph)} is never written by a run; after the run the human adopts your working copy as a new version or discards it.`,`2. Append a note with an ${O(`amendment`)} — ${O(`summary`)}, ${O(`reason`)}, and a ${O(`patch`)} when one helps. A patch is preferably a list of grooph ops, the JSON ${O(`grooph apply --ops`)} takes: ops name objects by id, so they survive reordering and can be replayed. The working copy is the record either way.`,``,E(s,`json`),``,`3. Record it in ${O(`PROGRESS.md`)} under **Amendments**, so the human can see the graph the run is actually following.`,`4. Check that the working copy still validates: run ${O(`grooph validate --for-export ${e.paths.workingCopy}`)} when ${O(`grooph`)} is on your PATH; otherwise check the brakes below by hand.`,`5. When an amendment changes a node's brief, model, effort, skills or capabilities, update its TOML file under ${O(`.codex/agents/`)} using ${O(e.paths.mapping)}. Do not assume this running session reloads it. Use the changed definition only when the harness confirms the role and its settings are available; otherwise record a proposal and halt for a fresh session with the same run id. Never replace a restricted critic with a less restricted stand-in.`,``,...J(),``,`At every adaptation level you may not remove or loosen:`,``,...Et.map(T),``,`You may tighten any of them. Loosening one is a ${O(`proposal`)} note for the human, never an amendment. A loop you add needs a stop, and a bar if it is a judgment loop, like any other.${c.length>0?` Policy ${c.map(e=>`${O(e.id)} (scope ${O(e.scope)})`).join(`, `)} forbids live rewrites where it applies: for anything it covers, record a proposal instead.`:``}`,``,`A node added mid-run needs its own TOML definition beside the others. Do not assume new custom agents are discovered without restarting; if the role is not available, record a proposal and halt. A fresh session may resume the same run id after loading the definition, keeping the same isolation and evidence rules.`)}function At(e){return C(`## 10. Validation warnings`,``,...e.length===0?[`The document validated clean for export: no warnings.`]:[`grooph raised these when compiling this package. They are not errors, and the human running this graph should see them:`,``,E(e.map(y).join(`
|
|
15
|
+
`),`text`)])}function Y(e){let t=(e.doc.nodes??[]).filter(e=>e.kind===`stop`),n=e.adaptation===`adaptive`;return C(`## 11. Ending`,``,`The run ends when you reach a stop node, when a stop fires and its action is to halt, or when no edge is left to take. A gate is different: the halt note of §7 stands as the final note until the human answers, and the run continues from it.`,``,...t.length>0?[`Stop nodes: ${t.map(e=>`${O(e.id)} (${e.kind===`stop`?e.outcome??`success`:``})`).join(`, `)}.`,``]:[`This graph has no stop node: the run ends when no edge is left to take.`,``],`Whichever way it ends, do all three:`,``,`1. Append one short line first, ${O(`{"at":"graph","outcome":"ending"}`)} with a ${O(`text`)} naming how the run ends, then the final note: ${O(`"at":"graph"`)} with the outcome and a ${O(`text`)} that names the stop that fired or the stop node reached. The ${O(`ending`)} line is how a monitor tells a run that finished from one that was cut off while finishing.`,`2. Write the last ${O(`PROGRESS.md`)}: which nodes ran, how many rounds, ${n?`every amendment to the working copy, `:``}and why the run ended.`,`3. Tell the human, in your reply, the run id, the rounds, the stop that ended the run, ${n?`whether the working copy was amended (so they can adopt or discard it), `:``}and what is left over.`,``,`The final note and ${O(`PROGRESS.md`)} are the record. Your last reply is the report: it summarizes them for whoever started this session and points at the run folder, ${O(`${e.paths.runs}/<run-id>/`)}.`)}var X=e=>`'`+e.replaceAll(`'`,`'"'"'`)+`'`;function jt(e){let t=(e.leadNode?.allow??e.profile.defaultCapabilities).includes(`web`)&&!(e.leadNode?.deny??[]).includes(`web`)?`live`:`disabled`,n=e.leadNode?e.leadNode.model?.pin?.codex??(e.leadNode.model?e.profile.models[e.leadNode.model.tier]:void 0):e.profile.models.frontier,r=n?` -m ${X(n)}`:``,i=e.profile.models[e.profile.defaultTier],a=new Map;for(let[t,n]of Object.entries(e.profile.models))a.set(n,[...a.get(n)??[],t]);let o=[...a].filter(([,e])=>e.length>1).map(([e,t])=>`${t.join(` and `)} resolve to ${e}`),s=o.length>0?`Model collapse: ${o.join(`; `)}; tier names do not guarantee distinct underlying models.`:`No two tiers resolve to the same model.`;return w(C(`# Mapping notes · ${e.doc.name}`,``,`CLI ${O(e.profile.verifiedAgainst)} help was read on ${e.profile.verifiedOn}; the package has not been run in Codex. The graph is the source of truth; re-exporting overwrites these generated files.`),C(`## Graph piece → file`,``,D([`piece`,`file`,`meaning`],[[`source`,O(e.paths.graph),`canonical graph; never written by a run`],[`lead`,O(e.paths.lead),`main session's brief, routing, loops, gates, adaptation and notes`],[`kickoff`,O(e.paths.kickoff),`paste this prompt into a fresh Codex session`],[`mapping`,O(e.paths.mapping),`this file`],[`working copy`,O(e.paths.workingCopy),`created at kickoff; adaptation: ${e.adaptation}`],[`progress`,O(e.paths.progress),`rewritten after every node and halt`],[`notes`,O(e.paths.notes),`append-only JSONL, unchanged run contract for grooph watch`],...e.agents.map(e=>[O(e.node.id),O(e.file),`${e.role}; model ${e.model??`session default`}; effort ${e.effort??`session default`}; sandbox ${e.sandbox}`])])),C(`## Start or resume`,``,`Place the package in the project before starting Codex. Custom agent names are the agent_type values given to spawn_agent; use fork_turns: none on fresh edges. This kickoff explicitly requests subagents. It sets the lead model when the graph supplies one (or when the graph has no lead node) and the default spawned-agent model for this invocation only. It names no approval option and writes nothing to the owner's configuration, hooks or skills.`,``,E(`codex exec --sandbox workspace-write -c agents.default_subagent_model=${X(U(i))} -c web_search=${X(U(t))}${r} -c model_reasoning_effort=${X(U(e.leadNode?.effort?e.profile.effort[e.leadNode.effort]:`high`))} - < ${X(e.paths.kickoff)}`,`sh`),``,`The owner chooses project trust and hook approvals. The package sets no approval policy: no agent file has an approval_policy key and this command has no approval option. Which approval policy then applies, to the lead under codex exec and to the agents it spawns, is unknown to this package: grooph has no run on record that shows it. Do not read the absence of a setting as the owner's configured policy applying. An unattended operation requiring owner approval may be refused when nobody can respond; that refusal is not a native graph halt. Graph gates and stops remain instructions the lead must follow. In an interactive session, paste KICKOFF.md. At a human gate the lead records outcome: halt before asking, then ends its turn. A headless run ends there. Resume with codex exec resume <session-id> and an explicit prompt naming the same grooph run id and the human's actual answer, or start a fresh session with that run id and answer. A session id is separate from the grooph run id. Silence never approves a gate.`),C(`## Hand-adjusting the package`,``,`# ${e.tiersNamed.length>0?`this export`:`profile`}: frontier → ${e.profile.models.frontier}, strong → ${e.profile.models.strong}, fast → ${e.profile.models.fast}`,``,s,``,`A node's TOML model and model_reasoning_effort are its tier or harness-specific pin and effort. A pin wins over export model overrides. Nodes with no model inherit the invocation's spawned-agent default, ${e.profile.models[e.profile.defaultTier]} (${e.profile.defaultTier}); explicit node models take precedence. The lead's settings belong to the main session at startup. Model and effort availability depends on the account and client; halt on an unavailable selection.`,``,`sandbox_mode is read-only unless edit-files or write-outputs is allowed and not denied. Then it is workspace-write. The package does not set approval_policy; which approval policy applies is unknown to this package (Start or resume, above). An unattended operation requiring owner approval may be refused when nobody can respond; that refusal is not a native graph halt. Graph gates and stops remain instructions the lead must follow. web_search is disabled unless web is explicitly allowed and not denied. By Codex's documentation a parent session's live sandbox and approval settings can supersede an agent file's; grooph has no run on record that shows it. There is no per-node native tool allowlist in this package: declared output paths, ownership, test-only commands, evidence limits and delegation limits are instructions, not filesystem or tool guarantees. read-files includes non-mutating shell reads where there is no native file-read tool; this does not grant general command execution. write-outputs can write its own named outputs while edit-files stays denied.`,``,`Node skills are explicitly requested in developer_instructions; Codex has no Claude-style skills preload field. The session must load applicable installed skills; a missing required skill halts the node. Custom capabilities are stated in the brief and require an available meaning and implementation.`,``,`Loops, edges, checks, gates, merge and stop nodes live in LEAD.md. Change stop numbers in the graph and re-export. During adaptive runs change only the run-local working copy with an amendment note. A changed or added custom-agent definition is used only after the harness confirms it is available; hot reload is unknown, so otherwise halt for a fresh session or record a proposal. Never loosen a brake or replace a critic with the lead.`),C(`## Limits and optional observation`,``,`dispatches and minutes are counted by the lead. usd, turns and tokens are advisory; Codex has no documented dollar-cap flag. A max-iterations backstop is never the acceptance bar. Concurrency and retry caps, stops and gates are lead instructions; grooph is a compiler, not a runtime or a hard budget controller.`,``,`grooph hooks install can separately add observation-only hooks; they need owner trust and review and this package neither installs nor depends on them. The lead still writes started and completed node notes and every loop pass, so grooph watch reads a Codex run without changes. Filesystem sandboxing does not provide evidence secrecy: fresh context means no inherited conversation, and the worker reads only its declared inputs and dispatched edge evidence.`))}function Mt(e,t,n={}){let r=$e(e,n),i=nt(r),a={[r.paths.graph]:d(e),[r.paths.lead]:rt(r,t),[r.paths.mapping]:jt(r),[r.paths.kickoff]:i};for(let e of r.agents)a[e.file]=qe(r,e);return{files:Object.fromEntries(Object.keys(a).sort().map(e=>[e,a[e]])),kickoff:i}}var Z=class extends Error{issues;constructor(e){let t=e.filter(e=>e.severity===`error`);super(`cannot export: ${t.length} validation error${t.length===1?``:`s`} — ${t.map(e=>e.code).join(`, `)}`),this.name=`CompileError`,this.issues=e}},Q={graphs:`saved graphs`,proposals:`proposal sets`,templates:`the project's templates`,events:`what the event hook records`,hooks:`the event hook`};function Nt(e){return Object.hasOwn(Q,e)?`A graph with the id "${e}" is not exported: its package would be placed in .grooph/${e}/, the folder grooph keeps ${Q[e]} in.`:void 0}function $(e,t,n={}){let r=ne(e);if(!r.doc)throw new Z(r.issues);let i=[...te(r.doc,{forExport:!0,loops:`errors`}),...Pt(r.doc,t)];if(re(i))throw new Z(i);let a=i.filter(e=>e.severity===`warning`);switch(t){case`codex`:{let e=Mt(r.doc,a,n);return{files:e.files,kickoff:e.kickoff,warnings:a}}case`claude-code`:{let e=Ke(r.doc,a,n);return{files:e.files,kickoff:e.kickoff,warnings:a}}default:throw Error(`unknown compile target "${String(t)}"; known targets: ${c.join(`, `)}`)}}function Pt(e,t){let n=e.target?.harness;return n===void 0||n===t||!p(n)||!p(t)?[]:[o(`E_NO_TARGET`,`the document names the harness "${n}" and the export is for "${t}": export it for ${n}, or name ${t} in the document first (grooph apply <file> --ops - --write, given [{"op":"setTarget","harness":"${t}"}]) and export into a project that does not hold this graph's ${n} package`,[e.id])]}function Ft(e,t,n={}){try{return{ok:!0,result:$(e,t,n)}}catch(e){if(e instanceof Z)return{ok:!1,issues:e.issues};throw e}}export{Z as CompileError,$ as compile,Nt as keptFolder,Ft as tryCompile};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var e=`<svg xmlns="http://www.w3.org/2000/svg" class="grooph-picture" data-picture="graph" viewBox="0 0 400 618" width="400" height="618" role="img" font-family="system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"><title>Add slugify, reviewed</title><style
|
|
1
|
+
var e=`<svg xmlns="http://www.w3.org/2000/svg" class="grooph-picture" data-picture="graph" viewBox="0 0 400 618" width="400" height="618" role="img" font-family="system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"><title>Add slugify, reviewed</title><style>:where(.grooph-picture){--gp-bg:#f1f4f3;--gp-surface:#fdfefe;--gp-surface-2:#e8edeb;--gp-ink:#1a201e;--gp-ink-2:#454c49;--gp-ink-3:#5f6764;--gp-line:#dce1df;--gp-line-strong:#b9c1be;--gp-accent:#1f5f4a;--gp-accent-soft:#dcefe6;--gp-gate:#b25e09;--gp-gate-soft:#fcf0d9;--gp-check:#2b5f9e;--gp-merge:#6b4fa0;--gp-stop:#5b5f66;--gp-ok:#1f7a4d;--gp-ok-soft:#dff3e6;--gp-warning:#955500;--gp-error:#b42318;--gp-error-soft:#fceae7;--gp-loop-0:#7a4cc2;--gp-loop-1:#0f7c8c;--gp-loop-2:#c2410c;--gp-loop-3:#b5306e}@media (prefers-color-scheme:dark){:where(.grooph-picture:not([data-theme="light"])){--gp-bg:#111514;--gp-surface:#1b201e;--gp-surface-2:#242a27;--gp-ink:#ebefed;--gp-ink-2:#c0c7c4;--gp-ink-3:#99a19e;--gp-line:#2d3331;--gp-line-strong:#4a524f;--gp-accent:#5dbb94;--gp-accent-soft:#1e3a30;--gp-gate:#eb9a45;--gp-gate-soft:#362a15;--gp-check:#6fa3e0;--gp-merge:#a88be0;--gp-stop:#9ba0a8;--gp-ok:#6fcf9d;--gp-ok-soft:#1c3a2a;--gp-warning:#f0b35a;--gp-error:#ff8f80;--gp-error-soft:#3a1e1b;--gp-loop-0:#a883f0;--gp-loop-1:#3cc0d0;--gp-loop-2:#f0874f;--gp-loop-3:#ee6fa6}}:where(.grooph-picture[data-theme="dark"]){--gp-bg:#111514;--gp-surface:#1b201e;--gp-surface-2:#242a27;--gp-ink:#ebefed;--gp-ink-2:#c0c7c4;--gp-ink-3:#99a19e;--gp-line:#2d3331;--gp-line-strong:#4a524f;--gp-accent:#5dbb94;--gp-accent-soft:#1e3a30;--gp-gate:#eb9a45;--gp-gate-soft:#362a15;--gp-check:#6fa3e0;--gp-merge:#a88be0;--gp-stop:#9ba0a8;--gp-ok:#6fcf9d;--gp-ok-soft:#1c3a2a;--gp-warning:#f0b35a;--gp-error:#ff8f80;--gp-error-soft:#3a1e1b;--gp-loop-0:#a883f0;--gp-loop-1:#3cc0d0;--gp-loop-2:#f0874f;--gp-loop-3:#ee6fa6}</style><rect x="0" y="0" width="400" height="618" style="fill:var(--gp-bg)"/><text x="12" y="35" font-size="17" font-weight="700" style="fill:var(--gp-ink)">Add slugify, reviewed</text><text x="12" y="52" font-size="11" style="fill:var(--gp-ink-3)">claude-code · 2 agents · 1 gate · 1 loop · up to 4 rounds · 10</text><text x="12" y="65" font-size="11" style="fill:var(--gp-ink-3)">dispatches</text><text x="12" y="84.5" font-size="11.5" style="fill:var(--gp-ink-2)">Add a slugify(text) function to src/strings.ts. Done when every</text><text x="12" y="99" font-size="11.5" style="fill:var(--gp-ink-2)">item in docs/REVIEW-CHECKLIST.md is shown to hold, \`pnpm</text><text x="12" y="113.5" font-size="11.5" style="fill:var(--gp-ink-2)">test\` passes, and a human approves the merge.</text><g data-edge="e-builder-critic"><path d="M179.5,187 V211.5" fill="none" stroke-width="1.5" stroke-linecap="round" style="stroke:var(--gp-ink-2)"/><path d="M179.5,216.5 l-3.6,-6.5 h7.2 z" style="fill:var(--gp-ink-2)"/></g><g data-edge="e-critic-pass"><path d="M179.5,276.5 V301" fill="none" stroke-width="1.5" stroke-linecap="round" style="stroke:var(--gp-ink-2)"/><path d="M179.5,306 l-3.6,-6.5 h7.2 z" style="fill:var(--gp-ink-2)"/></g><g data-edge="e-merge-gate-done"><path d="M179.5,366 V390.5" fill="none" stroke-width="1.5" stroke-linecap="round" style="stroke:var(--gp-ink-2)"/><path d="M179.5,395.5 l-3.6,-6.5 h7.2 z" style="fill:var(--gp-ink-2)"/></g><g data-edge="e-critic-fail"><path d="M347,246.8 H357 Q363,246.8 363,240.8 V157.3 Q363,151.3 357,151.3 H352.5" fill="none" stroke-width="1.6" stroke-linecap="round" stroke-dasharray="5 3" style="stroke:var(--gp-loop-0)"/><circle cx="347" cy="246.8" r="2.2" style="fill:var(--gp-loop-0)"/><path d="M347.5,151.3 l6.5,-3.6 v7.2 z" style="fill:var(--gp-loop-0)"/></g><g data-edge="e-merge-gate-reject"><path d="M347,336.3 H370 Q376,336.3 376,330.3 V169.3 Q376,163.3 370,163.3 H352.5" fill="none" stroke-width="1.6" stroke-linecap="round" stroke-dasharray="5 3" style="stroke:var(--gp-loop-0)"/><circle cx="347" cy="336.3" r="2.2" style="fill:var(--gp-loop-0)"/><path d="M347.5,163.3 l6.5,-3.6 v7.2 z" style="fill:var(--gp-loop-0)"/></g><g data-node="builder"><rect data-card="" x="12" y="127.5" width="335" height="59.5" rx="9" stroke-width="1" style="fill:var(--gp-surface);stroke:var(--gp-line-strong)"/><circle cx="25.5" cy="142.1" r="3.5" style="fill:var(--gp-accent)"/><text x="33" y="145.5" font-size="9.5" font-weight="700" style="fill:var(--gp-accent)">Agent</text><circle cx="333.5" cy="142.1" r="3.5" style="fill:var(--gp-loop-0)"/><text x="22" y="165" font-size="13.5" font-weight="700" style="fill:var(--gp-ink)">Builder</text><text x="22" y="178" font-size="10.5" style="fill:var(--gp-ink-2)">builder · strong · high</text></g><g data-node="critic"><rect data-card="" x="12" y="217" width="335" height="59.5" rx="9" stroke-width="1" style="fill:var(--gp-surface);stroke:var(--gp-line-strong)"/><circle cx="25.5" cy="231.6" r="3.5" style="fill:var(--gp-accent)"/><text x="33" y="235" font-size="9.5" font-weight="700" style="fill:var(--gp-accent)">Agent</text><circle cx="333.5" cy="231.6" r="3.5" style="fill:var(--gp-loop-0)"/><text x="22" y="254.5" font-size="13.5" font-weight="700" style="fill:var(--gp-ink)">Critic</text><text x="22" y="267.5" font-size="10.5" style="fill:var(--gp-ink-2)">critic · strong · high</text></g><g data-node="merge-gate"><rect data-card="" x="12" y="306.5" width="335" height="59.5" rx="9" stroke-width="1.8" style="fill:var(--gp-surface);stroke:var(--gp-gate)"/><circle cx="25.5" cy="321.1" r="3.5" style="fill:var(--gp-gate)"/><text x="33" y="324.5" font-size="9.5" font-weight="700" style="fill:var(--gp-gate)">Human gate</text><circle cx="333.5" cy="321.1" r="3.5" style="fill:var(--gp-loop-0)"/><text x="22" y="344" font-size="13.5" font-weight="700" style="fill:var(--gp-ink)">Merge approval</text><text x="22" y="357" font-size="10.5" style="fill:var(--gp-ink-2)">approve / reject with feedback</text></g><g data-node="done"><rect data-card="" x="12" y="396" width="335" height="59.5" rx="16" stroke-width="1" style="fill:var(--gp-surface);stroke:var(--gp-line-strong)"/><circle cx="25.5" cy="410.6" r="3.5" style="fill:var(--gp-stop)"/><text x="33" y="414" font-size="9.5" font-weight="700" style="fill:var(--gp-stop)">Stop</text><text x="22" y="433.5" font-size="13.5" font-weight="700" style="fill:var(--gp-ink)">Done</text><text x="22" y="446.5" font-size="10.5" style="fill:var(--gp-ink-2)">success</text></g><rect x="161.8" y="284.4" width="35.4" height="14.7" rx="7.4" stroke-width="1" style="fill:var(--gp-bg);stroke:var(--gp-ink-2)"/><text x="179.5" y="294.7" font-size="9.5" font-weight="700" text-anchor="middle" style="fill:var(--gp-ink-2)">pass</text><rect x="161.8" y="373.9" width="35.4" height="14.7" rx="7.4" stroke-width="1" style="fill:var(--gp-bg);stroke:var(--gp-ink-2)"/><text x="179.5" y="384.2" font-size="9.5" font-weight="700" text-anchor="middle" style="fill:var(--gp-ink-2)">pass</text><g transform="rotate(-90 363 199)"><rect x="350.3" y="192.5" width="25.4" height="13" rx="6.5" stroke-width="1" style="fill:var(--gp-bg);stroke:var(--gp-loop-0)"/><text x="363" y="202.2" font-size="9" font-weight="700" text-anchor="middle" style="fill:var(--gp-loop-0)">fail</text></g><g transform="rotate(-90 376 249.8)"><rect x="363.3" y="243.3" width="25.4" height="13" rx="6.5" stroke-width="1" style="fill:var(--gp-bg);stroke:var(--gp-loop-0)"/><text x="376" y="253" font-size="9" font-weight="700" text-anchor="middle" style="fill:var(--gp-loop-0)">fail</text></g><text x="12" y="484.5" font-size="12.5" font-weight="700" style="fill:var(--gp-ink)">Loop</text><g data-loop="review"><circle cx="17" cy="501.5" r="4.5" style="fill:var(--gp-loop-0)"/><text x="28" y="505.5" font-size="12" font-weight="700" style="fill:var(--gp-ink)">Review</text><text x="28" y="519" font-size="11" style="fill:var(--gp-ink-3)">judgment loop · Builder, Critic, Merge approval</text><text x="28" y="532.5" font-size="11" style="fill:var(--gp-ink-2)">Bar: Review checklist. Every checklist item is cited as satisfied</text><text x="28" y="546" font-size="11" style="fill:var(--gp-ink-2)">with a file and line, and \`pnpm test\` exits 0.</text><text x="28" y="559.5" font-size="11" style="fill:var(--gp-ink-2)">1. bar passed: follow the loop's pass exit edges</text><text x="28" y="573" font-size="11" style="fill:var(--gp-ink-2)">2. max iterations: 4: halt the run and report to the human</text><text x="28" y="586.5" font-size="11" style="fill:var(--gp-ink-2)">3. budget: 10 dispatches: halt the run and report to the</text><text x="28" y="600" font-size="11" style="fill:var(--gp-ink-2)">human</text></g></svg>
|
|
2
2
|
`,t=[{id:`grind-loop`,title:`Grind loop`,glyph:`<svg xmlns="http://www.w3.org/2000/svg" viewBox="-23 -24 153 62" width="153" height="62" role="img" fill="none" stroke-linecap="round" stroke-linejoin="round" class="grooph-glyph">
|
|
3
3
|
<title>Grind loop</title>
|
|
4
4
|
<rect x="-19" y="-20" width="101" height="54" rx="10" stroke-width="1.3" stroke-dasharray="4 3" style="stroke:var(--loop-0, #7a4cc2)"/>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=`.graph-full{z-index:2;border:1px solid var(--line-strong);background:var(--surface);min-height:36px;color:var(--ink);font:600 13px/1 var(--font);box-shadow:var(--shadow);cursor:pointer;pointer-events:auto;border-radius:10px;align-items:center;gap:6px;padding:0 12px;display:inline-flex;position:absolute;top:26px;left:10px}.graph-full svg{fill:none;stroke:currentColor;stroke-width:1.8px;stroke-linecap:round;stroke-linejoin:round;width:16px;height:16px}.graph-views.is-focus{z-index:100;background:var(--bg);pointer-events:auto;position:fixed}.graph-views.is-focus>.graph-switch{display:none}.graph-views.is-focus>.graph-space{padding-bottom:max(12px, env(safe-area-inset-bottom))}.graph-views.is-focus :is(.s3-more,.s3-bar>span,.space-note,.space-bar>span){display:none}.graph-views.is-focus :is(.graph-full,.graph-kinds,.s3-bar,.s3-steps,.s3-says,.space-bar,.space-time){transition:opacity .3s ease-out}.graph-views.is-idle :is(.graph-full,.graph-kinds,.s3-bar,.s3-steps,.s3-says,.space-bar,.space-time){opacity:0;pointer-events:none}@media (prefers-reduced-motion:reduce){.graph-views.is-focus :is(.graph-full,.graph-kinds,.s3-bar,.s3-steps,.s3-says,.space-bar,.space-time){transition:none}}`;export{e as t};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{t as e}from"./index-B5ZkSF4L.js";var t={yaw:-.5,pitch:.86},n=({arch:e,by:t,card:n,circle:r,edgeLine:i,ground:a,hue:o,stations:s,TAU:c,under:l})=>(u,d)=>{let f=[],p=Object.create(null),m=Object.create(null),h=Object.create(null),g=e=>{let t=u.loops.filter(t=>t.inside===e.id).map(g),n=s(u,e).length;return Math.max(e.inside?60:84,n*(e.inside?30:40),...t.map(e=>e+30),t.length>1?(Math.max(...t)+20)/Math.sin(Math.PI/n):0)},_=e=>g(e)+Math.max(0,...u.loops.filter(t=>t.inside===e.id).map(_)),v=e=>64+Math.max(0,1.16*g(e)*Math.cos(Math.PI/s(u,e).length)),y=e=>(Math.PI-.5)%(c/e)-Math.PI/e,b=(e,t,n,r)=>(n=>[e[0]+t*Math.sin(n),e[1],e[2]-t*Math.cos(n)])(c*n/r+(e[1]?y(r):0));function x(e,i){let a=s(u,e),c=g(e);m[e.id]={c:i,r:c,stops:a};let l=o(u,e.id);f.push({t:`poly`,pts:[...r(i,c+15,i[1],48),...r(i,c-15,i[1],48).reverse()],fill:l,fa:.2,key:`loop:${e.id}`});let h=u.run?d.until?.[e.id]??null:void 0;f.push({t:`text`,at:[i[0]-(c+24)*.65,i[1],i[2]+(c+24)*.76],text:`${e.name}${h===void 0?``:h===null?`
|
|
2
|
+
not entered`:`\nround ${Math.floor(h.now)}`}`,align:`right`,bold:!0,fill:l,size:10.5,max:110}),a.forEach((e,r)=>{let s=b(i,c,r,a.length);if(e.node)p[e.node]=s,f.push(n(t(u.nodes,e.node),s,{stand:!0,small:u.narrow}));else if(e.loop){let t=[s[0],s[1]+v(e.loop),s[2]];f.push({t:`line`,pts:[s,t],stroke:o(u,e.loop.id),w:1.2,dash:[2,3]},{t:`dot`,at:s,r:4,fill:o(u,e.loop.id),lift:-3990}),x(e.loop,t)}else e.away&&f.push({t:`text`,at:s,text:`${t(u.nodes,e.away).name}: on ${t(u.loops,t(u.nodes,e.away).loop).name}`,align:`center`,fill:`ink-3`,size:10.5,max:96})})}let S=u.narrow?Math.tan(.5):0,C=a(u),w=0;for(let e of C)if(e.node)p[e.node]=[w*S,0,w],f.push(n(t(u.nodes,e.node),p[e.node],{stand:!0,side:!0,small:u.loops.length>0}),{t:`dot`,at:p[e.node],r:3,fill:`ink-2`,lift:-3990}),w+=78;else if(e.loop){let t=_(e.loop)+6,n=e=>(e=>e?.86*v(e)+n(e):0)(s(u,e)[0]?.loop);w+=w?56+n(e.loop):0,x(e.loop,[(w+t)*S,0,w+t]),w+=2*t+58}let T=(e,n)=>e.findIndex(e=>e.node===n||!!e.loop&&(t(u.nodes,n).loop===e.loop.id||l(u.loops,t(u.nodes,n).loop??``,e.loop.id))),E=e=>d.took.some(t=>t.edge===e&&(d.k===0||t.step<=d.k)),D=(e,t,n,r)=>{let i=p[e.id],[a,o,s,c]=[n[0]-t[0],n[2]-t[2],i[0]-t[0],i[2]-t[2]],l=Math.hypot(a,o)||1,u=Math.max(0,Math.min(1,(s*a+c*o)/(l*l))),d=((c-o*u)*a-(s-a*u)*o)/(o<0?-l:l),f=e.loop?62:34;return(r===null?Math.hypot(s-a*u,c-o*u):u>.06&&u<.94?Math.abs(d-r*4*u*(1-u)):f)+6<f?(f+(r===null?Math.abs(d):d))/Math.max(.36,4*u*(1-u)):0},O=(e,t,n)=>{let r=u.nodes.filter(t=>t.id!==e.from&&t.id!==e.to),i=r.reduce((e,r)=>e+D(r,t,n,null),0);for(let e of i?[...r,...r,...r]:[])i=Math.max(i,D(e,t,n,i));return i};u.edges.forEach((t,n)=>{let[a,o]=[p[t.from],p[t.to]],s=u.edges.slice(0,n).filter(e=>e.from===t.from&&e.to===t.to).length,l=u.loops.filter(e=>e.members.includes(t.from)&&e.members.includes(t.to)).sort((e,t)=>e.members.length-t.members.length)[0],d;if(l){let{c:e,r:n,stops:r}=m[l.id],[i,c,u]=[T(r,t.from),T(r,t.to),r.length],f=(t,r)=>Array.from({length:25},(i,a)=>b(e,n+16*s,t+(r-t)*a/24,u));i>=0&&c>=0&&!t.back&&c===i+1?d=f(i,c):i>=0&&c>=0&&t.back===l.id&&i===u-1&&c===0&&(d=f(i,u)),d&&=[...r[i].loop||s?[a]:[],...d,...r[c%u].loop||s?[o]:[]]}if(!d&&t.from===t.to){let e=l?m[l.id].c:[a[0]+1,0,a[2]],t=(26+8*s)/(Math.hypot(a[0]-e[0],a[2]-e[2])||1),n=[a[0]+(a[0]-e[0])*t,a[1],a[2]+(a[2]-e[2])*t],i=Math.atan2(a[2]-n[2],a[0]-n[0]);d=r(n,26+8*s,a[1],24,i,i+c)}let[g,_]=[O(t,a,o),Math.hypot(o[0]-a[0],o[2]-a[2])||1],[v,y]=[(o[2]-a[2])/_,(o[0]-a[0])/_].map(e=>o[2]<a[2]?-e:e);h[t.id]=d??e(a,o,(t.back?54:0)+22*s,18).map((e,t)=>[e[0]-v*g*4*(t/18)*(1-t/18),e[1],e[2]+y*g*4*(t/18)*(1-t/18)]),f.push(i(u,t,h[t.id],u.run&&!E(t.id)?{alpha:.3}:{}))});for(let e of f)e.t!==`card`&&(e.grow=!0);return{prims:f,node:e=>p[e],path:e=>h[e]}},r={frontier:104,strong:68,fast:34,unset:16},[i,a]=[.76,.94],o=7.5,s=e=>`${Math.round(e*10)/10} min`;function c(e,t){let n=[];return e.run?.dispatches.forEach((r,i)=>{if(t.dispatches!==void 0&&i>=t.dispatches)return;let a=n.find(e=>e.id===r.node)??n[n.push({id:r.node,name:e.nodes.find(e=>e.id===r.node).name,words:[],total:``,sum:0,some:!1,more:!1})-1];a.words.push(`${r.round===null?``:`${e.loops.find(e=>e.id===r.loop)?.name??``} round ${r.round}, `}${r.minutes===null?`no time`:s(r.minutes)}${r.outcome===`pass`||!r.outcome?``:`, ${r.outcome}`}`),a.sum+=Math.round((r.minutes??0)*10)/10,a.some||=r.minutes!==null,a.more||=r.minutes===null,a.total=a.more?a.some?`${s(a.sum)} and more`:`no time`:s(a.sum)}),n}var l={yaw:-.18,pitch:.44},u=({card:e,edgeLine:t,hue:n,lerp:s})=>(l,u)=>{let d=[],f=Object.create(null),p=(e,t,n,r,i,a,o={})=>{let[s,c,l,u]=[e[0]-t/2,e[0]+t/2,e[2]-n/2,e[2]+n/2],f=(e,t)=>void d.push({t:`poly`,pts:e,fill:a,fa:t,stroke:`card`,w:.8,...o});f([[s,r,l],[c,r,l],[c,i,l],[s,i,l]],.5),f([[s,r,l],[s,r,u],[s,i,u],[s,i,l]],.62),f([[c,r,l],[c,r,u],[c,i,u],[c,i,l]],.62),f([[s,r,u],[c,r,u],[c,i,u],[s,i,u]],.8),f([[s,i,l],[c,i,l],[c,i,u],[s,i,u]],1)};for(let t of l.nodes){let n=f[t.id]=[t.at[0]*i,0,t.at[1]*a],s=5;if(l.run){p(n,66,42,0,4,`floor`),s=4,l.run.dispatches.forEach((e,r)=>{if(e.node!==t.id||u.dispatches!==void 0&&r>=u.dispatches)return;let i=Math.max(1,(e.minutes??0)*o);p(n,58,36,s,s+i,e.outcome===`pass`?`ok`:e.outcome===`fail`?`bad`:`ink-3`,{key:`node:${t.id}`,...e.minutes===null?{alpha:.4}:{}}),s+=i});let e=c(l,u).find(e=>e.id===t.id);e&&d.push({t:`text`,at:[n[0]-33,s,n[2]+18],text:e.total,align:`right`,size:10,fill:`ink-2`,or:[{at:[n[0],s+12,n[2]],align:`center`}]})}else t.tier?(s=r[t.tier]??r.unset,p(n,58,36,0,s,`k-agent`,{key:`node:${t.id}`,...t.tier===`unset`?{alpha:.4}:{}}),d.push({t:`text`,at:[n[0]-33,s/2,n[2]+18],text:t.tier===`unset`?`session default`:t.tier,align:`right`,size:10,fill:`ink-2`,or:[{at:[n[0],s+12,n[2]],align:`center`}]})):p(n,58,36,0,5,`floor`,{key:`node:${t.id}`});d.push(e(t,[n[0]+29,0,n[2]+18],{stand:!0,side:!0,small:l.narrow,moves:[[i,0,0],[0,0,a]]}))}for(let e of l.loops.filter(e=>e.members.length>0)){let t=e.members.map(e=>f[e]),r=e.inside?0:12,[i,a,o,s]=[Math.min(...t.map(e=>e[0]))-48-r,Math.max(...t.map(e=>e[0]))+48+r,Math.min(...t.map(e=>e[2]))-38-r,Math.max(...t.map(e=>e[2]))+38+r];d.push({t:`poly`,pts:[[i,-1,o],[a,-1,o],[a,-1,s],[i,-1,s]],fill:n(l,e.id),fa:.14,stroke:n(l,e.id),key:`loop:${e.id}`,lift:-600}),d.push({t:`text`,at:[i-4,0,s],text:e.name,align:`right`,fill:n(l,e.id),size:10.5,bold:!0,or:[{at:[a+4,0,s]}]})}let m=Object.create(null),h=(e,t)=>{let[n,r]=[t[0]-e[0],t[2]-e[2]],i=n||r?Math.min(.5,35/Math.abs(n||1e-9),24/Math.abs(r||1e-9)):0;return n||r?[e[0]+n*i,1,e[2]+r*i]:[e[0]-35,1,e[2]]},g=e=>u.took.some(t=>t.edge===e&&(u.k===0||t.step<=u.k));l.edges.forEach((e,n)=>{let[r,i]=[h(f[e.from],f[e.to]),h(f[e.to],f[e.from])],a=l.edges.slice(0,n).filter(t=>t.from===e.from&&t.to===e.to||t.from===e.to&&t.to===e.from).length,o=Math.hypot(i[0]-r[0],i[2]-r[2]),[c,u]=o?((e,t)=>e>0||!e&&t<0?[-e,-t]:[e,t])((r[2]-i[2])/o,(i[0]-r[0])/o):[-1,0],p=(e.back||!o?34+o*.16:0)+18*a;for(let t of o?[...l.nodes,...l.nodes,...l.nodes]:[]){if(t.id===e.from||t.id===e.to)continue;let[n,a]=[f[t.id][0]-r[0],f[t.id][2]-r[2]],s=(n*(i[0]-r[0])+a*(i[2]-r[2]))/(o*o),l=n*c+a*u;s>.06&&s<.94&&Math.abs(l-p*4*s*(1-s))<44&&(p=Math.max(p,(44+l)/Math.max(.36,4*s*(1-s))))}m[e.id]=p?Array.from({length:19},(e,t)=>(e=>[e[0]+c*p*4*(t/18)*(1-t/18),e[1],e[2]+u*p*4*(t/18)*(1-t/18)])(s(r,i,t/18))):[r,i],d.push(t(l,e,m[e.id],l.run&&!g(e.id)?{alpha:.3}:{}))});for(let e of d)e.t!==`card`&&(e.grow=!0);return{prims:d,node:e=>f[e],path:e=>m[e]}},d=e(),f=e=>({rings:{view:n(e),start:t,as:`a ring for each loop`,apart:7,says:`Each loop is a ring, with its own nodes around it in the order of a first pass. A loop inside another is a ring standing on the outer one; a node two loops share stands on one of them. A way back from a loop's last node to its first is the rest of the ring.`,under:(e,t)=>e.run&&e.loops.length?(0,d.jsx)(`ul`,{className:`s3-key`,"aria-label":`The round the run is in, loop by loop`,children:e.loops.map((e,n)=>{let r=t.until?.[e.id];return(0,d.jsxs)(`li`,{children:[(0,d.jsx)(`b`,{style:{color:`var(--loop-${n%4})`},children:e.name}),` `,r?`round ${Math.floor(r.now)}`:`not entered`]},e.id)})}):null},columns:{view:u(e),start:l,as:`columns`,apart:7,moves:!0,says:`Every node stands where the picture has it. On a template an agent's column is taller for a higher tier, frontier over strong over fast: the order the document asks for, not a price and not a model (a profile can give two tiers one model, and a pin names a model whatever the tier). A check, a gate, a stop or a person's step is a slab. On a run a column is a block for each dispatch, as tall as the minutes between its own start and end stamps, and a faint line where they do not say.`,under:(e,t)=>e.run?(0,d.jsx)(`ul`,{className:`s3-key`,"aria-label":`Each node's dispatches so far`,children:c(e,t).map(e=>(0,d.jsxs)(`li`,{children:[(0,d.jsx)(`b`,{children:e.name}),` `,e.words.join(`; `)]},e.id))}):null}});export{f as MORE};
|