agentweaver 0.1.14 → 0.1.15

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/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import { existsSync, readFileSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import process from "node:process";
5
5
  import { fileURLToPath } from "node:url";
6
- import { REVIEW_FILE_RE, bugAnalyzeArtifacts, bugAnalyzeJsonFile, bugFixDesignJsonFile, bugFixPlanJsonFile, designJsonFile, gitlabDiffFile, gitlabDiffJsonFile, ensureScopeWorkspaceDir, gitlabReviewFile, gitlabReviewJsonFile, latestArtifactIteration, nextArtifactIteration, planJsonFile, planArtifacts, qaJsonFile, readyToMergeFile, requireArtifacts, reviewAssessmentFile, reviewAssessmentJsonFile, reviewFile, reviewFixSelectionJsonFile, reviewJsonFile, scopeWorkspaceDir, flowStateFile, taskSummaryFile, } from "./artifacts.js";
6
+ import { bugAnalyzeArtifacts, bugAnalyzeJsonFile, bugFixDesignJsonFile, bugFixPlanJsonFile, designReviewFile, designReviewJsonFile, designJsonFile, gitlabDiffFile, gitlabDiffJsonFile, ensureScopeWorkspaceDir, gitlabReviewFile, gitlabReviewJsonFile, latestArtifactIteration, nextArtifactIteration, planJsonFile, planArtifacts, qaJsonFile, readyToMergeFile, requireArtifacts, reviewAssessmentFile, reviewAssessmentJsonFile, reviewFile, reviewFixSelectionJsonFile, reviewJsonFile, scopeWorkspaceDir, flowStateFile, taskSummaryFile, } from "./artifacts.js";
7
7
  import { FlowInterruptedError, TaskRunnerError } from "./errors.js";
8
8
  import { createFlowRunState, hasResumableFlowState, loadFlowRunState, prepareFlowStateForResume, resetFlowRunState, rewindFlowRunStateToPhase, saveFlowRunState, stripExecutionStatePayload, } from "./flow-state.js";
9
9
  import { requireJiraTaskFile } from "./jira.js";
@@ -20,11 +20,14 @@ import { resolveCmd } from "./runtime/command-resolution.js";
20
20
  import { loadTieredEnv } from "./runtime/env-loader.js";
21
21
  import { agentweaverHome } from "./runtime/agentweaver-home.js";
22
22
  import { runCommand } from "./runtime/process-runner.js";
23
+ import { resolveDesignReviewInputContract } from "./runtime/design-review-input-contract.js";
24
+ import { resolvePlanReviseInputContract } from "./runtime/plan-revise-input-contract.js";
25
+ import { clearReadyToMergeFile } from "./runtime/ready-to-merge.js";
23
26
  import { InteractiveUi } from "./interactive-ui.js";
24
27
  import { bye, printError, printInfo, printPanel, printSummary, setFlowExecutionState, stripAnsi, } from "./tui.js";
25
28
  import { requestUserInputInTerminal } from "./user-input.js";
26
29
  import { runDoctorCommand } from "./doctor/index.js";
27
- import { attachJiraContext, detectGitBranchName, requestJiraContext, resolveProjectScope, } from "./scope.js";
30
+ import { detectGitBranchName, requestJiraContext, resolveProjectScope, } from "./scope.js";
28
31
  const COMMANDS = [
29
32
  "auto-golang",
30
33
  "auto-common",
@@ -32,12 +35,14 @@ const COMMANDS = [
32
35
  "auto-reset",
33
36
  "bug-analyze",
34
37
  "bug-fix",
38
+ "design-review",
35
39
  "doctor",
36
40
  "git-commit",
37
41
  "gitlab-diff-review",
38
42
  "gitlab-review",
39
43
  "mr-description",
40
44
  "plan",
45
+ "plan-revise",
41
46
  "task-describe",
42
47
  "implement",
43
48
  "review",
@@ -99,9 +104,11 @@ function usage() {
99
104
  agentweaver gitlab-review [--dry] [--verbose] [--prompt <text>] [--scope <name>]
100
105
  agentweaver bug-analyze [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
101
106
  agentweaver bug-fix [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
107
+ agentweaver design-review [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
102
108
  agentweaver doctor [<category>|<check-id>] [--json]
103
109
  agentweaver mr-description [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
104
110
  agentweaver plan [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] [<jira-browse-url|jira-issue-key>]
111
+ agentweaver plan-revise [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
105
112
  agentweaver task-describe [--dry] [--verbose] [--prompt <text>] [<jira-browse-url|jira-issue-key>]
106
113
  agentweaver implement [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
107
114
  agentweaver review [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
@@ -364,17 +371,19 @@ function scopeWithRestoredJiraContext(scope, state) {
364
371
  if (scope.jiraRef || !state?.jiraRef?.trim()) {
365
372
  return scope;
366
373
  }
367
- return attachJiraContext(scope, state.jiraRef);
374
+ return resolveProjectScope(null, state.jiraRef);
375
+ }
376
+ function buildInteractiveBaseConfig(flowId, scope) {
377
+ return buildBaseConfig(flowId, {
378
+ ...(scope.jiraRef ? { jiraRef: scope.jiraRef } : {}),
379
+ });
368
380
  }
369
381
  function lookupInteractiveFlowResume(flowEntry, currentScope) {
370
382
  const directState = loadFlowRunState(currentScope.scopeKey, flowEntry.id);
371
383
  if (directState && hasResumableFlowState(directState)) {
372
384
  try {
373
385
  const effectiveScope = scopeWithRestoredJiraContext(currentScope, directState);
374
- const baseConfig = buildBaseConfig(flowEntry.id, {
375
- ...(effectiveScope.jiraRef ? { jiraRef: effectiveScope.jiraRef } : {}),
376
- scopeName: effectiveScope.scopeKey,
377
- });
386
+ const baseConfig = buildInteractiveBaseConfig(flowEntry.id, effectiveScope);
378
387
  const config = buildRuntimeConfig(baseConfig, effectiveScope);
379
388
  validateDeclarativeFlowResumeState(flowEntry, config, directState, directState.launchProfile);
380
389
  return {
@@ -410,21 +419,10 @@ function printAutoCommonPhasesHelp() {
410
419
  printPanel("Auto-Common Phases", phaseLines.join("\n"), "magenta");
411
420
  }
412
421
  function nextReviewIterationForTask(taskKey) {
413
- let maxIndex = 0;
414
- const workspaceDir = scopeWorkspaceDir(taskKey);
415
- if (!existsSync(workspaceDir)) {
416
- return 1;
417
- }
418
- for (const entry of readdirSync(workspaceDir, { withFileTypes: true })) {
419
- if (!entry.isFile()) {
420
- continue;
421
- }
422
- const match = REVIEW_FILE_RE.exec(entry.name);
423
- if (match && match[1] === taskKey) {
424
- maxIndex = Math.max(maxIndex, Number.parseInt(match[2] ?? "0", 10));
425
- }
426
- }
427
- return maxIndex + 1;
422
+ return nextArtifactIteration(taskKey, "review");
423
+ }
424
+ function nextDesignReviewIterationForTask(taskKey) {
425
+ return nextArtifactIteration(taskKey, "design-review");
428
426
  }
429
427
  function buildBaseConfig(command, options = {}) {
430
428
  return {
@@ -442,8 +440,10 @@ function buildBaseConfig(command, options = {}) {
442
440
  }
443
441
  function commandRequiresTask(command) {
444
442
  return (command === "plan" ||
443
+ command === "plan-revise" ||
445
444
  command === "bug-analyze" ||
446
445
  command === "bug-fix" ||
446
+ command === "design-review" ||
447
447
  command === "mr-description" ||
448
448
  command === "auto-golang" ||
449
449
  command === "auto-common" ||
@@ -865,6 +865,90 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
865
865
  }, launchProfile ? { launchProfile } : {}, requestUserInput, setSummary, launchMode, runtime);
866
866
  return false;
867
867
  }
868
+ if (config.command === "design-review") {
869
+ const iteration = nextDesignReviewIterationForTask(config.taskKey);
870
+ const inputContract = resolveDesignReviewInputContract(config.taskKey);
871
+ if (!config.dryRun) {
872
+ clearReadyToMergeFile(config.taskKey);
873
+ }
874
+ await runDeclarativeFlowBySpecFile("design-review.json", config, {
875
+ taskKey: config.taskKey,
876
+ iteration,
877
+ planningIteration: inputContract.planningIteration,
878
+ designFile: inputContract.designFile,
879
+ designJsonFile: inputContract.designJsonFile,
880
+ planFile: inputContract.planFile,
881
+ planJsonFile: inputContract.planJsonFile,
882
+ hasQaArtifacts: inputContract.hasQaArtifacts,
883
+ qaFilePath: inputContract.qaFilePath,
884
+ qaJsonFilePath: inputContract.qaJsonFilePath,
885
+ qaFile: inputContract.qaFile,
886
+ qaJsonFile: inputContract.qaJsonFile,
887
+ hasJiraTaskFile: inputContract.hasJiraTaskFile,
888
+ jiraTaskFilePath: inputContract.jiraTaskFilePath,
889
+ jiraTaskFile: inputContract.jiraTaskFile,
890
+ hasJiraAttachmentsManifestFile: inputContract.hasJiraAttachmentsManifestFile,
891
+ jiraAttachmentsManifestFilePath: inputContract.jiraAttachmentsManifestFilePath,
892
+ jiraAttachmentsManifestFile: inputContract.jiraAttachmentsManifestFile,
893
+ hasJiraAttachmentsContextFile: inputContract.hasJiraAttachmentsContextFile,
894
+ jiraAttachmentsContextFilePath: inputContract.jiraAttachmentsContextFilePath,
895
+ jiraAttachmentsContextFile: inputContract.jiraAttachmentsContextFile,
896
+ hasPlanningAnswersJsonFile: inputContract.hasPlanningAnswersJsonFile,
897
+ planningAnswersJsonFilePath: inputContract.planningAnswersJsonFilePath,
898
+ planningAnswersJsonFile: inputContract.planningAnswersJsonFile,
899
+ extraPrompt: config.extraPrompt,
900
+ }, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
901
+ if (!config.dryRun) {
902
+ printSummary("Design Review", `Artifacts:\n${designReviewFile(config.taskKey, iteration)}\n${designReviewJsonFile(config.taskKey, iteration)}`);
903
+ }
904
+ return false;
905
+ }
906
+ if (config.command === "plan-revise") {
907
+ const inputContract = resolvePlanReviseInputContract(config.taskKey);
908
+ if (!config.dryRun) {
909
+ clearReadyToMergeFile(config.taskKey);
910
+ }
911
+ await runDeclarativeFlowBySpecFile("plan-revise.json", config, {
912
+ taskKey: config.taskKey,
913
+ reviewIteration: inputContract.reviewIteration,
914
+ reviewFile: inputContract.reviewFile,
915
+ reviewJsonFile: inputContract.reviewJsonFile,
916
+ sourcePlanningIteration: inputContract.sourcePlanningIteration,
917
+ outputIteration: inputContract.outputIteration,
918
+ designFile: inputContract.designFile,
919
+ designJsonFile: inputContract.designJsonFile,
920
+ planFile: inputContract.planFile,
921
+ planJsonFile: inputContract.planJsonFile,
922
+ hasQaArtifacts: inputContract.hasQaArtifacts,
923
+ qaFilePath: inputContract.qaFilePath,
924
+ qaJsonFilePath: inputContract.qaJsonFilePath,
925
+ qaFile: inputContract.qaFile,
926
+ qaJsonFile: inputContract.qaJsonFile,
927
+ revisedDesignFile: inputContract.revisedDesignFile,
928
+ revisedDesignJsonFile: inputContract.revisedDesignJsonFile,
929
+ revisedPlanFile: inputContract.revisedPlanFile,
930
+ revisedPlanJsonFile: inputContract.revisedPlanJsonFile,
931
+ revisedQaFile: inputContract.revisedQaFile,
932
+ revisedQaJsonFile: inputContract.revisedQaJsonFile,
933
+ hasJiraTaskFile: inputContract.hasJiraTaskFile,
934
+ jiraTaskFilePath: inputContract.jiraTaskFilePath,
935
+ jiraTaskFile: inputContract.jiraTaskFile,
936
+ hasJiraAttachmentsManifestFile: inputContract.hasJiraAttachmentsManifestFile,
937
+ jiraAttachmentsManifestFilePath: inputContract.jiraAttachmentsManifestFilePath,
938
+ jiraAttachmentsManifestFile: inputContract.jiraAttachmentsManifestFile,
939
+ hasJiraAttachmentsContextFile: inputContract.hasJiraAttachmentsContextFile,
940
+ jiraAttachmentsContextFilePath: inputContract.jiraAttachmentsContextFilePath,
941
+ jiraAttachmentsContextFile: inputContract.jiraAttachmentsContextFile,
942
+ hasPlanningAnswersJsonFile: inputContract.hasPlanningAnswersJsonFile,
943
+ planningAnswersJsonFilePath: inputContract.planningAnswersJsonFilePath,
944
+ planningAnswersJsonFile: inputContract.planningAnswersJsonFile,
945
+ extraPrompt: config.extraPrompt,
946
+ }, launchProfile ? { launchProfile } : {}, requestUserInput, undefined, launchMode, runtime);
947
+ if (!config.dryRun) {
948
+ printSummary("Plan Revise", `Artifacts:\n${inputContract.revisedDesignFile}\n${inputContract.revisedDesignJsonFile}\n${inputContract.revisedPlanFile}\n${inputContract.revisedPlanJsonFile}\n${inputContract.revisedQaFile}\n${inputContract.revisedQaJsonFile}`);
949
+ }
950
+ return false;
951
+ }
868
952
  if (config.command === "gitlab-review") {
869
953
  const iteration = nextReviewIterationForTask(config.taskKey);
870
954
  const gitlabReviewIteration = nextArtifactIteration(config.taskKey, "gitlab-review");
@@ -1182,17 +1266,14 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
1182
1266
  throw new TaskRunnerError("Resume is impossible because launch profile was not saved. Use restart.");
1183
1267
  }
1184
1268
  const previousScopeKey = currentScope.scopeKey;
1185
- const baseConfig = buildBaseConfig(flowId, {
1186
- ...(currentScope.jiraRef ? { jiraRef: currentScope.jiraRef } : {}),
1187
- scopeName: currentScope.scopeKey,
1188
- });
1269
+ const baseConfig = buildInteractiveBaseConfig(flowId, currentScope);
1189
1270
  if (flowEntry.source === "built-in" && isBuiltInCommandFlowId(flowId)) {
1190
1271
  const nextScope = await resolveScopeForCommand(baseConfig, (form) => ui.requestUserInput(form));
1191
1272
  currentScope = nextScope;
1192
1273
  }
1193
1274
  else if (flowRequiresTaskScope(flowEntry) && !currentScope.jiraRef) {
1194
1275
  const jiraContext = await requestJiraContext((form) => ui.requestUserInput(form));
1195
- currentScope = attachJiraContext(currentScope, jiraContext.jiraRef);
1276
+ currentScope = resolveProjectScope(null, jiraContext.jiraRef);
1196
1277
  }
1197
1278
  ui.setScope(currentScope.scopeKey, currentScope.jiraIssueKey ?? null);
1198
1279
  if (previousScopeKey !== currentScope.scopeKey || currentScope.jiraIssueKey) {
@@ -1283,7 +1364,10 @@ export async function main(argv = process.argv.slice(2)) {
1283
1364
  return await runInteractive(args[0] ?? "", forceRefresh);
1284
1365
  }
1285
1366
  const parsedArgs = parseCliArgs(args);
1286
- await executeCommand(buildConfigFromArgs(parsedArgs));
1367
+ const commandCompleted = await executeCommand(buildConfigFromArgs(parsedArgs));
1368
+ if (parsedArgs.command === "doctor") {
1369
+ return commandCompleted ? 0 : 1;
1370
+ }
1287
1371
  return 0;
1288
1372
  }
1289
1373
  catch (error) {
@@ -8,11 +8,13 @@ export const BUILT_IN_COMMAND_FLOW_IDS = [
8
8
  "auto-common",
9
9
  "bug-analyze",
10
10
  "bug-fix",
11
+ "design-review",
11
12
  "git-commit",
12
13
  "gitlab-diff-review",
13
14
  "gitlab-review",
14
15
  "mr-description",
15
16
  "plan",
17
+ "plan-revise",
16
18
  "task-describe",
17
19
  "implement",
18
20
  "review",
@@ -26,11 +28,13 @@ const BUILT_IN_COMMAND_FLOW_FILES = {
26
28
  "auto-common": "auto-common.json",
27
29
  "bug-analyze": "bugz/bug-analyze.json",
28
30
  "bug-fix": "bugz/bug-fix.json",
31
+ "design-review": "design-review.json",
29
32
  "git-commit": "git-commit.json",
30
33
  "gitlab-diff-review": "gitlab/gitlab-diff-review.json",
31
34
  "gitlab-review": "gitlab/gitlab-review.json",
32
35
  "mr-description": "gitlab/mr-description.json",
33
36
  plan: "plan.json",
37
+ "plan-revise": "plan-revise.json",
34
38
  "task-describe": "task-describe.json",
35
39
  implement: "implement.json",
36
40
  review: "review/review.json",
@@ -0,0 +1,239 @@
1
+ {
2
+ "kind": "design-review-flow",
3
+ "version": 1,
4
+ "description": "Performs a structured planning critique of the latest completed planning artifacts against the task context available in the scope. Produces a verdict-oriented design review artifact and treats approved_with_warnings as ready to proceed.",
5
+ "phases": [
6
+ {
7
+ "id": "phase_1_load_contract",
8
+ "steps": [
9
+ {
10
+ "id": "validate_design_markdown_artifact",
11
+ "node": "file-check",
12
+ "params": {
13
+ "path": { "ref": "params.designFile" },
14
+ "panelTitle": { "const": "Design Markdown Artifact" },
15
+ "foundMessage": { "const": "Design markdown artifact found for the resolved planning iteration." },
16
+ "tone": { "const": "cyan" }
17
+ },
18
+ "expect": [
19
+ {
20
+ "kind": "require-file",
21
+ "path": { "ref": "params.designFile" },
22
+ "message": "Design-review contract is missing the required design markdown artifact."
23
+ }
24
+ ]
25
+ },
26
+ {
27
+ "id": "validate_design_json_artifact",
28
+ "node": "file-check",
29
+ "params": {
30
+ "path": { "ref": "params.designJsonFile" },
31
+ "panelTitle": { "const": "Design Structured Artifact" },
32
+ "foundMessage": { "const": "Design structured artifact found for the resolved planning iteration." },
33
+ "tone": { "const": "cyan" }
34
+ },
35
+ "expect": [
36
+ {
37
+ "kind": "require-file",
38
+ "path": { "ref": "params.designJsonFile" },
39
+ "message": "Design-review contract is missing the required design structured artifact."
40
+ }
41
+ ]
42
+ },
43
+ {
44
+ "id": "validate_plan_markdown_artifact",
45
+ "node": "file-check",
46
+ "params": {
47
+ "path": { "ref": "params.planFile" },
48
+ "panelTitle": { "const": "Plan Markdown Artifact" },
49
+ "foundMessage": { "const": "Implementation plan markdown artifact found for the resolved planning iteration." },
50
+ "tone": { "const": "cyan" }
51
+ },
52
+ "expect": [
53
+ {
54
+ "kind": "require-file",
55
+ "path": { "ref": "params.planFile" },
56
+ "message": "Design-review contract is missing the required implementation plan markdown artifact."
57
+ }
58
+ ]
59
+ },
60
+ {
61
+ "id": "validate_plan_json_artifact",
62
+ "node": "file-check",
63
+ "params": {
64
+ "path": { "ref": "params.planJsonFile" },
65
+ "panelTitle": { "const": "Plan Structured Artifact" },
66
+ "foundMessage": { "const": "Implementation plan structured artifact found for the resolved planning iteration." },
67
+ "tone": { "const": "cyan" }
68
+ },
69
+ "expect": [
70
+ {
71
+ "kind": "require-file",
72
+ "path": { "ref": "params.planJsonFile" },
73
+ "message": "Design-review contract is missing the required implementation plan structured artifact."
74
+ }
75
+ ]
76
+ },
77
+ {
78
+ "id": "validate_qa_markdown_artifact",
79
+ "when": { "ref": "params.hasQaArtifacts" },
80
+ "node": "file-check",
81
+ "params": {
82
+ "path": { "ref": "params.qaFilePath" },
83
+ "panelTitle": { "const": "QA Markdown Artifact" },
84
+ "foundMessage": { "const": "QA markdown artifact found and will be included in the critique." },
85
+ "tone": { "const": "cyan" }
86
+ },
87
+ "expect": [
88
+ {
89
+ "kind": "require-file",
90
+ "path": { "ref": "params.qaFilePath" },
91
+ "message": "Design-review contract expected a QA markdown artifact, but the file is missing."
92
+ }
93
+ ]
94
+ },
95
+ {
96
+ "id": "validate_qa_json_artifact",
97
+ "when": { "ref": "params.hasQaArtifacts" },
98
+ "node": "file-check",
99
+ "params": {
100
+ "path": { "ref": "params.qaJsonFilePath" },
101
+ "panelTitle": { "const": "QA Structured Artifact" },
102
+ "foundMessage": { "const": "QA structured artifact found and will be included in the critique." },
103
+ "tone": { "const": "cyan" }
104
+ },
105
+ "expect": [
106
+ {
107
+ "kind": "require-file",
108
+ "path": { "ref": "params.qaJsonFilePath" },
109
+ "message": "Design-review contract expected a QA structured artifact, but the file is missing."
110
+ }
111
+ ]
112
+ }
113
+ ]
114
+ },
115
+ {
116
+ "id": "phase_2_review",
117
+ "steps": [
118
+ {
119
+ "id": "run_design_review",
120
+ "node": "llm-prompt",
121
+ "prompt": {
122
+ "templateRef": "design-review",
123
+ "vars": {
124
+ "jira_task_file": { "ref": "params.jiraTaskFile" },
125
+ "jira_attachments_manifest_file": { "ref": "params.jiraAttachmentsManifestFile" },
126
+ "jira_attachments_context_file": { "ref": "params.jiraAttachmentsContextFile" },
127
+ "planning_answers_json_file": { "ref": "params.planningAnswersJsonFile" },
128
+ "design_file": { "ref": "params.designFile" },
129
+ "design_json_file": { "ref": "params.designJsonFile" },
130
+ "plan_file": { "ref": "params.planFile" },
131
+ "plan_json_file": { "ref": "params.planJsonFile" },
132
+ "qa_file": { "ref": "params.qaFile" },
133
+ "qa_json_file": { "ref": "params.qaJsonFile" },
134
+ "review_file": {
135
+ "artifact": {
136
+ "kind": "design-review-file",
137
+ "taskKey": { "ref": "params.taskKey" },
138
+ "iteration": { "ref": "params.iteration" }
139
+ }
140
+ },
141
+ "review_json_file": {
142
+ "artifact": {
143
+ "kind": "design-review-json-file",
144
+ "taskKey": { "ref": "params.taskKey" },
145
+ "iteration": { "ref": "params.iteration" }
146
+ }
147
+ }
148
+ },
149
+ "extraPrompt": { "ref": "params.extraPrompt" },
150
+ "format": "task-prompt"
151
+ },
152
+ "params": {
153
+ "labelText": {
154
+ "template": "Running design review (iteration {iteration})",
155
+ "vars": {
156
+ "iteration": { "ref": "params.iteration" }
157
+ }
158
+ },
159
+ "model": { "ref": "params.llmModel" },
160
+ "executor": { "ref": "params.llmExecutor" },
161
+ "requiredArtifacts": {
162
+ "list": [
163
+ {
164
+ "artifact": {
165
+ "kind": "design-review-file",
166
+ "taskKey": { "ref": "params.taskKey" },
167
+ "iteration": { "ref": "params.iteration" }
168
+ }
169
+ }
170
+ ]
171
+ }
172
+ },
173
+ "expect": [
174
+ {
175
+ "kind": "require-artifacts",
176
+ "when": { "not": { "ref": "context.dryRun" } },
177
+ "paths": {
178
+ "list": [
179
+ {
180
+ "artifact": {
181
+ "kind": "design-review-file",
182
+ "taskKey": { "ref": "params.taskKey" },
183
+ "iteration": { "ref": "params.iteration" }
184
+ }
185
+ }
186
+ ]
187
+ },
188
+ "message": "Design review did not produce the required review artifact."
189
+ },
190
+ {
191
+ "kind": "require-structured-artifacts",
192
+ "when": { "not": { "ref": "context.dryRun" } },
193
+ "items": [
194
+ {
195
+ "path": {
196
+ "artifact": {
197
+ "kind": "design-review-json-file",
198
+ "taskKey": { "ref": "params.taskKey" },
199
+ "iteration": { "ref": "params.iteration" }
200
+ }
201
+ },
202
+ "schemaId": "design-review/v1"
203
+ }
204
+ ],
205
+ "message": "Design review produced invalid structured artifacts."
206
+ }
207
+ ]
208
+ }
209
+ ]
210
+ },
211
+ {
212
+ "id": "phase_3_finalize",
213
+ "steps": [
214
+ {
215
+ "id": "check_ready_to_merge",
216
+ "node": "file-check",
217
+ "params": {
218
+ "path": {
219
+ "artifact": {
220
+ "kind": "ready-to-merge-file",
221
+ "taskKey": { "ref": "params.taskKey" }
222
+ }
223
+ },
224
+ "panelTitle": { "const": "Ready To Merge" },
225
+ "foundMessage": { "const": "Design review is ready to proceed. File ready-to-merge.md has been created." },
226
+ "tone": { "const": "green" }
227
+ }
228
+ },
229
+ {
230
+ "id": "notify_review_complete",
231
+ "node": "telegram-notify",
232
+ "params": {
233
+ "message": { "const": "Design review phase complete" }
234
+ }
235
+ }
236
+ ]
237
+ }
238
+ ]
239
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "kind": "implement-flow",
3
3
  "version": 1,
4
- "description": "Runs LLM-backed implementation based on previously approved design and plan artifacts. Executes code changes locally in the project working directory.",
4
+ "description": "Runs LLM-backed implementation based on previously approved design, plan, and QA artifacts. Executes code changes locally in the project working directory.",
5
5
  "phases": [
6
6
  {
7
7
  "id": "implement",
@@ -35,6 +35,18 @@
35
35
  "kind": "plan-json-file",
36
36
  "taskKey": { "ref": "params.taskKey" }
37
37
  }
38
+ },
39
+ "qa_file": {
40
+ "artifact": {
41
+ "kind": "qa-file",
42
+ "taskKey": { "ref": "params.taskKey" }
43
+ }
44
+ },
45
+ "qa_json_file": {
46
+ "artifact": {
47
+ "kind": "qa-json-file",
48
+ "taskKey": { "ref": "params.taskKey" }
49
+ }
38
50
  }
39
51
  },
40
52
  "extraPrompt": { "ref": "params.extraPrompt" },