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.
- package/README.md +18 -13
- package/bin/chati.js +32 -4
- package/framework/agents/build/dev.md +9 -5
- package/framework/agents/discover/brief.md +2 -0
- package/framework/agents/discover/brownfield-wu.md +2 -0
- package/framework/agents/discover/greenfield-wu.md +18 -1
- package/framework/agents/plan/detail.md +2 -0
- package/framework/agents/plan/tasks.md +31 -0
- package/framework/agents/plan/ux-brand-architect.md +21 -2
- package/framework/agents/plan/ux-component-engineer.md +10 -0
- package/framework/agents/quality/qa-implementation.md +1 -1
- package/framework/agents/quality/qa-planning.md +1 -1
- package/framework/agents/quality/qa-visual.md +33 -21
- package/framework/config.yaml +5 -4
- package/framework/constitution.md +58 -11
- package/framework/context/governance.md +12 -10
- package/framework/context/root.md +2 -2
- package/framework/data/entity-registry.yaml +12 -4
- package/framework/data/qa-rubrics.yaml +335 -0
- package/framework/domains/constitution.yaml +3 -3
- package/framework/executors/json-validate.js +80 -0
- package/framework/executors/npm-script.js +72 -0
- package/framework/executors/path-check.js +67 -0
- package/framework/executors/registry.yaml +36 -0
- package/framework/hooks/advance-trigger.js +47 -3
- package/framework/hooks/constitution-guard.js +9 -4
- package/framework/hooks/git-push-authority.js +113 -0
- package/framework/hooks/license-guard.js +59 -9
- package/framework/hooks/model-governance.js +40 -17
- package/framework/hooks/prism-engine.js +78 -11
- package/framework/hooks/session-digest.js +12 -3
- package/framework/hooks/settings.json +7 -3
- package/framework/hooks/style-guard.js +52 -3
- package/framework/hooks/team-quality-gate.js +43 -5
- package/framework/i18n/en.yaml +3 -3
- package/framework/i18n/es.yaml +3 -3
- package/framework/i18n/fr.yaml +3 -3
- package/framework/i18n/pt.yaml +3 -3
- package/framework/intelligence/confidence.yaml +85 -6
- package/framework/intelligence/context-engine.md +9 -5
- package/framework/intelligence/decision-engine.md +1 -1
- package/framework/orchestrator/chati-router.js +59 -5
- package/framework/orchestrator/chati-update.md +19 -3
- package/framework/orchestrator/chati.md +20 -1
- package/framework/quality-gates/planning-gate.md +2 -0
- package/framework/rules/human-writing-style.md +47 -0
- package/framework/scaffold/motion-premium/README.md +12 -2
- package/framework/scaffold/motion-premium/lib/animations/tokens.ts.template +1 -1
- package/framework/scaffold/motion-premium/scaffold.yaml +6 -1
- package/framework/scaffold/motion-premium-3d/README.md +10 -0
- package/framework/scaffold/motion-premium-3d/components/webgl/WebGLContext.tsx.template +1 -1
- package/framework/scaffold/motion-premium-3d/scaffold.yaml +3 -0
- package/framework/scaffold/saas-dashboard/README.md +58 -0
- package/framework/scaffold/saas-dashboard/app/dashboard/page.tsx.template +546 -0
- package/framework/scaffold/saas-dashboard/app/globals.css.template +746 -0
- package/framework/scaffold/saas-dashboard/app/layout.tsx.template +192 -0
- package/framework/scaffold/saas-dashboard/app/shell.css.template +1070 -0
- package/framework/scaffold/saas-dashboard/components/data/Chart.tsx.template +725 -0
- package/framework/scaffold/saas-dashboard/components/data/DataTable.tsx.template +623 -0
- package/framework/scaffold/saas-dashboard/components/data/DataTable.types.ts.template +104 -0
- package/framework/scaffold/saas-dashboard/components/data/DataTablePagination.tsx.template +234 -0
- package/framework/scaffold/saas-dashboard/components/data/SkeletonBlock.tsx.template +111 -0
- package/framework/scaffold/saas-dashboard/components/data/StatCard.tsx.template +393 -0
- package/framework/scaffold/saas-dashboard/components/data/StatCardGrid.tsx.template +121 -0
- package/framework/scaffold/saas-dashboard/components/feedback/EmptyState.tsx.template +281 -0
- package/framework/scaffold/saas-dashboard/components/interaction/CommandPalette.tsx.template +767 -0
- package/framework/scaffold/saas-dashboard/components/interaction/Field.tsx.template +351 -0
- package/framework/scaffold/saas-dashboard/components/interaction/Form.tsx.template +424 -0
- package/framework/scaffold/saas-dashboard/components/primitives/Badge.tsx.template +193 -0
- package/framework/scaffold/saas-dashboard/components/primitives/Button.tsx.template +261 -0
- package/framework/scaffold/saas-dashboard/components/primitives/Card.tsx.template +287 -0
- package/framework/scaffold/saas-dashboard/components/primitives/Input.tsx.template +310 -0
- package/framework/scaffold/saas-dashboard/components/primitives/Spinner.tsx.template +135 -0
- package/framework/scaffold/saas-dashboard/components/primitives/index.ts.template +60 -0
- package/framework/scaffold/saas-dashboard/components/shell/AppShell.tsx.template +174 -0
- package/framework/scaffold/saas-dashboard/components/shell/Breadcrumb.tsx.template +186 -0
- package/framework/scaffold/saas-dashboard/components/shell/Container.tsx.template +86 -0
- package/framework/scaffold/saas-dashboard/components/shell/NavItem.tsx.template +147 -0
- package/framework/scaffold/saas-dashboard/components/shell/Sidebar.tsx.template +378 -0
- package/framework/scaffold/saas-dashboard/components/shell/SidebarContext.tsx.template +169 -0
- package/framework/scaffold/saas-dashboard/components/shell/ThemeToggle.tsx.template +128 -0
- package/framework/scaffold/saas-dashboard/components/shell/Topbar.tsx.template +301 -0
- package/framework/scaffold/saas-dashboard/lib/brand.ts.template +324 -0
- package/framework/scaffold/saas-dashboard/scaffold.yaml +225 -0
- package/framework/schemas/session.schema.json +5 -0
- package/framework/schemas/task.schema.json +9 -0
- package/framework/scripts/visual-qa.js +101 -3
- package/framework/tasks/orchestrator-health.md +4 -4
- package/framework/tasks/qa-impl-verdict.md +11 -5
- package/framework/templates/brandbook-html-tmpl.md +1 -1
- package/framework/templates/qa-gate-tmpl.yaml +24 -11
- package/package.json +4 -3
- package/src/config/context-file-generator.js +0 -6
- package/src/dashboard/renderer.js +0 -36
- package/src/executors/runner.js +204 -0
- package/src/installer/core.js +13 -3
- package/src/installer/templates.js +5 -6
- package/src/intelligence/registry-manager.js +1 -1
- package/src/license/client.js +9 -3
- package/src/license/commands.js +12 -2
- package/src/license/machine-id.js +42 -1
- package/src/memory/gotchas.js +58 -40
- package/src/memory/magic-docs.js +1 -1
- package/src/memory/session-digest.js +9 -4
- package/src/orchestrator/cli.js +186 -15
- package/src/orchestrator/doctor.js +98 -16
- package/src/orchestrator/pipeline-manager.js +44 -22
- package/src/orchestrator/session-manager.js +64 -8
- package/src/telemetry/sender.js +7 -19
- package/src/terminal/run-team.js +3 -3
- package/src/upgrade/tracked-files-detector.js +34 -16
- package/src/utils/feature-flags.js +1 -1
- package/src/utils/flatten-entities.js +4 -40
- package/src/utils/schema-validator.js +0 -14
- package/src/wizard/i18n.js +3 -3
- package/src/api/index.js +0 -120
- package/src/autonomy/autonomous-gate.js +0 -294
- package/src/autonomy/build-loop.js +0 -281
- package/src/autonomy/build-state.js +0 -286
- package/src/autonomy/cause-analyzer.js +0 -177
- package/src/autonomy/escalation.js +0 -214
- package/src/autonomy/index.js +0 -51
- package/src/autonomy/mode-manager.js +0 -225
- package/src/autonomy/mode-suggester.js +0 -283
- package/src/autonomy/progress-reporter.js +0 -275
- package/src/autonomy/safety-net.js +0 -370
- package/src/config/agent-customizer.js +0 -231
- package/src/decision/analyzer.js +0 -291
- package/src/decision/engine.js +0 -250
- package/src/decision/index.js +0 -38
- package/src/decision/registry-healer.js +0 -468
- package/src/decision/registry-updater.js +0 -339
- package/src/extensions/loader.js +0 -145
- package/src/extensions/registry.js +0 -134
- package/src/gates/circuit-breaker.js +0 -151
- package/src/gates/g1-planning-complete.js +0 -154
- package/src/gates/g2-qa-planning.js +0 -156
- package/src/gates/g3-implementation.js +0 -215
- package/src/gates/g4-qa-implementation.js +0 -240
- package/src/gates/g5-deploy-ready.js +0 -181
- package/src/gates/gate-base.js +0 -185
- package/src/gates/index.js +0 -46
- package/src/health/auto-fix.js +0 -216
- package/src/health/engine.js +0 -246
- package/src/merger/semantic-merger.js +0 -292
- package/src/preview/detector.js +0 -238
- package/src/preview/index.js +0 -20
- package/src/preview/launcher.js +0 -235
- package/src/preview/log-buffer.js +0 -103
- package/src/quality/metrics-collector.js +0 -281
- package/src/quality/test-runner.js +0 -366
- package/src/tasks/executor.js +0 -195
- package/src/tasks/index.js +0 -4
- package/src/tasks/loader.js +0 -210
- package/src/tasks/router.js +0 -182
- 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 };
|