chati-dev 4.3.0 → 4.4.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 (156) hide show
  1. package/README.md +18 -13
  2. package/bin/chati.js +32 -4
  3. package/framework/agents/build/dev.md +9 -5
  4. package/framework/agents/discover/brief.md +2 -0
  5. package/framework/agents/discover/brownfield-wu.md +2 -0
  6. package/framework/agents/discover/greenfield-wu.md +18 -1
  7. package/framework/agents/plan/detail.md +2 -0
  8. package/framework/agents/plan/tasks.md +31 -0
  9. package/framework/agents/plan/ux-brand-architect.md +21 -2
  10. package/framework/agents/plan/ux-component-engineer.md +10 -0
  11. package/framework/agents/quality/qa-implementation.md +1 -1
  12. package/framework/agents/quality/qa-planning.md +1 -1
  13. package/framework/agents/quality/qa-visual.md +33 -21
  14. package/framework/config.yaml +5 -4
  15. package/framework/constitution.md +58 -11
  16. package/framework/context/governance.md +12 -10
  17. package/framework/context/root.md +2 -2
  18. package/framework/data/entity-registry.yaml +12 -4
  19. package/framework/data/qa-rubrics.yaml +335 -0
  20. package/framework/domains/constitution.yaml +3 -3
  21. package/framework/executors/json-validate.js +80 -0
  22. package/framework/executors/npm-script.js +72 -0
  23. package/framework/executors/path-check.js +67 -0
  24. package/framework/executors/registry.yaml +36 -0
  25. package/framework/hooks/advance-trigger.js +47 -3
  26. package/framework/hooks/constitution-guard.js +9 -4
  27. package/framework/hooks/git-push-authority.js +113 -0
  28. package/framework/hooks/license-guard.js +59 -9
  29. package/framework/hooks/model-governance.js +40 -17
  30. package/framework/hooks/prism-engine.js +78 -11
  31. package/framework/hooks/session-digest.js +12 -3
  32. package/framework/hooks/settings.json +7 -3
  33. package/framework/hooks/style-guard.js +52 -3
  34. package/framework/hooks/team-quality-gate.js +43 -5
  35. package/framework/i18n/en.yaml +3 -3
  36. package/framework/i18n/es.yaml +3 -3
  37. package/framework/i18n/fr.yaml +3 -3
  38. package/framework/i18n/pt.yaml +3 -3
  39. package/framework/intelligence/confidence.yaml +85 -6
  40. package/framework/intelligence/context-engine.md +9 -5
  41. package/framework/intelligence/decision-engine.md +1 -1
  42. package/framework/orchestrator/chati-router.js +59 -5
  43. package/framework/orchestrator/chati-update.md +19 -3
  44. package/framework/orchestrator/chati.md +20 -1
  45. package/framework/quality-gates/planning-gate.md +2 -0
  46. package/framework/rules/human-writing-style.md +47 -0
  47. package/framework/scaffold/motion-premium/README.md +12 -2
  48. package/framework/scaffold/motion-premium/lib/animations/tokens.ts.template +1 -1
  49. package/framework/scaffold/motion-premium/scaffold.yaml +6 -1
  50. package/framework/scaffold/motion-premium-3d/README.md +10 -0
  51. package/framework/scaffold/motion-premium-3d/components/webgl/WebGLContext.tsx.template +1 -1
  52. package/framework/scaffold/motion-premium-3d/scaffold.yaml +3 -0
  53. package/framework/scaffold/saas-dashboard/README.md +58 -0
  54. package/framework/scaffold/saas-dashboard/app/dashboard/page.tsx.template +546 -0
  55. package/framework/scaffold/saas-dashboard/app/globals.css.template +746 -0
  56. package/framework/scaffold/saas-dashboard/app/layout.tsx.template +192 -0
  57. package/framework/scaffold/saas-dashboard/app/shell.css.template +1070 -0
  58. package/framework/scaffold/saas-dashboard/components/data/Chart.tsx.template +725 -0
  59. package/framework/scaffold/saas-dashboard/components/data/DataTable.tsx.template +623 -0
  60. package/framework/scaffold/saas-dashboard/components/data/DataTable.types.ts.template +104 -0
  61. package/framework/scaffold/saas-dashboard/components/data/DataTablePagination.tsx.template +234 -0
  62. package/framework/scaffold/saas-dashboard/components/data/SkeletonBlock.tsx.template +111 -0
  63. package/framework/scaffold/saas-dashboard/components/data/StatCard.tsx.template +393 -0
  64. package/framework/scaffold/saas-dashboard/components/data/StatCardGrid.tsx.template +121 -0
  65. package/framework/scaffold/saas-dashboard/components/feedback/EmptyState.tsx.template +281 -0
  66. package/framework/scaffold/saas-dashboard/components/interaction/CommandPalette.tsx.template +767 -0
  67. package/framework/scaffold/saas-dashboard/components/interaction/Field.tsx.template +351 -0
  68. package/framework/scaffold/saas-dashboard/components/interaction/Form.tsx.template +424 -0
  69. package/framework/scaffold/saas-dashboard/components/primitives/Badge.tsx.template +193 -0
  70. package/framework/scaffold/saas-dashboard/components/primitives/Button.tsx.template +261 -0
  71. package/framework/scaffold/saas-dashboard/components/primitives/Card.tsx.template +287 -0
  72. package/framework/scaffold/saas-dashboard/components/primitives/Input.tsx.template +310 -0
  73. package/framework/scaffold/saas-dashboard/components/primitives/Spinner.tsx.template +135 -0
  74. package/framework/scaffold/saas-dashboard/components/primitives/index.ts.template +60 -0
  75. package/framework/scaffold/saas-dashboard/components/shell/AppShell.tsx.template +174 -0
  76. package/framework/scaffold/saas-dashboard/components/shell/Breadcrumb.tsx.template +186 -0
  77. package/framework/scaffold/saas-dashboard/components/shell/Container.tsx.template +86 -0
  78. package/framework/scaffold/saas-dashboard/components/shell/NavItem.tsx.template +147 -0
  79. package/framework/scaffold/saas-dashboard/components/shell/Sidebar.tsx.template +378 -0
  80. package/framework/scaffold/saas-dashboard/components/shell/SidebarContext.tsx.template +169 -0
  81. package/framework/scaffold/saas-dashboard/components/shell/ThemeToggle.tsx.template +128 -0
  82. package/framework/scaffold/saas-dashboard/components/shell/Topbar.tsx.template +301 -0
  83. package/framework/scaffold/saas-dashboard/lib/brand.ts.template +324 -0
  84. package/framework/scaffold/saas-dashboard/scaffold.yaml +225 -0
  85. package/framework/schemas/session.schema.json +5 -0
  86. package/framework/schemas/task.schema.json +9 -0
  87. package/framework/scripts/visual-qa.js +101 -3
  88. package/framework/tasks/orchestrator-health.md +4 -4
  89. package/framework/tasks/qa-impl-verdict.md +11 -5
  90. package/framework/templates/brandbook-html-tmpl.md +1 -1
  91. package/framework/templates/qa-gate-tmpl.yaml +24 -11
  92. package/package.json +4 -3
  93. package/src/config/context-file-generator.js +0 -6
  94. package/src/dashboard/renderer.js +0 -36
  95. package/src/executors/runner.js +204 -0
  96. package/src/installer/core.js +13 -3
  97. package/src/installer/templates.js +5 -6
  98. package/src/intelligence/registry-manager.js +1 -1
  99. package/src/license/client.js +9 -3
  100. package/src/license/commands.js +12 -2
  101. package/src/license/machine-id.js +42 -1
  102. package/src/memory/gotchas.js +58 -40
  103. package/src/memory/magic-docs.js +1 -1
  104. package/src/memory/session-digest.js +9 -4
  105. package/src/orchestrator/cli.js +186 -15
  106. package/src/orchestrator/doctor.js +98 -16
  107. package/src/orchestrator/pipeline-manager.js +44 -22
  108. package/src/orchestrator/session-manager.js +64 -8
  109. package/src/telemetry/sender.js +7 -19
  110. package/src/terminal/run-team.js +3 -3
  111. package/src/upgrade/tracked-files-detector.js +34 -16
  112. package/src/utils/feature-flags.js +1 -1
  113. package/src/utils/flatten-entities.js +4 -40
  114. package/src/utils/schema-validator.js +0 -14
  115. package/src/wizard/i18n.js +3 -3
  116. package/src/api/index.js +0 -120
  117. package/src/autonomy/autonomous-gate.js +0 -294
  118. package/src/autonomy/build-loop.js +0 -281
  119. package/src/autonomy/build-state.js +0 -286
  120. package/src/autonomy/cause-analyzer.js +0 -177
  121. package/src/autonomy/escalation.js +0 -214
  122. package/src/autonomy/index.js +0 -51
  123. package/src/autonomy/mode-manager.js +0 -225
  124. package/src/autonomy/mode-suggester.js +0 -283
  125. package/src/autonomy/progress-reporter.js +0 -275
  126. package/src/autonomy/safety-net.js +0 -370
  127. package/src/config/agent-customizer.js +0 -231
  128. package/src/decision/analyzer.js +0 -291
  129. package/src/decision/engine.js +0 -250
  130. package/src/decision/index.js +0 -38
  131. package/src/decision/registry-healer.js +0 -468
  132. package/src/decision/registry-updater.js +0 -339
  133. package/src/extensions/loader.js +0 -145
  134. package/src/extensions/registry.js +0 -134
  135. package/src/gates/circuit-breaker.js +0 -151
  136. package/src/gates/g1-planning-complete.js +0 -154
  137. package/src/gates/g2-qa-planning.js +0 -156
  138. package/src/gates/g3-implementation.js +0 -215
  139. package/src/gates/g4-qa-implementation.js +0 -240
  140. package/src/gates/g5-deploy-ready.js +0 -181
  141. package/src/gates/gate-base.js +0 -185
  142. package/src/gates/index.js +0 -46
  143. package/src/health/auto-fix.js +0 -216
  144. package/src/health/engine.js +0 -246
  145. package/src/merger/semantic-merger.js +0 -292
  146. package/src/preview/detector.js +0 -238
  147. package/src/preview/index.js +0 -20
  148. package/src/preview/launcher.js +0 -235
  149. package/src/preview/log-buffer.js +0 -103
  150. package/src/quality/metrics-collector.js +0 -281
  151. package/src/quality/test-runner.js +0 -366
  152. package/src/tasks/executor.js +0 -195
  153. package/src/tasks/index.js +0 -4
  154. package/src/tasks/loader.js +0 -210
  155. package/src/tasks/router.js +0 -182
  156. package/src/utils/event-bus.js +0 -126
@@ -1,286 +0,0 @@
1
- /**
2
- * @fileoverview Build state manager with checkpoint-based persistence.
3
- *
4
- * Manages the state of autonomous build execution, including
5
- * checkpoints saved after each task completion, abandoned build
6
- * detection, and attempt logging.
7
- *
8
- * Constitution Article XVII — Execution Mode Governance.
9
- */
10
-
11
- import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from 'fs';
12
- import { join, dirname } from 'path';
13
-
14
- // ---------------------------------------------------------------------------
15
- // Constants
16
- // ---------------------------------------------------------------------------
17
-
18
- /** Maximum iterations per individual task before giving up */
19
- export const MAX_ITERATIONS_PER_TASK = 10;
20
-
21
- /** Maximum time per individual task before giving up (10 minutes) */
22
- export const MAX_TIME_PER_TASK_MS = 10 * 60 * 1000;
23
-
24
- /** Global timeout for an autonomous build session (30 minutes) */
25
- export const GLOBAL_TIMEOUT_MS = 30 * 60 * 1000;
26
-
27
- /** Time after which a build is considered abandoned (1 hour) */
28
- export const ABANDONED_THRESHOLD_MS = 60 * 60 * 1000;
29
-
30
- /**
31
- * Build status enum.
32
- * @enum {string}
33
- */
34
- export const BuildStatus = {
35
- PENDING: 'pending',
36
- IN_PROGRESS: 'in_progress',
37
- PAUSED: 'paused',
38
- ABANDONED: 'abandoned',
39
- FAILED: 'failed',
40
- COMPLETED: 'completed',
41
- };
42
-
43
- /**
44
- * Task checkpoint status.
45
- * @enum {string}
46
- */
47
- export const CheckpointStatus = {
48
- PENDING: 'pending',
49
- IN_PROGRESS: 'in_progress',
50
- COMPLETED: 'completed',
51
- FAILED: 'failed',
52
- SKIPPED: 'skipped',
53
- };
54
-
55
- // ---------------------------------------------------------------------------
56
- // State Schema
57
- // ---------------------------------------------------------------------------
58
-
59
- /**
60
- * @typedef {object} TaskCheckpoint
61
- * @property {string} taskId
62
- * @property {string} status - CheckpointStatus
63
- * @property {number} attempts - Number of execution attempts
64
- * @property {string|null} firstAttempt - ISO timestamp of first attempt
65
- * @property {string|null} lastAttempt - ISO timestamp of last attempt
66
- * @property {string|null} output - Last output summary
67
- * @property {string|null} error - Last error message (if failed)
68
- */
69
-
70
- /**
71
- * @typedef {object} BuildState
72
- * @property {string} sessionId - Build session identifier
73
- * @property {string} status - BuildStatus
74
- * @property {TaskCheckpoint[]} checkpoints - Per-task checkpoints
75
- * @property {string} startedAt - ISO timestamp
76
- * @property {string|null} lastCheckpoint - ISO timestamp of last checkpoint
77
- * @property {string|null} completedAt - ISO timestamp of completion
78
- * @property {number} totalAttempts - Total execution attempts across all tasks
79
- */
80
-
81
- const BUILD_STATE_FILE = '.chati/build-state.json';
82
-
83
- // ---------------------------------------------------------------------------
84
- // State Management
85
- // ---------------------------------------------------------------------------
86
-
87
- /**
88
- * Initialize a new build state.
89
- *
90
- * @param {string[]} taskIds - Array of task IDs to execute
91
- * @returns {BuildState}
92
- */
93
- export function createBuildState(taskIds) {
94
- return {
95
- sessionId: `build-${Date.now()}`,
96
- status: BuildStatus.PENDING,
97
- checkpoints: taskIds.map((taskId) => ({
98
- taskId,
99
- status: CheckpointStatus.PENDING,
100
- attempts: 0,
101
- firstAttempt: null,
102
- lastAttempt: null,
103
- output: null,
104
- error: null,
105
- })),
106
- startedAt: new Date().toISOString(),
107
- lastCheckpoint: null,
108
- completedAt: null,
109
- totalAttempts: 0,
110
- };
111
- }
112
-
113
- /**
114
- * Load build state from disk.
115
- *
116
- * @param {string} projectDir
117
- * @returns {BuildState|null}
118
- */
119
- export function loadBuildState(projectDir) {
120
- const statePath = join(projectDir, BUILD_STATE_FILE);
121
- if (!existsSync(statePath)) return null;
122
- try {
123
- const state = JSON.parse(readFileSync(statePath, 'utf-8'));
124
-
125
- // Check for abandoned builds
126
- if (state.status === BuildStatus.IN_PROGRESS && state.lastCheckpoint) {
127
- const elapsed = Date.now() - new Date(state.lastCheckpoint).getTime();
128
- if (elapsed > ABANDONED_THRESHOLD_MS) {
129
- state.status = BuildStatus.ABANDONED;
130
- }
131
- }
132
-
133
- return state;
134
- } catch { /* expected: operation may fail gracefully */
135
- return null;
136
- }
137
- }
138
-
139
- /**
140
- * Save build state to disk.
141
- *
142
- * @param {string} projectDir
143
- * @param {BuildState} state
144
- */
145
- export function saveBuildState(projectDir, state) {
146
- const statePath = join(projectDir, BUILD_STATE_FILE);
147
- mkdirSync(dirname(statePath), { recursive: true });
148
- writeFileSync(statePath, JSON.stringify(state, null, 2));
149
- }
150
-
151
- /**
152
- * Update a task checkpoint.
153
- *
154
- * @param {BuildState} state
155
- * @param {string} taskId
156
- * @param {Partial<TaskCheckpoint>} update
157
- * @returns {BuildState}
158
- */
159
- export function updateCheckpoint(state, taskId, update) {
160
- const checkpoint = state.checkpoints.find((c) => c.taskId === taskId);
161
- if (!checkpoint) {
162
- throw new Error(`Task "${taskId}" not found in build state`);
163
- }
164
-
165
- // Set firstAttempt on the first execution attempt
166
- if (update.attempts !== undefined && update.attempts > 0 && !checkpoint.firstAttempt) {
167
- checkpoint.firstAttempt = new Date().toISOString();
168
- }
169
-
170
- Object.assign(checkpoint, update);
171
- state.lastCheckpoint = new Date().toISOString();
172
-
173
- if (update.attempts !== undefined) {
174
- state.totalAttempts = state.checkpoints.reduce((sum, c) => sum + c.attempts, 0);
175
- }
176
-
177
- return state;
178
- }
179
-
180
- /**
181
- * Mark the build as started.
182
- *
183
- * @param {BuildState} state
184
- * @returns {BuildState}
185
- */
186
- export function startBuild(state) {
187
- state.status = BuildStatus.IN_PROGRESS;
188
- state.startedAt = new Date().toISOString();
189
- return state;
190
- }
191
-
192
- /**
193
- * Mark the build as completed.
194
- *
195
- * @param {BuildState} state
196
- * @returns {BuildState}
197
- */
198
- export function completeBuild(state) {
199
- state.status = BuildStatus.COMPLETED;
200
- state.completedAt = new Date().toISOString();
201
- return state;
202
- }
203
-
204
- /**
205
- * Mark the build as failed.
206
- *
207
- * @param {BuildState} state
208
- * @param {string} [reason]
209
- * @returns {BuildState}
210
- */
211
- export function failBuild(state, _reason) {
212
- state.status = BuildStatus.FAILED;
213
- state.completedAt = new Date().toISOString();
214
- return state;
215
- }
216
-
217
- /**
218
- * Get the next pending task checkpoint.
219
- *
220
- * @param {BuildState} state
221
- * @returns {TaskCheckpoint|null}
222
- */
223
- export function getNextPendingTask(state) {
224
- return state.checkpoints.find(
225
- (c) => c.status === CheckpointStatus.PENDING || c.status === CheckpointStatus.IN_PROGRESS,
226
- ) || null;
227
- }
228
-
229
- /**
230
- * Check if a task has exceeded max iterations or max time.
231
- *
232
- * Dual check: exhausted if attempts >= MAX_ITERATIONS_PER_TASK
233
- * OR if elapsed time since first attempt >= MAX_TIME_PER_TASK_MS.
234
- *
235
- * @param {TaskCheckpoint} checkpoint
236
- * @returns {boolean}
237
- */
238
- export function isTaskExhausted(checkpoint) {
239
- if (checkpoint.attempts >= MAX_ITERATIONS_PER_TASK) return true;
240
- if (checkpoint.firstAttempt) {
241
- const elapsed = Date.now() - new Date(checkpoint.firstAttempt).getTime();
242
- if (elapsed >= MAX_TIME_PER_TASK_MS) return true;
243
- }
244
- return false;
245
- }
246
-
247
- /**
248
- * Check if the global timeout has been exceeded.
249
- *
250
- * @param {BuildState} state
251
- * @returns {boolean}
252
- */
253
- export function isTimedOut(state) {
254
- const elapsed = Date.now() - new Date(state.startedAt).getTime();
255
- return elapsed > GLOBAL_TIMEOUT_MS;
256
- }
257
-
258
- /**
259
- * Get build progress summary.
260
- *
261
- * @param {BuildState} state
262
- * @returns {{ total: number, completed: number, failed: number, pending: number, progress: number }}
263
- */
264
- export function getProgress(state) {
265
- const total = state.checkpoints.length;
266
- const completed = state.checkpoints.filter((c) => c.status === CheckpointStatus.COMPLETED).length;
267
- const failed = state.checkpoints.filter((c) => c.status === CheckpointStatus.FAILED).length;
268
- const pending = state.checkpoints.filter(
269
- (c) => c.status === CheckpointStatus.PENDING || c.status === CheckpointStatus.IN_PROGRESS,
270
- ).length;
271
- const progress = total > 0 ? Math.round((completed / total) * 100) : 0;
272
-
273
- return { total, completed, failed, pending, progress };
274
- }
275
-
276
- /**
277
- * Clear build state (after completion or manual reset).
278
- *
279
- * @param {string} projectDir
280
- */
281
- export function clearBuildState(projectDir) {
282
- const statePath = join(projectDir, BUILD_STATE_FILE);
283
- if (existsSync(statePath)) {
284
- unlinkSync(statePath);
285
- }
286
- }
@@ -1,177 +0,0 @@
1
- /**
2
- * @fileoverview Cause analysis for failed build loop tasks.
3
- *
4
- * Analyzes agent output to classify failure categories, detect
5
- * repetitive patterns, and generate retry guidance.
6
- *
7
- * Constitution Article XVII — Execution Mode Governance.
8
- */
9
-
10
- // ---------------------------------------------------------------------------
11
- // Failure Categories
12
- // ---------------------------------------------------------------------------
13
-
14
- /**
15
- * Categories of task failures.
16
- * @enum {string}
17
- */
18
- export const FAILURE_CATEGORIES = {
19
- SYNTAX_ERROR: 'syntax_error',
20
- TEST_FAILURE: 'test_failure',
21
- LINT_VIOLATION: 'lint_violation',
22
- TYPE_ERROR: 'type_error',
23
- TIMEOUT: 'timeout',
24
- SCOPE_VIOLATION: 'scope_violation',
25
- DEPENDENCY: 'dependency',
26
- RUNTIME_ERROR: 'runtime_error',
27
- UNKNOWN: 'unknown',
28
- };
29
-
30
- /**
31
- * Patterns for classifying failure output.
32
- */
33
- const CATEGORY_PATTERNS = [
34
- { regex: /SyntaxError|Unexpected token|parsing error|unterminated/i, category: FAILURE_CATEGORIES.SYNTAX_ERROR },
35
- { regex: /test.*fail|assertion.*fail|expect.*receive|expected.*but got/i, category: FAILURE_CATEGORIES.TEST_FAILURE },
36
- { regex: /lint.*error|eslint|prettier.*error|formatting/i, category: FAILURE_CATEGORIES.LINT_VIOLATION },
37
- { regex: /TypeError|type.*mismatch|cannot read propert|is not a function/i, category: FAILURE_CATEGORIES.TYPE_ERROR },
38
- { regex: /timeout|ETIMEDOUT|exceeded.*time|timed out/i, category: FAILURE_CATEGORIES.TIMEOUT },
39
- { regex: /scope.*violation|cannot write|not allowed.*mode|Article XI/i, category: FAILURE_CATEGORIES.SCOPE_VIOLATION },
40
- { regex: /Cannot find module|ENOENT|module not found|import.*failed|missing dependency/i, category: FAILURE_CATEGORIES.DEPENDENCY },
41
- { regex: /ReferenceError|RangeError|Error:|runtime error|ENOMEM/i, category: FAILURE_CATEGORIES.RUNTIME_ERROR },
42
- ];
43
-
44
- // ---------------------------------------------------------------------------
45
- // Public API
46
- // ---------------------------------------------------------------------------
47
-
48
- /**
49
- * Analyze the cause of a task failure.
50
- *
51
- * @param {string} output - Agent output/error text
52
- * @param {Array<{category: string, output: string}>} [previousAttempts=[]] - History of prior attempts
53
- * @returns {{ category: string, rootCause: string, suggestion: string, isRepetitive: boolean }}
54
- */
55
- export function analyzeCause(output, previousAttempts = []) {
56
- const text = output || '';
57
- const category = classifyOutput(text);
58
- const rootCause = extractRootCause(text, category);
59
- const suggestion = generateSuggestion(category, rootCause);
60
- const isRepetitive = checkRepetitive(category, previousAttempts);
61
-
62
- return { category, rootCause, suggestion, isRepetitive };
63
- }
64
-
65
- /**
66
- * Build retry guidance for the next attempt based on cause analysis.
67
- *
68
- * @param {{ category: string, rootCause: string, suggestion: string, isRepetitive: boolean }} analysis
69
- * @param {number} attempt - Current attempt number
70
- * @returns {string} Guidance text for the agent
71
- */
72
- export function buildRetryGuidance(analysis, attempt) {
73
- const parts = [];
74
-
75
- parts.push(`## Retry Guidance (Attempt ${attempt})`);
76
- parts.push('');
77
- parts.push(`**Previous failure**: ${analysis.category}`);
78
-
79
- if (analysis.rootCause) {
80
- parts.push(`**Root cause**: ${analysis.rootCause}`);
81
- }
82
-
83
- parts.push(`**Suggestion**: ${analysis.suggestion}`);
84
-
85
- if (analysis.isRepetitive) {
86
- parts.push('');
87
- parts.push('**WARNING**: This is a REPETITIVE failure. The same category of error occurred in previous attempts.');
88
- parts.push('You MUST try a fundamentally different approach — do not repeat the same fix.');
89
- }
90
-
91
- return parts.join('\n');
92
- }
93
-
94
- // ---------------------------------------------------------------------------
95
- // Internal Helpers
96
- // ---------------------------------------------------------------------------
97
-
98
- /**
99
- * Classify output text into a failure category.
100
- *
101
- * @param {string} text
102
- * @returns {string}
103
- */
104
- function classifyOutput(text) {
105
- for (const { regex, category } of CATEGORY_PATTERNS) {
106
- if (regex.test(text)) {
107
- return category;
108
- }
109
- }
110
- return FAILURE_CATEGORIES.UNKNOWN;
111
- }
112
-
113
- /**
114
- * Extract a concise root cause from the output.
115
- *
116
- * @param {string} text
117
- * @param {string} category
118
- * @returns {string}
119
- */
120
- function extractRootCause(text, category) {
121
- if (!text) return 'No output available';
122
-
123
- // Try to find the most informative error line
124
- const lines = text.split('\n').filter(l => l.trim());
125
-
126
- // Look for lines containing "Error:" or similar markers
127
- const errorLine = lines.find(l =>
128
- /error:|Error:|ERR!|FAIL|failed/i.test(l)
129
- );
130
-
131
- if (errorLine) {
132
- return errorLine.trim().slice(0, 200);
133
- }
134
-
135
- // Fall back to last non-empty line (often the summary)
136
- if (lines.length > 0) {
137
- return lines[lines.length - 1].trim().slice(0, 200);
138
- }
139
-
140
- return `${category} detected (no specific error line found)`;
141
- }
142
-
143
- /**
144
- * Generate a suggestion based on the failure category.
145
- *
146
- * @param {string} category
147
- * @param {string} rootCause
148
- * @returns {string}
149
- */
150
- function generateSuggestion(category, _rootCause) {
151
- const suggestions = {
152
- [FAILURE_CATEGORIES.SYNTAX_ERROR]: 'Check for missing brackets, semicolons, or malformed expressions. Review the exact line mentioned in the error.',
153
- [FAILURE_CATEGORIES.TEST_FAILURE]: 'Review the failing test assertions. Ensure the implementation matches the expected behavior described in the test.',
154
- [FAILURE_CATEGORIES.LINT_VIOLATION]: 'Fix formatting and style issues. Check import ordering, unused variables, and indentation.',
155
- [FAILURE_CATEGORIES.TYPE_ERROR]: 'Verify that variable types match expected types. Check for null/undefined access and incorrect function signatures.',
156
- [FAILURE_CATEGORIES.TIMEOUT]: 'The operation took too long. Consider optimizing the approach or breaking the task into smaller pieces.',
157
- [FAILURE_CATEGORIES.SCOPE_VIOLATION]: 'The write operation is outside the allowed scope for the current mode. Only modify files within the permitted directories.',
158
- [FAILURE_CATEGORIES.DEPENDENCY]: 'A required module or dependency is missing. Ensure all imports reference existing files and packages are installed.',
159
- [FAILURE_CATEGORIES.RUNTIME_ERROR]: 'A runtime error occurred during execution. Check for logic errors, invalid operations, and edge cases.',
160
- [FAILURE_CATEGORIES.UNKNOWN]: 'Review the full output to identify the issue. The error does not match known patterns.',
161
- };
162
-
163
- return suggestions[category] || suggestions[FAILURE_CATEGORIES.UNKNOWN];
164
- }
165
-
166
- /**
167
- * Check if the same failure category appeared in previous attempts.
168
- *
169
- * @param {string} category
170
- * @param {Array<{category: string}>} previousAttempts
171
- * @returns {boolean}
172
- */
173
- function checkRepetitive(category, previousAttempts) {
174
- if (category === FAILURE_CATEGORIES.UNKNOWN) return false;
175
- const sameCategory = previousAttempts.filter(a => a.category === category);
176
- return sameCategory.length >= 2;
177
- }
@@ -1,214 +0,0 @@
1
- /**
2
- * @fileoverview Progressive escalation for build loop retries.
3
- *
4
- * When a task fails repeatedly, escalation progressively increases
5
- * the resources (model tier, context enrichment) and ultimately
6
- * pauses for human intervention.
7
- *
8
- * Constitution Article XVII — Execution Mode Governance.
9
- */
10
-
11
- // ---------------------------------------------------------------------------
12
- // Escalation Levels
13
- // ---------------------------------------------------------------------------
14
-
15
- /**
16
- * Escalation level definitions.
17
- * @enum {number}
18
- */
19
- export const ESCALATION_LEVELS = {
20
- /** Standard — use configured model, no extra context. */
21
- STANDARD: 0,
22
- /** Enriched — same model, inject cause analysis + retry guidance. */
23
- ENRICHED: 1,
24
- /** Upgraded — escalate to higher-tier model (e.g. sonnet → opus). */
25
- UPGRADED: 2,
26
- /** Max — pause execution and request human intervention. */
27
- MAX: 3,
28
- };
29
-
30
- /**
31
- * Model upgrade map — maps current model tier to the next tier.
32
- * Keys are logical tier names used in chati config.
33
- */
34
- const MODEL_UPGRADE_MAP = {
35
- haiku: 'sonnet',
36
- sonnet: 'opus',
37
- opus: 'opus', // opus is ceiling
38
- // Gemini equivalents
39
- flash: 'pro',
40
- pro: 'pro',
41
- // Codex equivalents
42
- codex: 'codex',
43
- };
44
-
45
- /**
46
- * Thresholds for automatic escalation.
47
- */
48
- const ESCALATION_THRESHOLDS = {
49
- /** Attempts before escalating from STANDARD to ENRICHED. */
50
- ENRICHED_AFTER: 2,
51
- /** Attempts before escalating from ENRICHED to UPGRADED. */
52
- UPGRADED_AFTER: 4,
53
- /** Attempts before escalating from UPGRADED to MAX. */
54
- MAX_AFTER: 7,
55
- };
56
-
57
- // ---------------------------------------------------------------------------
58
- // Public API
59
- // ---------------------------------------------------------------------------
60
-
61
- /**
62
- * Get the current escalation level for a task checkpoint.
63
- *
64
- * @param {{ attempts: number, escalationLevel?: number }} checkpoint
65
- * @returns {number} Current escalation level (0-3)
66
- */
67
- export function getEscalationLevel(checkpoint) {
68
- if (!checkpoint) return ESCALATION_LEVELS.STANDARD;
69
-
70
- // Explicit level takes precedence (set by previous escalation)
71
- if (typeof checkpoint.escalationLevel === 'number') {
72
- return checkpoint.escalationLevel;
73
- }
74
-
75
- // Derive from attempt count
76
- const attempts = checkpoint.attempts || 0;
77
-
78
- if (attempts >= ESCALATION_THRESHOLDS.MAX_AFTER) {
79
- return ESCALATION_LEVELS.MAX;
80
- }
81
- if (attempts >= ESCALATION_THRESHOLDS.UPGRADED_AFTER) {
82
- return ESCALATION_LEVELS.UPGRADED;
83
- }
84
- if (attempts >= ESCALATION_THRESHOLDS.ENRICHED_AFTER) {
85
- return ESCALATION_LEVELS.ENRICHED;
86
- }
87
-
88
- return ESCALATION_LEVELS.STANDARD;
89
- }
90
-
91
- /**
92
- * Determine whether escalation should occur based on checkpoint
93
- * state and cause analysis.
94
- *
95
- * @param {{ attempts: number, escalationLevel?: number }} checkpoint
96
- * @param {{ isRepetitive: boolean, category: string }} analysis - Cause analysis result
97
- * @returns {{ escalate: boolean, newLevel: number, reason: string }}
98
- */
99
- export function shouldEscalate(checkpoint, analysis) {
100
- const currentLevel = getEscalationLevel(checkpoint);
101
- const attempts = checkpoint?.attempts || 0;
102
-
103
- // Already at max — no further escalation
104
- if (currentLevel >= ESCALATION_LEVELS.MAX) {
105
- return {
106
- escalate: false,
107
- newLevel: ESCALATION_LEVELS.MAX,
108
- reason: 'Already at maximum escalation level',
109
- };
110
- }
111
-
112
- // Repetitive failures trigger immediate escalation (+1 level)
113
- if (analysis?.isRepetitive) {
114
- const newLevel = Math.min(currentLevel + 1, ESCALATION_LEVELS.MAX);
115
- return {
116
- escalate: true,
117
- newLevel,
118
- reason: `Repetitive ${analysis.category} failure — escalating to level ${newLevel}`,
119
- };
120
- }
121
-
122
- // Threshold-based escalation
123
- let targetLevel = ESCALATION_LEVELS.STANDARD;
124
- if (attempts >= ESCALATION_THRESHOLDS.MAX_AFTER) {
125
- targetLevel = ESCALATION_LEVELS.MAX;
126
- } else if (attempts >= ESCALATION_THRESHOLDS.UPGRADED_AFTER) {
127
- targetLevel = ESCALATION_LEVELS.UPGRADED;
128
- } else if (attempts >= ESCALATION_THRESHOLDS.ENRICHED_AFTER) {
129
- targetLevel = ESCALATION_LEVELS.ENRICHED;
130
- }
131
-
132
- if (targetLevel > currentLevel) {
133
- return {
134
- escalate: true,
135
- newLevel: targetLevel,
136
- reason: `Attempt ${attempts} reached threshold for level ${targetLevel}`,
137
- };
138
- }
139
-
140
- return {
141
- escalate: false,
142
- newLevel: currentLevel,
143
- reason: 'No escalation needed',
144
- };
145
- }
146
-
147
- /**
148
- * Get configuration for a given escalation level.
149
- *
150
- * @param {number} level - Escalation level (0-3)
151
- * @param {string} [currentModel='sonnet'] - Current model tier name
152
- * @returns {{ model: string|null, contextBoost: boolean, shouldPause: boolean, description: string }}
153
- */
154
- export function getEscalationConfig(level, currentModel = 'sonnet') {
155
- switch (level) {
156
- case ESCALATION_LEVELS.STANDARD:
157
- return {
158
- model: null, // no override
159
- contextBoost: false,
160
- shouldPause: false,
161
- description: 'Standard execution — no escalation',
162
- };
163
-
164
- case ESCALATION_LEVELS.ENRICHED:
165
- return {
166
- model: null, // same model
167
- contextBoost: true,
168
- shouldPause: false,
169
- description: 'Enriched context — cause analysis + retry guidance injected',
170
- };
171
-
172
- case ESCALATION_LEVELS.UPGRADED:
173
- return {
174
- model: MODEL_UPGRADE_MAP[currentModel] || currentModel,
175
- contextBoost: true,
176
- shouldPause: false,
177
- description: `Model upgraded from ${currentModel} to ${MODEL_UPGRADE_MAP[currentModel] || currentModel}`,
178
- };
179
-
180
- case ESCALATION_LEVELS.MAX:
181
- return {
182
- model: MODEL_UPGRADE_MAP[currentModel] || currentModel,
183
- contextBoost: true,
184
- shouldPause: true,
185
- description: 'Maximum escalation — pausing for human intervention',
186
- };
187
-
188
- default:
189
- return {
190
- model: null,
191
- contextBoost: false,
192
- shouldPause: false,
193
- description: `Unknown escalation level: ${level}`,
194
- };
195
- }
196
- }
197
-
198
- /**
199
- * Build a human-readable escalation summary.
200
- *
201
- * @param {number} level - Escalation level
202
- * @param {string} reason - Escalation reason
203
- * @returns {string}
204
- */
205
- export function buildEscalationSummary(level, reason) {
206
- const levelNames = ['STANDARD', 'ENRICHED', 'UPGRADED', 'MAX'];
207
- const name = levelNames[level] || 'UNKNOWN';
208
- return `[Escalation ${name}] ${reason}`;
209
- }
210
-
211
- /**
212
- * Exported for testing — escalation thresholds.
213
- */
214
- export { ESCALATION_THRESHOLDS };