super-ralph 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 William Cory
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,126 @@
1
+ # super-ralph
2
+
3
+ > Reusable Ralph workflow - ticket-driven development with multi-agent review loops
4
+
5
+ An opinionated [Smithers](https://smithers.sh) workflow. You just provide the specs, this workflow does the rest.
6
+
7
+ Supports subscriptions.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ bun add super-ralph smithers-orchestrator
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```tsx
18
+ import {
19
+ SuperRalph,
20
+ ralphOutputSchemas,
21
+ } from "super-ralph";
22
+ import {
23
+ createSmithers,
24
+ ClaudeCodeAgent,
25
+ CodexAgent,
26
+ GeminiAgent,
27
+ } from "smithers-orchestrator";
28
+ import PRD from "./specs/PRD.mdx";
29
+ import EngineeringSpec from "./specs/Engineering.mdx";
30
+
31
+ const { smithers, outputs } = createSmithers(ralphOutputSchemas, {
32
+ dbPath: "./workflow.db",
33
+ });
34
+
35
+ export default smithers((ctx) => (
36
+ <SuperRalph
37
+ ctx={ctx}
38
+ outputs={outputs}
39
+ focuses={[
40
+ { id: "auth", name: "Authentication" },
41
+ { id: "api", name: "API Server" },
42
+ ]}
43
+ projectId="my-project"
44
+ projectName="My Project"
45
+ specsPath="docs/specs/"
46
+ referenceFiles={["docs/reference/"]}
47
+ buildCmds={{ go: "go build ./...", rust: "cargo build" }}
48
+ testCmds={{ go: "go test ./...", rust: "cargo test" }}
49
+ codeStyle="Go: snake_case, Rust: snake_case"
50
+ reviewChecklist={["Spec compliance", "Test coverage", "Security"]}
51
+ maxConcurrency={12}
52
+ agents={{
53
+ planning: new CodexAgent({ model: "gpt-5.3-codex", cwd: process.cwd(), yolo: true }),
54
+ implementation: new ClaudeCodeAgent({ model: "claude-sonnet-4-6", cwd: process.cwd() }),
55
+ testing: new ClaudeCodeAgent({ model: "claude-sonnet-4-6", cwd: process.cwd() }),
56
+ reviewing: new CodexAgent({ model: "gpt-5.3-codex", cwd: process.cwd(), yolo: true }),
57
+ reporting: new GeminiAgent({ model: "gemini-2.5-pro", cwd: process.cwd(), yolo: true }),
58
+ }}
59
+ >
60
+ <PRD />
61
+ <EngineeringSpec />
62
+ </SuperRalph>
63
+ ));
64
+ ```
65
+
66
+ That's it! 30 lines of configuration for a complete workflow.
67
+
68
+ ## The Pattern
69
+
70
+ Under the hood this opinionated workflow is the following steps all in parallel in a pipeline
71
+
72
+ ```
73
+ Ralph (infinite loop)
74
+ ├─ UpdateProgress → PROGRESS.md
75
+ ├─ CodebaseReview → per-focus reviews → tickets
76
+ ├─ Discover → new feature tickets
77
+ ├─ IntegrationTest → per-focus test runs
78
+ └─ TicketPipeline × N (parallel, in worktrees)
79
+ ├─ Research → gather context
80
+ ├─ Plan → TDD plan
81
+ ├─ ValidationLoop (loops until approved)
82
+ │ ├─ Implement → write tests + code
83
+ │ ├─ Test → run all tests
84
+ │ ├─ BuildVerify → check compilation
85
+ │ ├─ SpecReview + CodeReview (parallel)
86
+ │ └─ ReviewFix → fix issues
87
+ └─ Report → completion summary
88
+ ```
89
+
90
+ This opinionated workflow is optimized in following ways:
91
+
92
+ - Observability: multiple reporting steps and lots of data stored in sqlite
93
+ - Quality: via CI checks, review loops, and context-engineered research-plan-implement steps
94
+ - Planning: Optimizes ralph by in real time generating tickets rather than hardcoding them up front
95
+ - Parallelization: All tickets implemented in a JJ Workspace in parallel and then merged back into the main branch as stacked changes
96
+
97
+ ## Advanced: Custom Components
98
+
99
+ Override any step with a custom component:
100
+
101
+ ```tsx
102
+ <SuperRalph
103
+ {...props}
104
+ discover={<MyCustomDiscover agent={...} />}
105
+ />
106
+ ```
107
+
108
+ Or run additional logic in parallel:
109
+
110
+ ```tsx
111
+ <SuperRalph
112
+ {...props}
113
+ discover={
114
+ <Parallel>
115
+ <SuperRalph.Discover agent={...} specsPath="..." referenceFiles={[...]} />
116
+ <MyAdditionalDiscovery agent={...} />
117
+ </Parallel>
118
+ }
119
+ />
120
+ ```
121
+
122
+ These steps default to <SuperRalph.Component when not provided.
123
+
124
+ ## License
125
+
126
+ MIT
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "super-ralph",
3
+ "version": "0.1.0",
4
+ "description": "Reusable Ralph workflow pattern - ticket-driven development with multi-agent review loops",
5
+ "author": "William Cory",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "module": "src/index.ts",
9
+ "exports": {
10
+ ".": "./src/index.ts",
11
+ "./selectors": "./src/selectors.ts",
12
+ "./components": "./src/components/index.ts"
13
+ },
14
+ "files": [
15
+ "src/",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "engines": {
20
+ "bun": ">=1.3.0"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/evmts/super-ralph.git"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/evmts/super-ralph/issues"
28
+ },
29
+ "homepage": "https://github.com/evmts/super-ralph#readme",
30
+ "keywords": [
31
+ "ai",
32
+ "workflow",
33
+ "orchestration",
34
+ "ralph",
35
+ "multi-agent",
36
+ "tdd",
37
+ "code-review"
38
+ ],
39
+ "peerDependencies": {
40
+ "smithers-orchestrator": "*"
41
+ },
42
+ "devDependencies": {
43
+ "@types/react": "^18.2.0",
44
+ "typescript": "^5.6.0"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public",
48
+ "registry": "https://registry.npmjs.org/"
49
+ }
50
+ }
@@ -0,0 +1,412 @@
1
+ import { Ralph, Parallel, Sequence, Worktree, Task } from "smithers-orchestrator";
2
+ import type { SmithersCtx } from "smithers-orchestrator";
3
+ import { selectAllTickets, selectReviewTickets, selectProgressSummary, selectImplement, selectTestResults, selectSpecReview, selectCodeReviews } from "../selectors";
4
+ import React, { type ReactElement, type ReactNode } from "react";
5
+ import UpdateProgressPrompt from "../prompts/UpdateProgress.mdx";
6
+ import DiscoverPrompt from "../prompts/Discover.mdx";
7
+ import IntegrationTestPrompt from "../prompts/IntegrationTest.mdx";
8
+ import ResearchPrompt from "../prompts/Research.mdx";
9
+ import PlanPrompt from "../prompts/Plan.mdx";
10
+ import ImplementPrompt from "../prompts/Implement.mdx";
11
+ import TestPrompt from "../prompts/Test.mdx";
12
+ import BuildVerifyPrompt from "../prompts/BuildVerify.mdx";
13
+ import SpecReviewPrompt from "../prompts/SpecReview.mdx";
14
+ import CodeReviewPrompt from "../prompts/CodeReview.mdx";
15
+ import ReviewFixPrompt from "../prompts/ReviewFix.mdx";
16
+ import ReportPrompt from "../prompts/Report.mdx";
17
+ import CategoryReviewPrompt from "../prompts/CategoryReview.mdx";
18
+
19
+ // Main component props (simple API)
20
+ export type SuperRalphProps = {
21
+ ctx: SmithersCtx<any>;
22
+ focuses: ReadonlyArray<{ readonly id: string; readonly name: string }>;
23
+ outputs: any;
24
+
25
+ // Project config (flattened)
26
+ projectId: string;
27
+ projectName: string;
28
+ specsPath: string;
29
+ referenceFiles: string[];
30
+ buildCmds: Record<string, string>;
31
+ testCmds: Record<string, string>;
32
+ codeStyle: string;
33
+ reviewChecklist: string[];
34
+
35
+ maxConcurrency: number;
36
+ taskRetries?: number;
37
+
38
+ // Agents (grouped)
39
+ agents: {
40
+ planning: any;
41
+ implementation: any;
42
+ testing: any;
43
+ reviewing: any;
44
+ reporting: any;
45
+ };
46
+
47
+ // Configuration
48
+ progressFile?: string;
49
+ findingsFile?: string;
50
+ commitConfig?: {
51
+ prefix?: string;
52
+ mainBranch?: string;
53
+ emojiPrefixes?: string;
54
+ };
55
+ testSuites?: Array<{
56
+ name: string;
57
+ command: string;
58
+ description: string;
59
+ }>;
60
+ focusTestSuites?: Record<string, { suites: string[]; setupHints: string[]; testDirs: string[] }>;
61
+ focusDirs?: Record<string, string[]>;
62
+ skipPhases?: Set<string>;
63
+
64
+ // Advanced: Override any step with custom component
65
+ updateProgress?: ReactElement;
66
+ discover?: ReactElement;
67
+ integrationTest?: ReactElement;
68
+ categoryReview?: ReactElement;
69
+ research?: ReactElement;
70
+ plan?: ReactElement;
71
+ implement?: ReactElement;
72
+ test?: ReactElement;
73
+ buildVerify?: ReactElement;
74
+ specReview?: ReactElement;
75
+ codeReview?: ReactElement;
76
+ reviewFix?: ReactElement;
77
+ report?: ReactElement;
78
+
79
+ // Specs as children
80
+ children?: ReactNode;
81
+ };
82
+
83
+ export function SuperRalph({
84
+ ctx,
85
+ focuses,
86
+ outputs,
87
+ projectId,
88
+ projectName,
89
+ specsPath,
90
+ referenceFiles,
91
+ buildCmds,
92
+ testCmds,
93
+ codeStyle,
94
+ reviewChecklist,
95
+ maxConcurrency,
96
+ taskRetries = 3,
97
+ agents,
98
+ progressFile = "PROGRESS.md",
99
+ findingsFile = "docs/test-suite-findings.md",
100
+ commitConfig = {},
101
+ testSuites = [],
102
+ focusTestSuites = {},
103
+ focusDirs = {},
104
+ skipPhases = new Set(),
105
+
106
+ // Advanced overrides
107
+ updateProgress: customUpdateProgress,
108
+ discover: customDiscover,
109
+ integrationTest: customIntegrationTest,
110
+ categoryReview: customCategoryReview,
111
+ research: customResearch,
112
+ plan: customPlan,
113
+ implement: customImplement,
114
+ test: customTest,
115
+ buildVerify: customBuildVerify,
116
+ specReview: customSpecReview,
117
+ codeReview: customCodeReview,
118
+ reviewFix: customReviewFix,
119
+ report: customReport,
120
+
121
+ children,
122
+ }: SuperRalphProps) {
123
+ const { findings: reviewFindings } = selectReviewTickets(ctx, focuses, outputs);
124
+ const { completed: completedTicketIds, unfinished: unfinishedTickets } = selectAllTickets(ctx, focuses, outputs);
125
+ const progressSummary = selectProgressSummary(ctx, outputs);
126
+
127
+ const { prefix = "📝", mainBranch = "main", emojiPrefixes = "✨ feat, 🐛 fix, ♻️ refactor, 📝 docs, 🧪 test" } = commitConfig;
128
+
129
+ return (
130
+ <Ralph until={false} maxIterations={Infinity} onMaxReached="return-last">
131
+ <Parallel maxConcurrency={maxConcurrency}>
132
+ {!skipPhases.has("PROGRESS") && (customUpdateProgress || (
133
+ <Task id="update-progress" output={outputs.progress} agent={agents.reporting} retries={taskRetries}>
134
+ <UpdateProgressPrompt
135
+ projectName={projectName}
136
+ progressFile={progressFile}
137
+ commitMessage={`${prefix} docs: update progress`}
138
+ completedTickets={completedTicketIds}
139
+ />
140
+ </Task>
141
+ ))}
142
+
143
+ {!skipPhases.has("CODEBASE_REVIEW") && (customCategoryReview ? (
144
+ customCategoryReview
145
+ ) : (
146
+ <Parallel maxConcurrency={maxConcurrency}>
147
+ {focuses.map(({ id, name }) => (
148
+ <Task key={id} id={`codebase-review:${id}`} output={outputs.category_review} agent={agents.reviewing} retries={taskRetries}>
149
+ <CategoryReviewPrompt categoryId={id} categoryName={name} relevantDirs={focusDirs[id] ?? null} />
150
+ </Task>
151
+ ))}
152
+ </Parallel>
153
+ ))}
154
+
155
+ {!skipPhases.has("DISCOVER") && (customDiscover || (
156
+ <Task id="discover" output={outputs.discover} agent={agents.planning} retries={taskRetries}>
157
+ <DiscoverPrompt
158
+ projectName={projectName}
159
+ specsPath={specsPath}
160
+ referenceFiles={referenceFiles}
161
+ categories={focuses}
162
+ completedTicketIds={completedTicketIds}
163
+ previousProgress={progressSummary}
164
+ reviewFindings={reviewFindings}
165
+ />
166
+ </Task>
167
+ ))}
168
+
169
+ {!skipPhases.has("INTEGRATION_TEST") && (customIntegrationTest || (
170
+ <Parallel maxConcurrency={maxConcurrency}>
171
+ {focuses.map(({ id, name }) => {
172
+ const suiteInfo = focusTestSuites[id] ?? { suites: [], setupHints: [], testDirs: [] };
173
+ return (
174
+ <Task key={id} id={`integration-test:${id}`} output={outputs.integration_test} agent={agents.testing} retries={taskRetries}>
175
+ <IntegrationTestPrompt
176
+ categoryId={id}
177
+ categoryName={name}
178
+ suites={suiteInfo.suites}
179
+ setupHints={suiteInfo.setupHints}
180
+ testDirs={suiteInfo.testDirs}
181
+ findingsFile={findingsFile}
182
+ />
183
+ </Task>
184
+ );
185
+ })}
186
+ </Parallel>
187
+ ))}
188
+
189
+ {unfinishedTickets.map((ticket: any) => {
190
+ const researchData = ctx.outputMaybe(outputs.research, { nodeId: `${ticket.id}:research` });
191
+ const planData = ctx.outputMaybe(outputs.plan, { nodeId: `${ticket.id}:plan` });
192
+ const contextFilePath = researchData?.contextFilePath ?? `docs/context/${ticket.id}.md`;
193
+ const planFilePath = planData?.planFilePath ?? `docs/plans/${ticket.id}.md`;
194
+
195
+ return (
196
+ <Worktree key={ticket.id} id={`wt-${ticket.id}`} path={`/tmp/workflow-wt-${ticket.id}`}>
197
+ <Sequence skipIf={ctx.outputMaybe(outputs.report, { nodeId: `${ticket.id}:report` })?.status === "complete"}>
198
+ {customResearch || (
199
+ <Task id={`${ticket.id}:research`} output={outputs.research} agent={agents.planning} retries={taskRetries}>
200
+ <ResearchPrompt
201
+ ticketId={ticket.id}
202
+ ticketTitle={ticket.title}
203
+ ticketDescription={ticket.description}
204
+ ticketCategory={ticket.category}
205
+ referenceFiles={ticket.referenceFiles}
206
+ relevantFiles={ticket.relevantFiles}
207
+ contextFilePath={contextFilePath}
208
+ referencePaths={[specsPath, ...referenceFiles]}
209
+ />
210
+ </Task>
211
+ )}
212
+
213
+ {customPlan || (
214
+ <Task id={`${ticket.id}:plan`} output={outputs.plan} agent={agents.planning} retries={taskRetries}>
215
+ <PlanPrompt
216
+ ticketId={ticket.id}
217
+ ticketTitle={ticket.title}
218
+ ticketDescription={ticket.description}
219
+ ticketCategory={ticket.category}
220
+ acceptanceCriteria={ticket.acceptanceCriteria ?? []}
221
+ contextFilePath={contextFilePath}
222
+ researchSummary={researchData?.summary ?? null}
223
+ planFilePath={planFilePath}
224
+ tddPatterns={["Write tests FIRST, then implementation"]}
225
+ commitPrefix={prefix}
226
+ mainBranch={mainBranch}
227
+ />
228
+ </Task>
229
+ )}
230
+
231
+ {/* ValidationLoop */}
232
+ <Sequence>
233
+ {(() => {
234
+ const latestImplement = selectImplement(ctx, ticket.id);
235
+ const latestTest = selectTestResults(ctx, ticket.id);
236
+ const latestSpecReview = selectSpecReview(ctx, ticket.id);
237
+ const { worstSeverity: worstCodeSeverity, mergedIssues: mergedCodeIssues, mergedFeedback: mergedCodeFeedback } = selectCodeReviews(ctx, ticket.id, outputs);
238
+
239
+ const specApproved = latestSpecReview?.severity === "none";
240
+ const codeApproved = worstCodeSeverity === "none";
241
+ const noReviewIssues = specApproved && codeApproved;
242
+
243
+ const toArray = (v: unknown): string[] => Array.isArray(v) ? v : typeof v === "string" ? [v] : [];
244
+ const reviewFeedback = (() => {
245
+ const parts: string[] = [];
246
+ if (latestSpecReview && !specApproved) {
247
+ parts.push(`SPEC REVIEW (${latestSpecReview.severity}): ${latestSpecReview.feedback}`);
248
+ if (latestSpecReview.issues) parts.push(`Issues: ${toArray(latestSpecReview.issues).join("; ")}`);
249
+ }
250
+ if (!codeApproved && mergedCodeFeedback) {
251
+ parts.push(`CODE REVIEW (${worstCodeSeverity}): ${mergedCodeFeedback}`);
252
+ if (mergedCodeIssues.length > 0) parts.push(`Issues: ${mergedCodeIssues.join("; ")}`);
253
+ }
254
+ return parts.length > 0 ? parts.join("\n\n") : null;
255
+ })();
256
+
257
+ return (
258
+ <>
259
+ {customImplement || (
260
+ <Task id={`${ticket.id}:implement`} output={outputs.implement} agent={agents.implementation} retries={taskRetries}>
261
+ <ImplementPrompt
262
+ ticketId={ticket.id}
263
+ ticketTitle={ticket.title}
264
+ ticketCategory={ticket.category}
265
+ planFilePath={planFilePath}
266
+ contextFilePath={contextFilePath}
267
+ implementationSteps={planData?.implementationSteps ?? null}
268
+ previousImplementation={latestImplement ?? null}
269
+ reviewFeedback={reviewFeedback}
270
+ failingTests={latestTest?.failingSummary ?? null}
271
+ testWritingGuidance={["Write unit tests AND integration tests"]}
272
+ implementationGuidance={["Follow architecture patterns from specs"]}
273
+ formatterCommands={Object.entries(buildCmds).map(([lang, cmd]) => `Format ${lang}`)}
274
+ verifyCommands={Object.values(buildCmds)}
275
+ architectureRules={[`Read ${specsPath} for patterns`]}
276
+ commitPrefix={prefix}
277
+ mainBranch={mainBranch}
278
+ emojiPrefixes={emojiPrefixes}
279
+ />
280
+ </Task>
281
+ )}
282
+
283
+ {customTest || (
284
+ <Task id={`${ticket.id}:test`} output={outputs.test_results} agent={agents.testing} retries={taskRetries}>
285
+ <TestPrompt
286
+ ticketId={ticket.id}
287
+ ticketTitle={ticket.title}
288
+ ticketCategory={ticket.category}
289
+ testSuites={testSuites.length > 0 ? testSuites : Object.entries(testCmds).map(([name, command]) => ({
290
+ name: `${name} tests`,
291
+ command,
292
+ description: `Run ${name} tests`,
293
+ }))}
294
+ fixCommitPrefix={`🐛 fix`}
295
+ mainBranch={mainBranch}
296
+ />
297
+ </Task>
298
+ )}
299
+
300
+ {customBuildVerify || (
301
+ <Task id={`${ticket.id}:build-verify`} output={outputs.build_verify} agent={agents.testing} retries={taskRetries}>
302
+ <BuildVerifyPrompt
303
+ ticketId={ticket.id}
304
+ ticketTitle={ticket.title}
305
+ ticketCategory={ticket.category}
306
+ filesCreated={latestImplement?.filesCreated ?? null}
307
+ filesModified={latestImplement?.filesModified ?? null}
308
+ whatWasDone={latestImplement?.whatWasDone ?? null}
309
+ />
310
+ </Task>
311
+ )}
312
+
313
+ <Parallel maxConcurrency={maxConcurrency}>
314
+ {customSpecReview || (
315
+ <Task id={`${ticket.id}:spec-review`} output={outputs.spec_review} agent={agents.reviewing} retries={taskRetries}>
316
+ <SpecReviewPrompt
317
+ ticketId={ticket.id}
318
+ ticketTitle={ticket.title}
319
+ ticketCategory={ticket.category}
320
+ filesCreated={latestImplement?.filesCreated ?? null}
321
+ filesModified={latestImplement?.filesModified ?? null}
322
+ testResults={[
323
+ { name: "Tests", status: latestTest?.goTestsPassed ? "PASS" : "FAIL" },
324
+ ]}
325
+ failingSummary={latestTest?.failingSummary ?? null}
326
+ specChecks={[
327
+ { name: "Code Style", items: [codeStyle] },
328
+ { name: "Review Checklist", items: reviewChecklist },
329
+ ]}
330
+ />
331
+ </Task>
332
+ )}
333
+
334
+ {customCodeReview || (
335
+ <Task id={`${ticket.id}:code-review`} output={outputs.code_review} agent={agents.reviewing} retries={taskRetries}>
336
+ <CodeReviewPrompt
337
+ ticketId={ticket.id}
338
+ ticketTitle={ticket.title}
339
+ ticketCategory={ticket.category}
340
+ filesCreated={latestImplement?.filesCreated ?? null}
341
+ filesModified={latestImplement?.filesModified ?? null}
342
+ reviewChecklist={reviewChecklist}
343
+ />
344
+ </Task>
345
+ )}
346
+ </Parallel>
347
+
348
+ {customReviewFix || (!noReviewIssues && (
349
+ <Task id={`${ticket.id}:review-fix`} output={outputs.review_fix} agent={agents.implementation} retries={taskRetries}>
350
+ <ReviewFixPrompt
351
+ ticketId={ticket.id}
352
+ ticketTitle={ticket.title}
353
+ ticketCategory={ticket.category}
354
+ specSeverity={latestSpecReview?.severity ?? "none"}
355
+ specFeedback={latestSpecReview?.feedback ?? ""}
356
+ specIssues={latestSpecReview?.issues ?? null}
357
+ codeSeverity={worstCodeSeverity}
358
+ codeFeedback={mergedCodeFeedback}
359
+ codeIssues={mergedCodeIssues.length > 0 ? mergedCodeIssues : null}
360
+ validationCommands={Object.values(testCmds)}
361
+ commitPrefix={`🐛 fix`}
362
+ mainBranch={mainBranch}
363
+ emojiPrefixes={emojiPrefixes}
364
+ />
365
+ </Task>
366
+ ))}
367
+ </>
368
+ );
369
+ })()}
370
+ </Sequence>
371
+
372
+ {customReport || (
373
+ <Task id={`${ticket.id}:report`} output={outputs.report} agent={agents.reporting} retries={taskRetries}>
374
+ <ReportPrompt
375
+ ticketId={ticket.id}
376
+ ticketTitle={ticket.title}
377
+ ticketCategory={ticket.category}
378
+ acceptanceCriteria={ticket.acceptanceCriteria ?? []}
379
+ specSeverity={ctx.outputMaybe(outputs.spec_review, { nodeId: `${ticket.id}:spec-review` })?.severity ?? "none"}
380
+ codeSeverity={selectCodeReviews(ctx, ticket.id, outputs).worstSeverity}
381
+ allIssuesResolved={ctx.outputMaybe(outputs.review_fix, { nodeId: `${ticket.id}:review-fix` })?.allIssuesResolved ?? true}
382
+ reviewRounds={1}
383
+ goTests={selectTestResults(ctx, ticket.id)?.goTestsPassed ? "PASS" : "FAIL"}
384
+ rustTests={selectTestResults(ctx, ticket.id)?.rustTestsPassed ? "PASS" : "FAIL"}
385
+ e2eTests={selectTestResults(ctx, ticket.id)?.e2eTestsPassed ? "PASS" : "FAIL"}
386
+ sqlcGen={selectTestResults(ctx, ticket.id)?.sqlcGenPassed ? "PASS" : "FAIL"}
387
+ />
388
+ </Task>
389
+ )}
390
+ </Sequence>
391
+ </Worktree>
392
+ );
393
+ })}
394
+ </Parallel>
395
+ </Ralph>
396
+ );
397
+ }
398
+
399
+ // Compound components for advanced customization
400
+ SuperRalph.UpdateProgress = function UpdateProgress(_props: any) { return null; };
401
+ SuperRalph.Discover = function Discover(_props: any) { return null; };
402
+ SuperRalph.IntegrationTest = function IntegrationTest(_props: any) { return null; };
403
+ SuperRalph.CategoryReview = function CategoryReview(_props: any) { return null; };
404
+ SuperRalph.Research = function Research(_props: any) { return null; };
405
+ SuperRalph.Plan = function Plan(_props: any) { return null; };
406
+ SuperRalph.Implement = function Implement(_props: any) { return null; };
407
+ SuperRalph.Test = function Test(_props: any) { return null; };
408
+ SuperRalph.BuildVerify = function BuildVerify(_props: any) { return null; };
409
+ SuperRalph.SpecReview = function SpecReview(_props: any) { return null; };
410
+ SuperRalph.CodeReview = function CodeReview(_props: any) { return null; };
411
+ SuperRalph.ReviewFix = function ReviewFix(_props: any) { return null; };
412
+ SuperRalph.Report = function Report(_props: any) { return null; };
@@ -0,0 +1,2 @@
1
+ export { SuperRalph } from "./SuperRalph";
2
+ export type { SuperRalphProps } from "./SuperRalph";
@@ -0,0 +1,2 @@
1
+ export { useSuperRalph } from "./useSuperRalph";
2
+ export type { SuperRalphContext, UseSuperRalphConfig } from "./useSuperRalph";
@@ -0,0 +1,40 @@
1
+ import type { SmithersCtx } from "smithers-orchestrator";
2
+ import { selectAllTickets, selectReviewTickets, selectProgressSummary } from "../selectors";
3
+
4
+ export type SuperRalphContext = {
5
+ ctx: SmithersCtx<any>;
6
+ completedTicketIds: string[];
7
+ unfinishedTickets: any[];
8
+ reviewFindings: string | null;
9
+ progressSummary: string | null;
10
+ focuses: ReadonlyArray<{ readonly id: string; readonly name: string }>;
11
+ outputs: any;
12
+ target: any;
13
+ };
14
+
15
+ export type UseSuperRalphConfig = {
16
+ focuses: ReadonlyArray<{ readonly id: string; readonly name: string }>;
17
+ outputs: any;
18
+ target: any;
19
+ };
20
+
21
+ /**
22
+ * Hook to extract SuperRalph state from SmithersCtx.
23
+ * Use this for controlled component pattern or to access workflow state.
24
+ */
25
+ export function useSuperRalph(ctx: SmithersCtx<any>, config: UseSuperRalphConfig): SuperRalphContext {
26
+ const { findings: reviewFindings } = selectReviewTickets(ctx, config.focuses, config.outputs);
27
+ const { completed: completedTicketIds, unfinished: unfinishedTickets } = selectAllTickets(ctx, config.focuses, config.outputs);
28
+ const progressSummary = selectProgressSummary(ctx, config.outputs);
29
+
30
+ return {
31
+ ctx,
32
+ completedTicketIds,
33
+ unfinishedTickets,
34
+ reviewFindings,
35
+ progressSummary,
36
+ focuses: config.focuses,
37
+ outputs: config.outputs,
38
+ target: config.target,
39
+ };
40
+ }