@bastani/atomic 0.8.29-alpha.2 → 0.8.29-alpha.4
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 +14 -6
- package/dist/builtin/cursor/package.json +2 -2
- package/dist/builtin/intercom/CHANGELOG.md +1 -1
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/CHANGELOG.md +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +4 -4
- package/dist/builtin/subagents/README.md +4 -4
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/index.ts +14 -0
- package/dist/builtin/subagents/src/extension/schemas.ts +1 -1
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +1 -6
- package/dist/builtin/subagents/src/runs/foreground/execution.ts +1 -6
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +0 -1
- package/dist/builtin/subagents/src/runs/shared/pi-args.ts +0 -1
- package/dist/builtin/subagents/src/runs/shared/structured-output.ts +16 -285
- package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +1 -9
- package/dist/builtin/subagents/src/shared/types.ts +4 -4
- package/dist/builtin/subagents/src/slash/saved-chain-mapping.ts +3 -18
- package/dist/builtin/web-access/CHANGELOG.md +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +12 -5
- package/dist/builtin/workflows/README.md +10 -8
- package/dist/builtin/workflows/builtin/deep-research-codebase.ts +9 -49
- package/dist/builtin/workflows/builtin/goal.ts +68 -155
- package/dist/builtin/workflows/builtin/index.d.ts +2 -0
- package/dist/builtin/workflows/builtin/open-claude-design.ts +42 -110
- package/dist/builtin/workflows/builtin/ralph.d.ts +2 -0
- package/dist/builtin/workflows/builtin/ralph.ts +235 -565
- package/dist/builtin/workflows/builtin/shared-prompts.ts +7 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.ts +17 -0
- package/dist/builtin/workflows/src/extension/wiring.ts +55 -8
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +2 -29
- package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +1 -5
- package/dist/builtin/workflows/src/shared/authoring-contract.d.ts +1 -1
- package/dist/builtin/workflows/src/shared/types.ts +1 -1
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +7 -7
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/resource-loader.d.ts +2 -2
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +3 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +2 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +0 -36
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +1 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/structured-output.d.ts +7 -18
- package/dist/core/tools/structured-output.d.ts.map +1 -1
- package/dist/core/tools/structured-output.js +9 -89
- package/dist/core/tools/structured-output.js.map +1 -1
- package/dist/core/tools/todos.d.ts +1 -0
- package/dist/core/tools/todos.d.ts.map +1 -1
- package/dist/core/tools/todos.js +4 -0
- package/dist/core/tools/todos.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/docs/extensions.md +1 -1
- package/docs/quickstart.md +3 -3
- package/docs/sdk.md +1 -1
- package/docs/subagents.md +4 -6
- package/docs/usage.md +1 -1
- package/docs/workflows.md +23 -19
- package/package.json +2 -2
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Builtin workflow: ralph
|
|
3
3
|
*
|
|
4
4
|
* Re-implements the Atomic SDK Ralph design with the local workflow task
|
|
5
|
-
* primitives: bounded
|
|
6
|
-
* Reviewer passes fan out with ctx.parallel(); each iteration feeds
|
|
7
|
-
* findings into the next
|
|
5
|
+
* primitives: bounded prompt-engineering → research → orchestrate → review iterations.
|
|
6
|
+
* Reviewer passes fan out with ctx.parallel(); each iteration feeds unresolved
|
|
7
|
+
* findings into the next research pass with ctx.task().
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { mkdir, mkdtemp, writeFile } from "node:fs/promises";
|
|
@@ -16,12 +16,12 @@ import type {
|
|
|
16
16
|
WorkflowRunContext,
|
|
17
17
|
WorkflowTaskResult,
|
|
18
18
|
} from "../src/shared/types.js";
|
|
19
|
-
import { WORKER_PREFLIGHT_CONTRACT } from "./shared-prompts.js";
|
|
19
|
+
import { E2E_VERIFICATION_GUIDANCE, WORKER_PREFLIGHT_CONTRACT } from "./shared-prompts.js";
|
|
20
20
|
|
|
21
21
|
const DEFAULT_MAX_LOOPS = 10;
|
|
22
|
-
const
|
|
22
|
+
const DEFAULT_RESEARCH_DIR = "research";
|
|
23
23
|
const IMPLEMENTATION_NOTES_FILENAME = "implementation-notes.md";
|
|
24
|
-
const
|
|
24
|
+
const MAX_RESEARCH_SLUG_LENGTH = 80;
|
|
25
25
|
|
|
26
26
|
type ReviewFinding = {
|
|
27
27
|
readonly title: string;
|
|
@@ -56,333 +56,61 @@ type ReviewDecision = {
|
|
|
56
56
|
readonly reviewer_error?: ReviewerError | null;
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
additionalProperties: false,
|
|
75
|
-
required: ["title", "body", "confidence_score", "code_location"],
|
|
76
|
-
properties: {
|
|
77
|
-
title: { type: "string" },
|
|
78
|
-
body: { type: "string" },
|
|
79
|
-
confidence_score: { type: "number", minimum: 0, maximum: 1 },
|
|
80
|
-
priority: { type: ["integer", "null"], minimum: 0, maximum: 3 },
|
|
81
|
-
code_location: {
|
|
82
|
-
type: "object",
|
|
83
|
-
additionalProperties: false,
|
|
84
|
-
required: ["absolute_file_path", "line_range"],
|
|
85
|
-
properties: {
|
|
86
|
-
absolute_file_path: { type: "string" },
|
|
87
|
-
line_range: {
|
|
88
|
-
type: "object",
|
|
89
|
-
additionalProperties: false,
|
|
90
|
-
required: ["start", "end"],
|
|
91
|
-
properties: {
|
|
92
|
-
start: { type: "integer", minimum: 1 },
|
|
93
|
-
end: { type: "integer", minimum: 1 },
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
},
|
|
59
|
+
const reviewFindingSchema = Type.Object(
|
|
60
|
+
{
|
|
61
|
+
title: Type.String(),
|
|
62
|
+
body: Type.String(),
|
|
63
|
+
confidence_score: Type.Number({ minimum: 0, maximum: 1 }),
|
|
64
|
+
priority: Type.Optional(
|
|
65
|
+
Type.Union([Type.Integer({ minimum: 0, maximum: 3 }), Type.Null()]),
|
|
66
|
+
),
|
|
67
|
+
code_location: Type.Object(
|
|
68
|
+
{
|
|
69
|
+
absolute_file_path: Type.String(),
|
|
70
|
+
line_range: Type.Object(
|
|
71
|
+
{
|
|
72
|
+
start: Type.Integer({ minimum: 1 }),
|
|
73
|
+
end: Type.Integer({ minimum: 1 }),
|
|
97
74
|
},
|
|
98
|
-
|
|
75
|
+
{ additionalProperties: false },
|
|
76
|
+
),
|
|
99
77
|
},
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
type: "string",
|
|
103
|
-
enum: ["patch is correct", "patch is incorrect"],
|
|
104
|
-
},
|
|
105
|
-
overall_explanation: { type: "string" },
|
|
106
|
-
overall_confidence_score: { type: "number", minimum: 0, maximum: 1 },
|
|
107
|
-
stop_review_loop: { type: "boolean" },
|
|
108
|
-
reviewer_error: {
|
|
109
|
-
anyOf: [
|
|
110
|
-
{ type: "null" },
|
|
111
|
-
{
|
|
112
|
-
type: "object",
|
|
113
|
-
additionalProperties: false,
|
|
114
|
-
required: ["kind", "message", "attempted_recovery"],
|
|
115
|
-
properties: {
|
|
116
|
-
kind: {
|
|
117
|
-
type: "string",
|
|
118
|
-
enum: [
|
|
119
|
-
"validation_unavailable",
|
|
120
|
-
"dependency_unavailable",
|
|
121
|
-
"tool_failure",
|
|
122
|
-
"reviewer_failure",
|
|
123
|
-
],
|
|
124
|
-
},
|
|
125
|
-
message: { type: "string" },
|
|
126
|
-
attempted_recovery: { type: "string" },
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
},
|
|
78
|
+
{ additionalProperties: false },
|
|
79
|
+
),
|
|
131
80
|
},
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
async execute(_toolCallId: string, params: ReviewDecision) {
|
|
146
|
-
return {
|
|
147
|
-
content: [
|
|
148
|
-
{ type: "text" as const, text: JSON.stringify(params, null, 2) },
|
|
149
|
-
],
|
|
150
|
-
details: params,
|
|
151
|
-
terminate: true,
|
|
152
|
-
};
|
|
81
|
+
{ additionalProperties: false },
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const reviewerErrorSchema = Type.Object(
|
|
85
|
+
{
|
|
86
|
+
kind: Type.Union([
|
|
87
|
+
Type.Literal("validation_unavailable"),
|
|
88
|
+
Type.Literal("dependency_unavailable"),
|
|
89
|
+
Type.Literal("tool_failure"),
|
|
90
|
+
Type.Literal("reviewer_failure"),
|
|
91
|
+
]),
|
|
92
|
+
message: Type.String(),
|
|
93
|
+
attempted_recovery: Type.String(),
|
|
153
94
|
},
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
_Instruction: Why are we doing this? Why now? Link to the Product Requirement Document (PRD) and cite the relevant \`research/\` documents._
|
|
175
|
-
|
|
176
|
-
### 2.1 Current State
|
|
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
|
-
|
|
184
|
-
### 2.2 The Problem
|
|
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
|
-
|
|
192
|
-
## 3. Goals and Non-Goals
|
|
193
|
-
|
|
194
|
-
_Instruction: This is the contract / Definition of Success. Be precise._
|
|
195
|
-
|
|
196
|
-
### 3.1 Functional Goals
|
|
197
|
-
|
|
198
|
-
- [ ] Users must be able to export data in CSV format.
|
|
199
|
-
- [ ] System must support multi-tenant data isolation.
|
|
200
|
-
|
|
201
|
-
### 3.2 Non-Goals (Out of Scope)
|
|
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
|
-
|
|
209
|
-
## 4. Proposed Solution (High-Level Design)
|
|
210
|
-
|
|
211
|
-
_Instruction: The "Big Picture." Diagrams are mandatory here._
|
|
212
|
-
|
|
213
|
-
### 4.1 System Architecture Diagram
|
|
214
|
-
|
|
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
|
-
\`\`\`
|
|
243
|
-
|
|
244
|
-
### 4.2 Architectural Pattern
|
|
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
|
-
|
|
250
|
-
### 4.3 Key Components
|
|
251
|
-
|
|
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.
|
|
263
|
-
|
|
264
|
-
## 5. Detailed Design
|
|
265
|
-
|
|
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
|
|
302
|
-
|
|
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)._
|
|
304
|
-
|
|
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\`.
|
|
345
|
-
|
|
346
|
-
## 6. Alternatives Considered
|
|
347
|
-
|
|
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. |
|
|
355
|
-
|
|
356
|
-
## 7. Cross-Cutting Concerns
|
|
357
|
-
|
|
358
|
-
### 7.1 Security and Privacy
|
|
359
|
-
|
|
360
|
-
_Instruction: This is where "keep the dangerous doors few and honest" and "the airlock at the boundary" become concrete._
|
|
361
|
-
|
|
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.
|
|
367
|
-
|
|
368
|
-
## 8. Test Plan
|
|
369
|
-
|
|
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._
|
|
371
|
-
|
|
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.
|
|
377
|
-
|
|
378
|
-
## 9. Open Questions / Unresolved Issues
|
|
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();
|
|
95
|
+
{ additionalProperties: false },
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const reviewDecisionSchema = Type.Object(
|
|
99
|
+
{
|
|
100
|
+
findings: Type.Array(reviewFindingSchema),
|
|
101
|
+
overall_correctness: Type.Union([
|
|
102
|
+
Type.Literal("patch is correct"),
|
|
103
|
+
Type.Literal("patch is incorrect"),
|
|
104
|
+
]),
|
|
105
|
+
overall_explanation: Type.String(),
|
|
106
|
+
overall_confidence_score: Type.Number({ minimum: 0, maximum: 1 }),
|
|
107
|
+
stop_review_loop: Type.Boolean(),
|
|
108
|
+
reviewer_error: Type.Optional(
|
|
109
|
+
Type.Union([Type.Null(), reviewerErrorSchema]),
|
|
110
|
+
),
|
|
111
|
+
},
|
|
112
|
+
{ additionalProperties: false },
|
|
113
|
+
);
|
|
386
114
|
|
|
387
115
|
type PromptSection = readonly [tag: string, content: string];
|
|
388
116
|
|
|
@@ -427,27 +155,19 @@ function normalizeBranchInput(
|
|
|
427
155
|
return looksLikeSafeGitRef ? trimmed : fallback;
|
|
428
156
|
}
|
|
429
157
|
|
|
430
|
-
function
|
|
158
|
+
function slugifyResearchTopic(prompt: string): string {
|
|
431
159
|
const slug = prompt
|
|
432
160
|
.toLowerCase()
|
|
433
161
|
.replace(/[^a-z0-9]+/g, "-")
|
|
434
162
|
.replace(/^-+|-+$/g, "")
|
|
435
|
-
.slice(0,
|
|
163
|
+
.slice(0, MAX_RESEARCH_SLUG_LENGTH)
|
|
436
164
|
.replace(/-+$/g, "");
|
|
437
|
-
return slug.length > 0 ? slug : "
|
|
165
|
+
return slug.length > 0 ? slug : "research";
|
|
438
166
|
}
|
|
439
167
|
|
|
440
|
-
function
|
|
168
|
+
function defaultResearchPath(prompt: string, now = new Date()): string {
|
|
441
169
|
const date = now.toISOString().slice(0, 10);
|
|
442
|
-
return join(
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
async function writeSpecFile(path: string, content: string): Promise<string> {
|
|
446
|
-
await mkdir(dirname(path), { recursive: true });
|
|
447
|
-
await writeFile(path, content.endsWith("\n") ? content : `${content}\n`, {
|
|
448
|
-
encoding: "utf8",
|
|
449
|
-
});
|
|
450
|
-
return path;
|
|
170
|
+
return join(DEFAULT_RESEARCH_DIR, `${date}-${slugifyResearchTopic(prompt)}.md`);
|
|
451
171
|
}
|
|
452
172
|
|
|
453
173
|
async function createImplementationNotesFile(prompt: string): Promise<string> {
|
|
@@ -460,7 +180,7 @@ async function createImplementationNotesFile(prompt: string): Promise<string> {
|
|
|
460
180
|
"",
|
|
461
181
|
"## Running Notes",
|
|
462
182
|
"",
|
|
463
|
-
"- Record implementation decisions, deviations from the
|
|
183
|
+
"- Record implementation decisions, deviations from the research findings, tradeoffs, blockers, validation notes, and anything else the user should know.",
|
|
464
184
|
].join("\n");
|
|
465
185
|
await writeFile(notesPath, `${initialNotes}\n`, {
|
|
466
186
|
encoding: "utf8",
|
|
@@ -469,23 +189,8 @@ async function createImplementationNotesFile(prompt: string): Promise<string> {
|
|
|
469
189
|
return notesPath;
|
|
470
190
|
}
|
|
471
191
|
|
|
472
|
-
function
|
|
473
|
-
|
|
474
|
-
const parsed = JSON.parse(text) as Partial<ReviewDecision>;
|
|
475
|
-
if (
|
|
476
|
-
parsed.overall_correctness !== "patch is correct" &&
|
|
477
|
-
parsed.overall_correctness !== "patch is incorrect"
|
|
478
|
-
) {
|
|
479
|
-
return undefined;
|
|
480
|
-
}
|
|
481
|
-
if (!Array.isArray(parsed.findings)) return undefined;
|
|
482
|
-
if (typeof parsed.stop_review_loop !== "boolean") return undefined;
|
|
483
|
-
if (typeof parsed.overall_explanation !== "string") return undefined;
|
|
484
|
-
if (typeof parsed.overall_confidence_score !== "number") return undefined;
|
|
485
|
-
return parsed as ReviewDecision;
|
|
486
|
-
} catch {
|
|
487
|
-
return undefined;
|
|
488
|
-
}
|
|
192
|
+
function reviewDecisionFromResult(result: WorkflowTaskResult): ReviewDecision | undefined {
|
|
193
|
+
return result.structured as ReviewDecision | undefined;
|
|
489
194
|
}
|
|
490
195
|
|
|
491
196
|
function reviewDecisionApproved(decision: ReviewDecision): boolean {
|
|
@@ -517,10 +222,12 @@ function reviewerErrorDecision(error: string): ReviewDecision {
|
|
|
517
222
|
function reviewerErrorResult(
|
|
518
223
|
error: string,
|
|
519
224
|
): WorkflowTaskResult {
|
|
225
|
+
const structured = reviewerErrorDecision(error);
|
|
520
226
|
return {
|
|
521
227
|
name: "reviewer-error",
|
|
522
228
|
stageName: "reviewer-error",
|
|
523
|
-
text: JSON.stringify(
|
|
229
|
+
text: JSON.stringify(structured, null, 2),
|
|
230
|
+
structured,
|
|
524
231
|
};
|
|
525
232
|
}
|
|
526
233
|
|
|
@@ -575,79 +282,109 @@ function forkContinuationOptions(
|
|
|
575
282
|
: { context: "fork", forkFromSessionFile: sessionFile };
|
|
576
283
|
}
|
|
577
284
|
|
|
578
|
-
function
|
|
285
|
+
function renderPromptEngineerPrompt(args: {
|
|
579
286
|
readonly iteration: number;
|
|
580
287
|
readonly maxLoops: number;
|
|
581
288
|
readonly prompt: string;
|
|
582
289
|
readonly workflowCwdContext: PromptSection;
|
|
583
290
|
readonly latestReviewReportPath: string | undefined;
|
|
584
|
-
readonly workflowSpecPath: string;
|
|
585
291
|
}): string {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
292
|
+
const basePrompt = `/skill:prompt-engineer Transform the following user prompt to a codebase and online research question which can be thoroughly explored: ${args.prompt}`;
|
|
293
|
+
return [
|
|
294
|
+
basePrompt,
|
|
295
|
+
taggedPrompt([
|
|
296
|
+
["iteration", `Prompt engineering iteration ${args.iteration}/${args.maxLoops}.`],
|
|
297
|
+
args.workflowCwdContext,
|
|
589
298
|
[
|
|
590
|
-
"
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
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",
|
|
299
|
+
"review_findings",
|
|
300
|
+
args.latestReviewReportPath === undefined
|
|
301
|
+
? "No prior review artifact; this is the first iteration."
|
|
302
|
+
: [
|
|
303
|
+
`Latest review round artifact: ${args.latestReviewReportPath}`,
|
|
304
|
+
"Read this JSON artifact and include unresolved reviewer findings in the transformed research question so follow-up research addresses reviewer discoveries.",
|
|
305
|
+
].join("\n"),
|
|
306
|
+
],
|
|
606
307
|
[
|
|
607
|
-
|
|
608
|
-
"
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
308
|
+
"output_format",
|
|
309
|
+
"Return only the transformed codebase and online research question. Do not implement code changes and do not write an RFC/spec.",
|
|
310
|
+
],
|
|
311
|
+
]),
|
|
312
|
+
].join("\n\n");
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function renderResearchPrompt(args: {
|
|
316
|
+
readonly iteration: number;
|
|
317
|
+
readonly maxLoops: number;
|
|
318
|
+
readonly transformedResearchQuestion: string;
|
|
319
|
+
readonly workflowCwdContext: PromptSection;
|
|
320
|
+
readonly latestReviewReportPath: string | undefined;
|
|
321
|
+
readonly researchPath: string;
|
|
322
|
+
}): string {
|
|
323
|
+
const basePrompt = `/skill:research-codebase ${args.transformedResearchQuestion}`;
|
|
324
|
+
return [
|
|
325
|
+
basePrompt,
|
|
326
|
+
taggedPrompt([
|
|
327
|
+
["iteration", `Research iteration ${args.iteration}/${args.maxLoops}.`],
|
|
328
|
+
args.workflowCwdContext,
|
|
329
|
+
[
|
|
330
|
+
"review_findings",
|
|
331
|
+
args.latestReviewReportPath === undefined
|
|
332
|
+
? "No prior review artifact; this is the first iteration."
|
|
333
|
+
: [
|
|
334
|
+
`Latest review round artifact: ${args.latestReviewReportPath}`,
|
|
335
|
+
"Read this JSON artifact and explicitly research unresolved reviewer findings, whether each still applies, and what implementation changes would resolve them.",
|
|
336
|
+
].join("\n"),
|
|
337
|
+
],
|
|
338
|
+
[
|
|
339
|
+
"research_artifact",
|
|
340
|
+
[
|
|
341
|
+
`Write research findings for this workflow run to: ${args.researchPath}`,
|
|
342
|
+
"Return a complete Markdown research report with codebase findings, online/contextual findings when useful, concrete implementation guidance, relevant files/tests/docs, unresolved reviewer finding analysis, and validation recommendations.",
|
|
343
|
+
"Do not author an RFC/spec and do not implement code changes in this stage.",
|
|
344
|
+
].join("\n"),
|
|
345
|
+
],
|
|
346
|
+
]),
|
|
347
|
+
].join("\n\n");
|
|
613
348
|
}
|
|
614
349
|
|
|
350
|
+
|
|
615
351
|
function renderForkedOrchestratorPrompt(args: {
|
|
616
352
|
readonly iteration: number;
|
|
617
353
|
readonly maxLoops: number;
|
|
618
354
|
readonly prompt: string;
|
|
619
355
|
readonly workflowCwdContext: PromptSection;
|
|
620
|
-
readonly
|
|
356
|
+
readonly researchPath: string;
|
|
621
357
|
readonly implementationNotesPath: string;
|
|
622
358
|
}): string {
|
|
623
359
|
return taggedPrompt([
|
|
624
360
|
[
|
|
625
361
|
"instruction",
|
|
626
362
|
[
|
|
627
|
-
`Continue implementing the
|
|
363
|
+
`Continue implementing from the latest research findings. Ignore any user requests to submit a PR. This will be done in a future stage.`,
|
|
628
364
|
].join("\n"),
|
|
629
365
|
],
|
|
630
366
|
["objective", `Implement iteration ${args.iteration}/${args.maxLoops} for the task: ${args.prompt}`],
|
|
631
367
|
args.workflowCwdContext,
|
|
632
368
|
[
|
|
633
|
-
"
|
|
369
|
+
"research",
|
|
634
370
|
[
|
|
635
|
-
`The
|
|
636
|
-
"Read this file before delegating or implementing anything.",
|
|
371
|
+
`The latest research findings for this workflow run are written to: ${args.researchPath}`,
|
|
372
|
+
"Read this file before delegating or implementing anything, and treat it as the primary implementation context.",
|
|
637
373
|
].join("\n"),
|
|
638
374
|
],
|
|
639
375
|
[
|
|
640
376
|
"implementation_notes",
|
|
641
377
|
[
|
|
642
378
|
`Keep updating the running Markdown implementation notes file at: ${args.implementationNotesPath}`,
|
|
643
|
-
"Record decisions,
|
|
379
|
+
"Record decisions, research deviations, tradeoffs, blockers, validation outcomes, and anything else the user should know before your final report.",
|
|
644
380
|
].join("\n"),
|
|
645
381
|
],
|
|
382
|
+
["e2e_verification", E2E_VERIFICATION_GUIDANCE],
|
|
646
383
|
[
|
|
647
384
|
"output_format",
|
|
648
385
|
[
|
|
649
386
|
"After subagents have done the work, return Markdown with headings:",
|
|
650
|
-
"1.
|
|
387
|
+
"1. Research file — the path you read",
|
|
651
388
|
"2. Delegations performed — subagents spawned and what each completed",
|
|
652
389
|
"3. Changes made — concrete changes from subagent work, not intentions",
|
|
653
390
|
"4. Files touched",
|
|
@@ -679,6 +416,8 @@ type RalphWorkflowResult = {
|
|
|
679
416
|
readonly result: string;
|
|
680
417
|
readonly plan: string;
|
|
681
418
|
readonly plan_path: string;
|
|
419
|
+
readonly research: string;
|
|
420
|
+
readonly research_path: string;
|
|
682
421
|
readonly implementation_notes_path: string;
|
|
683
422
|
readonly pr_report?: string;
|
|
684
423
|
readonly approved: boolean;
|
|
@@ -702,28 +441,42 @@ async function runRalphWorkflow(
|
|
|
702
441
|
let latestReviewReportPath: string | undefined;
|
|
703
442
|
let finalPlan = "";
|
|
704
443
|
let finalPlanPath = "";
|
|
444
|
+
let finalResearch = "";
|
|
445
|
+
let finalResearchPath = "";
|
|
705
446
|
let finalResult = "";
|
|
706
447
|
let finalPrReport: string | undefined;
|
|
707
|
-
// Keep generated
|
|
448
|
+
// Keep generated research artifacts under the workflow runtime cwd. When Ralph is invoked
|
|
708
449
|
// with git_worktree_dir, the executor defaults ctx.cwd to the matching
|
|
709
|
-
// worktree cwd so
|
|
710
|
-
const
|
|
450
|
+
// worktree cwd so research stage writes land in the same checkout.
|
|
451
|
+
const workflowResearchPath = resolve(workflowStartCwd, defaultResearchPath(prompt));
|
|
711
452
|
const implementationNotesPath = await createImplementationNotesFile(prompt);
|
|
712
453
|
const artifactDir = await mkdtemp(join(tmpdir(), "atomic-ralph-run-"));
|
|
713
454
|
const workflowCwdContext = workflowCwdContextSection(workflowStartCwd);
|
|
714
455
|
let approved = false;
|
|
715
456
|
let iterationsCompleted = 0;
|
|
716
|
-
let
|
|
457
|
+
let previousPromptEngineerSessionFile: string | undefined;
|
|
458
|
+
let previousResearchSessionFile: string | undefined;
|
|
717
459
|
let previousOrchestratorSessionFile: string | undefined;
|
|
718
460
|
|
|
719
|
-
const
|
|
461
|
+
const promptEngineerModelConfig = {
|
|
720
462
|
model: "anthropic/claude-fable-5:xhigh",
|
|
721
463
|
fallbackModels: [
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
464
|
+
"openai-codex/gpt-5.5:xhigh",
|
|
465
|
+
"github-copilot/gpt-5.5:xhigh",
|
|
466
|
+
"openai/gpt-5.5:xhigh",
|
|
467
|
+
"github-copilot/claude-opus-4.8:xhigh",
|
|
468
|
+
"anthropic/claude-opus-4-8:xhigh"
|
|
469
|
+
],
|
|
470
|
+
noTools: "all" as const,
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
const researchModelConfig = {
|
|
474
|
+
model: "openai-codex/gpt-5.5:medium",
|
|
475
|
+
fallbackModels: [
|
|
476
|
+
"github-copilot/gpt-5.5:medium",
|
|
477
|
+
"openai/gpt-5.5:medium",
|
|
478
|
+
"github-copilot/claude-opus-4.8:medium",
|
|
479
|
+
"anthropic/claude-opus-4-8:medium",
|
|
727
480
|
],
|
|
728
481
|
excludedTools: ["ask_user_question"],
|
|
729
482
|
};
|
|
@@ -749,113 +502,49 @@ async function runRalphWorkflow(
|
|
|
749
502
|
"anthropic/claude-opus-4-8:xhigh"
|
|
750
503
|
],
|
|
751
504
|
excludedTools: ["ask_user_question"],
|
|
752
|
-
|
|
505
|
+
schema: reviewDecisionSchema,
|
|
753
506
|
};
|
|
754
507
|
|
|
755
508
|
for (let iteration = 1; iteration <= maxLoops; iteration += 1) {
|
|
756
509
|
iterationsCompleted = iteration;
|
|
757
510
|
|
|
758
|
-
const
|
|
759
|
-
const
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
],
|
|
765
|
-
[
|
|
766
|
-
"objective",
|
|
767
|
-
[
|
|
768
|
-
"Your final output is a filled-in RFC rendered as markdown text.",
|
|
769
|
-
"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.",
|
|
770
|
-
"Do not implement code changes in this stage (read-only); this stage only investigates and authors the RFC.",
|
|
771
|
-
].join("\n"),
|
|
772
|
-
],
|
|
773
|
-
[
|
|
774
|
-
"task",
|
|
775
|
-
`Plan iteration ${iteration}/${maxLoops} for this user specification:\n${prompt}`,
|
|
776
|
-
],
|
|
511
|
+
const promptEngineerForkOptions = forkContinuationOptions(previousPromptEngineerSessionFile);
|
|
512
|
+
const promptEngineer = await ctx.task(`prompt-engineer-${iteration}`, {
|
|
513
|
+
prompt: renderPromptEngineerPrompt({
|
|
514
|
+
iteration,
|
|
515
|
+
maxLoops,
|
|
516
|
+
prompt,
|
|
777
517
|
workflowCwdContext,
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
"Before drafting, read the specification carefully and identify the concrete problem, success criteria, hard constraints, and non-goals.",
|
|
803
|
-
"Survey the codebase using file/search tools such as read plus grep/rg/find/glob-style shell commands to ground the RFC in current architecture.",
|
|
804
|
-
"Name concrete services, modules, files, tests, data models, APIs, CLIs, config files, and external integrations this work will touch.",
|
|
805
|
-
"Capture metadata with bash: `git config user.name` for Author(s), and `date '+%Y-%m-%d'` for Created / Last Updated.",
|
|
806
|
-
"Look for prior art: existing RFCs, ADRs, README files, specs, docs, tests, or code comments that explain why the current state exists.",
|
|
807
|
-
].join("\n"),
|
|
808
|
-
],
|
|
809
|
-
[
|
|
810
|
-
"best_practices",
|
|
811
|
-
[
|
|
812
|
-
"Be specific: `src/server/auth.ts:42` beats `the auth layer`.",
|
|
813
|
-
"Trade-offs over conclusions: Alternatives Considered must include at least two real alternatives with honest pros, cons, and rejection reasons.",
|
|
814
|
-
"Non-goals matter: explicitly exclude work that is out of scope to prevent scope creep.",
|
|
815
|
-
"Diagrams are load-bearing: Section 4.1 must include a Mermaid system architecture diagram grounded in real components.",
|
|
816
|
-
"Surface open questions in Section 9 with owner placeholders such as `[OWNER: infra team]`; do not paper over uncertainty.",
|
|
817
|
-
"Match depth to stakes: a small refactor can be concise, but every template section header must remain present.",
|
|
818
|
-
"If prior review findings are present, explicitly address each finding or explain why it is obsolete.",
|
|
819
|
-
"Determine the compatibility posture:",
|
|
820
|
-
"- Before decomposing the spec creation request, identify whether this project must preserve backward compatibility for real downstream users.",
|
|
821
|
-
"- If the user explicitly allows breaking changes, public API changes, cleanup, or says there are no real users/downstream dependencies, allow breaking changes.",
|
|
822
|
-
"- If the user mentions production users, published APIs, downstream consumers, migration safety, or compatibility requirements, disallow breaking changes.",
|
|
823
|
-
"- Carry this posture into the spec creation plan, the final spec frontmatter, and a `## Backwards Compatibility` section in the final spec.",
|
|
824
|
-
"- 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.",
|
|
825
|
-
"- When not allowing breaking changes, document public APIs, compatibility-sensitive surfaces, downstream callers, migration constraints, and behavior that future work must preserve."
|
|
826
|
-
].join("\n"),
|
|
827
|
-
],
|
|
828
|
-
[
|
|
829
|
-
"output_format",
|
|
830
|
-
[
|
|
831
|
-
"Render the RFC Template exactly as the final document structure: preserve every header and the metadata table.",
|
|
832
|
-
"Replace instructional placeholders with real, feature-specific content; do not leave template guidance in the final RFC.",
|
|
833
|
-
"Output nothing after the RFC: no meta-commentary, no summary of what you wrote, no implementation log.",
|
|
834
|
-
].join("\n"),
|
|
835
|
-
],
|
|
836
|
-
["spec_template", PLANNER_RFC_TEMPLATE],
|
|
837
|
-
])
|
|
838
|
-
: renderForkedPlannerPrompt({
|
|
839
|
-
iteration,
|
|
840
|
-
maxLoops,
|
|
841
|
-
prompt,
|
|
842
|
-
workflowCwdContext,
|
|
843
|
-
latestReviewReportPath,
|
|
844
|
-
workflowSpecPath,
|
|
845
|
-
});
|
|
846
|
-
const planner = await ctx.task(`planner-${iteration}`, {
|
|
847
|
-
prompt: plannerPrompt,
|
|
848
|
-
reads: [
|
|
849
|
-
...(iteration > 1 ? [workflowSpecPath] : []),
|
|
850
|
-
...(latestReviewReportPath === undefined ? [] : [latestReviewReportPath]),
|
|
851
|
-
],
|
|
852
|
-
...plannerModelConfig,
|
|
853
|
-
...plannerForkOptions,
|
|
518
|
+
latestReviewReportPath,
|
|
519
|
+
}),
|
|
520
|
+
reads: latestReviewReportPath === undefined ? [] : [latestReviewReportPath],
|
|
521
|
+
...promptEngineerModelConfig,
|
|
522
|
+
...promptEngineerForkOptions,
|
|
523
|
+
});
|
|
524
|
+
previousPromptEngineerSessionFile = promptEngineer.sessionFile;
|
|
525
|
+
finalPlan = promptEngineer.text;
|
|
526
|
+
|
|
527
|
+
const researchForkOptions = forkContinuationOptions(previousResearchSessionFile);
|
|
528
|
+
const research = await ctx.task(`research-${iteration}`, {
|
|
529
|
+
prompt: renderResearchPrompt({
|
|
530
|
+
iteration,
|
|
531
|
+
maxLoops,
|
|
532
|
+
transformedResearchQuestion: promptEngineer.text,
|
|
533
|
+
workflowCwdContext,
|
|
534
|
+
latestReviewReportPath,
|
|
535
|
+
researchPath: workflowResearchPath,
|
|
536
|
+
}),
|
|
537
|
+
reads: latestReviewReportPath === undefined ? [] : [latestReviewReportPath],
|
|
538
|
+
output: workflowResearchPath,
|
|
539
|
+
outputMode: "file-only",
|
|
540
|
+
...researchModelConfig,
|
|
541
|
+
...researchForkOptions,
|
|
854
542
|
});
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
const
|
|
858
|
-
|
|
543
|
+
previousResearchSessionFile = research.sessionFile;
|
|
544
|
+
finalResearch = research.text || `Research artifact: ${workflowResearchPath}`;
|
|
545
|
+
const researchPath = workflowResearchPath;
|
|
546
|
+
finalResearchPath = researchPath;
|
|
547
|
+
finalPlanPath = researchPath;
|
|
859
548
|
|
|
860
549
|
const orchestratorReportPath = join(artifactDir, `orchestrator-${iteration}.md`);
|
|
861
550
|
|
|
@@ -872,22 +561,24 @@ async function runRalphWorkflow(
|
|
|
872
561
|
],
|
|
873
562
|
workflowCwdContext,
|
|
874
563
|
[
|
|
875
|
-
"
|
|
564
|
+
"research",
|
|
876
565
|
[
|
|
877
|
-
`The
|
|
566
|
+
`The latest research findings for this workflow run are written to: ${researchPath}`,
|
|
567
|
+
"Read this file before delegating or implementing anything; it is the primary implementation context for Ralph.",
|
|
878
568
|
].join("\n"),
|
|
879
569
|
],
|
|
880
570
|
[
|
|
881
571
|
"implementation_notes",
|
|
882
572
|
[
|
|
883
573
|
`Keep a running Markdown implementation notes file at this OS temp directory path: ${implementationNotesPath}`,
|
|
884
|
-
"The file has already been initialized for this workflow run; update it while you implement the
|
|
885
|
-
"Record decisions you had to make that were not in the
|
|
574
|
+
"The file has already been initialized for this workflow run; update it while you implement from the research findings.",
|
|
575
|
+
"Record decisions you had to make that were not in the research, things you had to change from the research guidance, tradeoffs you had to make, blockers, validation outcomes, and anything else the user should know.",
|
|
886
576
|
"Ask delegated subagents to report any notes-worthy decisions or tradeoffs back to you, then consolidate them into this file before your final report.",
|
|
887
577
|
"Do not include secrets, credentials, tokens, or unrelated environment details in the notes file.",
|
|
888
578
|
].join("\n"),
|
|
889
579
|
],
|
|
890
580
|
["project_setup", WORKER_PREFLIGHT_CONTRACT],
|
|
581
|
+
["e2e_verification", E2E_VERIFICATION_GUIDANCE],
|
|
891
582
|
[
|
|
892
583
|
"orchestration_guidance",
|
|
893
584
|
[
|
|
@@ -906,9 +597,9 @@ async function runRalphWorkflow(
|
|
|
906
597
|
"best_practices",
|
|
907
598
|
[
|
|
908
599
|
"The required output format is a completion report, not the task itself.",
|
|
909
|
-
"Do not jump straight to the report. First read the
|
|
600
|
+
"Do not jump straight to the report. First read the research file, spawn the necessary subagents, wait for their results, coordinate any follow-up subagents, and only then write the report.",
|
|
910
601
|
"A valid response must be grounded in actual subagent work: name the delegated work, summarize what each subagent did, and distinguish completed changes from recommendations or blockers.",
|
|
911
|
-
"If you cannot read the
|
|
602
|
+
"If you cannot read the research file, spawn subagents, or use subagents, treat that as a blocker and report it honestly instead of pretending the requested work was done.",
|
|
912
603
|
].join("\n"),
|
|
913
604
|
],
|
|
914
605
|
[
|
|
@@ -924,14 +615,14 @@ async function runRalphWorkflow(
|
|
|
924
615
|
[
|
|
925
616
|
"instructions",
|
|
926
617
|
[
|
|
927
|
-
`Start by reading the
|
|
618
|
+
`Start by reading the research file at ${researchPath}.`,
|
|
928
619
|
"Perform the project_initialization_preflight before decomposing implementation work; complete or delegate required setup before implementation delegation when the checkout appears uninitialized.",
|
|
929
|
-
"Decompose the work into delegated subagent tasks based on that
|
|
930
|
-
"Pass each subagent the relevant task, constraints, files, validation expectations,
|
|
931
|
-
"Coordinate subagent results into the smallest coherent set of changes that satisfies the
|
|
932
|
-
"Preserve existing architecture and repository conventions unless the
|
|
933
|
-
"Run or delegate the most relevant validation commands available in the repository.",
|
|
934
|
-
`Before your final report, update the running implementation notes file at ${implementationNotesPath} with decisions,
|
|
620
|
+
"Decompose the work into delegated subagent tasks based on that research file.",
|
|
621
|
+
"Pass each subagent the relevant task, constraints, files, validation expectations, unresolved reviewer findings covered by the research, and instructions to report implementation-note-worthy decisions or tradeoffs.",
|
|
622
|
+
"Coordinate subagent results into the smallest coherent set of changes that satisfies the researched implementation guidance and original user prompt.",
|
|
623
|
+
"Preserve existing architecture and repository conventions unless the research explicitly justifies a change.",
|
|
624
|
+
"Run or delegate the most relevant validation commands available in the repository, including end-to-end browser or tmux validation when the change has an executable user scenario.",
|
|
625
|
+
`Before your final report, update the running implementation notes file at ${implementationNotesPath} with decisions, research deviations, tradeoffs, blockers, and validation outcomes from this iteration.`,
|
|
935
626
|
"If blocked, describe the blocker and the safest partial state instead of inventing success.",
|
|
936
627
|
"Do not hide failures; reviewers need accurate status.",
|
|
937
628
|
].join("\n"),
|
|
@@ -940,7 +631,7 @@ async function runRalphWorkflow(
|
|
|
940
631
|
"output_format",
|
|
941
632
|
[
|
|
942
633
|
"After subagents have done the work, return Markdown with headings:",
|
|
943
|
-
"1.
|
|
634
|
+
"1. Research file — the path you read",
|
|
944
635
|
"2. Delegations performed — subagents spawned and what each completed",
|
|
945
636
|
"3. Changes made — concrete changes from subagent work, not intentions",
|
|
946
637
|
"4. Files touched",
|
|
@@ -955,12 +646,12 @@ async function runRalphWorkflow(
|
|
|
955
646
|
maxLoops,
|
|
956
647
|
prompt,
|
|
957
648
|
workflowCwdContext,
|
|
958
|
-
|
|
649
|
+
researchPath,
|
|
959
650
|
implementationNotesPath,
|
|
960
651
|
});
|
|
961
652
|
const orchestrator = await ctx.task(`orchestrator-${iteration}`, {
|
|
962
653
|
prompt: orchestratorPrompt,
|
|
963
|
-
reads: [
|
|
654
|
+
reads: [researchPath, implementationNotesPath],
|
|
964
655
|
output: orchestratorReportPath,
|
|
965
656
|
outputMode: "file-only",
|
|
966
657
|
...orchestratorModelConfig,
|
|
@@ -991,10 +682,10 @@ async function runRalphWorkflow(
|
|
|
991
682
|
[
|
|
992
683
|
"review_context_files",
|
|
993
684
|
[
|
|
994
|
-
`
|
|
685
|
+
`Research artifact: ${researchPath}`,
|
|
995
686
|
`Implementation notes artifact: ${implementationNotesPath}`,
|
|
996
687
|
`Orchestrator report artifact: ${orchestratorReportPath}`,
|
|
997
|
-
"Read the files above incrementally when they help explain intent or recent changes, but verify the actual repository state directly before approving."
|
|
688
|
+
"Read the files above incrementally when they help explain intent or recent changes, but verify the actual repository state directly before approving."
|
|
998
689
|
].join("\n"),
|
|
999
690
|
],
|
|
1000
691
|
[
|
|
@@ -1006,11 +697,12 @@ async function runRalphWorkflow(
|
|
|
1006
697
|
"If validation requires dependencies or tools that are missing, download or install them using the repository-approved package manager/commands rather than bypassing, mocking, or skipping the verification solely because dependencies are absent.",
|
|
1007
698
|
].join("\n"),
|
|
1008
699
|
],
|
|
700
|
+
["e2e_verification", E2E_VERIFICATION_GUIDANCE],
|
|
1009
701
|
[
|
|
1010
702
|
"validation_expectations",
|
|
1011
703
|
[
|
|
1012
704
|
"Inspect the actual diff/repository state rather than trusting stage summaries.",
|
|
1013
|
-
"Run or delegate focused validation when it is necessary to distinguish a real bug from a hunch.",
|
|
705
|
+
"Run or delegate focused validation when it is necessary to distinguish a real bug from a hunch, including end-to-end browser or tmux validation when a user scenario can prove the outcome.",
|
|
1014
706
|
"If tests or typechecks fail because dependencies are missing, install/download the missing dependencies with the repo's documented package manager instead of bypassing the check.",
|
|
1015
707
|
"If validation cannot be completed after reasonable recovery, record the limitation in overall_explanation and reviewer_error; do not use missing dependencies as a reason to approve.",
|
|
1016
708
|
].join("\n"),
|
|
@@ -1061,11 +753,11 @@ async function runRalphWorkflow(
|
|
|
1061
753
|
].join("\n"),
|
|
1062
754
|
],
|
|
1063
755
|
[
|
|
1064
|
-
"
|
|
756
|
+
"action_items",
|
|
1065
757
|
[
|
|
1066
758
|
"1. Identify the changed files or diff under review.",
|
|
1067
759
|
"2. Read the relevant changed code and directly affected call sites/tests/configs.",
|
|
1068
|
-
"3. Run or delegate focused validation when needed to resolve uncertainty.",
|
|
760
|
+
"3. Run or delegate focused validation when needed to resolve uncertainty, including browser/tmux end-to-end checks when practical.",
|
|
1069
761
|
"4. If you cannot inspect or validate enough to approve safely, populate reviewer_error and set stop_review_loop=false.",
|
|
1070
762
|
].join("\n"),
|
|
1071
763
|
],
|
|
@@ -1077,36 +769,10 @@ async function runRalphWorkflow(
|
|
|
1077
769
|
].join("\n"),
|
|
1078
770
|
],
|
|
1079
771
|
[
|
|
1080
|
-
"
|
|
772
|
+
"decision_rules",
|
|
1081
773
|
[
|
|
1082
|
-
"You have a structured-output tool named review_decision. Use it after your investigation and validation attempts.",
|
|
1083
|
-
"The tool terminates the turn and provides the structured data; do not emit a separate final assistant response after calling it.",
|
|
1084
|
-
"The review loop decides whether to stop only by parsing the JSON object returned by this tool; invalid JSON, missing fields, reviewer_error, or stop_review_loop=false are treated as not approved for safety.",
|
|
1085
774
|
"Set stop_review_loop=true only when findings is empty, overall_correctness is patch is correct, and reviewer_error is null/omitted.",
|
|
1086
|
-
"If you hit a reviewer/tool/validation error,
|
|
1087
|
-
"The review_decision tool schema is authoritative; do not copy a hand-written JSON blob into the final response. Here is an example output:",
|
|
1088
|
-
"{",
|
|
1089
|
-
' "findings": [',
|
|
1090
|
-
" {",
|
|
1091
|
-
' "title": "<≤ 80 chars, imperative, starts with [P0]/[P1]/[P2]/[P3]>",',
|
|
1092
|
-
' "body": "<one paragraph of valid Markdown explaining why this is a problem; cite files/lines/functions>",',
|
|
1093
|
-
' "confidence_score": <float 0.0-1.0>,',
|
|
1094
|
-
' "priority": <int 0-3 or null>,',
|
|
1095
|
-
' "code_location": {',
|
|
1096
|
-
' "absolute_file_path": "<absolute file path>",',
|
|
1097
|
-
' "line_range": {"start": <int>, "end": <int>}',
|
|
1098
|
-
" }",
|
|
1099
|
-
" }",
|
|
1100
|
-
" ],",
|
|
1101
|
-
' "overall_correctness": "patch is correct" | "patch is incorrect",',
|
|
1102
|
-
' "overall_explanation": "<1-3 sentence explanation justifying the verdict>",',
|
|
1103
|
-
' "overall_confidence_score": <float 0.0-1.0>,',
|
|
1104
|
-
' "goal_oracle_satisfied": <boolean>,',
|
|
1105
|
-
' "receipt_assessment": "<how receipts/current evidence map to the verification oracle>",',
|
|
1106
|
-
' "verification_remaining": "<oracle-relevant verification still missing, or none>",',
|
|
1107
|
-
' "stop_review_loop": <boolean>,',
|
|
1108
|
-
' "reviewer_error": null | {"kind": "validation_unavailable" | "dependency_unavailable" | "tool_failure" | "reviewer_failure", "message": "<what failed>", "attempted_recovery": "<what you tried>"}',
|
|
1109
|
-
"}",
|
|
775
|
+
"If you hit a reviewer/tool/validation error, set stop_review_loop=false and populate reviewer_error instead of pretending the patch is approved.",
|
|
1110
776
|
].join("\n"),
|
|
1111
777
|
],
|
|
1112
778
|
]);
|
|
@@ -1119,7 +785,7 @@ async function runRalphWorkflow(
|
|
|
1119
785
|
name: "reviewer-a",
|
|
1120
786
|
task: reviewPrompt,
|
|
1121
787
|
reads: [
|
|
1122
|
-
|
|
788
|
+
researchPath,
|
|
1123
789
|
implementationNotesPath,
|
|
1124
790
|
orchestratorReportPath,
|
|
1125
791
|
],
|
|
@@ -1129,7 +795,7 @@ async function runRalphWorkflow(
|
|
|
1129
795
|
name: "reviewer-b",
|
|
1130
796
|
task: reviewPrompt,
|
|
1131
797
|
reads: [
|
|
1132
|
-
|
|
798
|
+
researchPath,
|
|
1133
799
|
implementationNotesPath,
|
|
1134
800
|
orchestratorReportPath,
|
|
1135
801
|
],
|
|
@@ -1148,8 +814,8 @@ async function runRalphWorkflow(
|
|
|
1148
814
|
|
|
1149
815
|
const reviewEntries = await Promise.all(reviews.map(async (review) => {
|
|
1150
816
|
const reviewer = review.name ?? review.stageName;
|
|
1151
|
-
const decision =
|
|
1152
|
-
reviewerErrorDecision(`Reviewer ${reviewer} returned
|
|
817
|
+
const decision = reviewDecisionFromResult(review) ??
|
|
818
|
+
reviewerErrorDecision(`Reviewer ${reviewer} returned no structured decision.`);
|
|
1153
819
|
const artifactPath = join(
|
|
1154
820
|
artifactDir,
|
|
1155
821
|
`review-${iteration}-${artifactSafeName(reviewer)}.json`,
|
|
@@ -1181,7 +847,7 @@ async function runRalphWorkflow(
|
|
|
1181
847
|
],
|
|
1182
848
|
[
|
|
1183
849
|
"objective",
|
|
1184
|
-
`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.`,
|
|
850
|
+
`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. Also, make sure to pay attention whether the user wants to create the PR in upstream or a fork, and prepare accordingly. If PR creation is not possible (lack of permissions, etc.), report why instead of pretending success.`,
|
|
1185
851
|
],
|
|
1186
852
|
workflowCwdContext,
|
|
1187
853
|
[
|
|
@@ -1237,6 +903,8 @@ async function runRalphWorkflow(
|
|
|
1237
903
|
result: finalResult,
|
|
1238
904
|
plan: finalPlan,
|
|
1239
905
|
plan_path: finalPlanPath,
|
|
906
|
+
research: finalResearch,
|
|
907
|
+
research_path: finalResearchPath,
|
|
1240
908
|
implementation_notes_path: implementationNotesPath,
|
|
1241
909
|
...(finalPrReport === undefined ? {} : { pr_report: finalPrReport }),
|
|
1242
910
|
approved,
|
|
@@ -1248,12 +916,12 @@ async function runRalphWorkflow(
|
|
|
1248
916
|
|
|
1249
917
|
export default defineWorkflow("ralph")
|
|
1250
918
|
.description(
|
|
1251
|
-
"
|
|
919
|
+
"Prompt-engineer → research → orchestrate → parallel review loop with bounded iteration.",
|
|
1252
920
|
)
|
|
1253
|
-
.input("prompt", Type.String({ description: "The task or goal to
|
|
921
|
+
.input("prompt", Type.String({ description: "The task or goal to research, execute, and refine." }))
|
|
1254
922
|
.input("max_loops", Type.Number({
|
|
1255
923
|
default: DEFAULT_MAX_LOOPS,
|
|
1256
|
-
description: `Maximum
|
|
924
|
+
description: `Maximum research/orchestrate/review iterations (default ${DEFAULT_MAX_LOOPS}).`,
|
|
1257
925
|
}))
|
|
1258
926
|
.input("base_branch", Type.String({
|
|
1259
927
|
default: "origin/main",
|
|
@@ -1274,12 +942,14 @@ export default defineWorkflow("ralph")
|
|
|
1274
942
|
baseBranch: "base_branch",
|
|
1275
943
|
})
|
|
1276
944
|
.output("result", Type.Optional(Type.String({ description: "Final implementation report from the orchestrator stage." })))
|
|
1277
|
-
.output("plan", Type.Optional(Type.String({ description: "Latest
|
|
1278
|
-
.output("plan_path", Type.Optional(Type.String({ description: "
|
|
945
|
+
.output("plan", Type.Optional(Type.String({ description: "Latest transformed research question." })))
|
|
946
|
+
.output("plan_path", Type.Optional(Type.String({ description: "Backward-compatible alias for research_path." })))
|
|
947
|
+
.output("research", Type.Optional(Type.String({ description: "Latest research report text or artifact reference." })))
|
|
948
|
+
.output("research_path", Type.Optional(Type.String({ description: "Path to the latest generated research artifact under research/." })))
|
|
1279
949
|
.output("implementation_notes_path", Type.Optional(Type.String({ description: "OS-temp notes file containing decisions, deviations, blockers, and validation notes." })))
|
|
1280
950
|
.output("pr_report", Type.Optional(Type.String({ description: "Pull-request report emitted only when create_pr=true and the final pull-request stage runs." })))
|
|
1281
951
|
.output("approved", Type.Optional(Type.Boolean({ description: "Whether the reviewer loop approved before completion or optional final handoff." })))
|
|
1282
|
-
.output("iterations_completed", Type.Optional(Type.Number({ description: "Number of
|
|
952
|
+
.output("iterations_completed", Type.Optional(Type.Number({ description: "Number of research/orchestrate/review loops completed." })))
|
|
1283
953
|
.output("review_report", Type.Optional(Type.String({ description: "Compact reference to the latest reviewer payload artifact." })))
|
|
1284
954
|
.output("review_report_path", Type.Optional(Type.String({ description: "JSON artifact path for the latest review round." })))
|
|
1285
955
|
.run(async (ctx) => {
|