@gajae-code/coding-agent 0.14.2 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (345) hide show
  1. package/CHANGELOG.md +120 -1
  2. package/dist/types/async/job-manager.d.ts +4 -1
  3. package/dist/types/autoresearch/dashboard.d.ts +29 -4
  4. package/dist/types/autoresearch/data-context.d.ts +25 -0
  5. package/dist/types/autoresearch/git.d.ts +65 -7
  6. package/dist/types/autoresearch/harness.d.ts +58 -0
  7. package/dist/types/autoresearch/prompts.d.ts +51 -0
  8. package/dist/types/autoresearch/runs.d.ts +173 -0
  9. package/dist/types/autoresearch/types.d.ts +9 -139
  10. package/dist/types/capability/types.d.ts +20 -0
  11. package/dist/types/cli/accounts-cli.d.ts +1 -0
  12. package/dist/types/commands/{rlm.d.ts → autoresearch.d.ts} +2 -4
  13. package/dist/types/config/autorouting-contract.d.ts +146 -0
  14. package/dist/types/config/autorouting-generator.d.ts +40 -0
  15. package/dist/types/config/autorouting-tier-map.d.ts +16844 -0
  16. package/dist/types/config/autorouting.d.ts +35 -0
  17. package/dist/types/config/model-discovery-manager.d.ts +8 -1
  18. package/dist/types/config/model-registry.d.ts +30 -1
  19. package/dist/types/config/provider-selection-policy.d.ts +23 -0
  20. package/dist/types/config/settings-schema.d.ts +61 -3
  21. package/dist/types/config/settings.d.ts +3 -0
  22. package/dist/types/coordinator/contract.d.ts +1 -1
  23. package/dist/types/coordinator-mcp/durability.d.ts +29 -0
  24. package/dist/types/coordinator-mcp/event-webhook.d.ts +50 -0
  25. package/dist/types/coordinator-mcp/question-state.d.ts +1 -1
  26. package/dist/types/coordinator-mcp/server.d.ts +14 -0
  27. package/dist/types/crash/index-store.d.ts +3 -0
  28. package/dist/types/crash/record-loader.d.ts +2 -0
  29. package/dist/types/crash/upstream/relay.d.ts +5 -3
  30. package/dist/types/cursor.d.ts +12 -0
  31. package/dist/types/defaults/gjc-defaults.d.ts +35 -1
  32. package/dist/types/defaults/gjc-skills.generated.d.ts +1 -1
  33. package/dist/types/discovery/helpers.d.ts +1 -1
  34. package/dist/types/edit/index.d.ts +2 -1
  35. package/dist/types/eval/py/executor.d.ts +2 -0
  36. package/dist/types/extensibility/extensions/loader.d.ts +32 -0
  37. package/dist/types/extensibility/gjc-plugins/types.d.ts +1 -1
  38. package/dist/types/extensibility/skills.d.ts +2 -0
  39. package/dist/types/gjc-runtime/autoresearch-runtime.d.ts +194 -0
  40. package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
  41. package/dist/types/gjc-runtime/psmux-detect.d.ts +1 -1
  42. package/dist/types/gjc-runtime/python-transcript.d.ts +19 -0
  43. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +2 -8
  44. package/dist/types/gjc-runtime/session-layout.d.ts +6 -3
  45. package/dist/types/gjc-runtime/state-renderer.d.ts +0 -22
  46. package/dist/types/gjc-runtime/state-schema.d.ts +7 -7
  47. package/dist/types/gjc-runtime/tmux-common.d.ts +2 -2
  48. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +38 -1
  49. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +2 -24
  50. package/dist/types/gjc-runtime/ultragoal-validation-policy.d.ts +73 -0
  51. package/dist/types/gjc-runtime/workflow-recovery-projection.d.ts +100 -0
  52. package/dist/types/hooks/skill-keywords.d.ts +1 -1
  53. package/dist/types/main.d.ts +0 -10
  54. package/dist/types/modes/acp/acp-agent.d.ts +13 -6
  55. package/dist/types/modes/components/model-selector.d.ts +14 -0
  56. package/dist/types/modes/components/skill-hud/render.d.ts +6 -1
  57. package/dist/types/modes/components/smart-routing-panel.d.ts +82 -0
  58. package/dist/types/modes/controllers/selector-controller.d.ts +14 -0
  59. package/dist/types/modes/interactive-mode.d.ts +3 -0
  60. package/dist/types/modes/types.d.ts +9 -0
  61. package/dist/types/rlm/artifacts.d.ts +4 -4
  62. package/dist/types/runtime/optional-runtime-services.d.ts +2 -1
  63. package/dist/types/runtime/workspace-tree-service.d.ts +5 -1
  64. package/dist/types/runtime-mcp/config.d.ts +6 -0
  65. package/dist/types/runtime-mcp/manager.d.ts +10 -0
  66. package/dist/types/sdk/acp/adapter.d.ts +0 -4
  67. package/dist/types/sdk/broker/broker.d.ts +1 -1
  68. package/dist/types/sdk/broker/lifecycle.d.ts +57 -2
  69. package/dist/types/sdk/broker/session-index.d.ts +15 -0
  70. package/dist/types/sdk/bus/chat-daemon-control.d.ts +6 -0
  71. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -0
  72. package/dist/types/sdk/bus/index.d.ts +8 -0
  73. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +2 -0
  74. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -2
  75. package/dist/types/sdk/client/client.d.ts +39 -4
  76. package/dist/types/sdk/host/host.d.ts +1 -0
  77. package/dist/types/sdk/host/internal-autorouting-state.d.ts +3 -0
  78. package/dist/types/sdk/host/model-pin.d.ts +29 -0
  79. package/dist/types/sdk/host/session-runtime.d.ts +2 -1
  80. package/dist/types/sdk/lifecycle-session.d.ts +13 -0
  81. package/dist/types/sdk/router/session-router.d.ts +27 -1
  82. package/dist/types/sdk/session.d.ts +11 -1
  83. package/dist/types/session/agent-session.d.ts +36 -6
  84. package/dist/types/session/artifacts.d.ts +16 -1
  85. package/dist/types/session/internal/managed-session-storage.d.ts +7 -0
  86. package/dist/types/session/session-manager.d.ts +84 -2
  87. package/dist/types/session/session-staging-paths.d.ts +3 -0
  88. package/dist/types/skill-state/canonical-skills.d.ts +1 -1
  89. package/dist/types/skill-state/workflow-hud.d.ts +12 -21
  90. package/dist/types/skill-state/workflow-mutation-guard.d.ts +1 -0
  91. package/dist/types/slash-commands/helpers/autorouting-status.d.ts +8 -0
  92. package/dist/types/system-prompt.d.ts +23 -0
  93. package/dist/types/task/executor.d.ts +35 -4
  94. package/dist/types/task/index.d.ts +17 -3
  95. package/dist/types/task/receipt.d.ts +3 -1
  96. package/dist/types/task/types.d.ts +144 -0
  97. package/dist/types/tools/atomic-file-write.d.ts +72 -0
  98. package/dist/types/tools/descriptors.d.ts +1 -0
  99. package/dist/types/tools/index.d.ts +36 -1
  100. package/dist/types/tools/move-session.d.ts +40 -0
  101. package/dist/types/tools/python.d.ts +12 -0
  102. package/dist/types/tools/resource-gc.d.ts +0 -3
  103. package/dist/types/tools/search.d.ts +8 -1
  104. package/dist/types/tools/skill.d.ts +9 -4
  105. package/dist/types/tools/subagent.d.ts +33 -3
  106. package/dist/types/utils/xml-escape.d.ts +2 -0
  107. package/dist/types/workflow/workflow-intent-diff.d.ts +2 -2
  108. package/dist/types/workflow/workflow-intent-report-consumer.d.ts +1 -1
  109. package/package.json +11 -12
  110. package/scripts/build-sdk-package-smoke.ts +7 -5
  111. package/scripts/check-autorouting-tier-map.ts +183 -0
  112. package/scripts/generate-sdk-operation-inventory.ts +12 -0
  113. package/scripts/generate-tool-catalog.ts +40 -5
  114. package/src/async/job-manager.ts +5 -2
  115. package/src/autoresearch/dashboard.ts +134 -314
  116. package/src/autoresearch/data-context.ts +35 -0
  117. package/src/autoresearch/git.ts +186 -26
  118. package/src/autoresearch/{helpers.ts → harness.ts} +65 -40
  119. package/src/autoresearch/{prompt-setup.md → prompts/prompt-setup.md} +7 -7
  120. package/src/autoresearch/prompts/prompt.md +103 -0
  121. package/src/autoresearch/prompts.ts +126 -0
  122. package/src/autoresearch/runs.ts +708 -0
  123. package/src/autoresearch/types.ts +9 -150
  124. package/src/capability/index.ts +4 -4
  125. package/src/capability/types.ts +20 -0
  126. package/src/cli/accounts-cli.ts +21 -5
  127. package/src/cli/config-cli.ts +25 -1
  128. package/src/cli/fast-help.ts +1 -2
  129. package/src/cli/list-models.ts +5 -8
  130. package/src/cli/setup-cli.ts +8 -0
  131. package/src/cli.ts +2 -21
  132. package/src/commands/autoresearch.ts +33 -0
  133. package/src/commands/coordinator.ts +15 -0
  134. package/src/commands/launch.ts +2 -4
  135. package/src/commands/quick-lane.ts +1 -1
  136. package/src/commands/sdk.ts +24 -0
  137. package/src/commands/state.ts +1 -1
  138. package/src/config/autorouting-contract.ts +474 -0
  139. package/src/config/autorouting-generator.ts +238 -0
  140. package/src/config/autorouting-tier-map.ts +6108 -0
  141. package/src/config/autorouting.ts +120 -0
  142. package/src/config/model-discovery-manager.ts +42 -11
  143. package/src/config/model-registry.ts +348 -68
  144. package/src/config/provider-selection-policy.ts +60 -9
  145. package/src/config/settings-schema.ts +195 -6
  146. package/src/config/settings.ts +62 -1
  147. package/src/config.ts +2 -3
  148. package/src/coordinator/contract.ts +0 -1
  149. package/src/coordinator-mcp/codex-handoff.ts +68 -42
  150. package/src/coordinator-mcp/durability.ts +200 -0
  151. package/src/coordinator-mcp/event-webhook.ts +278 -0
  152. package/src/coordinator-mcp/question-state.ts +109 -46
  153. package/src/coordinator-mcp/server.ts +932 -504
  154. package/src/crash/index-store.ts +126 -22
  155. package/src/crash/record-loader.ts +7 -0
  156. package/src/crash/upstream/relay.ts +82 -34
  157. package/src/cursor.ts +84 -1
  158. package/src/debug/index.ts +26 -0
  159. package/src/defaults/gjc/skills/autoresearch/SKILL.md +116 -0
  160. package/src/defaults/gjc/skills/autoresearch/auto-critic.md +46 -0
  161. package/src/defaults/gjc/skills/autoresearch/auto-iterate.md +42 -0
  162. package/src/defaults/gjc/skills/deep-interview/SKILL.md +17 -37
  163. package/src/defaults/gjc/skills/ralplan/SKILL.md +21 -18
  164. package/src/defaults/gjc/skills/ultragoal/SKILL.md +6 -17
  165. package/src/defaults/gjc/skills/ultragoal/ai-slop-cleaner.md +2 -2
  166. package/src/defaults/gjc-defaults.ts +89 -2
  167. package/src/defaults/gjc-skills.generated.ts +24 -17
  168. package/src/discovery/builtin.ts +18 -5
  169. package/src/discovery/helpers.ts +6 -6
  170. package/src/edit/index.ts +5 -2
  171. package/src/eval/py/executor.ts +8 -0
  172. package/src/extensibility/extensions/loader.ts +135 -13
  173. package/src/extensibility/extensions/wrapper.ts +1 -2
  174. package/src/extensibility/gjc-plugins/schema.ts +1 -1
  175. package/src/extensibility/runtime-skill-discovery.ts +2 -2
  176. package/src/extensibility/skill-management.ts +2 -3
  177. package/src/extensibility/skills.ts +16 -8
  178. package/src/gjc-runtime/autoresearch-runtime.ts +1509 -0
  179. package/src/gjc-runtime/gc-render.ts +0 -1
  180. package/src/gjc-runtime/gc-runtime.ts +1 -11
  181. package/src/gjc-runtime/launch-tmux.ts +3 -3
  182. package/src/gjc-runtime/psmux-detect.ts +2 -2
  183. package/src/gjc-runtime/python-transcript.ts +58 -0
  184. package/src/gjc-runtime/ralplan-runtime.ts +6 -18
  185. package/src/gjc-runtime/session-layout.ts +18 -7
  186. package/src/gjc-runtime/session-state-sidecar.ts +58 -9
  187. package/src/gjc-runtime/state-renderer.ts +0 -38
  188. package/src/gjc-runtime/state-runtime.ts +43 -22
  189. package/src/gjc-runtime/state-schema.ts +1 -1
  190. package/src/gjc-runtime/tmux-common.ts +7 -7
  191. package/src/gjc-runtime/ultragoal-change-set.ts +232 -11
  192. package/src/gjc-runtime/ultragoal-runtime.ts +309 -114
  193. package/src/gjc-runtime/ultragoal-validation-policy.ts +247 -0
  194. package/src/gjc-runtime/workflow-command-ref.ts +15 -11
  195. package/src/gjc-runtime/workflow-manifest.generated.json +413 -532
  196. package/src/gjc-runtime/workflow-manifest.ts +43 -83
  197. package/src/gjc-runtime/workflow-recovery-projection.ts +616 -0
  198. package/src/hooks/skill-keywords.ts +3 -3
  199. package/src/hooks/skill-state.ts +2 -2
  200. package/src/internal-urls/docs-index.generated.ts +26 -24
  201. package/src/lsp/index.ts +25 -5
  202. package/src/main.ts +2 -27
  203. package/src/modes/acp/acp-agent.ts +200 -75
  204. package/src/modes/components/model-selector.ts +216 -13
  205. package/src/modes/components/oauth-selector.ts +5 -5
  206. package/src/modes/components/skill-hud/render.ts +173 -35
  207. package/src/modes/components/smart-routing-panel.ts +531 -0
  208. package/src/modes/components/tool-status-header.ts +6 -3
  209. package/src/modes/controllers/command-controller.ts +17 -7
  210. package/src/modes/controllers/input-controller.ts +44 -9
  211. package/src/modes/controllers/runtime-mcp-command-controller.ts +4 -0
  212. package/src/modes/controllers/selector-controller.ts +298 -65
  213. package/src/modes/interactive-mode.ts +8 -1
  214. package/src/modes/shared/agent-wire/workflow-gate-types.ts +1 -1
  215. package/src/modes/types.ts +9 -1
  216. package/src/prompts/agents/planner.md +1 -1
  217. package/src/prompts/system/system-prompt.md +1 -1
  218. package/src/prompts/system/volatile-project-context.md +2 -1
  219. package/src/prompts/tools/move-session.md +15 -0
  220. package/src/prompts/tools/python.md +12 -0
  221. package/src/prompts/tools/skill-discovery.md +1 -1
  222. package/src/prompts/tools/skill.md +2 -2
  223. package/src/prompts/tools/task-summary.md +1 -0
  224. package/src/prompts/tools/task.md +6 -0
  225. package/src/rlm/artifacts.ts +12 -10
  226. package/src/runtime/optional-runtime-services.ts +3 -2
  227. package/src/runtime/workspace-tree-service.ts +11 -3
  228. package/src/runtime-mcp/config.ts +8 -1
  229. package/src/runtime-mcp/manager.ts +33 -0
  230. package/src/sdk/acp/adapter.ts +48 -1
  231. package/src/sdk/broker/broker.ts +30 -2
  232. package/src/sdk/broker/discovery.ts +217 -2
  233. package/src/sdk/broker/lifecycle.ts +385 -59
  234. package/src/sdk/broker/session-index.ts +243 -48
  235. package/src/sdk/broker/transport.ts +1 -0
  236. package/src/sdk/bus/chat-daemon-control.ts +8 -2
  237. package/src/sdk/bus/chat-daemon-runtime.ts +5 -0
  238. package/src/sdk/bus/index.ts +208 -11
  239. package/src/sdk/bus/kind-aware-reconciliation.ts +16 -0
  240. package/src/sdk/bus/reconciliation-store.ts +27 -5
  241. package/src/sdk/bus/telegram-daemon-contract.ts +12 -2
  242. package/src/sdk/client/client.ts +204 -22
  243. package/src/sdk/host/host.ts +16 -0
  244. package/src/sdk/host/internal-autorouting-state.ts +13 -0
  245. package/src/sdk/host/model-pin.ts +101 -0
  246. package/src/sdk/host/reverse-leases.ts +2 -1
  247. package/src/sdk/host/session-runtime.test.ts +224 -1
  248. package/src/sdk/host/session-runtime.ts +125 -37
  249. package/src/sdk/lifecycle/service.ts +3 -1
  250. package/src/sdk/lifecycle-session.ts +42 -1
  251. package/src/sdk/protocol/operation-inventory.generated.json +66 -0
  252. package/src/sdk/router/session-router.ts +375 -36
  253. package/src/sdk/session.ts +547 -47
  254. package/src/session/account-inventory.ts +1 -1
  255. package/src/session/agent-session.ts +765 -189
  256. package/src/session/artifacts.ts +234 -4
  257. package/src/session/internal/managed-session-storage.ts +91 -4
  258. package/src/session/provider-safety-stop-hint.ts +3 -2
  259. package/src/session/session-manager.ts +775 -16
  260. package/src/session/session-staging-paths.ts +8 -0
  261. package/src/setup/hermes/templates/operator-instructions.v1.md +0 -1
  262. package/src/setup/host-plugin-setup.ts +2 -2
  263. package/src/skill-state/active-state.ts +2 -2
  264. package/src/skill-state/canonical-skills.ts +1 -1
  265. package/src/skill-state/initial-phase.ts +1 -1
  266. package/src/skill-state/workflow-hud.ts +41 -41
  267. package/src/skill-state/workflow-mutation-guard.ts +26 -7
  268. package/src/slash-commands/builtin-registry.ts +77 -0
  269. package/src/slash-commands/helpers/autorouting-status.ts +55 -0
  270. package/src/system-prompt.ts +102 -5
  271. package/src/task/discovery.ts +2 -3
  272. package/src/task/executor.ts +617 -43
  273. package/src/task/index.ts +262 -15
  274. package/src/task/receipt.ts +30 -0
  275. package/src/task/render.ts +11 -1
  276. package/src/task/types.ts +316 -0
  277. package/src/tools/atomic-file-write.ts +594 -0
  278. package/src/tools/bisect.ts +3 -2
  279. package/src/tools/descriptors.test.ts +3 -0
  280. package/src/tools/descriptors.ts +24 -0
  281. package/src/tools/image-gen.ts +5 -1
  282. package/src/tools/index.ts +62 -5
  283. package/src/tools/move-session.ts +107 -0
  284. package/src/tools/python.ts +205 -0
  285. package/src/tools/read.ts +88 -15
  286. package/src/tools/renderers.ts +2 -0
  287. package/src/tools/resource-gc.ts +8 -177
  288. package/src/tools/search.ts +30 -6
  289. package/src/tools/skill-discovery.ts +1 -0
  290. package/src/tools/skill.ts +37 -7
  291. package/src/tools/subagent-render.ts +119 -13
  292. package/src/tools/subagent.ts +91 -74
  293. package/src/tools/tool-catalog.generated.ts +64 -2
  294. package/src/tools/write.ts +16 -12
  295. package/src/utils/xml-escape.ts +14 -0
  296. package/src/web/search/providers/codex.ts +2 -2
  297. package/src/workflow/workflow-intent-diff.ts +13 -12
  298. package/src/workflow/workflow-intent-report-consumer.ts +6 -6
  299. package/dist/types/autoresearch/helpers.d.ts +0 -24
  300. package/dist/types/autoresearch/index.d.ts +0 -2
  301. package/dist/types/autoresearch/state.d.ts +0 -17
  302. package/dist/types/autoresearch/storage.d.ts +0 -142
  303. package/dist/types/autoresearch/tools/init-experiment.d.ts +0 -31
  304. package/dist/types/autoresearch/tools/log-experiment.d.ts +0 -23
  305. package/dist/types/autoresearch/tools/run-experiment.d.ts +0 -8
  306. package/dist/types/autoresearch/tools/update-notes.d.ts +0 -12
  307. package/dist/types/commands/team.d.ts +0 -24
  308. package/dist/types/gjc-runtime/team-gc.d.ts +0 -7
  309. package/dist/types/gjc-runtime/team-launch.d.ts +0 -61
  310. package/dist/types/gjc-runtime/team-notify.d.ts +0 -45
  311. package/dist/types/gjc-runtime/team-runtime.d.ts +0 -336
  312. package/dist/types/gjc-runtime/team-store.d.ts +0 -168
  313. package/dist/types/gjc-runtime/team-worker-heartbeat.d.ts +0 -43
  314. package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +0 -92
  315. package/dist/types/gjc-runtime/team-workers.d.ts +0 -83
  316. package/dist/types/rlm/index.d.ts +0 -47
  317. package/dist/types/rlm/preset.d.ts +0 -23
  318. package/dist/types/rlm/python-tool.d.ts +0 -16
  319. package/src/autoresearch/command-resume.md +0 -14
  320. package/src/autoresearch/index.ts +0 -536
  321. package/src/autoresearch/prompt.md +0 -103
  322. package/src/autoresearch/resume-message.md +0 -10
  323. package/src/autoresearch/state.ts +0 -273
  324. package/src/autoresearch/storage.ts +0 -699
  325. package/src/autoresearch/tools/init-experiment.ts +0 -272
  326. package/src/autoresearch/tools/log-experiment.ts +0 -524
  327. package/src/autoresearch/tools/run-experiment.ts +0 -475
  328. package/src/autoresearch/tools/update-notes.ts +0 -109
  329. package/src/commands/rlm.ts +0 -19
  330. package/src/commands/team.ts +0 -238
  331. package/src/defaults/gjc/skills/deep-interview/auto-research-greenfield.md +0 -42
  332. package/src/defaults/gjc/skills/team/SKILL.md +0 -467
  333. package/src/gjc-runtime/team-gc.ts +0 -49
  334. package/src/gjc-runtime/team-launch.ts +0 -404
  335. package/src/gjc-runtime/team-notify.ts +0 -326
  336. package/src/gjc-runtime/team-runtime.ts +0 -5923
  337. package/src/gjc-runtime/team-store.ts +0 -878
  338. package/src/gjc-runtime/team-worker-heartbeat.ts +0 -149
  339. package/src/gjc-runtime/team-worker-memory-guard.ts +0 -363
  340. package/src/gjc-runtime/team-workers.ts +0 -1123
  341. package/src/prompts/system/rlm-report-command.md +0 -1
  342. package/src/prompts/system/rlm-research.md +0 -23
  343. package/src/rlm/index.ts +0 -365
  344. package/src/rlm/preset.ts +0 -76
  345. package/src/rlm/python-tool.ts +0 -68
@@ -1,8 +1,22 @@
1
- import type { ExtensionAPI } from "../extensibility/extensions";
1
+ /**
2
+ * Autoresearch branch isolation (ported from the deleted extension's `git.ts`).
3
+ *
4
+ * Every mission is meant to run on a dedicated `autoresearch/*` branch created
5
+ * from a slugified goal. On that branch `keep` auto-commits the iteration and
6
+ * `discard` resets the worktree to HEAD; off-branch (no repo, or a dirty tree
7
+ * that prevents branch creation) we degrade: `keep` skips auto-commits and
8
+ * `discard` reverts only run-modified paths instead of resetting to baseline.
9
+ *
10
+ * Dirty-worktree behavior is faithful to the extension contract: when the tree
11
+ * is dirty and we are NOT already on an autoresearch branch, we return a
12
+ * warning and continue on the current branch rather than failing.
13
+ */
14
+ import * as fs from "node:fs";
15
+ import * as path from "node:path";
2
16
  import * as git from "../utils/git";
3
- import { normalizePathSpec } from "./helpers";
17
+ import { HARNESS_FILENAME, normalizePathSpec } from "./harness";
4
18
 
5
- const AUTORESEARCH_BRANCH_PREFIX = "autoresearch/";
19
+ export const AUTORESEARCH_BRANCH_PREFIX = "autoresearch/";
6
20
  const BRANCH_NAME_MAX_LENGTH = 48;
7
21
 
8
22
  export interface EnsureAutoresearchBranchFailure {
@@ -19,21 +33,38 @@ export interface EnsureAutoresearchBranchSuccess {
19
33
 
20
34
  export type EnsureAutoresearchBranchResult = EnsureAutoresearchBranchFailure | EnsureAutoresearchBranchSuccess;
21
35
 
22
- export async function getCurrentAutoresearchBranch(_api: ExtensionAPI, workDir: string): Promise<string | null> {
23
- const currentBranch = (await git.branch.current(workDir)) ?? "";
24
- return currentBranch.startsWith(AUTORESEARCH_BRANCH_PREFIX) ? currentBranch : null;
36
+ export function getCurrentAutoresearchBranch(workDir: string): Promise<string | null> {
37
+ return git.branch.current(workDir).then(currentBranch => {
38
+ return currentBranch?.startsWith(AUTORESEARCH_BRANCH_PREFIX) ? currentBranch : null;
39
+ });
40
+ }
41
+
42
+ /**
43
+ * True when `rawPath` is one of the mission's own research artifacts: the
44
+ * `autoresearch.sh` harness at the working-directory root. This is the complete
45
+ * agent-writable surface for an active mission — product code, manifests,
46
+ * dependencies, and every other path stay blocked by the research-only
47
+ * mutation guard regardless of branch name.
48
+ *
49
+ * Mission state under `.gjc/**` is intentionally NOT listed here: it is
50
+ * runtime-owned and only the sanctioned `gjc autoresearch` CLI writes it.
51
+ */
52
+ export function isAutoresearchAuthorizedResearchPath(_cwd: string, rawPath: string): boolean {
53
+ const normalized = normalizePathSpec(rawPath);
54
+ if (normalized === ".") return false;
55
+ return normalized === HARNESS_FILENAME;
25
56
  }
26
57
 
27
58
  /**
28
59
  * Ensure the working tree is on an `autoresearch/*` branch when possible.
29
60
  *
30
- * If the worktree is dirty and we're not already on an autoresearch branch, this returns
31
- * `{ ok: true, branchName: null, warning }` rather than failing. The caller surfaces the
32
- * warning and continues on the current branch — `keep` will skip auto-commits and `discard`
33
- * will revert only run-modified paths instead of resetting to baseline.
61
+ * When the worktree is dirty and we are not already on an autoresearch branch,
62
+ * this returns `{ ok: true, branchName: null, warning }` rather than failing:
63
+ * the caller surfaces the warning and continues on the current branch — `keep`
64
+ * will skip auto-commits and `discard` will revert only run-modified paths
65
+ * instead of resetting to baseline.
34
66
  */
35
67
  export async function ensureAutoresearchBranch(
36
- api: ExtensionAPI,
37
68
  workDir: string,
38
69
  goal: string | null,
39
70
  ): Promise<EnsureAutoresearchBranchResult> {
@@ -58,21 +89,27 @@ export async function ensureAutoresearchBranch(
58
89
  };
59
90
  }
60
91
 
61
- const workDirPrefix = await readGitWorkDirPrefix(api, workDir);
92
+ const workDirPrefix = await readGitWorkDirPrefix(workDir);
62
93
  const dirtyPaths = collectRelativeDirtyPaths(dirtyPathsOutput, workDirPrefix);
63
- const currentBranch = await getCurrentAutoresearchBranch(api, workDir);
94
+ const currentBranch = await getCurrentAutoresearchBranch(workDir);
64
95
  if (currentBranch) {
65
96
  return { ok: true, branchName: currentBranch, created: false };
66
97
  }
67
98
  if (dirtyPaths.length > 0) {
68
99
  const preview = formatDirtyPaths(dirtyPaths);
69
100
  return {
70
- ok: false,
71
- error: `Worktree is dirty (${preview}). Commit or stash these changes before starting autoresearch — a fresh autoresearch/* branch needs a clean baseline.`,
101
+ ok: true,
102
+ branchName: null,
103
+ created: false,
104
+ warning:
105
+ `Worktree is dirty (${preview}). Continuing autoresearch on the current branch without a dedicated ` +
106
+ "`autoresearch/*` branch: `keep` will skip auto-commits and `discard` will revert only run-modified " +
107
+ "files instead of resetting to baseline. Commit or stash these changes and re-run to get full branch " +
108
+ "isolation.",
72
109
  };
73
110
  }
74
111
 
75
- const branchName = await allocateBranchName(api, workDir, goal);
112
+ const branchName = await allocateBranchName(workDir, goal);
76
113
  try {
77
114
  await git.branch.checkoutNew(workDir, branchName);
78
115
  } catch (err) {
@@ -109,8 +146,7 @@ export function relativizeGitPathToWorkDir(repoRelativePath: string, workDirPref
109
146
  return normalizePathSpec(normalizedPath.slice(normalizedPrefix.length + 1));
110
147
  }
111
148
 
112
- async function readGitWorkDirPrefix(api: ExtensionAPI, workDir: string): Promise<string> {
113
- void api;
149
+ async function readGitWorkDirPrefix(workDir: string): Promise<string> {
114
150
  try {
115
151
  return await git.show.prefix(workDir);
116
152
  } catch {
@@ -170,23 +206,18 @@ export function normalizeStatusPath(rawPath: string): string {
170
206
  return normalizePathSpec(normalized);
171
207
  }
172
208
 
173
- async function allocateBranchName(api: ExtensionAPI, workDir: string, goal: string | null): Promise<string> {
209
+ async function allocateBranchName(workDir: string, goal: string | null): Promise<string> {
174
210
  const baseName = `${AUTORESEARCH_BRANCH_PREFIX}${slugifyGoal(goal)}-${currentDateStamp()}`;
175
211
  let candidate = baseName;
176
212
  let suffix = 2;
177
- while (await branchExists(api, workDir, candidate)) {
213
+ while (await git.ref.exists(workDir, `refs/heads/${candidate}`)) {
178
214
  candidate = `${baseName}-${suffix}`;
179
215
  suffix += 1;
180
216
  }
181
217
  return candidate;
182
218
  }
183
219
 
184
- async function branchExists(api: ExtensionAPI, workDir: string, branchName: string): Promise<boolean> {
185
- void api;
186
- return git.ref.exists(workDir, `refs/heads/${branchName}`);
187
- }
188
-
189
- function slugifyGoal(goal: string | null): string {
220
+ export function slugifyGoal(goal: string | null): string {
190
221
  const normalized = (goal ?? "")
191
222
  .toLowerCase()
192
223
  .replace(/[^a-z0-9]+/g, "-")
@@ -317,3 +348,132 @@ export function computeRunModifiedPaths(
317
348
  }
318
349
  return { tracked, untracked };
319
350
  }
351
+
352
+ // ---------------------------------------------------------------------------
353
+ // keep / discard
354
+ // ---------------------------------------------------------------------------
355
+
356
+ export interface KeepAutoresearchRunInput {
357
+ cwd: string;
358
+ description: string;
359
+ status: string;
360
+ metric: number;
361
+ metrics: { [key: string]: number };
362
+ /** Paths modified by this run (workdir-relative). */
363
+ files: string[];
364
+ /** True when on a dedicated `autoresearch/*` branch. */
365
+ onAutoresearchBranch: boolean;
366
+ primaryMetric: string;
367
+ }
368
+
369
+ export interface KeepAutoresearchRunResult {
370
+ /** Error text when the commit failed; `ok` is false. */
371
+ error?: string;
372
+ /** Human note for the caller (e.g. "nothing to commit", "committed", "skipped"). */
373
+ note?: string;
374
+ /** Commit SHA after the keep (null when nothing was committed). */
375
+ commitHash?: string | null;
376
+ }
377
+
378
+ /**
379
+ * Keep an iteration: commit the modified files on a dedicated autoresearch
380
+ * branch. Off-branch (degraded mode) auto-commit is skipped and the files stay
381
+ * in the worktree, matching the extension contract.
382
+ */
383
+ export async function keepAutoresearchRun(input: KeepAutoresearchRunInput): Promise<KeepAutoresearchRunResult> {
384
+ if (!input.onAutoresearchBranch) {
385
+ return {
386
+ note: "Auto-commit skipped: not on a dedicated autoresearch branch. Modified files remain in the worktree.",
387
+ commitHash: null,
388
+ };
389
+ }
390
+ if (input.files.length === 0) {
391
+ return { note: "nothing to commit", commitHash: null };
392
+ }
393
+ try {
394
+ await git.stage.files(input.cwd, input.files);
395
+ } catch (err) {
396
+ return { error: `git add failed: ${err instanceof Error ? err.message : String(err)}` };
397
+ }
398
+ if (!(await git.diff.has(input.cwd, { cached: true, files: input.files }))) {
399
+ return { note: "nothing to commit", commitHash: null };
400
+ }
401
+ const payload: { [key: string]: string | number } = {
402
+ status: input.status,
403
+ [input.primaryMetric]: input.metric,
404
+ };
405
+ for (const [name, value] of Object.entries(input.metrics)) {
406
+ payload[name] = value;
407
+ }
408
+ const commitMessage = `${input.description}\n\nResult: ${JSON.stringify(payload)}`;
409
+ try {
410
+ await git.commit(input.cwd, commitMessage, { files: input.files });
411
+ const commitHash = await git.head.sha(input.cwd);
412
+ return { note: `committed at ${(commitHash ?? "").slice(0, 12)}`, commitHash };
413
+ } catch (err) {
414
+ return { error: `git commit failed: ${err instanceof Error ? err.message : String(err)}` };
415
+ }
416
+ }
417
+
418
+ export interface DiscardAutoresearchRunInput {
419
+ cwd: string;
420
+ /** Paths that were already dirty before the run started (workdir-relative). */
421
+ preRunDirtyPaths: string[];
422
+ /** True when on a dedicated `autoresearch/*` branch. */
423
+ onAutoresearchBranch: boolean;
424
+ }
425
+
426
+ export interface DiscardAutoresearchRunResult {
427
+ error?: string;
428
+ note?: string;
429
+ }
430
+
431
+ /**
432
+ * Discard a failed/crashed iteration. On a dedicated autoresearch branch the
433
+ * worktree resets to HEAD (never rewinding prior `keep` commits); off-branch
434
+ * only run-modified paths are reverted so pre-existing user dirt survives.
435
+ */
436
+ export async function discardAutoresearchRun(
437
+ input: DiscardAutoresearchRunInput,
438
+ ): Promise<DiscardAutoresearchRunResult> {
439
+ if (input.onAutoresearchBranch) {
440
+ try {
441
+ await git.reset(input.cwd, { hard: true, target: "HEAD" });
442
+ await git.clean(input.cwd);
443
+ return { note: "worktree reset to HEAD" };
444
+ } catch (err) {
445
+ return { error: `git reset/clean failed: ${err instanceof Error ? err.message : String(err)}` };
446
+ }
447
+ }
448
+
449
+ let statusText: string;
450
+ try {
451
+ statusText = await git.status(input.cwd, { porcelainV1: true, untrackedFiles: "all", z: true });
452
+ } catch (err) {
453
+ return { error: `git status failed: ${err instanceof Error ? err.message : String(err)}` };
454
+ }
455
+ let workDirPrefix: string;
456
+ try {
457
+ workDirPrefix = await git.show.prefix(input.cwd);
458
+ } catch {
459
+ workDirPrefix = "";
460
+ }
461
+ const { tracked, untracked } = computeRunModifiedPaths(input.preRunDirtyPaths, statusText, workDirPrefix);
462
+ const total = tracked.length + untracked.length;
463
+ if (total === 0) return { note: "nothing to revert" };
464
+ if (tracked.length > 0) {
465
+ try {
466
+ await git.restore(input.cwd, { files: tracked, source: "HEAD", staged: true, worktree: true });
467
+ } catch (err) {
468
+ return { error: `git restore failed: ${err instanceof Error ? err.message : String(err)}` };
469
+ }
470
+ }
471
+ for (const filePath of untracked) {
472
+ try {
473
+ fs.rmSync(path.join(input.cwd, filePath), { force: true, recursive: true });
474
+ } catch {
475
+ // best effort
476
+ }
477
+ }
478
+ return { note: `reverted ${total} file${total === 1 ? "" : "s"}` };
479
+ }
@@ -1,6 +1,24 @@
1
- import * as git from "../utils/git";
1
+ /**
2
+ * Autoresearch harness contract: the `autoresearch.sh` entrypoint contract plus
3
+ * parsing of `METRIC <name>=<value>` and `ASI <key>=<value>` lines out of
4
+ * captured output.
5
+ *
6
+ * The contract (ported from the deleted extension's `helpers.ts`):
7
+ * - the harness is `./autoresearch.sh` at the working directory;
8
+ * - it exits 0 on success and non-zero on failure;
9
+ * - it prints the primary metric as `METRIC <name>=<value>` and any secondary
10
+ * metrics as additional `METRIC` lines;
11
+ * - it may print `ASI <key>=<value>` lines for opaque structured learnings.
12
+ *
13
+ * Parsing tolerates interleaved noise: only well-formed lines are recognized,
14
+ * malformed lines are ignored (never thrown), and prototype-polluting key
15
+ * names are dropped.
16
+ */
2
17
  import type { ASIData, ASIValue, MetricDirection, NumericMetricMap } from "./types";
3
18
 
19
+ export const HARNESS_FILENAME = "autoresearch.sh";
20
+ export const DEFAULT_HARNESS_COMMAND = `bash ${HARNESS_FILENAME}`;
21
+
4
22
  export const METRIC_LINE_PREFIX = "METRIC";
5
23
  export const ASI_LINE_PREFIX = "ASI";
6
24
  export const EXPERIMENT_MAX_LINES = 10;
@@ -8,33 +26,36 @@ export const EXPERIMENT_MAX_BYTES = 4 * 1024;
8
26
 
9
27
  const DENIED_KEY_NAMES = new Set(["__proto__", "constructor", "prototype"]);
10
28
 
29
+ const METRIC_LINE_RE = new RegExp(`^${METRIC_LINE_PREFIX}\\s+([\\w.µ-]+)=(\\S+)\\s*$`, "gm");
30
+ const ASI_LINE_RE = new RegExp(`^${ASI_LINE_PREFIX}\\s+([\\w.-]+)=(.+)\\s*$`, "gm");
31
+
32
+ /** All `METRIC name=value` lines found anywhere in the captured output. */
11
33
  export function parseMetricLines(output: string): Map<string, number> {
12
34
  const metrics = new Map<string, number>();
13
- const regex = new RegExp(`^${METRIC_LINE_PREFIX}\\s+([\\w.µ-]+)=(\\S+)\\s*$`, "gm");
14
- let match = regex.exec(output);
35
+ let match = METRIC_LINE_RE.exec(output);
15
36
  while (match !== null) {
16
- const name = match[1];
37
+ const name = match[1] ?? "";
17
38
  if (!DENIED_KEY_NAMES.has(name)) {
18
39
  const value = Number(match[2]);
19
40
  if (Number.isFinite(value)) {
20
41
  metrics.set(name, value);
21
42
  }
22
43
  }
23
- match = regex.exec(output);
44
+ match = METRIC_LINE_RE.exec(output);
24
45
  }
25
46
  return metrics;
26
47
  }
27
48
 
49
+ /** All `ASI key=value` lines found anywhere in the captured output, or null when none parse. */
28
50
  export function parseAsiLines(output: string): ASIData | null {
29
51
  const asi: ASIData = {};
30
- const regex = new RegExp(`^${ASI_LINE_PREFIX}\\s+([\\w.-]+)=(.+)\\s*$`, "gm");
31
- let match = regex.exec(output);
52
+ let match = ASI_LINE_RE.exec(output);
32
53
  while (match !== null) {
33
- const key = match[1];
54
+ const key = match[1] ?? "";
34
55
  if (!DENIED_KEY_NAMES.has(key)) {
35
- asi[key] = parseAsiValue(match[2]);
56
+ asi[key] = parseAsiValue(match[2] ?? "");
36
57
  }
37
- match = regex.exec(output);
58
+ match = ASI_LINE_RE.exec(output);
38
59
  }
39
60
  return Object.keys(asi).length > 0 ? asi : null;
40
61
  }
@@ -50,8 +71,7 @@ function parseAsiValue(raw: string): ASIValue {
50
71
  }
51
72
  if (value.startsWith("{") || value.startsWith("[") || value.startsWith('"')) {
52
73
  try {
53
- const parsed = JSON.parse(value) as ASIValue;
54
- return parsed;
74
+ return JSON.parse(value) as ASIValue;
55
75
  } catch {
56
76
  return value;
57
77
  }
@@ -59,6 +79,38 @@ function parseAsiValue(raw: string): ASIValue {
59
79
  return value;
60
80
  }
61
81
 
82
+ export interface ParsedHarnessOutput {
83
+ /** Every parsed metric, keyed by name. */
84
+ metrics: NumericMetricMap;
85
+ /**
86
+ * The surfaced primary metric: `metrics[primaryMetricName]` when a name is
87
+ * supplied, otherwise the value of the first `METRIC` line in the output.
88
+ */
89
+ primary: number | null;
90
+ /** Parsed ASI learnings, or null when no ASI line parsed. */
91
+ asi: ASIData | null;
92
+ }
93
+
94
+ /**
95
+ * Parse captured harness output into the primary metric, secondary values, and
96
+ * ASI learnings. Malformed or noisy lines are ignored, never thrown.
97
+ */
98
+ export function parseHarnessOutput(output: string, primaryMetricName?: string): ParsedHarnessOutput {
99
+ const metricMap = parseMetricLines(output);
100
+ const metrics: NumericMetricMap = {};
101
+ for (const [name, value] of metricMap.entries()) {
102
+ metrics[name] = value;
103
+ }
104
+ let primary: number | null = null;
105
+ if (primaryMetricName !== undefined) {
106
+ primary = metricMap.get(primaryMetricName) ?? null;
107
+ } else {
108
+ const first = metricMap.entries().next();
109
+ primary = first.done ? null : first.value[1];
110
+ }
111
+ return { metrics, primary, asi: parseAsiLines(output) };
112
+ }
113
+
62
114
  export function mergeAsi(base: ASIData | null, override: ASIData | undefined): ASIData | undefined {
63
115
  if (!base && !override) return undefined;
64
116
  return {
@@ -101,18 +153,7 @@ export function formatElapsed(milliseconds: number): string {
101
153
  return `${seconds}s`;
102
154
  }
103
155
 
104
- export function killTree(pid: number, signal: NodeJS.Signals | number = "SIGTERM"): void {
105
- try {
106
- process.kill(-pid, signal);
107
- } catch {
108
- try {
109
- process.kill(pid, signal);
110
- } catch {
111
- // Process already exited.
112
- }
113
- }
114
- }
115
-
156
+ /** True when `current` beats `best` under the given direction. */
116
157
  export function isBetter(current: number, best: number, direction: MetricDirection): boolean {
117
158
  return direction === "lower" ? current < best : current > best;
118
159
  }
@@ -200,19 +241,3 @@ function sanitizeAsiValue(value: unknown): ASIValue | undefined {
200
241
  }
201
242
  return undefined;
202
243
  }
203
-
204
- export async function tryGitStatus(cwd: string): Promise<string> {
205
- try {
206
- return await git.status(cwd, { porcelainV1: true, untrackedFiles: "all", z: true });
207
- } catch {
208
- return "";
209
- }
210
- }
211
-
212
- export async function tryGitPrefix(cwd: string): Promise<string> {
213
- try {
214
- return await git.show.prefix(cwd);
215
- } catch {
216
- return "";
217
- }
218
- }
@@ -2,13 +2,13 @@
2
2
 
3
3
  ## Autoresearch Mode — Phase 1: Harness Setup
4
4
 
5
- Autoresearch mode is active and there is no session yet. Your job in this turn is to **build the benchmark harness**, not to optimise anything. Optimisation starts only after you call `init_experiment`.
5
+ Autoresearch mode is active and there is no mission yet. Your job in this turn is to **build the benchmark harness**, not to optimise anything. Optimisation starts only after the mission is written.
6
6
 
7
7
  {{#if has_goal}}
8
8
  Primary goal (for context — implement the harness so it can measure this):
9
9
  {{goal}}
10
10
  {{else}}
11
- There is no goal recorded yet. Infer what to optimise from the latest user message and design the harness to measure that. Capture the goal when you call `init_experiment`.
11
+ There is no goal recorded yet. Infer what to optimise from the latest user message and design the harness to measure that. Capture the goal when you write the mission.
12
12
  {{/if}}
13
13
 
14
14
  Working directory: `{{working_dir}}`
@@ -27,17 +27,17 @@ Write `./autoresearch.sh` at the working directory. It is the canonical benchmar
27
27
  - print any secondary metrics as additional `METRIC <name>=<value>` lines;
28
28
  - run the same workload deterministically every time (no live network, no time-of-day dependencies, fixed seeds where applicable).
29
29
 
30
- You **may** edit anything else needed to make `autoresearch.sh` work — benchmark binaries, `Cargo.toml`, `package.json`, helper scripts, fixtures. All those edits are part of the harness baseline and will be committed for you when you call `init_experiment` on an autoresearch branch.
30
+ Do not edit product source, manifests, or dependencies. The harness must use existing checked-in commands and fixtures, or report that the mission requires the normal approval-gated implementation path before benchmarking can proceed.
31
31
 
32
32
  ### Steps
33
33
 
34
34
  1. Inspect the target. Read source, identify what to measure, decide on the workload.
35
- 2. Write `autoresearch.sh` plus any supporting files (benchmark binaries, fixtures, etc.).
35
+ 2. Write `autoresearch.sh` only when it is an explicitly approved research artifact; otherwise use an existing benchmark command.
36
36
  3. Validate it: invoke `bash autoresearch.sh` through the regular `bash` tool. Confirm it exits 0 and emits at least one `METRIC` line. Iterate on the harness until it does.
37
- 4. Call `init_experiment` with the goal, primary metric (matching the `METRIC` name), and scope. This snapshots the worktree as the baseline and starts Phase 2 (the iteration loop).
37
+ 4. Start the mission with `gjc autoresearch` (cold intake: goal, constraints, deliverables, and an explicit mode) or `gjc autoresearch --spec <spec>` (handoff intake). Record the primary metric name and its direction (lower/higher is better) with the mission. The harness baseline is snapshotted at mission start and Phase 2 (the iteration loop) begins.
38
38
 
39
39
  ### Rules
40
40
 
41
- - Do **not** call `run_experiment`, `log_experiment`, or `update_notes` yet. They will error with "no active autoresearch session" until `init_experiment` runs.
41
+ - Do **not** start the iteration loop yet. The experiment machinery is unavailable until a mission exists.
42
42
  - Do **not** treat a compile-only check as a benchmark. The harness must actually execute the workload and emit `METRIC`.
43
- - Do **not** create `autoresearch.md`, `autoresearch.checks.sh`, `autoresearch.program.md`, `autoresearch.ideas.md`, `autoresearch.jsonl`, `.autoresearch/`, or `autoresearch.config.json`. Session state is tracked for you.
43
+ - Do **not** create `autoresearch.md`, `autoresearch.checks.sh`, `autoresearch.program.md`, `autoresearch.ideas.md`, `autoresearch.jsonl`, `.autoresearch/`, or `autoresearch.config.json`. Mission and run state is tracked for you under `.gjc/_session-{id}/autoresearch/`.
@@ -0,0 +1,103 @@
1
+ {{base_system_prompt}}
2
+
3
+ ## Autoresearch Mode
4
+
5
+ Autoresearch mode is active.
6
+
7
+ {{#if has_goal}}
8
+ Primary goal:
9
+ {{goal}}
10
+ {{else}}
11
+ There is no goal recorded for this mission yet. Infer what to optimize from the latest user message and the conversation; capture the goal in your notes once it is clear.
12
+ {{/if}}
13
+
14
+ Mission state and run artifacts are managed for you under `.gjc/_session-{id}/autoresearch/`. The benchmark entrypoint is `bash autoresearch.sh` (committed during Phase 1). Do not edit `autoresearch.sh` mid-segment unless you intentionally start a fresh baseline. Do not create `autoresearch.md` or `.autoresearch/` in this repo.
15
+
16
+ Working directory: `{{working_dir}}`
17
+ {{#if has_branch}}Active branch: `{{branch}}`{{/if}}
18
+ {{#if has_baseline_commit}}Baseline commit: `{{baseline_commit}}`{{/if}}
19
+
20
+ You are running a research-only evidence loop. Do not modify product code, manifests, dependencies, or benchmark harnesses. Stop with a caveat when evidence requires an implementation change.
21
+
22
+ ### Available tools
23
+
24
+ - `python` — a persistent session REPL. Variables, imports, and loaded data survive across calls.
25
+ - `bash` — run an existing benchmark command. Output is captured automatically; `METRIC name=value` and `ASI key=value` lines are parsed back to you, with any other output treated as noise.
26
+ - Run ledger — record each experiment result and flag suspect runs; flagged runs are excluded from baseline and best-metric math.
27
+
28
+ ### Operating protocol
29
+
30
+ 1. Understand the target without modifying it: read source, identify the bottleneck, verify prerequisites and benchmark inputs.
31
+ 2. Record the goal, scope, and constraints with the mission.
32
+ 3. Establish a baseline first.
33
+ 4. Iterate: run the existing benchmark or data experiment, record honestly. One coherent experiment per iteration.
34
+ 5. Keep the primary metric as the decision maker:
35
+ - `keep` when it improves;
36
+ - `discard` when it regresses or stays flat;
37
+ - `crash` when the run fails;
38
+ - `checks_failed` when validation fails (you decide what validation means; run it through the regular `bash` tool).
39
+ 6. Use ASI freely — it is opaque, just stash useful learnings (`hypothesis`, `rollback_reason`, `next_action_hint`, anything else).
40
+ 7. When confidence is low, re-run promising changes before keeping them. Each kept run reports a confidence score (multiples of the observed noise floor).
41
+
42
+ ### Scope, off-limits, and accountability
43
+
44
+ - Product edits are prohibited. A benchmark or data experiment that needs changes is evidence for a separate approval-gated implementation request.
45
+ - If a previous run looks reward-hacked or otherwise wrong, flag it so it is excluded from baseline and best-metric calculations.
46
+
47
+ {{#if has_notes}}
48
+ ### Your notes
49
+
50
+ {{notes}}
51
+
52
+ {{/if}}
53
+ {{#if has_recent_results}}
54
+ ### Current segment snapshot
55
+ - segment: `{{current_segment}}`
56
+ - runs in current segment: `{{current_segment_run_count}}`
57
+ {{#if has_baseline_metric}}
58
+ - baseline `{{metric_name}}`: `{{baseline_metric_display}}`
59
+ {{/if}}
60
+ {{#if has_best_result}}
61
+ - best kept `{{metric_name}}`: `{{best_metric_display}}`{{#if best_run_number}} from run `#{{best_run_number}}`{{/if}}
62
+ {{/if}}
63
+
64
+ Recent runs:
65
+ {{#each recent_results}}
66
+ - run `#{{run_number}}`: `{{status}}` `{{metric_display}}` — {{description}}
67
+ {{#if has_asi_summary}}
68
+ ASI: {{asi_summary}}
69
+ {{/if}}
70
+ {{#if has_deviations}}
71
+ Modified outside scope: {{deviations}}{{#unless justified}} (no justification){{/unless}}
72
+ {{/if}}
73
+ {{#if flagged}}
74
+ FLAGGED: {{flagged_reason}}
75
+ {{/if}}
76
+ {{/each}}
77
+ {{/if}}
78
+ {{#if has_unjustified_runs}}
79
+
80
+ ### Unjustified deviations
81
+ {{#each unjustified_runs}}
82
+ - run `#{{run_number}}` modified `{{paths}}` outside scope without justification. Either accept it, justify it on the next log, or flag it.
83
+ {{/each}}
84
+ {{/if}}
85
+ {{#if has_pending_run}}
86
+
87
+ ### Pending run
88
+ An unlogged run is waiting:
89
+ - run: `#{{pending_run_number}}`
90
+ - command: `{{pending_run_command}}`
91
+ {{#if has_pending_run_metric}}
92
+ - parsed `{{metric_name}}`: `{{pending_run_metric_display}}`
93
+ {{/if}}
94
+ - result: {{#if pending_run_passed}}passed{{else}}failed{{/if}}
95
+
96
+ Record the run in the ledger before starting another benchmark.
97
+ {{/if}}
98
+
99
+ ### Guardrails
100
+ - Do not game the benchmark.
101
+ - Do not overfit to synthetic inputs if the real workload is broader.
102
+ - Preserve correctness.
103
+ - If the user sends another message while a run is in progress, finish the current run and recording cycle first, then address the new input in the next iteration.