@theodo/bernard 0.1.0-beta.0 → 0.1.0-beta.1

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 (260) hide show
  1. package/README.md +13 -12
  2. package/core/README.md +1 -1
  3. package/core/agents/review-quality.md +13 -12
  4. package/core/agents/review-security-bugs.md +1 -1
  5. package/core/agents/review-tests.md +1 -1
  6. package/core/coder/template/main.tf +14 -1
  7. package/core/skills/pull-request/SKILL.md +17 -7
  8. package/core/skills/pull-request/ci-fixer.md +3 -3
  9. package/dist/bin.mjs +4979 -3176
  10. package/dist/{config-BawD0TwQ.mjs → config-DtCwJKWe.mjs} +70 -6
  11. package/dist/config.d.mts +72 -4
  12. package/dist/config.mjs +1 -1
  13. package/dist/docs/404.html +23 -0
  14. package/dist/docs/CLAUDE.html +30 -0
  15. package/dist/docs/CONTEXT.html +30 -0
  16. package/dist/docs/CONTRIBUTING.html +62 -0
  17. package/dist/docs/assets/CLAUDE.md.BLSB-5gK.js +1 -0
  18. package/dist/docs/assets/CLAUDE.md.BLSB-5gK.lean.js +1 -0
  19. package/dist/docs/assets/CONTEXT.md.BovnvMGK.js +1 -0
  20. package/dist/docs/assets/CONTEXT.md.BovnvMGK.lean.js +1 -0
  21. package/dist/docs/assets/CONTRIBUTING.md.DwJsKrz3.js +33 -0
  22. package/dist/docs/assets/CONTRIBUTING.md.DwJsKrz3.lean.js +1 -0
  23. package/dist/docs/assets/app.URmpjWHb.js +312 -0
  24. package/dist/docs/assets/bernard.RCgrvDdX.svg +94 -0
  25. package/dist/docs/assets/chunks/@localSearchIndexroot.Ylq685Cw.js +1 -0
  26. package/dist/docs/assets/chunks/VPLocalSearchBox.C8gcRVj0.js +9 -0
  27. package/dist/docs/assets/chunks/dagre-GXQ25YYZ.2qvw8AqH.js +4 -0
  28. package/dist/docs/assets/chunks/framework.hgjl6W6o.js +20 -0
  29. package/dist/docs/assets/chunks/sizeCapture-INFHLROL.BEqL2zFi.js +1 -0
  30. package/dist/docs/assets/chunks/stateDiagram-v2-MP3YSRHH.3X7wNXKX.js +231 -0
  31. package/dist/docs/assets/chunks/theme.Cksh_UJH.js +2 -0
  32. package/dist/docs/assets/chunks/virtual_mermaid-config.DDnGl6nM.js +1 -0
  33. package/dist/docs/assets/core_README.md.DyxYsikc.js +15 -0
  34. package/dist/docs/assets/core_README.md.DyxYsikc.lean.js +1 -0
  35. package/dist/docs/assets/demo_README.md.Cpi2pUU9.js +18 -0
  36. package/dist/docs/assets/demo_README.md.Cpi2pUU9.lean.js +1 -0
  37. package/dist/docs/assets/docs_adr_0001-what-the-core-is-built-for.md.BxOoMjST.js +1 -0
  38. package/dist/docs/assets/docs_adr_0001-what-the-core-is-built-for.md.BxOoMjST.lean.js +1 -0
  39. package/dist/docs/assets/docs_adr_0002-which-harness-a-project-runs-on.md.2GwF-nj0.js +1 -0
  40. package/dist/docs/assets/docs_adr_0002-which-harness-a-project-runs-on.md.2GwF-nj0.lean.js +1 -0
  41. package/dist/docs/assets/docs_adr_0003-how-core-content-reaches-each-harness.md.BMWE00Mf.js +1 -0
  42. package/dist/docs/assets/docs_adr_0003-how-core-content-reaches-each-harness.md.BMWE00Mf.lean.js +1 -0
  43. package/dist/docs/assets/docs_adr_0004-how-the-core-uses-external-tools.md.B7dtE7MB.js +1 -0
  44. package/dist/docs/assets/docs_adr_0004-how-the-core-uses-external-tools.md.B7dtE7MB.lean.js +1 -0
  45. package/dist/docs/assets/docs_adr_0005-what-drives-the-pipeline.md.DGn0CJeq.js +1 -0
  46. package/dist/docs/assets/docs_adr_0005-what-drives-the-pipeline.md.DGn0CJeq.lean.js +1 -0
  47. package/dist/docs/assets/docs_adr_0006-what-steps-hand-off-to-each-other.md.BflT5ugJ.js +1 -0
  48. package/dist/docs/assets/docs_adr_0006-what-steps-hand-off-to-each-other.md.BflT5ugJ.lean.js +1 -0
  49. package/dist/docs/assets/docs_adr_0007-how-humans-take-part-in-the-pipeline.md.CNX3gUD-.js +1 -0
  50. package/dist/docs/assets/docs_adr_0007-how-humans-take-part-in-the-pipeline.md.CNX3gUD-.lean.js +1 -0
  51. package/dist/docs/assets/docs_adr_0008-where-a-steps-reviewers-run.md.aGbaBOpB.js +1 -0
  52. package/dist/docs/assets/docs_adr_0008-where-a-steps-reviewers-run.md.aGbaBOpB.lean.js +1 -0
  53. package/dist/docs/assets/docs_adr_0009-where-pipeline-state-and-handoffs-live.md.C3tKmV-E.js +1 -0
  54. package/dist/docs/assets/docs_adr_0009-where-pipeline-state-and-handoffs-live.md.C3tKmV-E.lean.js +1 -0
  55. package/dist/docs/assets/docs_adr_0010-how-a-project-adapts-the-core.md.Bp7N6tUE.js +1 -0
  56. package/dist/docs/assets/docs_adr_0010-how-a-project-adapts-the-core.md.Bp7N6tUE.lean.js +1 -0
  57. package/dist/docs/assets/docs_adr_0011-how-the-factory-is-packaged-and-versioned.md.BBJIwnqa.js +1 -0
  58. package/dist/docs/assets/docs_adr_0011-how-the-factory-is-packaged-and-versioned.md.BBJIwnqa.lean.js +1 -0
  59. package/dist/docs/assets/docs_adr_0012-how-a-steps-failures-are-told-apart-and-routed.md.uCyrBfug.js +1 -0
  60. package/dist/docs/assets/docs_adr_0012-how-a-steps-failures-are-told-apart-and-routed.md.uCyrBfug.lean.js +1 -0
  61. package/dist/docs/assets/docs_adr_0013-when-code-lands.md.oXgiobfz.js +1 -0
  62. package/dist/docs/assets/docs_adr_0013-when-code-lands.md.oXgiobfz.lean.js +1 -0
  63. package/dist/docs/assets/docs_adr_0014-what-a-step-may-change.md.Zslg3bnJ.js +1 -0
  64. package/dist/docs/assets/docs_adr_0014-what-a-step-may-change.md.Zslg3bnJ.lean.js +1 -0
  65. package/dist/docs/assets/docs_adr_0015-how-a-project-shapes-its-ticket-flow.md.DNHMxwIT.js +1 -0
  66. package/dist/docs/assets/docs_adr_0015-how-a-project-shapes-its-ticket-flow.md.DNHMxwIT.lean.js +1 -0
  67. package/dist/docs/assets/docs_adr_0016-which-core-the-factory-repo-runs.md.CLN6LqBZ.js +1 -0
  68. package/dist/docs/assets/docs_adr_0016-which-core-the-factory-repo-runs.md.CLN6LqBZ.lean.js +1 -0
  69. package/dist/docs/assets/docs_adr_0017-where-a-tickets-record-lives.md.jjUeM-ex.js +1 -0
  70. package/dist/docs/assets/docs_adr_0017-where-a-tickets-record-lives.md.jjUeM-ex.lean.js +1 -0
  71. package/dist/docs/assets/docs_adr_0018-the-products-name.md.6xaX9vDs.js +1 -0
  72. package/dist/docs/assets/docs_adr_0018-the-products-name.md.6xaX9vDs.lean.js +1 -0
  73. package/dist/docs/assets/docs_adr_0019-how-a-finding-is-answered-and-judged.md.CKKoO9-3.js +1 -0
  74. package/dist/docs/assets/docs_adr_0019-how-a-finding-is-answered-and-judged.md.CKKoO9-3.lean.js +1 -0
  75. package/dist/docs/assets/docs_adr_0020-how-an-interrupted-run-resumes.md.DTIP-MBK.js +1 -0
  76. package/dist/docs/assets/docs_adr_0020-how-an-interrupted-run-resumes.md.DTIP-MBK.lean.js +1 -0
  77. package/dist/docs/assets/docs_adr_0021-how-a-step-runs-in-its-workspace.md.B2r3-FPg.js +1 -0
  78. package/dist/docs/assets/docs_adr_0021-how-a-step-runs-in-its-workspace.md.B2r3-FPg.lean.js +1 -0
  79. package/dist/docs/assets/docs_adr_0022-who-names-the-branches.md.DaVk8X0i.js +1 -0
  80. package/dist/docs/assets/docs_adr_0022-who-names-the-branches.md.DaVk8X0i.lean.js +1 -0
  81. package/dist/docs/assets/docs_adr_0023-how-a-version-ships-before-npm.md.C7-wuCbq.js +1 -0
  82. package/dist/docs/assets/docs_adr_0023-how-a-version-ships-before-npm.md.C7-wuCbq.lean.js +1 -0
  83. package/dist/docs/assets/docs_adr_0024-how-the-config-type-carries-its-docs.md.CQmwhWXs.js +1 -0
  84. package/dist/docs/assets/docs_adr_0024-how-the-config-type-carries-its-docs.md.CQmwhWXs.lean.js +1 -0
  85. package/dist/docs/assets/docs_adr_0025-how-a-steps-handoff-is-written-and-checked.md.CgXYdF2v.js +1 -0
  86. package/dist/docs/assets/docs_adr_0025-how-a-steps-handoff-is-written-and-checked.md.CgXYdF2v.lean.js +1 -0
  87. package/dist/docs/assets/docs_adr_0026-support-for-github-copilot.md.DJsawm-R.js +1 -0
  88. package/dist/docs/assets/docs_adr_0026-support-for-github-copilot.md.DJsawm-R.lean.js +1 -0
  89. package/dist/docs/assets/docs_adr_0027-which-forge-a-project-is-on.md.CY0qy9xh.js +1 -0
  90. package/dist/docs/assets/docs_adr_0027-which-forge-a-project-is-on.md.CY0qy9xh.lean.js +1 -0
  91. package/dist/docs/assets/docs_adr_0028-several-ticket-flows-in-one-pipeline.md.oxffo2qg.js +1 -0
  92. package/dist/docs/assets/docs_adr_0028-several-ticket-flows-in-one-pipeline.md.oxffo2qg.lean.js +1 -0
  93. package/dist/docs/assets/docs_adr_0029-the-branch-a-feature-works-on.md.CrgkP6YJ.js +1 -0
  94. package/dist/docs/assets/docs_adr_0029-the-branch-a-feature-works-on.md.CrgkP6YJ.lean.js +1 -0
  95. package/dist/docs/assets/docs_adr_0030-a-runner-without-coder.md.CW8sF7jK.js +1 -0
  96. package/dist/docs/assets/docs_adr_0030-a-runner-without-coder.md.CW8sF7jK.lean.js +1 -0
  97. package/dist/docs/assets/docs_adr_0031-how-the-run-viewer-reads-its-runs.md.opQYnVF2.js +1 -0
  98. package/dist/docs/assets/docs_adr_0031-how-the-run-viewer-reads-its-runs.md.opQYnVF2.lean.js +1 -0
  99. package/dist/docs/assets/docs_adr_0032-how-a-project-gets-the-viewer.md.DB9NjYJf.js +1 -0
  100. package/dist/docs/assets/docs_adr_0032-how-a-project-gets-the-viewer.md.DB9NjYJf.lean.js +1 -0
  101. package/dist/docs/assets/docs_adr_0033-the-repos-license.md.NPlkrPj8.js +1 -0
  102. package/dist/docs/assets/docs_adr_0033-the-repos-license.md.NPlkrPj8.lean.js +1 -0
  103. package/dist/docs/assets/docs_adr_0034-how-code-review-launches-its-reviewers.md.DSNpkfaL.js +1 -0
  104. package/dist/docs/assets/docs_adr_0034-how-code-review-launches-its-reviewers.md.DSNpkfaL.lean.js +1 -0
  105. package/dist/docs/assets/docs_adr_0035-how-the-package-is-published.md.C1f4B5eb.js +1 -0
  106. package/dist/docs/assets/docs_adr_0035-how-the-package-is-published.md.C1f4B5eb.lean.js +1 -0
  107. package/dist/docs/assets/docs_adr_0036-what-the-cores-pull-request-skill-does.md.op39HLAT.js +1 -0
  108. package/dist/docs/assets/docs_adr_0036-what-the-cores-pull-request-skill-does.md.op39HLAT.lean.js +1 -0
  109. package/dist/docs/assets/docs_adr_0037-how-a-team-reads-the-factorys-documentation.md.Dar_Xo4t.js +1 -0
  110. package/dist/docs/assets/docs_adr_0037-how-a-team-reads-the-factorys-documentation.md.Dar_Xo4t.lean.js +1 -0
  111. package/dist/docs/assets/docs_adr_0038-where-adapters-live.md.CkVD3riv.js +1 -0
  112. package/dist/docs/assets/docs_adr_0038-where-adapters-live.md.CkVD3riv.lean.js +1 -0
  113. package/dist/docs/assets/docs_adr_0039-the-repos-license.md.C_rraWpb.js +1 -0
  114. package/dist/docs/assets/docs_adr_0039-the-repos-license.md.C_rraWpb.lean.js +1 -0
  115. package/dist/docs/assets/docs_adr_0040-how-a-version-is-released.md.CCdwC07z.js +1 -0
  116. package/dist/docs/assets/docs_adr_0040-how-a-version-is-released.md.CCdwC07z.lean.js +1 -0
  117. package/dist/docs/assets/docs_adr_0041-who-opens-the-features-pull-request.md.qLu9WLaG.js +1 -0
  118. package/dist/docs/assets/docs_adr_0041-who-opens-the-features-pull-request.md.qLu9WLaG.lean.js +1 -0
  119. package/dist/docs/assets/docs_adr_0042-how-secrets-are-kept-out-of-committed-records.md.DJWazI4H.js +1 -0
  120. package/dist/docs/assets/docs_adr_0042-how-secrets-are-kept-out-of-committed-records.md.DJWazI4H.lean.js +1 -0
  121. package/dist/docs/assets/docs_getting-started.md.BpQFS8Gr.js +12 -0
  122. package/dist/docs/assets/docs_getting-started.md.BpQFS8Gr.lean.js +1 -0
  123. package/dist/docs/assets/docs_github.md.D4gkW71G.js +1 -0
  124. package/dist/docs/assets/docs_github.md.D4gkW71G.lean.js +1 -0
  125. package/dist/docs/assets/docs_overlay.md.DoWVb6OM.js +35 -0
  126. package/dist/docs/assets/docs_overlay.md.DoWVb6OM.lean.js +1 -0
  127. package/dist/docs/assets/docs_pipeline.md.DdHywWSk.js +1 -0
  128. package/dist/docs/assets/docs_pipeline.md.DdHywWSk.lean.js +1 -0
  129. package/dist/docs/assets/docs_recipes.md.8Xf6BsRA.js +45 -0
  130. package/dist/docs/assets/docs_recipes.md.8Xf6BsRA.lean.js +1 -0
  131. package/dist/docs/assets/fixtures_README.md.DzbQ0hft.js +1 -0
  132. package/dist/docs/assets/fixtures_README.md.DzbQ0hft.lean.js +1 -0
  133. package/dist/docs/assets/index.md.DHgL6CzS.js +28 -0
  134. package/dist/docs/assets/index.md.DHgL6CzS.lean.js +1 -0
  135. package/dist/docs/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
  136. package/dist/docs/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
  137. package/dist/docs/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
  138. package/dist/docs/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
  139. package/dist/docs/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
  140. package/dist/docs/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
  141. package/dist/docs/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
  142. package/dist/docs/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
  143. package/dist/docs/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
  144. package/dist/docs/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
  145. package/dist/docs/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
  146. package/dist/docs/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
  147. package/dist/docs/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
  148. package/dist/docs/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
  149. package/dist/docs/assets/packages_docs_README.md.PV1a27nC.js +3 -0
  150. package/dist/docs/assets/packages_docs_README.md.PV1a27nC.lean.js +1 -0
  151. package/dist/docs/assets/packages_viewer_README.md.BynKZSdN.js +3 -0
  152. package/dist/docs/assets/packages_viewer_README.md.BynKZSdN.lean.js +1 -0
  153. package/dist/docs/assets/style.C3E8Ltp6.css +1 -0
  154. package/dist/docs/bernard.svg +94 -0
  155. package/dist/docs/core/README.html +44 -0
  156. package/dist/docs/demo/README.html +47 -0
  157. package/dist/docs/docs/adr/0001-what-the-core-is-built-for.html +30 -0
  158. package/dist/docs/docs/adr/0002-which-harness-a-project-runs-on.html +30 -0
  159. package/dist/docs/docs/adr/0003-how-core-content-reaches-each-harness.html +30 -0
  160. package/dist/docs/docs/adr/0004-how-the-core-uses-external-tools.html +30 -0
  161. package/dist/docs/docs/adr/0005-what-drives-the-pipeline.html +30 -0
  162. package/dist/docs/docs/adr/0006-what-steps-hand-off-to-each-other.html +30 -0
  163. package/dist/docs/docs/adr/0007-how-humans-take-part-in-the-pipeline.html +30 -0
  164. package/dist/docs/docs/adr/0008-where-a-steps-reviewers-run.html +30 -0
  165. package/dist/docs/docs/adr/0009-where-pipeline-state-and-handoffs-live.html +30 -0
  166. package/dist/docs/docs/adr/0010-how-a-project-adapts-the-core.html +30 -0
  167. package/dist/docs/docs/adr/0011-how-the-factory-is-packaged-and-versioned.html +30 -0
  168. package/dist/docs/docs/adr/0012-how-a-steps-failures-are-told-apart-and-routed.html +30 -0
  169. package/dist/docs/docs/adr/0013-when-code-lands.html +30 -0
  170. package/dist/docs/docs/adr/0014-what-a-step-may-change.html +30 -0
  171. package/dist/docs/docs/adr/0015-how-a-project-shapes-its-ticket-flow.html +30 -0
  172. package/dist/docs/docs/adr/0016-which-core-the-factory-repo-runs.html +30 -0
  173. package/dist/docs/docs/adr/0017-where-a-tickets-record-lives.html +30 -0
  174. package/dist/docs/docs/adr/0018-the-products-name.html +30 -0
  175. package/dist/docs/docs/adr/0019-how-a-finding-is-answered-and-judged.html +30 -0
  176. package/dist/docs/docs/adr/0020-how-an-interrupted-run-resumes.html +30 -0
  177. package/dist/docs/docs/adr/0021-how-a-step-runs-in-its-workspace.html +30 -0
  178. package/dist/docs/docs/adr/0022-who-names-the-branches.html +30 -0
  179. package/dist/docs/docs/adr/0023-how-a-version-ships-before-npm.html +30 -0
  180. package/dist/docs/docs/adr/0024-how-the-config-type-carries-its-docs.html +30 -0
  181. package/dist/docs/docs/adr/0025-how-a-steps-handoff-is-written-and-checked.html +30 -0
  182. package/dist/docs/docs/adr/0026-support-for-github-copilot.html +30 -0
  183. package/dist/docs/docs/adr/0027-which-forge-a-project-is-on.html +30 -0
  184. package/dist/docs/docs/adr/0028-several-ticket-flows-in-one-pipeline.html +30 -0
  185. package/dist/docs/docs/adr/0029-the-branch-a-feature-works-on.html +30 -0
  186. package/dist/docs/docs/adr/0030-a-runner-without-coder.html +30 -0
  187. package/dist/docs/docs/adr/0031-how-the-run-viewer-reads-its-runs.html +30 -0
  188. package/dist/docs/docs/adr/0032-how-a-project-gets-the-viewer.html +30 -0
  189. package/dist/docs/docs/adr/0033-the-repos-license.html +30 -0
  190. package/dist/docs/docs/adr/0034-how-code-review-launches-its-reviewers.html +30 -0
  191. package/dist/docs/docs/adr/0035-how-the-package-is-published.html +30 -0
  192. package/dist/docs/docs/adr/0036-what-the-cores-pull-request-skill-does.html +30 -0
  193. package/dist/docs/docs/adr/0037-how-a-team-reads-the-factorys-documentation.html +30 -0
  194. package/dist/docs/docs/adr/0038-where-adapters-live.html +30 -0
  195. package/dist/docs/docs/adr/0039-the-repos-license.html +30 -0
  196. package/dist/docs/docs/adr/0040-how-a-version-is-released.html +30 -0
  197. package/dist/docs/docs/adr/0041-who-opens-the-features-pull-request.html +30 -0
  198. package/dist/docs/docs/adr/0042-how-secrets-are-kept-out-of-committed-records.html +30 -0
  199. package/dist/docs/docs/getting-started.html +41 -0
  200. package/dist/docs/docs/github.html +30 -0
  201. package/dist/docs/docs/overlay.html +64 -0
  202. package/dist/docs/docs/pipeline.html +30 -0
  203. package/dist/docs/docs/recipes.html +74 -0
  204. package/dist/docs/fixtures/README.html +30 -0
  205. package/dist/docs/hashmap.json +1 -0
  206. package/dist/docs/index.html +57 -0
  207. package/dist/docs/packages/docs/README.html +32 -0
  208. package/dist/docs/packages/viewer/README.html +32 -0
  209. package/dist/docs/vp-icons.css +0 -0
  210. package/dist/viewer/assets/Heading-C5Va5XMQ.js +1 -0
  211. package/dist/viewer/assets/SelectionIndicator-DxHiKGW_.js +1 -0
  212. package/dist/viewer/assets/alert-BI2omIo2.js +1 -0
  213. package/dist/viewer/assets/attempt-Dunc67N6.js +1 -0
  214. package/dist/viewer/assets/card-IsZ2ZhSn.js +1 -0
  215. package/dist/viewer/assets/{chunk-WQQL4CDK-DSpW9ZO8.js → chunk-WQQL4CDK-Bd4bkond.js} +1 -1
  216. package/dist/viewer/assets/context-CzP3x3DO.js +7 -0
  217. package/dist/viewer/assets/{dagre-NK4GIZVX-BzzW8pOj.js → dagre-NK4GIZVX-Bhq8pl9Y.js} +1 -1
  218. package/dist/viewer/assets/{index-CnzUhLCJ.css → index-B90XVeY-.css} +1 -1
  219. package/dist/viewer/assets/index-tH-KNwJL.js +10 -0
  220. package/dist/viewer/assets/{mermaid.core-D1FXi6mg.js → mermaid.core-I426eCuN.js} +4 -4
  221. package/dist/viewer/assets/pipeline-Hw_7tocN.js +1 -0
  222. package/dist/viewer/assets/routes-BOl7_JgK.js +1 -0
  223. package/dist/viewer/assets/runs._runId-D4q1mLCt.js +6 -0
  224. package/dist/viewer/assets/runs._runId.index-B-woIhKA.js +1 -0
  225. package/dist/viewer/assets/runs._runId.machines-SkWqrTI6.js +2 -0
  226. package/dist/viewer/assets/runs._runId.sessions-DBVWEbLY.js +1 -0
  227. package/dist/viewer/assets/{stateDiagram-v2-A6IESKWZ-J9KnYRFT.js → stateDiagram-v2-A6IESKWZ-DUwIje_A.js} +1 -1
  228. package/dist/viewer/assets/surface-B6LupC_E.js +1 -0
  229. package/dist/viewer/assets/table-B8t5lQz5.js +1 -0
  230. package/dist/viewer/assets/useEvent-DXj38gmC.js +1 -0
  231. package/dist/viewer/assets/useToggleState-D_NUC1vr.js +2 -0
  232. package/dist/viewer/index.html +5 -5
  233. package/fixtures/starters/azure-devops/.factory/factory.config.ts +2 -7
  234. package/fixtures/starters/azure-devops/.factory/factory.lock.json +7 -8
  235. package/fixtures/starters/github/.factory/factory.config.ts +2 -0
  236. package/fixtures/starters/github/.factory/factory.lock.json +6 -7
  237. package/fixtures/starters/gitlab/.factory/factory.config.ts +9 -0
  238. package/fixtures/starters/gitlab/.factory/factory.lock.json +19 -0
  239. package/fixtures/starters/gitlab/.factory/slots/checks.command.md +1 -0
  240. package/fixtures/starters/gitlab/.factory/slots/context.sources.md +3 -0
  241. package/fixtures/starters/gitlab/.factory/slots/review.detectors.md +5 -0
  242. package/package.json +9 -7
  243. package/core/skills/pull-request/scripts/pr-drive.sh +0 -208
  244. package/dist/viewer/assets/Heading-Dm1Q0gV2.js +0 -1
  245. package/dist/viewer/assets/api-C4lMOGdC.js +0 -7
  246. package/dist/viewer/assets/attempt-lbW5voCR.js +0 -1
  247. package/dist/viewer/assets/card-CtJKot3P.js +0 -1
  248. package/dist/viewer/assets/context-DEB-Y3Iz.js +0 -1
  249. package/dist/viewer/assets/index-Co9KwXRI.js +0 -10
  250. package/dist/viewer/assets/jsx-runtime-CkXRJ162.js +0 -1
  251. package/dist/viewer/assets/pipeline-B-myvM6K.js +0 -1
  252. package/dist/viewer/assets/routes-BzvVN6y-.js +0 -1
  253. package/dist/viewer/assets/runs._runId-BVT2VH_w.js +0 -6
  254. package/dist/viewer/assets/runs._runId.index-C3bbzwAC.js +0 -1
  255. package/dist/viewer/assets/runs._runId.machines-Cj21KdLd.js +0 -2
  256. package/dist/viewer/assets/runs._runId.sessions-Tg6Ou3_9.js +0 -1
  257. package/dist/viewer/assets/table-DzduWiYu.js +0 -1
  258. package/dist/viewer/assets/useEvent-CtTfOmE7.js +0 -1
  259. package/dist/viewer/assets/useToggleState-DfzJwOaN.js +0 -2
  260. package/fixtures/starters/azure-devops/.factory/skills/pull-request/SKILL.md +0 -29
@@ -0,0 +1,45 @@
1
+ import{_ as i,o as e,c as a,a3 as t}from"./chunks/framework.hgjl6W6o.js";const c=JSON.parse('{"title":"Recipes","description":"","frontmatter":{},"headers":[],"relativePath":"docs/recipes.md","filePath":"docs/recipes.md"}'),n={name:"docs/recipes.md"};function h(l,s,r,p,k,d){return e(),a("div",null,[...s[0]||(s[0]=[t(`<h1 id="recipes" tabindex="-1">Recipes <a class="header-anchor" href="#recipes" aria-label="Permalink to &quot;Recipes&quot;">​</a></h1><p>Common changes to an overlay. Each one ends with <code>bernard sync</code> and a commit of <code>.factory/</code> with the generated files. The fields and slots they use are described in <a href="./overlay.html">overlay.md</a>.</p><h2 id="add-a-project-check-to-the-review" tabindex="-1">Add a project check to the review <a class="header-anchor" href="#add-a-project-check-to-the-review" aria-label="Permalink to &quot;Add a project check to the review&quot;">​</a></h2><p>Say your project runs <code>knip</code> to catch dead code.</p><ol><li><p>Add it to the command the steps run, in <code>.factory/slots/checks.command.md</code>:</p><div class="language-md vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">md</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">\`pnpm check\`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, which runs the linter, the type-checker, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">\`knip\`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> and the unit tests.</span></span></code></pre></div><p><code>implement</code> and <code>apply-review</code> run it before they finish, <code>validate</code> reports it red, and <code>code-review</code> stops on red before it pays three reviewers.</p></li><li><p>Tell the reviewers what it guarantees, in a row of <code>.factory/slots/review.detectors.md</code>:</p><div class="language-md vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">md</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">| Family | Detector | Guarantee |</span></span>
2
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">|---|---|---|</span></span>
3
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">| An export, a type or a file that nothing consumes | </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">\`knip\`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> | hard |</span></span></code></pre></div><p>A <code>hard</code> family is left to the tool. A partial guarantee says where it stops, such as <code>only for the cases the tests exercise</code> or <code>baseline: the files listed in knip.json</code>.</p></li></ol><h2 id="give-a-reviewer-the-project-s-own-criteria" tabindex="-1">Give a reviewer the project&#39;s own criteria <a class="header-anchor" href="#give-a-reviewer-the-project-s-own-criteria" aria-label="Permalink to &quot;Give a reviewer the project&#39;s own criteria&quot;">​</a></h2><p>Copy the default criteria from the core agent, such as the list in <a href="https://github.com/theodo-group/bernard/blob/v0.1.0-beta.1/core/agents/review-security-bugs.md" target="_blank" rel="noreferrer">core/agents/review-security-bugs.md</a> between <code>{{#slot &quot;review.security-bugs.criteria&quot;}}</code> and <code>{{/slot}}</code>, into <code>.factory/slots/review.security-bugs.criteria.md</code>. Then edit it: add your rules, drop the ones that do not apply. The slot replaces the whole list, all three of its subsections: <em>Security</em>, <em>Bugs</em> and <em>Performance</em>.</p><h2 id="override-a-core-skill" tabindex="-1">Override a core skill <a class="header-anchor" href="#override-a-core-skill" aria-label="Permalink to &quot;Override a core skill&quot;">​</a></h2><p>When no slot covers the change, replace the whole skill. The demo replaces <code>pull-request</code> because it has no forge:</p><ol><li><p>Copy <code>core/skills/pull-request/</code> to <code>.factory/skills/pull-request/</code>, and edit it. Keep its <code>name</code> and <code>disable-model-invocation: true</code>.</p></li><li><p>Declare it in <code>.factory/factory.config.ts</code>:</p><div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">overrides</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: [</span></span>
4
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { kind: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;skill&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, name: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;pull-request&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, reason: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;No forge: the skill pushes the branch and runs the checks on the merge with the base.&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
5
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">],</span></span></code></pre></div></li></ol><p>From now on, <code>bernard sync</code> warns when the core changes <code>pull-request</code>: compare your version with the new one. An agent or a fragment is overridden the same way, with <code>kind: &#39;agent&#39;</code> or <code>kind: &#39;fragment&#39;</code>.</p><h2 id="add-a-step-to-the-delivery-graph" tabindex="-1">Add a step to the delivery graph <a class="header-anchor" href="#add-a-step-to-the-delivery-graph" aria-label="Permalink to &quot;Add a step to the delivery graph&quot;">​</a></h2><p>Say every ticket goes through a security audit after the code review. Write the skill in <code>.factory/skills/security-audit/SKILL.md</code>, with <code>disable-model-invocation: true</code>, and insert it in the graph. <code>delivery.graphs</code> replaces the core&#39;s graph, so write it whole:</p><div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pipeline</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
6
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> delivery</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
7
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> graphs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
8
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> ticket</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
9
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> steps</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
10
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> implement</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">onPass</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;validate&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
11
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> validate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">onPass</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;code-review&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">onFail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;implement&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
12
+ <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &#39;code-review&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { onPass: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;security-audit&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, onFail: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;apply-review&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, onExhausted: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;security-audit&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
13
+ <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &#39;apply-review&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { onPass: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;code-review&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
14
+ <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &#39;security-audit&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { onPass: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;pull-request&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, onFail: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;implement&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
15
+ <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &#39;pull-request&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { onPass: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;human-review&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
16
+ <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &#39;human-review&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { gate: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, onPass: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;DONE&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
17
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
18
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
19
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
20
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
21
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">},</span></span></code></pre></div><p><code>bernard pipeline</code> draws the result. The new step may write anywhere: to restrict it, give its skill a scope (<a href="#restrict-what-a-step-may-write">below</a>).</p><h2 id="deliver-tickets-through-different-graphs" tabindex="-1">Deliver tickets through different graphs <a class="header-anchor" href="#deliver-tickets-through-different-graphs" aria-label="Permalink to &quot;Deliver tickets through different graphs&quot;">​</a></h2><p>Say a feature splits into backend and frontend tickets, which do not go through the same steps. Declare a graph for each in <code>delivery.graphs</code>:</p><div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pipeline</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
22
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> delivery</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
23
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> graphs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
24
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> backend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">steps</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">implement</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">onPass</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;validate&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">validate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">onPass</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;pull-request&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">onFail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;implement&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;pull-request&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { onPass: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;DONE&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> } } },</span></span>
25
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> frontend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">start</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;design&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">steps</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">design</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">onPass</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;implement&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">implement</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">onPass</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;pull-request&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;pull-request&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { onPass: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;DONE&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> } } },</span></span>
26
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
27
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
28
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">},</span></span></code></pre></div><p>Then override the <code>tickets</code> skill so that it writes each ticket&#39;s <code>graph</code>, <code>backend</code> or <code>frontend</code> (<a href="./adr/0028-several-ticket-flows-in-one-pipeline.html">ADR-0028</a>). With several graphs, a ticket that names none fails the feature. <code>bernard pipeline</code> draws each graph.</p><h2 id="remove-a-step-or-a-gate" tabindex="-1">Remove a step or a gate <a class="header-anchor" href="#remove-a-step-or-a-gate" aria-label="Permalink to &quot;Remove a step or a gate&quot;">​</a></h2><p>Reconnect the edges around it. Say the team reviews nothing: <code>pull-request</code> ends the graph.</p><div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;pull-request&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { onPass: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;DONE&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span></code></pre></div><p>and delete the <code>human-review</code> entry: the tickets of the graph then merge themselves into the feature branch, and the team reviews the feature&#39;s pull request only (<a href="./adr/0041-who-opens-the-features-pull-request.html">ADR-0041</a>). To keep the gate but let it decide alone, set its <code>policy: &#39;decide-and-log&#39;</code> instead: the decision is logged.</p><h2 id="route-a-failure-by-its-class" tabindex="-1">Route a failure by its class <a class="header-anchor" href="#route-a-failure-by-its-class" aria-label="Permalink to &quot;Route a failure by its class&quot;">​</a></h2><p>A step&#39;s failure class can go to a different step. To send a red CI back to <code>implement</code> rather than retrying <code>pull-request</code>:</p><div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;pull-request&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { onPass: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;human-review&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, onFail: { </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;ci-red&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;implement&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> } },</span></span></code></pre></div><p>A class the map does not name retries the step.</p><h2 id="restrict-what-a-step-may-write" tabindex="-1">Restrict what a step may write <a class="header-anchor" href="#restrict-what-a-step-may-write" aria-label="Permalink to &quot;Restrict what a step may write&quot;">​</a></h2><div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pipeline</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
29
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> scopes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
30
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> implement</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">write</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;src/**&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;test/**&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">] },</span></span>
31
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> validate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">write</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;tmp/**&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">] },</span></span>
32
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
33
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">},</span></span></code></pre></div><p>A scope is keyed by the skill a step runs, so two steps that run one skill share it. A step without a scope may write anywhere. A step that changes a file outside its scope fails, and the file is restored.</p><h2 id="switch-harness" tabindex="-1">Switch harness <a class="header-anchor" href="#switch-harness" aria-label="Permalink to &quot;Switch harness&quot;">​</a></h2><ol><li><p>Set <code>harness: &#39;pi&#39;</code>, <code>&#39;copilot&#39;</code> or <code>&#39;claude-code&#39;</code> in the config.</p></li><li><p>On Pi and GitHub Copilot, map the agent tiers to models, or the agents run on the session&#39;s model:</p><div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">models</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pi</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">fast</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;anthropic/claude-haiku-5-5&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">balanced</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;anthropic/claude-sonnet-5-5&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">deep</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;anthropic/claude-opus-5-5&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> } },</span></span>
34
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">models</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">copilot</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">fast</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;claude-haiku-4.5&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">balanced</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;claude-sonnet-4.5&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">deep</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;claude-opus-4.5&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> } },</span></span></code></pre></div></li><li><p><code>bernard sync</code> writes the new harness&#39;s files (<code>.pi/</code>, or <code>.github/skills/</code> and <code>.github/agents/</code> for GitHub Copilot) and deletes the files its lock listed for the old one. Commit both.</p></li></ol><p><code>bernard run</code> drives Claude Code and GitHub Copilot. A GitHub Copilot session reads its token from <code>COPILOT_GITHUB_TOKEN</code>, <code>GH_TOKEN</code> or <code>GITHUB_TOKEN</code>: a fine-grained personal access token with the <em>Copilot Requests</em> permission.</p><h2 id="change-forge" tabindex="-1">Change forge <a class="header-anchor" href="#change-forge" aria-label="Permalink to &quot;Change forge&quot;">​</a></h2><p>Set <code>adapters.forge</code> in <code>.factory/factory.config.ts</code>: <code>{ type: &#39;github&#39; }</code>, <code>{ type: &#39;gitlab&#39;, host: &#39;gitlab.example.com&#39; }</code> or <code>{ type: &#39;azure-devops&#39; }</code> (<a href="./overlay.html#forge">the overlay</a>), and sign in with the forge&#39;s tool where the steps run. Nothing to sync: <code>bernard pull-request</code> reads the config on each run. Your tracker does not change the forge: with Jira and Azure DevOps, take <code>azure-devops</code>.</p><h2 id="start-a-feature-from-a-jira-issue" tabindex="-1">Start a feature from a Jira issue <a class="header-anchor" href="#start-a-feature-from-a-jira-issue" aria-label="Permalink to &quot;Start a feature from a Jira issue&quot;">​</a></h2><p>Name the tracker in <code>.factory/factory.config.ts</code>, with the status of your workflow each event moves an issue to, as the REST API names it (<a href="./overlay.html#tracker">the overlay</a>):</p><div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">adapters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
35
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> tracker</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
36
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;jira&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
37
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> statuses</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">started</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;In Progress&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">inReview</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;In Review&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">blocked</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;Blocked&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">done</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;Done&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
38
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
39
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">},</span></span></code></pre></div><p>Then give <code>bernard run</code> your Jira site and an API token, and start the feature from its issue:</p><div class="language-sh vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">export</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> JIRA_BASE_URL</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">https://acme.atlassian.net JIRA_EMAIL</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">dev@acme.com JIRA_API_TOKEN</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">… JIRA_PROJECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">DATA</span></span>
40
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">bernard</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --feature</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> DATA-12</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # the feature data-12, on feat/data-12-&lt;slug&gt;</span></span>
41
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">bernard</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> status</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --feature</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> data-12</span></span></code></pre></div><p>For the tickets&#39; issues to follow too, create them in Jira and have your <code>tickets</code> skill return each one&#39;s key as the ticket&#39;s <code>trackerKey</code>. Bernard does not create them yet (<a href="https://github.com/theodo-group/bernard/issues/146" target="_blank" rel="noreferrer">#146</a>).</p><h2 id="change-the-runner" tabindex="-1">Change the runner <a class="header-anchor" href="#change-the-runner" aria-label="Permalink to &quot;Change the runner&quot;">​</a></h2><p>The local runner runs the feature and each ticket in a git worktree on your machine. A worktree only has the files its branch tracks: list the ignored files it needs from your checkout, then give it the command that makes it ready to test:</p><div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">adapters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
42
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> runner</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;local&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">worktree</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">copy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;.env&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">], </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">setup</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;pnpm install --frozen-lockfile&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> } },</span></span>
43
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">},</span></span></code></pre></div><p>Coder gives each ticket its own workspace instead. Point it at your project&#39;s template and repository:</p><div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">adapters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
44
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> runner</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: { </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;coder&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;my-project&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">repo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;my-org/my-project&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">base</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;develop&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
45
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">},</span></span></code></pre></div><h3 id="the-template-contract" tabindex="-1">The template contract <a class="header-anchor" href="#the-template-contract" aria-label="Permalink to &quot;The template contract&quot;">​</a></h3><p>The runner creates each workspace with <code>coder create --template &lt;template&gt;</code> and four parameters. The template must define each of them, and give the workspace:</p><table tabindex="0"><thead><tr><th>Parameter</th><th>The workspace</th></tr></thead><tbody><tr><td><code>repo</code></td><td>Has the repository cloned at <code>~/work</code>.</td></tr><tr><td><code>branch</code></td><td>Has <code>~/work</code> on this branch.</td></tr><tr><td><code>base</code></td><td>Creates <code>branch</code> from this branch when the remote has none.</td></tr><tr><td><code>with_db</code></td><td>Has a database of its own when <code>true</code>: ticket workspaces get one, the feature&#39;s does not.</td></tr></tbody></table><p>Node, pnpm, the <code>claude</code> CLI and, for the pull requests, the <code>gh</code> CLI are installed, and the startup scripts are done before the runner connects. Add whatever else your checks need.</p><p>The <a href="https://github.com/theodo-group/bernard/blob/v0.1.0-beta.1/core/coder/template/main.tf" target="_blank" rel="noreferrer">reference template</a> fulfils the contract on Docker: a <code>node:24-bookworm</code> container, a clone from GitHub, a Postgres at <code>DATABASE_URL</code> with a password generated per workspace. Copy it as the starting point of your own. Its <code>github_token</code> variable is the token the workspaces clone and push with: git reads it from <code>GH_TOKEN</code> through a credential helper at each call, so it never lands on disk. Claude Code, <code>gh</code> and <code>jq</code> install at pinned versions, from npm and from checksum-verified releases; bump them in the template.</p><h3 id="a-local-coder" tabindex="-1">A local Coder <a class="header-anchor" href="#a-local-coder" aria-label="Permalink to &quot;A local Coder&quot;">​</a></h3><p>With <code>local: true</code> on the runner, <code>bernard run</code> and <code>bernard resume</code> run the workspaces on a Coder in Docker on this machine, at <a href="http://localhost:7080" target="_blank" rel="noreferrer">http://localhost:7080</a>:</p><ul><li>they start the <code>bernard-coder</code> container from <code>ghcr.io/coder/coder</code>, published on 127.0.0.1 only, with the Docker socket mounted so it creates the workspaces as sibling containers, unless it already answers;</li><li>they put it on the <code>bernard</code> Docker network, created if missing; the workspaces join it through the template&#39;s <code>coder_network</code> variable and reach the Coder at <a href="http://bernard-coder:7080" target="_blank" rel="noreferrer">http://bernard-coder:7080</a>, its access URL;</li><li>on its first start, they create its admin, <code>admin@bernard.local</code>, with a password kept in <code>~/.config/bernard/local-coder-password</code>;</li><li>they sign the Coder CLI in to it, in place of any other Coder it was signed in to.</li></ul><p>The reference template is pushed with <code>coder_network=bernard</code> when the Coder CLI is signed in to the local Coder.</p><p>The Docker socket mounted into the container, which Coder&#39;s provisioner needs to create the workspaces, gives the container root on the host: the local Coder is for a single developer on their own machine, and its admin is root-equivalent on the host. Do not share it.</p><p>The container and its volume stay up after the run, for the next one. <code>bernard coder up</code> does the same on its own. <code>docker stop bernard-coder</code> stops it; <code>docker rm bernard-coder &amp;&amp; docker volume rm bernard-coder</code> removes it with its workspaces&#39; history.</p><h3 id="the-setup-check" tabindex="-1">The setup check <a class="header-anchor" href="#the-setup-check" aria-label="Permalink to &quot;The setup check&quot;">​</a></h3><p>Before anything else, <code>bernard run</code> and <code>bernard resume</code> check the Coder setup:</p><ul><li><strong>Not logged in:</strong> with <code>CODER_URL</code> and <code>CODER_SESSION_TOKEN</code> set, they log in with them; at a terminal, they ask <code>Start a local Coder in Docker at http://localhost:7080? (Y/n)</code>, and start <code>coder login</code> when told no.</li><li><strong>Template missing:</strong> at a terminal, they ask <code>Create template &lt;name&gt; from the reference template? (Y/n)</code> and push it. <code>--yes</code> pushes it without asking. The push passes <code>GH_TOKEN</code> (or <code>GITHUB_TOKEN</code>) as <code>github_token</code>.</li><li><strong>Parameters missing:</strong> at a terminal, they name the parameters the template lacks and ask whether to replace it with the reference template. <code>--yes</code> replaces it without asking. The template&#39;s previous version stays in its history: <code>coder templates versions promote</code> brings it back.</li></ul><p>Without a terminal, they never prompt: they refuse and print the command to run or the variable to set. In CI, set <code>CODER_URL</code> and <code>CODER_SESSION_TOKEN</code>, and pass <code>--yes</code>.</p><p><code>coder create</code> times out after 10 minutes. Its build output goes to the run&#39;s telemetry, as the session <code>&lt;workspace&gt;/create</code> (<code>sessions/&lt;workspace&gt;/create.log</code> in the run&#39;s folder, with the default telemetry); on timeout the run stops with an error naming the workspace.</p><p>A new kind of runner is a new adapter in the factory (<a href="./adr/0004-how-the-core-uses-external-tools.html">ADR-0004</a>).</p>`,63)])])}const E=i(n,[["render",h]]);export{c as __pageData,E as default};
@@ -0,0 +1 @@
1
+ import{_ as i,o as e,c as a,a3 as t}from"./chunks/framework.hgjl6W6o.js";const c=JSON.parse('{"title":"Recipes","description":"","frontmatter":{},"headers":[],"relativePath":"docs/recipes.md","filePath":"docs/recipes.md"}'),n={name:"docs/recipes.md"};function h(l,s,r,p,k,d){return e(),a("div",null,[...s[0]||(s[0]=[t("",63)])])}const E=i(n,[["render",h]]);export{c as __pageData,E as default};
@@ -0,0 +1 @@
1
+ import{_ as o,o as t,c as r,a3 as c}from"./chunks/framework.hgjl6W6o.js";const p=JSON.parse('{"title":"Fixtures","description":"","frontmatter":{},"headers":[],"relativePath":"fixtures/README.md","filePath":"fixtures/README.md"}'),a={name:"fixtures/README.md"};function d(s,e,n,i,l,h){return t(),r("div",null,[...e[0]||(e[0]=[c('<h1 id="fixtures" tabindex="-1">Fixtures <a class="header-anchor" href="#fixtures" aria-label="Permalink to &quot;Fixtures&quot;">​</a></h1><p>Reference projects. Each one has a <code>.factory/</code> overlay and commits the files <code>bernard sync</code> generates from it.</p><ul><li><code>claude-code/</code>, <code>pi/</code> and <code>copilot/</code>: one per harness, for the render tests.</li><li><code>starters/&lt;forge&gt;/</code>: the minimal overlay <code>bernard init --forge &lt;forge&gt;</code> writes into a project, following <a href="./../docs/getting-started.html">docs/getting-started.md</a>: <code>github/</code> and <code>azure-devops/</code> (ADR-0027).</li></ul><p>CI runs <code>pnpm check:sync</code>, which also checks this repo&#39;s own <code>.claude/</code> (ADR-0016). Any change to <code>core/</code> or to a renderer therefore shows up here as a diff to review: run <code>pnpm sync:all</code> and commit the result with the change.</p>',4)])])}const u=o(a,[["render",d]]);export{p as __pageData,u as default};
@@ -0,0 +1 @@
1
+ import{_ as o,o as t,c as r,a3 as c}from"./chunks/framework.hgjl6W6o.js";const p=JSON.parse('{"title":"Fixtures","description":"","frontmatter":{},"headers":[],"relativePath":"fixtures/README.md","filePath":"fixtures/README.md"}'),a={name:"fixtures/README.md"};function d(s,e,n,i,l,h){return t(),r("div",null,[...e[0]||(e[0]=[c("",4)])])}const u=o(a,[["render",d]]);export{p as __pageData,u as default};
@@ -0,0 +1,28 @@
1
+ import{_ as a,o as t,c as s,a3 as n}from"./chunks/framework.hgjl6W6o.js";const r="/docs/assets/bernard.RCgrvDdX.svg",g=JSON.parse('{"title":"Bernard the taktician","description":"","frontmatter":{},"headers":[],"relativePath":"index.md","filePath":"README.md"}'),i={name:"index.md"};function o(l,e,d,h,c,p){return t(),s("div",null,[...e[0]||(e[0]=[n('<h1 id="bernard-the-taktician" tabindex="-1">Bernard the taktician <a class="header-anchor" href="#bernard-the-taktician" aria-label="Permalink to &quot;Bernard the taktician&quot;">​</a></h1><p align="center"><img src="'+r+`" alt="Bernard the taktician, the hermit crab" width="180"></p><p>An agentic factory that takes a feature from its specification to pull requests ready for your team&#39;s review, for new builds and legacy migrations alike. Coding agents do the work, a fresh session for each step, along a pipeline that a state machine drives. By default, humans validate the specification and the plan, and review the pull requests. Each of these gates can instead decide on its own and log its decision, so a project goes fully autonomous once its quality holds.</p><p>Its default pipeline is a legacy migration&#39;s: moving an application off a legacy stack, such as COBOL, a 4GL or an aging Java or PHP codebase, to a new one, starting with an analysis of what the legacy does today. A new build drops that analysis and starts from the specification.</p><p>It is for teams that build or migrate an application feature by feature with a coding agent, <a href="https://docs.claude.com/en/docs/claude-code" target="_blank" rel="noreferrer">Claude Code</a>, <a href="https://github.com/badlogic/pi-mono" target="_blank" rel="noreferrer">Pi</a> or <a href="https://docs.github.com/copilot/how-tos/copilot-cli" target="_blank" rel="noreferrer">GitHub Copilot</a>, which Bernard calls the project&#39;s <strong>harness</strong>. And for organisations that run several such projects: they all share one versioned <strong>core</strong>, and each project adapts it to its own context with an <strong>overlay</strong>.</p><p>Bernard is in alpha: <a href="#status">Status</a> says what works today and what is planned.</p><h2 id="why" tabindex="-1">Why <a class="header-anchor" href="#why" aria-label="Permalink to &quot;Why&quot;">​</a></h2><p>A team that delivers with coding agents ends up writing its own factory: the skills, agents and workflows that drive the agents. Teams on similar projects run the same pipeline, yet each one rewrites it. Generic know-how (writing a PRD, the product requirements document, running a code review, looping implementation and fixes) gets mixed with project context: the stack, the domain vocabulary, the issue tracker, the forge that hosts the code. The copies drift apart, and what one team learns doesn&#39;t reach the others.</p><p>Bernard keeps the two apart. The core holds the generic know-how, shared and versioned. A project&#39;s overlay, in its own repo, holds its context. <code>bernard sync</code> renders both into the native files of the project&#39;s harness. These words, and the rest of the vocabulary, are defined in <a href="./CONTEXT.html">CONTEXT.md</a>.</p><h2 id="how-a-feature-flows" tabindex="-1">How a feature flows <a class="header-anchor" href="#how-a-feature-flows" aria-label="Permalink to &quot;How a feature flows&quot;">​</a></h2><p><strong>The product manager validates the PRD, the developer validates the plan, and the factory delivers that plan up to the review of each pull request.</strong></p><ol><li><strong>Legacy analysis</strong> (migrations only). Automatic. It feeds the PRD.</li><li><strong>PRD.</strong> The factory drafts it, and the product manager reviews it in a pull request, like code.</li><li><strong>Plan.</strong> How to migrate the feature, or how to build it. Built in conversation with the developer, who validates it.</li><li><strong>Tickets → implementation → validation → review → PR → green CI.</strong> Automatic: the factory splits the plan into tickets, the smallest units it implements, and delivers each one along a graph of steps the project can reshape. The factory opens one pull request per feature, from the feature&#39;s own branch, <code>feat/&lt;feature&gt;-&lt;slug&gt;</code>, named by its first step, to the base branch. A ticket whose graph has no gate after <code>pull-request</code> merges itself into the feature branch, with no PR of its own, so the change is reviewed once, in the feature&#39;s PR. A graph with a gate keeps the ticket&#39;s PR to the feature branch: the team reviews it, acts on its review and merges it, and a ticket that depends on one not merged yet starts from its branch, so its PR stacks on the other. The team merges the feature&#39;s PR once its tickets are in.</li></ol><p>A project chooses the steps before the tickets, and the policy of each gate, in <a href="./docs/overlay.html#pipeline">its config</a>. <a href="./docs/pipeline.html">docs/pipeline.md</a> draws the default pipeline, a migration&#39;s, from the orchestrator&#39;s state machines.</p><h2 id="status" tabindex="-1">Status <a class="header-anchor" href="#status" aria-label="Permalink to &quot;Status&quot;">​</a></h2><p>What works today:</p><ul><li>The orchestrator runs the whole pipeline above, and resumes a run from the records it commits in the project&#39;s repo.</li><li><code>bernard status</code> shows the most recently active features and where each ticket stands, read from the records on the project&#39;s remote.</li><li>A run that waits on a human stalls with the command that answers it: <code>bernard resume --feature &lt;slug&gt;</code> with <code>--approve</code>, <code>--request-changes &quot;&lt;feedback&gt;&quot;</code>, <code>--retry</code> or <code>--answer &lt;questionId&gt;=&lt;text&gt;</code>, and <code>--ticket &lt;id&gt;</code> for a ticket. <code>bernard status</code> prints the same commands.</li><li>A tracker port, with a Jira Cloud adapter: <code>bernard run --feature DATA-12</code> starts a feature from a Jira issue, and the issues of the feature and its tickets follow the run.</li><li><code>bernard view</code> follows a project&#39;s runs live in the browser: each ticket&#39;s way through its graph of steps, every attempt and its session&#39;s log, and the machines, read from the project&#39;s telemetry. Next to it, under <code>/docs/</code>, it serves this documentation as a website, for the version the project pins.</li><li>The core ships the delivery steps: <code>implement</code>, <code>validate</code>, <code>code-review</code> with its three reviewers, <code>apply-review</code> and <code>pull-request</code>, which lands the ticket on the feature branch or opens its pull request, keeps the feature&#39;s pull request, on GitHub, GitLab or Azure DevOps with <code>bernard pull-request</code>, and drives its CI green, and a <code>tickets</code> step that reports the feature as a single ticket.</li><li><code>bernard sync</code> renders the core and an overlay for Claude Code, Pi and GitHub Copilot.</li><li><code>bernard init</code> writes a starter overlay for the project&#39;s forge, GitHub, GitLab or Azure DevOps, whatever its tracker: the forge is <code>adapters.forge</code> in its config.</li><li><code>bernard run</code> runs each step as a Claude Code or GitHub Copilot session, on your machine in a git worktree per ticket, or in a <a href="https://coder.com/" target="_blank" rel="noreferrer">Coder</a> workspace per ticket. The <a href="./demo/README.html">demo</a> delivers a feature end to end both ways.</li></ul><p>What is planned:</p><ul><li>Core skills for the legacy analysis, the PRD and the migration plan, a <code>tickets</code> skill that splits the plan into several tickets, and the legacy tracers that map a legacy application (<a href="https://github.com/theodo-group/bernard/issues/28" target="_blank" rel="noreferrer">#28</a>). Until then, a project writes them in its overlay or does them by hand, as the demo does.</li><li>A Pi driver for <code>bernard run</code>.</li></ul><h2 id="install" tabindex="-1">Install <a class="header-anchor" href="#install" aria-label="Permalink to &quot;Install&quot;">​</a></h2><p>You need Node 24, pnpm, git, and the harness your project runs.</p><p>From your project&#39;s root:</p><div class="language-sh vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">mkdir</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> .factory</span></span>
2
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pnpm</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --dir</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> .factory</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --ignore-workspace</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> add</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> -E</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> @theodo/bernard@next</span></span>
3
+ <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">alias</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> bernard</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.factory/node_modules/.bin/bernard </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># in your shell profile: each project runs the version it pins</span></span>
4
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">bernard</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> init</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --harness</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> claude-code</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --forge</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> github</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;My project&quot;</span></span>
5
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">bernard</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> sync</span></span></code></pre></div><p>The first two lines install the factory&#39;s package, <a href="https://www.npmjs.com/package/@theodo/bernard" target="_blank" rel="noreferrer"><code>@theodo/bernard</code></a>, from npm in <code>.factory/</code>, pinned to its exact version. While Bernard is in beta, each version is a pre-release, under the dist-tag <code>next</code>. <code>bernard init</code> writes a starter overlay next to it, and <code>bernard sync</code> renders the core and the overlay into the harness&#39;s folder, <code>.claude/</code> here. Commit <code>.factory/</code> and that folder: your team and your CI install the factory with <code>pnpm --dir .factory --ignore-workspace install</code>.</p><p>Then adapt the factory to your project:</p><ul><li><a href="./docs/getting-started.html">Getting started</a>: from the factory&#39;s package to a green <code>bernard sync</code> in your project.</li><li><a href="./docs/overlay.html">The overlay</a>: every config field and slot, and how to add or override a skill.</li><li><a href="./docs/recipes.html">Recipes</a>: add a check to the review, reshape the delivery graph, switch harness, change the runner.</li></ul><h2 id="decisions" tabindex="-1">Decisions <a class="header-anchor" href="#decisions" aria-label="Permalink to &quot;Decisions&quot;">​</a></h2><p>The factory&#39;s decisions are recorded, with the criteria and the options they weighed, as ADRs (architecture decision records) in <a href="https://github.com/theodo-group/bernard/tree/v0.1.0-beta.1/docs/adr" target="_blank" rel="noreferrer">docs/adr/</a>. <a href="./CONTEXT.html">CONTEXT.md</a> defines the vocabulary, and <a href="./CONTRIBUTING.html#how-a-run-fits-together">CONTRIBUTING.md</a> shows how a run fits together.</p><h2 id="layout" tabindex="-1">Layout <a class="header-anchor" href="#layout" aria-label="Permalink to &quot;Layout&quot;">​</a></h2><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>bernard/</span></span>
6
+ <span class="line"><span>├── core/ # skills, agents and shared fragments, in a harness-neutral format</span></span>
7
+ <span class="line"><span>├── packages/</span></span>
8
+ <span class="line"><span>│ ├── contracts/ # Zod schemas: overlay config, pipeline, skill frontmatter, step handoffs</span></span>
9
+ <span class="line"><span>│ ├── orchestrator/ # the state machines that drive the pipeline, and the records they replay</span></span>
10
+ <span class="line"><span>│ ├── render/ # core + overlay → Handlebars render</span></span>
11
+ <span class="line"><span>│ ├── harness-claude-code/ # Claude Code&#39;s native files, and its headless driver</span></span>
12
+ <span class="line"><span>│ ├── harness-pi/ # Pi&#39;s native files</span></span>
13
+ <span class="line"><span>│ ├── harness-copilot/ # GitHub Copilot&#39;s native files, and its headless driver</span></span>
14
+ <span class="line"><span>│ ├── telemetry/ # the telemetry port, and its default adapter: JSON lines on disk</span></span>
15
+ <span class="line"><span>│ ├── viewer/ # the run viewer&#39;s page, and its server on the telemetry port</span></span>
16
+ <span class="line"><span>│ ├── docs/ # the documentation site: VitePress, on the markdown of this repo</span></span>
17
+ <span class="line"><span>│ └── cli/ # bernard init | sync | pipeline | run | resume | status | view, and the adapters</span></span>
18
+ <span class="line"><span>├── fixtures/ # reference projects for the render tests, and a starter overlay per forge</span></span>
19
+ <span class="line"><span>├── demo/ # a feature delivered end to end, on a local Coder or locally</span></span>
20
+ <span class="line"><span>└── docs/ # the guides, the pipeline and the ADRs, which packages/docs renders</span></span></code></pre></div><p>The adapters live in <code>packages/cli/src/adapters/</code>, one folder per tool: <code>coder/</code> and <code>local/</code> for the runners, <code>jira/</code> for the tracker (ADR-0038). The ports, and the logic their adapters share, stay in <code>packages/cli/src/runners/</code> and <code>packages/cli/src/trackers/</code>. The JSON lines telemetry sits next to its port in <code>packages/telemetry/</code>.</p><p>In a project:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>my-project/</span></span>
21
+ <span class="line"><span>├── .factory/ # the overlay, owned by the project</span></span>
22
+ <span class="line"><span>│ ├── factory.config.ts # harness, project, paths, models, pipeline, adapters, handoffs, overrides</span></span>
23
+ <span class="line"><span>│ ├── slots/ # content injected into core skills and agents</span></span>
24
+ <span class="line"><span>│ ├── skills/ agents/ fragments/ # the project&#39;s own, or the core&#39;s it overrides</span></span>
25
+ <span class="line"><span>│ ├── factory.lock.json # what the last sync generated: the only files sync may overwrite or delete</span></span>
26
+ <span class="line"><span>│ └── package.json # pinned factory version</span></span>
27
+ <span class="line"><span>├── .claude/, .pi/ or .github/ # generated by \`bernard sync\`, committed</span></span>
28
+ <span class="line"><span>└── docs/features/&lt;feature&gt;/ # PRD, migration plan, tickets, and the run&#39;s records</span></span></code></pre></div><h2 id="development" tabindex="-1">Development <a class="header-anchor" href="#development" aria-label="Permalink to &quot;Development&quot;">​</a></h2><p>See <a href="./CONTRIBUTING.html">CONTRIBUTING.md</a>.</p><h2 id="license" tabindex="-1">License <a class="header-anchor" href="#license" aria-label="Permalink to &quot;License&quot;">​</a></h2><p><a href="https://github.com/theodo-group/bernard/blob/v0.1.0-beta.1/LICENSE" target="_blank" rel="noreferrer">PolyForm Shield License 1.0.0</a>, copyright Theodo Group.</p>`,36)])])}const f=a(i,[["render",o]]);export{g as __pageData,f as default};
@@ -0,0 +1 @@
1
+ import{_ as a,o as t,c as s,a3 as n}from"./chunks/framework.hgjl6W6o.js";const r="/docs/assets/bernard.RCgrvDdX.svg",g=JSON.parse('{"title":"Bernard the taktician","description":"","frontmatter":{},"headers":[],"relativePath":"index.md","filePath":"README.md"}'),i={name:"index.md"};function o(l,e,d,h,c,p){return t(),s("div",null,[...e[0]||(e[0]=[n("",36)])])}const f=a(i,[["render",o]]);export{g as __pageData,f as default};
@@ -0,0 +1,3 @@
1
+ import{_ as a,o as s,c as t,a3 as i}from"./chunks/framework.hgjl6W6o.js";const g=JSON.parse('{"title":"Documentation site","description":"","frontmatter":{},"headers":[],"relativePath":"packages/docs/README.md","filePath":"packages/docs/README.md"}'),o={name:"packages/docs/README.md"};function n(r,e,d,h,l,c){return s(),t("div",null,[...e[0]||(e[0]=[i(`<h1 id="documentation-site" tabindex="-1">Documentation site <a class="header-anchor" href="#documentation-site" aria-label="Permalink to &quot;Documentation site&quot;">​</a></h1><p>The factory&#39;s README, guides, vocabulary and ADRs, rendered as a website with <a href="https://vitepress.dev" target="_blank" rel="noreferrer">VitePress</a>. The package ships it, and <code>bernard view</code> serves it under <code>/docs/</code>, next to the run viewer: each links to the other from its header (ADR-0037).</p><div class="language-sh vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> dev:docs</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # VitePress, live, on http://localhost:5173/docs/</span></span>
2
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> check:docs</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # docs/pipeline.md is up to date, and the site builds into dist/docs/ with no dead link</span></span>
3
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> build</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # the CLI, the viewer, then this site into dist/docs/</span></span></code></pre></div><p>The pages are the markdown files of the repo, written for GitHub and read there as they are: this package holds the site&#39;s configuration and theme, not a copy of the pages.</p><h2 id="what-makes-a-page" tabindex="-1">What makes a page <a class="header-anchor" href="#what-makes-a-page" aria-label="Permalink to &quot;What makes a page&quot;">​</a></h2><p><code>SECTIONS</code> in <a href="https://github.com/theodo-group/bernard/blob/v0.1.0-beta.1/packages/docs/.vitepress/config.ts" target="_blank" rel="noreferrer">.vitepress/config.ts</a> is the sidebar, the nav and the list of pages. Every ADR of <code>docs/adr/</code> is a page, numbered in the sidebar under its <code># heading</code>; every other page is listed there, in reading order. <code>NOT_PAGES</code> lists the markdown that is never a page: the core&#39;s skills and agents, the fixtures, the demo&#39;s app, a feature&#39;s records and <code>tmp/</code>.</p><ul><li><strong>A new ADR</strong> needs nothing more.</li><li><strong>Any other new markdown file</strong> goes in a section of <code>SECTIONS</code>, or in <code>NOT_PAGES</code>: the build fails on a file in neither.</li></ul><p>A page&#39;s sidebar label is its first <code># heading</code>, or the one <code>LABELS</code> gives it when headings would clash or not fit.</p><h2 id="links" tabindex="-1">Links <a class="header-anchor" href="#links" aria-label="Permalink to &quot;Links&quot;">​</a></h2><p>Write links as GitHub reads them, relative to the page. On the site:</p><ul><li>a link to a page stays on the site;</li><li>a link to any other file or folder of the repo, such as <code>packages/cli/src/package.ts</code> or <code>core/skills/</code>, opens it on GitHub, at the tag of the version the site documents;</li><li>a link to nothing fails the build, whatever its extension. A link to <code>localhost</code> passes: the guides name the local Coder and the viewer.</li></ul><p>An image in an HTML tag starts with <code>./</code>, such as <code>&lt;img src=&quot;./docs/assets/bernard.svg&quot;&gt;</code>: VitePress resolves the path as a module otherwise. <code>docs/assets/</code> is the site&#39;s public folder: the logo comes from there.</p><h2 id="markdown" tabindex="-1">Markdown <a class="header-anchor" href="#markdown" aria-label="Permalink to &quot;Markdown&quot;">​</a></h2><ul><li><strong>Mermaid</strong>: a <code>mermaid</code> fence draws a diagram. The build bundles only the kind the orchestrator draws, <code>MERMAID_DIAGRAM</code> in <a href="https://github.com/theodo-group/bernard/blob/v0.1.0-beta.1/packages/viewer/mermaid-diagram.ts" target="_blank" rel="noreferrer">packages/viewer/mermaid-diagram.ts</a>, as the viewer does; drawing another kind means allowing it there. The build fails if Mermaid moves its diagrams.</li><li><strong>No Vue</strong>: a <code>{{</code> stays text, in code as in prose. A page cannot use Vue&#39;s expressions or components.</li></ul>`,14)])])}const k=a(o,[["render",n]]);export{g as __pageData,k as default};
@@ -0,0 +1 @@
1
+ import{_ as a,o as s,c as t,a3 as i}from"./chunks/framework.hgjl6W6o.js";const g=JSON.parse('{"title":"Documentation site","description":"","frontmatter":{},"headers":[],"relativePath":"packages/docs/README.md","filePath":"packages/docs/README.md"}'),o={name:"packages/docs/README.md"};function n(r,e,d,h,l,c){return s(),t("div",null,[...e[0]||(e[0]=[i("",14)])])}const k=a(o,[["render",n]]);export{g as __pageData,k as default};
@@ -0,0 +1,3 @@
1
+ import{_ as s,o as t,c as a,a3 as i}from"./chunks/framework.hgjl6W6o.js";const u=JSON.parse('{"title":"Run viewer","description":"","frontmatter":{},"headers":[],"relativePath":"packages/viewer/README.md","filePath":"packages/viewer/README.md"}'),n={name:"packages/viewer/README.md"};function o(r,e,d,h,c,l){return t(),a("div",null,[...e[0]||(e[0]=[i(`<h1 id="run-viewer" tabindex="-1">Run viewer <a class="header-anchor" href="#run-viewer" aria-label="Permalink to &quot;Run viewer&quot;">​</a></h1><p>A live page on a project&#39;s runs: the feature&#39;s steps, each ticket&#39;s graph of steps with every attempt and the subagents its session launched, the sessions&#39; logs as they are written, the decisions taken without a human, the environments the runner worked in, and the XState machines and their snapshots.</p><p>A single-page app: Vite, React 19, TanStack Router and Query, HeroUI v3 on Tailwind CSS v4. A small Node API serves it the runs, read through the telemetry port (ADR-0031).</p><div class="language-sh vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">bernard</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> view</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> [--port </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">4321]</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> [--cwd </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&lt;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">projec</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">t</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&gt;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">]</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # the built page, on the project&#39;s telemetry</span></span>
2
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> dev:viewer</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # from the factory&#39;s repo: Vite, on tmp/factory/runs or $BERNARD_RUNS</span></span>
3
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> build</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # the CLI, then this page into dist/viewer/</span></span></code></pre></div><p>The package ships the page in <code>dist/viewer/</code>, which <code>bernard view</code> serves (ADR-0035). From this repo&#39;s source, <code>pnpm bernard view</code> serves the <code>dist/viewer/</code> that <code>pnpm build</code> built.</p><p><code>bernard view</code> also serves the documentation site in <code>dist/docs/</code> under <code>/docs/</code> (ADR-0037). The header&#39;s <em>Documentation</em> button opens it, and the site&#39;s <em>Run viewer</em> button comes back. <code>pnpm dev:viewer</code> does not serve it: <code>pnpm dev:docs</code> does, live.</p><p>The page draws only the kind of diagram the orchestrator draws, <code>MERMAID_DIAGRAM</code>: the build drops Mermaid&#39;s other diagram types, its other layout engines and KaTeX (<code>mermaid-diagram.ts</code>), over 4 MB. Drawing another kind of diagram means allowing it there. The documentation site uses the same plugin.</p><p>Node 24 and pnpm. The repository&#39;s <code>pnpm lint</code> and <code>pnpm typecheck</code> check it with the rest.</p><h2 id="what-it-reads" tabindex="-1">What it reads <a class="header-anchor" href="#what-it-reads" aria-label="Permalink to &quot;What it reads&quot;">​</a></h2><p>It reads, and never writes, through the project&#39;s telemetry:</p><ul><li>each run&#39;s trace, followed live, and once in full for a run another one resumed;</li><li>the pipeline&#39;s XState machines and each actor&#39;s latest snapshot;</li><li>a session&#39;s readable log, followed live, and its raw output, for the subagents it launched.</li></ul><p>Runs that wrote no machines show none.</p><h2 id="resumed-runs" tabindex="-1">Resumed runs <a class="header-anchor" href="#resumed-runs" aria-label="Permalink to &quot;Resumed runs&quot;">​</a></h2><p><code>bernard resume</code> starts a new run, whose trace holds only what happens from then on. The API links it to the run it resumed, the latest earlier run of its feature, and the viewer shows the chain as one: the newest run&#39;s page has every attempt of every run of it, the <em>Reprises</em> card says where each run stopped and what <code>bernard resume</code> sent, and an earlier run&#39;s URL leads to the newest one. The run selector lists the newest run of each chain.</p><h2 id="pages" tabindex="-1">Pages <a class="header-anchor" href="#pages" aria-label="Permalink to &quot;Pages&quot;">​</a></h2><p>Every page is a URL, the drawer included:</p><ul><li><code>/</code>: the runs in flight, each actor&#39;s state and the session it runs now, then the recent runs;</li><li><code>/runs/&lt;run&gt;</code>: the pipeline, the resumes, the environments and the decisions. A step opens its last attempt&#39;s session, as its chip does;</li><li><code>/runs/&lt;run&gt;/sessions</code>: every attempt, newest first;</li><li><code>/runs/&lt;run&gt;/machines?actor=ticket-02</code>: an actor&#39;s machine, its active states highlighted, and its snapshot;</li><li><code>?session=&lt;run&gt;/&lt;where&gt;/&lt;name&gt;</code> on any of them opens a session&#39;s log, with its handoff once the step has ended. The run comes first: each run numbers its sessions anew.</li></ul><h2 id="layout" tabindex="-1">Layout <a class="header-anchor" href="#layout" aria-label="Permalink to &quot;Layout&quot;">​</a></h2><ul><li><code>shared/</code>: what the API returns. The trace&#39;s events are the telemetry&#39;s (<code>packages/telemetry/src/trace.ts</code>), named in <code>src/run/trace.ts</code>.</li><li><code>server/</code>: the API on the telemetry port (<code>api.ts</code>), the runs&#39; summaries and a session&#39;s subagents (<code>runs.ts</code>), a growing stream as server-sent events (<code>stream.ts</code>), and the server of the built page and of the documentation site that <code>bernard view</code> starts (<code>serve.ts</code>).</li><li><code>src/run/</code>: the run&#39;s view, derived from its trace (<code>derive.ts</code>) and followed live (<code>tail.ts</code>, <code>context.tsx</code>), and what it tells the viewer while the page is open: the tab&#39;s title and toasts (<code>notify.ts</code>).</li><li><code>src/routes/</code>: TanStack Router&#39;s file routes; its Vite plugin generates <code>src/routeTree.gen.ts</code>.</li><li><code>src/theme.css</code>: HeroUI&#39;s theme in Bernard&#39;s colors, light and dark; <code>src/theme.ts</code> picks one, the system&#39;s unless the viewer chose.</li><li><code>src/components/</code>: the lanes, chips, drawer, a session&#39;s log, the Mermaid diagram, and Bernard, the factory&#39;s hermit crab, drawn from <code>docs/assets/bernard.svg</code>: he works while a run goes on.</li><li><code>src/index.css</code> and <code>src/motion.ts</code>: the motion, kept light. Only <code>transform</code> and <code>opacity</code> animate; what loops (Bernard at work, a live dot) loops only while something goes on, pauses while the tab is hidden, stops offscreen, and nothing moves under <code>prefers-reduced-motion</code>.</li></ul>`,19)])])}const m=s(n,[["render",o]]);export{u as __pageData,m as default};
@@ -0,0 +1 @@
1
+ import{_ as s,o as t,c as a,a3 as i}from"./chunks/framework.hgjl6W6o.js";const u=JSON.parse('{"title":"Run viewer","description":"","frontmatter":{},"headers":[],"relativePath":"packages/viewer/README.md","filePath":"packages/viewer/README.md"}'),n={name:"packages/viewer/README.md"};function o(r,e,d,h,c,l){return t(),a("div",null,[...e[0]||(e[0]=[i("",19)])])}const m=s(n,[["render",o]]);export{u as __pageData,m as default};