grooph 0.4.1 → 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.
Files changed (57) hide show
  1. package/README.md +33 -31
  2. package/THIRD-PARTY-NOTICES.md +5 -1
  3. package/dist/app/assets/App-CT9KxzfQ.css +1 -0
  4. package/dist/app/assets/App-D1pDFbEY.js +5 -0
  5. package/dist/app/assets/EmbedApp-CTausQWY.js +1 -0
  6. package/dist/app/assets/ExportPanel-CbRH4C69.js +8 -0
  7. package/dist/app/assets/Import-DF0XLxRI.js +1 -0
  8. package/dist/app/assets/SketchView-BjeUHG4X.js +143 -0
  9. package/dist/app/assets/{brakes-BJPDrrJT.js → brakes-B9b3hDX8.js} +1 -1
  10. package/dist/app/assets/{builtins-D_22fKRF.js → builtins-DzyPaYdd.js} +1 -1
  11. package/dist/app/assets/{compile-BAvi-rd0.js → compile-DL2budwP.js} +8 -8
  12. package/dist/app/assets/{front-BRl7rl-u.js → front-OxfL-OaD.js} +1 -1
  13. package/dist/app/assets/full-C0asYORc.js +1 -0
  14. package/dist/app/assets/graph-more-Dn_hNa20.js +2 -0
  15. package/dist/app/assets/graph-stage-CwNePnN4.js +4 -0
  16. package/dist/app/assets/graph-views-CmkQ41zC.js +2 -0
  17. package/dist/app/assets/{index-CeaKwKuU.js → index-B5ZkSF4L.js} +2 -2
  18. package/dist/app/assets/mermaid-read-cyqJ32v-.js +17 -0
  19. package/dist/app/assets/{plan-templates-BQu8G2sX.js → plan-templates-u2fF3GnS.js} +1 -1
  20. package/dist/app/assets/runs-BA5HKtsx.js +1 -0
  21. package/dist/app/assets/screens-CMMuexid.js +16 -0
  22. package/dist/app/assets/share-DV3wsAsJ.js +8 -0
  23. package/dist/app/assets/site-looks.v1.css +223 -0
  24. package/dist/app/assets/space-Cuy4YqyZ.js +1 -0
  25. package/dist/app/assets/styles-Dlp-yYlE.css +1 -0
  26. package/dist/app/assets/templates-DbAhWvP6.js +2 -0
  27. package/dist/app/assets/{themes-DtOBrLk0.js → themes-C34TrvOJ.js} +1 -1
  28. package/dist/app/assets/{units-DOIUgOIU.js → units-DlniUjfH.js} +1 -1
  29. package/dist/app/assets/{views-CLwg5VNv.js → views-CFqRShXD.js} +2 -1
  30. package/dist/app/grooph-app.json +1 -1
  31. package/dist/app/index.html +14 -11
  32. package/dist/app/llms.txt +6 -7
  33. package/dist/app/manifest.webmanifest +1 -1
  34. package/dist/app/og.png +0 -0
  35. package/dist/app/routes.json +42 -33
  36. package/dist/app/sw.js +7 -2
  37. package/dist/browser/grooph.d.ts +44 -0
  38. package/dist/browser/grooph.global.js +7 -0
  39. package/dist/browser/grooph.js +7 -0
  40. package/dist/bundle/grooph.js +6734 -2536
  41. package/hooks/grooph-event.mjs +163 -14
  42. package/package.json +14 -6
  43. package/dist/app/assets/App-48y1Ec5B.js +0 -5
  44. package/dist/app/assets/App-Cv1auMeh.css +0 -1
  45. package/dist/app/assets/EmbedApp-390Z2d4x.js +0 -1
  46. package/dist/app/assets/ExportPanel-Ca3wBB38.js +0 -8
  47. package/dist/app/assets/Import-ByMI7Uz9.js +0 -1
  48. package/dist/app/assets/graph-more-393HTgQE.js +0 -2
  49. package/dist/app/assets/graph-stage-BkrQGQ-z.js +0 -4
  50. package/dist/app/assets/graph-views-COQF_QTQ.js +0 -2
  51. package/dist/app/assets/runs-DcjRi7S6.js +0 -1
  52. package/dist/app/assets/screens-aIjMSzZg.js +0 -16
  53. package/dist/app/assets/share-BzIzB4EY.js +0 -8
  54. package/dist/app/assets/space-DWgrlnfB.js +0 -1
  55. package/dist/app/assets/styles-BA1aVO9R.css +0 -1
  56. package/dist/app/assets/templates-CoNTzuuz.js +0 -2
  57. package/dist/app/demo/run.txt +0 -1
@@ -1 +1 @@
1
- import{cn as e}from"./share-BzIzB4EY.js";import{p as t}from"./templates-CoNTzuuz.js";var n=Object.assign({"../../../../patterns/contradiction-seeker.grooph.json":{grooph:0,id:`contradiction-seeker`,name:`Contradiction seeker`,version:1,goal:`{{task}} Done when a critic with a fixed budget finds no counterexample to this: {{claim}}`,target:{harness:`claude-code`},template:{kind:`graph`,title:`Contradiction seeker`,summary:`A critic hunts one counterexample to a stated claim on a fixed budget; finding none is a pass, finding one sends it back to the builder.`,whenToUse:`A claim can be broken by one concrete counterexample (a property, an invariant, an edge case), and a bounded search for it is worth more than an open-ended review.`,notFor:`Claims no single input can break (style, clarity), or proving correctness: a hunt that finds nothing is evidence, not proof.`,profile:{cost:`low`,speed:`fast`,rigor:`standard`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add a slugify(text) function to src/strings.ts that lowercases, strips accents and joins words with single hyphens.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`},{key:`claim`,ask:`What claim must hold, stated so that one counterexample would break it?`,example:`slugify(text) always returns lowercase ASCII words joined by single hyphens, and slugify(slugify(text)) equals slugify(text).`}],tags:[`loop`,`counterexample`,`budget`],demo:`experiments/patterns/contradiction-seeker/README.md`},description:`A fast builder works; a critic in a fresh context tries to break the stated claim with one concrete counterexample, on a fixed effort. A counterexample goes back to the builder as a failing case; none found is a pass. The hunt is bounded by design: the critic's brief caps how many attempts it makes, and the loop's budget and round cap end the loop regardless.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:"Make the task true in the code, with tests, so that the claim holds. On a later round, turn the counterexample in COUNTEREXAMPLE.md into a test and make it pass without special-casing that one input. Run `{{test-command}}` before you report.",inputs:[`the task`,`the claim: {{claim}}`,`COUNTEREXAMPLE.md (from round 1 on)`],outputs:[`the change, with tests`,`CHANGES.md`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`critic`,kind:`agent`,name:`Counterexample hunter`,role:`critic`,model:{tier:`strong`},effort:`medium`,brief:`Try to break the claim with one concrete counterexample: an input, how to run it, and the wrong result. Stop at the first one you can reproduce, or after about ten distinct attempts; finding none is a pass. You hunt; you do not fix.`,inputs:[`the claim: {{claim}}`,`diff of the change`,`the repository as the change leaves it, read-only`],outputs:[`COUNTEREXAMPLE.md: the reproducible counterexample, or the attempts that failed to find one`,`verdict: pass | fail`],allow:[`read-files`,`write-outputs`,`run-commands`,`run-tests`],deny:[`edit-files`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-critic`,from:`builder`,to:`critic`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`,`output of {{test-command}}`]},{id:`e-critic-fail`,from:`critic`,to:`builder`,when:`fail`,evidence:[`COUNTEREXAMPLE.md`]},{id:`e-critic-pass`,from:`critic`,to:`done`,when:`pass`}],loops:[{id:`hunt`,name:`Hunt`,members:[`builder`,`critic`],back:[`e-critic-fail`],mode:`judgment`,bar:{name:`No counterexample`,inspects:[{kind:`artifact`,ref:`COUNTEREXAMPLE.md`},{kind:`artifact`,ref:`output of {{test-command}}`}],acceptance:"A full hunt finds no input that breaks the claim, and `{{test-command}}` exits 0."},stops:[{kind:`bar-passed`},{kind:`budget`,measure:`dispatches`,limit:8},{kind:`max-iterations`,n:3}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/debate-then-build.grooph.json":{grooph:0,id:`debate-then-build`,name:`Debate then build`,version:1,goal:"{{task}} Done when a judged plan is approved by a human and its first version passes `{{test-command}}`.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Debate then build`,summary:`Two planners argue opposite approaches for a capped number of rounds, a judge writes the plan, a human approves it, and a small grind loop builds it.`,whenToUse:`Short adversarial planning, then a small build graph: the right approach is genuinely unclear and a wrong choice is expensive to undo.`,notFor:`Work with an obvious approach, or where drafts are cheaper than arguments (use tournament-then-judge).`,profile:{cost:`medium`,speed:`medium`,rigor:`standard`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Choose and build a caching approach for the slow /search endpoint in src/api/search.ts.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`}],tags:[`planning`,`debate`,`judge`,`human-gate`,`grind`],demo:`experiments/patterns/debate-then-build/README.md`},description:`One planner argues for the simplest approach that could work, the other against it and for the strongest alternative. A frontier judge reads both cases and either asks for one more exchange or writes PLAN.md; the debate has a round cap, after which the judge must decide. A human approves the plan, and a fast builder grinds the first version against the tests.`,nodes:[{id:`planner-a`,kind:`agent`,name:`Planner A`,role:`planner`,model:{tier:`strong`},effort:`high`,brief:`Argue for the simplest approach that could do the task, and answer the other side's strongest point when there is one. Keep your case under a page in CASE-A.md and concede the risks that are real. Write no code.`,inputs:[`the task`,`CASE-B.md and REBUT.md (from round 1 on)`],outputs:[`CASE-A.md`],allow:[`read-files`,`write-outputs`]},{id:`planner-b`,kind:`agent`,name:`Planner B`,role:`planner`,model:{tier:`strong`},effort:`high`,brief:`Argue against the approach in CASE-A.md and for the strongest alternative, answering its best points directly. Keep your case under a page in CASE-B.md and concede the risks that are real. Write no code.`,inputs:[`the task`,`CASE-A.md`],outputs:[`CASE-B.md`],allow:[`read-files`,`write-outputs`]},{id:`judge`,kind:`agent`,name:`Judge`,role:`judge`,model:{tier:`frontier`},effort:`high`,brief:`Read both cases. If one more exchange would change your choice, emit verdict rebut and write REBUT.md naming the point each side must answer; otherwise, or when the debate is out of rounds, write PLAN.md with the approach, why it won, and the smallest first version with its checks, and emit pass. You decide; you write no code.`,inputs:[`CASE-A.md`,`CASE-B.md`],outputs:[`PLAN.md, or REBUT.md when asking for another round`,`verdict: pass | rebut`],allow:[`read-files`,`write-outputs`],deny:[`edit-files`]},{id:`plan-gate`,kind:`human-gate`,name:`Approve the plan`,prompt:`Build the first version PLAN.md describes?`,options:[`approve`,`reject`]},{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:`Build the first version PLAN.md describes and nothing past it, with tests. On a later round, start from the failing output you are handed. Report what you built and anything in the plan that proved wrong.`,inputs:[`PLAN.md`,`failing test output (from round 1 on)`],outputs:[`the change, with tests`,`CHANGES.md`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`tests`,kind:`check`,name:`Tests`,check:{kind:`tests`,run:`{{test-command}}`,pass:`exit code 0 and no test skipped`}},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-planner-a-planner-b`,from:`planner-a`,to:`planner-b`,evidence:[`CASE-A.md`]},{id:`e-planner-b-judge`,from:`planner-b`,to:`judge`,evidence:[`CASE-A.md`,`CASE-B.md`]},{id:`e-judge-rebut`,from:`judge`,to:`planner-a`,when:{verdict:`rebut`},evidence:[`CASE-B.md`,`REBUT.md`]},{id:`e-judge-plan-gate`,from:`judge`,to:`plan-gate`,when:`pass`},{id:`e-plan-gate-builder`,from:`plan-gate`,to:`builder`,when:`pass`},{id:`e-builder-tests`,from:`builder`,to:`tests`},{id:`e-tests-fail`,from:`tests`,to:`builder`,when:`fail`,evidence:[`test output`]},{id:`e-tests-pass`,from:`tests`,to:`done`,when:`pass`}],loops:[{id:`debate`,name:`Debate`,members:[`planner-a`,`planner-b`,`judge`],back:[`e-judge-rebut`],mode:`judgment`,bar:{name:`Both cases answered`,inspects:[{kind:`file`,ref:`CASE-A.md`},{kind:`file`,ref:`CASE-B.md`}],acceptance:`Each case answers the other's strongest point, and the judge has written PLAN.md.`},stops:[{kind:`bar-passed`},{kind:`max-iterations`,n:2,then:`judge`},{kind:`budget`,measure:`dispatches`,limit:8}]},{id:`build`,name:`Build`,members:[`builder`,`tests`],back:[`e-tests-fail`],mode:`grind`,stops:[{kind:`max-iterations`,n:5},{kind:`budget`,measure:`minutes`,limit:30}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/dual-bar.grooph.json":{grooph:0,id:`dual-bar`,name:`Dual bar`,version:1,goal:`{{task}} Ship when this holds: {{ship-line}} Aim toward: {{aspiration}}`,target:{harness:`claude-code`},template:{kind:`graph`,title:`Dual bar`,summary:`A builder and an isolated critic loop against two lines: a reachable ship line that stops the loop, and an aspiration that only steers the findings.`,whenToUse:`The work needs both a ship line and a directional aspiration: good enough to ship is clear, and better is worth pointing at without ever blocking.`,notFor:`Work where only the aspiration is written down; a bar that cannot be met never stops, so write the ship line first.`,profile:{cost:`medium`,speed:`medium`,rigor:`standard`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add a GET /users/:id endpoint to the API in src/api/.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`},{key:`ship-line`,ask:`What must be true to ship? A reachable line the critic can check.`,example:"`pnpm test` passes, GET /users/:id returns 404 for unknown ids, and the README documents the endpoint."},{key:`aspiration`,ask:`What would great look like? Direction only: it never blocks shipping.`,example:`A new contributor can use the endpoint correctly from the README alone, on the first try.`}],tags:[`loop`,`review`,`bar`],demo:`experiments/patterns/dual-bar/README.md`},description:`A builder works and a frontier critic in a fresh context reports against both lines of the bar. The acceptance (the ship line) decides the verdict; the aspiration only ranks what to improve next and never fails a round. The loop stops when the ship line holds, when rounds stop bringing improvement, at its round cap, or at its dispatch budget.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"Do the task until the ship line holds, then spend any remaining rounds on the aspiration findings the critic ranks highest. Run `{{test-command}}` before you report; on a later round, start from REVIEW.md.",inputs:[`the task`,`the ship line: {{ship-line}}`,`the aspiration: {{aspiration}}`,`REVIEW.md (from round 1 on)`],outputs:[`the change, with tests`,`CHANGES.md: what changed this round`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`critic`,kind:`agent`,name:`Critic`,role:`critic`,model:{tier:`frontier`},effort:`high`,brief:`Judge the change against both lines of the bar and say, with file and line, where it stands on each. The ship line alone decides the verdict: pass when it holds. The aspiration never fails a round; use it only to rank what to improve next. You judge; you do not fix.`,inputs:[`the ship line: {{ship-line}}`,`the aspiration: {{aspiration}}`,`diff of the change`,`the repository as the change leaves it, read-only`],outputs:[`REVIEW.md: the ship line item by item, then ranked aspiration findings, then a verdict line`,`verdict: pass | fail | invalid-evidence`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-critic`,from:`builder`,to:`critic`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`,`output of {{test-command}}`]},{id:`e-critic-fail`,from:`critic`,to:`builder`,when:`fail`,evidence:[`REVIEW.md`]},{id:`e-critic-pass`,from:`critic`,to:`done`,when:`pass`}],loops:[{id:`review`,name:`Review`,members:[`builder`,`critic`],back:[`e-critic-fail`],mode:`judgment`,bar:{name:`Ship line and aspiration`,inspects:[{kind:`artifact`,ref:`diff of the change`},{kind:`artifact`,ref:`output of {{test-command}}`}],acceptance:`{{ship-line}}`,aspiration:`{{aspiration}}`},stops:[{kind:`bar-passed`},{kind:`diminishing-returns`,rounds:2},{kind:`max-iterations`,n:5},{kind:`budget`,measure:`dispatches`,limit:12}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/fresh-grind-rare-judge.grooph.json":{grooph:0,id:`fresh-grind-rare-judge`,name:`Fresh grind, rare judge`,version:1,goal:"{{task}} Done when every phase in {{phase-checklist}} has passed the judge and `{{test-command}}` passes.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Fresh grind, rare judge`,summary:`A fast builder grinds each phase against the tests; an expensive judge runs only at phase boundaries, against a phase checklist.`,whenToUse:`Cheap iteration most steps, an expensive critic at phase boundaries: long work splits into phases that tests can drive but only judgment can sign off.`,notFor:`Short tasks with one phase (use grind-loop or review-gate), or work where the tests cannot drive any of the steps.`,profile:{cost:`medium`,speed:`medium`,rigor:`high`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Migrate the storage layer in src/storage/ from callbacks to async/await, one module per phase.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`},{key:`phase-checklist`,ask:`Which file lists the phases, each with what the judge checks at its boundary?`,example:`docs/PHASES.md`}],tags:[`loop`,`grind`,`judge`,`phases`],demo:`experiments/patterns/fresh-grind-rare-judge/README.md`},description:`An inner grind loop, a fast builder against the tests, runs inside an outer judgment loop. Only when the tests pass does a frontier judge review the phase against the phase checklist: fail returns to the builder to fix the phase, next-phase returns it to start the next one, and pass ends the run. Both loops are budgeted.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:"Work through the current phase of {{phase-checklist}} until `{{test-command}}` passes; one phase at a time, never ahead. On a failing test, start from its output; after a judge review, start from PHASE-REVIEW.md. Report which phase you are on and what changed.",inputs:[`the task`,`{{phase-checklist}}`,`test output or PHASE-REVIEW.md (from round 1 on)`],outputs:[`the change for this phase, with tests`,`CHANGES.md: the phase and what changed`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`tests`,kind:`check`,name:`Tests`,check:{kind:`tests`,run:`{{test-command}}`,pass:`exit code 0 and no test skipped`}},{id:`judge`,kind:`agent`,name:`Phase judge`,description:`Runs only at phase boundaries: after the tests pass.`,role:`judge`,model:{tier:`frontier`},effort:`high`,brief:`Judge the finished phase against its entry in {{phase-checklist}}, citing file and line for each item. Emit fail with findings when the phase is not done, next-phase when it is and phases remain, and pass when the last phase is done. You judge; you do not fix.`,inputs:[`diff since the last phase boundary`,`the repository as the change leaves it, read-only`,`{{phase-checklist}}`],outputs:[`PHASE-REVIEW.md: the phase item by item and a verdict line`,`verdict: pass | fail | next-phase`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-tests`,from:`builder`,to:`tests`},{id:`e-tests-fail`,from:`tests`,to:`builder`,when:`fail`,evidence:[`test output`]},{id:`e-tests-judge`,from:`tests`,to:`judge`,when:`pass`,evidence:[`diff since the last phase boundary`,`the repository as the change leaves it, read-only`,`{{phase-checklist}}`,`test output`]},{id:`e-judge-fail`,from:`judge`,to:`builder`,when:`fail`,evidence:[`PHASE-REVIEW.md`]},{id:`e-judge-next-phase`,from:`judge`,to:`builder`,when:{verdict:`next-phase`},evidence:[`PHASE-REVIEW.md`]},{id:`e-judge-pass`,from:`judge`,to:`done`,when:`pass`}],loops:[{id:`grind`,name:`Grind`,members:[`builder`,`tests`],back:[`e-tests-fail`],mode:`grind`,stops:[{kind:`max-iterations`,n:5},{kind:`budget`,measure:`minutes`,limit:20}]},{id:`phases`,name:`Phases`,members:[`builder`,`tests`,`judge`],back:[`e-judge-fail`,`e-judge-next-phase`],mode:`judgment`,bar:{name:`Phase checklist`,inspects:[{kind:`checklist`,ref:`{{phase-checklist}}`},{kind:`artifact`,ref:`output of {{test-command}}`}],acceptance:"Every phase in {{phase-checklist}} is signed off by the judge with evidence, and `{{test-command}}` exits 0."},stops:[{kind:`bar-passed`},{kind:`max-iterations`,n:5},{kind:`budget`,measure:`dispatches`,limit:55}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/gauntlet-decomposed.grooph.json":{grooph:0,id:`gauntlet-decomposed`,name:`Gauntlet, decomposed`,version:2,goal:`{{task}} Done when every piece in PIECES.md is ticked by its critic against {{reference}}, the whole has been compared once more, and the human releases it.`,target:{harness:`claude-code`},template:{kind:`graph`,title:`Gauntlet, decomposed`,summary:`A planner cuts the work into pieces and a human approves the cut; one piece at a time an owner polishes while a fresh frontier critic compares captures blind against the reference; an integrator and a final blind comparison close it.`,whenToUse:`A rendered artifact must match a named reference and is too large for one owner and one critic: the pieces have real seams, each can be captured and judged alone, and they are coupled enough to build in sequence.`,notFor:`One small artifact (taste-polish), no reference (spec-then-loop), or independent pieces that could fan out (ownership-not-swarm); never a way to keep going until the reference loses.`,profile:{cost:`high`,speed:`slow`,rigor:`high`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a planner can cut into pieces.`,example:"Make the summary card rendered by `npm run render` from src/card.mjs match the reference card, piece by piece."},{key:`reference`,ask:`What named reference should the result be compared against: a file, a URL or a capture?`,example:`references/card-reference.svg`},{key:`capture-command`,ask:`Which command captures the artifact for the critics (screenshots, a rendered file, a text dump)?`,example:`npm run capture`}],tags:[`loop`,`gauntlet`,`reference`,`nested`],demo:`experiments/patterns/gauntlet-decomposed/README.md`,credits:[{name:`Matt Shumer's Gauntlet Loop and the Claude of Duty repository`,url:`https://github.com/mshumer/Claude-of-Duty`,note:`decompose, then builder plus fresh critic per piece against a real reference, blind side by side; here bounded, and pieces run in sequence because the repository's own note says fan-out lost on coupled work`}]},description:`The Gauntlet with its pieces, bounded. A frontier planner writes PIECES.md (pieces along real seams, each with an owner scope, a reference cut, an acceptance and a box) and a human approves the cut. The outer loop takes one piece per round: the owner works inside its scope and captures, a check refuses bad captures, and a fresh frontier critic compares the captures blind against the piece's cut of the reference, ticking the box on a pass; the inner loop returns the top gaps to the owner. When no piece remains an integrator assembles the whole, a fresh final critic compares it blind, and a human gate releases it. Both loops are capped and budgeted; no brief asks for a win over the reference. The outer loop pauses for you between pieces at its human stop, so a long decomposition is never unattended.`,nodes:[{id:`planner`,kind:`agent`,name:`Planner`,role:`planner`,model:{tier:`frontier`},effort:`high`,brief:`Cut the task into pieces along the real seams of the artifact and the code, few enough to build in sequence, and write PIECES.md: one entry per piece with its owner scope (what it may change), its cut of {{reference}} (which part of the reference the piece must match, named, never copied: no values, coordinates or colors out of the reference, which the owner must not see), its acceptance, and an unchecked box for the critic. Order the pieces so each can be judged on its own once the earlier ones are done. Plan only; build nothing.`,inputs:[`the task`,`{{reference}}`,`the project`],outputs:[`PIECES.md: the pieces in order, each with owner scope, reference cut, acceptance and a box`],allow:[`read-files`,`write-outputs`]},{id:`decomposition-gate`,kind:`human-gate`,name:`Approve the pieces`,prompt:`PIECES.md cuts the work into pieces, each with an owner scope, a reference cut and an acceptance. Are these the right pieces, in the right order?`,options:[`approve`,`revise`]},{id:`owner`,kind:`agent`,name:`Piece owner`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"You own the first unchecked piece of PIECES.md and change only what its owner scope allows, working from its acceptance and, from the second round on, the ranked gaps in GAPS.md, largest first, without regressing what already matches. Capture with `{{capture-command}}` before you report, and say which gaps you closed and which you left on purpose. The reference itself is the critic's; you work from the piece's cut of it.",inputs:[`PIECES.md`,`GAPS.md (from the second round on)`],outputs:[`the revised piece`,`captures/ of this revision`,`CHANGES.md: what changed, gaps closed`],allow:[`read-files`,`edit-files`,`run-commands`]},{id:`capture-check`,kind:`check`,name:`Capture check`,check:{kind:`evidence`,run:`{{capture-command}}`,pass:`every capture exists, opens, is legible at full size, and shows the current revision`}},{id:`critic`,kind:`agent`,name:`Piece critic`,role:`critic`,model:{tier:`frontier`},effort:`high`,brief:`Compare the captures of the current piece against its cut of {{reference}} side by side with the labels stripped and in random order, say which is better and why, then rank the gaps that matter most, at most five, each with where it shows and what closing it would look like. You judge; you do not fix. Report invalid-evidence rather than guessing when a capture is missing or unreadable. Verdict pass only when no gap you would call major remains, and on a pass tick the piece's box in PIECES.md.`,inputs:[`captures/ of the current revision`,`{{reference}}`,`PIECES.md`],outputs:[`GAPS.md: which is better and why, ranked gaps, a verdict line`,`PIECES.md with the box ticked on a pass`,`verdict: pass | fail | invalid-evidence`],allow:[`read-files`,`write-outputs`],deny:[`edit-files`]},{id:`next-piece`,kind:`check`,name:`A piece remains?`,description:`Pass means more work: an unchecked piece remains. Fail means every box is ticked.`,check:{kind:`command`,run:`grep -q '^- \\[ \\]' PIECES.md`,pass:`exit code 0: an unchecked piece remains in PIECES.md`}},{id:`integrator`,kind:`agent`,name:`Integrator`,role:`builder`,model:{tier:`strong`},effort:`medium`,brief:"Assemble the finished pieces into the whole without reworking any: resolve what shows only where they meet, then capture the whole with `{{capture-command}}`. Report what the assembly changed.",inputs:[`PIECES.md`,`the project`],outputs:[`the assembled artifact`,`captures/ of the whole`,`ASSEMBLY.md: what the assembly changed`],allow:[`read-files`,`edit-files`,`run-commands`]},{id:`final-critic`,kind:`agent`,name:`Final critic`,role:`critic`,model:{tier:`frontier`},effort:`high`,brief:`Compare the captures of the whole against {{reference}} side by side with the labels stripped and in random order, say which is better and why, then rank the gaps that remain, at most five. You judge; you do not fix. Verdict pass when no major gap remains; the human decides with your report either way.`,inputs:[`captures/ of the whole`,`{{reference}}`],outputs:[`FINAL.md: which is better and why, ranked gaps, a verdict line`,`verdict: pass | fail`],allow:[`read-files`,`write-outputs`],deny:[`edit-files`]},{id:`release-gate`,kind:`human-gate`,name:`Release`,prompt:`Every piece passed its critic and FINAL.md compares the whole against the reference. Release it as it is?`},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-planner-gate`,from:`planner`,to:`decomposition-gate`},{id:`e-gate-owner`,from:`decomposition-gate`,to:`owner`,when:`pass`},{id:`e-owner-capture-check`,from:`owner`,to:`capture-check`},{id:`e-capture-check-fail`,from:`capture-check`,to:`owner`,when:`fail`,evidence:[`capture check output`]},{id:`e-capture-check-critic`,from:`capture-check`,to:`critic`,when:`pass`,evidence:[`captures/ of the current revision`,`{{reference}}`,`PIECES.md`]},{id:`e-critic-fail`,from:`critic`,to:`owner`,when:`fail`,evidence:[`GAPS.md`]},{id:`e-critic-pass`,from:`critic`,to:`next-piece`,when:`pass`},{id:`e-next-piece-pass`,from:`next-piece`,to:`owner`,when:`pass`,evidence:[`PIECES.md`]},{id:`e-next-piece-fail`,from:`next-piece`,to:`integrator`,when:`fail`},{id:`e-integrator-final-critic`,from:`integrator`,to:`final-critic`,evidence:[`captures/ of the whole`,`{{reference}}`]},{id:`e-final-critic-release-gate`,from:`final-critic`,to:`release-gate`,evidence:[`FINAL.md`]},{id:`e-release-gate-done`,from:`release-gate`,to:`done`,when:`pass`}],loops:[{id:`polish`,name:`Polish a piece`,members:[`owner`,`capture-check`,`critic`],back:[`e-capture-check-fail`,`e-critic-fail`],mode:`judgment`,bar:{name:`The piece matches its cut`,inspects:[{kind:`artifact`,ref:`{{reference}}`,note:`the piece's cut of it`},{kind:`artifact`,ref:`captures/ of the current revision`},{kind:`checklist`,ref:`PIECES.md`}],acceptance:`The critic finds no major gap between the captures and the piece's cut of {{reference}}, and every capture is current and readable.`},stops:[{kind:`bar-passed`},{kind:`diminishing-returns`,rounds:2,metric:`major gaps remaining`},{kind:`max-iterations`,n:3},{kind:`budget`,measure:`dispatches`,limit:10}]},{id:`pieces`,name:`Pieces`,members:[`owner`,`capture-check`,`critic`,`next-piece`],back:[`e-next-piece-pass`],mode:`judgment`,bar:{name:`Every piece done`,inspects:[{kind:`checklist`,ref:`PIECES.md`,note:`one box per piece, ticked by its critic`}],acceptance:`Every box in PIECES.md is ticked by the critic with a pass verdict on record.`},stops:[{kind:`bar-passed`,then:`integrator`},{kind:`human`,every:2},{kind:`max-iterations`,n:4},{kind:`budget`,measure:`dispatches`,limit:42}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/grind-loop.grooph.json":{grooph:0,id:`grind-loop`,name:`Grind loop`,version:1,goal:"{{task}} Done when `{{test-command}}` passes.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Grind loop`,summary:`A fast builder works against the test suite until it passes, bounded by rounds and minutes.`,whenToUse:`Done and good are the same: tests, types or a task list supply the back pressure, so a passing check is the finish line.`,notFor:`Work where passing tests is not the same as good (taste, design, prose); use review-gate or taste-polish there.`,profile:{cost:`low`,speed:`fast`,rigor:`light`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add a slugify(text) function to src/strings.ts that lowercases, strips accents and joins words with single hyphens.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`}],tags:[`loop`,`grind`,`tests`],demo:`experiments/patterns/grind-loop/README.md`},description:`A builder changes the code and a check runs the tests. A failure goes back to the builder with the test output; a pass ends the run. The check is the bar, so there is no critic. The loop stops at its round cap or its time budget if the tests never pass.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:`Make the task true by changing the code, adding tests where they are missing. Do not skip, weaken or delete a test to get a pass. On a later round, start from the failing output you are handed. Report what you changed and which tests now pass.`,inputs:[`the task`,`failing test output (from round 1 on)`],outputs:[`the change`,`CHANGES.md: what changed this round and why`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`tests`,kind:`check`,name:`Tests`,check:{kind:`tests`,run:`{{test-command}}`,pass:`exit code 0 and no test skipped`}},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-tests`,from:`builder`,to:`tests`},{id:`e-tests-fail`,from:`tests`,to:`builder`,when:`fail`,evidence:[`test output`]},{id:`e-tests-pass`,from:`tests`,to:`done`,when:`pass`}],loops:[{id:`grind`,name:`Grind`,members:[`builder`,`tests`],back:[`e-tests-fail`],mode:`grind`,stops:[{kind:`max-iterations`,n:5},{kind:`budget`,measure:`minutes`,limit:30}]}]},"../../../../patterns/heterogeneous-critic.grooph.json":{grooph:0,id:`heterogeneous-critic`,name:`Heterogeneous critic`,version:1,goal:"{{task}} Done when every item in {{checklist}} is shown to hold, `{{test-command}}` passes, and a human approves the merge.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Heterogeneous critic`,summary:`The review gate with the critic on a different model tier from the builder, so it need not share the builder's blind spots.`,whenToUse:`The judge should not share the builder's model when isolating taste or blind spots matters: a same-model critic may keep approving the mistakes the builder makes.`,notFor:`Cheap, routine changes where a same-tier review-gate is enough, or checks a test can make; a stronger critic costs more every round.`,profile:{cost:`medium`,speed:`medium`,rigor:`high`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add a slugify(text) function to src/strings.ts that lowercases, strips accents and joins words with single hyphens.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`},{key:`checklist`,ask:`Which file holds the checklist the critic judges against?`,example:`docs/REVIEW-CHECKLIST.md`}],tags:[`loop`,`review`,`human-gate`,`heterogeneous`],demo:`experiments/patterns/heterogeneous-critic/README.md`},description:`The review-gate shape with the critic on the frontier tier and the builder on the strong tier. Within one harness both tiers are one model family, so this varies capability, not lineage; true cross-family judging needs a critic running in a second harness, which arrives with dual-harness nodes (stage 11).`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:`Do the task in the code, with tests. On a later round, read REVIEW.md first and address each finding, or say why it does not apply. Run the test command before you report, and do not review your own work beyond that.`,inputs:[`the task`,`{{checklist}}`,`REVIEW.md (from round 1 on)`],outputs:[`the change, with tests`,`CHANGES.md: what changed this round and which findings it addresses`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`critic`,kind:`agent`,name:`Critic`,description:`Runs on a different tier from the builder on purpose. For a critic from another model family, re-host this node on a second harness when dual-harness nodes exist (stage 11).`,role:`critic`,model:{tier:`frontier`},effort:`high`,brief:`Judge the change against the checklist, one line per item, citing the file and line that satisfies it or saying it is unmet; use the repository only to understand what the change touches. Run the test command yourself rather than trusting a report. You judge; you do not fix. Verdict pass only when every item holds and the tests pass; invalid-evidence when the diff or checklist cannot be read.`,inputs:[`diff of the change`,`the repository as the change leaves it, read-only`,`{{checklist}}`],outputs:[`REVIEW.md: one line per checklist item and a verdict line`,`verdict: pass | fail | invalid-evidence`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`merge-gate`,kind:`human-gate`,name:`Merge approval`,prompt:`The critic passed the change against the checklist. Merge it?`,options:[`approve`,`reject with feedback`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-critic`,from:`builder`,to:`critic`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`,`output of {{test-command}}`,`{{checklist}}`]},{id:`e-critic-fail`,from:`critic`,to:`builder`,when:`fail`,evidence:[`REVIEW.md`]},{id:`e-critic-pass`,from:`critic`,to:`merge-gate`,when:`pass`},{id:`e-merge-gate-done`,from:`merge-gate`,to:`done`,when:`pass`},{id:`e-merge-gate-reject`,from:`merge-gate`,to:`builder`,when:`fail`,evidence:[`the human's feedback`]}],loops:[{id:`review`,name:`Review`,members:[`builder`,`critic`,`merge-gate`],back:[`e-critic-fail`,`e-merge-gate-reject`],mode:`judgment`,bar:{name:`Review checklist`,inspects:[{kind:`checklist`,ref:`{{checklist}}`},{kind:`artifact`,ref:`output of {{test-command}}`}],acceptance:"Every checklist item is cited as satisfied with a file and line, and `{{test-command}}` exits 0."},stops:[{kind:`bar-passed`},{kind:`max-iterations`,n:4},{kind:`budget`,measure:`dispatches`,limit:10}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/human-gated-irreversible.grooph.json":{grooph:0,id:`human-gated-irreversible`,name:`Human-gated irreversible step`,version:1,template:{kind:`fragment`,title:`Human-gated irreversible step`,summary:`A human gate, then the one node that performs an irreversible action, then a stop: insert it before any merge, publish, spend or delete.`,whenToUse:`A step cannot be undone (merge, spend, publish, delete) and must wait for a human decision; this fragment satisfies E_IRREVERSIBLE_NO_GATE.`,notFor:`Reversible steps, where a gate only slows the run; and it gates only the way in you connect to it: every edge into the action must pass the gate.`,profile:{cost:`low`,speed:`fast`,rigor:`standard`},slots:[{key:`action`,ask:`What is the irreversible step, in one sentence?`,example:`Merge the release branch into main.`},{key:`irreversible`,ask:`Which kind of irreversible action is it: merge, publish, spend or delete?`,example:`merge`}],tags:[`fragment`,`human-gate`,`irreversible`,`brake`],demo:`experiments/patterns/human-gated-irreversible/README.md`},nodes:[{id:`gate`,kind:`human-gate`,name:`Irreversible step approval`,prompt:`About to do this, and it cannot be undone: {{action}} Go ahead?`,options:[`go ahead`,`stop`]},{id:`act`,kind:`agent`,name:`Irreversible step`,role:`builder`,model:{tier:`strong`},effort:`medium`,brief:`Carry out exactly this, once: {{action}} Do nothing else, and do not retry on failure; report the result instead. Write down what was done so a human can check it.`,inputs:[`the approved action`],outputs:[`ACTION.md: what was done and its result`],allow:[`read-files`,`run-commands`,`write-outputs`],irreversible:[`{{irreversible}}`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-gate-act`,from:`gate`,to:`act`,when:`pass`},{id:`e-act-done`,from:`act`,to:`done`}],loops:[]},"../../../../patterns/merge-queue.grooph.json":{grooph:0,id:`merge-queue`,name:`Merge queue`,version:1,template:{kind:`fragment`,title:`Merge queue`,summary:`An integration check on the queued batch; a failure goes to a bisector that finds the change at fault and leaves the rest queued; a pass waits for a human, who lets one node land the batch.`,whenToUse:`Several reviewed changes wait to land together and the integration command (build, lint, tests) is the arbiter: insert this before the host's stop node so a batch lands only green and only on a human's word.`,notFor:`A single change (human-gated-irreversible is enough), or a host whose changes are not yet reviewed: the queue integrates, it does not review.`,profile:{cost:`low`,speed:`medium`,rigor:`standard`},slots:[{key:`integration-command`,ask:`Which command integrates the queued batch and exits 0 only when it builds, lints and passes its tests?`,example:`npm run integrate`},{key:`queue-file`,ask:`Which file lists the queued changes and their status, in landing order?`,example:`QUEUE.md`},{key:`land-command`,ask:`Which command lands the queued batch, once?`,example:`npm run land`}],tags:[`fragment`,`merge`,`bisect`,`human-gate`,`irreversible`,`brake`],demo:`experiments/patterns/merge-queue/README.md`,credits:[{name:`Steve Yegge's Gas Town Refinery`,url:`https://github.com/gastownhall/gastown`,note:`a merge queue that integrates a batch, bisects on failure and lands by a human's word`},{name:`Bors`,url:`https://github.com/bors-ng/bors-ng`,note:`batch then bisect`}]},description:`A merge queue as a fragment. The integration check runs the integration command on the whole queued batch. On a failure a bisector splits the batch, finds the change that fails, holds it with the reason and leaves the rest queued, so the next integration is of a batch that can pass. On a pass a human gate asks, and one node carries out the land command once. The loop stops at its round cap or its dispatch budget; insert it before the host's stop node, as the human-gated-irreversible fragment is.`,nodes:[{id:`integrate`,kind:`check`,name:`Integrate the batch`,check:{kind:`command`,run:`{{integration-command}}`,pass:`exit code 0: the queued batch builds, lints and passes its tests together`}},{id:`bisect`,kind:`agent`,name:`Bisector`,role:`builder`,model:{tier:`strong`},effort:`medium`,brief:"The queued batch in {{queue-file}} fails `{{integration-command}}`: split the batch, run the integration on the parts, and find the change at fault. Hold that change in {{queue-file}} with the failure as the reason and leave every other change queued in its order; change nothing else, and fix nothing. Report what failed, how you found it and what stays queued in BISECT.md.",inputs:[`the integration output`,`{{queue-file}}`,`the project`],outputs:[`{{queue-file}} with the change at fault held and the rest queued`,`BISECT.md: the change at fault, how it was found, what stays queued`],allow:[`read-files`,`edit-files`,`run-commands`,`write-outputs`],owns:[`{{queue-file}}`]},{id:`land-gate`,kind:`human-gate`,name:`Land the batch`,prompt:`The queued batch in {{queue-file}} integrates green (BISECT.md, if present, says what was held and why). Land it now? It cannot be taken back.`,options:[`land`,`stop`]},{id:`land`,kind:`agent`,name:`Land`,role:`builder`,model:{tier:`strong`},effort:`medium`,brief:"Carry out `{{land-command}}` exactly once for the batch queued in {{queue-file}}. Do nothing else, and do not retry on failure; report the result instead. Write down what landed so a human can check it.",inputs:[`{{queue-file}}`,`the human's go-ahead`],outputs:[`LANDING.md: what landed and the command's result`],allow:[`read-files`,`run-commands`,`write-outputs`],irreversible:[`merge`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-integrate-fail`,from:`integrate`,to:`bisect`,when:`fail`,evidence:[`the integration output`,`{{queue-file}}`]},{id:`e-bisect-integrate`,from:`bisect`,to:`integrate`},{id:`e-integrate-land-gate`,from:`integrate`,to:`land-gate`,when:`pass`},{id:`e-land-gate-land`,from:`land-gate`,to:`land`,when:`pass`},{id:`e-land-done`,from:`land`,to:`done`}],loops:[{id:`queue`,name:`Queue`,members:[`integrate`,`bisect`],back:[`e-bisect-integrate`],mode:`grind`,stops:[{kind:`max-iterations`,n:4},{kind:`budget`,measure:`dispatches`,limit:9}]}]},"../../../../patterns/metric-sandwich.grooph.json":{grooph:0,id:`metric-sandwich`,name:`Metric sandwich`,version:1,goal:"{{task}} Done when `{{test-command}}` passes and a critic finds nothing unmet in {{checklist}}.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Metric sandwich`,summary:`Cheap deterministic checks run first; an expensive critic judges only what they cannot see, and either failure returns to the builder.`,whenToUse:`Cheap deterministic checks first, expensive judgment only on what those cannot see: lint and tests catch most failures, and a reviewer should not spend a round on them.`,notFor:`Work with no deterministic check to run first (use review-gate), or where the checks are the whole story (use grind-loop).`,profile:{cost:`medium`,speed:`medium`,rigor:`standard`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add a slugify(text) function to src/strings.ts that lowercases, strips accents and joins words with single hyphens.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm lint && pnpm test`},{key:`checklist`,ask:`Which file holds the checklist the critic judges against?`,example:`docs/REVIEW-CHECKLIST.md`}],tags:[`loop`,`checks`,`review`],demo:`experiments/patterns/metric-sandwich/README.md`},description:`A builder works; a check runs the cheap deterministic suite. Only a passing change reaches the frontier critic, which judges what the suite cannot see against a checklist. Either failing returns to the builder, so one loop has two back edges. It stops when the critic passes, at its round cap, or at its dispatch budget.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"Do the task in the code, with tests. On a later round you get either the failing check output or REVIEW.md; address what it names. Run `{{test-command}}` before you report.",inputs:[`the task`,`{{checklist}}`,`check output or REVIEW.md (from round 1 on)`],outputs:[`the change, with tests`,`CHANGES.md: what changed this round`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`checks`,kind:`check`,name:`Cheap checks`,check:{kind:`command`,run:`{{test-command}}`,pass:`exit code 0`}},{id:`critic`,kind:`agent`,name:`Critic`,role:`critic`,model:{tier:`frontier`},effort:`high`,brief:"Judge only what `{{test-command}}` cannot see, the items in the checklist, for a change that already passes it; use the repository only to understand what the change touches. Do not rerun or restate the checks. Cite the file and line for each finding; you judge, you do not fix. Verdict pass when no checklist item is unmet.",inputs:[`diff of the change`,`the repository as the change leaves it, read-only`,`{{checklist}}`],outputs:[`REVIEW.md: findings by checklist item and a verdict line`,`verdict: pass | fail | invalid-evidence`],allow:[`read-files`,`write-outputs`],deny:[`edit-files`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-checks`,from:`builder`,to:`checks`},{id:`e-checks-fail`,from:`checks`,to:`builder`,when:`fail`,evidence:[`check output`]},{id:`e-checks-critic`,from:`checks`,to:`critic`,when:`pass`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`,`{{checklist}}`]},{id:`e-critic-fail`,from:`critic`,to:`builder`,when:`fail`,evidence:[`REVIEW.md`]},{id:`e-critic-pass`,from:`critic`,to:`done`,when:`pass`}],loops:[{id:`sandwich`,name:`Sandwich`,members:[`builder`,`checks`,`critic`],back:[`e-checks-fail`,`e-critic-fail`],mode:`judgment`,bar:{name:`Checks, then checklist`,inspects:[{kind:`artifact`,ref:`output of {{test-command}}`},{kind:`checklist`,ref:`{{checklist}}`}],acceptance:"`{{test-command}}` exits 0 and the critic finds no unmet item in {{checklist}}."},stops:[{kind:`bar-passed`},{kind:`max-iterations`,n:5},{kind:`budget`,measure:`dispatches`,limit:16}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/ownership-not-swarm.grooph.json":{grooph:0,id:`ownership-not-swarm`,name:`Ownership, not swarm`,version:2,goal:"{{task}} Done when the pieces are integrated and `{{test-command}}` passes, with each coupled subsystem changed by exactly one owner.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Ownership, not swarm`,summary:`Coupled subsystems get one owner each, in sequence; only the independent pieces fan out to fast workers; an integrator joins them and the tests close it.`,whenToUse:`Coupled subsystems get one owner; fan out only the independent pieces. The work spans a shared core and several leaf pieces that touch nothing else.`,notFor:`Work that is all coupled (use one builder in a grind-loop or review-gate) or all independent (a fan-out alone); and never as a way to parallelise a shared core.`,profile:{cost:`medium`,speed:`medium`,rigor:`standard`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add tagging to notes: a tags table, tag endpoints in the API, and tag chips, a tag filter and a tag editor in the web UI.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`},{key:`subsystem-a`,ask:`Which folder is the first coupled subsystem, the one the others build on?`,example:`src/db/`},{key:`subsystem-b`,ask:`Which folder is the second coupled subsystem, built on the first?`,example:`src/api/`}],tags:[`ownership`,`fan-out`,`coupled`,`grind`],demo:`experiments/patterns/ownership-not-swarm/README.md`,credits:[{name:`Claude of Duty process note (Matt Shumer)`,url:`https://github.com/mshumer/Claude-of-Duty`,note:`sequential ownership beat parallel fan-out on coupled systems in the repository's own write-up`}]},description:`A planner splits the task into the coupled core and independent pieces. Two owners, both marked coupled and each owning one subsystem, work in sequence, the second building on the first. Only then do fast workers take the independent pieces, up to three at a time. An integrator joins everything and a test check closes a short grind loop. Parallel fan-out lost to sequential ownership on coupled systems (spec §13); this graph fans out only where nothing is coupled.`,nodes:[{id:`planner`,kind:`agent`,name:`Planner`,role:`planner`,model:{tier:`strong`},effort:`high`,brief:`Split the task into the coupled core, the work inside {{subsystem-a}} and {{subsystem-b}}, and independent pieces that touch neither. Write PLAN.md with what each owner does and the independent pieces as a list, each small enough for one worker. Write no code.`,inputs:[`the task`],outputs:[`PLAN.md`],allow:[`read-files`,`write-outputs`]},{id:`owner-a`,kind:`agent`,name:`Owner A`,coupled:!0,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"You are the only node that changes {{subsystem-a}}. Do its part of PLAN.md and record every interface the next owner relies on in HANDOFF-A.md. Run `{{test-command}}` before you report.",inputs:[`PLAN.md`],outputs:[`changes in {{subsystem-a}}`,`HANDOFF-A.md: interfaces and changes the next owner relies on`],allow:[`read-files`,`edit-files`,`run-tests`],owns:[`{{subsystem-a}}`]},{id:`owner-b`,kind:`agent`,name:`Owner B`,coupled:!0,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"You are the only node that changes {{subsystem-b}}. Do its part of PLAN.md on top of what HANDOFF-A.md describes; if {{subsystem-a}} needs to change, say so rather than changing it. Run `{{test-command}}` before you report.",inputs:[`PLAN.md`,`HANDOFF-A.md`],outputs:[`changes in {{subsystem-b}}`,`HANDOFF-B.md: interfaces the pieces rely on`],allow:[`read-files`,`edit-files`,`run-tests`],owns:[`{{subsystem-b}}`]},{id:`worker`,kind:`agent`,name:`Piece worker`,description:`One dispatch per independent piece in PLAN.md.`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:`Build one independent piece from PLAN.md, touching only its own files; {{subsystem-a}} and {{subsystem-b}} are read-only to you. If the piece turns out to need a change in either, stop and say so instead of making it. Report the files you touched.`,inputs:[`PLAN.md`,`HANDOFF-B.md`,`the piece to build`],outputs:[`the piece`,`a list of the files it touched`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`integrator`,kind:`agent`,name:`Integrator`,role:`synthesizer`,model:{tier:`strong`},effort:`high`,brief:`Join the pieces to the coupled core so the task works end to end, changing only the seams between them. {{subsystem-a}} and {{subsystem-b}} belong to their owners: a change there is a note in your report, not an edit. Write ARCHITECTURE.md naming each owner, what they own, and how the pieces connect.`,inputs:[`PLAN.md`,`HANDOFF-A.md`,`HANDOFF-B.md`,`the pieces`,`failing test output (from round 1 on)`],outputs:[`the integrated change`,`ARCHITECTURE.md`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`tests`,kind:`check`,name:`Tests`,check:{kind:`tests`,run:`{{test-command}}`,pass:`exit code 0 and no test skipped`}},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-planner-owner-a`,from:`planner`,to:`owner-a`,evidence:[`PLAN.md`]},{id:`e-owner-a-owner-b`,from:`owner-a`,to:`owner-b`,evidence:[`PLAN.md`,`HANDOFF-A.md`]},{id:`e-owner-b-worker`,from:`owner-b`,to:`worker`,concurrency:{max:3},evidence:[`PLAN.md`,`HANDOFF-B.md`],label:`one worker per independent piece`},{id:`e-worker-integrator`,from:`worker`,to:`integrator`,label:`after every piece is in`},{id:`e-integrator-tests`,from:`integrator`,to:`tests`},{id:`e-tests-fail`,from:`tests`,to:`integrator`,when:`fail`,evidence:[`test output`]},{id:`e-tests-pass`,from:`tests`,to:`done`,when:`pass`}],loops:[{id:`integrate`,name:`Integrate`,members:[`integrator`,`tests`],back:[`e-tests-fail`],mode:`grind`,stops:[{kind:`max-iterations`,n:3},{kind:`budget`,measure:`minutes`,limit:30}]}]},"../../../../patterns/patrol-pulse.grooph.json":{grooph:0,id:`patrol-pulse`,name:`Patrol pulse`,version:3,goal:`{{task}} One pulse is one run: it ends clean, or with new tickets in {{ticket-store}} for a human to prioritize. Nothing else changes.`,target:{harness:`claude-code`},template:{kind:`graph`,title:`Patrol pulse`,summary:`A scan lists candidate signals, a read-only investigator judges them against the project and the existing tickets, a writer files the new ones to the ticket store, and a human prioritizes; one pulse is one run.`,whenToUse:`Something should be looked at on a schedule (a log, a queue, a report) and the output of each look is a triaged record for a human, never a change: run the package under the harness's scheduler and each pulse is a run.`,notFor:`Anything that should change code in the same run (use a loop with a builder), or a watch with no durable store to file into: the memory is the project's ticket file or database, named by the slot, not grooph.`,profile:{cost:`low`,speed:`fast`,rigor:`standard`},slots:[{key:`task`,ask:`What does this patrol watch for, in one or two sentences?`,example:`Watch the API's log for faults users would feel: failed requests, retries that never succeed, resources running out.`},{key:`scan-command`,ask:`Which command lists the candidate signals for this pulse (errors in a log, failed jobs, stale items)? It should exit non-zero when there is nothing to look at.`,example:`grep -n -E ' (ERROR|WARN) ' logs/app.log`},{key:`ticket-store`,ask:`Which file or store holds the tickets, read by the investigator and appended to by the writer?`,example:`TICKETS.md`}],tags:[`pulse`,`scheduled`,`read-only`,`tickets`,`human-gate`],demo:`experiments/patterns/patrol-pulse/README.md`,credits:[{name:`u/croovies's "Lloyd" heartbeat orchestrator, as written up by explainx.ai (2026-08-14; a secondary source)`,url:`https://www.explainx.ai/blog/claude-code-loop-orchestrator-heartbeat-ticket-memory-august-2026`,note:`a standing checklist per pulse, a read-only investigator, findings to a durable ticket store, humans prioritize`},{name:`Steve Yegge's Gas Town patrols`,url:`https://github.com/gastownhall/gastown`,note:`patrol agents that loop by design; here one pulse is one run`}]},description:`A pulse, not a loop. The scan check runs the command that lists candidate signals; nothing to look at ends the pulse clean. Otherwise a read-only investigator judges each signal against the project and the tickets already filed and writes its findings; a clean verdict ends the pulse, a finding goes to a writer that owns only the ticket store, files one ticket per new finding and never files what is already filed. A human gate ends the pulse with the new tickets waiting to be prioritized. The package recurs through the harness's scheduler (the target doc says how); every pulse is its own run, so the run list is the pulse log.`,nodes:[{id:`scan`,kind:`check`,name:`Scan`,check:{kind:`command`,run:`{{scan-command}}`,pass:`exit code 0 and at least one candidate signal printed; nothing printed is a fail and ends the pulse clean`}},{id:`investigator`,kind:`agent`,name:`Investigator`,description:`Read-only by policy: it may read and run, never edit. Its judgment is what separates a fault from routine noise.`,role:`critic`,model:{tier:`strong`},effort:`high`,brief:`Judge every signal the scan listed: read the project and run what you need to tell a genuine fault from routine noise, and check {{ticket-store}} so a fault already on file is named by its ticket rather than reported as new. Write PULSE.md with one entry per genuine fault (the evidence, the likely cause, whether a ticket already covers it) and a line for what you dismissed and why. You investigate; you change nothing. Verdict finding when at least one genuine fault is not yet on file, clean otherwise.`,inputs:[`the scan output`,`{{ticket-store}}, read-only`,`the repository, read-only`],outputs:[`PULSE.md: genuine faults with evidence, dismissed signals, and a verdict line`,`verdict: clean | finding`],allow:[`read-files`,`run-commands`,`write-outputs`],deny:[`edit-files`]},{id:`ticket-writer`,kind:`agent`,name:`Ticket writer`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:`Append one ticket to {{ticket-store}} for each finding in PULSE.md that no existing ticket covers, in the store's own format, cross-referencing the tickets that already cover the rest. Never duplicate a ticket, never edit an existing one, and never touch code or anything outside {{ticket-store}}. Report what you filed and what you left because it was already on file in FILED.md.`,inputs:[`PULSE.md`,`{{ticket-store}}`],outputs:[`{{ticket-store}} with one new ticket per new finding`,`FILED.md: tickets filed and findings already covered`],allow:[`read-files`,`edit-files`,`write-outputs`],owns:[`{{ticket-store}}`]},{id:`prioritize`,kind:`human-gate`,name:`Prioritize`,prompt:`This pulse filed new tickets in {{ticket-store}} (FILED.md says which) and changed nothing else. Prioritize them when you can; the next pulse is a new run.`,options:[`prioritized`,`later`]},{id:`clean`,kind:`stop`,name:`Clean`,outcome:`success`},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-scan-clean`,from:`scan`,to:`clean`,when:`fail`,label:`nothing to look at`},{id:`e-scan-investigator`,from:`scan`,to:`investigator`,when:`pass`,evidence:[`the scan output`,`{{ticket-store}}, read-only`,`the repository, read-only`]},{id:`e-investigator-clean`,from:`investigator`,to:`clean`,when:{verdict:`clean`}},{id:`e-investigator-ticket-writer`,from:`investigator`,to:`ticket-writer`,when:{verdict:`finding`},evidence:[`PULSE.md`,`{{ticket-store}}`]},{id:`e-ticket-writer-prioritize`,from:`ticket-writer`,to:`prioritize`},{id:`e-prioritize-done`,from:`prioritize`,to:`done`}],loops:[],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/ralph-loop.grooph.json":{grooph:0,id:`ralph-loop`,name:`Ralph loop`,version:1,goal:"{{task}} Done when every item in {{plan-file}} is marked done and `{{test-command}}` passes.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Ralph loop`,summary:`A fresh builder takes the top unchecked item of a plan file each round, commits on green and appends what it learned to an agent file, until the plan is empty.`,whenToUse:`The work is already a prioritized list of small items that tests can check one at a time, and you want a fresh context per item with the plan and an agent file as the only memory.`,notFor:`One item that needs judgment rather than a test (use review-gate or taste-polish), or a plan whose items are too large or too coupled to finish one at a time.`,profile:{cost:`low`,speed:`fast`,rigor:`light`},slots:[{key:`task`,ask:`What is the work as a whole, in one or two sentences? The plan file holds the items.`,example:`Build the text-statistics module the plan describes, one item at a time.`},{key:`plan-file`,ask:"Which file holds the plan: a Markdown checklist, top item first, `- [ ]` open and `- [x]` done?",example:`PLAN.md`},{key:`agent-file`,ask:`Which file holds the operating notes the builder reads first and may append learnings to?`,example:`AGENT.md`},{key:`test-command`,ask:`Which command runs the tests?`,example:`npm test`}],tags:[`loop`,`grind`,`plan`,`tests`,`fresh-context`],demo:`experiments/patterns/ralph-loop/README.md`,credits:[{name:`Geoffrey Huntley's ralph loop`,url:`https://ghuntley.com/ralph/`,note:`one prompt piped into a fresh session per pass; one item of work per pass from a plan file; state on disk; the human as the brake, here replaced by stops`}]},description:`The plan file is the unit of work. Each round a fresh builder takes the top unchecked item, makes it true, runs the tests, and on green marks it done, commits and appends what it learned to the agent file. A test check catches an item that broke something; a plan check sends the builder back for the next item while any remains. The loop stops when the plan is empty, when the same failure repeats, at its round cap or at its dispatch budget: the stops the original loop leaves to the human.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:"Read {{agent-file}} first, then take only the top unchecked item of {{plan-file}}: make it true in the code with tests, run `{{test-command}}`, and on green mark the item done, append what you learned to {{agent-file}} and commit. Never start a second item, and never skip, weaken or delete a test to get a pass. On a later round, start from the failing output you are handed, which may come from the item you just marked done. Report the item you took, what changed and whether the tests passed.",inputs:[`{{plan-file}}`,`{{agent-file}}`,`the project`,`failing test output (from round 1 on)`],outputs:[`the change for the item, with tests`,`{{plan-file}} with the item marked done`,`{{agent-file}} with what was learned appended`,`a commit on green`],allow:[`read-files`,`edit-files`,`run-tests`,`run-commands`]},{id:`tests`,kind:`check`,name:`Tests`,check:{kind:`tests`,run:`{{test-command}}`,pass:`exit code 0 and no test skipped`}},{id:`plan-check`,kind:`check`,name:`Plan empty?`,description:`Counts the unchecked items left in the plan. grep prints the count and exits 1 when it is zero, so the printed count is the result, not the exit code.`,check:{kind:`command`,run:`grep -c '^- \\[ \\]' {{plan-file}}`,pass:`the printed count is 0: no unchecked item remains in {{plan-file}}`}},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-tests`,from:`builder`,to:`tests`},{id:`e-tests-fail`,from:`tests`,to:`builder`,when:`fail`,evidence:[`test output`]},{id:`e-tests-plan-check`,from:`tests`,to:`plan-check`,when:`pass`},{id:`e-plan-check-fail`,from:`plan-check`,to:`builder`,when:`fail`,evidence:[`{{plan-file}}`],label:`next item`},{id:`e-plan-check-pass`,from:`plan-check`,to:`done`,when:`pass`}],loops:[{id:`ralph`,name:`Ralph`,members:[`builder`,`tests`,`plan-check`],back:[`e-tests-fail`,`e-plan-check-fail`],mode:`grind`,stops:[{kind:`diminishing-returns`,rounds:2,metric:`the same test failure`},{kind:`max-iterations`,n:5},{kind:`budget`,measure:`dispatches`,limit:17}]}]},"../../../../patterns/red-team-loop.grooph.json":{grooph:0,id:`red-team-loop`,name:`Red-team loop`,version:1,goal:`{{task}} Done when a red team attacking {{attack-surface}} stops finding new failing traces.`,target:{harness:`claude-code`},template:{kind:`graph`,title:`Red-team loop`,summary:`A separate attacker records reproducible failing traces; the builder is given only the traces, until no new one appears.`,whenToUse:`A separate attacker should produce failing traces and the builder should see only those: hardening against inputs or abuse nobody has listed yet.`,notFor:`Work with a known list of cases to satisfy (use grind-loop against tests), or judging quality rather than breaking things.`,profile:{cost:`medium`,speed:`medium`,rigor:`high`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Harden login and session handling in src/auth/ against malformed input, replayed tokens and timing leaks.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`},{key:`attack-surface`,ask:`What should the red team attack?`,example:`the login endpoint and session handling in src/auth/`}],tags:[`loop`,`red-team`,`traces`,`hardening`],demo:`experiments/patterns/red-team-loop/README.md`},description:`A builder hardens the code; a frontier red team in a fresh context attacks the stated surface and writes each reproducible failure as a trace in traces/, which it alone owns. The builder sees only the traces, not the attacker's reasoning. The loop ends when an attack round finds nothing, stops when rounds stop bringing a new failing trace, and is capped by rounds and dispatches.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"Make the task hold under attack. Each round you see only the failing traces in traces/: make each one pass without special-casing it, and add it to the test suite. Keep `{{test-command}}` green and report which traces now pass.",inputs:[`the task`,`traces/ (from round 1 on)`],outputs:[`the change, with a test per fixed trace`,`CHANGES.md: traces fixed this round`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`red-team`,kind:`agent`,name:`Red team`,role:`red-team`,model:{tier:`frontier`},effort:`high`,brief:`Attack {{attack-surface}} and record each failure you can reproduce as a trace in traces/: the input, how to run it, and the wrong result. Traces are all the builder sees, so each must reproduce on its own. You attack; you do not fix. Verdict fail when you have a new failing trace, pass when you found none.`,inputs:[`the running code`,`source of {{attack-surface}}`],outputs:[`traces/: one reproducible failing trace per file`,`ATTACK.md: what was attacked and how, written whether or not a trace was found`,`verdict: pass | fail`],allow:[`read-files`,`write-outputs`,`run-commands`],deny:[`edit-files`],owns:[`traces`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-red-team`,from:`builder`,to:`red-team`,evidence:[`the running code`,`source of {{attack-surface}}`,`traces/ already recorded`]},{id:`e-red-team-fail`,from:`red-team`,to:`builder`,when:`fail`,evidence:[`traces/`]},{id:`e-red-team-pass`,from:`red-team`,to:`done`,when:`pass`}],loops:[{id:`attack`,name:`Attack`,members:[`builder`,`red-team`],back:[`e-red-team-fail`],mode:`judgment`,bar:{name:`No new failing trace`,inspects:[{kind:`file`,ref:`traces/`},{kind:`artifact`,ref:`output of {{test-command}}`}],acceptance:"A full attack round finds no failing trace, and every recorded trace passes in `{{test-command}}`."},stops:[{kind:`diminishing-returns`,rounds:2,metric:`new failing traces`},{kind:`max-iterations`,n:5},{kind:`budget`,measure:`dispatches`,limit:12}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/retrospective-rewrite.grooph.json":{grooph:0,id:`retrospective-rewrite`,name:`Retrospective rewrite`,version:1,goal:"{{task}} Done when `{{test-command}}` passes, or the loop runs out; either way, end with proposals to improve this graph.",target:{harness:`claude-code`},adaptation:`propose`,template:{kind:`graph`,title:`Retrospective rewrite`,summary:`A grind loop that ends, pass or fail, with a researcher reading the run's notes and proposing graph changes for the human to adopt.`,whenToUse:`After a run, propose graph edits rather than rewriting mid-flight: the graph is being tuned across runs and the human wants every change as a proposal.`,notFor:`One-off tasks nobody will run again, or when the lead should adapt the graph during the run (the default adaptive level does that).`,profile:{cost:`low`,speed:`medium`,rigor:`standard`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add a slugify(text) function to src/strings.ts that lowercases, strips accents and joins words with single hyphens.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`}],tags:[`loop`,`grind`,`retrospective`,`proposals`],demo:`experiments/patterns/retrospective-rewrite/README.md`},description:`The grind-loop shape with adaptation set to propose: the lead changes nothing during the run. When the tests pass, or when the loop stops on its rounds or minutes, a researcher reads the run's notes and progress log and writes proposals for the next version of this graph, each with its evidence and an op-list patch. The human decides after the run.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:`Make the task true by changing the code, adding tests where they are missing. Do not skip, weaken or delete a test to get a pass. On a later round, start from the failing output you are handed. Report what you changed and which tests now pass.`,inputs:[`the task`,`failing test output (from round 1 on)`],outputs:[`the change`,`CHANGES.md: what changed this round and why`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`tests`,kind:`check`,name:`Tests`,check:{kind:`tests`,run:`{{test-command}}`,pass:`exit code 0 and no test skipped`}},{id:`retro`,kind:`agent`,name:`Retrospective`,role:`researcher`,model:{tier:`strong`},effort:`medium`,brief:`Read this run's notes and progress log and propose changes to the graph for next time: a node to add or drop, a loop or stop that misfired, a brief that no longer fits. Each proposal needs a summary, the evidence from this run, and a patch as a list of grooph ops. Change nothing yourself; the human decides.`,inputs:[`the run's notes`,`PROGRESS.md`,`the graph`],outputs:[`PROPOSALS.md: one section per proposal with its summary, evidence and op-list patch; the lead appends each as a proposal note`],allow:[`read-files`,`write-outputs`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-tests`,from:`builder`,to:`tests`},{id:`e-tests-fail`,from:`tests`,to:`builder`,when:`fail`,evidence:[`test output`]},{id:`e-tests-retro`,from:`tests`,to:`retro`,when:`pass`,evidence:[`the run's notes`,`PROGRESS.md`]},{id:`e-retro-done`,from:`retro`,to:`done`}],loops:[{id:`grind`,name:`Grind`,members:[`builder`,`tests`],back:[`e-tests-fail`],mode:`grind`,stops:[{kind:`max-iterations`,n:5,then:`retro`},{kind:`budget`,measure:`minutes`,limit:30,then:`retro`}]}]},"../../../../patterns/review-gate.grooph.json":{grooph:0,id:`review-gate`,name:`Review gate`,version:1,goal:"{{task}} Done when every item in {{checklist}} is shown to hold, `{{test-command}}` passes, and a human approves the merge.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Review gate`,summary:`A builder works, an isolated critic checks the change against a written checklist, failures loop back, and a human approves the merge.`,whenToUse:`Work, then a separate reviewer, then iterate or pass: the change needs a second pair of eyes against a checklist that already exists.`,notFor:`Work where the tests alone decide done (use grind-loop), or where nobody has written down what good means yet (use spec-then-loop).`,profile:{cost:`medium`,speed:`medium`,rigor:`standard`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add a slugify(text) function to src/strings.ts that lowercases, strips accents and joins words with single hyphens.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`},{key:`checklist`,ask:`Which file holds the checklist the critic judges against?`,example:`docs/REVIEW-CHECKLIST.md`}],tags:[`loop`,`review`,`human-gate`],demo:`experiments/patterns/review-gate/README.md`},description:`A builder implements the task. A critic in a fresh context checks the diff and test output against a written checklist; failures return to the builder with REVIEW.md, and a pass goes to a human merge gate, whose rejection also returns to the builder. The loop stops when the bar passes, at its round cap, or at its dispatch budget. Builder and critic share a tier, which the validator flags; heterogeneous-critic is the same shape with the critic on another tier.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:`Do the task in the code, with tests. On a later round, read REVIEW.md first and address each finding, or say why it does not apply. Run the test command before you report, and do not review your own work beyond that.`,inputs:[`the task`,`{{checklist}}`,`REVIEW.md (from round 1 on)`],outputs:[`the change, with tests`,`CHANGES.md: what changed this round and which findings it addresses`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`critic`,kind:`agent`,name:`Critic`,role:`critic`,model:{tier:`strong`},effort:`high`,brief:`Judge the change against the checklist, one line per item, citing the file and line that satisfies it or saying it is unmet; use the repository only to understand what the change touches. Run the test command yourself rather than trusting a report; you judge, you do not fix. Verdict pass only when every item holds and the tests pass; invalid-evidence when the diff or checklist cannot be read.`,inputs:[`diff of the change`,`the repository as the change leaves it, read-only`,`{{checklist}}`],outputs:[`REVIEW.md: one line per checklist item and a verdict line`,`verdict: pass | fail | invalid-evidence`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`merge-gate`,kind:`human-gate`,name:`Merge approval`,prompt:`The critic passed the change against the checklist. Merge it?`,options:[`approve`,`reject with feedback`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-critic`,from:`builder`,to:`critic`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`,`output of {{test-command}}`,`{{checklist}}`]},{id:`e-critic-fail`,from:`critic`,to:`builder`,when:`fail`,evidence:[`REVIEW.md`]},{id:`e-critic-pass`,from:`critic`,to:`merge-gate`,when:`pass`},{id:`e-merge-gate-done`,from:`merge-gate`,to:`done`,when:`pass`},{id:`e-merge-gate-reject`,from:`merge-gate`,to:`builder`,when:`fail`,evidence:[`the human's feedback`]}],loops:[{id:`review`,name:`Review`,members:[`builder`,`critic`,`merge-gate`],back:[`e-critic-fail`,`e-merge-gate-reject`],mode:`judgment`,bar:{name:`Review checklist`,inspects:[{kind:`checklist`,ref:`{{checklist}}`},{kind:`artifact`,ref:`output of {{test-command}}`}],acceptance:"Every checklist item is cited as satisfied with a file and line, and `{{test-command}}` exits 0."},stops:[{kind:`bar-passed`},{kind:`max-iterations`,n:4},{kind:`budget`,measure:`dispatches`,limit:10}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/spec-then-loop.grooph.json":{grooph:0,id:`spec-then-loop`,name:`Spec then loop`,version:2,goal:"{{task}} Done when every line of an approved ACCEPTANCE.md is shown to hold and `{{test-command}}` passes.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Spec then loop`,summary:`A planner writes the answer key, a human approves it, then a builder and an isolated critic loop against it.`,whenToUse:`No external reference product exists: a planning pass writes an answer key (ACCEPTANCE.md) that becomes the bar. The default for zero-to-one work.`,notFor:`Work with a reference or checklist already in hand (use review-gate or taste-polish), or changes so small that writing acceptance criteria costs more than the change.`,profile:{cost:`medium`,speed:`medium`,rigor:`high`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add CSV export to the reports page: a download button that exports the rows currently shown.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`}],tags:[`loop`,`answer-key`,`human-gate`,`zero-to-one`],demo:`experiments/patterns/spec-then-loop/README.md`,credits:[{name:`Answer-key-first Gauntlet, a community modification of Matt Pocock's Wayfinder`,url:`https://github.com/mattpocock/skills/blob/main/docs/engineering/wayfinder.md`,note:`write the spec and a pass/fail answer key before the loop; the modification is known from a secondary write-up, not a primary source`}]},description:`A frontier planner turns the task into ACCEPTANCE.md, a short list of observable behaviors. A human approves it; rejecting ends the run so the task can be restated. A builder then works and a critic in a fresh context judges the change line by line against the approved file, which is the loop's bar. The loop stops when every line holds, at its round cap, or at its dispatch budget.`,nodes:[{id:`planner`,kind:`agent`,name:`Planner`,role:`planner`,model:{tier:`frontier`},effort:`high`,brief:"Turn the task into ACCEPTANCE.md: the observable behaviors that would make it done, each checkable by reading code or running `{{test-command}}`. Name what is out of scope. Keep it short enough to review in a minute, and write no code.",inputs:[`the task`],outputs:[`ACCEPTANCE.md`],allow:[`read-files`,`write-outputs`]},{id:`spec-gate`,kind:`human-gate`,name:`Approve the spec`,prompt:`Is ACCEPTANCE.md the right definition of done? Rejecting ends the run so the task can be restated.`,options:[`approve`,`reject`]},{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"Do the task until every line of ACCEPTANCE.md holds. The acceptance file is fixed: if a line is wrong, say so in your report rather than working around it. Run `{{test-command}}` before you report; on a later round, start from REVIEW.md.",inputs:[`the task`,`ACCEPTANCE.md`,`REVIEW.md (from round 1 on)`],outputs:[`the change, with tests`,`CHANGES.md: what changed this round`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`critic`,kind:`agent`,name:`Critic`,role:`critic`,model:{tier:`strong`},effort:`high`,brief:"Judge the change against ACCEPTANCE.md line by line, citing the file and line or the test that shows each one holds; use the repository only to understand what the change touches. Run `{{test-command}}` yourself. You judge; you do not fix, and you do not edit the acceptance file. Verdict pass only when every line holds; invalid-evidence when the diff or the file cannot be read.",inputs:[`diff of the change`,`the repository as the change leaves it, read-only`,`ACCEPTANCE.md`],outputs:[`REVIEW.md: one line per acceptance item and a verdict line`,`verdict: pass | fail | invalid-evidence`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-planner-spec-gate`,from:`planner`,to:`spec-gate`},{id:`e-spec-gate-builder`,from:`spec-gate`,to:`builder`,when:`pass`},{id:`e-builder-critic`,from:`builder`,to:`critic`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`,`output of {{test-command}}`,`ACCEPTANCE.md`]},{id:`e-critic-fail`,from:`critic`,to:`builder`,when:`fail`,evidence:[`REVIEW.md`]},{id:`e-critic-pass`,from:`critic`,to:`done`,when:`pass`}],loops:[{id:`build`,name:`Build`,members:[`builder`,`critic`],back:[`e-critic-fail`],mode:`judgment`,bar:{name:`ACCEPTANCE.md`,inspects:[{kind:`answer-key`,ref:`ACCEPTANCE.md`},{kind:`artifact`,ref:`output of {{test-command}}`}],acceptance:"Every line of ACCEPTANCE.md is shown to hold, and `{{test-command}}` exits 0.",answerKeyFrom:`planner`},stops:[{kind:`bar-passed`},{kind:`max-iterations`,n:4},{kind:`budget`,measure:`dispatches`,limit:10}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/specialist-critic-bank.grooph.json":{grooph:0,id:`specialist-critic-bank`,name:`Specialist critic bank`,version:1,goal:"{{task}} Done when the merged review lists no blocker and no major finding, `{{test-command}}` passes, and a human accepts the change.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Specialist critic bank`,summary:`Four fresh specialist critics review in parallel, a triage judge merges their findings by severity, and the change loops until nothing blocker or major remains.`,whenToUse:`Multiple disjoint judges (correctness, security, performance, taste) should each look at the change, and their findings need merging by severity.`,notFor:`Small or low-risk changes, where one review-gate critic is enough; four reviewers and a judge are each a dispatch, every round.`,profile:{cost:`high`,speed:`medium`,rigor:`high`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add password reset by email to the account service in src/account/.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`}],tags:[`loop`,`review`,`parallel`,`severity`],demo:`experiments/patterns/specialist-critic-bank/README.md`},description:`A builder works; four critics, each in a fresh context and each on one concern, review the same diff in parallel (at most four at a time). A frontier triage judge merges their reports into one list by severity and emits the verdict. Blockers or majors return to the builder; a clean list goes to a human, whose rejection also returns to the builder. The loop stops when the merged list is clean, at its round cap, or at its dispatch budget.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"Do the task in the code, with tests. On a later round, address every blocker and major in TRIAGE.md, or say why it does not apply; minors are optional. Run `{{test-command}}` before you report.",inputs:[`the task`,`TRIAGE.md (from round 1 on)`],outputs:[`the change, with tests`,`CHANGES.md: what changed this round`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`correctness`,kind:`agent`,name:`Correctness critic`,role:`critic`,model:{tier:`strong`},effort:`medium`,brief:"Review the change for correctness: wrong results, unhandled cases and missing tests; run `{{test-command}}` yourself only; other reviewers cover the rest. Rate each finding blocker, major or minor, with the file and line that shows it. You review; you do not fix. Report invalid-evidence rather than guessing when the diff cannot be read.",inputs:[`diff of the change`,`the repository as the change leaves it, read-only`],outputs:[`REVIEW-CORRECTNESS.md: findings rated blocker, major or minor`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`security`,kind:`agent`,name:`Security critic`,role:`critic`,model:{tier:`strong`},effort:`medium`,brief:`Review the change for security: input handling, secrets, authentication and authorization, and dependencies only; other reviewers cover the rest. Rate each finding blocker, major or minor, with the file and line that shows it. You review; you do not fix. Report invalid-evidence rather than guessing when the diff cannot be read.`,inputs:[`diff of the change`,`the repository as the change leaves it, read-only`],outputs:[`REVIEW-SECURITY.md: findings rated blocker, major or minor`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`performance`,kind:`agent`,name:`Performance critic`,role:`critic`,model:{tier:`strong`},effort:`medium`,brief:`Review the change for performance: work that grows badly with input, needless I/O, and memory only; other reviewers cover the rest. Rate each finding blocker, major or minor, with the file and line that shows it. You review; you do not fix. Report invalid-evidence rather than guessing when the diff cannot be read.`,inputs:[`diff of the change`,`the repository as the change leaves it, read-only`],outputs:[`REVIEW-PERFORMANCE.md: findings rated blocker, major or minor`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`taste`,kind:`agent`,name:`Taste critic`,role:`critic`,model:{tier:`strong`},effort:`medium`,brief:`Review the change for taste: naming, structure, readability and fit with the surrounding code only; other reviewers cover the rest. Rate each finding blocker, major or minor, with the file and line that shows it. You review; you do not fix. Report invalid-evidence rather than guessing when the diff cannot be read.`,inputs:[`diff of the change`,`the repository as the change leaves it, read-only`],outputs:[`REVIEW-TASTE.md: findings rated blocker, major or minor`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`triage`,kind:`agent`,name:`Triage judge`,description:`Runs once per round, after all four reviews are in.`,role:`judge`,model:{tier:`frontier`},effort:`high`,brief:`Merge the four reviews into one list ranked blocker, major, minor, dropping duplicates and any finding its reviewer could not show. Emit the verdict: pass only when nothing is blocker or major. You rank; you do not review again and you do not fix.`,inputs:[`the four REVIEW-*.md files`],outputs:[`TRIAGE.md: merged findings by severity and a verdict line`,`verdict: pass | fail`],allow:[`read-files`,`write-outputs`],deny:[`edit-files`]},{id:`gate`,kind:`human-gate`,name:`Accept the change`,prompt:`Triage found no blocker or major finding. Accept the change?`,options:[`accept`,`reject with feedback`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-correctness`,from:`builder`,to:`correctness`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`,`output of {{test-command}}`]},{id:`e-builder-security`,from:`builder`,to:`security`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`,`dependency manifest`]},{id:`e-builder-performance`,from:`builder`,to:`performance`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`]},{id:`e-builder-taste`,from:`builder`,to:`taste`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`]},{id:`e-correctness-triage`,from:`correctness`,to:`triage`,evidence:[`REVIEW-CORRECTNESS.md`]},{id:`e-security-triage`,from:`security`,to:`triage`,evidence:[`REVIEW-SECURITY.md`]},{id:`e-performance-triage`,from:`performance`,to:`triage`,evidence:[`REVIEW-PERFORMANCE.md`]},{id:`e-taste-triage`,from:`taste`,to:`triage`,evidence:[`REVIEW-TASTE.md`]},{id:`e-triage-fail`,from:`triage`,to:`builder`,when:`fail`,evidence:[`TRIAGE.md`]},{id:`e-triage-gate`,from:`triage`,to:`gate`,when:`pass`},{id:`e-gate-done`,from:`gate`,to:`done`,when:`pass`},{id:`e-gate-reject`,from:`gate`,to:`builder`,when:`fail`,evidence:[`the human's feedback`]}],loops:[{id:`review`,name:`Review`,members:[`builder`,`correctness`,`security`,`performance`,`taste`,`triage`,`gate`],back:[`e-triage-fail`,`e-gate-reject`],mode:`judgment`,bar:{name:`Merged severity list`,inspects:[{kind:`artifact`,ref:`TRIAGE.md`},{kind:`artifact`,ref:`output of {{test-command}}`}],acceptance:"TRIAGE.md lists no blocker and no major finding, and `{{test-command}}` exits 0."},stops:[{kind:`bar-passed`},{kind:`max-iterations`,n:4},{kind:`budget`,measure:`dispatches`,limit:26}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`},{id:`p-concurrency-cap`,kind:`concurrency-cap`,scope:`graph`,params:{max:4}}]},"../../../../patterns/taste-polish.grooph.json":{grooph:0,id:`taste-polish`,name:`Taste polish`,version:2,goal:`{{task}} Done when a frontier critic comparing captures against {{reference}} finds no major gap, or when the human stops the polish.`,target:{harness:`claude-code`},template:{kind:`graph`,title:`Taste polish`,summary:`One owner polishes an existing artifact while an isolated frontier critic compares readable captures against a named reference, bounded by rounds, budget and a human check-in.`,whenToUse:`Done and good have split: the artifact works but must match a named, inspectable reference, and the polish can be judged from captures.`,notFor:`Zero-to-one work with no reference (use spec-then-loop), or a default for every task: unbounded 'keep going until it beats the reference' is exactly what the stops here prevent.`,profile:{cost:`high`,speed:`slow`,rigor:`high`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Polish the pricing page in site/pricing/ until it reads as clearly as the reference.`},{key:`reference`,ask:`What named reference should the result be compared against: a file, a URL or a capture?`,example:`references/pricing-reference.png`},{key:`capture-command`,ask:`Which command captures the artifact for the critic (screenshots, a rendered PDF, a recording)?`,example:`pnpm run capture -- --out captures/`}],tags:[`loop`,`taste`,`gauntlet`,`reference`],demo:`experiments/patterns/taste-polish/README.md`,credits:[{name:`Matt Shumer's Gauntlet Loop (Claude of Duty)`,url:`https://github.com/mshumer/Claude-of-Duty`,note:`the bounded form of the Gauntlet: one owner, an isolated critic against a named reference, real stops`}]},description:`The Gauntlet-style entry, bounded. One owner revises the artifact and captures it; a check refuses captures that are missing, stale or unreadable before any judging happens; a frontier critic in a fresh context compares the captures against the named reference and returns the top gaps. The loop stops when the critic finds no major gap, when rounds stop bringing improvement, when the human says so at their regular check-in, at its round cap, or at the dispatch budget.`,nodes:[{id:`owner`,kind:`agent`,name:`Owner`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"You own the artifact the task names and are the only node that changes it. Each round, close the gaps the critic ranked highest, largest first, without regressing what already matches the reference. Capture the artifact with `{{capture-command}}` before you report, and say which gaps you closed and which you left on purpose.",inputs:[`the task`,`{{reference}}`,`GAPS.md (from round 1 on)`],outputs:[`the revised artifact`,`captures/ of this revision`,`CHANGES.md: gaps closed this round`],allow:[`read-files`,`edit-files`,`run-commands`]},{id:`capture-check`,kind:`check`,name:`Capture check`,description:`Evidence quality is gated (spec §13): a bad capture can fake a pass or a fail, so the critic never sees one.`,check:{kind:`evidence`,run:`{{capture-command}}`,pass:`every capture exists, opens, is legible at full size, and shows the current revision`}},{id:`critic`,kind:`agent`,name:`Critic`,role:`critic`,model:{tier:`frontier`},effort:`high`,brief:`Compare the captures against {{reference}} side by side with the labels stripped and in random order, say which is better and why, then rank the gaps that matter most, at most five, each with where it shows and what closing it would look like. You judge; you do not fix. Report invalid-evidence rather than guessing when a capture is missing or unreadable. Verdict pass only when no gap you would call major remains.`,inputs:[`captures/ of the current revision`,`{{reference}}`],outputs:[`GAPS.md: ranked gaps and a verdict line`,`verdict: pass | fail | invalid-evidence`],allow:[`read-files`,`write-outputs`],deny:[`edit-files`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-owner-capture-check`,from:`owner`,to:`capture-check`},{id:`e-capture-check-fail`,from:`capture-check`,to:`owner`,when:`fail`,evidence:[`capture check output`]},{id:`e-capture-check-critic`,from:`capture-check`,to:`critic`,when:`pass`,evidence:[`captures/ of the current revision`,`{{reference}}`]},{id:`e-critic-fail`,from:`critic`,to:`owner`,when:`fail`,evidence:[`GAPS.md`]},{id:`e-critic-pass`,from:`critic`,to:`done`,when:`pass`}],loops:[{id:`polish`,name:`Polish`,members:[`owner`,`capture-check`,`critic`],back:[`e-capture-check-fail`,`e-critic-fail`],mode:`judgment`,bar:{name:`Match the reference`,inspects:[{kind:`artifact`,ref:`{{reference}}`,note:`the named reference`},{kind:`artifact`,ref:`captures/ of the current revision`}],acceptance:`The critic finds no major gap between the captures and {{reference}}, and every capture is current and readable.`},stops:[{kind:`bar-passed`},{kind:`diminishing-returns`,rounds:2,metric:`major gaps remaining`},{kind:`human`,every:2},{kind:`max-iterations`,n:5},{kind:`budget`,measure:`dispatches`,limit:16}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/tournament-then-judge.grooph.json":{grooph:0,id:`tournament-then-judge`,name:`Tournament then judge`,version:1,goal:"{{task}} Done when the finalist the judge picks is finished and `{{test-command}}` passes.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Tournament then judge`,summary:`Three fast candidates build in parallel, a cheap check drops the broken ones, a frontier judge picks one finalist, and a builder finishes it.`,whenToUse:`Cheap candidates, then spend the expensive critic on finalists: the approach is open, drafts are cheap, and comparing working options beats arguing about them.`,notFor:`Work with one obvious approach, or coupled changes to a shared core: candidates that must touch the same files cannot run side by side.`,profile:{cost:`medium`,speed:`fast`,rigor:`standard`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add a slugify(text) function to src/strings.ts that lowercases, strips accents and joins words with single hyphens.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`},{key:`criteria`,ask:`What should the judge prefer among candidates that all pass the tests?`,example:`The smallest change that reads clearly and handles empty and non-ASCII input.`}],tags:[`parallel`,`candidates`,`judge`],demo:`experiments/patterns/tournament-then-judge/README.md`},description:`Three fast builders each draft the whole task in their own folder, in parallel. A check runs the tests on each and keeps the ones that pass. A frontier judge sees only those finalists and picks one against the stated criteria, naming any idea worth borrowing from the others. A builder finishes the winner. No loop: the tournament runs once.`,nodes:[{id:`candidate-a`,kind:`agent`,name:`Candidate A`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:`Build your own complete take on the task inside candidates/a/ and nowhere else. A runnable draft beats a polished fragment, and an approach different from the obvious one is welcome. Report your approach in one paragraph.`,inputs:[`the task`],outputs:[`candidates/a/`,`candidates/a/APPROACH.md`],allow:[`read-files`,`edit-files`,`run-tests`],owns:[`candidates/a`]},{id:`candidate-b`,kind:`agent`,name:`Candidate B`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:`Build your own complete take on the task inside candidates/b/ and nowhere else. A runnable draft beats a polished fragment, and an approach different from the obvious one is welcome. Report your approach in one paragraph.`,inputs:[`the task`],outputs:[`candidates/b/`,`candidates/b/APPROACH.md`],allow:[`read-files`,`edit-files`,`run-tests`],owns:[`candidates/b`]},{id:`candidate-c`,kind:`agent`,name:`Candidate C`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:`Build your own complete take on the task inside candidates/c/ and nowhere else. A runnable draft beats a polished fragment, and an approach different from the obvious one is welcome. Report your approach in one paragraph.`,inputs:[`the task`],outputs:[`candidates/c/`,`candidates/c/APPROACH.md`],allow:[`read-files`,`edit-files`,`run-tests`],owns:[`candidates/c`]},{id:`filter`,kind:`check`,name:`Finalist filter`,description:`Runs once, after all three candidates are in.`,check:{kind:`tests`,run:`{{test-command}}`,pass:`at least one candidate passes; those that pass are the finalists`}},{id:`judge`,kind:`agent`,name:`Judge`,role:`judge`,model:{tier:`frontier`},effort:`high`,brief:`Pick the one finalist to finish, judging against {{criteria}}; you see only the candidates that passed the filter. Write PICK.md naming the winner, why, and the best idea worth borrowing from another finalist. You pick; you do not edit any candidate.`,inputs:[`the finalists' folders`],outputs:[`PICK.md: the winner, the reason, one idea to borrow`,`verdict: pass`],allow:[`read-files`,`write-outputs`],deny:[`edit-files`]},{id:`finisher`,kind:`agent`,name:`Finisher`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"Finish the candidate PICK.md names: move it into its real place, close its gaps, and fold in the borrowed idea if it fits. Leave the other candidates untouched. Run `{{test-command}}` before you report.",inputs:[`PICK.md`,`the winning candidate`],outputs:[`the finished change`,`CHANGES.md`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-candidate-a-filter`,from:`candidate-a`,to:`filter`},{id:`e-candidate-b-filter`,from:`candidate-b`,to:`filter`},{id:`e-candidate-c-filter`,from:`candidate-c`,to:`filter`},{id:`e-filter-judge`,from:`filter`,to:`judge`,when:`pass`,evidence:[`the finalists' folders`,`filter output`]},{id:`e-judge-finisher`,from:`judge`,to:`finisher`,when:`pass`,evidence:[`PICK.md`,`the winning candidate`]},{id:`e-finisher-done`,from:`finisher`,to:`done`}],loops:[],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]}});function r(){let r=[];for(let[t,i]of Object.entries(n)){let n=e(i);n.doc?.template?r.push(n.doc):console.warn(`grooph: skipped ${t}: not a template document`)}return t(r)}var i=r();export{i as BUILT_IN_TEMPLATES};
1
+ import{pn as e}from"./share-DV3wsAsJ.js";import{p as t}from"./templates-DbAhWvP6.js";var n=Object.assign({"../../../../patterns/contradiction-seeker.grooph.json":{grooph:0,id:`contradiction-seeker`,name:`Contradiction seeker`,version:1,goal:`{{task}} Done when a critic with a fixed budget finds no counterexample to this: {{claim}}`,target:{harness:`claude-code`},template:{kind:`graph`,title:`Contradiction seeker`,summary:`A critic hunts one counterexample to a stated claim on a fixed budget; finding none is a pass, finding one sends it back to the builder.`,whenToUse:`A claim can be broken by one concrete counterexample (a property, an invariant, an edge case), and a bounded search for it is worth more than an open-ended review.`,notFor:`Claims no single input can break (style, clarity), or proving correctness: a hunt that finds nothing is evidence, not proof.`,profile:{cost:`low`,speed:`fast`,rigor:`standard`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add a slugify(text) function to src/strings.ts that lowercases, strips accents and joins words with single hyphens.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`},{key:`claim`,ask:`What claim must hold, stated so that one counterexample would break it?`,example:`slugify(text) always returns lowercase ASCII words joined by single hyphens, and slugify(slugify(text)) equals slugify(text).`}],tags:[`loop`,`counterexample`,`budget`],demo:`experiments/patterns/contradiction-seeker/README.md`},description:`A fast builder works; a critic in a fresh context tries to break the stated claim with one concrete counterexample, on a fixed effort. A counterexample goes back to the builder as a failing case; none found is a pass. The hunt is bounded by design: the critic's brief caps how many attempts it makes, and the loop's budget and round cap end the loop regardless.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:"Make the task true in the code, with tests, so that the claim holds. On a later round, turn the counterexample in COUNTEREXAMPLE.md into a test and make it pass without special-casing that one input. Run `{{test-command}}` before you report.",inputs:[`the task`,`the claim: {{claim}}`,`COUNTEREXAMPLE.md (from round 1 on)`],outputs:[`the change, with tests`,`CHANGES.md`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`critic`,kind:`agent`,name:`Counterexample hunter`,role:`critic`,model:{tier:`strong`},effort:`medium`,brief:`Try to break the claim with one concrete counterexample: an input, how to run it, and the wrong result. Stop at the first one you can reproduce, or after about ten distinct attempts; finding none is a pass. You hunt; you do not fix.`,inputs:[`the claim: {{claim}}`,`diff of the change`,`the repository as the change leaves it, read-only`],outputs:[`COUNTEREXAMPLE.md: the reproducible counterexample, or the attempts that failed to find one`,`verdict: pass | fail`],allow:[`read-files`,`write-outputs`,`run-commands`,`run-tests`],deny:[`edit-files`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-critic`,from:`builder`,to:`critic`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`,`output of {{test-command}}`]},{id:`e-critic-fail`,from:`critic`,to:`builder`,when:`fail`,evidence:[`COUNTEREXAMPLE.md`]},{id:`e-critic-pass`,from:`critic`,to:`done`,when:`pass`}],loops:[{id:`hunt`,name:`Hunt`,members:[`builder`,`critic`],back:[`e-critic-fail`],mode:`judgment`,bar:{name:`No counterexample`,inspects:[{kind:`artifact`,ref:`COUNTEREXAMPLE.md`},{kind:`artifact`,ref:`output of {{test-command}}`}],acceptance:"A full hunt finds no input that breaks the claim, and `{{test-command}}` exits 0."},stops:[{kind:`bar-passed`},{kind:`budget`,measure:`dispatches`,limit:8},{kind:`max-iterations`,n:3}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/debate-then-build.grooph.json":{grooph:0,id:`debate-then-build`,name:`Debate then build`,version:1,goal:"{{task}} Done when a judged plan is approved by a human and its first version passes `{{test-command}}`.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Debate then build`,summary:`Two planners argue opposite approaches for a capped number of rounds, a judge writes the plan, a human approves it, and a small grind loop builds it.`,whenToUse:`Short adversarial planning, then a small build graph: the right approach is genuinely unclear and a wrong choice is expensive to undo.`,notFor:`Work with an obvious approach, or where drafts are cheaper than arguments (use tournament-then-judge).`,profile:{cost:`medium`,speed:`medium`,rigor:`standard`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Choose and build a caching approach for the slow /search endpoint in src/api/search.ts.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`}],tags:[`planning`,`debate`,`judge`,`human-gate`,`grind`],demo:`experiments/patterns/debate-then-build/README.md`},description:`One planner argues for the simplest approach that could work, the other against it and for the strongest alternative. A frontier judge reads both cases and either asks for one more exchange or writes PLAN.md; the debate has a round cap, after which the judge must decide. A human approves the plan, and a fast builder grinds the first version against the tests.`,nodes:[{id:`planner-a`,kind:`agent`,name:`Planner A`,role:`planner`,model:{tier:`strong`},effort:`high`,brief:`Argue for the simplest approach that could do the task, and answer the other side's strongest point when there is one. Keep your case under a page in CASE-A.md and concede the risks that are real. Write no code.`,inputs:[`the task`,`CASE-B.md and REBUT.md (from round 1 on)`],outputs:[`CASE-A.md`],allow:[`read-files`,`write-outputs`]},{id:`planner-b`,kind:`agent`,name:`Planner B`,role:`planner`,model:{tier:`strong`},effort:`high`,brief:`Argue against the approach in CASE-A.md and for the strongest alternative, answering its best points directly. Keep your case under a page in CASE-B.md and concede the risks that are real. Write no code.`,inputs:[`the task`,`CASE-A.md`],outputs:[`CASE-B.md`],allow:[`read-files`,`write-outputs`]},{id:`judge`,kind:`agent`,name:`Judge`,role:`judge`,model:{tier:`frontier`},effort:`high`,brief:`Read both cases. If one more exchange would change your choice, emit verdict rebut and write REBUT.md naming the point each side must answer; otherwise, or when the debate is out of rounds, write PLAN.md with the approach, why it won, and the smallest first version with its checks, and emit pass. You decide; you write no code.`,inputs:[`CASE-A.md`,`CASE-B.md`],outputs:[`PLAN.md, or REBUT.md when asking for another round`,`verdict: pass | rebut`],allow:[`read-files`,`write-outputs`],deny:[`edit-files`]},{id:`plan-gate`,kind:`human-gate`,name:`Approve the plan`,prompt:`Build the first version PLAN.md describes?`,options:[`approve`,`reject`]},{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:`Build the first version PLAN.md describes and nothing past it, with tests. On a later round, start from the failing output you are handed. Report what you built and anything in the plan that proved wrong.`,inputs:[`PLAN.md`,`failing test output (from round 1 on)`],outputs:[`the change, with tests`,`CHANGES.md`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`tests`,kind:`check`,name:`Tests`,check:{kind:`tests`,run:`{{test-command}}`,pass:`exit code 0 and no test skipped`}},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-planner-a-planner-b`,from:`planner-a`,to:`planner-b`,evidence:[`CASE-A.md`]},{id:`e-planner-b-judge`,from:`planner-b`,to:`judge`,evidence:[`CASE-A.md`,`CASE-B.md`]},{id:`e-judge-rebut`,from:`judge`,to:`planner-a`,when:{verdict:`rebut`},evidence:[`CASE-B.md`,`REBUT.md`]},{id:`e-judge-plan-gate`,from:`judge`,to:`plan-gate`,when:`pass`},{id:`e-plan-gate-builder`,from:`plan-gate`,to:`builder`,when:`pass`},{id:`e-builder-tests`,from:`builder`,to:`tests`},{id:`e-tests-fail`,from:`tests`,to:`builder`,when:`fail`,evidence:[`test output`]},{id:`e-tests-pass`,from:`tests`,to:`done`,when:`pass`}],loops:[{id:`debate`,name:`Debate`,members:[`planner-a`,`planner-b`,`judge`],back:[`e-judge-rebut`],mode:`judgment`,bar:{name:`Both cases answered`,inspects:[{kind:`file`,ref:`CASE-A.md`},{kind:`file`,ref:`CASE-B.md`}],acceptance:`Each case answers the other's strongest point, and the judge has written PLAN.md.`},stops:[{kind:`bar-passed`},{kind:`max-iterations`,n:2,then:`judge`},{kind:`budget`,measure:`dispatches`,limit:8}]},{id:`build`,name:`Build`,members:[`builder`,`tests`],back:[`e-tests-fail`],mode:`grind`,stops:[{kind:`max-iterations`,n:5},{kind:`budget`,measure:`minutes`,limit:30}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/dual-bar.grooph.json":{grooph:0,id:`dual-bar`,name:`Dual bar`,version:1,goal:`{{task}} Ship when this holds: {{ship-line}} Aim toward: {{aspiration}}`,target:{harness:`claude-code`},template:{kind:`graph`,title:`Dual bar`,summary:`A builder and an isolated critic loop against two lines: a reachable ship line that stops the loop, and an aspiration that only steers the findings.`,whenToUse:`The work needs both a ship line and a directional aspiration: good enough to ship is clear, and better is worth pointing at without ever blocking.`,notFor:`Work where only the aspiration is written down; a bar that cannot be met never stops, so write the ship line first.`,profile:{cost:`medium`,speed:`medium`,rigor:`standard`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add a GET /users/:id endpoint to the API in src/api/.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`},{key:`ship-line`,ask:`What must be true to ship? A reachable line the critic can check.`,example:"`pnpm test` passes, GET /users/:id returns 404 for unknown ids, and the README documents the endpoint."},{key:`aspiration`,ask:`What would great look like? Direction only: it never blocks shipping.`,example:`A new contributor can use the endpoint correctly from the README alone, on the first try.`}],tags:[`loop`,`review`,`bar`],demo:`experiments/patterns/dual-bar/README.md`},description:`A builder works and a frontier critic in a fresh context reports against both lines of the bar. The acceptance (the ship line) decides the verdict; the aspiration only ranks what to improve next and never fails a round. The loop stops when the ship line holds, when rounds stop bringing improvement, at its round cap, or at its dispatch budget.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"Do the task until the ship line holds, then spend any remaining rounds on the aspiration findings the critic ranks highest. Run `{{test-command}}` before you report; on a later round, start from REVIEW.md.",inputs:[`the task`,`the ship line: {{ship-line}}`,`the aspiration: {{aspiration}}`,`REVIEW.md (from round 1 on)`],outputs:[`the change, with tests`,`CHANGES.md: what changed this round`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`critic`,kind:`agent`,name:`Critic`,role:`critic`,model:{tier:`frontier`},effort:`high`,brief:`Judge the change against both lines of the bar and say, with file and line, where it stands on each. The ship line alone decides the verdict: pass when it holds. The aspiration never fails a round; use it only to rank what to improve next. You judge; you do not fix.`,inputs:[`the ship line: {{ship-line}}`,`the aspiration: {{aspiration}}`,`diff of the change`,`the repository as the change leaves it, read-only`],outputs:[`REVIEW.md: the ship line item by item, then ranked aspiration findings, then a verdict line`,`verdict: pass | fail | invalid-evidence`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-critic`,from:`builder`,to:`critic`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`,`output of {{test-command}}`]},{id:`e-critic-fail`,from:`critic`,to:`builder`,when:`fail`,evidence:[`REVIEW.md`]},{id:`e-critic-pass`,from:`critic`,to:`done`,when:`pass`}],loops:[{id:`review`,name:`Review`,members:[`builder`,`critic`],back:[`e-critic-fail`],mode:`judgment`,bar:{name:`Ship line and aspiration`,inspects:[{kind:`artifact`,ref:`diff of the change`},{kind:`artifact`,ref:`output of {{test-command}}`}],acceptance:`{{ship-line}}`,aspiration:`{{aspiration}}`},stops:[{kind:`bar-passed`},{kind:`diminishing-returns`,rounds:2},{kind:`max-iterations`,n:5},{kind:`budget`,measure:`dispatches`,limit:12}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/fresh-grind-rare-judge.grooph.json":{grooph:0,id:`fresh-grind-rare-judge`,name:`Fresh grind, rare judge`,version:1,goal:"{{task}} Done when every phase in {{phase-checklist}} has passed the judge and `{{test-command}}` passes.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Fresh grind, rare judge`,summary:`A fast builder grinds each phase against the tests; an expensive judge runs only at phase boundaries, against a phase checklist.`,whenToUse:`Cheap iteration most steps, an expensive critic at phase boundaries: long work splits into phases that tests can drive but only judgment can sign off.`,notFor:`Short tasks with one phase (use grind-loop or review-gate), or work where the tests cannot drive any of the steps.`,profile:{cost:`medium`,speed:`medium`,rigor:`high`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Migrate the storage layer in src/storage/ from callbacks to async/await, one module per phase.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`},{key:`phase-checklist`,ask:`Which file lists the phases, each with what the judge checks at its boundary?`,example:`docs/PHASES.md`}],tags:[`loop`,`grind`,`judge`,`phases`],demo:`experiments/patterns/fresh-grind-rare-judge/README.md`},description:`An inner grind loop, a fast builder against the tests, runs inside an outer judgment loop. Only when the tests pass does a frontier judge review the phase against the phase checklist: fail returns to the builder to fix the phase, next-phase returns it to start the next one, and pass ends the run. Both loops are budgeted.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:"Work through the current phase of {{phase-checklist}} until `{{test-command}}` passes; one phase at a time, never ahead. On a failing test, start from its output; after a judge review, start from PHASE-REVIEW.md. Report which phase you are on and what changed.",inputs:[`the task`,`{{phase-checklist}}`,`test output or PHASE-REVIEW.md (from round 1 on)`],outputs:[`the change for this phase, with tests`,`CHANGES.md: the phase and what changed`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`tests`,kind:`check`,name:`Tests`,check:{kind:`tests`,run:`{{test-command}}`,pass:`exit code 0 and no test skipped`}},{id:`judge`,kind:`agent`,name:`Phase judge`,description:`Runs only at phase boundaries: after the tests pass.`,role:`judge`,model:{tier:`frontier`},effort:`high`,brief:`Judge the finished phase against its entry in {{phase-checklist}}, citing file and line for each item. Emit fail with findings when the phase is not done, next-phase when it is and phases remain, and pass when the last phase is done. You judge; you do not fix.`,inputs:[`diff since the last phase boundary`,`the repository as the change leaves it, read-only`,`{{phase-checklist}}`],outputs:[`PHASE-REVIEW.md: the phase item by item and a verdict line`,`verdict: pass | fail | next-phase`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-tests`,from:`builder`,to:`tests`},{id:`e-tests-fail`,from:`tests`,to:`builder`,when:`fail`,evidence:[`test output`]},{id:`e-tests-judge`,from:`tests`,to:`judge`,when:`pass`,evidence:[`diff since the last phase boundary`,`the repository as the change leaves it, read-only`,`{{phase-checklist}}`,`test output`]},{id:`e-judge-fail`,from:`judge`,to:`builder`,when:`fail`,evidence:[`PHASE-REVIEW.md`]},{id:`e-judge-next-phase`,from:`judge`,to:`builder`,when:{verdict:`next-phase`},evidence:[`PHASE-REVIEW.md`]},{id:`e-judge-pass`,from:`judge`,to:`done`,when:`pass`}],loops:[{id:`grind`,name:`Grind`,members:[`builder`,`tests`],back:[`e-tests-fail`],mode:`grind`,stops:[{kind:`max-iterations`,n:5},{kind:`budget`,measure:`minutes`,limit:20}]},{id:`phases`,name:`Phases`,members:[`builder`,`tests`,`judge`],back:[`e-judge-fail`,`e-judge-next-phase`],mode:`judgment`,bar:{name:`Phase checklist`,inspects:[{kind:`checklist`,ref:`{{phase-checklist}}`},{kind:`artifact`,ref:`output of {{test-command}}`}],acceptance:"Every phase in {{phase-checklist}} is signed off by the judge with evidence, and `{{test-command}}` exits 0."},stops:[{kind:`bar-passed`},{kind:`max-iterations`,n:5},{kind:`budget`,measure:`dispatches`,limit:55}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/gauntlet-decomposed.grooph.json":{grooph:0,id:`gauntlet-decomposed`,name:`Gauntlet, decomposed`,version:2,goal:`{{task}} Done when every piece in PIECES.md is ticked by its critic against {{reference}}, the whole has been compared once more, and the human releases it.`,target:{harness:`claude-code`},template:{kind:`graph`,title:`Gauntlet, decomposed`,summary:`A planner cuts the work into pieces and a human approves the cut; one piece at a time an owner polishes while a fresh frontier critic compares captures blind against the reference; an integrator and a final blind comparison close it.`,whenToUse:`A rendered artifact must match a named reference and is too large for one owner and one critic: the pieces have real seams, each can be captured and judged alone, and they are coupled enough to build in sequence.`,notFor:`One small artifact (taste-polish), no reference (spec-then-loop), or independent pieces that could fan out (ownership-not-swarm); never a way to keep going until the reference loses.`,profile:{cost:`high`,speed:`slow`,rigor:`high`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a planner can cut into pieces.`,example:"Make the summary card rendered by `npm run render` from src/card.mjs match the reference card, piece by piece."},{key:`reference`,ask:`What named reference should the result be compared against: a file, a URL or a capture?`,example:`references/card-reference.svg`},{key:`capture-command`,ask:`Which command captures the artifact for the critics (screenshots, a rendered file, a text dump)?`,example:`npm run capture`}],tags:[`loop`,`gauntlet`,`reference`,`nested`],demo:`experiments/patterns/gauntlet-decomposed/README.md`,credits:[{name:`Matt Shumer's Gauntlet Loop and the Claude of Duty repository`,url:`https://github.com/mshumer/Claude-of-Duty`,note:`decompose, then builder plus fresh critic per piece against a real reference, blind side by side; here bounded, and pieces run in sequence because the repository's own note says fan-out lost on coupled work`}]},description:`The Gauntlet with its pieces, bounded. A frontier planner writes PIECES.md (pieces along real seams, each with an owner scope, a reference cut, an acceptance and a box) and a human approves the cut. The outer loop takes one piece per round: the owner works inside its scope and captures, a check refuses bad captures, and a fresh frontier critic compares the captures blind against the piece's cut of the reference, ticking the box on a pass; the inner loop returns the top gaps to the owner. When no piece remains an integrator assembles the whole, a fresh final critic compares it blind, and a human gate releases it. Both loops are capped and budgeted; no brief asks for a win over the reference. The outer loop pauses for you between pieces at its human stop, so a long decomposition is never unattended.`,nodes:[{id:`planner`,kind:`agent`,name:`Planner`,role:`planner`,model:{tier:`frontier`},effort:`high`,brief:`Cut the task into pieces along the real seams of the artifact and the code, few enough to build in sequence, and write PIECES.md: one entry per piece with its owner scope (what it may change), its cut of {{reference}} (which part of the reference the piece must match, named, never copied: no values, coordinates or colors out of the reference, which the owner must not see), its acceptance, and an unchecked box for the critic. Order the pieces so each can be judged on its own once the earlier ones are done. Plan only; build nothing.`,inputs:[`the task`,`{{reference}}`,`the project`],outputs:[`PIECES.md: the pieces in order, each with owner scope, reference cut, acceptance and a box`],allow:[`read-files`,`write-outputs`]},{id:`decomposition-gate`,kind:`human-gate`,name:`Approve the pieces`,prompt:`PIECES.md cuts the work into pieces, each with an owner scope, a reference cut and an acceptance. Are these the right pieces, in the right order?`,options:[`approve`,`revise`]},{id:`owner`,kind:`agent`,name:`Piece owner`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"You own the first unchecked piece of PIECES.md and change only what its owner scope allows, working from its acceptance and, from the second round on, the ranked gaps in GAPS.md, largest first, without regressing what already matches. Capture with `{{capture-command}}` before you report, and say which gaps you closed and which you left on purpose. The reference itself is the critic's; you work from the piece's cut of it.",inputs:[`PIECES.md`,`GAPS.md (from the second round on)`],outputs:[`the revised piece`,`captures/ of this revision`,`CHANGES.md: what changed, gaps closed`],allow:[`read-files`,`edit-files`,`run-commands`]},{id:`capture-check`,kind:`check`,name:`Capture check`,check:{kind:`evidence`,run:`{{capture-command}}`,pass:`every capture exists, opens, is legible at full size, and shows the current revision`}},{id:`critic`,kind:`agent`,name:`Piece critic`,role:`critic`,model:{tier:`frontier`},effort:`high`,brief:`Compare the captures of the current piece against its cut of {{reference}} side by side with the labels stripped and in random order, say which is better and why, then rank the gaps that matter most, at most five, each with where it shows and what closing it would look like. You judge; you do not fix. Report invalid-evidence rather than guessing when a capture is missing or unreadable. Verdict pass only when no gap you would call major remains, and on a pass tick the piece's box in PIECES.md.`,inputs:[`captures/ of the current revision`,`{{reference}}`,`PIECES.md`],outputs:[`GAPS.md: which is better and why, ranked gaps, a verdict line`,`PIECES.md with the box ticked on a pass`,`verdict: pass | fail | invalid-evidence`],allow:[`read-files`,`write-outputs`],deny:[`edit-files`]},{id:`next-piece`,kind:`check`,name:`A piece remains?`,description:`Pass means more work: an unchecked piece remains. Fail means every box is ticked.`,check:{kind:`command`,run:`grep -q '^- \\[ \\]' PIECES.md`,pass:`exit code 0: an unchecked piece remains in PIECES.md`}},{id:`integrator`,kind:`agent`,name:`Integrator`,role:`builder`,model:{tier:`strong`},effort:`medium`,brief:"Assemble the finished pieces into the whole without reworking any: resolve what shows only where they meet, then capture the whole with `{{capture-command}}`. Report what the assembly changed.",inputs:[`PIECES.md`,`the project`],outputs:[`the assembled artifact`,`captures/ of the whole`,`ASSEMBLY.md: what the assembly changed`],allow:[`read-files`,`edit-files`,`run-commands`]},{id:`final-critic`,kind:`agent`,name:`Final critic`,role:`critic`,model:{tier:`frontier`},effort:`high`,brief:`Compare the captures of the whole against {{reference}} side by side with the labels stripped and in random order, say which is better and why, then rank the gaps that remain, at most five. You judge; you do not fix. Verdict pass when no major gap remains; the human decides with your report either way.`,inputs:[`captures/ of the whole`,`{{reference}}`],outputs:[`FINAL.md: which is better and why, ranked gaps, a verdict line`,`verdict: pass | fail`],allow:[`read-files`,`write-outputs`],deny:[`edit-files`]},{id:`release-gate`,kind:`human-gate`,name:`Release`,prompt:`Every piece passed its critic and FINAL.md compares the whole against the reference. Release it as it is?`},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-planner-gate`,from:`planner`,to:`decomposition-gate`},{id:`e-gate-owner`,from:`decomposition-gate`,to:`owner`,when:`pass`},{id:`e-owner-capture-check`,from:`owner`,to:`capture-check`},{id:`e-capture-check-fail`,from:`capture-check`,to:`owner`,when:`fail`,evidence:[`capture check output`]},{id:`e-capture-check-critic`,from:`capture-check`,to:`critic`,when:`pass`,evidence:[`captures/ of the current revision`,`{{reference}}`,`PIECES.md`]},{id:`e-critic-fail`,from:`critic`,to:`owner`,when:`fail`,evidence:[`GAPS.md`]},{id:`e-critic-pass`,from:`critic`,to:`next-piece`,when:`pass`},{id:`e-next-piece-pass`,from:`next-piece`,to:`owner`,when:`pass`,evidence:[`PIECES.md`]},{id:`e-next-piece-fail`,from:`next-piece`,to:`integrator`,when:`fail`},{id:`e-integrator-final-critic`,from:`integrator`,to:`final-critic`,evidence:[`captures/ of the whole`,`{{reference}}`]},{id:`e-final-critic-release-gate`,from:`final-critic`,to:`release-gate`,evidence:[`FINAL.md`]},{id:`e-release-gate-done`,from:`release-gate`,to:`done`,when:`pass`}],loops:[{id:`polish`,name:`Polish a piece`,members:[`owner`,`capture-check`,`critic`],back:[`e-capture-check-fail`,`e-critic-fail`],mode:`judgment`,bar:{name:`The piece matches its cut`,inspects:[{kind:`artifact`,ref:`{{reference}}`,note:`the piece's cut of it`},{kind:`artifact`,ref:`captures/ of the current revision`},{kind:`checklist`,ref:`PIECES.md`}],acceptance:`The critic finds no major gap between the captures and the piece's cut of {{reference}}, and every capture is current and readable.`},stops:[{kind:`bar-passed`},{kind:`diminishing-returns`,rounds:2,metric:`major gaps remaining`},{kind:`max-iterations`,n:3},{kind:`budget`,measure:`dispatches`,limit:10}]},{id:`pieces`,name:`Pieces`,members:[`owner`,`capture-check`,`critic`,`next-piece`],back:[`e-next-piece-pass`],mode:`judgment`,bar:{name:`Every piece done`,inspects:[{kind:`checklist`,ref:`PIECES.md`,note:`one box per piece, ticked by its critic`}],acceptance:`Every box in PIECES.md is ticked by the critic with a pass verdict on record.`},stops:[{kind:`bar-passed`,then:`integrator`},{kind:`human`,every:2},{kind:`max-iterations`,n:4},{kind:`budget`,measure:`dispatches`,limit:42}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/grind-loop.grooph.json":{grooph:0,id:`grind-loop`,name:`Grind loop`,version:1,goal:"{{task}} Done when `{{test-command}}` passes.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Grind loop`,summary:`A fast builder works against the test suite until it passes, bounded by rounds and minutes.`,whenToUse:`Done and good are the same: tests, types or a task list supply the back pressure, so a passing check is the finish line.`,notFor:`Work where passing tests is not the same as good (taste, design, prose); use review-gate or taste-polish there.`,profile:{cost:`low`,speed:`fast`,rigor:`light`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add a slugify(text) function to src/strings.ts that lowercases, strips accents and joins words with single hyphens.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`}],tags:[`loop`,`grind`,`tests`],demo:`experiments/patterns/grind-loop/README.md`},description:`A builder changes the code and a check runs the tests. A failure goes back to the builder with the test output; a pass ends the run. The check is the bar, so there is no critic. The loop stops at its round cap or its time budget if the tests never pass.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:`Make the task true by changing the code, adding tests where they are missing. Do not skip, weaken or delete a test to get a pass. On a later round, start from the failing output you are handed. Report what you changed and which tests now pass.`,inputs:[`the task`,`failing test output (from round 1 on)`],outputs:[`the change`,`CHANGES.md: what changed this round and why`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`tests`,kind:`check`,name:`Tests`,check:{kind:`tests`,run:`{{test-command}}`,pass:`exit code 0 and no test skipped`}},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-tests`,from:`builder`,to:`tests`},{id:`e-tests-fail`,from:`tests`,to:`builder`,when:`fail`,evidence:[`test output`]},{id:`e-tests-pass`,from:`tests`,to:`done`,when:`pass`}],loops:[{id:`grind`,name:`Grind`,members:[`builder`,`tests`],back:[`e-tests-fail`],mode:`grind`,stops:[{kind:`max-iterations`,n:5},{kind:`budget`,measure:`minutes`,limit:30}]}]},"../../../../patterns/heterogeneous-critic.grooph.json":{grooph:0,id:`heterogeneous-critic`,name:`Heterogeneous critic`,version:1,goal:"{{task}} Done when every item in {{checklist}} is shown to hold, `{{test-command}}` passes, and a human approves the merge.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Heterogeneous critic`,summary:`The review gate with the critic on a different model tier from the builder, so it need not share the builder's blind spots.`,whenToUse:`The judge should not share the builder's model when isolating taste or blind spots matters: a same-model critic may keep approving the mistakes the builder makes.`,notFor:`Cheap, routine changes where a same-tier review-gate is enough, or checks a test can make; a stronger critic costs more every round.`,profile:{cost:`medium`,speed:`medium`,rigor:`high`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add a slugify(text) function to src/strings.ts that lowercases, strips accents and joins words with single hyphens.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`},{key:`checklist`,ask:`Which file holds the checklist the critic judges against?`,example:`docs/REVIEW-CHECKLIST.md`}],tags:[`loop`,`review`,`human-gate`,`heterogeneous`],demo:`experiments/patterns/heterogeneous-critic/README.md`},description:`The review-gate shape with the critic on the frontier tier and the builder on the strong tier. Within one harness both tiers are one model family, so this varies capability, not lineage; true cross-family judging needs a critic running in a second harness, which arrives with dual-harness nodes (stage 11).`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:`Do the task in the code, with tests. On a later round, read REVIEW.md first and address each finding, or say why it does not apply. Run the test command before you report, and do not review your own work beyond that.`,inputs:[`the task`,`{{checklist}}`,`REVIEW.md (from round 1 on)`],outputs:[`the change, with tests`,`CHANGES.md: what changed this round and which findings it addresses`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`critic`,kind:`agent`,name:`Critic`,description:`Runs on a different tier from the builder on purpose. For a critic from another model family, re-host this node on a second harness when dual-harness nodes exist (stage 11).`,role:`critic`,model:{tier:`frontier`},effort:`high`,brief:`Judge the change against the checklist, one line per item, citing the file and line that satisfies it or saying it is unmet; use the repository only to understand what the change touches. Run the test command yourself rather than trusting a report. You judge; you do not fix. Verdict pass only when every item holds and the tests pass; invalid-evidence when the diff or checklist cannot be read.`,inputs:[`diff of the change`,`the repository as the change leaves it, read-only`,`{{checklist}}`],outputs:[`REVIEW.md: one line per checklist item and a verdict line`,`verdict: pass | fail | invalid-evidence`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`merge-gate`,kind:`human-gate`,name:`Merge approval`,prompt:`The critic passed the change against the checklist. Merge it?`,options:[`approve`,`reject with feedback`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-critic`,from:`builder`,to:`critic`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`,`output of {{test-command}}`,`{{checklist}}`]},{id:`e-critic-fail`,from:`critic`,to:`builder`,when:`fail`,evidence:[`REVIEW.md`]},{id:`e-critic-pass`,from:`critic`,to:`merge-gate`,when:`pass`},{id:`e-merge-gate-done`,from:`merge-gate`,to:`done`,when:`pass`},{id:`e-merge-gate-reject`,from:`merge-gate`,to:`builder`,when:`fail`,evidence:[`the human's feedback`]}],loops:[{id:`review`,name:`Review`,members:[`builder`,`critic`,`merge-gate`],back:[`e-critic-fail`,`e-merge-gate-reject`],mode:`judgment`,bar:{name:`Review checklist`,inspects:[{kind:`checklist`,ref:`{{checklist}}`},{kind:`artifact`,ref:`output of {{test-command}}`}],acceptance:"Every checklist item is cited as satisfied with a file and line, and `{{test-command}}` exits 0."},stops:[{kind:`bar-passed`},{kind:`max-iterations`,n:4},{kind:`budget`,measure:`dispatches`,limit:10}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/human-gated-irreversible.grooph.json":{grooph:0,id:`human-gated-irreversible`,name:`Human-gated irreversible step`,version:1,template:{kind:`fragment`,title:`Human-gated irreversible step`,summary:`A human gate, then the one node that performs an irreversible action, then a stop: insert it before any merge, publish, spend or delete.`,whenToUse:`A step cannot be undone (merge, spend, publish, delete) and must wait for a human decision; this fragment satisfies E_IRREVERSIBLE_NO_GATE.`,notFor:`Reversible steps, where a gate only slows the run; and it gates only the way in you connect to it: every edge into the action must pass the gate.`,profile:{cost:`low`,speed:`fast`,rigor:`standard`},slots:[{key:`action`,ask:`What is the irreversible step, in one sentence?`,example:`Merge the release branch into main.`},{key:`irreversible`,ask:`Which kind of irreversible action is it: merge, publish, spend or delete?`,example:`merge`}],tags:[`fragment`,`human-gate`,`irreversible`,`brake`],demo:`experiments/patterns/human-gated-irreversible/README.md`},nodes:[{id:`gate`,kind:`human-gate`,name:`Irreversible step approval`,prompt:`About to do this, and it cannot be undone: {{action}} Go ahead?`,options:[`go ahead`,`stop`]},{id:`act`,kind:`agent`,name:`Irreversible step`,role:`builder`,model:{tier:`strong`},effort:`medium`,brief:`Carry out exactly this, once: {{action}} Do nothing else, and do not retry on failure; report the result instead. Write down what was done so a human can check it.`,inputs:[`the approved action`],outputs:[`ACTION.md: what was done and its result`],allow:[`read-files`,`run-commands`,`write-outputs`],irreversible:[`{{irreversible}}`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-gate-act`,from:`gate`,to:`act`,when:`pass`},{id:`e-act-done`,from:`act`,to:`done`}],loops:[]},"../../../../patterns/merge-queue.grooph.json":{grooph:0,id:`merge-queue`,name:`Merge queue`,version:1,template:{kind:`fragment`,title:`Merge queue`,summary:`An integration check on the queued batch; a failure goes to a bisector that finds the change at fault and leaves the rest queued; a pass waits for a human, who lets one node land the batch.`,whenToUse:`Several reviewed changes wait to land together and the integration command (build, lint, tests) is the arbiter: insert this before the host's stop node so a batch lands only green and only on a human's word.`,notFor:`A single change (human-gated-irreversible is enough), or a host whose changes are not yet reviewed: the queue integrates, it does not review.`,profile:{cost:`low`,speed:`medium`,rigor:`standard`},slots:[{key:`integration-command`,ask:`Which command integrates the queued batch and exits 0 only when it builds, lints and passes its tests?`,example:`npm run integrate`},{key:`queue-file`,ask:`Which file lists the queued changes and their status, in landing order?`,example:`QUEUE.md`},{key:`land-command`,ask:`Which command lands the queued batch, once?`,example:`npm run land`}],tags:[`fragment`,`merge`,`bisect`,`human-gate`,`irreversible`,`brake`],demo:`experiments/patterns/merge-queue/README.md`,credits:[{name:`Steve Yegge's Gas Town Refinery`,url:`https://github.com/gastownhall/gastown`,note:`a merge queue that integrates a batch, bisects on failure and lands by a human's word`},{name:`Bors`,url:`https://github.com/bors-ng/bors-ng`,note:`batch then bisect`}]},description:`A merge queue as a fragment. The integration check runs the integration command on the whole queued batch. On a failure a bisector splits the batch, finds the change that fails, holds it with the reason and leaves the rest queued, so the next integration is of a batch that can pass. On a pass a human gate asks, and one node carries out the land command once. The loop stops at its round cap or its dispatch budget; insert it before the host's stop node, as the human-gated-irreversible fragment is.`,nodes:[{id:`integrate`,kind:`check`,name:`Integrate the batch`,check:{kind:`command`,run:`{{integration-command}}`,pass:`exit code 0: the queued batch builds, lints and passes its tests together`}},{id:`bisect`,kind:`agent`,name:`Bisector`,role:`builder`,model:{tier:`strong`},effort:`medium`,brief:"The queued batch in {{queue-file}} fails `{{integration-command}}`: split the batch, run the integration on the parts, and find the change at fault. Hold that change in {{queue-file}} with the failure as the reason and leave every other change queued in its order; change nothing else, and fix nothing. Report what failed, how you found it and what stays queued in BISECT.md.",inputs:[`the integration output`,`{{queue-file}}`,`the project`],outputs:[`{{queue-file}} with the change at fault held and the rest queued`,`BISECT.md: the change at fault, how it was found, what stays queued`],allow:[`read-files`,`edit-files`,`run-commands`,`write-outputs`],owns:[`{{queue-file}}`]},{id:`land-gate`,kind:`human-gate`,name:`Land the batch`,prompt:`The queued batch in {{queue-file}} integrates green (BISECT.md, if present, says what was held and why). Land it now? It cannot be taken back.`,options:[`land`,`stop`]},{id:`land`,kind:`agent`,name:`Land`,role:`builder`,model:{tier:`strong`},effort:`medium`,brief:"Carry out `{{land-command}}` exactly once for the batch queued in {{queue-file}}. Do nothing else, and do not retry on failure; report the result instead. Write down what landed so a human can check it.",inputs:[`{{queue-file}}`,`the human's go-ahead`],outputs:[`LANDING.md: what landed and the command's result`],allow:[`read-files`,`run-commands`,`write-outputs`],irreversible:[`merge`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-integrate-fail`,from:`integrate`,to:`bisect`,when:`fail`,evidence:[`the integration output`,`{{queue-file}}`]},{id:`e-bisect-integrate`,from:`bisect`,to:`integrate`},{id:`e-integrate-land-gate`,from:`integrate`,to:`land-gate`,when:`pass`},{id:`e-land-gate-land`,from:`land-gate`,to:`land`,when:`pass`},{id:`e-land-done`,from:`land`,to:`done`}],loops:[{id:`queue`,name:`Queue`,members:[`integrate`,`bisect`],back:[`e-bisect-integrate`],mode:`grind`,stops:[{kind:`max-iterations`,n:4},{kind:`budget`,measure:`dispatches`,limit:9}]}]},"../../../../patterns/metric-sandwich.grooph.json":{grooph:0,id:`metric-sandwich`,name:`Metric sandwich`,version:1,goal:"{{task}} Done when `{{test-command}}` passes and a critic finds nothing unmet in {{checklist}}.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Metric sandwich`,summary:`Cheap deterministic checks run first; an expensive critic judges only what they cannot see, and either failure returns to the builder.`,whenToUse:`Cheap deterministic checks first, expensive judgment only on what those cannot see: lint and tests catch most failures, and a reviewer should not spend a round on them.`,notFor:`Work with no deterministic check to run first (use review-gate), or where the checks are the whole story (use grind-loop).`,profile:{cost:`medium`,speed:`medium`,rigor:`standard`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add a slugify(text) function to src/strings.ts that lowercases, strips accents and joins words with single hyphens.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm lint && pnpm test`},{key:`checklist`,ask:`Which file holds the checklist the critic judges against?`,example:`docs/REVIEW-CHECKLIST.md`}],tags:[`loop`,`checks`,`review`],demo:`experiments/patterns/metric-sandwich/README.md`},description:`A builder works; a check runs the cheap deterministic suite. Only a passing change reaches the frontier critic, which judges what the suite cannot see against a checklist. Either failing returns to the builder, so one loop has two back edges. It stops when the critic passes, at its round cap, or at its dispatch budget.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"Do the task in the code, with tests. On a later round you get either the failing check output or REVIEW.md; address what it names. Run `{{test-command}}` before you report.",inputs:[`the task`,`{{checklist}}`,`check output or REVIEW.md (from round 1 on)`],outputs:[`the change, with tests`,`CHANGES.md: what changed this round`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`checks`,kind:`check`,name:`Cheap checks`,check:{kind:`command`,run:`{{test-command}}`,pass:`exit code 0`}},{id:`critic`,kind:`agent`,name:`Critic`,role:`critic`,model:{tier:`frontier`},effort:`high`,brief:"Judge only what `{{test-command}}` cannot see, the items in the checklist, for a change that already passes it; use the repository only to understand what the change touches. Do not rerun or restate the checks. Cite the file and line for each finding; you judge, you do not fix. Verdict pass when no checklist item is unmet.",inputs:[`diff of the change`,`the repository as the change leaves it, read-only`,`{{checklist}}`],outputs:[`REVIEW.md: findings by checklist item and a verdict line`,`verdict: pass | fail | invalid-evidence`],allow:[`read-files`,`write-outputs`],deny:[`edit-files`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-checks`,from:`builder`,to:`checks`},{id:`e-checks-fail`,from:`checks`,to:`builder`,when:`fail`,evidence:[`check output`]},{id:`e-checks-critic`,from:`checks`,to:`critic`,when:`pass`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`,`{{checklist}}`]},{id:`e-critic-fail`,from:`critic`,to:`builder`,when:`fail`,evidence:[`REVIEW.md`]},{id:`e-critic-pass`,from:`critic`,to:`done`,when:`pass`}],loops:[{id:`sandwich`,name:`Sandwich`,members:[`builder`,`checks`,`critic`],back:[`e-checks-fail`,`e-critic-fail`],mode:`judgment`,bar:{name:`Checks, then checklist`,inspects:[{kind:`artifact`,ref:`output of {{test-command}}`},{kind:`checklist`,ref:`{{checklist}}`}],acceptance:"`{{test-command}}` exits 0 and the critic finds no unmet item in {{checklist}}."},stops:[{kind:`bar-passed`},{kind:`max-iterations`,n:5},{kind:`budget`,measure:`dispatches`,limit:16}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/ownership-not-swarm.grooph.json":{grooph:0,id:`ownership-not-swarm`,name:`Ownership, not swarm`,version:2,goal:"{{task}} Done when the pieces are integrated and `{{test-command}}` passes, with each coupled subsystem changed by exactly one owner.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Ownership, not swarm`,summary:`Coupled subsystems get one owner each, in sequence; only the independent pieces fan out to fast workers; an integrator joins them and the tests close it.`,whenToUse:`Coupled subsystems get one owner; fan out only the independent pieces. The work spans a shared core and several leaf pieces that touch nothing else.`,notFor:`Work that is all coupled (use one builder in a grind-loop or review-gate) or all independent (a fan-out alone); and never as a way to parallelise a shared core.`,profile:{cost:`medium`,speed:`medium`,rigor:`standard`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add tagging to notes: a tags table, tag endpoints in the API, and tag chips, a tag filter and a tag editor in the web UI.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`},{key:`subsystem-a`,ask:`Which folder is the first coupled subsystem, the one the others build on?`,example:`src/db/`},{key:`subsystem-b`,ask:`Which folder is the second coupled subsystem, built on the first?`,example:`src/api/`}],tags:[`ownership`,`fan-out`,`coupled`,`grind`],demo:`experiments/patterns/ownership-not-swarm/README.md`,credits:[{name:`Claude of Duty process note (Matt Shumer)`,url:`https://github.com/mshumer/Claude-of-Duty`,note:`sequential ownership beat parallel fan-out on coupled systems in the repository's own write-up`}]},description:`A planner splits the task into the coupled core and independent pieces. Two owners, both marked coupled and each owning one subsystem, work in sequence, the second building on the first. Only then do fast workers take the independent pieces, up to three at a time. An integrator joins everything and a test check closes a short grind loop. Parallel fan-out lost to sequential ownership on coupled systems (spec §13); this graph fans out only where nothing is coupled.`,nodes:[{id:`planner`,kind:`agent`,name:`Planner`,role:`planner`,model:{tier:`strong`},effort:`high`,brief:`Split the task into the coupled core, the work inside {{subsystem-a}} and {{subsystem-b}}, and independent pieces that touch neither. Write PLAN.md with what each owner does and the independent pieces as a list, each small enough for one worker. Write no code.`,inputs:[`the task`],outputs:[`PLAN.md`],allow:[`read-files`,`write-outputs`]},{id:`owner-a`,kind:`agent`,name:`Owner A`,coupled:!0,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"You are the only node that changes {{subsystem-a}}. Do its part of PLAN.md and record every interface the next owner relies on in HANDOFF-A.md. Run `{{test-command}}` before you report.",inputs:[`PLAN.md`],outputs:[`changes in {{subsystem-a}}`,`HANDOFF-A.md: interfaces and changes the next owner relies on`],allow:[`read-files`,`edit-files`,`run-tests`],owns:[`{{subsystem-a}}`]},{id:`owner-b`,kind:`agent`,name:`Owner B`,coupled:!0,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"You are the only node that changes {{subsystem-b}}. Do its part of PLAN.md on top of what HANDOFF-A.md describes; if {{subsystem-a}} needs to change, say so rather than changing it. Run `{{test-command}}` before you report.",inputs:[`PLAN.md`,`HANDOFF-A.md`],outputs:[`changes in {{subsystem-b}}`,`HANDOFF-B.md: interfaces the pieces rely on`],allow:[`read-files`,`edit-files`,`run-tests`],owns:[`{{subsystem-b}}`]},{id:`worker`,kind:`agent`,name:`Piece worker`,description:`One dispatch per independent piece in PLAN.md.`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:`Build one independent piece from PLAN.md, touching only its own files; {{subsystem-a}} and {{subsystem-b}} are read-only to you. If the piece turns out to need a change in either, stop and say so instead of making it. Report the files you touched.`,inputs:[`PLAN.md`,`HANDOFF-B.md`,`the piece to build`],outputs:[`the piece`,`a list of the files it touched`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`integrator`,kind:`agent`,name:`Integrator`,role:`synthesizer`,model:{tier:`strong`},effort:`high`,brief:`Join the pieces to the coupled core so the task works end to end, changing only the seams between them. {{subsystem-a}} and {{subsystem-b}} belong to their owners: a change there is a note in your report, not an edit. Write ARCHITECTURE.md naming each owner, what they own, and how the pieces connect.`,inputs:[`PLAN.md`,`HANDOFF-A.md`,`HANDOFF-B.md`,`the pieces`,`failing test output (from round 1 on)`],outputs:[`the integrated change`,`ARCHITECTURE.md`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`tests`,kind:`check`,name:`Tests`,check:{kind:`tests`,run:`{{test-command}}`,pass:`exit code 0 and no test skipped`}},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-planner-owner-a`,from:`planner`,to:`owner-a`,evidence:[`PLAN.md`]},{id:`e-owner-a-owner-b`,from:`owner-a`,to:`owner-b`,evidence:[`PLAN.md`,`HANDOFF-A.md`]},{id:`e-owner-b-worker`,from:`owner-b`,to:`worker`,concurrency:{max:3},evidence:[`PLAN.md`,`HANDOFF-B.md`],label:`one worker per independent piece`},{id:`e-worker-integrator`,from:`worker`,to:`integrator`,label:`after every piece is in`},{id:`e-integrator-tests`,from:`integrator`,to:`tests`},{id:`e-tests-fail`,from:`tests`,to:`integrator`,when:`fail`,evidence:[`test output`]},{id:`e-tests-pass`,from:`tests`,to:`done`,when:`pass`}],loops:[{id:`integrate`,name:`Integrate`,members:[`integrator`,`tests`],back:[`e-tests-fail`],mode:`grind`,stops:[{kind:`max-iterations`,n:3},{kind:`budget`,measure:`minutes`,limit:30}]}]},"../../../../patterns/patrol-pulse.grooph.json":{grooph:0,id:`patrol-pulse`,name:`Patrol pulse`,version:3,goal:`{{task}} One pulse is one run: it ends clean, or with new tickets in {{ticket-store}} for a human to prioritize. Nothing else changes.`,target:{harness:`claude-code`},template:{kind:`graph`,title:`Patrol pulse`,summary:`A scan lists candidate signals, a read-only investigator judges them against the project and the existing tickets, a writer files the new ones to the ticket store, and a human prioritizes; one pulse is one run.`,whenToUse:`Something should be looked at on a schedule (a log, a queue, a report) and the output of each look is a triaged record for a human, never a change: run the package under the harness's scheduler and each pulse is a run.`,notFor:`Anything that should change code in the same run (use a loop with a builder), or a watch with no durable store to file into: the memory is the project's ticket file or database, named by the slot, not grooph.`,profile:{cost:`low`,speed:`fast`,rigor:`standard`},slots:[{key:`task`,ask:`What does this patrol watch for, in one or two sentences?`,example:`Watch the API's log for faults users would feel: failed requests, retries that never succeed, resources running out.`},{key:`scan-command`,ask:`Which command lists the candidate signals for this pulse (errors in a log, failed jobs, stale items)? It should exit non-zero when there is nothing to look at.`,example:`grep -n -E ' (ERROR|WARN) ' logs/app.log`},{key:`ticket-store`,ask:`Which file or store holds the tickets, read by the investigator and appended to by the writer?`,example:`TICKETS.md`}],tags:[`pulse`,`scheduled`,`read-only`,`tickets`,`human-gate`],demo:`experiments/patterns/patrol-pulse/README.md`,credits:[{name:`u/croovies's "Lloyd" heartbeat orchestrator, as written up by explainx.ai (2026-08-14; a secondary source)`,url:`https://www.explainx.ai/blog/claude-code-loop-orchestrator-heartbeat-ticket-memory-august-2026`,note:`a standing checklist per pulse, a read-only investigator, findings to a durable ticket store, humans prioritize`},{name:`Steve Yegge's Gas Town patrols`,url:`https://github.com/gastownhall/gastown`,note:`patrol agents that loop by design; here one pulse is one run`}]},description:`A pulse, not a loop. The scan check runs the command that lists candidate signals; nothing to look at ends the pulse clean. Otherwise a read-only investigator judges each signal against the project and the tickets already filed and writes its findings; a clean verdict ends the pulse, a finding goes to a writer that owns only the ticket store, files one ticket per new finding and never files what is already filed. A human gate ends the pulse with the new tickets waiting to be prioritized. The package recurs through the harness's scheduler (the target doc says how); every pulse is its own run, so the run list is the pulse log.`,nodes:[{id:`scan`,kind:`check`,name:`Scan`,check:{kind:`command`,run:`{{scan-command}}`,pass:`exit code 0 and at least one candidate signal printed; nothing printed is a fail and ends the pulse clean`}},{id:`investigator`,kind:`agent`,name:`Investigator`,description:`Read-only by policy: it may read and run, never edit. Its judgment is what separates a fault from routine noise.`,role:`critic`,model:{tier:`strong`},effort:`high`,brief:`Judge every signal the scan listed: read the project and run what you need to tell a genuine fault from routine noise, and check {{ticket-store}} so a fault already on file is named by its ticket rather than reported as new. Write PULSE.md with one entry per genuine fault (the evidence, the likely cause, whether a ticket already covers it) and a line for what you dismissed and why. You investigate; you change nothing. Verdict finding when at least one genuine fault is not yet on file, clean otherwise.`,inputs:[`the scan output`,`{{ticket-store}}, read-only`,`the repository, read-only`],outputs:[`PULSE.md: genuine faults with evidence, dismissed signals, and a verdict line`,`verdict: clean | finding`],allow:[`read-files`,`run-commands`,`write-outputs`],deny:[`edit-files`]},{id:`ticket-writer`,kind:`agent`,name:`Ticket writer`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:`Append one ticket to {{ticket-store}} for each finding in PULSE.md that no existing ticket covers, in the store's own format, cross-referencing the tickets that already cover the rest. Never duplicate a ticket, never edit an existing one, and never touch code or anything outside {{ticket-store}}. Report what you filed and what you left because it was already on file in FILED.md.`,inputs:[`PULSE.md`,`{{ticket-store}}`],outputs:[`{{ticket-store}} with one new ticket per new finding`,`FILED.md: tickets filed and findings already covered`],allow:[`read-files`,`edit-files`,`write-outputs`],owns:[`{{ticket-store}}`]},{id:`prioritize`,kind:`human-gate`,name:`Prioritize`,prompt:`This pulse filed new tickets in {{ticket-store}} (FILED.md says which) and changed nothing else. Prioritize them when you can; the next pulse is a new run.`,options:[`prioritized`,`later`]},{id:`clean`,kind:`stop`,name:`Clean`,outcome:`success`},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-scan-clean`,from:`scan`,to:`clean`,when:`fail`,label:`nothing to look at`},{id:`e-scan-investigator`,from:`scan`,to:`investigator`,when:`pass`,evidence:[`the scan output`,`{{ticket-store}}, read-only`,`the repository, read-only`]},{id:`e-investigator-clean`,from:`investigator`,to:`clean`,when:{verdict:`clean`}},{id:`e-investigator-ticket-writer`,from:`investigator`,to:`ticket-writer`,when:{verdict:`finding`},evidence:[`PULSE.md`,`{{ticket-store}}`]},{id:`e-ticket-writer-prioritize`,from:`ticket-writer`,to:`prioritize`},{id:`e-prioritize-done`,from:`prioritize`,to:`done`}],loops:[],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/ralph-loop.grooph.json":{grooph:0,id:`ralph-loop`,name:`Ralph loop`,version:1,goal:"{{task}} Done when every item in {{plan-file}} is marked done and `{{test-command}}` passes.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Ralph loop`,summary:`A fresh builder takes the top unchecked item of a plan file each round, commits on green and appends what it learned to an agent file, until the plan is empty.`,whenToUse:`The work is already a prioritized list of small items that tests can check one at a time, and you want a fresh context per item with the plan and an agent file as the only memory.`,notFor:`One item that needs judgment rather than a test (use review-gate or taste-polish), or a plan whose items are too large or too coupled to finish one at a time.`,profile:{cost:`low`,speed:`fast`,rigor:`light`},slots:[{key:`task`,ask:`What is the work as a whole, in one or two sentences? The plan file holds the items.`,example:`Build the text-statistics module the plan describes, one item at a time.`},{key:`plan-file`,ask:"Which file holds the plan: a Markdown checklist, top item first, `- [ ]` open and `- [x]` done?",example:`PLAN.md`},{key:`agent-file`,ask:`Which file holds the operating notes the builder reads first and may append learnings to?`,example:`AGENT.md`},{key:`test-command`,ask:`Which command runs the tests?`,example:`npm test`}],tags:[`loop`,`grind`,`plan`,`tests`,`fresh-context`],demo:`experiments/patterns/ralph-loop/README.md`,credits:[{name:`Geoffrey Huntley's ralph loop`,url:`https://ghuntley.com/ralph/`,note:`one prompt piped into a fresh session per pass; one item of work per pass from a plan file; state on disk; the human as the brake, here replaced by stops`}]},description:`The plan file is the unit of work. Each round a fresh builder takes the top unchecked item, makes it true, runs the tests, and on green marks it done, commits and appends what it learned to the agent file. A test check catches an item that broke something; a plan check sends the builder back for the next item while any remains. The loop stops when the plan is empty, when the same failure repeats, at its round cap or at its dispatch budget: the stops the original loop leaves to the human.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:"Read {{agent-file}} first, then take only the top unchecked item of {{plan-file}}: make it true in the code with tests, run `{{test-command}}`, and on green mark the item done, append what you learned to {{agent-file}} and commit. Never start a second item, and never skip, weaken or delete a test to get a pass. On a later round, start from the failing output you are handed, which may come from the item you just marked done. Report the item you took, what changed and whether the tests passed.",inputs:[`{{plan-file}}`,`{{agent-file}}`,`the project`,`failing test output (from round 1 on)`],outputs:[`the change for the item, with tests`,`{{plan-file}} with the item marked done`,`{{agent-file}} with what was learned appended`,`a commit on green`],allow:[`read-files`,`edit-files`,`run-tests`,`run-commands`]},{id:`tests`,kind:`check`,name:`Tests`,check:{kind:`tests`,run:`{{test-command}}`,pass:`exit code 0 and no test skipped`}},{id:`plan-check`,kind:`check`,name:`Plan empty?`,description:`Counts the unchecked items left in the plan. grep prints the count and exits 1 when it is zero, so the printed count is the result, not the exit code.`,check:{kind:`command`,run:`grep -c '^- \\[ \\]' {{plan-file}}`,pass:`the printed count is 0: no unchecked item remains in {{plan-file}}`}},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-tests`,from:`builder`,to:`tests`},{id:`e-tests-fail`,from:`tests`,to:`builder`,when:`fail`,evidence:[`test output`]},{id:`e-tests-plan-check`,from:`tests`,to:`plan-check`,when:`pass`},{id:`e-plan-check-fail`,from:`plan-check`,to:`builder`,when:`fail`,evidence:[`{{plan-file}}`],label:`next item`},{id:`e-plan-check-pass`,from:`plan-check`,to:`done`,when:`pass`}],loops:[{id:`ralph`,name:`Ralph`,members:[`builder`,`tests`,`plan-check`],back:[`e-tests-fail`,`e-plan-check-fail`],mode:`grind`,stops:[{kind:`diminishing-returns`,rounds:2,metric:`the same test failure`},{kind:`max-iterations`,n:5},{kind:`budget`,measure:`dispatches`,limit:17}]}]},"../../../../patterns/red-team-loop.grooph.json":{grooph:0,id:`red-team-loop`,name:`Red-team loop`,version:1,goal:`{{task}} Done when a red team attacking {{attack-surface}} stops finding new failing traces.`,target:{harness:`claude-code`},template:{kind:`graph`,title:`Red-team loop`,summary:`A separate attacker records reproducible failing traces; the builder is given only the traces, until no new one appears.`,whenToUse:`A separate attacker should produce failing traces and the builder should see only those: hardening against inputs or abuse nobody has listed yet.`,notFor:`Work with a known list of cases to satisfy (use grind-loop against tests), or judging quality rather than breaking things.`,profile:{cost:`medium`,speed:`medium`,rigor:`high`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Harden login and session handling in src/auth/ against malformed input, replayed tokens and timing leaks.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`},{key:`attack-surface`,ask:`What should the red team attack?`,example:`the login endpoint and session handling in src/auth/`}],tags:[`loop`,`red-team`,`traces`,`hardening`],demo:`experiments/patterns/red-team-loop/README.md`},description:`A builder hardens the code; a frontier red team in a fresh context attacks the stated surface and writes each reproducible failure as a trace in traces/, which it alone owns. The builder sees only the traces, not the attacker's reasoning. The loop ends when an attack round finds nothing, stops when rounds stop bringing a new failing trace, and is capped by rounds and dispatches.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"Make the task hold under attack. Each round you see only the failing traces in traces/: make each one pass without special-casing it, and add it to the test suite. Keep `{{test-command}}` green and report which traces now pass.",inputs:[`the task`,`traces/ (from round 1 on)`],outputs:[`the change, with a test per fixed trace`,`CHANGES.md: traces fixed this round`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`red-team`,kind:`agent`,name:`Red team`,role:`red-team`,model:{tier:`frontier`},effort:`high`,brief:`Attack {{attack-surface}} and record each failure you can reproduce as a trace in traces/: the input, how to run it, and the wrong result. Traces are all the builder sees, so each must reproduce on its own. You attack; you do not fix. Verdict fail when you have a new failing trace, pass when you found none.`,inputs:[`the running code`,`source of {{attack-surface}}`],outputs:[`traces/: one reproducible failing trace per file`,`ATTACK.md: what was attacked and how, written whether or not a trace was found`,`verdict: pass | fail`],allow:[`read-files`,`write-outputs`,`run-commands`],deny:[`edit-files`],owns:[`traces`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-red-team`,from:`builder`,to:`red-team`,evidence:[`the running code`,`source of {{attack-surface}}`,`traces/ already recorded`]},{id:`e-red-team-fail`,from:`red-team`,to:`builder`,when:`fail`,evidence:[`traces/`]},{id:`e-red-team-pass`,from:`red-team`,to:`done`,when:`pass`}],loops:[{id:`attack`,name:`Attack`,members:[`builder`,`red-team`],back:[`e-red-team-fail`],mode:`judgment`,bar:{name:`No new failing trace`,inspects:[{kind:`file`,ref:`traces/`},{kind:`artifact`,ref:`output of {{test-command}}`}],acceptance:"A full attack round finds no failing trace, and every recorded trace passes in `{{test-command}}`."},stops:[{kind:`diminishing-returns`,rounds:2,metric:`new failing traces`},{kind:`max-iterations`,n:5},{kind:`budget`,measure:`dispatches`,limit:12}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/retrospective-rewrite.grooph.json":{grooph:0,id:`retrospective-rewrite`,name:`Retrospective rewrite`,version:1,goal:"{{task}} Done when `{{test-command}}` passes, or the loop runs out; either way, end with proposals to improve this graph.",target:{harness:`claude-code`},adaptation:`propose`,template:{kind:`graph`,title:`Retrospective rewrite`,summary:`A grind loop that ends, pass or fail, with a researcher reading the run's notes and proposing graph changes for the human to adopt.`,whenToUse:`After a run, propose graph edits rather than rewriting mid-flight: the graph is being tuned across runs and the human wants every change as a proposal.`,notFor:`One-off tasks nobody will run again, or when the lead should adapt the graph during the run (the default adaptive level does that).`,profile:{cost:`low`,speed:`medium`,rigor:`standard`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add a slugify(text) function to src/strings.ts that lowercases, strips accents and joins words with single hyphens.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`}],tags:[`loop`,`grind`,`retrospective`,`proposals`],demo:`experiments/patterns/retrospective-rewrite/README.md`},description:`The grind-loop shape with adaptation set to propose: the lead changes nothing during the run. When the tests pass, or when the loop stops on its rounds or minutes, a researcher reads the run's notes and progress log and writes proposals for the next version of this graph, each with its evidence and an op-list patch. The human decides after the run.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:`Make the task true by changing the code, adding tests where they are missing. Do not skip, weaken or delete a test to get a pass. On a later round, start from the failing output you are handed. Report what you changed and which tests now pass.`,inputs:[`the task`,`failing test output (from round 1 on)`],outputs:[`the change`,`CHANGES.md: what changed this round and why`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`tests`,kind:`check`,name:`Tests`,check:{kind:`tests`,run:`{{test-command}}`,pass:`exit code 0 and no test skipped`}},{id:`retro`,kind:`agent`,name:`Retrospective`,role:`researcher`,model:{tier:`strong`},effort:`medium`,brief:`Read this run's notes and progress log and propose changes to the graph for next time: a node to add or drop, a loop or stop that misfired, a brief that no longer fits. Each proposal needs a summary, the evidence from this run, and a patch as a list of grooph ops. Change nothing yourself; the human decides.`,inputs:[`the run's notes`,`PROGRESS.md`,`the graph`],outputs:[`PROPOSALS.md: one section per proposal with its summary, evidence and op-list patch; the lead appends each as a proposal note`],allow:[`read-files`,`write-outputs`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-tests`,from:`builder`,to:`tests`},{id:`e-tests-fail`,from:`tests`,to:`builder`,when:`fail`,evidence:[`test output`]},{id:`e-tests-retro`,from:`tests`,to:`retro`,when:`pass`,evidence:[`the run's notes`,`PROGRESS.md`]},{id:`e-retro-done`,from:`retro`,to:`done`}],loops:[{id:`grind`,name:`Grind`,members:[`builder`,`tests`],back:[`e-tests-fail`],mode:`grind`,stops:[{kind:`max-iterations`,n:5,then:`retro`},{kind:`budget`,measure:`minutes`,limit:30,then:`retro`}]}]},"../../../../patterns/review-gate.grooph.json":{grooph:0,id:`review-gate`,name:`Review gate`,version:1,goal:"{{task}} Done when every item in {{checklist}} is shown to hold, `{{test-command}}` passes, and a human approves the merge.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Review gate`,summary:`A builder works, an isolated critic checks the change against a written checklist, failures loop back, and a human approves the merge.`,whenToUse:`Work, then a separate reviewer, then iterate or pass: the change needs a second pair of eyes against a checklist that already exists.`,notFor:`Work where the tests alone decide done (use grind-loop), or where nobody has written down what good means yet (use spec-then-loop).`,profile:{cost:`medium`,speed:`medium`,rigor:`standard`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add a slugify(text) function to src/strings.ts that lowercases, strips accents and joins words with single hyphens.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`},{key:`checklist`,ask:`Which file holds the checklist the critic judges against?`,example:`docs/REVIEW-CHECKLIST.md`}],tags:[`loop`,`review`,`human-gate`],demo:`experiments/patterns/review-gate/README.md`},description:`A builder implements the task. A critic in a fresh context checks the diff and test output against a written checklist; failures return to the builder with REVIEW.md, and a pass goes to a human merge gate, whose rejection also returns to the builder. The loop stops when the bar passes, at its round cap, or at its dispatch budget. Builder and critic share a tier, which the validator flags; heterogeneous-critic is the same shape with the critic on another tier.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:`Do the task in the code, with tests. On a later round, read REVIEW.md first and address each finding, or say why it does not apply. Run the test command before you report, and do not review your own work beyond that.`,inputs:[`the task`,`{{checklist}}`,`REVIEW.md (from round 1 on)`],outputs:[`the change, with tests`,`CHANGES.md: what changed this round and which findings it addresses`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`critic`,kind:`agent`,name:`Critic`,role:`critic`,model:{tier:`strong`},effort:`high`,brief:`Judge the change against the checklist, one line per item, citing the file and line that satisfies it or saying it is unmet; use the repository only to understand what the change touches. Run the test command yourself rather than trusting a report; you judge, you do not fix. Verdict pass only when every item holds and the tests pass; invalid-evidence when the diff or checklist cannot be read.`,inputs:[`diff of the change`,`the repository as the change leaves it, read-only`,`{{checklist}}`],outputs:[`REVIEW.md: one line per checklist item and a verdict line`,`verdict: pass | fail | invalid-evidence`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`merge-gate`,kind:`human-gate`,name:`Merge approval`,prompt:`The critic passed the change against the checklist. Merge it?`,options:[`approve`,`reject with feedback`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-critic`,from:`builder`,to:`critic`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`,`output of {{test-command}}`,`{{checklist}}`]},{id:`e-critic-fail`,from:`critic`,to:`builder`,when:`fail`,evidence:[`REVIEW.md`]},{id:`e-critic-pass`,from:`critic`,to:`merge-gate`,when:`pass`},{id:`e-merge-gate-done`,from:`merge-gate`,to:`done`,when:`pass`},{id:`e-merge-gate-reject`,from:`merge-gate`,to:`builder`,when:`fail`,evidence:[`the human's feedback`]}],loops:[{id:`review`,name:`Review`,members:[`builder`,`critic`,`merge-gate`],back:[`e-critic-fail`,`e-merge-gate-reject`],mode:`judgment`,bar:{name:`Review checklist`,inspects:[{kind:`checklist`,ref:`{{checklist}}`},{kind:`artifact`,ref:`output of {{test-command}}`}],acceptance:"Every checklist item is cited as satisfied with a file and line, and `{{test-command}}` exits 0."},stops:[{kind:`bar-passed`},{kind:`max-iterations`,n:4},{kind:`budget`,measure:`dispatches`,limit:10}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/spec-then-loop.grooph.json":{grooph:0,id:`spec-then-loop`,name:`Spec then loop`,version:2,goal:"{{task}} Done when every line of an approved ACCEPTANCE.md is shown to hold and `{{test-command}}` passes.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Spec then loop`,summary:`A planner writes the answer key, a human approves it, then a builder and an isolated critic loop against it.`,whenToUse:`No external reference product exists: a planning pass writes an answer key (ACCEPTANCE.md) that becomes the bar. The default for zero-to-one work.`,notFor:`Work with a reference or checklist already in hand (use review-gate or taste-polish), or changes so small that writing acceptance criteria costs more than the change.`,profile:{cost:`medium`,speed:`medium`,rigor:`high`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add CSV export to the reports page: a download button that exports the rows currently shown.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`}],tags:[`loop`,`answer-key`,`human-gate`,`zero-to-one`],demo:`experiments/patterns/spec-then-loop/README.md`,credits:[{name:`Answer-key-first Gauntlet, a community modification of Matt Pocock's Wayfinder`,url:`https://github.com/mattpocock/skills/blob/main/docs/engineering/wayfinder.md`,note:`write the spec and a pass/fail answer key before the loop; the modification is known from a secondary write-up, not a primary source`}]},description:`A frontier planner turns the task into ACCEPTANCE.md, a short list of observable behaviors. A human approves it; rejecting ends the run so the task can be restated. A builder then works and a critic in a fresh context judges the change line by line against the approved file, which is the loop's bar. The loop stops when every line holds, at its round cap, or at its dispatch budget.`,nodes:[{id:`planner`,kind:`agent`,name:`Planner`,role:`planner`,model:{tier:`frontier`},effort:`high`,brief:"Turn the task into ACCEPTANCE.md: the observable behaviors that would make it done, each checkable by reading code or running `{{test-command}}`. Name what is out of scope. Keep it short enough to review in a minute, and write no code.",inputs:[`the task`],outputs:[`ACCEPTANCE.md`],allow:[`read-files`,`write-outputs`]},{id:`spec-gate`,kind:`human-gate`,name:`Approve the spec`,prompt:`Is ACCEPTANCE.md the right definition of done? Rejecting ends the run so the task can be restated.`,options:[`approve`,`reject`]},{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"Do the task until every line of ACCEPTANCE.md holds. The acceptance file is fixed: if a line is wrong, say so in your report rather than working around it. Run `{{test-command}}` before you report; on a later round, start from REVIEW.md.",inputs:[`the task`,`ACCEPTANCE.md`,`REVIEW.md (from round 1 on)`],outputs:[`the change, with tests`,`CHANGES.md: what changed this round`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`critic`,kind:`agent`,name:`Critic`,role:`critic`,model:{tier:`strong`},effort:`high`,brief:"Judge the change against ACCEPTANCE.md line by line, citing the file and line or the test that shows each one holds; use the repository only to understand what the change touches. Run `{{test-command}}` yourself. You judge; you do not fix, and you do not edit the acceptance file. Verdict pass only when every line holds; invalid-evidence when the diff or the file cannot be read.",inputs:[`diff of the change`,`the repository as the change leaves it, read-only`,`ACCEPTANCE.md`],outputs:[`REVIEW.md: one line per acceptance item and a verdict line`,`verdict: pass | fail | invalid-evidence`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-planner-spec-gate`,from:`planner`,to:`spec-gate`},{id:`e-spec-gate-builder`,from:`spec-gate`,to:`builder`,when:`pass`},{id:`e-builder-critic`,from:`builder`,to:`critic`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`,`output of {{test-command}}`,`ACCEPTANCE.md`]},{id:`e-critic-fail`,from:`critic`,to:`builder`,when:`fail`,evidence:[`REVIEW.md`]},{id:`e-critic-pass`,from:`critic`,to:`done`,when:`pass`}],loops:[{id:`build`,name:`Build`,members:[`builder`,`critic`],back:[`e-critic-fail`],mode:`judgment`,bar:{name:`ACCEPTANCE.md`,inspects:[{kind:`answer-key`,ref:`ACCEPTANCE.md`},{kind:`artifact`,ref:`output of {{test-command}}`}],acceptance:"Every line of ACCEPTANCE.md is shown to hold, and `{{test-command}}` exits 0.",answerKeyFrom:`planner`},stops:[{kind:`bar-passed`},{kind:`max-iterations`,n:4},{kind:`budget`,measure:`dispatches`,limit:10}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/specialist-critic-bank.grooph.json":{grooph:0,id:`specialist-critic-bank`,name:`Specialist critic bank`,version:1,goal:"{{task}} Done when the merged review lists no blocker and no major finding, `{{test-command}}` passes, and a human accepts the change.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Specialist critic bank`,summary:`Four fresh specialist critics review in parallel, a triage judge merges their findings by severity, and the change loops until nothing blocker or major remains.`,whenToUse:`Multiple disjoint judges (correctness, security, performance, taste) should each look at the change, and their findings need merging by severity.`,notFor:`Small or low-risk changes, where one review-gate critic is enough; four reviewers and a judge are each a dispatch, every round.`,profile:{cost:`high`,speed:`medium`,rigor:`high`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add password reset by email to the account service in src/account/.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`}],tags:[`loop`,`review`,`parallel`,`severity`],demo:`experiments/patterns/specialist-critic-bank/README.md`},description:`A builder works; four critics, each in a fresh context and each on one concern, review the same diff in parallel (at most four at a time). A frontier triage judge merges their reports into one list by severity and emits the verdict. Blockers or majors return to the builder; a clean list goes to a human, whose rejection also returns to the builder. The loop stops when the merged list is clean, at its round cap, or at its dispatch budget.`,nodes:[{id:`builder`,kind:`agent`,name:`Builder`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"Do the task in the code, with tests. On a later round, address every blocker and major in TRIAGE.md, or say why it does not apply; minors are optional. Run `{{test-command}}` before you report.",inputs:[`the task`,`TRIAGE.md (from round 1 on)`],outputs:[`the change, with tests`,`CHANGES.md: what changed this round`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`correctness`,kind:`agent`,name:`Correctness critic`,role:`critic`,model:{tier:`strong`},effort:`medium`,brief:"Review the change for correctness: wrong results, unhandled cases and missing tests; run `{{test-command}}` yourself only; other reviewers cover the rest. Rate each finding blocker, major or minor, with the file and line that shows it. You review; you do not fix. Report invalid-evidence rather than guessing when the diff cannot be read.",inputs:[`diff of the change`,`the repository as the change leaves it, read-only`],outputs:[`REVIEW-CORRECTNESS.md: findings rated blocker, major or minor`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`security`,kind:`agent`,name:`Security critic`,role:`critic`,model:{tier:`strong`},effort:`medium`,brief:`Review the change for security: input handling, secrets, authentication and authorization, and dependencies only; other reviewers cover the rest. Rate each finding blocker, major or minor, with the file and line that shows it. You review; you do not fix. Report invalid-evidence rather than guessing when the diff cannot be read.`,inputs:[`diff of the change`,`the repository as the change leaves it, read-only`],outputs:[`REVIEW-SECURITY.md: findings rated blocker, major or minor`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`performance`,kind:`agent`,name:`Performance critic`,role:`critic`,model:{tier:`strong`},effort:`medium`,brief:`Review the change for performance: work that grows badly with input, needless I/O, and memory only; other reviewers cover the rest. Rate each finding blocker, major or minor, with the file and line that shows it. You review; you do not fix. Report invalid-evidence rather than guessing when the diff cannot be read.`,inputs:[`diff of the change`,`the repository as the change leaves it, read-only`],outputs:[`REVIEW-PERFORMANCE.md: findings rated blocker, major or minor`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`taste`,kind:`agent`,name:`Taste critic`,role:`critic`,model:{tier:`strong`},effort:`medium`,brief:`Review the change for taste: naming, structure, readability and fit with the surrounding code only; other reviewers cover the rest. Rate each finding blocker, major or minor, with the file and line that shows it. You review; you do not fix. Report invalid-evidence rather than guessing when the diff cannot be read.`,inputs:[`diff of the change`,`the repository as the change leaves it, read-only`],outputs:[`REVIEW-TASTE.md: findings rated blocker, major or minor`],allow:[`read-files`,`write-outputs`,`run-tests`],deny:[`edit-files`]},{id:`triage`,kind:`agent`,name:`Triage judge`,description:`Runs once per round, after all four reviews are in.`,role:`judge`,model:{tier:`frontier`},effort:`high`,brief:`Merge the four reviews into one list ranked blocker, major, minor, dropping duplicates and any finding its reviewer could not show. Emit the verdict: pass only when nothing is blocker or major. You rank; you do not review again and you do not fix.`,inputs:[`the four REVIEW-*.md files`],outputs:[`TRIAGE.md: merged findings by severity and a verdict line`,`verdict: pass | fail`],allow:[`read-files`,`write-outputs`],deny:[`edit-files`]},{id:`gate`,kind:`human-gate`,name:`Accept the change`,prompt:`Triage found no blocker or major finding. Accept the change?`,options:[`accept`,`reject with feedback`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-builder-correctness`,from:`builder`,to:`correctness`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`,`output of {{test-command}}`]},{id:`e-builder-security`,from:`builder`,to:`security`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`,`dependency manifest`]},{id:`e-builder-performance`,from:`builder`,to:`performance`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`]},{id:`e-builder-taste`,from:`builder`,to:`taste`,evidence:[`diff of the change`,`the repository as the change leaves it, read-only`]},{id:`e-correctness-triage`,from:`correctness`,to:`triage`,evidence:[`REVIEW-CORRECTNESS.md`]},{id:`e-security-triage`,from:`security`,to:`triage`,evidence:[`REVIEW-SECURITY.md`]},{id:`e-performance-triage`,from:`performance`,to:`triage`,evidence:[`REVIEW-PERFORMANCE.md`]},{id:`e-taste-triage`,from:`taste`,to:`triage`,evidence:[`REVIEW-TASTE.md`]},{id:`e-triage-fail`,from:`triage`,to:`builder`,when:`fail`,evidence:[`TRIAGE.md`]},{id:`e-triage-gate`,from:`triage`,to:`gate`,when:`pass`},{id:`e-gate-done`,from:`gate`,to:`done`,when:`pass`},{id:`e-gate-reject`,from:`gate`,to:`builder`,when:`fail`,evidence:[`the human's feedback`]}],loops:[{id:`review`,name:`Review`,members:[`builder`,`correctness`,`security`,`performance`,`taste`,`triage`,`gate`],back:[`e-triage-fail`,`e-gate-reject`],mode:`judgment`,bar:{name:`Merged severity list`,inspects:[{kind:`artifact`,ref:`TRIAGE.md`},{kind:`artifact`,ref:`output of {{test-command}}`}],acceptance:"TRIAGE.md lists no blocker and no major finding, and `{{test-command}}` exits 0."},stops:[{kind:`bar-passed`},{kind:`max-iterations`,n:4},{kind:`budget`,measure:`dispatches`,limit:26}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`},{id:`p-concurrency-cap`,kind:`concurrency-cap`,scope:`graph`,params:{max:4}}]},"../../../../patterns/taste-polish.grooph.json":{grooph:0,id:`taste-polish`,name:`Taste polish`,version:2,goal:`{{task}} Done when a frontier critic comparing captures against {{reference}} finds no major gap, or when the human stops the polish.`,target:{harness:`claude-code`},template:{kind:`graph`,title:`Taste polish`,summary:`One owner polishes an existing artifact while an isolated frontier critic compares readable captures against a named reference, bounded by rounds, budget and a human check-in.`,whenToUse:`Done and good have split: the artifact works but must match a named, inspectable reference, and the polish can be judged from captures.`,notFor:`Zero-to-one work with no reference (use spec-then-loop), or a default for every task: unbounded 'keep going until it beats the reference' is exactly what the stops here prevent.`,profile:{cost:`high`,speed:`slow`,rigor:`high`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Polish the pricing page in site/pricing/ until it reads as clearly as the reference.`},{key:`reference`,ask:`What named reference should the result be compared against: a file, a URL or a capture?`,example:`references/pricing-reference.png`},{key:`capture-command`,ask:`Which command captures the artifact for the critic (screenshots, a rendered PDF, a recording)?`,example:`pnpm run capture -- --out captures/`}],tags:[`loop`,`taste`,`gauntlet`,`reference`],demo:`experiments/patterns/taste-polish/README.md`,credits:[{name:`Matt Shumer's Gauntlet Loop (Claude of Duty)`,url:`https://github.com/mshumer/Claude-of-Duty`,note:`the bounded form of the Gauntlet: one owner, an isolated critic against a named reference, real stops`}]},description:`The Gauntlet-style entry, bounded. One owner revises the artifact and captures it; a check refuses captures that are missing, stale or unreadable before any judging happens; a frontier critic in a fresh context compares the captures against the named reference and returns the top gaps. The loop stops when the critic finds no major gap, when rounds stop bringing improvement, when the human says so at their regular check-in, at its round cap, or at the dispatch budget.`,nodes:[{id:`owner`,kind:`agent`,name:`Owner`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"You own the artifact the task names and are the only node that changes it. Each round, close the gaps the critic ranked highest, largest first, without regressing what already matches the reference. Capture the artifact with `{{capture-command}}` before you report, and say which gaps you closed and which you left on purpose.",inputs:[`the task`,`{{reference}}`,`GAPS.md (from round 1 on)`],outputs:[`the revised artifact`,`captures/ of this revision`,`CHANGES.md: gaps closed this round`],allow:[`read-files`,`edit-files`,`run-commands`]},{id:`capture-check`,kind:`check`,name:`Capture check`,description:`Evidence quality is gated (spec §13): a bad capture can fake a pass or a fail, so the critic never sees one.`,check:{kind:`evidence`,run:`{{capture-command}}`,pass:`every capture exists, opens, is legible at full size, and shows the current revision`}},{id:`critic`,kind:`agent`,name:`Critic`,role:`critic`,model:{tier:`frontier`},effort:`high`,brief:`Compare the captures against {{reference}} side by side with the labels stripped and in random order, say which is better and why, then rank the gaps that matter most, at most five, each with where it shows and what closing it would look like. You judge; you do not fix. Report invalid-evidence rather than guessing when a capture is missing or unreadable. Verdict pass only when no gap you would call major remains.`,inputs:[`captures/ of the current revision`,`{{reference}}`],outputs:[`GAPS.md: ranked gaps and a verdict line`,`verdict: pass | fail | invalid-evidence`],allow:[`read-files`,`write-outputs`],deny:[`edit-files`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-owner-capture-check`,from:`owner`,to:`capture-check`},{id:`e-capture-check-fail`,from:`capture-check`,to:`owner`,when:`fail`,evidence:[`capture check output`]},{id:`e-capture-check-critic`,from:`capture-check`,to:`critic`,when:`pass`,evidence:[`captures/ of the current revision`,`{{reference}}`]},{id:`e-critic-fail`,from:`critic`,to:`owner`,when:`fail`,evidence:[`GAPS.md`]},{id:`e-critic-pass`,from:`critic`,to:`done`,when:`pass`}],loops:[{id:`polish`,name:`Polish`,members:[`owner`,`capture-check`,`critic`],back:[`e-capture-check-fail`,`e-critic-fail`],mode:`judgment`,bar:{name:`Match the reference`,inspects:[{kind:`artifact`,ref:`{{reference}}`,note:`the named reference`},{kind:`artifact`,ref:`captures/ of the current revision`}],acceptance:`The critic finds no major gap between the captures and {{reference}}, and every capture is current and readable.`},stops:[{kind:`bar-passed`},{kind:`diminishing-returns`,rounds:2,metric:`major gaps remaining`},{kind:`human`,every:2},{kind:`max-iterations`,n:5},{kind:`budget`,measure:`dispatches`,limit:16}]}],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]},"../../../../patterns/tournament-then-judge.grooph.json":{grooph:0,id:`tournament-then-judge`,name:`Tournament then judge`,version:1,goal:"{{task}} Done when the finalist the judge picks is finished and `{{test-command}}` passes.",target:{harness:`claude-code`},template:{kind:`graph`,title:`Tournament then judge`,summary:`Three fast candidates build in parallel, a cheap check drops the broken ones, a frontier judge picks one finalist, and a builder finishes it.`,whenToUse:`Cheap candidates, then spend the expensive critic on finalists: the approach is open, drafts are cheap, and comparing working options beats arguing about them.`,notFor:`Work with one obvious approach, or coupled changes to a shared core: candidates that must touch the same files cannot run side by side.`,profile:{cost:`medium`,speed:`fast`,rigor:`standard`},slots:[{key:`task`,ask:`What should be built or changed? One or two sentences a builder can act on.`,example:`Add a slugify(text) function to src/strings.ts that lowercases, strips accents and joins words with single hyphens.`},{key:`test-command`,ask:`Which command runs the tests?`,example:`pnpm test`},{key:`criteria`,ask:`What should the judge prefer among candidates that all pass the tests?`,example:`The smallest change that reads clearly and handles empty and non-ASCII input.`}],tags:[`parallel`,`candidates`,`judge`],demo:`experiments/patterns/tournament-then-judge/README.md`},description:`Three fast builders each draft the whole task in their own folder, in parallel. A check runs the tests on each and keeps the ones that pass. A frontier judge sees only those finalists and picks one against the stated criteria, naming any idea worth borrowing from the others. A builder finishes the winner. No loop: the tournament runs once.`,nodes:[{id:`candidate-a`,kind:`agent`,name:`Candidate A`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:`Build your own complete take on the task inside candidates/a/ and nowhere else. A runnable draft beats a polished fragment, and an approach different from the obvious one is welcome. Report your approach in one paragraph.`,inputs:[`the task`],outputs:[`candidates/a/`,`candidates/a/APPROACH.md`],allow:[`read-files`,`edit-files`,`run-tests`],owns:[`candidates/a`]},{id:`candidate-b`,kind:`agent`,name:`Candidate B`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:`Build your own complete take on the task inside candidates/b/ and nowhere else. A runnable draft beats a polished fragment, and an approach different from the obvious one is welcome. Report your approach in one paragraph.`,inputs:[`the task`],outputs:[`candidates/b/`,`candidates/b/APPROACH.md`],allow:[`read-files`,`edit-files`,`run-tests`],owns:[`candidates/b`]},{id:`candidate-c`,kind:`agent`,name:`Candidate C`,role:`builder`,model:{tier:`fast`},effort:`medium`,brief:`Build your own complete take on the task inside candidates/c/ and nowhere else. A runnable draft beats a polished fragment, and an approach different from the obvious one is welcome. Report your approach in one paragraph.`,inputs:[`the task`],outputs:[`candidates/c/`,`candidates/c/APPROACH.md`],allow:[`read-files`,`edit-files`,`run-tests`],owns:[`candidates/c`]},{id:`filter`,kind:`check`,name:`Finalist filter`,description:`Runs once, after all three candidates are in.`,check:{kind:`tests`,run:`{{test-command}}`,pass:`at least one candidate passes; those that pass are the finalists`}},{id:`judge`,kind:`agent`,name:`Judge`,role:`judge`,model:{tier:`frontier`},effort:`high`,brief:`Pick the one finalist to finish, judging against {{criteria}}; you see only the candidates that passed the filter. Write PICK.md naming the winner, why, and the best idea worth borrowing from another finalist. You pick; you do not edit any candidate.`,inputs:[`the finalists' folders`],outputs:[`PICK.md: the winner, the reason, one idea to borrow`,`verdict: pass`],allow:[`read-files`,`write-outputs`],deny:[`edit-files`]},{id:`finisher`,kind:`agent`,name:`Finisher`,role:`builder`,model:{tier:`strong`},effort:`high`,brief:"Finish the candidate PICK.md names: move it into its real place, close its gaps, and fold in the borrowed idea if it fits. Leave the other candidates untouched. Run `{{test-command}}` before you report.",inputs:[`PICK.md`,`the winning candidate`],outputs:[`the finished change`,`CHANGES.md`],allow:[`read-files`,`edit-files`,`run-tests`]},{id:`done`,kind:`stop`,name:`Done`,outcome:`success`}],edges:[{id:`e-candidate-a-filter`,from:`candidate-a`,to:`filter`},{id:`e-candidate-b-filter`,from:`candidate-b`,to:`filter`},{id:`e-candidate-c-filter`,from:`candidate-c`,to:`filter`},{id:`e-filter-judge`,from:`filter`,to:`judge`,when:`pass`,evidence:[`the finalists' folders`,`filter output`]},{id:`e-judge-finisher`,from:`judge`,to:`finisher`,when:`pass`,evidence:[`PICK.md`,`the winning candidate`]},{id:`e-finisher-done`,from:`finisher`,to:`done`}],loops:[],policies:[{id:`p-critic-isolation`,kind:`critic-isolation`,scope:`graph`},{id:`p-no-self-grading`,kind:`no-self-grading`,scope:`graph`}]}});function r(){let r=[];for(let[t,i]of Object.entries(n)){let n=e(i);n.doc?.template?r.push(n.doc):console.warn(`grooph: skipped ${t}: not a template document`)}return t(r)}var i=r();export{i as BUILT_IN_TEMPLATES};