@bastani/atomic 0.9.5-alpha.7 → 0.9.5-alpha.8
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 +17 -2
- package/dist/builtin/cursor/package.json +2 -2
- package/dist/builtin/intercom/CHANGELOG.md +17 -0
- package/dist/builtin/intercom/README.md +27 -21
- package/dist/builtin/intercom/broker/broker.ts +3 -6
- package/dist/builtin/intercom/broker/paths.ts +42 -5
- package/dist/builtin/intercom/broker/spawn.ts +78 -10
- package/dist/builtin/intercom/config.ts +6 -3
- package/dist/builtin/intercom/contact-supervisor-tool.ts +6 -4
- package/dist/builtin/intercom/index-heavy.ts +1 -1
- package/dist/builtin/intercom/index.ts +162 -130
- package/dist/builtin/intercom/intercom-tool.ts +9 -3
- package/dist/builtin/intercom/package.json +10 -1
- package/dist/builtin/intercom/result-renderers.ts +1 -1
- package/dist/builtin/intercom/ui/compose.ts +2 -2
- package/dist/builtin/intercom/ui/inline-message.ts +2 -2
- package/dist/builtin/intercom/ui/session-list.ts +2 -2
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +6 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/tui/render-event-formatting.ts +15 -48
- package/dist/builtin/subagents/src/tui/render-layout.ts +9 -0
- package/dist/builtin/subagents/src/tui/render-widget-graph.ts +27 -25
- package/dist/builtin/subagents/src/tui/render-widget.ts +46 -69
- package/dist/builtin/subagents/src/tui/render.ts +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +11 -0
- package/dist/builtin/workflows/README.md +3 -1
- package/dist/builtin/workflows/builtin/goal-artifacts.ts +3 -1
- package/dist/builtin/workflows/builtin/goal-prompts.ts +23 -2
- package/dist/builtin/workflows/builtin/goal-reducer.ts +21 -0
- package/dist/builtin/workflows/builtin/goal-review.ts +60 -6
- package/dist/builtin/workflows/builtin/goal-runner.ts +32 -21
- package/dist/builtin/workflows/builtin/goal-types.ts +7 -1
- package/dist/builtin/workflows/builtin/ralph-core.ts +52 -4
- package/dist/builtin/workflows/builtin/ralph-review-gate.ts +10 -3
- package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +23 -2
- package/dist/builtin/workflows/builtin/ralph-runner.ts +38 -16
- package/dist/builtin/workflows/builtin/review-convergence.ts +111 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/create-spec/SKILL.md +2 -0
- package/dist/builtin/workflows/src/durable/child-primitive.ts +12 -8
- package/dist/builtin/workflows/src/durable/dbos-backend.ts +47 -2
- package/dist/builtin/workflows/src/durable/dbos-envelope.ts +28 -0
- package/dist/builtin/workflows/src/durable/stage-primitive.ts +165 -27
- package/dist/builtin/workflows/src/durable/types.ts +14 -1
- package/dist/builtin/workflows/src/engine/run.ts +12 -13
- package/dist/builtin/workflows/src/extension/workflow-prompts.ts +2 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-continuation.ts +13 -3
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +1 -1
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +18 -19
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-unresolved-overflow.ts +38 -0
- package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +18 -1
- package/dist/builtin/workflows/src/shared/persistence-restore.ts +23 -5
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +4 -0
- package/dist/bun/internal-intercom-broker.d.ts +6 -0
- package/dist/bun/internal-intercom-broker.d.ts.map +1 -0
- package/dist/bun/internal-intercom-broker.js +39 -0
- package/dist/bun/internal-intercom-broker.js.map +1 -0
- package/dist/bun/split-loader.d.ts.map +1 -1
- package/dist/bun/split-loader.js +21 -8
- package/dist/bun/split-loader.js.map +1 -1
- package/dist/core/agent-session-auto-compaction.d.ts +4 -5
- package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-auto-compaction.js +49 -9
- package/dist/core/agent-session-auto-compaction.js.map +1 -1
- package/dist/core/agent-session-events.d.ts.map +1 -1
- package/dist/core/agent-session-events.js +3 -1
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +9 -3
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-models.d.ts +2 -2
- package/dist/core/agent-session-models.d.ts.map +1 -1
- package/dist/core/agent-session-models.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +1 -0
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-retry.d.ts +2 -0
- package/dist/core/agent-session-retry.d.ts.map +1 -1
- package/dist/core/agent-session-retry.js +152 -4
- package/dist/core/agent-session-retry.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session-types.d.ts +15 -1
- package/dist/core/agent-session-types.d.ts.map +1 -1
- package/dist/core/agent-session-types.js.map +1 -1
- package/dist/core/agent-session.d.ts +4 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +4 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/context-compaction-runner.d.ts.map +1 -1
- package/dist/core/compaction/context-compaction-runner.js +36 -20
- package/dist/core/compaction/context-compaction-runner.js.map +1 -1
- package/dist/core/copilot-anthropic-sse-repair.d.ts +23 -0
- package/dist/core/copilot-anthropic-sse-repair.d.ts.map +1 -0
- package/dist/core/copilot-anthropic-sse-repair.js +340 -0
- package/dist/core/copilot-anthropic-sse-repair.js.map +1 -0
- package/dist/core/copilot-gemini-reasoning.d.ts +16 -8
- package/dist/core/copilot-gemini-reasoning.d.ts.map +1 -1
- package/dist/core/copilot-gemini-reasoning.js +27 -22
- package/dist/core/copilot-gemini-reasoning.js.map +1 -1
- package/dist/core/copilot-hosts.d.ts +12 -0
- package/dist/core/copilot-hosts.d.ts.map +1 -0
- package/dist/core/copilot-hosts.js +33 -0
- package/dist/core/copilot-hosts.js.map +1 -0
- package/dist/core/extensions/agent-events.d.ts +1 -1
- package/dist/core/extensions/agent-events.d.ts.map +1 -1
- package/dist/core/extensions/agent-events.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +5 -5
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/sdk-types.d.ts +2 -0
- package/dist/core/sdk-types.d.ts.map +1 -1
- package/dist/core/sdk-types.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager-basic-accessors.d.ts +1 -0
- package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-basic-accessors.js +6 -0
- package/dist/core/settings-manager-basic-accessors.js.map +1 -1
- package/dist/core/settings-types.d.ts +1 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/index-extensions.d.ts +1 -1
- package/dist/index-extensions.d.ts.map +1 -1
- package/dist/index-extensions.js.map +1 -1
- package/dist/main-deferred-startup.d.ts +12 -0
- package/dist/main-deferred-startup.d.ts.map +1 -1
- package/dist/main-deferred-startup.js +21 -0
- package/dist/main-deferred-startup.js.map +1 -1
- package/dist/main-early-input.d.ts +41 -0
- package/dist/main-early-input.d.ts.map +1 -0
- package/dist/main-early-input.js +147 -0
- package/dist/main-early-input.js.map +1 -0
- package/dist/main.d.ts +0 -6
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +19 -13
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-events.js +15 -0
- package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
- package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-agent-events.js +24 -0
- package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.js +279 -181
- package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +9 -0
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +36 -0
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-types.d.ts +3 -0
- package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-types.js.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.js +21 -9
- package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +6 -1
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/docs/compaction.md +4 -0
- package/docs/json.md +3 -1
- package/docs/providers.md +1 -0
- package/docs/rpc.md +3 -1
- package/docs/settings.md +26 -0
- package/docs/terminal-setup.md +4 -0
- package/docs/usage.md +2 -2
- package/docs/workflows.md +13 -3
- package/npm-shrinkwrap.json +513 -23
- package/package.json +3 -2
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
WORKER_PREFLIGHT_CONTRACT,
|
|
10
10
|
} from "./shared-prompts.js";
|
|
11
11
|
import { renderRalphReviewerPrompt } from "./ralph-reviewer-prompt.js";
|
|
12
|
-
import { reviewDecisionApproved } from "./ralph-review-gate.js";
|
|
13
12
|
import {
|
|
14
13
|
REVIEWER_COUNT,
|
|
15
14
|
artifactSafeName,
|
|
@@ -22,8 +21,8 @@ import {
|
|
|
22
21
|
renderResearchPromptRefinementPrompt,
|
|
23
22
|
renderQaE2eVideoGuidance,
|
|
24
23
|
renderResearchPrompt,
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
parsedReviewDecisionFromResult,
|
|
25
|
+
ralphReviewConvergence,
|
|
27
26
|
reviewerErrorResult,
|
|
28
27
|
taggedPrompt,
|
|
29
28
|
workflowCwdContextSection,
|
|
@@ -32,6 +31,7 @@ import {
|
|
|
32
31
|
type RalphWorkflowOptions,
|
|
33
32
|
type RalphWorkflowResult,
|
|
34
33
|
} from "./ralph-core.js";
|
|
34
|
+
import { summarizeReviewConvergence } from "./review-convergence.js";
|
|
35
35
|
import {
|
|
36
36
|
orchestratorModelConfig,
|
|
37
37
|
promptEngineerModelConfig,
|
|
@@ -106,7 +106,7 @@ export async function runRalphWorkflow(
|
|
|
106
106
|
? taggedPrompt([
|
|
107
107
|
[
|
|
108
108
|
"role",
|
|
109
|
-
"You are a sub-agent orchestrator. Your primary implementation tool is the `subagent` tool. Ignore any user requests to submit a PR
|
|
109
|
+
"You are a sub-agent orchestrator. Your primary implementation tool is the `subagent` tool. Ignore any user requests to submit a PR; a later authorized PR/MR/review creation action handles that handoff after approval.",
|
|
110
110
|
],
|
|
111
111
|
[
|
|
112
112
|
"objective",
|
|
@@ -118,8 +118,8 @@ export async function runRalphWorkflow(
|
|
|
118
118
|
[
|
|
119
119
|
"research",
|
|
120
120
|
[
|
|
121
|
-
`The latest research findings for
|
|
122
|
-
"Read this file before delegating or implementing anything; it is the primary implementation context for
|
|
121
|
+
`The latest research findings for the requested work are written to: ${researchPath}`,
|
|
122
|
+
"Read this file before delegating or implementing anything; it is the primary implementation context for the requested work.",
|
|
123
123
|
].join("\n"),
|
|
124
124
|
],
|
|
125
125
|
[
|
|
@@ -226,6 +226,7 @@ export async function runRalphWorkflow(
|
|
|
226
226
|
implementationNotesPath,
|
|
227
227
|
orchestratorReportPath,
|
|
228
228
|
qaVideoPath,
|
|
229
|
+
createPr,
|
|
229
230
|
});
|
|
230
231
|
let reviews: WorkflowTaskResult[];
|
|
231
232
|
try {
|
|
@@ -268,38 +269,59 @@ export async function runRalphWorkflow(
|
|
|
268
269
|
},
|
|
269
270
|
);
|
|
270
271
|
} catch (err) {
|
|
271
|
-
|
|
272
|
-
reviews = [reviewerErrorResult(message)];
|
|
272
|
+
reviews = [reviewerErrorResult(err)];
|
|
273
273
|
}
|
|
274
274
|
const reviewEntries = await Promise.all(reviews.map(async (review) => {
|
|
275
275
|
const reviewer = review.name ?? review.stageName;
|
|
276
|
-
const
|
|
277
|
-
|
|
276
|
+
const parsed = parsedReviewDecisionFromResult(review, reviewer);
|
|
277
|
+
const convergenceDecision = ralphReviewConvergence({
|
|
278
|
+
decision: parsed.decision,
|
|
279
|
+
parsed: parsed.parsed,
|
|
280
|
+
diagnostics: parsed.diagnostics,
|
|
281
|
+
allowFinalActionRemaining: createPr,
|
|
282
|
+
});
|
|
278
283
|
const artifactPath = join(
|
|
279
284
|
artifactDir,
|
|
280
285
|
`review-${artifactSafeName(reviewer)}.json`,
|
|
281
286
|
);
|
|
282
287
|
await writeJsonArtifact(artifactPath, {
|
|
283
288
|
reviewer,
|
|
284
|
-
decision,
|
|
289
|
+
decision: parsed.decision,
|
|
290
|
+
convergence_decision: convergenceDecision,
|
|
285
291
|
raw_text: review.text,
|
|
286
292
|
});
|
|
287
|
-
return {
|
|
293
|
+
return {
|
|
294
|
+
reviewer,
|
|
295
|
+
artifact_path: artifactPath,
|
|
296
|
+
decision: parsed.decision,
|
|
297
|
+
convergence_decision: convergenceDecision,
|
|
298
|
+
};
|
|
288
299
|
}));
|
|
289
300
|
const approvalCount = reviewEntries.filter((review) =>
|
|
290
|
-
|
|
301
|
+
review.convergence_decision.approved,
|
|
291
302
|
).length;
|
|
292
303
|
approved =
|
|
293
304
|
reviewEntries.length === REVIEWER_COUNT &&
|
|
294
305
|
approvalCount === REVIEWER_COUNT;
|
|
306
|
+
const nextAction = approved
|
|
307
|
+
? (createPr ? "pull-request" : "finish")
|
|
308
|
+
: "implementation";
|
|
309
|
+
const roundConvergenceDecision = summarizeReviewConvergence({
|
|
310
|
+
parsed: reviewEntries.every((review) => review.convergence_decision.parsed),
|
|
311
|
+
approved,
|
|
312
|
+
stopReviewLoop: approved,
|
|
313
|
+
nextAction,
|
|
314
|
+
finalActionRemaining: approved && createPr,
|
|
315
|
+
diagnostics: reviewEntries.flatMap((review) => review.convergence_decision.diagnostics),
|
|
316
|
+
});
|
|
295
317
|
latestReviewReportPath = await writeJsonArtifact(
|
|
296
318
|
join(artifactDir, "review-round-latest.json"),
|
|
297
|
-
{ reviews: reviewEntries },
|
|
319
|
+
{ convergence_decision: roundConvergenceDecision, reviews: reviewEntries },
|
|
298
320
|
);
|
|
299
321
|
if (approved) break;
|
|
300
322
|
}
|
|
301
323
|
const qaVideoAvailable = existsSync(qaVideoPath);
|
|
302
|
-
if (createPr === true) {
|
|
324
|
+
if (createPr === true && approved) {
|
|
303
325
|
const prResult = await ctx.task("pull-request", {
|
|
304
326
|
prompt: taggedPrompt([
|
|
305
327
|
[
|
|
@@ -345,7 +367,7 @@ export async function runRalphWorkflow(
|
|
|
345
367
|
[
|
|
346
368
|
"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.",
|
|
347
369
|
"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.",
|
|
348
|
-
"When you successfully create or update the review request, create a provider-appropriate comment containing the implementation notes file contents as the last action
|
|
370
|
+
"When you successfully create or update the review request, create a provider-appropriate comment containing the implementation notes file contents as the last action after the review request exists.",
|
|
349
371
|
"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.",
|
|
350
372
|
"Leave the worktree intact for retries or user recovery.",
|
|
351
373
|
"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.",
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
export type ReviewNextAction =
|
|
2
|
+
| "implementation"
|
|
3
|
+
| "pull-request"
|
|
4
|
+
| "finish"
|
|
5
|
+
| "blocked"
|
|
6
|
+
| "needs_human";
|
|
7
|
+
|
|
8
|
+
export type ParseDiagnostics = {
|
|
9
|
+
readonly kind: "structured_output_parse_failure";
|
|
10
|
+
readonly message: string;
|
|
11
|
+
readonly raw_text_preview?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type ReviewConvergenceSummary = {
|
|
15
|
+
readonly parsed: boolean;
|
|
16
|
+
readonly approved: boolean;
|
|
17
|
+
readonly stopReviewLoop: boolean;
|
|
18
|
+
readonly nextAction: ReviewNextAction;
|
|
19
|
+
readonly finalActionRemaining: boolean;
|
|
20
|
+
readonly diagnostics: readonly string[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type ParsedReviewDecision<TDecision> = {
|
|
24
|
+
readonly decision: TDecision;
|
|
25
|
+
readonly parsed: boolean;
|
|
26
|
+
readonly diagnostics: readonly string[];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type RequirementTraceabilityLike = {
|
|
30
|
+
readonly requirement: string;
|
|
31
|
+
readonly status: "proven" | "contradicted" | "missing" | "unverified";
|
|
32
|
+
readonly evidence: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const FINAL_ACTION_PATTERN = /\b(?:pr|pull[- ]request|merge[- ]request|review request|github pr|create pr)\b/iu;
|
|
36
|
+
|
|
37
|
+
export function isFinalActionTraceability(
|
|
38
|
+
entry: RequirementTraceabilityLike,
|
|
39
|
+
): boolean {
|
|
40
|
+
return FINAL_ACTION_PATTERN.test(`${entry.requirement}\n${entry.evidence}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function finalActionRemaining(
|
|
44
|
+
traceability: readonly RequirementTraceabilityLike[],
|
|
45
|
+
): boolean {
|
|
46
|
+
return traceability.some(
|
|
47
|
+
(entry) => entry.status !== "proven" && isFinalActionTraceability(entry),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function traceabilityProvenExceptFinalAction(args: {
|
|
52
|
+
readonly traceability: readonly RequirementTraceabilityLike[];
|
|
53
|
+
readonly allowFinalActionRemaining: boolean;
|
|
54
|
+
}): boolean {
|
|
55
|
+
return args.traceability.length > 0 && args.traceability.every((entry) => {
|
|
56
|
+
if (entry.status === "proven") return true;
|
|
57
|
+
return args.allowFinalActionRemaining &&
|
|
58
|
+
entry.status !== "contradicted" &&
|
|
59
|
+
isFinalActionTraceability(entry);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const PREVIEW_LIMIT = 500;
|
|
64
|
+
|
|
65
|
+
function rawTextPreview(rawText: string): string | undefined {
|
|
66
|
+
const trimmed = rawText.trim();
|
|
67
|
+
if (trimmed.length === 0) return undefined;
|
|
68
|
+
return trimmed.slice(0, PREVIEW_LIMIT);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function parseFailureDiagnostics(
|
|
72
|
+
reviewer: string,
|
|
73
|
+
rawText: string,
|
|
74
|
+
): readonly string[] {
|
|
75
|
+
const preview = rawTextPreview(rawText);
|
|
76
|
+
const base = `Structured reviewer decision parse failed for ${reviewer}: no schema-valid JSON decision was returned.`;
|
|
77
|
+
return preview === undefined ? [base] : [base, `Raw reviewer output preview: ${preview}`];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function errorMessage(error: unknown): string {
|
|
81
|
+
return error instanceof Error ? error.message : String(error);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function reviewerFailureText(error: unknown): string {
|
|
85
|
+
const messages = [errorMessage(error)];
|
|
86
|
+
if (error instanceof AggregateError) {
|
|
87
|
+
for (const nested of error.errors) {
|
|
88
|
+
messages.push(errorMessage(nested));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return [...new Set(messages.map((message) => message.trim()).filter(Boolean))]
|
|
92
|
+
.join("\n");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function summarizeReviewConvergence(args: {
|
|
96
|
+
readonly parsed: boolean;
|
|
97
|
+
readonly approved: boolean;
|
|
98
|
+
readonly stopReviewLoop: boolean;
|
|
99
|
+
readonly nextAction: ReviewNextAction;
|
|
100
|
+
readonly finalActionRemaining?: boolean;
|
|
101
|
+
readonly diagnostics: readonly string[];
|
|
102
|
+
}): ReviewConvergenceSummary {
|
|
103
|
+
return {
|
|
104
|
+
parsed: args.parsed,
|
|
105
|
+
approved: args.approved,
|
|
106
|
+
stopReviewLoop: args.stopReviewLoop,
|
|
107
|
+
nextAction: args.nextAction,
|
|
108
|
+
finalActionRemaining: args.finalActionRemaining ?? args.nextAction === "pull-request",
|
|
109
|
+
diagnostics: args.diagnostics,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -64,6 +64,8 @@ For each non-trivial entrypoint the spec introduces or changes, walk these in or
|
|
|
64
64
|
- Please DO NOT implement anything in this stage, just create the comprehensive spec as described below.
|
|
65
65
|
- When writing the spec, DO NOT include information about concrete dates/timelines (e.g. # minutes, hours, days, weeks, etc.) and favor explicit phases (e.g. Phase 1, Phase 2, etc.).
|
|
66
66
|
- The spec MUST treat its entrypoint set as a first-class artifact. Section 5 is built around the **doors** (typed signatures, named failures, refusals expressed in types) and must pass the rubric above. Do not let intent leak into mechanism: a reviewer should reconstruct the system's purpose from §4.4 and §5.1 alone.
|
|
67
|
+
- If the spec is for a workflow with reviewer gates plus final actions (for example PR/MR/review creation, release tagging, deployment, or publication), explicitly separate implementation/review acceptance from those post-approval final actions. The workflow design should tell reviewers/reducers to approve and stop the implementation loop when implementation and validation criteria are proven and only an explicitly authorized final action remains; carry that remainder as a next action/final-action field instead of another implementation iteration.
|
|
68
|
+
- If the spec is for a workflow or workflow prompt refactor, require local, action-oriented stage/reviewer/reducer prompts. A model stage sees its prompt, artifacts, tools, and reads — not the workflow graph's name or surrounding implementation details unless explicitly provided — so the spec should prefer instructions like "review the current code delta" or "create/update the review request" over labels such as "the create-PR workflow stage", "this Goal run", or "the Ralph reviewer" unless that name is user-visible context or materially changes behavior.
|
|
67
69
|
- Once the spec is generated ask questions one at a time OR in logical groups:
|
|
68
70
|
- Refer to section "## 9. Open Questions / Unresolved Issues", go through each question one by one, and use **contrastive clarification** (presenting 2-3 specific options with concrete tradeoffs) rather than open-ended questions. This means presenting interpretations like "(A) Option X — tradeoff Y" and "(B) Option Z — tradeoff W" instead of asking "what do you think about X?". Update the spec with the user's answers as you walk through the questions.
|
|
69
71
|
- Interview the user relentlessly about every aspect of this plan/spec until you reach a shared understanding with them. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer (i.e., **contrastive clarification**). Pay special attention to the **doors**: every disagreement about what a joint is, what a door promises, what it refuses, or where a dangerous effect is funneled is a question worth resolving with the user.
|
|
@@ -10,13 +10,14 @@ import { isWorkflowDefinition, workflowDefinitionRequirementMessage } from "../r
|
|
|
10
10
|
import type { DurableWorkflowBackend } from "./backend.js";
|
|
11
11
|
import type { DurableScope } from "./scoped-backend.js";
|
|
12
12
|
import { recordCheckpointDurably } from "./tool-primitive.js";
|
|
13
|
+
import { stageCheckpointWithOutput, type DurableCompletedStageCheckpoint } from "./stage-primitive.js";
|
|
13
14
|
|
|
14
15
|
export function createDurableChildWorkflowPrimitive(input: {
|
|
15
16
|
readonly workflowId: string;
|
|
16
17
|
readonly rootWorkflowId: string;
|
|
17
18
|
readonly backend: DurableWorkflowBackend;
|
|
18
19
|
readonly nextReplayKey: (name: string) => string;
|
|
19
|
-
readonly recordCachedStage: (name: string, replayKey: string,
|
|
20
|
+
readonly recordCachedStage: (name: string, replayKey: string, checkpoint: DurableCompletedStageCheckpoint) => void;
|
|
20
21
|
/**
|
|
21
22
|
* Publish the durable scope computed for the next child invocation so the
|
|
22
23
|
* child runner can consume it and route its internal side-effect checkpoints
|
|
@@ -44,16 +45,17 @@ export function createDurableChildWorkflowPrimitive(input: {
|
|
|
44
45
|
const options = args[0] as { readonly stageName?: string } | undefined;
|
|
45
46
|
const boundaryName = options?.stageName ?? `workflow:${child.normalizedName}`;
|
|
46
47
|
const replayKey = input.nextReplayKey(boundaryName);
|
|
48
|
+
const cached = stageCheckpointWithOutput(input.backend, input.workflowId, replayKey, isWorkflowChildResult);
|
|
49
|
+
if (cached !== undefined && isWorkflowChildResult(cached.output)) {
|
|
50
|
+
input.recordCachedStage(boundaryName, replayKey, cached);
|
|
51
|
+
return cached.output as WorkflowChildResult<TChildOutputs>;
|
|
52
|
+
}
|
|
47
53
|
// Route this child's internal side-effect checkpoints under the root
|
|
48
|
-
// workflow with
|
|
49
|
-
//
|
|
54
|
+
// workflow with the same stable boundary key used by the live boundary
|
|
55
|
+
// stage, so mixed cached/live repeated child calls do not desynchronize
|
|
56
|
+
// boundary and durable child-scope ordinals.
|
|
50
57
|
// cross-ref: issue #1498.
|
|
51
58
|
input.setChildDurableScope({ rootWorkflowId: input.rootWorkflowId, scopePrefix: replayKey });
|
|
52
|
-
const cached = input.backend.getStageOutput(input.workflowId, replayKey);
|
|
53
|
-
if (cached !== undefined && isWorkflowChildResult(cached)) {
|
|
54
|
-
input.recordCachedStage(boundaryName, replayKey, cached);
|
|
55
|
-
return cached as WorkflowChildResult<TChildOutputs>;
|
|
56
|
-
}
|
|
57
59
|
const result = await input.workflow(child, ...args);
|
|
58
60
|
await recordCheckpointDurably(input.backend, {
|
|
59
61
|
kind: "stage",
|
|
@@ -63,11 +65,13 @@ export function createDurableChildWorkflowPrimitive(input: {
|
|
|
63
65
|
replayKey,
|
|
64
66
|
output: result as WorkflowSerializableValue,
|
|
65
67
|
completedAt: Date.now(),
|
|
68
|
+
result: result.status,
|
|
66
69
|
});
|
|
67
70
|
return result as WorkflowChildResult<TChildOutputs>;
|
|
68
71
|
};
|
|
69
72
|
}
|
|
70
73
|
|
|
74
|
+
|
|
71
75
|
function isWorkflowChildResult(value: WorkflowSerializableValue): value is WorkflowChildResult<WorkflowOutputValues> {
|
|
72
76
|
return typeof value === "object"
|
|
73
77
|
&& value !== null
|
|
@@ -377,7 +377,7 @@ function isMetadataStep(stepName: string): boolean {
|
|
|
377
377
|
interface DbosMetadataEnvelope {
|
|
378
378
|
readonly __atomicDurableMetadata: true;
|
|
379
379
|
readonly version: 1;
|
|
380
|
-
readonly entry:
|
|
380
|
+
readonly entry: WorkflowSerializableValue;
|
|
381
381
|
}
|
|
382
382
|
|
|
383
383
|
function encodeMetadata(entry: DurableCheckpointEntry): WorkflowSerializableValue {
|
|
@@ -404,7 +404,52 @@ function decodeMetadata(value: WorkflowSerializableValue): DurableCheckpointEntr
|
|
|
404
404
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
|
|
405
405
|
const raw = value as Partial<DbosMetadataEnvelope>;
|
|
406
406
|
if (raw.__atomicDurableMetadata !== true || raw.version !== METADATA_VERSION) return undefined;
|
|
407
|
-
|
|
407
|
+
const entry = raw.entry;
|
|
408
|
+
if (entry === undefined) return undefined;
|
|
409
|
+
return parseDurableCheckpointEntry(entry);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function parseDurableCheckpointEntry(value: WorkflowSerializableValue): DurableCheckpointEntry | undefined {
|
|
413
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
|
|
414
|
+
const entry = value as Partial<DurableCheckpointEntry>;
|
|
415
|
+
if (entry.type !== "workflow.durable.checkpoint"
|
|
416
|
+
|| typeof entry.workflowId !== "string"
|
|
417
|
+
|| typeof entry.name !== "string"
|
|
418
|
+
|| typeof entry.inputs !== "object"
|
|
419
|
+
|| entry.inputs === null
|
|
420
|
+
|| Array.isArray(entry.inputs)
|
|
421
|
+
|| typeof entry.status !== "string"
|
|
422
|
+
|| !isDurableWorkflowStatus(entry.status)
|
|
423
|
+
|| typeof entry.completedCheckpoints !== "number"
|
|
424
|
+
|| typeof entry.pendingPrompts !== "number"
|
|
425
|
+
|| typeof entry.ts !== "number"
|
|
426
|
+
|| (entry.label !== undefined && typeof entry.label !== "string")
|
|
427
|
+
|| (entry.rootWorkflowId !== undefined && typeof entry.rootWorkflowId !== "string")
|
|
428
|
+
|| (entry.resumable !== undefined && typeof entry.resumable !== "boolean")) {
|
|
429
|
+
return undefined;
|
|
430
|
+
}
|
|
431
|
+
return {
|
|
432
|
+
type: "workflow.durable.checkpoint",
|
|
433
|
+
workflowId: entry.workflowId,
|
|
434
|
+
name: entry.name,
|
|
435
|
+
inputs: entry.inputs,
|
|
436
|
+
status: entry.status,
|
|
437
|
+
completedCheckpoints: entry.completedCheckpoints,
|
|
438
|
+
pendingPrompts: entry.pendingPrompts,
|
|
439
|
+
ts: entry.ts,
|
|
440
|
+
...(entry.label !== undefined ? { label: entry.label } : {}),
|
|
441
|
+
...(entry.rootWorkflowId !== undefined ? { rootWorkflowId: entry.rootWorkflowId } : {}),
|
|
442
|
+
...(entry.resumable !== undefined ? { resumable: entry.resumable } : {}),
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function isDurableWorkflowStatus(value: string): value is DurableWorkflowStatus {
|
|
447
|
+
return value === "running"
|
|
448
|
+
|| value === "paused"
|
|
449
|
+
|| value === "completed"
|
|
450
|
+
|| value === "failed"
|
|
451
|
+
|| value === "cancelled"
|
|
452
|
+
|| value === "blocked";
|
|
408
453
|
}
|
|
409
454
|
|
|
410
455
|
function dbosStatusToDurable(status: string): DurableWorkflowStatus {
|
|
@@ -51,6 +51,14 @@ export interface DbosCheckpointEnvelope extends WorkflowSerializableObject {
|
|
|
51
51
|
readonly sessionId?: string;
|
|
52
52
|
readonly sessionFile?: string;
|
|
53
53
|
readonly completedAt: number;
|
|
54
|
+
readonly startedAt?: number;
|
|
55
|
+
readonly endedAt?: number;
|
|
56
|
+
readonly durationMs?: number;
|
|
57
|
+
readonly result?: string;
|
|
58
|
+
readonly model?: string;
|
|
59
|
+
readonly fastMode?: boolean;
|
|
60
|
+
readonly attemptedModels?: WorkflowSerializableValue;
|
|
61
|
+
readonly modelAttempts?: WorkflowSerializableValue;
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
/**
|
|
@@ -82,6 +90,14 @@ export function encodeCheckpoint(cp: DurableCheckpoint): DbosCheckpointEnvelope
|
|
|
82
90
|
replayKey: s.replayKey,
|
|
83
91
|
...(s.sessionId !== undefined ? { sessionId: s.sessionId } : {}),
|
|
84
92
|
...(s.sessionFile !== undefined ? { sessionFile: s.sessionFile } : {}),
|
|
93
|
+
...(s.startedAt !== undefined ? { startedAt: s.startedAt } : {}),
|
|
94
|
+
...(s.endedAt !== undefined ? { endedAt: s.endedAt } : {}),
|
|
95
|
+
...(s.durationMs !== undefined ? { durationMs: s.durationMs } : {}),
|
|
96
|
+
...(s.result !== undefined ? { result: s.result } : {}),
|
|
97
|
+
...(s.model !== undefined ? { model: s.model } : {}),
|
|
98
|
+
...(s.fastMode !== undefined ? { fastMode: s.fastMode } : {}),
|
|
99
|
+
...(s.attemptedModels !== undefined ? { attemptedModels: [...s.attemptedModels] } : {}),
|
|
100
|
+
...(s.modelAttempts !== undefined ? { modelAttempts: s.modelAttempts as WorkflowSerializableValue } : {}),
|
|
85
101
|
};
|
|
86
102
|
}
|
|
87
103
|
|
|
@@ -144,9 +160,21 @@ function decodeEnvelope(workflowId: string, env: DbosCheckpointEnvelope): Durabl
|
|
|
144
160
|
...(env.hasOutput !== false && env.output !== undefined ? { output: env.output } : {}),
|
|
145
161
|
...(env.sessionId !== undefined ? { sessionId: env.sessionId } : {}),
|
|
146
162
|
...(env.sessionFile !== undefined ? { sessionFile: env.sessionFile } : {}),
|
|
163
|
+
...(typeof env.startedAt === "number" ? { startedAt: env.startedAt } : {}),
|
|
164
|
+
...(typeof env.endedAt === "number" ? { endedAt: env.endedAt } : {}),
|
|
165
|
+
...(typeof env.durationMs === "number" ? { durationMs: env.durationMs } : {}),
|
|
166
|
+
...(typeof env.result === "string" ? { result: env.result } : {}),
|
|
167
|
+
...(typeof env.model === "string" ? { model: env.model } : {}),
|
|
168
|
+
...(typeof env.fastMode === "boolean" ? { fastMode: env.fastMode } : {}),
|
|
169
|
+
...(isStringArray(env.attemptedModels) ? { attemptedModels: env.attemptedModels } : {}),
|
|
170
|
+
...(Array.isArray(env.modelAttempts) ? { modelAttempts: env.modelAttempts as DurableStageCheckpoint["modelAttempts"] } : {}),
|
|
147
171
|
} as DurableStageCheckpoint;
|
|
148
172
|
}
|
|
149
173
|
|
|
174
|
+
|
|
175
|
+
function isStringArray(value: WorkflowSerializableValue | undefined): value is readonly string[] {
|
|
176
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
177
|
+
}
|
|
150
178
|
function decodeLegacy(
|
|
151
179
|
workflowId: string,
|
|
152
180
|
stepName: string,
|