MCPcopy Create free account
hub / github.com/QodeXcli/QodeX / processNode

Method processNode

src/orchestration/scheduler.ts:149–218  ·  view source on GitHub ↗

* Process one node end-to-end: run → review (retry on fail) → dry-merge → * commit. Mutates node.status. Returns when the node reaches a terminal * state (committed or failed).

(node: TaskNode)

Source from the content-addressed store, hash-verified

147 * state (committed or failed).
148 */
149 private async processNode(node: TaskNode): Promise<void> {
150 node.status = 'running';
151 this.emit({ type: 'node-start', id: node.id, role: node.role });
152
153 while (true) {
154 // RUN
155 let result: WorkerResult;
156 try {
157 result = await this.hooks.runNode(node, this.opts.signal);
158 } catch (e: any) {
159 node.status = 'failed';
160 this.emit({ type: 'node-failed', id: node.id, error: e?.message ?? String(e) });
161 return;
162 }
163 node.result = result;
164 if (!result.ok) {
165 if (node.attempts < this.opts.maxAttempts - 1) {
166 node.attempts++;
167 this.emit({ type: 'node-retry', id: node.id, attempt: node.attempts, blockers: [result.error ?? 'worker failed'] });
168 node.instruction += `\n\n[Retry ${node.attempts}] Previous attempt failed: ${result.error ?? 'unknown'}. Fix and complete.`;
169 continue;
170 }
171 node.status = 'failed';
172 this.emit({ type: 'node-failed', id: node.id, error: result.error ?? 'worker failed' });
173 return;
174 }
175
176 // REVIEW
177 node.status = 'review';
178 const verdict = await this.hooks.review(node, result, this.opts.signal);
179 this.emit({ type: 'node-review', id: node.id, passed: verdict.passed });
180 if (!verdict.passed) {
181 if (node.attempts < this.opts.maxAttempts - 1) {
182 node.attempts++;
183 this.emit({ type: 'node-retry', id: node.id, attempt: node.attempts, blockers: verdict.blockers });
184 node.instruction +=
185 `\n\n[Retry ${node.attempts}] QA found blockers:\n` +
186 verdict.blockers.map(b => `- ${b}`).join('\n') +
187 `\nAddress every blocker. Re-output the complete file(s).`;
188 node.status = 'running';
189 this.emit({ type: 'node-start', id: node.id, role: node.role });
190 continue;
191 }
192 node.status = 'failed';
193 this.emit({ type: 'node-failed', id: node.id, error: `QA blockers: ${verdict.blockers.join('; ')}` });
194 return;
195 }
196
197 // DRY-RUN MERGE
198 node.status = 'staged';
199 const conflicts = await this.hooks.dryRunMerge(node, result);
200 if (conflicts.length > 0) {
201 for (const c of conflicts) { this.conflicts.push(c); this.emit({ type: 'conflict', record: c }); }
202 // A logical/unresolvable conflict fails the node; serialized conflicts
203 // are handled by the file-lock guard so shouldn't reach here.
204 const fatal = conflicts.some(c => c.resolution !== 'serialized');
205 if (fatal) {
206 node.status = 'failed';

Callers 1

runMethod · 0.95

Calls 6

emitMethod · 0.95
runNodeMethod · 0.80
reviewMethod · 0.65
dryRunMergeMethod · 0.65
commitMethod · 0.65
pushMethod · 0.45

Tested by

no test coverage detected