wogiflow 1.8.5 → 1.8.7

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.
@@ -102,6 +102,6 @@
102
102
  ]
103
103
  },
104
104
  "_wogiFlowManaged": true,
105
- "_wogiFlowVersion": "1.5.9",
105
+ "_wogiFlowVersion": "1.8.6",
106
106
  "_comment": "Shared WogiFlow hook configuration. Committed to repo for team use. User-specific overrides go in settings.local.json."
107
107
  }
@@ -327,16 +327,23 @@ Last synced: ${new Date().toISOString()}
327
327
  // Do not override - see security-patterns.md rule #2
328
328
 
329
329
  /**
330
- * Get installed WogiFlow version from settings.json (set by postinstall)
330
+ * Get installed WogiFlow version from package.json (canonical source)
331
331
  * @returns {string} Version string or 'unknown'
332
332
  */
333
333
  _getInstalledVersion() {
334
334
  try {
335
- const settingsPath = path.join(this.projectDir, this.cliFolder, 'settings.json');
336
- const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
337
- return settings._wogiFlowVersion || 'unknown';
335
+ const pkgPath = path.join(this.projectDir, 'node_modules', 'wogiflow', 'package.json');
336
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
337
+ return pkg.version || 'unknown';
338
338
  } catch (err) {
339
- return 'unknown';
339
+ // Fallback: try settings.json (legacy)
340
+ try {
341
+ const settingsPath = path.join(this.projectDir, this.cliFolder, 'settings.json');
342
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
343
+ return settings._wogiFlowVersion || 'unknown';
344
+ } catch (err) {
345
+ return 'unknown';
346
+ }
340
347
  }
341
348
  }
342
349
 
package/lib/installer.js CHANGED
@@ -40,7 +40,6 @@ const DEFAULT_CONFIG = {
40
40
  version: PACKAGE_VERSION,
41
41
  projectName: '',
42
42
  cliKey: 'claude',
43
- strictMode: true,
44
43
  projectType: 'unknown'
45
44
  };
46
45
 
@@ -77,7 +76,17 @@ function getDefaultConfig(overrides = {}) {
77
76
  small: { maxFiles: 3, maxHours: 1 },
78
77
  medium: { maxFiles: 10, maxHours: 4 },
79
78
  large: { minFiles: 10, minHours: 4 }
80
- }
79
+ },
80
+ taskGating: { enabled: true, blockWithoutTask: true, autoCreateTask: false },
81
+ scopeGating: {
82
+ enabled: true,
83
+ mode: 'warn',
84
+ exemptPatterns: ['.workflow/state/**', '.workflow/specs/**', '.workflow/plans/**', 'package.json', 'package-lock.json', 'tsconfig.json']
85
+ },
86
+ implementationGate: { enabled: true },
87
+ todoWriteGate: { enabled: true, blockImplementationWithoutTask: true },
88
+ routingGate: { enabled: true },
89
+ loopEnforcement: { enabled: true }
81
90
  },
82
91
  execution: {
83
92
  maxIterations: 20,
@@ -104,6 +113,12 @@ function getDefaultConfig(overrides = {}) {
104
113
  simpleMode: { enabled: false },
105
114
  recheckAllAfterFix: true,
106
115
  regressionOnRecheck: 'warn'
116
+ },
117
+ tdd: {
118
+ enforced: true,
119
+ defaultForTypes: ['bugfix'],
120
+ requireFailingTestFirst: true,
121
+ testFrameworkDetection: true
107
122
  }
108
123
  },
109
124
  errorRecovery: {
@@ -114,8 +129,7 @@ function getDefaultConfig(overrides = {}) {
114
129
  maxAttemptsPerLevel: 3,
115
130
  architecturalReassessment: { enabled: false },
116
131
  recursive: { enabled: false },
117
- hypothesisGeneration: { usePatterns: true, useAI: false, aiModel: 'haiku' },
118
- learning: { recordSuccessfulFixes: true, recordFailedHypotheses: true }
132
+ hypothesisGeneration: { usePatterns: true, useAI: false, aiModel: 'haiku' }
119
133
  },
120
134
  workflow: { planningStyle: 'feature-based', agentStructure: 'unified' },
121
135
  parallelExecution: {
@@ -195,30 +209,24 @@ function getDefaultConfig(overrides = {}) {
195
209
  scopeByTaskType: true,
196
210
  alwaysCheck: ['naming', 'security'],
197
211
  similarityThreshold: 0.8,
198
- similarityWarningThreshold: 0.6,
199
- learning: { enabled: false }
200
- },
201
- validation: {
202
- afterFileEdit: { enabled: false },
203
- afterTaskComplete: { enabled: false },
204
- beforeCommit: { enabled: false }
212
+ similarityWarningThreshold: 0.6
205
213
  },
206
214
  checkpoint: { enabled: false },
207
215
  regressionTesting: { enabled: false },
208
- tdd: {
209
- enforced: true,
210
- defaultForTypes: ['bugfix'],
211
- requireFailingTestFirst: true,
212
- testFrameworkDetection: true
213
- },
214
- componentRules: {
216
+ componentReuse: {
217
+ enabled: true,
215
218
  preferVariants: true,
216
219
  requireAppMapEntry: true,
217
220
  requireDetailDoc: false,
218
221
  autoGenerateStorybook: false,
219
- storybookPath: 'src/stories'
222
+ storybookPath: 'src/stories',
223
+ threshold: 30,
224
+ allRegistries: true,
225
+ aiAsJudge: true,
226
+ blockOnSimilar: false,
227
+ injectContext: true
220
228
  },
221
- strictMode: {
229
+ reporting: {
222
230
  verificationChecklist: false,
223
231
  correctionReportsOnFail: false,
224
232
  featureReportsOnComplete: false
@@ -249,7 +257,10 @@ function getDefaultConfig(overrides = {}) {
249
257
  modelSpecificLearning: true
250
258
  },
251
259
  modelAdapters: { enabled: false },
252
- skill: { enabled: false }
260
+ skill: { enabled: false },
261
+ standardsLearning: { enabled: false },
262
+ errorRecoveryLearning: { enabled: false },
263
+ bugFlowLearning: { enabled: false }
253
264
  },
254
265
  bugFlow: {
255
266
  investigationAgents: {
@@ -258,7 +269,6 @@ function getDefaultConfig(overrides = {}) {
258
269
  dependencyAnalyzer: { enabled: false }
259
270
  },
260
271
  autoRoute: true,
261
- learningEnforcement: { enabled: false },
262
272
  inlineDiscovery: {
263
273
  maxSearchOperations: 3,
264
274
  maxFileReads: 2,
@@ -299,7 +309,7 @@ function getDefaultConfig(overrides = {}) {
299
309
  promotion: { enabled: false }
300
310
  },
301
311
  prd: { enabled: false },
302
- context: {
312
+ contextManagement: {
303
313
  compaction: {
304
314
  enabled: false,
305
315
  thresholds: { warnAt: 50000, compactAt: 80000, maxExpanded: 20000 },
@@ -328,8 +338,9 @@ function getDefaultConfig(overrides = {}) {
328
338
  useHaiku: true,
329
339
  phases: ['exploring', 'spec_review', 'scenario', 'criteria_check', 'validating']
330
340
  },
331
- scoring: { enabled: false },
332
- monitor: { enabled: false },
341
+ monitor: { enabled: false }
342
+ },
343
+ taskContext: {
333
344
  auto: {
334
345
  enabled: false,
335
346
  strategy: 'dynamic',
@@ -346,6 +357,7 @@ function getDefaultConfig(overrides = {}) {
346
357
  fallbackLimits: { maxFilesHard: 50, maxTokensHard: 150000 },
347
358
  lspEnrichment: { enabled: false }
348
359
  },
360
+ scoring: { enabled: false },
349
361
  session: { enabled: false }
350
362
  },
351
363
  eval: {
@@ -525,34 +537,23 @@ function getDefaultConfig(overrides = {}) {
525
537
  },
526
538
  multiApproach: { enabled: false },
527
539
  hooks: {
528
- enabled: false,
540
+ enabled: true,
529
541
  targets: ['claude-code'],
530
542
  gracefulDegradation: true,
531
543
  timeout: 600000,
532
544
  rules: {
533
- enforcement: {
534
- taskGating: { enabled: false, blockWithoutTask: true, autoCreateTask: false },
535
- scopeGating: {
536
- enabled: false,
537
- mode: 'warn',
538
- exemptPatterns: ['.workflow/state/**', '.workflow/specs/**', '.workflow/plans/**', 'package.json', 'package-lock.json', 'tsconfig.json']
539
- },
540
- implementationGate: { enabled: false },
541
- todoWriteGate: { enabled: false, blockImplementationWithoutTask: true },
542
- routingGate: { enabled: false },
543
- loopEnforcement: { enabled: false }
544
- },
545
545
  intelligence: {
546
- componentReuse: { enabled: false, threshold: 30, allRegistries: true, aiAsJudge: true, blockOnSimilar: false, injectContext: true },
547
- sessionContext: { enabled: false, loadSuspendedTasks: true, loadDecisions: true, loadRecentActivity: true },
548
- validation: { enabled: false, runAfterEdit: true }
546
+ sessionContext: { enabled: true, loadSuspendedTasks: true, loadDecisions: true, loadRecentActivity: true },
547
+ promptCapture: { enabled: true },
548
+ correctionDetection: { enabled: true },
549
+ validation: { enabled: true, runAfterEdit: true, afterTaskComplete: false, beforeCommit: false }
549
550
  },
550
551
  lifecycle: {
551
- taskCompleted: { enabled: false },
552
+ taskCompleted: { enabled: true },
552
553
  completionSummaries: { enabled: true },
553
554
  autoLogging: { enabled: false },
554
555
  configChange: { enabled: false },
555
- setup: { enabled: false, autoOnboard: false, maintenanceTasks: ['healthCheck', 'cleanupLocks'] },
556
+ setup: { enabled: true, autoOnboard: false, maintenanceTasks: ['healthCheck', 'cleanupLocks'] },
556
557
  sessionCleanup: { enabled: true },
557
558
  phaseGate: { enabled: true }
558
559
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wogiflow",
3
- "version": "1.8.5",
3
+ "version": "1.8.7",
4
4
  "description": "AI-powered development workflow management system with multi-model support",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -356,7 +356,8 @@ function searchComponentIndex(keywords, config = null) {
356
356
 
357
357
  // Use config values if available
358
358
  const cfg = config || getConfig();
359
- const maxComponentMatches = cfg.context?.auto?.maxComponentMatches || 15;
359
+ const autoCfg = cfg.taskContext?.auto || cfg.context?.auto;
360
+ const maxComponentMatches = autoCfg?.maxComponentMatches || 15;
360
361
 
361
362
  try {
362
363
  // Use safeJsonParse for prototype pollution protection
@@ -419,8 +420,9 @@ function grepCodebase(keywords, maxResults = 10, config = null) {
419
420
 
420
421
  // Use config values if available
421
422
  const cfg = config || getConfig();
422
- const effectiveMaxResults = cfg.context?.auto?.maxGrepResults || maxResults;
423
- const maxContentLines = cfg.context?.auto?.maxContentLines || 50;
423
+ const grepAutoCfg = cfg.taskContext?.auto || cfg.context?.auto;
424
+ const effectiveMaxResults = grepAutoCfg?.maxGrepResults || maxResults;
425
+ const maxContentLines = grepAutoCfg?.maxContentLines || 50;
424
426
 
425
427
  // Only grep for high-value keywords to avoid noise
426
428
  const searchKeywords = keywords.high.slice(0, 5);
@@ -454,7 +456,7 @@ function grepCodebase(keywords, maxResults = 10, config = null) {
454
456
  if (!results.some(r => r.path === relPath)) {
455
457
  // Optionally read file content with truncation
456
458
  let content = null;
457
- if (cfg.context?.auto?.includeContent && isPathWithinProject(file)) {
459
+ if (grepAutoCfg?.includeContent && isPathWithinProject(file)) {
458
460
  try {
459
461
  const fullContent = fs.readFileSync(file, 'utf-8');
460
462
  const lines = fullContent.split('\n');
@@ -558,7 +560,8 @@ async function searchSemanticMemory(keywords, config = null) {
558
560
  if (!cfg.memory?.enabled) return [];
559
561
 
560
562
  const results = [];
561
- const maxFacts = cfg.context?.auto?.maxSemanticFacts || 5;
563
+ const semAutoCfg = cfg.taskContext?.auto || cfg.context?.auto;
564
+ const maxFacts = semAutoCfg?.maxSemanticFacts || 5;
562
565
 
563
566
  try {
564
567
  // Build query from high-value keywords
@@ -604,7 +607,8 @@ async function searchSemanticMemory(keywords, config = null) {
604
607
  */
605
608
  async function enrichWithLSP(fileResults, config) {
606
609
  // Skip if disabled
607
- if (!config.context?.auto?.lspEnrichment?.enabled) return fileResults;
610
+ const autoConfig = config.taskContext?.auto || config.context?.auto;
611
+ if (!autoConfig?.lspEnrichment?.enabled) return fileResults;
608
612
 
609
613
  // Lazy load LSP module to avoid circular dependencies
610
614
  let getLSP, isLSPEnabled;
@@ -621,8 +625,8 @@ async function enrichWithLSP(fileResults, config) {
621
625
  const lsp = await getLSP();
622
626
  if (!lsp) return fileResults;
623
627
 
624
- const timeout = config.context?.auto?.lspEnrichment?.timeoutMs || 2000;
625
- const maxFiles = config.context?.auto?.lspEnrichment?.maxFiles || 5;
628
+ const timeout = autoConfig?.lspEnrichment?.timeoutMs || 2000;
629
+ const maxFiles = autoConfig?.lspEnrichment?.maxFiles || 5;
626
630
 
627
631
  // Only enrich top N JS/TS files to limit latency
628
632
  const filesToEnrich = fileResults
@@ -688,12 +692,13 @@ function searchWithAstGrep(keywords, taskType = null, config = null) {
688
692
  const cfg = config || getConfig();
689
693
 
690
694
  // Skip if disabled or ast-grep not available
691
- if (!cfg.context?.auto?.useAstGrep || !isAstGrepAvailable()) {
695
+ const astAutoCfg = cfg.taskContext?.auto || cfg.context?.auto;
696
+ if (!astAutoCfg?.useAstGrep || !isAstGrepAvailable()) {
692
697
  return [];
693
698
  }
694
699
 
695
700
  const results = [];
696
- const maxResults = cfg.context?.auto?.maxAstGrepResults || 5;
701
+ const maxResults = astAutoCfg?.maxAstGrepResults || 5;
697
702
 
698
703
  try {
699
704
  // Determine search strategy based on task type
@@ -855,12 +860,13 @@ async function getAutoContext(description, options = {}) {
855
860
  const config = getConfig();
856
861
 
857
862
  // Check if auto-context is enabled
858
- if (config.context?.auto?.enabled === false) {
863
+ const autoCtx = config.taskContext?.auto || config.context?.auto;
864
+ if (autoCtx?.enabled === false) {
859
865
  return { enabled: false, files: [], context: [] };
860
866
  }
861
867
 
862
868
  // v3.0: Use Smart Context System if strategy is 'dynamic'
863
- const strategy = config.context?.auto?.strategy || 'fixed';
869
+ const strategy = autoCtx?.strategy || 'fixed';
864
870
  if (strategy === 'dynamic' && smartContextGatherer) {
865
871
  const smartResult = await getSmartContext(description, options);
866
872
  if (smartResult) {
@@ -897,8 +903,9 @@ async function getLegacyContext(description, options = {}, config = null) {
897
903
  checkAndRefreshIndex(config);
898
904
  }
899
905
 
900
- const maxFiles = options.maxFiles || config.context?.auto?.maxFilesToLoad || 10;
901
- const showFiles = options.showFiles ?? config.context?.auto?.showLoadedFiles ?? true;
906
+ const autoConf = config.taskContext?.auto || config.context?.auto;
907
+ const maxFiles = options.maxFiles || autoConf?.maxFilesToLoad || 10;
908
+ const showFiles = options.showFiles ?? autoConf?.showLoadedFiles ?? true;
902
909
 
903
910
  // Extract keywords
904
911
  const keywords = extractKeywords(description);
@@ -955,7 +962,8 @@ async function getLegacyContext(description, options = {}, config = null) {
955
962
  const enrichedUnique = await enrichWithLSP(unique, config);
956
963
 
957
964
  // Re-sort: prioritize files without errors (if LSP enrichment added data)
958
- if (config.context?.auto?.lspEnrichment?.prioritizeHealthyFiles !== false) {
965
+ const autoLsp = config.taskContext?.auto || config.context?.auto;
966
+ if (autoLsp?.lspEnrichment?.prioritizeHealthyFiles !== false) {
959
967
  enrichedUnique.sort((a, b) => {
960
968
  // Files with LSP errors go to bottom
961
969
  const aErrors = a.lsp?.errorCount || 0;
@@ -607,6 +607,7 @@ module.exports = {
607
607
  normalizeIssueToPattern,
608
608
  getAutoLearnConfig,
609
609
  loadAutoPatterns,
610
+ saveAutoPatterns,
610
611
  showStatus
611
612
  };
612
613
 
@@ -50,20 +50,20 @@ const KNOWN_CONFIG_KEYS = [
50
50
  // Core
51
51
  'version', 'projectName', 'cli', 'scripts', 'requireApproval',
52
52
  // Feature toggles
53
- 'autoLog', 'autoUpdateAppMap', 'strictMode',
53
+ 'autoLog', 'autoUpdateAppMap', 'strictMode', 'reporting',
54
54
  // Execution
55
55
  'hybrid', 'parallel', 'worktree', 'enforcement', 'tasks', 'workflow',
56
56
  'loops', 'taskQueue', 'durableSteps', 'suspension', 'phases',
57
57
  // Quality & validation
58
58
  'qualityGates', 'testing', 'validation', 'specificationMode', 'tdd',
59
59
  // Components & registries
60
- 'componentRules', 'componentIndex', 'registries', 'functionRegistry', 'apiRegistry',
60
+ 'componentRules', 'componentReuse', 'componentIndex', 'registries', 'functionRegistry', 'apiRegistry',
61
61
  // Learning & memory
62
62
  'learning', 'corrections', 'automaticMemory', 'automaticPromotion',
63
63
  'crossSessionLearning', 'sessionLearning', 'skillLearning', 'memory',
64
64
  'codebaseInsights', 'knowledgeRouting',
65
65
  // Skills & context
66
- 'skills', 'autoContext', 'context', 'contextMonitor', 'contextScoring',
66
+ 'skills', 'autoContext', 'context', 'contextManagement', 'taskContext', 'contextMonitor', 'contextScoring',
67
67
  // Review & analysis
68
68
  'review', 'reviewFix', 'originTaskTracing', 'standardsCompliance',
69
69
  'semanticMatching', 'peerReview', 'triage', 'consistency',
@@ -162,18 +162,18 @@ function validateConfig(config, warnOnUnknown = true) {
162
162
  function applyConfigCompatShim(config) {
163
163
  if (!config || typeof config !== 'object') return config;
164
164
 
165
- // execution <-> tasks + loops (bidirectional)
165
+ // execution <-> tasks + loops (bidirectional, shallow copy to avoid shared references)
166
166
  if (config.execution && !config.tasks) {
167
- config.tasks = config.execution;
167
+ config.tasks = { ...config.execution };
168
168
  }
169
169
  if (config.tasks && !config.execution) {
170
- config.execution = config.tasks;
170
+ config.execution = { ...config.tasks };
171
171
  }
172
172
  if (config.execution && config.execution.loops && !config.loops) {
173
- config.loops = config.execution.loops;
173
+ config.loops = { ...config.execution.loops };
174
174
  }
175
175
  if (config.loops && config.execution && !config.execution.loops) {
176
- config.execution.loops = config.loops;
176
+ config.execution.loops = { ...config.loops };
177
177
  }
178
178
 
179
179
  // memory <- memory.automatic, memory.promotion
@@ -276,6 +276,87 @@ function applyConfigCompatShim(config) {
276
276
  config.communitySync = config.community.sync;
277
277
  }
278
278
 
279
+ // v1.8.6: Config restructuring compat shims
280
+ // AC1: hooks.rules.enforcement -> enforcement (moved to top-level enforcement)
281
+ if (config.hooks?.rules?.enforcement) {
282
+ const hooksEnforcement = config.hooks.rules.enforcement;
283
+ if (!config.enforcement) config.enforcement = {};
284
+ if (hooksEnforcement.taskGating && !config.enforcement.taskGating) config.enforcement.taskGating = hooksEnforcement.taskGating;
285
+ if (hooksEnforcement.scopeGating && !config.enforcement.scopeGating) config.enforcement.scopeGating = hooksEnforcement.scopeGating;
286
+ if (hooksEnforcement.implementationGate && !config.enforcement.implementationGate) config.enforcement.implementationGate = hooksEnforcement.implementationGate;
287
+ if (hooksEnforcement.todoWriteGate && !config.enforcement.todoWriteGate) config.enforcement.todoWriteGate = hooksEnforcement.todoWriteGate;
288
+ if (hooksEnforcement.routingGate && !config.enforcement.routingGate) config.enforcement.routingGate = hooksEnforcement.routingGate;
289
+ if (hooksEnforcement.loopEnforcement && !config.enforcement.loopEnforcement) config.enforcement.loopEnforcement = hooksEnforcement.loopEnforcement;
290
+ }
291
+
292
+ // AC3: strictMode object -> reporting
293
+ if (config.strictMode && typeof config.strictMode === 'object' && !config.reporting) {
294
+ config.reporting = config.strictMode;
295
+ }
296
+
297
+ // AC4: componentRules + hooks.rules.intelligence.componentReuse -> componentReuse
298
+ if (config.componentRules && !config.componentReuse) {
299
+ config.componentReuse = config.componentRules;
300
+ }
301
+ if (config.hooks?.rules?.intelligence?.componentReuse) {
302
+ if (!config.componentReuse) config.componentReuse = {};
303
+ const hooksComponentReuse = config.hooks.rules.intelligence.componentReuse;
304
+ for (const key of Object.keys(hooksComponentReuse)) {
305
+ if (config.componentReuse[key] === undefined) {
306
+ config.componentReuse[key] = hooksComponentReuse[key];
307
+ }
308
+ }
309
+ }
310
+ // Also expose back as hooks.rules.componentReuse for code that reads that path
311
+ if (config.componentReuse && config.hooks?.rules) {
312
+ if (!config.hooks.rules.componentReuse) config.hooks.rules.componentReuse = { ...config.componentReuse };
313
+ }
314
+
315
+ // AC5: scattered learning sub-keys -> learning.*
316
+ if (config.standardsCompliance?.learning && config.learning && !config.learning.standardsLearning) {
317
+ config.learning.standardsLearning = config.standardsCompliance.learning;
318
+ }
319
+ if (config.errorRecovery?.learning && config.learning && !config.learning.errorRecoveryLearning) {
320
+ config.learning.errorRecoveryLearning = config.errorRecovery.learning;
321
+ }
322
+ if (config.bugFlow?.learningEnforcement && config.learning && !config.learning.bugFlowLearning) {
323
+ config.learning.bugFlowLearning = config.bugFlow.learningEnforcement;
324
+ }
325
+
326
+ // AC6: context -> contextManagement + taskContext
327
+ // Note: mergeWithDefaults may have already created these keys, so check sub-keys not top-level
328
+ if (config.context) {
329
+ if (!config.contextManagement) config.contextManagement = {};
330
+ if (config.context.compaction && !config.contextManagement.compaction) config.contextManagement.compaction = config.context.compaction;
331
+ if (config.context.smart && !config.contextManagement.smart) config.contextManagement.smart = config.context.smart;
332
+ if (config.context.proactive && !config.contextManagement.proactive) config.contextManagement.proactive = config.context.proactive;
333
+ if (config.context.monitor && !config.contextManagement.monitor) config.contextManagement.monitor = config.context.monitor;
334
+
335
+ if (!config.taskContext) config.taskContext = {};
336
+ if (config.context.auto && !config.taskContext.auto) config.taskContext.auto = config.context.auto;
337
+ if (config.context.scoring && !config.taskContext.scoring) config.taskContext.scoring = config.context.scoring;
338
+ if (config.context.session && !config.taskContext.session) config.taskContext.session = config.context.session;
339
+ }
340
+
341
+ // AC7: tdd -> execution.tdd
342
+ if (config.tdd && config.execution && !config.execution.tdd) {
343
+ config.execution.tdd = config.tdd;
344
+ }
345
+ if (config.execution?.tdd && !config.tdd) {
346
+ config.tdd = config.execution.tdd;
347
+ }
348
+
349
+ // AC2: top-level validation -> hooks.rules.intelligence.validation
350
+ if (config.validation && config.hooks?.rules?.intelligence?.validation) {
351
+ const v = config.hooks.rules.intelligence.validation;
352
+ if (config.validation.afterTaskComplete !== undefined && v.afterTaskComplete === undefined) {
353
+ v.afterTaskComplete = config.validation.afterTaskComplete;
354
+ }
355
+ if (config.validation.beforeCommit !== undefined && v.beforeCommit === undefined) {
356
+ v.beforeCommit = config.validation.beforeCommit;
357
+ }
358
+ }
359
+
279
360
  return config;
280
361
  }
281
362
 
@@ -114,7 +114,7 @@ const KNOWN_CONFIG_KEYS = [
114
114
  'worktree',
115
115
  'qualityGates',
116
116
  'testing',
117
- 'componentRules',
117
+ 'componentReuse',
118
118
  'mandatorySteps',
119
119
  'phases',
120
120
  'corrections',
@@ -136,6 +136,9 @@ const KNOWN_CONFIG_KEYS = [
136
136
  'damageControl',
137
137
  'storyDecomposition',
138
138
  'specificationMode',
139
+ 'contextManagement',
140
+ 'taskContext',
141
+ 'reporting',
139
142
  ];
140
143
 
141
144
  module.exports = {
@@ -276,7 +276,7 @@ function collapseAll() {
276
276
  */
277
277
  function checkContextPressure() {
278
278
  const config = getConfig();
279
- const compactionConfig = config.context?.compaction || {};
279
+ const compactionConfig = config.contextManagement?.compaction || config.context?.compaction || {};
280
280
  const thresholds = compactionConfig.thresholds || {
281
281
  warnAt: 50000,
282
282
  compactAt: 80000,
@@ -125,7 +125,7 @@ function selectNodes(tree, options = {}) {
125
125
  } = options;
126
126
 
127
127
  const config = getConfig();
128
- const compactionConfig = config.context?.compaction || {};
128
+ const compactionConfig = config.contextManagement?.compaction || config.context?.compaction || {};
129
129
 
130
130
  const queryKeywords = extractKeywords(query);
131
131
  const selected = [];
@@ -259,7 +259,7 @@ function mergeIntoTree(existingTree, newData) {
259
259
 
260
260
  // Apply relevance decay to existing nodes
261
261
  const config = getConfig();
262
- const compactionConfig = config.context?.compaction || DEFAULT_COMPACTION_CONFIG;
262
+ const compactionConfig = config.contextManagement?.compaction || config.context?.compaction || DEFAULT_COMPACTION_CONFIG;
263
263
 
264
264
  if (compactionConfig.relevanceDecay?.enabled) {
265
265
  const decayRate = compactionConfig.relevanceDecay.decayPerTurn || 0.05;