chati-dev 4.1.6 → 4.2.1

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.
@@ -0,0 +1,56 @@
1
+ # Team Build Tasks Template (Article XXI)
2
+ # Used by orchestrator when forming a Build Team (BUILD phase)
3
+ # QA reviews EACH task as Dev completes it (per-task review, not batch)
4
+
5
+ team_type: build
6
+ phase: build
7
+ mission: "Implement all tasks with per-task QA review and evidence-based quality gates"
8
+
9
+ # NOTE: Tasks are dynamically populated from tasks.md at team formation time.
10
+ # This template defines the PATTERN, not the actual tasks.
11
+ # The orchestrator reads tasks.md and generates TT-BLD-{NNN} entries per task.
12
+
13
+ task_pattern:
14
+ dev_task:
15
+ id_format: "TT-BLD-{NNN}-DEV"
16
+ assigned_to: dev
17
+ status: pending
18
+ score: null
19
+ blocker: null
20
+ threshold: 95
21
+ on_complete:
22
+ notify: qa-implementation
23
+ message_type: task_ready_for_review
24
+ include: [task_id, commit_hash, score, artifacts, self_critique_findings]
25
+
26
+ qa_review:
27
+ id_format: "TT-BLD-{NNN}-QA"
28
+ assigned_to: qa-implementation
29
+ status: pending
30
+ score: null
31
+ blocker: null
32
+ depends_on: ["TT-BLD-{NNN}-DEV"]
33
+ threshold: 95
34
+ protocol:
35
+ - "Run Evidence Gate (Phase 4c): lint, typecheck, test output required"
36
+ - "Classify fault origin (Phase 4d): INTENT | SPEC | CODE | DEFER"
37
+ - "Check Decision Trail for known-bad states before review"
38
+ - "Issue per-task verdict: pass | warn | block"
39
+ on_block:
40
+ action: notify_dev
41
+ message_type: task_review_findings
42
+ include: [task_id, verdict, findings, evidence, fault_origin]
43
+
44
+ # Example of dynamically generated tasks (orchestrator produces these):
45
+ #
46
+ # tasks:
47
+ # - id: TT-BLD-001-DEV
48
+ # title: "T1.1: Initialize Next.js + App Router"
49
+ # assigned_to: dev
50
+ # source_task: T1.1
51
+ # ...
52
+ # - id: TT-BLD-001-QA
53
+ # title: "T1.1: Review + approve"
54
+ # assigned_to: qa-implementation
55
+ # depends_on: [TT-BLD-001-DEV]
56
+ # ...
@@ -0,0 +1,73 @@
1
+ # Team Planning Tasks Template (Article XXI)
2
+ # Used by orchestrator when forming a Planning Team (post-Brief)
3
+ # Cross-review assignments form a circular review: Detail -> Architect -> UX -> Detail
4
+
5
+ team_type: planning
6
+ phase: plan
7
+ mission: "Co-design PRD, architecture, and UX specification with cross-validated alignment"
8
+
9
+ tasks:
10
+ - id: TT-PLN-001
11
+ title: "Expand Brief into PRD with acceptance criteria"
12
+ assigned_to: detail
13
+ status: pending
14
+ score: null
15
+ blocker: null
16
+ depends_on: []
17
+ cross_review:
18
+ target: architect
19
+ questions:
20
+ - "Do PRD entities align with the data architecture constraints you anticipate?"
21
+ - "Are there requirements that would create architectural contradictions?"
22
+ - "Are scope decisions in Section 4 likely to conflict with your design?"
23
+ artifacts_produced:
24
+ - prd.md
25
+ threshold: 90
26
+
27
+ - id: TT-PLN-002
28
+ title: "Design system architecture and data architecture"
29
+ assigned_to: architect
30
+ status: pending
31
+ score: null
32
+ blocker: null
33
+ depends_on: []
34
+ cross_review:
35
+ target: ux
36
+ questions:
37
+ - "Does the responsive strategy conflict with deployment architecture constraints?"
38
+ - "Do Design System tokens account for API response latency in loading states?"
39
+ - "Are there component complexity choices that contradict the scalability approach?"
40
+ artifacts_produced:
41
+ - architecture.md
42
+ threshold: 90
43
+
44
+ - id: TT-PLN-003
45
+ title: "Design UX specification with brandbook, user flows, and components"
46
+ assigned_to: ux
47
+ status: pending
48
+ score: null
49
+ blocker: null
50
+ depends_on: []
51
+ cross_review:
52
+ target: detail
53
+ questions:
54
+ - "Do any PRD requirements conflict with the selected visual direction?"
55
+ - "Are there user flows implying requirements not captured in the PRD?"
56
+ - "Is the target user profile consistent with UX research findings?"
57
+ artifacts_produced:
58
+ - ux-specification.md
59
+ - brandbook.md
60
+ - brandbook.html
61
+ threshold: 90
62
+
63
+ - id: TT-PLN-004
64
+ title: "Cross-validate alignment: PRD entities = DB tables = UI components"
65
+ assigned_to: null
66
+ status: pending
67
+ score: null
68
+ blocker: null
69
+ depends_on: [TT-PLN-001, TT-PLN-002, TT-PLN-003]
70
+ cross_review: null
71
+ artifacts_produced: []
72
+ threshold: 95
73
+ notes: "Assigned by Team Lead after individual tasks complete. All 3 members participate."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chati-dev",
3
- "version": "4.1.6",
3
+ "version": "4.2.1",
4
4
  "description": "AI-Powered Multi-Agent Orchestration System — Structured vibe coding for Full Stack Development",
5
5
  "type": "module",
6
6
  "bin": {
@@ -166,6 +166,114 @@ function checkBinExists(packageRoot, results) {
166
166
  }
167
167
  }
168
168
 
169
+ /**
170
+ * Validate that every hook referenced in hooks/settings.json exists on disk.
171
+ * Prevents shipping a bundle where settings.json references a hook file that
172
+ * was never copied.
173
+ */
174
+ function checkHooksParity(packageRoot, results) {
175
+ results.checks++;
176
+ const settingsPath = join(packageRoot, 'framework', 'hooks', 'settings.json');
177
+ if (!existsSync(settingsPath)) {
178
+ results.warnings.push('framework/hooks/settings.json not found — skipping hooks parity check.');
179
+ results.passed++;
180
+ return;
181
+ }
182
+
183
+ try {
184
+ const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
185
+ const missing = new Set();
186
+ const hooksDir = join(packageRoot, 'framework', 'hooks');
187
+
188
+ // Walk all hook events and collect referenced .js files
189
+ for (const event of Object.keys(settings.hooks || {})) {
190
+ for (const matcher of settings.hooks[event] || []) {
191
+ for (const hook of matcher.hooks || []) {
192
+ if (hook.type === 'command' && typeof hook.command === 'string') {
193
+ // command format: "node chati.dev/hooks/foo.js"
194
+ const match = hook.command.match(/hooks\/([^\s]+\.js)/);
195
+ if (match) {
196
+ const hookFile = match[1];
197
+ if (!existsSync(join(hooksDir, hookFile))) {
198
+ missing.add(hookFile);
199
+ }
200
+ }
201
+ }
202
+ }
203
+ }
204
+ }
205
+
206
+ if (missing.size === 0) {
207
+ results.passed++;
208
+ } else {
209
+ results.errors.push(`Hooks referenced in settings.json but missing from framework/hooks/: ${[...missing].join(', ')}`);
210
+ }
211
+ } catch (err) {
212
+ results.errors.push(`Failed to parse hooks/settings.json: ${err.message}`);
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Parity check: every file in source chati.dev/ (that belongs to a copied
218
+ * directory) must exist in framework/. Catches bundling bugs where new files
219
+ * are added to the source but not included in the bundle.
220
+ *
221
+ * Only runs when the monorepo source is available (skipped inside the
222
+ * published package since the source is not shipped).
223
+ */
224
+ function checkSourceBundleParity(packageRoot, results) {
225
+ results.checks++;
226
+ const monorepoSource = join(packageRoot, '..', '..', 'chati.dev');
227
+ const bundleDir = join(packageRoot, 'framework');
228
+
229
+ if (!existsSync(monorepoSource)) {
230
+ // Running inside the published package — skip (nothing to compare against)
231
+ results.passed++;
232
+ return;
233
+ }
234
+
235
+ // Collect all files in source, grouped by the top-level directory
236
+ const missing = [];
237
+ const COPIED_DIRS = [
238
+ 'orchestrator', 'agents', 'templates', 'workflows', 'quality-gates',
239
+ 'schemas', 'frameworks', 'intelligence', 'patterns', 'hooks',
240
+ 'domains', 'i18n', 'migrations', 'data', 'context', 'tasks', 'presets',
241
+ ];
242
+ const COPIED_ROOT_FILES = ['constitution.md'];
243
+
244
+ function walk(srcDir, relBase) {
245
+ if (!existsSync(srcDir)) return;
246
+ for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
247
+ const srcPath = join(srcDir, entry.name);
248
+ const relPath = relBase ? `${relBase}/${entry.name}` : entry.name;
249
+ if (entry.isDirectory()) {
250
+ walk(srcPath, relPath);
251
+ } else if (entry.isFile()) {
252
+ const bundlePath = join(bundleDir, relPath);
253
+ if (!existsSync(bundlePath)) missing.push(relPath);
254
+ }
255
+ }
256
+ }
257
+
258
+ for (const file of COPIED_ROOT_FILES) {
259
+ const src = join(monorepoSource, file);
260
+ const bundled = join(bundleDir, file);
261
+ if (existsSync(src) && !existsSync(bundled)) missing.push(file);
262
+ }
263
+
264
+ for (const dir of COPIED_DIRS) {
265
+ walk(join(monorepoSource, dir), dir);
266
+ }
267
+
268
+ if (missing.length === 0) {
269
+ results.passed++;
270
+ } else {
271
+ const sample = missing.slice(0, 10).join(', ');
272
+ const more = missing.length > 10 ? ` (+ ${missing.length - 10} more)` : '';
273
+ results.errors.push(`Files in source chati.dev/ missing from framework/ bundle: ${sample}${more}`);
274
+ }
275
+ }
276
+
169
277
  /**
170
278
  * Compare entity count in registry vs actual files in framework/.
171
279
  */
@@ -216,6 +324,8 @@ export function validatePackage(packageRoot) {
216
324
  checkExportsResolve(packageRoot, results);
217
325
  checkBinExists(packageRoot, results);
218
326
  checkEntityCount(packageRoot, results);
327
+ checkHooksParity(packageRoot, results);
328
+ checkSourceBundleParity(packageRoot, results);
219
329
 
220
330
  return results;
221
331
  }
@@ -12,6 +12,7 @@ export const SAFETY_TRIGGERS = {
12
12
  CRITICAL_RISK: 'critical_risk', // Security/data risk detected
13
13
  TIMEOUT: 'timeout', // Agent exceeded time limit
14
14
  GOTCHA_SPIKE: 'gotcha_spike', // 5+ new gotchas in current session
15
+ EDIT_LOOP: 'edit_loop', // Same file edited 3+ times in current task (Article XX)
15
16
  };
16
17
 
17
18
  // Critical risk keywords
@@ -98,6 +99,9 @@ export function evaluateTrigger(trigger, state) {
98
99
  case SAFETY_TRIGGERS.GOTCHA_SPIKE:
99
100
  return evaluateGotchaSpike(state);
100
101
 
102
+ case SAFETY_TRIGGERS.EDIT_LOOP:
103
+ return evaluateEditLoop(state);
104
+
101
105
  default:
102
106
  return {
103
107
  triggered: false,
@@ -291,6 +295,34 @@ function evaluateGotchaSpike(state) {
291
295
  };
292
296
  }
293
297
 
298
+ /**
299
+ * Article XX — Detect edit loops (same file edited 3+ times in current task).
300
+ * state.fileEdits should be an object: { 'path/to/file.tsx': 3, ... }
301
+ */
302
+ function evaluateEditLoop(state) {
303
+ const threshold = 3;
304
+ const fileEdits = state.fileEdits || {};
305
+ const loopedFiles = Object.entries(fileEdits)
306
+ .filter(([, count]) => count >= threshold);
307
+
308
+ if (loopedFiles.length > 0) {
309
+ const details = loopedFiles
310
+ .map(([path, count]) => `${path} (${count} edits)`)
311
+ .join(', ');
312
+ return {
313
+ triggered: true,
314
+ severity: 'critical',
315
+ details: `Edit loop detected (Article XX): ${details}. Stop and diagnose root cause before continuing.`,
316
+ };
317
+ }
318
+
319
+ return {
320
+ triggered: false,
321
+ severity: 'warning',
322
+ details: 'No edit loops detected',
323
+ };
324
+ }
325
+
294
326
  /**
295
327
  * Get critical risk keywords list.
296
328
  * @returns {string[]}
@@ -1,4 +1,4 @@
1
- import { mkdirSync, writeFileSync, copyFileSync, existsSync, readFileSync } from 'fs';
1
+ import { mkdirSync, writeFileSync, copyFileSync, existsSync, readFileSync, readdirSync } from 'fs';
2
2
  import { join, dirname } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { IDE_CONFIGS, IDE_TO_PROVIDER } from '../config/ide-configs.js';
@@ -146,144 +146,103 @@ export async function installFramework(config) {
146
146
  }
147
147
 
148
148
  /**
149
- * Copy framework files from the Chati.dev source directory
149
+ * Framework directories to copy recursively from source to target.
150
+ * This list controls WHICH top-level directories under chati.dev/ get copied.
151
+ * Everything inside each directory is copied automatically — no need to
152
+ * enumerate individual files.
153
+ *
154
+ * New files added to the source framework are picked up automatically.
150
155
  */
151
- export function copyFrameworkFiles(destDir, provider = 'claude') {
152
- if (!existsSync(FRAMEWORK_SOURCE)) return;
153
-
154
- const filesToCopy = [
155
- 'constitution.md',
156
- 'orchestrator/chati.md',
157
- // DISCOVER agents
158
- 'agents/discover/greenfield-wu.md',
159
- 'agents/discover/brownfield-wu.md',
160
- 'agents/discover/brief.md',
161
- // PLAN agents
162
- 'agents/plan/detail.md',
163
- 'agents/plan/architect.md',
164
- 'agents/plan/architect-system.md',
165
- 'agents/plan/architect-data-engineer.md',
166
- 'agents/plan/ux.md',
167
- 'agents/plan/ux-brand-architect.md',
168
- 'agents/plan/ux-researcher.md',
169
- 'agents/plan/ux-component-engineer.md',
170
- 'agents/plan/phases.md',
171
- 'agents/plan/tasks.md',
172
- // Quality agents
173
- 'agents/quality/qa-planning.md',
174
- 'agents/quality/qa-implementation.md',
175
- // BUILD + DEPLOY agents
176
- 'agents/build/dev.md',
177
- 'agents/deploy/devops.md',
178
- // Templates
179
- 'templates/prd-tmpl.yaml',
180
- 'templates/brownfield-prd-tmpl.yaml',
181
- 'templates/fullstack-architecture-tmpl.yaml',
182
- 'templates/task-tmpl.yaml',
183
- 'templates/qa-gate-tmpl.yaml',
184
- 'templates/quick-brief-tmpl.yaml',
185
- 'templates/brandbook-tmpl.yaml',
186
- 'templates/brandbook-html-tmpl.md',
187
- 'templates/design-token-tmpl.yaml',
188
- 'templates/component-spec-tmpl.yaml',
189
- 'templates/icon-system-tmpl.yaml',
190
- 'templates/session-memory-tmpl.yaml',
191
- // Workflows
192
- 'workflows/greenfield-fullstack.yaml',
193
- 'workflows/brownfield-fullstack.yaml',
194
- 'workflows/brownfield-discovery.yaml',
195
- 'workflows/brownfield-service.yaml',
196
- 'workflows/brownfield-ui.yaml',
197
- 'workflows/quick-flow.yaml',
198
- 'workflows/standard-flow.yaml',
199
- // Quality gates
200
- 'quality-gates/planning-gate.md',
201
- 'quality-gates/implementation-gate.md',
202
- // Schemas
203
- 'schemas/session.schema.json',
204
- 'schemas/config.schema.json',
205
- 'schemas/task.schema.json',
206
- 'schemas/context.schema.json',
207
- 'schemas/memory.schema.json',
208
- // Frameworks
209
- 'frameworks/quality-dimensions.yaml',
210
- 'frameworks/decision-heuristics.yaml',
211
- // Intelligence
212
- 'intelligence/gotchas.yaml',
213
- 'intelligence/patterns.yaml',
214
- 'intelligence/confidence.yaml',
215
- 'intelligence/context-engine.md',
216
- 'intelligence/memory-layer.md',
217
- 'intelligence/decision-engine.md',
218
- // Patterns
219
- 'patterns/elicitation.md',
220
- 'patterns/elicitation-library.yaml',
221
- // Hooks
222
- 'hooks/prism-engine.js',
223
- 'hooks/mode-governance.js',
224
- 'hooks/constitution-guard.js',
225
- 'hooks/session-digest.js',
226
- 'hooks/model-governance.js',
227
- 'hooks/settings.json',
228
- 'hooks/read-protection.js',
229
- // Domains (PRISM Context Engine)
230
- 'domains/constitution.yaml',
231
- 'domains/global.yaml',
232
- 'domains/agents/orchestrator.yaml',
233
- 'domains/agents/greenfield-wu.yaml',
234
- 'domains/agents/brownfield-wu.yaml',
235
- 'domains/agents/brief.yaml',
236
- 'domains/agents/detail.yaml',
237
- 'domains/agents/architect.yaml',
238
- 'domains/agents/ux.yaml',
239
- 'domains/agents/phases.yaml',
240
- 'domains/agents/tasks.yaml',
241
- 'domains/agents/qa-planning.yaml',
242
- 'domains/agents/qa-implementation.yaml',
243
- 'domains/agents/dev.yaml',
244
- 'domains/agents/devops.yaml',
245
- 'domains/workflows/greenfield-fullstack.yaml',
246
- 'domains/workflows/brownfield-fullstack.yaml',
247
- 'domains/workflows/brownfield-discovery.yaml',
248
- 'domains/workflows/brownfield-service.yaml',
249
- 'domains/workflows/brownfield-ui.yaml',
250
- 'domains/workflows/quick-flow.yaml',
251
- 'domains/workflows/standard-flow.yaml',
252
- // i18n
253
- 'i18n/en.yaml',
254
- 'i18n/pt.yaml',
255
- 'i18n/es.yaml',
256
- 'i18n/fr.yaml',
257
- // Migrations
258
- 'migrations/v1.0-to-v1.1.yaml',
259
- // Data
260
- 'data/entity-registry.yaml',
261
- // Context (@ import chain for CLAUDE.md)
262
- 'context/root.md',
263
- 'context/governance.md',
264
- 'context/protocols.md',
265
- 'context/quality.md',
266
- ];
267
-
268
- for (const file of filesToCopy) {
269
- const src = join(FRAMEWORK_SOURCE, file);
270
- const dest = join(destDir, file);
156
+ const FRAMEWORK_DIRS_TO_COPY = [
157
+ 'orchestrator',
158
+ 'agents',
159
+ 'templates',
160
+ 'workflows',
161
+ 'quality-gates',
162
+ 'schemas',
163
+ 'frameworks',
164
+ 'intelligence',
165
+ 'patterns',
166
+ 'hooks',
167
+ 'domains',
168
+ 'i18n',
169
+ 'migrations',
170
+ 'data',
171
+ 'context',
172
+ 'tasks',
173
+ 'presets',
174
+ ];
271
175
 
272
- if (existsSync(src)) {
273
- createDir(dirname(dest));
176
+ /**
177
+ * Loose files at the root of chati.dev/ that get copied directly.
178
+ */
179
+ const FRAMEWORK_ROOT_FILES = [
180
+ 'constitution.md',
181
+ ];
274
182
 
275
- if (provider !== 'claude' && ADAPTABLE_FILES.has(file)) {
183
+ /**
184
+ * Recursively copy a directory from source to destination, applying provider
185
+ * adaptation for files listed in ADAPTABLE_FILES when the provider is not Claude.
186
+ */
187
+ function copyDirRecursive(srcDir, destDir, provider, relBase = '') {
188
+ if (!existsSync(srcDir)) return;
189
+ createDir(destDir);
190
+
191
+ const entries = readdirSync(srcDir, { withFileTypes: true });
192
+ for (const entry of entries) {
193
+ const srcPath = join(srcDir, entry.name);
194
+ const destPath = join(destDir, entry.name);
195
+ const relPath = relBase ? join(relBase, entry.name) : entry.name;
196
+
197
+ if (entry.isDirectory()) {
198
+ copyDirRecursive(srcPath, destPath, provider, relPath);
199
+ } else if (entry.isFile()) {
200
+ if (provider !== 'claude' && ADAPTABLE_FILES.has(relPath)) {
276
201
  // Non-Claude provider: read, adapt, write
277
- const content = readFileSync(src, 'utf-8');
278
- writeFileSync(dest, adaptFrameworkFile(content, file, provider), 'utf-8');
202
+ const content = readFileSync(srcPath, 'utf-8');
203
+ writeFileSync(destPath, adaptFrameworkFile(content, relPath, provider), 'utf-8');
279
204
  } else {
280
205
  // Claude or non-adaptable: direct copy
281
- copyFileSync(src, dest);
206
+ copyFileSync(srcPath, destPath);
282
207
  }
283
208
  }
284
209
  }
285
210
  }
286
211
 
212
+ /**
213
+ * Copy framework files from the Chati.dev source directory.
214
+ *
215
+ * Uses recursive directory copy instead of a hardcoded file list, so that
216
+ * new files added to the framework source are automatically picked up.
217
+ *
218
+ * @param {string} destDir - Target framework directory (e.g., {project}/chati.dev/)
219
+ * @param {string} provider - Primary provider for text adaptation ('claude' | 'gemini' | 'codex')
220
+ */
221
+ export function copyFrameworkFiles(destDir, provider = 'claude') {
222
+ if (!existsSync(FRAMEWORK_SOURCE)) return;
223
+
224
+ // Copy loose root files
225
+ for (const file of FRAMEWORK_ROOT_FILES) {
226
+ const src = join(FRAMEWORK_SOURCE, file);
227
+ if (!existsSync(src)) continue;
228
+ const dest = join(destDir, file);
229
+ createDir(dirname(dest));
230
+ if (provider !== 'claude' && ADAPTABLE_FILES.has(file)) {
231
+ const content = readFileSync(src, 'utf-8');
232
+ writeFileSync(dest, adaptFrameworkFile(content, file, provider), 'utf-8');
233
+ } else {
234
+ copyFileSync(src, dest);
235
+ }
236
+ }
237
+
238
+ // Copy each framework directory recursively
239
+ for (const dir of FRAMEWORK_DIRS_TO_COPY) {
240
+ const srcDir = join(FRAMEWORK_SOURCE, dir);
241
+ const destSubDir = join(destDir, dir);
242
+ copyDirRecursive(srcDir, destSubDir, provider, dir);
243
+ }
244
+ }
245
+
287
246
  /**
288
247
  * Configure a specific IDE
289
248
  */
@@ -108,6 +108,40 @@ export function generateConfigYaml(config) {
108
108
  },
109
109
  };
110
110
 
111
+ // Feature flags (Intelligence Upgrade + Article XXI Agent Teams)
112
+ // Must match source chati.dev/config.yaml features block.
113
+ configData.features = {
114
+ hybrid_budget: true,
115
+ anti_dash: true,
116
+ rate_limiter_integration: true,
117
+ l5_keywords: true,
118
+ prompt_size_guard: true,
119
+ ids_decision_engine: true,
120
+ surface_criteria: true,
121
+ parallel_fallback: true,
122
+ tool_mesh: true,
123
+ tech_presets: true,
124
+ doctor_autofix: true,
125
+ brandbook: true,
126
+ // Intelligence Upgrade (v4.0.5 — Claude Code patterns)
127
+ undercover_mode: true,
128
+ memory_extraction: true,
129
+ memory_consolidation: true,
130
+ daily_digest: true,
131
+ structured_session_memory: true,
132
+ static_prism_boundary: true,
133
+ token_bracket_estimation: true,
134
+ model_fallback: true,
135
+ frustration_detection: true,
136
+ bash_security_checks: true,
137
+ // Agent Teams (v4.2.0 — Article XXI)
138
+ agent_teams: false,
139
+ team_planning_size: 3,
140
+ team_build_size: 2,
141
+ team_echo_threshold: 0.92,
142
+ team_correction_cycles_max: 2,
143
+ };
144
+
111
145
  // Add telemetry config
112
146
  configData.telemetry = {
113
147
  enabled: config.telemetryEnabled !== false,