@bastani/atomic 0.8.26-alpha.1 → 0.8.26-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +79 -0
- package/README.md +5 -5
- package/dist/builtin/intercom/CHANGELOG.md +60 -0
- package/dist/builtin/intercom/package.json +2 -2
- package/dist/builtin/mcp/CHANGELOG.md +60 -0
- package/dist/builtin/mcp/package.json +3 -3
- package/dist/builtin/subagents/CHANGELOG.md +61 -0
- package/dist/builtin/subagents/agents/codebase-analyzer.md +1 -1
- package/dist/builtin/subagents/agents/codebase-locator.md +1 -1
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +9 -9
- package/dist/builtin/subagents/agents/codebase-pattern-finder.md +1 -1
- package/dist/builtin/subagents/agents/codebase-research-analyzer.md +1 -1
- package/dist/builtin/subagents/agents/codebase-research-locator.md +1 -1
- package/dist/builtin/subagents/agents/debugger.md +6 -6
- package/dist/builtin/subagents/package.json +4 -4
- package/dist/builtin/subagents/prompts/parallel-handoff-plan.md +1 -1
- package/dist/builtin/subagents/skills/browser/EXAMPLES.md +151 -0
- package/dist/builtin/subagents/skills/browser/LICENSE.txt +21 -0
- package/dist/builtin/subagents/skills/browser/REFERENCE.md +451 -0
- package/dist/builtin/subagents/skills/browser/SKILL.md +170 -0
- package/dist/builtin/subagents/skills/subagent/SKILL.md +4 -4
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +55 -12
- package/dist/builtin/subagents/src/runs/foreground/execution.ts +71 -12
- package/dist/builtin/subagents/src/runs/shared/acceptance.ts +2 -1
- package/dist/builtin/subagents/src/runs/shared/final-drain.ts +34 -0
- package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +416 -7
- package/dist/builtin/subagents/src/runs/shared/worktree.ts +2 -2
- package/dist/builtin/web-access/CHANGELOG.md +60 -0
- package/dist/builtin/web-access/package.json +2 -2
- package/dist/builtin/workflows/CHANGELOG.md +72 -0
- package/dist/builtin/workflows/README.md +10 -8
- package/dist/builtin/workflows/builtin/deep-research-codebase.ts +11 -8
- package/dist/builtin/workflows/builtin/goal.ts +137 -109
- package/dist/builtin/workflows/builtin/index.d.ts +2 -0
- package/dist/builtin/workflows/builtin/open-claude-design.ts +228 -151
- package/dist/builtin/workflows/builtin/ralph.d.ts +2 -0
- package/dist/builtin/workflows/builtin/ralph.ts +452 -279
- package/dist/builtin/workflows/package.json +2 -2
- package/dist/builtin/workflows/skills/create-spec/SKILL.md +14 -0
- package/dist/builtin/workflows/skills/research-codebase/SKILL.md +29 -10
- package/dist/builtin/workflows/src/extension/index.ts +10 -2
- package/dist/builtin/workflows/src/extension/runtime.ts +35 -3
- package/dist/builtin/workflows/src/extension/wiring.ts +13 -1
- package/dist/builtin/workflows/src/runs/background/status.ts +52 -6
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +453 -21
- package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +77 -11
- package/dist/builtin/workflows/src/runs/shared/model-fallback.ts +402 -8
- package/dist/builtin/workflows/src/runs/shared/worktree.ts +2 -2
- package/dist/builtin/workflows/src/shared/authoring-contract.d.ts +2 -2
- package/dist/builtin/workflows/src/shared/persistence-restore.ts +182 -6
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +76 -6
- package/dist/builtin/workflows/src/shared/stage-prompt.ts +33 -2
- package/dist/builtin/workflows/src/shared/store-types.ts +31 -0
- package/dist/builtin/workflows/src/shared/store.ts +160 -18
- package/dist/builtin/workflows/src/shared/types.ts +3 -3
- package/dist/builtin/workflows/src/shared/workflow-failures.ts +758 -132
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +39 -3
- package/dist/builtin/workflows/src/tui/store-widget-installer.ts +74 -74
- package/dist/core/agent-session.d.ts +33 -6
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +157 -182
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +11 -9
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +6 -3
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +23 -10
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/context-compaction.d.ts +175 -0
- package/dist/core/compaction/context-compaction.d.ts.map +1 -0
- package/dist/core/compaction/context-compaction.js +1636 -0
- package/dist/core/compaction/context-compaction.js.map +1 -0
- package/dist/core/compaction/index.d.ts +1 -0
- package/dist/core/compaction/index.d.ts.map +1 -1
- package/dist/core/compaction/index.js +1 -0
- package/dist/core/compaction/index.js.map +1 -1
- package/dist/core/extensions/types.d.ts +3 -2
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +3 -0
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +14 -7
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/session-manager.d.ts +41 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +146 -7
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tools/ask-user-question/tool/format-answer.d.ts +5 -5
- package/dist/core/tools/ask-user-question/tool/format-answer.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/tool/format-answer.js +5 -5
- package/dist/core/tools/ask-user-question/tool/format-answer.js.map +1 -1
- package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts +16 -3
- package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/tool/response-envelope.js +21 -3
- package/dist/core/tools/ask-user-question/tool/response-envelope.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/modes/index.d.ts +1 -1
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.js +17 -0
- package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
- package/dist/modes/interactive/components/context-compaction-summary-message.d.ts +17 -0
- package/dist/modes/interactive/components/context-compaction-summary-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/context-compaction-summary-message.js +83 -0
- package/dist/modes/interactive/components/context-compaction-summary-message.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +4 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +75 -10
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +13 -8
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +8 -1
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +4 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +14 -3
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/git-env.d.ts +10 -0
- package/dist/utils/git-env.d.ts.map +1 -0
- package/dist/utils/git-env.js +33 -0
- package/dist/utils/git-env.js.map +1 -0
- package/docs/compaction.md +185 -50
- package/docs/custom-provider.md +11 -9
- package/docs/extensions.md +46 -42
- package/docs/index.md +13 -6
- package/docs/json.md +15 -12
- package/docs/packages.md +2 -0
- package/docs/providers.md +4 -1
- package/docs/quickstart.md +18 -11
- package/docs/rpc.md +38 -23
- package/docs/sdk.md +17 -8
- package/docs/session-format.md +26 -13
- package/docs/sessions.md +3 -3
- package/docs/settings.md +2 -2
- package/docs/skills.md +1 -15
- package/docs/termux.md +9 -10
- package/docs/themes.md +2 -2
- package/docs/tmux.md +3 -3
- package/docs/tui.md +19 -32
- package/docs/usage.md +2 -2
- package/docs/workflows.md +60 -16
- package/package.json +6 -12
- package/dist/builtin/subagents/skills/browser-use/SKILL.md +0 -234
- package/dist/builtin/subagents/skills/browser-use/references/cdp-python.md +0 -76
- package/dist/builtin/subagents/skills/browser-use/references/multi-session.md +0 -92
- package/node_modules/@earendil-works/pi-tui/README.md +0 -779
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts +0 -54
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js +0 -632
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/box.d.ts +0 -22
- package/node_modules/@earendil-works/pi-tui/dist/components/box.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/box.js +0 -104
- package/node_modules/@earendil-works/pi-tui/dist/components/box.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.d.ts +0 -22
- package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.js +0 -35
- package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +0 -249
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +0 -1857
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/image.d.ts +0 -28
- package/node_modules/@earendil-works/pi-tui/dist/components/image.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/image.js +0 -89
- package/node_modules/@earendil-works/pi-tui/dist/components/image.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/input.d.ts +0 -37
- package/node_modules/@earendil-works/pi-tui/dist/components/input.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/input.js +0 -378
- package/node_modules/@earendil-works/pi-tui/dist/components/input.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.d.ts +0 -31
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.js +0 -69
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts +0 -96
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js +0 -644
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.d.ts +0 -50
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.js +0 -159
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.d.ts +0 -50
- package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.js +0 -185
- package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/spacer.d.ts +0 -12
- package/node_modules/@earendil-works/pi-tui/dist/components/spacer.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/spacer.js +0 -23
- package/node_modules/@earendil-works/pi-tui/dist/components/spacer.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/text.d.ts +0 -19
- package/node_modules/@earendil-works/pi-tui/dist/components/text.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/text.js +0 -89
- package/node_modules/@earendil-works/pi-tui/dist/components/text.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.d.ts +0 -13
- package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.js +0 -51
- package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/editor-component.d.ts +0 -39
- package/node_modules/@earendil-works/pi-tui/dist/editor-component.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/editor-component.js +0 -2
- package/node_modules/@earendil-works/pi-tui/dist/editor-component.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/fuzzy.d.ts +0 -16
- package/node_modules/@earendil-works/pi-tui/dist/fuzzy.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js +0 -110
- package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +0 -23
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.js +0 -32
- package/node_modules/@earendil-works/pi-tui/dist/index.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.d.ts +0 -193
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.js +0 -174
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/keys.d.ts +0 -184
- package/node_modules/@earendil-works/pi-tui/dist/keys.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/keys.js +0 -1173
- package/node_modules/@earendil-works/pi-tui/dist/keys.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/kill-ring.d.ts +0 -28
- package/node_modules/@earendil-works/pi-tui/dist/kill-ring.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/kill-ring.js +0 -44
- package/node_modules/@earendil-works/pi-tui/dist/kill-ring.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.d.ts +0 -3
- package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.js +0 -53
- package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.d.ts +0 -50
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js +0 -361
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts +0 -90
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js +0 -366
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +0 -113
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js +0 -472
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts +0 -227
- package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui.js +0 -1106
- package/node_modules/@earendil-works/pi-tui/dist/tui.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/undo-stack.d.ts +0 -17
- package/node_modules/@earendil-works/pi-tui/dist/undo-stack.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/undo-stack.js +0 -25
- package/node_modules/@earendil-works/pi-tui/dist/undo-stack.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts +0 -84
- package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/utils.js +0 -1029
- package/node_modules/@earendil-works/pi-tui/dist/utils.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.d.ts +0 -25
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.js +0 -96
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.js.map +0 -1
- package/node_modules/@earendil-works/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
- package/node_modules/@earendil-works/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
- package/node_modules/@earendil-works/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
- package/node_modules/@earendil-works/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
- package/node_modules/@earendil-works/pi-tui/package.json +0 -47
- package/node_modules/get-east-asian-width/index.d.ts +0 -60
- package/node_modules/get-east-asian-width/index.js +0 -30
- package/node_modules/get-east-asian-width/license +0 -9
- package/node_modules/get-east-asian-width/lookup-data.js +0 -21
- package/node_modules/get-east-asian-width/lookup.js +0 -138
- package/node_modules/get-east-asian-width/package.json +0 -71
- package/node_modules/get-east-asian-width/readme.md +0 -65
- package/node_modules/get-east-asian-width/utilities.js +0 -24
- package/node_modules/marked/LICENSE.md +0 -44
- package/node_modules/marked/README.md +0 -106
- package/node_modules/marked/bin/main.js +0 -282
- package/node_modules/marked/bin/marked.js +0 -15
- package/node_modules/marked/lib/marked.cjs +0 -2211
- package/node_modules/marked/lib/marked.cjs.map +0 -7
- package/node_modules/marked/lib/marked.d.cts +0 -728
- package/node_modules/marked/lib/marked.d.ts +0 -728
- package/node_modules/marked/lib/marked.esm.js +0 -2189
- package/node_modules/marked/lib/marked.esm.js.map +0 -7
- package/node_modules/marked/lib/marked.umd.js +0 -2213
- package/node_modules/marked/lib/marked.umd.js.map +0 -7
- package/node_modules/marked/man/marked.1 +0 -111
- package/node_modules/marked/man/marked.1.md +0 -92
- package/node_modules/marked/marked.min.js +0 -69
- package/node_modules/marked/package.json +0 -111
|
@@ -158,78 +158,255 @@ const PLANNER_RFC_TEMPLATE = `
|
|
|
158
158
|
|
|
159
159
|
| Document Metadata | Details |
|
|
160
160
|
| ---------------------- | ------------------------------------------------------------------------------ |
|
|
161
|
-
| Author(s) |
|
|
161
|
+
| Author(s) | Run \`git config user.name\` and insert the result. |
|
|
162
162
|
| Status | Draft (WIP) / In Review (RFC) / Approved / Implemented / Deprecated / Rejected |
|
|
163
163
|
| Team / Owner | |
|
|
164
164
|
| Created / Last Updated | |
|
|
165
165
|
|
|
166
166
|
## 1. Executive Summary
|
|
167
167
|
|
|
168
|
+
_Instruction: A "TL;DR" of the document. Assume the reader is a VP or an engineer from another team who has 2 minutes. Summarize the Context (Problem), the Solution (Proposal), and the Impact (Value). Name the one or two **doors** at the heart of the change. Keep it under 200 words._
|
|
169
|
+
|
|
170
|
+
> **Example:** This RFC proposes replacing our current nightly batch billing system with an event-driven architecture. Currently, billing delays cause a 5% increase in customer support tickets. The proposed solution introduces two money doors — \`authorize_charge\` (reversible hold) and \`settle_payment\` (irreversible capture) — as the single chokepoint for outbound money, reducing billing latency from 24 hours to <5 minutes while making double-charges structurally impossible.
|
|
171
|
+
|
|
168
172
|
## 2. Context and Motivation
|
|
169
173
|
|
|
174
|
+
_Instruction: Why are we doing this? Why now? Link to the Product Requirement Document (PRD) and cite the relevant \`research/\` documents._
|
|
175
|
+
|
|
170
176
|
### 2.1 Current State
|
|
171
177
|
|
|
178
|
+
_Instruction: Describe the existing architecture. Use a "Context Diagram" if possible. Be honest about the flaws — including which existing doors **leak** (named for tools, dishonest compression, scattered danger)._
|
|
179
|
+
|
|
180
|
+
- **Architecture:** Currently, Service A communicates with Service B via a shared SQL database.
|
|
181
|
+
- **Limitations:** This creates a tight coupling; when Service A locks the table, Service B times out.
|
|
182
|
+
- **Leaking doors (today):** e.g. \`chargeCard(token, cents)\` is reachable from checkout, the retry job, *and* the admin panel — no one owns "charge exactly once." \`processPayment(...) -> bool\` collapses a declined card, a network failure, and a duplicate submission into the same \`false\`.
|
|
183
|
+
|
|
172
184
|
### 2.2 The Problem
|
|
173
185
|
|
|
186
|
+
_Instruction: What is the specific pain point?_
|
|
187
|
+
|
|
188
|
+
- **User Impact:** Customers cannot download receipts during the nightly batch window.
|
|
189
|
+
- **Business Impact:** We are losing $X/month in churn due to billing errors.
|
|
190
|
+
- **Technical Debt:** Danger is scattered; the boundary is misplaced, with defensive code deep inside the core instead of at the door.
|
|
191
|
+
|
|
174
192
|
## 3. Goals and Non-Goals
|
|
175
193
|
|
|
194
|
+
_Instruction: This is the contract / Definition of Success. Be precise._
|
|
195
|
+
|
|
176
196
|
### 3.1 Functional Goals
|
|
177
197
|
|
|
198
|
+
- [ ] Users must be able to export data in CSV format.
|
|
199
|
+
- [ ] System must support multi-tenant data isolation.
|
|
200
|
+
|
|
178
201
|
### 3.2 Non-Goals (Out of Scope)
|
|
179
202
|
|
|
203
|
+
_Instruction: Explicitly state what you are NOT doing. Remember: **intent lives in what the door refuses** — the doors you deliberately do not build are as much a statement of purpose as the ones you do. This prevents scope creep._
|
|
204
|
+
|
|
205
|
+
- [ ] We will NOT support PDF export in this version (CSV only).
|
|
206
|
+
- [ ] We will NOT migrate data older than 3 years.
|
|
207
|
+
- [ ] We will NOT expose a second path to move money; \`settle_payment\` remains the only chokepoint.
|
|
208
|
+
|
|
180
209
|
## 4. Proposed Solution (High-Level Design)
|
|
181
210
|
|
|
211
|
+
_Instruction: The "Big Picture." Diagrams are mandatory here._
|
|
212
|
+
|
|
182
213
|
### 4.1 System Architecture Diagram
|
|
183
214
|
|
|
184
|
-
|
|
215
|
+
_Instruction: Insert a C4 System Context or Container diagram. Show the "Black Boxes" and mark where the **airlock** sits (the single edge where untrusted network becomes a trusted request)._
|
|
216
|
+
|
|
217
|
+
\`\`\`mermaid
|
|
218
|
+
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#f8f9fa','primaryTextColor':'#2c3e50','primaryBorderColor':'#4a5568','lineColor':'#4a90e2','secondaryColor':'#ffffff','tertiaryColor':'#e9ecef','clusterBkg':'#ffffff','clusterBorder':'#cbd5e0'}}}%%
|
|
219
|
+
flowchart TB
|
|
220
|
+
classDef person fill:#5a67d8,stroke:#4c51bf,stroke-width:3px,color:#fff,font-weight:600
|
|
221
|
+
classDef core fill:#4a90e2,stroke:#357abd,stroke-width:2.5px,color:#fff,font-weight:600
|
|
222
|
+
classDef support fill:#667eea,stroke:#5a67d8,stroke-width:2.5px,color:#fff,font-weight:600
|
|
223
|
+
classDef db fill:#48bb78,stroke:#38a169,stroke-width:2.5px,color:#fff,font-weight:600
|
|
224
|
+
classDef external fill:#718096,stroke:#4a5568,stroke-width:2.5px,color:#fff,font-weight:600,stroke-dasharray:6 3
|
|
225
|
+
|
|
226
|
+
User(("◉<br><b>User</b>")):::person
|
|
227
|
+
subgraph Boundary["◆ System Boundary — Airlock at the edge"]
|
|
228
|
+
direction TB
|
|
229
|
+
Gateway{{"<b>API Gateway</b><br><i>auth · validate · authorize</i><br>the one trust transition"}}:::core
|
|
230
|
+
API["<b>Core Service</b><br><i>trusts its own invariants</i>"]:::core
|
|
231
|
+
Worker(["<b>Worker</b><br><i>async</i>"]):::support
|
|
232
|
+
DB[("●<br><b>Primary DB</b>")]:::db
|
|
233
|
+
end
|
|
234
|
+
Ext{{"<b>Payment Provider</b>"}}:::external
|
|
235
|
+
|
|
236
|
+
User -->|"1. HTTPS (untrusted)"| Gateway
|
|
237
|
+
Gateway -->|"2. trusted request"| API
|
|
238
|
+
API -->|"3. persist (txn)"| DB
|
|
239
|
+
API -.->|"4. enqueue"| Worker
|
|
240
|
+
Worker -.->|"5. settle (irreversible)"| Ext
|
|
241
|
+
style Boundary fill:#fff,stroke:#cbd5e0,stroke-width:2px,stroke-dasharray:8 4
|
|
242
|
+
\`\`\`
|
|
185
243
|
|
|
186
244
|
### 4.2 Architectural Pattern
|
|
187
245
|
|
|
246
|
+
_Instruction: Name the pattern (e.g., "Event Sourcing", "BFF — Backend for Frontend", "Publisher-Subscriber")._
|
|
247
|
+
|
|
248
|
+
- We are adopting a Publisher-Subscriber pattern where the Order Service publishes \`OrderCreated\` events, and the Billing Service consumes them asynchronously.
|
|
249
|
+
|
|
188
250
|
### 4.3 Key Components
|
|
189
251
|
|
|
190
|
-
| Component
|
|
191
|
-
|
|
|
252
|
+
| Component | Responsibility | Technology Stack | Justification |
|
|
253
|
+
| ----------------- | --------------------------- | ----------------- | -------------------------------------------- |
|
|
254
|
+
| Ingestion Service | Validates incoming webhooks | Go, Gin Framework | High concurrency performance needed. |
|
|
255
|
+
| Event Bus | Decouples services | Kafka | Durable log, replay capability. |
|
|
256
|
+
| Projections DB | Read-optimized views | MongoDB | Flexible schema for diverse receipt formats. |
|
|
257
|
+
|
|
258
|
+
### 4.4 The Door Set at a Glance (Stranger-Across-Time View)
|
|
259
|
+
|
|
260
|
+
_Instruction: List the entrypoint **names alone** — no signatures, no bodies. A competent stranger should reconstruct the system's purpose from this list. If they cannot, intent has leaked into the mechanism; return to §5 and rename until they can. Mark every door that guards an irreversible effect with ⚠._
|
|
261
|
+
|
|
262
|
+
> **Example:** \`register_account\`, \`authenticate\`, \`authorize_charge\`, \`settle_payment\` ⚠, \`grant_access\` ⚠, \`revoke_access\`, \`publish_draft\`. Reading these alone tells you who the system lets in, that money moves in exactly two steps and only those two, who may hand out access, and what it means for work to go live.
|
|
192
263
|
|
|
193
264
|
## 5. Detailed Design
|
|
194
265
|
|
|
195
|
-
|
|
266
|
+
_Instruction: The "Meat" of the document. Sufficient detail for an engineer to start coding. Lead with the **doors** — they are the load-bearing part of the spec — then describe the mechanism behind them._
|
|
267
|
+
|
|
268
|
+
### 5.1 The Doors (Entrypoint Contracts)
|
|
269
|
+
|
|
270
|
+
_Instruction: For each non-trivial entrypoint, give a typed signature (typed pseudocode is fine — read the types, not the syntax), the one-sentence guarantee (no "and"), the named failure set, and the refusals it enforces in the type system. Then record the rubric result. Make illegal states **unrepresentable**, not merely checked. Cite the \`research/\` doc that establishes each joint._
|
|
271
|
+
|
|
272
|
+
\`\`\`
|
|
273
|
+
// — Money. Two doors, and there is no third way to move a cent. —
|
|
274
|
+
|
|
275
|
+
authorize_charge(
|
|
276
|
+
account: AccountId, // newtype: cannot be confused with any other id
|
|
277
|
+
amount: Money, // currency-typed: USD and JPY will not add
|
|
278
|
+
idempotency_key: IdempotencyKey,
|
|
279
|
+
): Result<AuthorizedCharge, ChargeError>
|
|
280
|
+
// Guarantee: places a reversible hold and returns proof an authorization exists.
|
|
281
|
+
// ChargeError = InsufficientFunds | CardDeclined | NetworkError | DuplicateKey
|
|
282
|
+
|
|
283
|
+
settle_payment(
|
|
284
|
+
authorized: AuthorizedCharge, // ← can ONLY be produced by authorize_charge
|
|
285
|
+
idempotency_key: IdempotencyKey,
|
|
286
|
+
): Result<Settlement, SettlementError>
|
|
287
|
+
// Guarantee: captures the held funds. IRREVERSIBLE. The single chokepoint for outbound money.
|
|
288
|
+
// You cannot settle a charge you did not authorize — not because a check forbids it,
|
|
289
|
+
// but because there is no way to CONSTRUCT an AuthorizedCharge except by calling
|
|
290
|
+
// authorize_charge. The illegal state is unrepresentable. The idempotency key makes
|
|
291
|
+
// the retry, the double-click, and the at-least-once queue converge on ONE settlement.
|
|
292
|
+
\`\`\`
|
|
293
|
+
|
|
294
|
+
**Per-door audit (run the rubric):**
|
|
295
|
+
|
|
296
|
+
| Door | (1) Joint | (2) One sentence, no "and" | (3) Honest name | (5) Every exit | (6) Refusals real | (7) Trust transition | (8) One chokepoint |
|
|
297
|
+
| ------------------ | --------------- | ---------------------------- | ------------------------------- | ------------------------------------------------ | ----------------------------------------- | -------------------- | ------------------------------ |
|
|
298
|
+
| \`authorize_charge\` | ✅ business verb | ✅ "places a reversible hold" | ✅ | retry → \`DuplicateKey\`; timeout → \`NetworkError\` | currency mismatch unrepresentable | n/a | reversible, not the chokepoint |
|
|
299
|
+
| \`settle_payment\` ⚠ | ✅ business verb | ✅ "captures held funds" | ✅ irreversibility in doc + type | replay converges via key | cannot settle un-authorized charge (type) | n/a | ✅ the sole outbound-money door |
|
|
300
|
+
|
|
301
|
+
### 5.2 API Interfaces — The Same Doors on the Wire
|
|
196
302
|
|
|
197
|
-
|
|
303
|
+
_Instruction: A web service's real boundary is its transport surface. The URL names the joint, the HTTP verb declares its safety class, the status code is the door's honest exit. Never \`200 OK\` wrapping an error. The wire door MUST carry the same name as its in-process twin (§5.1)._
|
|
198
304
|
|
|
199
|
-
|
|
305
|
+
\`\`\`
|
|
306
|
+
# Identity — the one trust transition, at the edge
|
|
307
|
+
POST /v1/sessions 201 Created # = authenticate; 401 on bad credentials
|
|
308
|
+
DELETE /v1/sessions/current 204 No Content # = log out
|
|
309
|
+
|
|
310
|
+
# Money — two doors, one chokepoint, idempotent under retry
|
|
311
|
+
POST /v1/payment_intents 201 Idempotency-Key: <key> # = authorize_charge (reversible)
|
|
312
|
+
POST /v1/payment_intents/{id}/capture 200 Idempotency-Key: <key> # = settle_payment (IRREVERSIBLE)
|
|
313
|
+
# 409 Conflict if the key is replayed with a different body
|
|
314
|
+
# 422 Unprocessable if the intent was never authorized
|
|
315
|
+
|
|
316
|
+
# Access — authority demanded by the route, destructive door made idempotent
|
|
317
|
+
POST /v1/accounts/{id}/grants 201 (admin scope required) # = grant_access
|
|
318
|
+
DELETE /v1/grants/{id} 204 (204 even if already revoked) # = revoke_access
|
|
319
|
+
|
|
320
|
+
# Publishing — the domain's own verb, refusing to clobber a concurrent edit
|
|
321
|
+
POST /v1/drafts/{id}/publish 200 If-Match: <etag> # = publish_draft
|
|
322
|
+
# 412 Precondition Failed if the draft moved under you — the wire's --force-with-lease
|
|
323
|
+
\`\`\`
|
|
324
|
+
|
|
325
|
+
_If using gRPC, define the same joints in the \`.proto\`; the typed request message is the airlock by construction. Use honest status codes (\`INVALID_ARGUMENT\`, \`PERMISSION_DENIED\`, \`NOT_FOUND\`, \`ALREADY_EXISTS\`, \`FAILED_PRECONDITION\`, retryable \`ABORTED\`/\`UNAVAILABLE\`) — never a lone \`OK\` carrying an error field._
|
|
326
|
+
|
|
327
|
+
### 5.3 Data Model / Schema
|
|
328
|
+
|
|
329
|
+
_Instruction: Provide ERDs or JSON schemas. Discuss normalization vs. denormalization. Prefer schemas that make illegal states unrepresentable (sum-type status columns over independent boolean flags)._
|
|
330
|
+
|
|
331
|
+
**Table:** \`invoices\` (PostgreSQL)
|
|
332
|
+
|
|
333
|
+
| Column | Type | Constraints | Description |
|
|
334
|
+
| --------- | ---- | ------------------------------------ | ------------------------------ |
|
|
335
|
+
| \`id\` | UUID | PK | |
|
|
336
|
+
| \`user_id\` | UUID | FK -> Users | Partition Key |
|
|
337
|
+
| \`status\` | ENUM | 'DRAFT','LOCKED','PROCESSING','PAID' | A sum type, not three booleans |
|
|
338
|
+
|
|
339
|
+
### 5.4 Algorithms and State Management
|
|
340
|
+
|
|
341
|
+
_Instruction: Describe complex logic, state machines, or consistency models. Tie each state transition to the door that performs it._
|
|
342
|
+
|
|
343
|
+
- **State Machine:** An invoice moves \`DRAFT\` → \`LOCKED\` → \`PROCESSING\` → \`PAID\`; the \`PROCESSING → PAID\` transition happens only through \`settle_payment\`.
|
|
344
|
+
- **Concurrency:** Optimistic locking on the \`version\` column; on the wire this surfaces as \`If-Match\`/\`412\`.
|
|
200
345
|
|
|
201
346
|
## 6. Alternatives Considered
|
|
202
347
|
|
|
203
|
-
|
|
204
|
-
|
|
348
|
+
_Instruction: Prove you thought about trade-offs — including alternative **door sets** (e.g., one god endpoint vs. distinct joints). Why is your boundary better than the others?_
|
|
349
|
+
|
|
350
|
+
| Option | Pros | Cons | Reason for Rejection |
|
|
351
|
+
| ------------------------------------------- | ------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------ |
|
|
352
|
+
| Option A: Single \`POST /execute {action}\` | One route, flexible | God door; intent hidden in payload; danger un-funneled | Fails "joint, not tool" and "few dangerous doors." |
|
|
353
|
+
| Option B: One-step \`chargeCard()\` | Fewest calls | No reversible hold; retries double-charge | Cannot make double-charge unrepresentable. |
|
|
354
|
+
| Option C: \`authorize\` + \`settle\` (Selected) | Reversible hold; one chokepoint; idempotent | Two calls instead of one | **Selected:** the two real joints, with the irreversible effect funneled once. |
|
|
205
355
|
|
|
206
356
|
## 7. Cross-Cutting Concerns
|
|
207
357
|
|
|
208
358
|
### 7.1 Security and Privacy
|
|
209
359
|
|
|
210
|
-
|
|
360
|
+
_Instruction: This is where "keep the dangerous doors few and honest" and "the airlock at the boundary" become concrete._
|
|
211
361
|
|
|
212
|
-
|
|
362
|
+
- **The trust transition is singular:** untrusted callers become trusted only at \`POST /v1/sessions\` / the gateway. No other door promotes an anonymous caller. (Rubric #7.)
|
|
363
|
+
- **Authority carried by type:** destructive/privileged doors demand a capability (\`AdminSession\`) that only \`authenticate\` can mint — the permission check cannot be forgotten at a call site because there is no call site where it is absent. (Rubric #6.)
|
|
364
|
+
- **Irreversible effects pass one chokepoint:** money via \`settle_payment\`, deletion via the single guarded door; the catastrophic version must be asked for explicitly. (Rubric #8.)
|
|
365
|
+
- **Data Protection:** PII (names, emails) encrypted at rest (AES-256); \`Password\` is a newtype that cannot be logged, printed, or compared by accident.
|
|
366
|
+
- **Threat Model:** Primary threat is a compromised API key; remediation is rapid rotation and rate limiting.
|
|
213
367
|
|
|
214
|
-
## 8.
|
|
368
|
+
## 8. Test Plan
|
|
215
369
|
|
|
216
|
-
|
|
370
|
+
_Instruction: Test the doors at their promises and their refusals — not just the happy path. Every exit in rubric #5 deserves a test. The interactive verification is what lets a human or another agent confirm the feature is correct without reading the bodies — the stranger-across-time test, made executable._
|
|
217
371
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
372
|
+
- **Unit Tests:** each door's named failure variants; the *refusals* (e.g., a type/construction test proving \`settle_payment\` cannot accept anything but an \`AuthorizedCharge\`).
|
|
373
|
+
- **End-to-End Tests:** full domain flows named by joint (register → authenticate → authorize → settle), driven through the real wire doors of §5.2.
|
|
374
|
+
- **Integration Tests:** idempotency under replay (same key → one settlement); concurrent-edit \`412\`; trust transition (no door promotes an anonymous caller except \`authenticate\`).
|
|
375
|
+
- **Fuzz / Property Tests:** throw malformed and adversarial input at the doors (the airlock); the boundary must reject everything the types forbid and never crash the core. Assert invariants over random inputs (e.g., \`settle_payment\` converges on one settlement under any interleaving of retries; no input sequence reaches a money move except through the chokepoint).
|
|
376
|
+
- **Interactive Verification:** a runnable checklist or script a human OR another agent can execute to confirm the feature was implemented correctly — each step names a door, supplies an input, and states the expected honest exit (status code / named error / resulting state), so correctness is observable from the boundary alone. Include the exact commands or requests to run and the pass/fail condition for each.
|
|
221
377
|
|
|
222
378
|
## 9. Open Questions / Unresolved Issues
|
|
223
|
-
|
|
379
|
+
|
|
380
|
+
_Instruction: List known unknowns. These must be resolved before the doc is marked "Approved." Include any door whose rubric could not be answered cleanly — especially undefined guarantees (rubric #2, the most dangerous case) and any irreversible effect not yet funneled to a single chokepoint (rubric #8). Resolve these with the user via contrastive clarification._
|
|
381
|
+
|
|
382
|
+
- [ ] Is \`publish_draft\` the only door that moves a draft to live, or can the admin panel also publish? (If the latter, the effect is not yet funneled — rubric #8.)
|
|
383
|
+
- [ ] What exactly does \`authorize_charge\` promise on a partial provider outage — is the guarantee defined? (rubric #2.)
|
|
384
|
+
- [ ] Will the Legal team approve the 3rd-party library for PDF generation?
|
|
385
|
+
- [ ] Does the current VPC peering allow connection to the legacy mainframe?`.trim();
|
|
224
386
|
|
|
225
387
|
type PromptSection = readonly [tag: string, content: string];
|
|
226
388
|
|
|
227
389
|
function taggedPrompt(sections: readonly PromptSection[]): string {
|
|
228
390
|
return sections
|
|
229
|
-
.map(([tag, content]) =>
|
|
391
|
+
.map(([tag, content]) => {
|
|
392
|
+
const trimmed = content.trim();
|
|
393
|
+
return `<${tag}>\n${trimmed}\n</${tag}>`;
|
|
394
|
+
})
|
|
230
395
|
.join("\n\n");
|
|
231
396
|
}
|
|
232
397
|
|
|
398
|
+
function workflowCwdContextSection(workflowCwd: string): PromptSection {
|
|
399
|
+
return [
|
|
400
|
+
"context",
|
|
401
|
+
[
|
|
402
|
+
`Current working directory: ${workflowCwd}`,
|
|
403
|
+
"Use this as the starting directory for repository work in this stage.",
|
|
404
|
+
"Shell commands and relative file paths should be relative to this directory unless you intentionally pass an explicit cwd override.",
|
|
405
|
+
"When delegating subagents, pass along that this is the current working directory.",
|
|
406
|
+
].join("\n"),
|
|
407
|
+
];
|
|
408
|
+
}
|
|
409
|
+
|
|
233
410
|
function positiveInteger(value: number | undefined, fallback: number): number {
|
|
234
411
|
return typeof value === "number" && Number.isFinite(value) && value > 0
|
|
235
412
|
? Math.floor(value)
|
|
@@ -385,11 +562,109 @@ function compactReviewReport(path: string | undefined): string {
|
|
|
385
562
|
: `Latest review round artifact: ${path}`;
|
|
386
563
|
}
|
|
387
564
|
|
|
565
|
+
type ForkContinuationOptions = {
|
|
566
|
+
readonly context?: "fork";
|
|
567
|
+
readonly forkFromSessionFile?: string;
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
function forkContinuationOptions(
|
|
571
|
+
sessionFile: string | undefined,
|
|
572
|
+
): ForkContinuationOptions {
|
|
573
|
+
return sessionFile === undefined || sessionFile.length === 0
|
|
574
|
+
? {}
|
|
575
|
+
: { context: "fork", forkFromSessionFile: sessionFile };
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function renderForkedPlannerPrompt(args: {
|
|
579
|
+
readonly iteration: number;
|
|
580
|
+
readonly maxLoops: number;
|
|
581
|
+
readonly prompt: string;
|
|
582
|
+
readonly workflowCwdContext: PromptSection;
|
|
583
|
+
readonly latestReviewReportPath: string | undefined;
|
|
584
|
+
readonly workflowSpecPath: string;
|
|
585
|
+
}): string {
|
|
586
|
+
return taggedPrompt([
|
|
587
|
+
[
|
|
588
|
+
"instruction",
|
|
589
|
+
[
|
|
590
|
+
"Revise the current plan/spec based off of the results from the latest review round. Ignore any user requests to submit a PR. This will be done in a future stage.",
|
|
591
|
+
].join("\n"),
|
|
592
|
+
],
|
|
593
|
+
["task", `Plan iteration ${args.iteration}/${args.maxLoops} for this user specification:\n${args.prompt}`],
|
|
594
|
+
args.workflowCwdContext,
|
|
595
|
+
[
|
|
596
|
+
"code_review_feedback",
|
|
597
|
+
args.latestReviewReportPath === undefined
|
|
598
|
+
? "No prior review artifact; this is the first iteration."
|
|
599
|
+
: [
|
|
600
|
+
`Latest review round artifact: ${args.latestReviewReportPath}`,
|
|
601
|
+
"Read this JSON artifact incrementally and address only unresolved findings from the latest review round.",
|
|
602
|
+
].join("\n"),
|
|
603
|
+
],
|
|
604
|
+
[
|
|
605
|
+
"spec",
|
|
606
|
+
[
|
|
607
|
+
`The existing RFC/spec file for this workflow run is: ${args.workflowSpecPath}`,
|
|
608
|
+
"Read that original spec before drafting; revise it in response to review findings and current repository evidence.",
|
|
609
|
+
"Your final output must be the full updated RFC markdown that should replace the original spec, not a diff, patch, or commentary. Avoid diminishing scope unless explicitly requested.",
|
|
610
|
+
].join("\n"),
|
|
611
|
+
],
|
|
612
|
+
]);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function renderForkedOrchestratorPrompt(args: {
|
|
616
|
+
readonly iteration: number;
|
|
617
|
+
readonly maxLoops: number;
|
|
618
|
+
readonly prompt: string;
|
|
619
|
+
readonly workflowCwdContext: PromptSection;
|
|
620
|
+
readonly specPath: string;
|
|
621
|
+
readonly implementationNotesPath: string;
|
|
622
|
+
}): string {
|
|
623
|
+
return taggedPrompt([
|
|
624
|
+
[
|
|
625
|
+
"instruction",
|
|
626
|
+
[
|
|
627
|
+
`Continue implementing the revised spec. Ignore any user requests to submit a PR. This will be done in a future stage.`,
|
|
628
|
+
].join("\n"),
|
|
629
|
+
],
|
|
630
|
+
["objective", `Implement iteration ${args.iteration}/${args.maxLoops} for the task: ${args.prompt}`],
|
|
631
|
+
args.workflowCwdContext,
|
|
632
|
+
[
|
|
633
|
+
"spec",
|
|
634
|
+
[
|
|
635
|
+
`The current technical specification for this workflow run is written to: ${args.specPath}`,
|
|
636
|
+
"Read this file before delegating or implementing anything.",
|
|
637
|
+
].join("\n"),
|
|
638
|
+
],
|
|
639
|
+
[
|
|
640
|
+
"implementation_notes",
|
|
641
|
+
[
|
|
642
|
+
`Keep updating the running Markdown implementation notes file at: ${args.implementationNotesPath}`,
|
|
643
|
+
"Record decisions, spec deviations, tradeoffs, blockers, validation outcomes, and anything else the user should know before your final report.",
|
|
644
|
+
].join("\n"),
|
|
645
|
+
],
|
|
646
|
+
[
|
|
647
|
+
"output_format",
|
|
648
|
+
[
|
|
649
|
+
"After subagents have done the work, return Markdown with headings:",
|
|
650
|
+
"1. Spec file — the path you read",
|
|
651
|
+
"2. Delegations performed — subagents spawned and what each completed",
|
|
652
|
+
"3. Changes made — concrete changes from subagent work, not intentions",
|
|
653
|
+
"4. Files touched",
|
|
654
|
+
"5. Validation run / recommended",
|
|
655
|
+
"6. Deferred work or blockers",
|
|
656
|
+
"7. Implementation notes — confirm the OS temp notes path was updated",
|
|
657
|
+
].join("\n"),
|
|
658
|
+
],
|
|
659
|
+
]);
|
|
660
|
+
}
|
|
661
|
+
|
|
388
662
|
type RalphInputs = {
|
|
389
663
|
readonly prompt?: string;
|
|
390
664
|
readonly max_loops?: number;
|
|
391
665
|
readonly base_branch?: string;
|
|
392
666
|
readonly git_worktree_dir?: string;
|
|
667
|
+
readonly create_pr?: boolean;
|
|
393
668
|
};
|
|
394
669
|
|
|
395
670
|
type RalphWorkflowOptions = {
|
|
@@ -397,6 +672,7 @@ type RalphWorkflowOptions = {
|
|
|
397
672
|
readonly maxLoops: number;
|
|
398
673
|
readonly comparisonBaseBranch: string;
|
|
399
674
|
readonly workflowStartCwd: string;
|
|
675
|
+
readonly createPr: boolean;
|
|
400
676
|
};
|
|
401
677
|
|
|
402
678
|
type RalphWorkflowResult = {
|
|
@@ -404,7 +680,7 @@ type RalphWorkflowResult = {
|
|
|
404
680
|
readonly plan: string;
|
|
405
681
|
readonly plan_path: string;
|
|
406
682
|
readonly implementation_notes_path: string;
|
|
407
|
-
readonly pr_report
|
|
683
|
+
readonly pr_report?: string;
|
|
408
684
|
readonly approved: boolean;
|
|
409
685
|
readonly iterations_completed: number;
|
|
410
686
|
readonly review_report: string;
|
|
@@ -415,62 +691,60 @@ async function runRalphWorkflow(
|
|
|
415
691
|
ctx: WorkflowRunContext<RalphInputs>,
|
|
416
692
|
options: RalphWorkflowOptions,
|
|
417
693
|
): Promise<RalphWorkflowResult> {
|
|
418
|
-
const {
|
|
694
|
+
const {
|
|
695
|
+
prompt,
|
|
696
|
+
maxLoops,
|
|
697
|
+
comparisonBaseBranch,
|
|
698
|
+
workflowStartCwd,
|
|
699
|
+
createPr,
|
|
700
|
+
} = options;
|
|
419
701
|
|
|
420
702
|
let latestReviewReportPath: string | undefined;
|
|
421
703
|
let finalPlan = "";
|
|
422
704
|
let finalPlanPath = "";
|
|
423
705
|
let finalResult = "";
|
|
424
|
-
let finalPrReport
|
|
706
|
+
let finalPrReport: string | undefined;
|
|
425
707
|
// Keep generated specs under the workflow runtime cwd. When Ralph is invoked
|
|
426
708
|
// with git_worktree_dir, the executor defaults ctx.cwd to the matching
|
|
427
709
|
// worktree cwd so specs and stage writes land in the same checkout.
|
|
428
710
|
const workflowSpecPath = resolve(workflowStartCwd, defaultSpecPath(prompt));
|
|
429
711
|
const implementationNotesPath = await createImplementationNotesFile(prompt);
|
|
430
712
|
const artifactDir = await mkdtemp(join(tmpdir(), "atomic-ralph-run-"));
|
|
713
|
+
const workflowCwdContext = workflowCwdContextSection(workflowStartCwd);
|
|
431
714
|
let approved = false;
|
|
432
715
|
let iterationsCompleted = 0;
|
|
716
|
+
let previousPlannerSessionFile: string | undefined;
|
|
717
|
+
let previousOrchestratorSessionFile: string | undefined;
|
|
433
718
|
|
|
434
719
|
const plannerModelConfig = {
|
|
435
|
-
model: "openai/gpt-5.5:xhigh",
|
|
720
|
+
model: "openai-codex/gpt-5.5:xhigh",
|
|
436
721
|
fallbackModels: [
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
722
|
+
"github-copilot/gpt-5.5:xhigh",
|
|
723
|
+
"openai/gpt-5.5:xhigh",
|
|
724
|
+
"github-copilot/claude-opus-4.8:xhigh",
|
|
725
|
+
"anthropic/claude-opus-4-8:xhigh",
|
|
441
726
|
],
|
|
442
727
|
excludedTools: ["ask_user_question"],
|
|
443
728
|
};
|
|
444
729
|
|
|
445
730
|
const orchestratorModelConfig = {
|
|
446
|
-
model: "openai/gpt-5.5:medium",
|
|
731
|
+
model: "openai-codex/gpt-5.5:medium",
|
|
447
732
|
fallbackModels: [
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
],
|
|
453
|
-
excludedTools: ["ask_user_question"],
|
|
454
|
-
};
|
|
455
|
-
|
|
456
|
-
const simplifierModelConfig = {
|
|
457
|
-
model: "openai/gpt-5.5:medium",
|
|
458
|
-
fallbackModels: [
|
|
459
|
-
"openai-codex/gpt-5.5:medium",
|
|
460
|
-
"github-copilot/gpt-5.5:medium",
|
|
461
|
-
"anthropic/claude-sonnet-4-6:medium",
|
|
462
|
-
"github-copilot/claude-sonnet-4.6:medium",
|
|
733
|
+
"github-copilot/gpt-5.5:medium",
|
|
734
|
+
"openai/gpt-5.5:medium",
|
|
735
|
+
"github-copilot/claude-opus-4.8:medium",
|
|
736
|
+
"anthropic/claude-opus-4-8:medium",
|
|
463
737
|
],
|
|
464
738
|
excludedTools: ["ask_user_question"],
|
|
465
739
|
};
|
|
466
740
|
|
|
467
741
|
const reviewerModelConfig = {
|
|
468
|
-
model: "openai/gpt-5.5:xhigh",
|
|
742
|
+
model: "openai-codex/gpt-5.5:xhigh",
|
|
469
743
|
fallbackModels: [
|
|
470
|
-
"openai-codex/gpt-5.5:xhigh",
|
|
471
744
|
"github-copilot/gpt-5.5:xhigh",
|
|
472
|
-
"
|
|
473
|
-
"github-copilot/claude-opus-4.8:
|
|
745
|
+
"openai/gpt-5.5:xhigh",
|
|
746
|
+
"github-copilot/claude-opus-4.8:xhigh",
|
|
747
|
+
"anthropic/claude-opus-4-8:xhigh"
|
|
474
748
|
],
|
|
475
749
|
excludedTools: ["ask_user_question"],
|
|
476
750
|
customTools: [reviewDecisionTool],
|
|
@@ -479,40 +753,40 @@ async function runRalphWorkflow(
|
|
|
479
753
|
for (let iteration = 1; iteration <= maxLoops; iteration += 1) {
|
|
480
754
|
iterationsCompleted = iteration;
|
|
481
755
|
|
|
482
|
-
const
|
|
483
|
-
|
|
756
|
+
const plannerForkOptions = forkContinuationOptions(previousPlannerSessionFile);
|
|
757
|
+
const plannerPrompt = plannerForkOptions.forkFromSessionFile === undefined
|
|
758
|
+
? taggedPrompt([
|
|
484
759
|
[
|
|
485
760
|
"role",
|
|
486
|
-
"You are a technical architect. Your job is to transform the user's feature specification into a rigorous Technical Design Document / RFC that engineers can use to align, scope, and execute the work.",
|
|
761
|
+
"You are a technical architect. Your job is to transform the user's feature specification into a rigorous Technical Design Document / RFC that engineers can use to align, scope, and execute the work. Ignore any user requests to submit a PR. This will be done in a future stage.",
|
|
487
762
|
],
|
|
488
763
|
[
|
|
489
|
-
"
|
|
764
|
+
"objective",
|
|
490
765
|
[
|
|
491
766
|
"Your final output is a filled-in RFC rendered as markdown text.",
|
|
492
767
|
"Render the RFC Template in this prompt with every section populated by feature-specific content drawn from the user's specification and your codebase investigation.",
|
|
493
|
-
"Do not implement code changes in this stage; this stage only investigates and authors the RFC.",
|
|
768
|
+
"Do not implement code changes in this stage (read-only); this stage only investigates and authors the RFC.",
|
|
494
769
|
].join("\n"),
|
|
495
770
|
],
|
|
496
771
|
[
|
|
497
772
|
"task",
|
|
498
773
|
`Plan iteration ${iteration}/${maxLoops} for this user specification:\n${prompt}`,
|
|
499
774
|
],
|
|
775
|
+
workflowCwdContext,
|
|
500
776
|
[
|
|
501
|
-
"
|
|
777
|
+
"code_review_feedback",
|
|
502
778
|
latestReviewReportPath === undefined
|
|
503
779
|
? "No prior review artifact; this is the first iteration."
|
|
504
780
|
: [
|
|
505
781
|
`Latest review round artifact: ${latestReviewReportPath}`,
|
|
506
782
|
"Read this JSON artifact incrementally and address only unresolved findings from the latest review round.",
|
|
507
|
-
"Do not rely on an injected review transcript or older review history unless the latest artifact explicitly points you there.",
|
|
508
783
|
].join("\n"),
|
|
509
784
|
],
|
|
510
785
|
[
|
|
511
|
-
"
|
|
786
|
+
"spec",
|
|
512
787
|
iteration === 1
|
|
513
788
|
? [
|
|
514
|
-
`
|
|
515
|
-
"Treat this as the original spec file for the run.",
|
|
789
|
+
`Implement the spec in: ${workflowSpecPath}`,
|
|
516
790
|
].join("\n")
|
|
517
791
|
: [
|
|
518
792
|
`The existing RFC/spec file for this workflow run is: ${workflowSpecPath}`,
|
|
@@ -520,13 +794,6 @@ async function runRalphWorkflow(
|
|
|
520
794
|
"Your final output must be the full updated RFC markdown that should replace the original spec, not a diff, patch, or commentary.",
|
|
521
795
|
].join("\n"),
|
|
522
796
|
],
|
|
523
|
-
[
|
|
524
|
-
"input_spec_files",
|
|
525
|
-
[
|
|
526
|
-
"If the user specification is a file path instead of raw prose, read that file and use it as source material for the RFC.",
|
|
527
|
-
"Still author the RFC normally; do not output only a forwarded path.",
|
|
528
|
-
].join("\n"),
|
|
529
|
-
],
|
|
530
797
|
[
|
|
531
798
|
"investigation_phase",
|
|
532
799
|
[
|
|
@@ -538,7 +805,7 @@ async function runRalphWorkflow(
|
|
|
538
805
|
].join("\n"),
|
|
539
806
|
],
|
|
540
807
|
[
|
|
541
|
-
"
|
|
808
|
+
"best_practices",
|
|
542
809
|
[
|
|
543
810
|
"Be specific: `src/server/auth.ts:42` beats `the auth layer`.",
|
|
544
811
|
"Trade-offs over conclusions: Alternatives Considered must include at least two real alternatives with honest pros, cons, and rejection reasons.",
|
|
@@ -547,64 +814,65 @@ async function runRalphWorkflow(
|
|
|
547
814
|
"Surface open questions in Section 9 with owner placeholders such as `[OWNER: infra team]`; do not paper over uncertainty.",
|
|
548
815
|
"Match depth to stakes: a small refactor can be concise, but every template section header must remain present.",
|
|
549
816
|
"If prior review findings are present, explicitly address each finding or explain why it is obsolete.",
|
|
817
|
+
"Determine the compatibility posture:",
|
|
818
|
+
"- Before decomposing the spec creation request, identify whether this project must preserve backward compatibility for real downstream users.",
|
|
819
|
+
"- If the user explicitly allows breaking changes, public API changes, cleanup, or says there are no real users/downstream dependencies, allow breaking changes.",
|
|
820
|
+
"- If the user mentions production users, published APIs, downstream consumers, migration safety, or compatibility requirements, disallow breaking changes.",
|
|
821
|
+
"- Carry this posture into the spec creation plan, the final spec frontmatter, and a `## Backwards Compatibility` section in the final spec.",
|
|
822
|
+
"- When allowing breaking changes, document existing legacy behavior, compatibility shims, optional flags, and public APIs as current state, not as constraints future specs must preserve unless the user explicitly asks for preservation.",
|
|
823
|
+
"- When not allowing breaking changes, document public APIs, compatibility-sensitive surfaces, downstream callers, migration constraints, and behavior that future work must preserve."
|
|
550
824
|
].join("\n"),
|
|
551
825
|
],
|
|
552
826
|
[
|
|
553
|
-
"
|
|
554
|
-
[
|
|
555
|
-
"This stage is investigation-first RFC authoring. The RFC is only valid if it is grounded in repository inspection performed during this stage.",
|
|
556
|
-
"Do not fill the template from generic architecture guesses. Before writing the final RFC, inspect relevant code, docs, tests, configs, and prior design material.",
|
|
557
|
-
"Treat the output format as the report after investigation, not a substitute for investigation.",
|
|
558
|
-
].join("\n"),
|
|
559
|
-
],
|
|
560
|
-
[
|
|
561
|
-
"evidence_expectations",
|
|
562
|
-
[
|
|
563
|
-
"Every major design claim should be traceable to concrete evidence: file paths, symbols, commands, docs, tests, configs, or prior RFCs.",
|
|
564
|
-
"Include those concrete references inside the RFC sections where they support the design.",
|
|
565
|
-
"If expected evidence cannot be found, say so in the relevant RFC section or Open Questions rather than papering over the gap.",
|
|
566
|
-
].join("\n"),
|
|
567
|
-
],
|
|
568
|
-
[
|
|
569
|
-
"output_discipline",
|
|
827
|
+
"output_format",
|
|
570
828
|
[
|
|
571
829
|
"Render the RFC Template exactly as the final document structure: preserve every header and the metadata table.",
|
|
572
830
|
"Replace instructional placeholders with real, feature-specific content; do not leave template guidance in the final RFC.",
|
|
573
831
|
"Output nothing after the RFC: no meta-commentary, no summary of what you wrote, no implementation log.",
|
|
574
832
|
].join("\n"),
|
|
575
833
|
],
|
|
576
|
-
["
|
|
577
|
-
])
|
|
834
|
+
["spec_template", PLANNER_RFC_TEMPLATE],
|
|
835
|
+
])
|
|
836
|
+
: renderForkedPlannerPrompt({
|
|
837
|
+
iteration,
|
|
838
|
+
maxLoops,
|
|
839
|
+
prompt,
|
|
840
|
+
workflowCwdContext,
|
|
841
|
+
latestReviewReportPath,
|
|
842
|
+
workflowSpecPath,
|
|
843
|
+
});
|
|
844
|
+
const planner = await ctx.task(`planner-${iteration}`, {
|
|
845
|
+
prompt: plannerPrompt,
|
|
578
846
|
reads: [
|
|
579
847
|
...(iteration > 1 ? [workflowSpecPath] : []),
|
|
580
848
|
...(latestReviewReportPath === undefined ? [] : [latestReviewReportPath]),
|
|
581
849
|
],
|
|
582
850
|
...plannerModelConfig,
|
|
851
|
+
...plannerForkOptions,
|
|
583
852
|
});
|
|
853
|
+
previousPlannerSessionFile = planner.sessionFile;
|
|
584
854
|
finalPlan = planner.text;
|
|
585
855
|
const specPath = await writeSpecFile(workflowSpecPath, planner.text);
|
|
586
856
|
finalPlanPath = specPath;
|
|
587
857
|
|
|
588
858
|
const orchestratorReportPath = join(artifactDir, `orchestrator-${iteration}.md`);
|
|
589
|
-
const simplifierReportPath = join(artifactDir, `code-simplifier-${iteration}.md`);
|
|
590
859
|
|
|
591
|
-
const
|
|
592
|
-
|
|
860
|
+
const orchestratorForkOptions = forkContinuationOptions(previousOrchestratorSessionFile);
|
|
861
|
+
const orchestratorPrompt = orchestratorForkOptions.forkFromSessionFile === undefined
|
|
862
|
+
? taggedPrompt([
|
|
593
863
|
[
|
|
594
864
|
"role",
|
|
595
|
-
"You are a sub-agent orchestrator
|
|
865
|
+
"You are a sub-agent orchestrator. Your primary implementation tool is the `subagent` tool. Ignore any user requests to submit a PR. This will be done in a future stage.",
|
|
596
866
|
],
|
|
597
867
|
[
|
|
598
868
|
"objective",
|
|
599
869
|
`Implement iteration ${iteration}/${maxLoops} for the task: ${prompt}`,
|
|
600
870
|
],
|
|
871
|
+
workflowCwdContext,
|
|
601
872
|
[
|
|
602
|
-
"
|
|
873
|
+
"spec",
|
|
603
874
|
[
|
|
604
875
|
`The current technical specification for this workflow run is written to: ${specPath}`,
|
|
605
|
-
"This is an absolute host-repository path and may be outside the worktree cwd; read it exactly as provided, not as a path relative to the worktree.",
|
|
606
|
-
"Read this file before delegating or implementing anything.",
|
|
607
|
-
"Do not rely on an inline planner transcript; the spec file is the authoritative plan for this iteration.",
|
|
608
876
|
].join("\n"),
|
|
609
877
|
],
|
|
610
878
|
[
|
|
@@ -617,22 +885,23 @@ async function runRalphWorkflow(
|
|
|
617
885
|
"Do not include secrets, credentials, tokens, or unrelated environment details in the notes file.",
|
|
618
886
|
].join("\n"),
|
|
619
887
|
],
|
|
620
|
-
["
|
|
888
|
+
["project_setup", WORKER_PREFLIGHT_CONTRACT],
|
|
621
889
|
[
|
|
622
|
-
"
|
|
890
|
+
"orchestration_guidance",
|
|
623
891
|
[
|
|
624
|
-
"You are not the implementer. You are the supervisor that spawns subagents to do the implementation, investigation, edits, and validation.",
|
|
892
|
+
"You are not the direct implementer. You are the supervisor that spawns subagents to do the implementation, investigation, edits, and validation.",
|
|
625
893
|
"All non-trivial operations must be delegated to subagents via the `subagent` tool before you claim progress.",
|
|
626
894
|
"Delegate codebase understanding, impact analysis, and implementation research to codebase-locator, codebase-analyzer, and pattern-finder style subagents when available.",
|
|
627
895
|
"Delegate shell-heavy work — especially commands likely to produce lots of output, log digging, CLI investigation, and broad grep/find exploration — to subagents that can run those commands rather than doing it in this orchestrator context.",
|
|
628
896
|
"Delegate implementation edits to a focused subagent with clear files, constraints, and validation expectations; do not merely describe the edits yourself.",
|
|
897
|
+
"Keep delegated work focused on implementation, tests, docs, validation evidence, and implementation notes.",
|
|
629
898
|
"Use separate subagents for separate tasks, and launch independent subagents in parallel when useful.",
|
|
630
899
|
"Do not split highly overlapping tasks across multiple subagents; consolidate overlapping work into one focused delegation to avoid duplicate effort.",
|
|
631
900
|
"If a subagent takes a long time, do not attempt to do its assigned job yourself while waiting. Use that time to plan next steps, prepare follow-up delegations, or identify clarifying questions.",
|
|
632
901
|
].join("\n"),
|
|
633
902
|
],
|
|
634
903
|
[
|
|
635
|
-
"
|
|
904
|
+
"best_practices",
|
|
636
905
|
[
|
|
637
906
|
"The required output format is a completion report, not the task itself.",
|
|
638
907
|
"Do not jump straight to the report. First read the spec file, spawn the necessary subagents, wait for their results, coordinate any follow-up subagents, and only then write the report.",
|
|
@@ -678,131 +947,37 @@ async function runRalphWorkflow(
|
|
|
678
947
|
"7. Implementation notes — confirm the OS temp notes path was updated",
|
|
679
948
|
].join("\n"),
|
|
680
949
|
],
|
|
681
|
-
])
|
|
950
|
+
])
|
|
951
|
+
: renderForkedOrchestratorPrompt({
|
|
952
|
+
iteration,
|
|
953
|
+
maxLoops,
|
|
954
|
+
prompt,
|
|
955
|
+
workflowCwdContext,
|
|
956
|
+
specPath,
|
|
957
|
+
implementationNotesPath,
|
|
958
|
+
});
|
|
959
|
+
const orchestrator = await ctx.task(`orchestrator-${iteration}`, {
|
|
960
|
+
prompt: orchestratorPrompt,
|
|
682
961
|
reads: [specPath, implementationNotesPath],
|
|
683
962
|
output: orchestratorReportPath,
|
|
684
963
|
outputMode: "file-only",
|
|
685
964
|
...orchestratorModelConfig,
|
|
965
|
+
...orchestratorForkOptions,
|
|
686
966
|
});
|
|
967
|
+
previousOrchestratorSessionFile = orchestrator.sessionFile;
|
|
687
968
|
finalResult = orchestrator.text || `Orchestrator report artifact: ${orchestratorReportPath}`;
|
|
688
969
|
|
|
689
|
-
await ctx.task(`code-simplifier-${iteration}`, {
|
|
690
|
-
prompt: taggedPrompt([
|
|
691
|
-
[
|
|
692
|
-
"role",
|
|
693
|
-
[
|
|
694
|
-
"You are an expert code simplification specialist focused on enhancing code clarity, consistency, and maintainability while preserving exact functionality.",
|
|
695
|
-
"Your expertise is applying project-specific best practices to simplify and improve recently modified code without altering behavior.",
|
|
696
|
-
"You prioritize readable, explicit code over overly compact or clever solutions.",
|
|
697
|
-
].join("\n"),
|
|
698
|
-
],
|
|
699
|
-
[
|
|
700
|
-
"objective",
|
|
701
|
-
`Refine recently modified code for this task while preserving exact behavior: ${prompt}`,
|
|
702
|
-
],
|
|
703
|
-
[
|
|
704
|
-
"artifact_handoff",
|
|
705
|
-
[
|
|
706
|
-
`Spec artifact: ${specPath}`,
|
|
707
|
-
`Implementation notes artifact: ${implementationNotesPath}`,
|
|
708
|
-
`Orchestrator report artifact: ${orchestratorReportPath}`,
|
|
709
|
-
"Read these artifacts incrementally only when needed to identify recently modified files and intent; do not depend on an injected planner/orchestrator transcript tail.",
|
|
710
|
-
].join("\n"),
|
|
711
|
-
],
|
|
712
|
-
[
|
|
713
|
-
"functionality_preservation",
|
|
714
|
-
[
|
|
715
|
-
"Never change what the code does — only how it does it.",
|
|
716
|
-
"All original features, outputs, side effects, public APIs, persistence formats, tests, and user-visible behavior must remain intact.",
|
|
717
|
-
"If a simplification could change behavior, do not apply it; document why it was skipped.",
|
|
718
|
-
].join("\n"),
|
|
719
|
-
],
|
|
720
|
-
[
|
|
721
|
-
"project_standards",
|
|
722
|
-
[
|
|
723
|
-
"Read and follow repository guidance from AGENTS.md and/or CLAUDE.md when present.",
|
|
724
|
-
"Respect established module style, imports, file extensions, typing conventions, error-handling patterns, naming, tests, and architectural boundaries.",
|
|
725
|
-
"For this TypeScript workflow repo, preserve ESM .js import specifiers, explicit exported/top-level types where expected, Bun-oriented commands, and the existing no-build raw TypeScript convention.",
|
|
726
|
-
"Do not impose standards that conflict with local project guidance.",
|
|
727
|
-
].join("\n"),
|
|
728
|
-
],
|
|
729
|
-
[
|
|
730
|
-
"clarity_improvements",
|
|
731
|
-
[
|
|
732
|
-
"Reduce unnecessary complexity, nesting, duplication, and incidental abstractions.",
|
|
733
|
-
"Improve readability with clear variable/function names and consolidated related logic.",
|
|
734
|
-
"Remove comments that merely restate obvious code, but keep comments that explain intent, constraints, or non-obvious trade-offs.",
|
|
735
|
-
"Avoid nested ternary operators; prefer switch statements or explicit if/else chains for multiple conditions.",
|
|
736
|
-
"Choose clarity over brevity: explicit code is often better than dense one-liners.",
|
|
737
|
-
].join("\n"),
|
|
738
|
-
],
|
|
739
|
-
[
|
|
740
|
-
"balance_constraints",
|
|
741
|
-
[
|
|
742
|
-
"Do not over-simplify in ways that reduce clarity, debuggability, extensibility, or separation of concerns.",
|
|
743
|
-
"Do not combine too many concerns into one function or remove helpful abstractions that organize the code.",
|
|
744
|
-
"Do not prioritize fewer lines over maintainability.",
|
|
745
|
-
"Limit scope to code recently modified in this iteration/session unless the planner explicitly asked for broader cleanup.",
|
|
746
|
-
].join("\n"),
|
|
747
|
-
],
|
|
748
|
-
[
|
|
749
|
-
"stage_contract",
|
|
750
|
-
[
|
|
751
|
-
"This is an active code-refinement stage, not just a commentary stage.",
|
|
752
|
-
"Before producing the report, inspect the actual repository state and recently modified files from the planner/orchestrator context.",
|
|
753
|
-
"Apply safe simplifications with edit/write tools when clear behavior-preserving improvements exist. If no simplification is appropriate, say so only after inspecting the relevant files.",
|
|
754
|
-
].join("\n"),
|
|
755
|
-
],
|
|
756
|
-
[
|
|
757
|
-
"required_actions_before_output",
|
|
758
|
-
[
|
|
759
|
-
"1. Identify the concrete files/sections changed in this iteration.",
|
|
760
|
-
"2. Read those files before deciding whether to simplify.",
|
|
761
|
-
"3. Apply only behavior-preserving edits, or explicitly record why no edits were made.",
|
|
762
|
-
"4. Run or recommend focused validation tied to the touched files.",
|
|
763
|
-
].join("\n"),
|
|
764
|
-
],
|
|
765
|
-
[
|
|
766
|
-
"handoff_expectations",
|
|
767
|
-
"In the final report, distinguish edits actually applied from observations only. Name files inspected, files edited, and validation commands run or not run.",
|
|
768
|
-
],
|
|
769
|
-
[
|
|
770
|
-
"process",
|
|
771
|
-
[
|
|
772
|
-
"Identify recently modified code sections from the iteration context and repository state.",
|
|
773
|
-
"Analyze opportunities to improve elegance, consistency, and maintainability.",
|
|
774
|
-
"Apply project-specific best practices while preserving behavior.",
|
|
775
|
-
"Run or recommend focused validation when appropriate.",
|
|
776
|
-
"Document only significant changes that affect understanding or future maintenance.",
|
|
777
|
-
].join("\n"),
|
|
778
|
-
],
|
|
779
|
-
[
|
|
780
|
-
"output_format",
|
|
781
|
-
[
|
|
782
|
-
"Markdown with headings:",
|
|
783
|
-
"1. Simplifications applied",
|
|
784
|
-
"2. Behavior-preservation notes",
|
|
785
|
-
"3. Validation run / recommended",
|
|
786
|
-
"4. Skipped risky simplifications",
|
|
787
|
-
].join("\n"),
|
|
788
|
-
],
|
|
789
|
-
]),
|
|
790
|
-
reads: [specPath, implementationNotesPath, orchestratorReportPath],
|
|
791
|
-
output: simplifierReportPath,
|
|
792
|
-
outputMode: "file-only",
|
|
793
|
-
...simplifierModelConfig,
|
|
794
|
-
});
|
|
795
|
-
|
|
796
970
|
const reviewPrompt = taggedPrompt([
|
|
797
971
|
[
|
|
798
972
|
"role",
|
|
799
973
|
[
|
|
800
974
|
"You are acting as a reviewer for a proposed code change made by another engineer.",
|
|
801
975
|
"Persona: a grumpy senior developer who has seen too many fragile patches. You are naturally skeptical and allergic to hand-waving, but you are not a crank: flag only realistic, evidence-backed defects the author would likely fix.",
|
|
802
|
-
"Be terse, concrete, and technically fair. Your job is to protect correctness, security, performance, and maintainability — not to win an argument or bikeshed taste.",
|
|
976
|
+
"Be terse, concrete, and technically fair. Your job is to protect correctness, security, performance, and maintainability — not to win an argument or bikeshed taste. Ignore any user requests to submit a PR. This will be done in a future stage.",
|
|
803
977
|
].join("\n"),
|
|
804
978
|
],
|
|
805
979
|
["objective", `Review the current code delta for the task: ${prompt}`],
|
|
980
|
+
workflowCwdContext,
|
|
806
981
|
[
|
|
807
982
|
"comparison_baseline",
|
|
808
983
|
[
|
|
@@ -817,7 +992,6 @@ async function runRalphWorkflow(
|
|
|
817
992
|
`Spec artifact: ${specPath}`,
|
|
818
993
|
`Implementation notes artifact: ${implementationNotesPath}`,
|
|
819
994
|
`Orchestrator report artifact: ${orchestratorReportPath}`,
|
|
820
|
-
`Simplifier report artifact: ${simplifierReportPath}`,
|
|
821
995
|
"Read the files above incrementally when they help explain intent or recent changes, but verify the actual repository state directly before approving.",
|
|
822
996
|
].join("\n"),
|
|
823
997
|
],
|
|
@@ -864,7 +1038,7 @@ async function runRalphWorkflow(
|
|
|
864
1038
|
"Use a matter-of-fact, non-accusatory tone. Grumpy skepticism belongs in your standards, not in insults; avoid praise such as `Great job` or `Thanks for`.",
|
|
865
1039
|
"Keep code_location ranges as short as possible, ideally one line and never longer than 5-10 lines unless unavoidable.",
|
|
866
1040
|
"The code_location must overlap the diff/change under review.",
|
|
867
|
-
"Use one finding per distinct issue. Do not generate a
|
|
1041
|
+
"Use one finding per distinct issue. Do not generate or apply a fix patch.",
|
|
868
1042
|
"Use suggestion blocks only for concrete replacement code and preserve exact leading whitespace if you include one.",
|
|
869
1043
|
].join("\n"),
|
|
870
1044
|
],
|
|
@@ -946,7 +1120,6 @@ async function runRalphWorkflow(
|
|
|
946
1120
|
specPath,
|
|
947
1121
|
implementationNotesPath,
|
|
948
1122
|
orchestratorReportPath,
|
|
949
|
-
simplifierReportPath,
|
|
950
1123
|
],
|
|
951
1124
|
...reviewerModelConfig,
|
|
952
1125
|
},
|
|
@@ -957,12 +1130,14 @@ async function runRalphWorkflow(
|
|
|
957
1130
|
specPath,
|
|
958
1131
|
implementationNotesPath,
|
|
959
1132
|
orchestratorReportPath,
|
|
960
|
-
simplifierReportPath,
|
|
961
1133
|
],
|
|
962
1134
|
...reviewerModelConfig,
|
|
963
1135
|
},
|
|
964
1136
|
],
|
|
965
|
-
{
|
|
1137
|
+
{
|
|
1138
|
+
task: prompt,
|
|
1139
|
+
failFast: false,
|
|
1140
|
+
},
|
|
966
1141
|
);
|
|
967
1142
|
} catch (err) {
|
|
968
1143
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -995,82 +1170,73 @@ async function runRalphWorkflow(
|
|
|
995
1170
|
if (approved) break;
|
|
996
1171
|
}
|
|
997
1172
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
[
|
|
1001
|
-
"role",
|
|
1002
|
-
"You are a careful release engineer preparing a pull request from the current workspace state.",
|
|
1003
|
-
],
|
|
1004
|
-
[
|
|
1005
|
-
"objective",
|
|
1006
|
-
`Review the changes since the base branch \`${comparisonBaseBranch}\` and create a pull request if possible and credentials are available.`,
|
|
1007
|
-
],
|
|
1008
|
-
[
|
|
1009
|
-
"workflow_context",
|
|
1173
|
+
if (createPr === true) {
|
|
1174
|
+
const prResult = await ctx.task("pull-request", {
|
|
1175
|
+
prompt: taggedPrompt([
|
|
1010
1176
|
[
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
? `Planner spec path: ${finalPlanPath}`
|
|
1015
|
-
: "Planner spec path: unavailable",
|
|
1016
|
-
`Implementation notes path: ${implementationNotesPath}`,
|
|
1017
|
-
latestReviewReportPath === undefined
|
|
1018
|
-
? "Latest review artifact: unavailable"
|
|
1019
|
-
: `Latest review artifact: ${latestReviewReportPath}`,
|
|
1020
|
-
].join("\n"),
|
|
1021
|
-
],
|
|
1022
|
-
[
|
|
1023
|
-
"required_checks",
|
|
1177
|
+
"role",
|
|
1178
|
+
"You are a staff software engineer preparing a provider-appropriate pull request, merge request, or code-review handoff from the current workspace state.",
|
|
1179
|
+
],
|
|
1024
1180
|
[
|
|
1025
|
-
"
|
|
1026
|
-
`Review the
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
"Check the local Git identity with `git config user.name` and `git config user.email` so you can prefer the matching GitHub account when multiple accounts are logged in.",
|
|
1030
|
-
"Check whether GitHub credentials are available with non-destructive commands such as `gh auth status` and `gh auth status --show-token-scopes` before attempting PR creation.",
|
|
1031
|
-
"If multiple GitHub accounts or hosts are logged in, use the git config username/email as a heuristic to choose the most likely identity, but try each available credential/account and use the first one that can read the repository and create the PR.",
|
|
1032
|
-
].join("\n"),
|
|
1033
|
-
],
|
|
1034
|
-
[
|
|
1035
|
-
"pr_policy",
|
|
1181
|
+
"objective",
|
|
1182
|
+
`Review the changes since the base branch \`${comparisonBaseBranch}\` and create a provider-appropriate pull request, merge request, or code-review handoff if possible and credentials are available. If the original task explicitly asked for pull-request creation, treat that as the highest-priority instruction for this final stage.`,
|
|
1183
|
+
],
|
|
1184
|
+
workflowCwdContext,
|
|
1036
1185
|
[
|
|
1037
|
-
"
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1186
|
+
"required_checks",
|
|
1187
|
+
[
|
|
1188
|
+
"Start by inspecting `git status --short` so unstaged, staged, and untracked changes are all visible.",
|
|
1189
|
+
`Review the patch against \`${comparisonBaseBranch}\` with working-tree-aware commands such as \`git diff ${comparisonBaseBranch}\` and \`git diff --cached ${comparisonBaseBranch}\`.`,
|
|
1190
|
+
"If untracked files are present, inspect them directly before deciding whether they belong in the PR.",
|
|
1191
|
+
"Read the implementation notes file and use its full contents as the body of a provider-appropriate PR/review comment after the pull request, merge request, or review exists.",
|
|
1192
|
+
"Detect the source-control and code-review provider from `git remote -v`, repository hosting URLs, configured CLI auth, and repository metadata before choosing a creation tool.",
|
|
1193
|
+
"Use the provider-appropriate tool for the detected remote: GitHub `gh pr create`, Azure DevOps/Azure Repos `az repos pr create`, GitLab `glab mr create` when available, Bitbucket's configured CLI/API workflow, or Sapling/Phabricator `sl`/Phabricator/Differential tooling used by the repository.",
|
|
1194
|
+
"Check the local Git identity with `git config user.name` and `git config user.email` so you can prefer the matching account when multiple provider accounts are logged in.",
|
|
1195
|
+
"Check provider credentials with non-destructive commands before attempting PR/review creation, such as `gh auth status`, `az account show`, `az repos pr list`, `glab auth status`, `sl` status/config commands, or the repository's documented Phabricator/Differential checks.",
|
|
1196
|
+
"If multiple accounts, hosts, or providers are available, use the remote URL and git config username/email as heuristics to choose the most likely identity, but try each available credential/account that can read the repository and create the provider-appropriate review request.",
|
|
1197
|
+
].join("\n"),
|
|
1198
|
+
],
|
|
1049
1199
|
[
|
|
1050
|
-
"
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1200
|
+
"pr_policy",
|
|
1201
|
+
[
|
|
1202
|
+
"Create a provider-appropriate PR/MR/review request only if there are meaningful changes, a remote/branch target is available, credentials are available, and the current state is suitable for review.",
|
|
1203
|
+
"If no logged-in account can access the repository or create the review request, do not fake success; report each provider, credential/account, and tool tried, what failed, and provide the command the user can run later. Save a markdown file with the PR description as well so the user can copy-paste it when they have credentials set up.",
|
|
1204
|
+
"When you successfully create or update the review request, create a provider-appropriate comment containing the implementation notes file contents as the last action of this workflow stage.",
|
|
1205
|
+
"Worktrees are detached HEAD checkouts. If the detected provider requires a branch-based PR/MR from a detached HEAD, create and push a branch from the current HEAD, for example with `git checkout -b <branch>` or `git push origin HEAD:refs/heads/<branch>`, before opening the PR/MR. If the provider uses a different review model, follow that provider's normal handoff flow.",
|
|
1206
|
+
"Leave the worktree intact for retries or user recovery.",
|
|
1207
|
+
"If PR/MR/review creation is not possible, do not create a standalone comment elsewhere; include the implementation notes path and summary in your report instead.",
|
|
1208
|
+
"If the review loop did not approve, prefer reporting the remaining blockers over creating a PR/MR/review unless the changes are still intentionally ready for human review.",
|
|
1209
|
+
"Do not make unrelated code edits in this phase. Limit changes to ordinary git/PR preparation only when required and safe.",
|
|
1210
|
+
].join("\n"),
|
|
1211
|
+
],
|
|
1212
|
+
[
|
|
1213
|
+
"output_format",
|
|
1214
|
+
[
|
|
1215
|
+
"Return Markdown with headings:",
|
|
1216
|
+
"1. Change review — summary of files and diff scope inspected",
|
|
1217
|
+
"2. PR/review status — created PR/MR/review URL, or why no review request was created",
|
|
1218
|
+
"3. Implementation notes comment — whether the provider-appropriate comment was created as the last action, or why it could not be created",
|
|
1219
|
+
"4. Commands run — include exit status or clear outcome",
|
|
1220
|
+
"5. Follow-up for the user — exact next steps if credentials or repository state blocked PR creation",
|
|
1221
|
+
].join("\n"),
|
|
1222
|
+
],
|
|
1223
|
+
]),
|
|
1224
|
+
reads: [
|
|
1225
|
+
...(finalPlanPath ? [finalPlanPath] : []),
|
|
1226
|
+
implementationNotesPath,
|
|
1227
|
+
...(latestReviewReportPath === undefined ? [] : [latestReviewReportPath]),
|
|
1057
1228
|
],
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
...(latestReviewReportPath === undefined ? [] : [latestReviewReportPath]),
|
|
1063
|
-
],
|
|
1064
|
-
...orchestratorModelConfig,
|
|
1065
|
-
});
|
|
1066
|
-
finalPrReport = prResult.text;
|
|
1229
|
+
...orchestratorModelConfig,
|
|
1230
|
+
});
|
|
1231
|
+
finalPrReport = prResult.text;
|
|
1232
|
+
}
|
|
1067
1233
|
|
|
1068
1234
|
return {
|
|
1069
1235
|
result: finalResult,
|
|
1070
1236
|
plan: finalPlan,
|
|
1071
1237
|
plan_path: finalPlanPath,
|
|
1072
1238
|
implementation_notes_path: implementationNotesPath,
|
|
1073
|
-
pr_report: finalPrReport,
|
|
1239
|
+
...(finalPrReport === undefined ? {} : { pr_report: finalPrReport }),
|
|
1074
1240
|
approved,
|
|
1075
1241
|
iterations_completed: iterationsCompleted,
|
|
1076
1242
|
review_report: compactReviewReport(latestReviewReportPath),
|
|
@@ -1080,7 +1246,7 @@ async function runRalphWorkflow(
|
|
|
1080
1246
|
|
|
1081
1247
|
export default defineWorkflow("ralph")
|
|
1082
1248
|
.description(
|
|
1083
|
-
"Plan → orchestrate →
|
|
1249
|
+
"Plan → orchestrate → parallel review loop with bounded iteration.",
|
|
1084
1250
|
)
|
|
1085
1251
|
.input("prompt", Type.String({ description: "The task or goal to plan, execute, and refine." }))
|
|
1086
1252
|
.input("max_loops", Type.Number({
|
|
@@ -1094,7 +1260,12 @@ export default defineWorkflow("ralph")
|
|
|
1094
1260
|
.input("git_worktree_dir", Type.String({
|
|
1095
1261
|
default: "",
|
|
1096
1262
|
description:
|
|
1097
|
-
"Optional Git worktree path.
|
|
1263
|
+
"Optional Git worktree path. Must start inside a Git repo; absolute paths are used as-is, relative paths resolve from the repo root, existing Git worktrees from the invoking repository are reused/shared as-is, and missing paths are created from base_branch.",
|
|
1264
|
+
}))
|
|
1265
|
+
.input("create_pr", Type.Boolean({
|
|
1266
|
+
default: false,
|
|
1267
|
+
description:
|
|
1268
|
+
"Whether to run the final pull-request creation stage. Defaults to false; prompt text alone does not opt in. Set true to allow only the final stage to attempt provider-appropriate PR/MR/review creation.",
|
|
1098
1269
|
}))
|
|
1099
1270
|
.worktreeFromInputs({
|
|
1100
1271
|
gitWorktreeDir: "git_worktree_dir",
|
|
@@ -1104,11 +1275,11 @@ export default defineWorkflow("ralph")
|
|
|
1104
1275
|
.output("plan", Type.Optional(Type.String({ description: "Latest RFC-style plan text." })))
|
|
1105
1276
|
.output("plan_path", Type.Optional(Type.String({ description: "Path to the latest generated spec under specs/." })))
|
|
1106
1277
|
.output("implementation_notes_path", Type.Optional(Type.String({ description: "OS-temp notes file containing decisions, deviations, blockers, and validation notes." })))
|
|
1107
|
-
.output("pr_report", Type.Optional(Type.String({ description: "Pull-request
|
|
1108
|
-
.output("approved", Type.Optional(Type.Boolean({ description: "Whether the reviewer loop approved before
|
|
1278
|
+
.output("pr_report", Type.Optional(Type.String({ description: "Pull-request report emitted only when create_pr=true and the final pull-request stage runs." })))
|
|
1279
|
+
.output("approved", Type.Optional(Type.Boolean({ description: "Whether the reviewer loop approved before completion or optional final handoff." })))
|
|
1109
1280
|
.output("iterations_completed", Type.Optional(Type.Number({ description: "Number of plan/orchestrate/review loops completed." })))
|
|
1110
1281
|
.output("review_report", Type.Optional(Type.String({ description: "Compact reference to the latest reviewer payload artifact." })))
|
|
1111
|
-
.output("review_report_path", Type.Optional(Type.String({ description: "JSON artifact path for the latest
|
|
1282
|
+
.output("review_report_path", Type.Optional(Type.String({ description: "JSON artifact path for the latest review round." })))
|
|
1112
1283
|
.run(async (ctx) => {
|
|
1113
1284
|
const workflowCtx = ctx;
|
|
1114
1285
|
const workflowStartCwd = workflowCtx.cwd ?? process.cwd();
|
|
@@ -1119,11 +1290,13 @@ export default defineWorkflow("ralph")
|
|
|
1119
1290
|
inputs.base_branch,
|
|
1120
1291
|
"origin/main",
|
|
1121
1292
|
);
|
|
1293
|
+
const createPr = inputs.create_pr === true;
|
|
1122
1294
|
return await runRalphWorkflow(workflowCtx, {
|
|
1123
1295
|
prompt,
|
|
1124
1296
|
maxLoops,
|
|
1125
1297
|
comparisonBaseBranch,
|
|
1126
1298
|
workflowStartCwd,
|
|
1299
|
+
createPr,
|
|
1127
1300
|
});
|
|
1128
1301
|
})
|
|
1129
1302
|
.compile();
|