chati-dev 4.2.2 → 4.3.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.
Files changed (220) hide show
  1. package/README.md +80 -53
  2. package/bin/chati.js +150 -5
  3. package/framework/agents/build/dev.md +509 -76
  4. package/framework/agents/deploy/devops.md +40 -25
  5. package/framework/agents/discover/brief.md +156 -22
  6. package/framework/agents/discover/brownfield-wu.md +24 -14
  7. package/framework/agents/discover/greenfield-wu.md +100 -14
  8. package/framework/agents/plan/architect-data-engineer.md +6 -6
  9. package/framework/agents/plan/architect-system.md +46 -12
  10. package/framework/agents/plan/architect.md +40 -20
  11. package/framework/agents/plan/detail.md +36 -24
  12. package/framework/agents/plan/phases.md +36 -26
  13. package/framework/agents/plan/tasks.md +114 -33
  14. package/framework/agents/plan/ux-brand-architect.md +240 -8
  15. package/framework/agents/plan/ux-component-engineer.md +28 -13
  16. package/framework/agents/plan/ux-researcher.md +7 -6
  17. package/framework/agents/plan/ux.md +55 -22
  18. package/framework/agents/quality/qa-implementation.md +143 -74
  19. package/framework/agents/quality/qa-planning.md +115 -42
  20. package/framework/agents/quality/qa-visual.md +439 -0
  21. package/framework/agents/shared/visualizer.md +128 -0
  22. package/framework/config.yaml +7 -6
  23. package/framework/constitution.md +127 -44
  24. package/framework/context/governance.md +12 -7
  25. package/framework/context/quality.md +6 -5
  26. package/framework/context/root.md +6 -6
  27. package/framework/data/entity-registry.yaml +377 -4
  28. package/framework/data/model-limits.json +19 -0
  29. package/framework/domains/agents/qa-visual.yaml +74 -0
  30. package/framework/domains/constitution.yaml +46 -2
  31. package/framework/domains/workflows/greenfield-fullstack.yaml +2 -2
  32. package/framework/hooks/advance-trigger.js +131 -0
  33. package/framework/hooks/brief-validator.js +83 -0
  34. package/framework/hooks/constitution-guard.js +24 -5
  35. package/framework/hooks/license-guard.js +62 -27
  36. package/framework/hooks/mode-governance.js +13 -2
  37. package/framework/hooks/model-governance.js +1 -0
  38. package/framework/hooks/post-dev.js +207 -0
  39. package/framework/hooks/prism-engine.js +274 -105
  40. package/framework/hooks/reasoning-escalator.js +371 -0
  41. package/framework/hooks/reference-trigger.js +117 -0
  42. package/framework/hooks/session-digest.js +50 -1
  43. package/framework/hooks/settings.json +32 -1
  44. package/framework/hooks/style-guard.js +25 -6
  45. package/framework/hooks/team-quality-gate.js +19 -12
  46. package/framework/hooks/undercover-guard.js +4 -2
  47. package/framework/i18n/en.yaml +3 -3
  48. package/framework/i18n/es.yaml +3 -3
  49. package/framework/i18n/fr.yaml +3 -3
  50. package/framework/i18n/pt.yaml +3 -3
  51. package/framework/intelligence/context-engine.md +4 -5
  52. package/framework/intelligence/decision-engine.md +1 -1
  53. package/framework/intelligence/hooks-performance.md +3 -3
  54. package/framework/migrations/v1.0-to-v1.1.yaml +1 -1
  55. package/framework/migrations/v1.4-to-v2.0.yaml +11 -11
  56. package/framework/migrations/v4.0-to-v4.1.yaml +2 -2
  57. package/framework/migrations/v4.2-to-v4.3.yaml +29 -0
  58. package/framework/orchestrator/chati-router.js +387 -0
  59. package/framework/orchestrator/chati-update.md +40 -40
  60. package/framework/orchestrator/chati.md +294 -94
  61. package/framework/scaffold/motion-premium/README.md +89 -0
  62. package/framework/scaffold/motion-premium/app/globals.css.template +400 -0
  63. package/framework/scaffold/motion-premium/app/layout.tsx.template +110 -0
  64. package/framework/scaffold/motion-premium/components/animation/BackgroundCrossfadeProvider.tsx.template +170 -0
  65. package/framework/scaffold/motion-premium/components/animation/LenisProvider.tsx.template +49 -0
  66. package/framework/scaffold/motion-premium/components/animation/PageTransitionWrapper.tsx.template +83 -0
  67. package/framework/scaffold/motion-premium/components/animation/Preloader.tsx.template +171 -0
  68. package/framework/scaffold/motion-premium/components/ui/Container.tsx.template +69 -0
  69. package/framework/scaffold/motion-premium/components/ui/PageSection.tsx.template +74 -0
  70. package/framework/scaffold/motion-premium/lib/animations/gsap.ts.template +112 -0
  71. package/framework/scaffold/motion-premium/lib/animations/refreshCoordinator.ts.template +75 -0
  72. package/framework/scaffold/motion-premium/lib/animations/tokens.ts.template +119 -0
  73. package/framework/scaffold/motion-premium/lib/animations/useGsapContext.ts.template +92 -0
  74. package/framework/scaffold/motion-premium/lib/animations/useScrollSnapStepper.ts.template +265 -0
  75. package/framework/scaffold/motion-premium/lib/animations/useSmoothScroll.ts.template +67 -0
  76. package/framework/scaffold/motion-premium/lib/brand.ts.template +43 -0
  77. package/framework/scaffold/motion-premium/scaffold.yaml +174 -0
  78. package/framework/scaffold/motion-premium-3d/README.md +80 -0
  79. package/framework/scaffold/motion-premium-3d/app/(3d)/scroll-demo/ScrollDemoCanvas.tsx.template +81 -0
  80. package/framework/scaffold/motion-premium-3d/app/(3d)/scroll-demo/ScrollDemoClient.tsx.template +75 -0
  81. package/framework/scaffold/motion-premium-3d/app/(3d)/scroll-demo/page.tsx.template +26 -0
  82. package/framework/scaffold/motion-premium-3d/components/3d/CameraRig.tsx.template +100 -0
  83. package/framework/scaffold/motion-premium-3d/components/3d/CanvasProvider.tsx.template +85 -0
  84. package/framework/scaffold/motion-premium-3d/components/3d/InvalidateOnScroll.tsx.template +51 -0
  85. package/framework/scaffold/motion-premium-3d/components/3d/MeshCrossfade.tsx.template +79 -0
  86. package/framework/scaffold/motion-premium-3d/components/3d/ScrollCrossfade.tsx.template +88 -0
  87. package/framework/scaffold/motion-premium-3d/components/3d/ScrollScene.tsx.template +121 -0
  88. package/framework/scaffold/motion-premium-3d/components/webgl/SceneFallback.tsx.template +65 -0
  89. package/framework/scaffold/motion-premium-3d/components/webgl/WebGLContext.tsx.template +68 -0
  90. package/framework/scaffold/motion-premium-3d/lib/webgl/detect.ts.template +69 -0
  91. package/framework/scaffold/motion-premium-3d/scaffold.yaml +133 -0
  92. package/framework/schemas/session.schema.json +109 -21
  93. package/framework/scripts/reference-capture.js +430 -0
  94. package/framework/scripts/visual-qa.js +674 -0
  95. package/framework/tasks/orchestrator-handoff.md +1 -1
  96. package/framework/tasks/orchestrator-resume.md +1 -1
  97. package/framework/tasks/orchestrator-route.md +1 -1
  98. package/framework/tasks/orchestrator-status.md +3 -3
  99. package/framework/tasks/qa-planning-gate-define.md +1 -1
  100. package/framework/templates/brandbook-html-tmpl.md +1 -1
  101. package/framework/templates/brandbook-tmpl.yaml +1 -1
  102. package/framework/templates/component-spec-tmpl.yaml +1 -1
  103. package/framework/templates/design-token-tmpl.yaml +1 -1
  104. package/framework/templates/icon-system-tmpl.yaml +1 -1
  105. package/framework/templates/team-planning-tasks.yaml +6 -5
  106. package/framework/workflows/brownfield-discovery.yaml +2 -2
  107. package/framework/workflows/brownfield-fullstack.yaml +15 -11
  108. package/framework/workflows/brownfield-service.yaml +14 -10
  109. package/framework/workflows/brownfield-ui.yaml +15 -11
  110. package/framework/workflows/greenfield-fullstack.yaml +16 -13
  111. package/framework/workflows/quick-flow.yaml +3 -3
  112. package/framework/workflows/standard-flow.yaml +12 -9
  113. package/package.json +10 -5
  114. package/src/autonomy/autonomous-gate.js +1 -0
  115. package/src/autonomy/build-state.js +1 -2
  116. package/src/autonomy/progress-reporter.js +1 -1
  117. package/src/config/agent-customizer.js +11 -3
  118. package/src/config/claude-settings-generator.js +27 -7
  119. package/src/config/context-file-generator.js +41 -21
  120. package/src/config/framework-adapter.js +1 -0
  121. package/src/config/gemini-hooks-generator.js +19 -7
  122. package/src/config/mcp-configs.js +1 -0
  123. package/src/context/layers/l1-global.js +2 -1
  124. package/src/dashboard/data-reader.js +4 -3
  125. package/src/dashboard/layout.js +2 -1
  126. package/src/decision/analyzer.js +6 -30
  127. package/src/decision/engine.js +4 -28
  128. package/src/decision/registry-healer.js +3 -2
  129. package/src/decision/registry-updater.js +23 -14
  130. package/src/extensions/loader.js +2 -8
  131. package/src/gates/g1-planning-complete.js +2 -1
  132. package/src/gates/g2-qa-planning.js +2 -1
  133. package/src/gates/g3-implementation.js +2 -1
  134. package/src/gates/g4-qa-implementation.js +3 -2
  135. package/src/gates/g5-deploy-ready.js +2 -1
  136. package/src/health/engine.js +4 -3
  137. package/src/installer/core.js +422 -81
  138. package/src/installer/preflight.js +131 -0
  139. package/src/installer/provider-overlay.js +3 -3
  140. package/src/installer/scaffold-applier.js +358 -0
  141. package/src/installer/templates.js +46 -29
  142. package/src/installer/validator.js +17 -12
  143. package/src/intelligence/registry-manager.js +22 -29
  144. package/src/intelligence/timeline.js +11 -6
  145. package/src/license/commands.js +1 -1
  146. package/src/license/wait.js +102 -0
  147. package/src/memory/agent-memory.js +81 -0
  148. package/src/memory/dream.js +32 -1
  149. package/src/merger/replace-merger.js +28 -15
  150. package/src/orchestrator/agent-selector.js +2 -1
  151. package/src/orchestrator/cli.js +1869 -71
  152. package/src/orchestrator/doctor.js +270 -0
  153. package/src/orchestrator/handoff-engine.js +4 -3
  154. package/src/orchestrator/index.js +2 -0
  155. package/src/orchestrator/pipeline-manager.js +306 -15
  156. package/src/orchestrator/session-manager.js +331 -6
  157. package/src/tasks/handoff.js +3 -2
  158. package/src/telemetry/config.js +4 -3
  159. package/src/telemetry/schema.js +1 -0
  160. package/src/terminal/collector.js +3 -2
  161. package/src/terminal/index.js +1 -2
  162. package/src/terminal/isolation.js +52 -18
  163. package/src/terminal/prompt-builder.js +42 -25
  164. package/src/terminal/run-parallel.js +1 -1
  165. package/src/terminal/run-team.js +3 -3
  166. package/src/terminal/team-task-list.js +43 -4
  167. package/src/upgrade/backup.js +3 -2
  168. package/src/upgrade/checker.js +3 -2
  169. package/src/upgrade/migrator.js +65 -7
  170. package/src/upgrade/tracked-files-detector.js +86 -0
  171. package/src/upgrade/user-messages.js +94 -0
  172. package/src/utils/config-parser.js +2 -1
  173. package/src/utils/feature-flags.js +2 -1
  174. package/src/utils/flatten-entities.js +69 -0
  175. package/src/utils/framework-dir.js +16 -0
  176. package/src/utils/model-id.js +85 -0
  177. package/src/utils/provider-limits.js +84 -23
  178. package/src/utils/schema-validator.js +1 -1
  179. package/src/wizard/i18n.js +5 -4
  180. package/src/wizard/index.js +14 -0
  181. package/assets/logo - c/303/263pia.png +0 -0
  182. package/assets/logo.svg +0 -42
  183. package/assets/logo2.png +0 -0
  184. package/assets/social-preview.png +0 -0
  185. package/scripts/bundle-framework.js +0 -69
  186. package/scripts/changelog-generator.js +0 -222
  187. package/scripts/codebase-mapper.js +0 -728
  188. package/scripts/commit-message-generator.js +0 -167
  189. package/scripts/coverage-analyzer.js +0 -260
  190. package/scripts/dependency-analyzer.js +0 -280
  191. package/scripts/doctor/checks/agents.js +0 -77
  192. package/scripts/doctor/checks/constitution.js +0 -41
  193. package/scripts/doctor/checks/domain-alignment.js +0 -58
  194. package/scripts/doctor/checks/prism-layers.js +0 -84
  195. package/scripts/doctor/checks/registry.js +0 -55
  196. package/scripts/doctor/checks/schemas.js +0 -61
  197. package/scripts/doctor/fixes/reference-fix.js +0 -100
  198. package/scripts/doctor/fixes/registry-fix.js +0 -56
  199. package/scripts/doctor/index.js +0 -212
  200. package/scripts/framework-analyzer.js +0 -308
  201. package/scripts/generate-constitution-domain.js +0 -253
  202. package/scripts/generate-signing-key.js +0 -33
  203. package/scripts/health-check.js +0 -481
  204. package/scripts/ide-sync.js +0 -326
  205. package/scripts/performance-analyzer.js +0 -325
  206. package/scripts/plan-tracker.js +0 -278
  207. package/scripts/populate-entity-registry.js +0 -481
  208. package/scripts/pr-review.js +0 -317
  209. package/scripts/rollback-manager.js +0 -310
  210. package/scripts/semantic-lint.js +0 -328
  211. package/scripts/sign-manifest.js +0 -53
  212. package/scripts/stuck-detector.js +0 -343
  213. package/scripts/test-quality-assessment.js +0 -257
  214. package/scripts/validate-agents.js +0 -368
  215. package/scripts/validate-package.js +0 -505
  216. package/scripts/validate-tasks.js +0 -465
  217. package/src/autonomy/worktree-manager.js +0 -250
  218. package/src/intelligence/decision-engine.js +0 -256
  219. package/src/intelligence/document-sharder.js +0 -221
  220. package/src/intelligence/elicitation.js +0 -265
@@ -55,6 +55,7 @@ export const AGENT_STATUS = {
55
55
  COMPLETED: 'completed',
56
56
  SKIPPED: 'skipped',
57
57
  NEEDS_REVALIDATION: 'needs_revalidation',
58
+ PENDING_REWORK: 'pending_rework',
58
59
  FAILED: 'failed',
59
60
  };
60
61
 
@@ -63,6 +64,131 @@ export const AGENT_STATUS = {
63
64
  */
64
65
  const QA_PLANNING_THRESHOLD = 95;
65
66
  const QA_IMPLEMENTATION_THRESHOLD = 95;
67
+ const QA_VISUAL_THRESHOLD = 90;
68
+
69
+ /**
70
+ * Fault Vector Protocol (Article XXII) — max correction cycles per (QA, target) pair
71
+ * before escalating to human.
72
+ */
73
+ export const MAX_CORRECTION_CYCLES = 2;
74
+
75
+ /**
76
+ * Fault Vector Protocol routing table. Given a failing QA agent and the majority
77
+ * fault_origin of its Decision Trail entries, returns the agent that should
78
+ * re-work. Origins not listed default to UNKNOWN → each QA's primary rework target.
79
+ */
80
+ const FVP_ROUTING = {
81
+ 'qa-planning': {
82
+ INTENT: 'brief',
83
+ SPEC: 'architect',
84
+ CODE: 'detail',
85
+ UNKNOWN: 'architect',
86
+ },
87
+ 'qa-implementation': {
88
+ INTENT: 'brief',
89
+ SPEC: 'tasks',
90
+ CODE: 'dev',
91
+ UNKNOWN: 'dev',
92
+ },
93
+ 'qa-visual': {
94
+ INTENT: 'brief',
95
+ SPEC: 'ux',
96
+ CODE: 'dev',
97
+ UNKNOWN: 'dev',
98
+ },
99
+ };
100
+
101
+ /**
102
+ * QA thresholds by agent name.
103
+ */
104
+ const QA_THRESHOLDS = {
105
+ 'qa-planning': QA_PLANNING_THRESHOLD,
106
+ 'qa-implementation': QA_IMPLEMENTATION_THRESHOLD,
107
+ 'qa-visual': QA_VISUAL_THRESHOLD,
108
+ };
109
+
110
+ /**
111
+ * Route a failing QA to the correct rework target via Fault Vector Protocol.
112
+ *
113
+ * @param {string} qaAgent - The QA agent that failed ('qa-planning' | 'qa-implementation' | 'qa-visual')
114
+ * @param {string} faultOrigin - INTENT | SPEC | CODE | DEFER | UNKNOWN
115
+ * @param {object} state - Pipeline state (used to honor brownfield greenfield-wu routing)
116
+ * @returns {{ nextAgent: string|null, reason: string }}
117
+ */
118
+ export function routeByFaultVector(qaAgent, faultOrigin, state = {}) {
119
+ const origin = (faultOrigin || 'UNKNOWN').toUpperCase();
120
+
121
+ // DEFER = defect acknowledged but out of scope for current pipeline — no rework.
122
+ if (origin === 'DEFER') {
123
+ return { nextAgent: null, reason: `${qaAgent} flagged DEFER — logging only, no rework` };
124
+ }
125
+
126
+ const table = FVP_ROUTING[qaAgent];
127
+ if (!table) {
128
+ return { nextAgent: null, reason: `Unknown QA agent: ${qaAgent}` };
129
+ }
130
+
131
+ let target = table[origin] || table.UNKNOWN;
132
+
133
+ // Honor greenfield/brownfield: INTENT issues in brownfield route to brownfield-wu
134
+ if (origin === 'INTENT' && state.isGreenfield === false) {
135
+ target = 'brownfield-wu';
136
+ }
137
+
138
+ return {
139
+ nextAgent: target,
140
+ reason: `${qaAgent} failed with fault_origin=${origin}, routing to ${target}`,
141
+ };
142
+ }
143
+
144
+ /**
145
+ * Determine the primary fault_origin from Decision Trail entries (majority vote of
146
+ * unresolved entries). Falls back to UNKNOWN when no data is available.
147
+ *
148
+ * @param {object} handoffData - { decision_trail_entries?: object[], resolved_decision_trail?: string[] }
149
+ * @returns {string} INTENT | SPEC | CODE | DEFER | UNKNOWN
150
+ */
151
+ export function extractPrimaryFaultOrigin(handoffData) {
152
+ const entries = handoffData?.decision_trail_entries || [];
153
+ if (entries.length === 0) return 'UNKNOWN';
154
+
155
+ // Consider only unresolved entries — resolved ones describe historical routing.
156
+ const active = entries.filter(e => !e.resolved);
157
+ if (active.length === 0) return 'UNKNOWN';
158
+
159
+ const counts = {};
160
+ for (const e of active) {
161
+ const origin = (e.fault_origin || 'UNKNOWN').toUpperCase();
162
+ counts[origin] = (counts[origin] || 0) + 1;
163
+ }
164
+
165
+ let winner = 'UNKNOWN';
166
+ let max = 0;
167
+ for (const [origin, count] of Object.entries(counts)) {
168
+ if (count > max) {
169
+ max = count;
170
+ winner = origin;
171
+ }
172
+ }
173
+ return winner;
174
+ }
175
+
176
+ /**
177
+ * Build the key used to track correction cycles per (qa, target) pair.
178
+ */
179
+ function correctionKey(qaAgent, target) {
180
+ return `${qaAgent}->${target}`;
181
+ }
182
+
183
+ /**
184
+ * Check if a QA agent's score is below its threshold.
185
+ */
186
+ function qaScoreBelowThreshold(qaAgent, score) {
187
+ const threshold = QA_THRESHOLDS[qaAgent];
188
+ if (threshold === undefined) return false;
189
+ if (score === null || score === undefined) return true;
190
+ return score < threshold;
191
+ }
66
192
 
67
193
  /**
68
194
  * Initialize a new pipeline for a project.
@@ -119,6 +245,7 @@ export function initPipeline(options = {}) {
119
245
  currentAgent: null,
120
246
  modeTransitions: [],
121
247
  history: [],
248
+ correctionCycles: {},
122
249
  };
123
250
  }
124
251
 
@@ -128,7 +255,8 @@ export function initPipeline(options = {}) {
128
255
  */
129
256
  const STANDARD_FLOW_AGENTS = [
130
257
  'brief', 'detail', 'architect', 'tasks',
131
- 'qa-planning', 'dev', 'qa-implementation', 'devops',
258
+ // qa-planning removed — now inside Planning Team, score synced via handleTeamDissolve
259
+ 'dev', 'qa-implementation', 'devops',
132
260
  ];
133
261
 
134
262
  /**
@@ -183,6 +311,7 @@ export function initQuickFlowPipeline(options = {}) {
183
311
  currentAgent: null,
184
312
  modeTransitions: [],
185
313
  history: [],
314
+ correctionCycles: {},
186
315
  };
187
316
  }
188
317
 
@@ -233,6 +362,7 @@ export function initStandardFlowPipeline(options = {}) {
233
362
  currentAgent: null,
234
363
  modeTransitions: [],
235
364
  history: [],
365
+ correctionCycles: {},
236
366
  };
237
367
  }
238
368
 
@@ -279,14 +409,15 @@ export function advancePipeline(pipelineState, completedAgent, results = {}) {
279
409
  });
280
410
 
281
411
  // Special handling for QA agents - check if they meet thresholds
282
- const isQAAgent = completedAgent === 'qa-planning' || completedAgent === 'qa-implementation';
412
+ const isQAAgent = completedAgent === 'qa-planning' || completedAgent === 'qa-implementation' || completedAgent === 'qa-visual';
283
413
  if (isQAAgent) {
284
414
  const transitionCheck = checkPhaseTransition(newState);
285
415
 
286
416
  if (transitionCheck.canAdvance) {
287
- // User Preview gate: when QA-Implementation passes in build phase,
288
- // don't advance to deploy yet — let user preview first.
289
- if (completedAgent === 'qa-implementation' && newState.phase === 'build') {
417
+ // User Preview gate: when the final QA gate (qa-visual) passes in build phase,
418
+ // don't advance to deploy yet — let user preview first. (4.3.0+: qa-visual is
419
+ // the final build gate; previously this was qa-implementation.)
420
+ if (completedAgent === 'qa-visual' && newState.phase === 'build') {
290
421
  return {
291
422
  state: newState,
292
423
  nextAction: 'user_preview',
@@ -294,7 +425,9 @@ export function advancePipeline(pipelineState, completedAgent, results = {}) {
294
425
  needsModeSwitch: false,
295
426
  contextBracket,
296
427
  previewContext: {
297
- qaScore: newState.agents['qa-implementation'].score,
428
+ qaImplScore: newState.agents['qa-implementation']?.score,
429
+ qaVisualScore: newState.agents['qa-visual']?.score,
430
+ qaScore: newState.agents['qa-visual']?.score,
298
431
  },
299
432
  };
300
433
  }
@@ -334,13 +467,119 @@ export function advancePipeline(pipelineState, completedAgent, results = {}) {
334
467
  contextBracket,
335
468
  };
336
469
  } else {
337
- // QA failed - wait for issues to be fixed
470
+ // QA failed trigger backward transition via Fault Vector Protocol.
471
+ // We re-open the rework target, NOT wait for manual intervention.
472
+ const faultOrigin = (results.faultOrigin || extractPrimaryFaultOrigin(results.handoffData)).toUpperCase();
473
+ const { nextAgent: correctionTarget, reason: routingReason } = routeByFaultVector(
474
+ completedAgent,
475
+ faultOrigin,
476
+ newState,
477
+ );
478
+
479
+ // DEFER or unroutable — log and stay in wait (legacy behavior)
480
+ if (!correctionTarget) {
481
+ newState.history.push({
482
+ agent: completedAgent,
483
+ action: 'qa_failed_no_rework',
484
+ timestamp: new Date().toISOString(),
485
+ faultOrigin,
486
+ score: results.score ?? null,
487
+ });
488
+ return {
489
+ state: newState,
490
+ nextAction: 'wait',
491
+ nextAgent: null,
492
+ needsModeSwitch: false,
493
+ contextBracket,
494
+ correction: {
495
+ source: completedAgent,
496
+ target: null,
497
+ faultOrigin,
498
+ reason: routingReason,
499
+ },
500
+ };
501
+ }
502
+
503
+ // Increment correction cycle counter for (qa → target) pair
504
+ newState.correctionCycles = newState.correctionCycles || {};
505
+ const key = correctionKey(completedAgent, correctionTarget);
506
+ const cycle = (newState.correctionCycles[key] || 0) + 1;
507
+ newState.correctionCycles[key] = cycle;
508
+
509
+ // Exceeded max cycles — escalate to human, don't loop forever
510
+ if (cycle > MAX_CORRECTION_CYCLES) {
511
+ newState.history.push({
512
+ agent: completedAgent,
513
+ action: 'correction_escalated',
514
+ target: correctionTarget,
515
+ cycle,
516
+ faultOrigin,
517
+ timestamp: new Date().toISOString(),
518
+ });
519
+ return {
520
+ state: newState,
521
+ nextAction: 'escalate',
522
+ nextAgent: null,
523
+ needsModeSwitch: false,
524
+ contextBracket,
525
+ correction: {
526
+ source: completedAgent,
527
+ target: correctionTarget,
528
+ faultOrigin,
529
+ cycle,
530
+ exceeded: true,
531
+ reason: `Max ${MAX_CORRECTION_CYCLES} correction cycles exceeded for ${key}. Human intervention required.`,
532
+ findings: results.findings || [],
533
+ score: results.score ?? null,
534
+ },
535
+ };
536
+ }
537
+
538
+ // Re-open target and every agent after it (they will need to re-run
539
+ // after target re-completes).
540
+ const targetIndex = AGENT_PIPELINE.findIndex(a => a.name === correctionTarget);
541
+ if (targetIndex >= 0) {
542
+ const toReset = AGENT_PIPELINE.slice(targetIndex).map(a => a.name);
543
+ for (const agentName of toReset) {
544
+ if (!newState.agents[agentName]) continue;
545
+ const isTarget = agentName === correctionTarget;
546
+ newState.agents[agentName] = {
547
+ status: isTarget ? AGENT_STATUS.PENDING_REWORK : AGENT_STATUS.PENDING,
548
+ score: null,
549
+ startedAt: isTarget ? new Date().toISOString() : null,
550
+ completedAt: null,
551
+ };
552
+ }
553
+ newState.completedAgents = newState.completedAgents.filter(a => !toReset.includes(a));
554
+ }
555
+
556
+ newState.currentAgent = correctionTarget;
557
+ newState.history.push({
558
+ agent: completedAgent,
559
+ action: 'triggered_correction',
560
+ target: correctionTarget,
561
+ cycle,
562
+ faultOrigin,
563
+ score: results.score ?? null,
564
+ timestamp: new Date().toISOString(),
565
+ });
566
+
338
567
  return {
339
568
  state: newState,
340
- nextAction: 'wait',
341
- nextAgent: null,
569
+ nextAction: 'correction_loop',
570
+ nextAgent: correctionTarget,
342
571
  needsModeSwitch: false,
343
572
  contextBracket,
573
+ correction: {
574
+ source: completedAgent,
575
+ target: correctionTarget,
576
+ faultOrigin,
577
+ cycle,
578
+ exceeded: false,
579
+ reason: routingReason,
580
+ findings: results.findings || [],
581
+ score: results.score ?? null,
582
+ },
344
583
  };
345
584
  }
346
585
  }
@@ -454,8 +693,16 @@ export function checkPhaseTransition(pipelineState) {
454
693
 
455
694
  // Check based on current phase
456
695
  if (phase === 'discover') {
457
- // DISCOVER -> PLAN transition: all discover agents must be completed
458
- // (brief must be done to advance to plan)
696
+ // DISCOVER -> PLAN transition: brief must be completed
697
+ // (WU runs first, then brief — both must finish before advancing to plan)
698
+ const brief = agents['brief'];
699
+ if (!brief || brief.status !== AGENT_STATUS.COMPLETED) {
700
+ return {
701
+ canAdvance: false,
702
+ reason: 'Brief agent not yet completed',
703
+ requiredScore: null,
704
+ };
705
+ }
459
706
  return {
460
707
  canAdvance: true,
461
708
  reason: 'Discover phase agents completed',
@@ -464,8 +711,14 @@ export function checkPhaseTransition(pipelineState) {
464
711
  }
465
712
 
466
713
  if (phase === 'plan') {
467
- // Need QA-Planning to be completed with score >= 95
714
+ // PLAN -> BUILD transition requires:
715
+ // 1. QA-Planning completed with score >= 95 (validates Planning Team output)
716
+ // 2. Phases completed (work broken into phases)
717
+ // 3. Tasks completed (task list created)
468
718
  const qaPlanning = agents['qa-planning'];
719
+ const phases = agents['phases'];
720
+ const tasks = agents['tasks'];
721
+
469
722
  if (!qaPlanning) {
470
723
  return {
471
724
  canAdvance: false,
@@ -490,17 +743,37 @@ export function checkPhaseTransition(pipelineState) {
490
743
  };
491
744
  }
492
745
 
746
+ if (!phases || phases.status !== AGENT_STATUS.COMPLETED) {
747
+ return {
748
+ canAdvance: false,
749
+ reason: 'Phases agent not yet completed',
750
+ requiredScore: null,
751
+ };
752
+ }
753
+
754
+ if (!tasks || tasks.status !== AGENT_STATUS.COMPLETED) {
755
+ return {
756
+ canAdvance: false,
757
+ reason: 'Tasks agent not yet completed',
758
+ requiredScore: null,
759
+ };
760
+ }
761
+
493
762
  return {
494
763
  canAdvance: true,
495
- reason: `QA-Planning approved with score ${qaPlanning.score}`,
764
+ reason: `Plan phase complete: QA-Planning ${qaPlanning.score}%, phases + tasks done`,
496
765
  requiredScore: null,
497
766
  };
498
767
  }
499
768
 
500
769
  if (phase === 'build') {
501
- // Need dev and QA-Implementation completed
770
+ // BUILD -> DEPLOY transition requires:
771
+ // 1. Dev completed (implementation)
772
+ // 2. QA-Implementation completed with score >= 95 (code quality)
773
+ // 3. QA-Visual completed with score >= 90 (visual quality)
502
774
  const dev = agents['dev'];
503
775
  const qaImpl = agents['qa-implementation'];
776
+ const qaVisual = agents['qa-visual'];
504
777
 
505
778
  if (!dev || !qaImpl) {
506
779
  return {
@@ -534,9 +807,27 @@ export function checkPhaseTransition(pipelineState) {
534
807
  };
535
808
  }
536
809
 
810
+ // QA-Visual is optional if agent is not in pipeline (older sessions)
811
+ if (qaVisual) {
812
+ if (qaVisual.status !== AGENT_STATUS.COMPLETED) {
813
+ return {
814
+ canAdvance: false,
815
+ reason: 'QA-Visual not yet completed',
816
+ requiredScore: 90,
817
+ };
818
+ }
819
+ if (qaVisual.score === null || qaVisual.score < 90) {
820
+ return {
821
+ canAdvance: false,
822
+ reason: `QA-Visual score ${qaVisual.score || 0} below threshold 90`,
823
+ requiredScore: 90,
824
+ };
825
+ }
826
+ }
827
+
537
828
  return {
538
829
  canAdvance: true,
539
- reason: `QA-Implementation approved with score ${qaImpl.score}`,
830
+ reason: `Build phase complete: QA-Impl ${qaImpl.score}%, QA-Visual ${qaVisual?.score ?? 'n/a'}%`,
540
831
  requiredScore: null,
541
832
  };
542
833
  }