musubi-sdd 3.6.0 → 3.6.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.
- package/README.md +10 -2
- package/bin/musubi-orchestrate.js +309 -0
- package/package.json +1 -1
- package/src/orchestration/replanning/adaptive-goal-modifier.js +1150 -0
- package/src/orchestration/replanning/goal-progress-tracker.js +727 -0
- package/src/orchestration/replanning/index.js +44 -2
- package/src/orchestration/replanning/proactive-path-optimizer.js +972 -0
- package/src/templates/agents/claude-code/CLAUDE.md +45 -0
- package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +20 -0
- package/src/templates/agents/claude-code/skills/orchestrator/patterns.md +89 -0
- package/src/templates/agents/codex/AGENTS.md +13 -0
- package/src/templates/agents/cursor/AGENTS.md +13 -0
- package/src/templates/agents/gemini-cli/GEMINI.md +13 -0
- package/src/templates/agents/github-copilot/AGENTS.md +13 -0
- package/src/templates/agents/qwen-code/QWEN.md +13 -0
- package/src/templates/agents/windsurf/AGENTS.md +13 -0
|
@@ -0,0 +1,1150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AdaptiveGoalModifier - 状況に応じた目標の動的調整
|
|
3
|
+
*
|
|
4
|
+
* Goal-Driven Replanning の完全実装 (Phase 2/3)
|
|
5
|
+
* 目標の優先度・スコープ・タイムライン・成功基準を動的に調整
|
|
6
|
+
*
|
|
7
|
+
* @module orchestration/replanning/adaptive-goal-modifier
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 目標調整の理由を分類
|
|
14
|
+
*/
|
|
15
|
+
const ModificationReason = {
|
|
16
|
+
RESOURCE_CONSTRAINT: 'resource_constraint', // リソース制約
|
|
17
|
+
TIME_CONSTRAINT: 'time_constraint', // 時間制約
|
|
18
|
+
DEPENDENCY_FAILURE: 'dependency_failure', // 依存関係の失敗
|
|
19
|
+
PRIORITY_SHIFT: 'priority_shift', // 優先度の変更
|
|
20
|
+
SCOPE_CREEP: 'scope_creep', // スコープの拡大
|
|
21
|
+
EXTERNAL_CHANGE: 'external_change', // 外部要因の変化
|
|
22
|
+
PERFORMANCE_ISSUE: 'performance_issue', // パフォーマンス問題
|
|
23
|
+
USER_REQUEST: 'user_request', // ユーザーリクエスト
|
|
24
|
+
STRATEGIC_PIVOT: 'strategic_pivot' // 戦略的転換
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 調整タイプ
|
|
29
|
+
*/
|
|
30
|
+
const ModificationType = {
|
|
31
|
+
PRIORITY_ADJUSTMENT: 'priority_adjustment', // 優先度調整
|
|
32
|
+
SCOPE_REDUCTION: 'scope_reduction', // スコープ縮小
|
|
33
|
+
SCOPE_EXPANSION: 'scope_expansion', // スコープ拡大
|
|
34
|
+
TIMELINE_EXTENSION: 'timeline_extension', // タイムライン延長
|
|
35
|
+
TIMELINE_COMPRESSION: 'timeline_compression', // タイムライン圧縮
|
|
36
|
+
SUCCESS_CRITERIA_RELAXATION: 'criteria_relaxation', // 成功基準緩和
|
|
37
|
+
SUCCESS_CRITERIA_TIGHTENING: 'criteria_tightening', // 成功基準厳格化
|
|
38
|
+
GOAL_DECOMPOSITION: 'goal_decomposition', // 目標分解
|
|
39
|
+
GOAL_MERGE: 'goal_merge', // 目標統合
|
|
40
|
+
GOAL_DEFERRAL: 'goal_deferral', // 目標延期
|
|
41
|
+
GOAL_CANCELLATION: 'goal_cancellation' // 目標キャンセル
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 影響分析エンジン
|
|
46
|
+
* 目標変更による影響を分析
|
|
47
|
+
*/
|
|
48
|
+
class ImpactAnalyzer {
|
|
49
|
+
/**
|
|
50
|
+
* @param {Object} options - 設定オプション
|
|
51
|
+
*/
|
|
52
|
+
constructor(options = {}) {
|
|
53
|
+
this.config = {
|
|
54
|
+
cascadeDepth: options.cascadeDepth || 3,
|
|
55
|
+
impactThreshold: options.impactThreshold || 0.3,
|
|
56
|
+
...options
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 目標変更の影響を分析
|
|
62
|
+
* @param {Object} goal - 対象目標
|
|
63
|
+
* @param {Object} modification - 変更内容
|
|
64
|
+
* @param {Object} context - コンテキスト
|
|
65
|
+
* @returns {Object} 影響分析結果
|
|
66
|
+
*/
|
|
67
|
+
analyzeImpact(goal, modification, context) {
|
|
68
|
+
const directImpact = this._analyzeDirectImpact(goal, modification);
|
|
69
|
+
const cascadeImpact = this._analyzeCascadeImpact(goal, modification, context);
|
|
70
|
+
const resourceImpact = this._analyzeResourceImpact(goal, modification, context);
|
|
71
|
+
const timelineImpact = this._analyzeTimelineImpact(goal, modification, context);
|
|
72
|
+
|
|
73
|
+
const totalScore = this._calculateTotalImpact(
|
|
74
|
+
directImpact,
|
|
75
|
+
cascadeImpact,
|
|
76
|
+
resourceImpact,
|
|
77
|
+
timelineImpact
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
goalId: goal.id,
|
|
82
|
+
modificationType: modification.type,
|
|
83
|
+
directImpact,
|
|
84
|
+
cascadeImpact,
|
|
85
|
+
resourceImpact,
|
|
86
|
+
timelineImpact,
|
|
87
|
+
totalScore,
|
|
88
|
+
riskLevel: this._categorizeRisk(totalScore),
|
|
89
|
+
recommendations: this._generateRecommendations(totalScore, modification),
|
|
90
|
+
timestamp: new Date().toISOString()
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 直接的な影響を分析
|
|
96
|
+
* @private
|
|
97
|
+
*/
|
|
98
|
+
_analyzeDirectImpact(goal, modification) {
|
|
99
|
+
const impacts = {
|
|
100
|
+
[ModificationType.PRIORITY_ADJUSTMENT]: 0.3,
|
|
101
|
+
[ModificationType.SCOPE_REDUCTION]: 0.5,
|
|
102
|
+
[ModificationType.SCOPE_EXPANSION]: 0.6,
|
|
103
|
+
[ModificationType.TIMELINE_EXTENSION]: 0.4,
|
|
104
|
+
[ModificationType.TIMELINE_COMPRESSION]: 0.7,
|
|
105
|
+
[ModificationType.SUCCESS_CRITERIA_RELAXATION]: 0.3,
|
|
106
|
+
[ModificationType.SUCCESS_CRITERIA_TIGHTENING]: 0.5,
|
|
107
|
+
[ModificationType.GOAL_DECOMPOSITION]: 0.4,
|
|
108
|
+
[ModificationType.GOAL_MERGE]: 0.5,
|
|
109
|
+
[ModificationType.GOAL_DEFERRAL]: 0.6,
|
|
110
|
+
[ModificationType.GOAL_CANCELLATION]: 1.0
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const baseImpact = impacts[modification.type] || 0.5;
|
|
114
|
+
const priorityMultiplier = goal.priority === 'critical' ? 1.5 :
|
|
115
|
+
goal.priority === 'high' ? 1.2 : 1.0;
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
score: Math.min(1.0, baseImpact * priorityMultiplier),
|
|
119
|
+
affectedAreas: this._identifyAffectedAreas(modification.type)
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* カスケード影響を分析
|
|
125
|
+
* @private
|
|
126
|
+
*/
|
|
127
|
+
_analyzeCascadeImpact(goal, modification, context) {
|
|
128
|
+
const dependentGoals = context.goals?.filter(g =>
|
|
129
|
+
g.dependencies?.includes(goal.id)
|
|
130
|
+
) || [];
|
|
131
|
+
|
|
132
|
+
let totalCascade = 0;
|
|
133
|
+
const affectedGoals = [];
|
|
134
|
+
|
|
135
|
+
for (const depGoal of dependentGoals) {
|
|
136
|
+
const impact = this._calculateDependencyImpact(depGoal, modification);
|
|
137
|
+
totalCascade += impact;
|
|
138
|
+
if (impact > this.config.impactThreshold) {
|
|
139
|
+
affectedGoals.push({
|
|
140
|
+
goalId: depGoal.id,
|
|
141
|
+
impact,
|
|
142
|
+
requires: this._determineCascadeAction(impact)
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
score: Math.min(1.0, totalCascade / Math.max(1, dependentGoals.length)),
|
|
149
|
+
affectedGoals,
|
|
150
|
+
depth: Math.min(affectedGoals.length, this.config.cascadeDepth)
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* リソース影響を分析
|
|
156
|
+
* @private
|
|
157
|
+
*/
|
|
158
|
+
_analyzeResourceImpact(goal, modification, context) {
|
|
159
|
+
const resourceChanges = {
|
|
160
|
+
freed: [],
|
|
161
|
+
required: [],
|
|
162
|
+
conflicting: []
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
if (modification.type === ModificationType.SCOPE_REDUCTION ||
|
|
166
|
+
modification.type === ModificationType.GOAL_CANCELLATION) {
|
|
167
|
+
resourceChanges.freed = goal.resources || [];
|
|
168
|
+
} else if (modification.type === ModificationType.SCOPE_EXPANSION ||
|
|
169
|
+
modification.type === ModificationType.TIMELINE_COMPRESSION) {
|
|
170
|
+
resourceChanges.required = this._estimateAdditionalResources(goal, modification);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// リソース競合チェック
|
|
174
|
+
if (context.resourcePool) {
|
|
175
|
+
resourceChanges.conflicting = this._findConflicts(
|
|
176
|
+
resourceChanges.required,
|
|
177
|
+
context.resourcePool
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
score: resourceChanges.conflicting.length > 0 ? 0.7 : 0.3,
|
|
183
|
+
changes: resourceChanges,
|
|
184
|
+
feasibility: resourceChanges.conflicting.length === 0
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* タイムライン影響を分析
|
|
190
|
+
* @private
|
|
191
|
+
*/
|
|
192
|
+
_analyzeTimelineImpact(goal, modification, context) {
|
|
193
|
+
let shift = 0;
|
|
194
|
+
let affectedMilestones = [];
|
|
195
|
+
|
|
196
|
+
switch (modification.type) {
|
|
197
|
+
case ModificationType.TIMELINE_EXTENSION:
|
|
198
|
+
shift = modification.extensionDays || 7;
|
|
199
|
+
break;
|
|
200
|
+
case ModificationType.TIMELINE_COMPRESSION:
|
|
201
|
+
shift = -(modification.compressionDays || 3);
|
|
202
|
+
break;
|
|
203
|
+
case ModificationType.SCOPE_EXPANSION:
|
|
204
|
+
shift = modification.estimatedDays || 5;
|
|
205
|
+
break;
|
|
206
|
+
case ModificationType.SCOPE_REDUCTION:
|
|
207
|
+
shift = -(modification.savedDays || 2);
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (context.milestones) {
|
|
212
|
+
affectedMilestones = context.milestones.filter(m =>
|
|
213
|
+
new Date(m.dueDate) >= new Date(goal.targetDate)
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
score: Math.min(1.0, Math.abs(shift) / 14), // 2週間を基準
|
|
219
|
+
shiftDays: shift,
|
|
220
|
+
direction: shift > 0 ? 'delay' : 'accelerate',
|
|
221
|
+
affectedMilestones: affectedMilestones.length
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* 総合影響スコアを計算
|
|
227
|
+
* @private
|
|
228
|
+
*/
|
|
229
|
+
_calculateTotalImpact(direct, cascade, resource, timeline) {
|
|
230
|
+
return (
|
|
231
|
+
direct.score * 0.3 +
|
|
232
|
+
cascade.score * 0.25 +
|
|
233
|
+
resource.score * 0.25 +
|
|
234
|
+
timeline.score * 0.2
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* リスクレベルを分類
|
|
240
|
+
* @private
|
|
241
|
+
*/
|
|
242
|
+
_categorizeRisk(score) {
|
|
243
|
+
if (score >= 0.8) return 'critical';
|
|
244
|
+
if (score >= 0.6) return 'high';
|
|
245
|
+
if (score >= 0.4) return 'medium';
|
|
246
|
+
return 'low';
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* 影響を受けるエリアを特定
|
|
251
|
+
* @private
|
|
252
|
+
*/
|
|
253
|
+
_identifyAffectedAreas(modificationType) {
|
|
254
|
+
const areaMap = {
|
|
255
|
+
[ModificationType.PRIORITY_ADJUSTMENT]: ['scheduling', 'resources'],
|
|
256
|
+
[ModificationType.SCOPE_REDUCTION]: ['deliverables', 'testing'],
|
|
257
|
+
[ModificationType.SCOPE_EXPANSION]: ['deliverables', 'testing', 'resources'],
|
|
258
|
+
[ModificationType.TIMELINE_EXTENSION]: ['milestones', 'dependencies'],
|
|
259
|
+
[ModificationType.TIMELINE_COMPRESSION]: ['quality', 'resources', 'scope'],
|
|
260
|
+
[ModificationType.SUCCESS_CRITERIA_RELAXATION]: ['quality', 'testing'],
|
|
261
|
+
[ModificationType.SUCCESS_CRITERIA_TIGHTENING]: ['quality', 'testing', 'resources'],
|
|
262
|
+
[ModificationType.GOAL_DECOMPOSITION]: ['tracking', 'dependencies'],
|
|
263
|
+
[ModificationType.GOAL_MERGE]: ['tracking', 'dependencies', 'scope'],
|
|
264
|
+
[ModificationType.GOAL_DEFERRAL]: ['milestones', 'dependencies'],
|
|
265
|
+
[ModificationType.GOAL_CANCELLATION]: ['all']
|
|
266
|
+
};
|
|
267
|
+
return areaMap[modificationType] || ['unknown'];
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* 依存関係の影響を計算
|
|
272
|
+
* @private
|
|
273
|
+
*/
|
|
274
|
+
_calculateDependencyImpact(depGoal, modification) {
|
|
275
|
+
const baseImpact = depGoal.dependencyStrength || 0.5;
|
|
276
|
+
const typeMultiplier =
|
|
277
|
+
modification.type === ModificationType.GOAL_CANCELLATION ? 1.0 :
|
|
278
|
+
modification.type === ModificationType.TIMELINE_EXTENSION ? 0.7 :
|
|
279
|
+
0.4;
|
|
280
|
+
return baseImpact * typeMultiplier;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* カスケードアクションを決定
|
|
285
|
+
* @private
|
|
286
|
+
*/
|
|
287
|
+
_determineCascadeAction(impact) {
|
|
288
|
+
if (impact >= 0.8) return 'immediate_replanning';
|
|
289
|
+
if (impact >= 0.5) return 'review_required';
|
|
290
|
+
return 'monitor';
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* 追加リソースを見積もり
|
|
295
|
+
* @private
|
|
296
|
+
*/
|
|
297
|
+
_estimateAdditionalResources(goal, modification) {
|
|
298
|
+
const currentResources = goal.resources || [];
|
|
299
|
+
const expansionFactor = modification.expansionFactor || 1.5;
|
|
300
|
+
return currentResources.map(r => ({
|
|
301
|
+
...r,
|
|
302
|
+
amount: Math.ceil(r.amount * (expansionFactor - 1))
|
|
303
|
+
}));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* リソース競合を検出
|
|
308
|
+
* @private
|
|
309
|
+
*/
|
|
310
|
+
_findConflicts(required, pool) {
|
|
311
|
+
return required.filter(r => {
|
|
312
|
+
const available = pool.find(p => p.type === r.type);
|
|
313
|
+
return !available || available.available < r.amount;
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* 推奨事項を生成
|
|
319
|
+
* @private
|
|
320
|
+
*/
|
|
321
|
+
_generateRecommendations(totalScore, modification) {
|
|
322
|
+
const recommendations = [];
|
|
323
|
+
|
|
324
|
+
if (totalScore >= 0.7) {
|
|
325
|
+
recommendations.push({
|
|
326
|
+
priority: 'high',
|
|
327
|
+
action: 'Conduct stakeholder review before proceeding',
|
|
328
|
+
rationale: 'High impact modification requires approval'
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (modification.type === ModificationType.SCOPE_EXPANSION) {
|
|
333
|
+
recommendations.push({
|
|
334
|
+
priority: 'medium',
|
|
335
|
+
action: 'Review resource allocation',
|
|
336
|
+
rationale: 'Scope expansion typically requires additional resources'
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (modification.type === ModificationType.TIMELINE_COMPRESSION) {
|
|
341
|
+
recommendations.push({
|
|
342
|
+
priority: 'high',
|
|
343
|
+
action: 'Assess quality risks',
|
|
344
|
+
rationale: 'Compressed timelines may affect deliverable quality'
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return recommendations;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* 目標調整ストラテジー
|
|
354
|
+
* 状況に応じた調整戦略を決定
|
|
355
|
+
*/
|
|
356
|
+
class ModificationStrategy {
|
|
357
|
+
/**
|
|
358
|
+
* @param {Object} options - 設定オプション
|
|
359
|
+
*/
|
|
360
|
+
constructor(options = {}) {
|
|
361
|
+
this.config = {
|
|
362
|
+
conservativeMode: options.conservativeMode || false,
|
|
363
|
+
autoApproveThreshold: options.autoApproveThreshold || 0.3,
|
|
364
|
+
...options
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* 最適な調整戦略を決定
|
|
370
|
+
* @param {Object} goal - 対象目標
|
|
371
|
+
* @param {Object} trigger - トリガー情報
|
|
372
|
+
* @param {Object} context - コンテキスト
|
|
373
|
+
* @returns {Object} 調整戦略
|
|
374
|
+
*/
|
|
375
|
+
determineStrategy(goal, trigger, context) {
|
|
376
|
+
const strategies = this._generateCandidateStrategies(goal, trigger, context);
|
|
377
|
+
const evaluated = strategies.map(s => ({
|
|
378
|
+
...s,
|
|
379
|
+
score: this._evaluateStrategy(s, goal, context)
|
|
380
|
+
}));
|
|
381
|
+
|
|
382
|
+
evaluated.sort((a, b) => b.score - a.score);
|
|
383
|
+
|
|
384
|
+
return {
|
|
385
|
+
recommended: evaluated[0],
|
|
386
|
+
alternatives: evaluated.slice(1, 3),
|
|
387
|
+
confidence: evaluated[0]?.score || 0,
|
|
388
|
+
autoApprovable: evaluated[0]?.score >= (1 - this.config.autoApproveThreshold)
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* 候補戦略を生成
|
|
394
|
+
* @private
|
|
395
|
+
*/
|
|
396
|
+
_generateCandidateStrategies(goal, trigger, context) {
|
|
397
|
+
const strategies = [];
|
|
398
|
+
|
|
399
|
+
switch (trigger.reason) {
|
|
400
|
+
case ModificationReason.TIME_CONSTRAINT:
|
|
401
|
+
strategies.push(
|
|
402
|
+
this._createScopeReductionStrategy(goal, trigger),
|
|
403
|
+
this._createTimelineExtensionStrategy(goal, trigger),
|
|
404
|
+
this._createCriteriaRelaxationStrategy(goal, trigger)
|
|
405
|
+
);
|
|
406
|
+
break;
|
|
407
|
+
|
|
408
|
+
case ModificationReason.RESOURCE_CONSTRAINT:
|
|
409
|
+
strategies.push(
|
|
410
|
+
this._createScopeReductionStrategy(goal, trigger),
|
|
411
|
+
this._createTimelineExtensionStrategy(goal, trigger),
|
|
412
|
+
this._createGoalDeferralStrategy(goal, trigger)
|
|
413
|
+
);
|
|
414
|
+
break;
|
|
415
|
+
|
|
416
|
+
case ModificationReason.DEPENDENCY_FAILURE:
|
|
417
|
+
strategies.push(
|
|
418
|
+
this._createGoalDecompositionStrategy(goal, trigger),
|
|
419
|
+
this._createTimelineExtensionStrategy(goal, trigger),
|
|
420
|
+
this._createGoalDeferralStrategy(goal, trigger)
|
|
421
|
+
);
|
|
422
|
+
break;
|
|
423
|
+
|
|
424
|
+
case ModificationReason.PRIORITY_SHIFT:
|
|
425
|
+
strategies.push(
|
|
426
|
+
this._createPriorityAdjustmentStrategy(goal, trigger),
|
|
427
|
+
this._createTimelineCompressionStrategy(goal, trigger),
|
|
428
|
+
this._createScopeExpansionStrategy(goal, trigger)
|
|
429
|
+
);
|
|
430
|
+
break;
|
|
431
|
+
|
|
432
|
+
case ModificationReason.SCOPE_CREEP:
|
|
433
|
+
strategies.push(
|
|
434
|
+
this._createScopeReductionStrategy(goal, trigger),
|
|
435
|
+
this._createGoalDecompositionStrategy(goal, trigger),
|
|
436
|
+
this._createTimelineExtensionStrategy(goal, trigger)
|
|
437
|
+
);
|
|
438
|
+
break;
|
|
439
|
+
|
|
440
|
+
case ModificationReason.PERFORMANCE_ISSUE:
|
|
441
|
+
strategies.push(
|
|
442
|
+
this._createCriteriaRelaxationStrategy(goal, trigger),
|
|
443
|
+
this._createTimelineExtensionStrategy(goal, trigger),
|
|
444
|
+
this._createScopeReductionStrategy(goal, trigger)
|
|
445
|
+
);
|
|
446
|
+
break;
|
|
447
|
+
|
|
448
|
+
default:
|
|
449
|
+
strategies.push(
|
|
450
|
+
this._createTimelineExtensionStrategy(goal, trigger),
|
|
451
|
+
this._createScopeReductionStrategy(goal, trigger)
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return strategies.filter(s => s !== null);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* 戦略を評価
|
|
460
|
+
* @private
|
|
461
|
+
*/
|
|
462
|
+
_evaluateStrategy(strategy, goal, context) {
|
|
463
|
+
let score = 0.5; // ベーススコア
|
|
464
|
+
|
|
465
|
+
// 目標優先度との整合性
|
|
466
|
+
if (goal.priority === 'critical' && strategy.preservesCore) {
|
|
467
|
+
score += 0.2;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// リソース効率
|
|
471
|
+
if (strategy.resourceEfficiency === 'high') {
|
|
472
|
+
score += 0.15;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// リスクレベル
|
|
476
|
+
score -= strategy.riskLevel === 'high' ? 0.2 :
|
|
477
|
+
strategy.riskLevel === 'medium' ? 0.1 : 0;
|
|
478
|
+
|
|
479
|
+
// 保守的モードの場合
|
|
480
|
+
if (this.config.conservativeMode && strategy.conservative) {
|
|
481
|
+
score += 0.1;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// 実現可能性
|
|
485
|
+
score += strategy.feasibility * 0.15;
|
|
486
|
+
|
|
487
|
+
return Math.max(0, Math.min(1, score));
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// 戦略生成メソッド群
|
|
491
|
+
_createScopeReductionStrategy(goal, trigger) {
|
|
492
|
+
return {
|
|
493
|
+
type: ModificationType.SCOPE_REDUCTION,
|
|
494
|
+
description: 'Reduce scope to meet constraints',
|
|
495
|
+
reductionTargets: this._identifyReductionTargets(goal),
|
|
496
|
+
preservesCore: true,
|
|
497
|
+
resourceEfficiency: 'high',
|
|
498
|
+
riskLevel: 'medium',
|
|
499
|
+
conservative: true,
|
|
500
|
+
feasibility: 0.8
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
_createTimelineExtensionStrategy(goal, trigger) {
|
|
505
|
+
return {
|
|
506
|
+
type: ModificationType.TIMELINE_EXTENSION,
|
|
507
|
+
description: 'Extend timeline to accommodate current progress',
|
|
508
|
+
extensionDays: Math.ceil((trigger.gap || 0.2) * 14),
|
|
509
|
+
preservesCore: true,
|
|
510
|
+
resourceEfficiency: 'medium',
|
|
511
|
+
riskLevel: 'low',
|
|
512
|
+
conservative: true,
|
|
513
|
+
feasibility: 0.9
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
_createTimelineCompressionStrategy(goal, trigger) {
|
|
518
|
+
return {
|
|
519
|
+
type: ModificationType.TIMELINE_COMPRESSION,
|
|
520
|
+
description: 'Compress timeline for higher priority',
|
|
521
|
+
compressionDays: Math.ceil((trigger.urgency || 0.3) * 7),
|
|
522
|
+
preservesCore: true,
|
|
523
|
+
resourceEfficiency: 'low',
|
|
524
|
+
riskLevel: 'high',
|
|
525
|
+
conservative: false,
|
|
526
|
+
feasibility: 0.6
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
_createCriteriaRelaxationStrategy(goal, trigger) {
|
|
531
|
+
return {
|
|
532
|
+
type: ModificationType.SUCCESS_CRITERIA_RELAXATION,
|
|
533
|
+
description: 'Relax success criteria to achievable levels',
|
|
534
|
+
relaxationTargets: ['performance', 'coverage'],
|
|
535
|
+
preservesCore: false,
|
|
536
|
+
resourceEfficiency: 'high',
|
|
537
|
+
riskLevel: 'medium',
|
|
538
|
+
conservative: false,
|
|
539
|
+
feasibility: 0.7
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
_createGoalDecompositionStrategy(goal, trigger) {
|
|
544
|
+
return {
|
|
545
|
+
type: ModificationType.GOAL_DECOMPOSITION,
|
|
546
|
+
description: 'Decompose goal into smaller, manageable sub-goals',
|
|
547
|
+
suggestedSubGoals: this._suggestSubGoals(goal),
|
|
548
|
+
preservesCore: true,
|
|
549
|
+
resourceEfficiency: 'medium',
|
|
550
|
+
riskLevel: 'low',
|
|
551
|
+
conservative: true,
|
|
552
|
+
feasibility: 0.85
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
_createGoalDeferralStrategy(goal, trigger) {
|
|
557
|
+
return {
|
|
558
|
+
type: ModificationType.GOAL_DEFERRAL,
|
|
559
|
+
description: 'Defer goal to next iteration',
|
|
560
|
+
deferralReason: trigger.reason,
|
|
561
|
+
preservesCore: true,
|
|
562
|
+
resourceEfficiency: 'high',
|
|
563
|
+
riskLevel: 'medium',
|
|
564
|
+
conservative: true,
|
|
565
|
+
feasibility: 0.9
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
_createPriorityAdjustmentStrategy(goal, trigger) {
|
|
570
|
+
return {
|
|
571
|
+
type: ModificationType.PRIORITY_ADJUSTMENT,
|
|
572
|
+
description: 'Adjust goal priority based on new context',
|
|
573
|
+
newPriority: trigger.suggestedPriority || 'high',
|
|
574
|
+
preservesCore: true,
|
|
575
|
+
resourceEfficiency: 'medium',
|
|
576
|
+
riskLevel: 'low',
|
|
577
|
+
conservative: true,
|
|
578
|
+
feasibility: 0.95
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
_createScopeExpansionStrategy(goal, trigger) {
|
|
583
|
+
return {
|
|
584
|
+
type: ModificationType.SCOPE_EXPANSION,
|
|
585
|
+
description: 'Expand scope to include additional requirements',
|
|
586
|
+
expansionItems: trigger.additionalRequirements || [],
|
|
587
|
+
preservesCore: true,
|
|
588
|
+
resourceEfficiency: 'low',
|
|
589
|
+
riskLevel: 'medium',
|
|
590
|
+
conservative: false,
|
|
591
|
+
feasibility: 0.7
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* 削減対象を特定
|
|
597
|
+
* @private
|
|
598
|
+
*/
|
|
599
|
+
_identifyReductionTargets(goal) {
|
|
600
|
+
const deliverables = goal.deliverables || [];
|
|
601
|
+
return deliverables
|
|
602
|
+
.filter(d => d.priority !== 'critical')
|
|
603
|
+
.map(d => ({
|
|
604
|
+
id: d.id,
|
|
605
|
+
name: d.name,
|
|
606
|
+
estimatedSavings: d.effort || 1
|
|
607
|
+
}));
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* サブゴールを提案
|
|
612
|
+
* @private
|
|
613
|
+
*/
|
|
614
|
+
_suggestSubGoals(goal) {
|
|
615
|
+
const phases = ['core', 'enhancement', 'polish'];
|
|
616
|
+
return phases.map((phase, index) => ({
|
|
617
|
+
id: `${goal.id}-${phase}`,
|
|
618
|
+
name: `${goal.name} - ${phase.charAt(0).toUpperCase() + phase.slice(1)} Phase`,
|
|
619
|
+
priority: index === 0 ? goal.priority : 'normal',
|
|
620
|
+
estimatedEffort: Math.ceil((goal.estimatedEffort || 10) / 3)
|
|
621
|
+
}));
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* 調整履歴マネージャー
|
|
627
|
+
* 目標調整の履歴を管理・分析
|
|
628
|
+
*/
|
|
629
|
+
class ModificationHistoryManager {
|
|
630
|
+
/**
|
|
631
|
+
* @param {Object} options - 設定オプション
|
|
632
|
+
*/
|
|
633
|
+
constructor(options = {}) {
|
|
634
|
+
this.history = new Map();
|
|
635
|
+
this.config = {
|
|
636
|
+
maxHistoryPerGoal: options.maxHistoryPerGoal || 50,
|
|
637
|
+
...options
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* 調整を記録
|
|
643
|
+
* @param {string} goalId - 目標ID
|
|
644
|
+
* @param {Object} modification - 調整内容
|
|
645
|
+
* @param {Object} impact - 影響分析結果
|
|
646
|
+
*/
|
|
647
|
+
recordModification(goalId, modification, impact) {
|
|
648
|
+
if (!this.history.has(goalId)) {
|
|
649
|
+
this.history.set(goalId, []);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
const history = this.history.get(goalId);
|
|
653
|
+
history.push({
|
|
654
|
+
id: `mod-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
655
|
+
modification,
|
|
656
|
+
impact,
|
|
657
|
+
timestamp: new Date().toISOString(),
|
|
658
|
+
status: 'applied'
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
// 履歴サイズ制限
|
|
662
|
+
if (history.length > this.config.maxHistoryPerGoal) {
|
|
663
|
+
history.shift();
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* 目標の調整履歴を取得
|
|
669
|
+
* @param {string} goalId - 目標ID
|
|
670
|
+
* @returns {Array} 調整履歴
|
|
671
|
+
*/
|
|
672
|
+
getHistory(goalId) {
|
|
673
|
+
return this.history.get(goalId) || [];
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* 調整パターンを分析
|
|
678
|
+
* @param {string} goalId - 目標ID
|
|
679
|
+
* @returns {Object} パターン分析結果
|
|
680
|
+
*/
|
|
681
|
+
analyzePatterns(goalId) {
|
|
682
|
+
const history = this.getHistory(goalId);
|
|
683
|
+
if (history.length === 0) {
|
|
684
|
+
return { patterns: [], insights: [] };
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
const typeCounts = {};
|
|
688
|
+
const reasonCounts = {};
|
|
689
|
+
let totalImpact = 0;
|
|
690
|
+
|
|
691
|
+
for (const entry of history) {
|
|
692
|
+
const type = entry.modification.type;
|
|
693
|
+
const reason = entry.modification.reason;
|
|
694
|
+
|
|
695
|
+
typeCounts[type] = (typeCounts[type] || 0) + 1;
|
|
696
|
+
reasonCounts[reason] = (reasonCounts[reason] || 0) + 1;
|
|
697
|
+
totalImpact += entry.impact?.totalScore || 0;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const dominantType = Object.entries(typeCounts)
|
|
701
|
+
.sort((a, b) => b[1] - a[1])[0];
|
|
702
|
+
const dominantReason = Object.entries(reasonCounts)
|
|
703
|
+
.sort((a, b) => b[1] - a[1])[0];
|
|
704
|
+
|
|
705
|
+
const insights = [];
|
|
706
|
+
|
|
707
|
+
if (history.length > 5) {
|
|
708
|
+
insights.push({
|
|
709
|
+
type: 'volatility',
|
|
710
|
+
message: 'Goal has been modified frequently - consider stabilization',
|
|
711
|
+
severity: 'warning'
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
if (dominantType && dominantType[1] > history.length * 0.5) {
|
|
716
|
+
insights.push({
|
|
717
|
+
type: 'pattern',
|
|
718
|
+
message: `Recurring ${dominantType[0]} modifications detected`,
|
|
719
|
+
suggestion: `Address root cause of ${dominantReason?.[0] || 'unknown'}`
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
return {
|
|
724
|
+
totalModifications: history.length,
|
|
725
|
+
averageImpact: totalImpact / history.length,
|
|
726
|
+
typeDistribution: typeCounts,
|
|
727
|
+
reasonDistribution: reasonCounts,
|
|
728
|
+
dominantType: dominantType?.[0],
|
|
729
|
+
dominantReason: dominantReason?.[0],
|
|
730
|
+
insights
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* 調整をロールバック
|
|
736
|
+
* @param {string} goalId - 目標ID
|
|
737
|
+
* @param {string} modificationId - 調整ID
|
|
738
|
+
* @returns {Object|null} ロールバックされた調整
|
|
739
|
+
*/
|
|
740
|
+
rollback(goalId, modificationId) {
|
|
741
|
+
const history = this.history.get(goalId);
|
|
742
|
+
if (!history) return null;
|
|
743
|
+
|
|
744
|
+
const index = history.findIndex(h => h.id === modificationId);
|
|
745
|
+
if (index === -1) return null;
|
|
746
|
+
|
|
747
|
+
const entry = history[index];
|
|
748
|
+
entry.status = 'rolled_back';
|
|
749
|
+
entry.rolledBackAt = new Date().toISOString();
|
|
750
|
+
|
|
751
|
+
return entry;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* AdaptiveGoalModifier
|
|
757
|
+
* 状況に応じた目標の動的調整を行うメインクラス
|
|
758
|
+
*/
|
|
759
|
+
class AdaptiveGoalModifier {
|
|
760
|
+
/**
|
|
761
|
+
* @param {Object} options - 設定オプション
|
|
762
|
+
*/
|
|
763
|
+
constructor(options = {}) {
|
|
764
|
+
this.impactAnalyzer = new ImpactAnalyzer(options.impact);
|
|
765
|
+
this.strategy = new ModificationStrategy(options.strategy);
|
|
766
|
+
this.historyManager = new ModificationHistoryManager(options.history);
|
|
767
|
+
|
|
768
|
+
this.config = {
|
|
769
|
+
requireApproval: options.requireApproval ?? true,
|
|
770
|
+
autoModifyThreshold: options.autoModifyThreshold || 0.3,
|
|
771
|
+
notifyOnModification: options.notifyOnModification ?? true,
|
|
772
|
+
...options
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
this.goals = new Map();
|
|
776
|
+
this.pendingModifications = new Map();
|
|
777
|
+
this.eventHandlers = new Map();
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* 目標を登録
|
|
782
|
+
* @param {Object} goal - 目標定義
|
|
783
|
+
* @returns {Object} 登録された目標
|
|
784
|
+
*/
|
|
785
|
+
registerGoal(goal) {
|
|
786
|
+
const normalizedGoal = {
|
|
787
|
+
id: goal.id || `goal-${Date.now()}`,
|
|
788
|
+
name: goal.name,
|
|
789
|
+
description: goal.description,
|
|
790
|
+
priority: goal.priority || 'normal',
|
|
791
|
+
targetDate: goal.targetDate,
|
|
792
|
+
successCriteria: goal.successCriteria || [],
|
|
793
|
+
deliverables: goal.deliverables || [],
|
|
794
|
+
dependencies: goal.dependencies || [],
|
|
795
|
+
resources: goal.resources || [],
|
|
796
|
+
estimatedEffort: goal.estimatedEffort,
|
|
797
|
+
status: 'active',
|
|
798
|
+
createdAt: new Date().toISOString(),
|
|
799
|
+
modificationCount: 0
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
this.goals.set(normalizedGoal.id, normalizedGoal);
|
|
803
|
+
return normalizedGoal;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* 目標調整をトリガー
|
|
808
|
+
* @param {string} goalId - 目標ID
|
|
809
|
+
* @param {Object} trigger - トリガー情報
|
|
810
|
+
* @returns {Promise<Object>} 調整結果
|
|
811
|
+
*/
|
|
812
|
+
async triggerModification(goalId, trigger) {
|
|
813
|
+
const goal = this.goals.get(goalId);
|
|
814
|
+
if (!goal) {
|
|
815
|
+
throw new Error(`Goal not found: ${goalId}`);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// コンテキスト構築
|
|
819
|
+
const context = this._buildContext(goalId);
|
|
820
|
+
|
|
821
|
+
// 戦略決定
|
|
822
|
+
const strategyResult = this.strategy.determineStrategy(goal, trigger, context);
|
|
823
|
+
|
|
824
|
+
// 影響分析
|
|
825
|
+
const impact = this.impactAnalyzer.analyzeImpact(
|
|
826
|
+
goal,
|
|
827
|
+
{ type: strategyResult.recommended.type, ...strategyResult.recommended },
|
|
828
|
+
context
|
|
829
|
+
);
|
|
830
|
+
|
|
831
|
+
const modification = {
|
|
832
|
+
id: `mod-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
833
|
+
goalId,
|
|
834
|
+
trigger,
|
|
835
|
+
strategy: strategyResult.recommended,
|
|
836
|
+
alternatives: strategyResult.alternatives,
|
|
837
|
+
impact,
|
|
838
|
+
confidence: strategyResult.confidence,
|
|
839
|
+
status: 'pending',
|
|
840
|
+
createdAt: new Date().toISOString()
|
|
841
|
+
};
|
|
842
|
+
|
|
843
|
+
// 自動承認チェック
|
|
844
|
+
if (!this.config.requireApproval ||
|
|
845
|
+
(strategyResult.autoApprovable && impact.totalScore < this.config.autoModifyThreshold)) {
|
|
846
|
+
return this._applyModification(modification);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// 承認待ち
|
|
850
|
+
this.pendingModifications.set(modification.id, modification);
|
|
851
|
+
this._emit('modification_pending', modification);
|
|
852
|
+
|
|
853
|
+
return {
|
|
854
|
+
status: 'pending_approval',
|
|
855
|
+
modification,
|
|
856
|
+
message: 'Modification requires approval before application'
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* 調整を承認
|
|
862
|
+
* @param {string} modificationId - 調整ID
|
|
863
|
+
* @returns {Object} 適用結果
|
|
864
|
+
*/
|
|
865
|
+
approveModification(modificationId) {
|
|
866
|
+
const modification = this.pendingModifications.get(modificationId);
|
|
867
|
+
if (!modification) {
|
|
868
|
+
throw new Error(`Modification not found: ${modificationId}`);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
this.pendingModifications.delete(modificationId);
|
|
872
|
+
return this._applyModification(modification);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* 調整を拒否
|
|
877
|
+
* @param {string} modificationId - 調整ID
|
|
878
|
+
* @param {string} reason - 拒否理由
|
|
879
|
+
* @returns {Object} 拒否結果
|
|
880
|
+
*/
|
|
881
|
+
rejectModification(modificationId, reason) {
|
|
882
|
+
const modification = this.pendingModifications.get(modificationId);
|
|
883
|
+
if (!modification) {
|
|
884
|
+
throw new Error(`Modification not found: ${modificationId}`);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
modification.status = 'rejected';
|
|
888
|
+
modification.rejectedAt = new Date().toISOString();
|
|
889
|
+
modification.rejectionReason = reason;
|
|
890
|
+
|
|
891
|
+
this.pendingModifications.delete(modificationId);
|
|
892
|
+
this._emit('modification_rejected', modification);
|
|
893
|
+
|
|
894
|
+
return {
|
|
895
|
+
status: 'rejected',
|
|
896
|
+
modification,
|
|
897
|
+
message: `Modification rejected: ${reason}`
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* 調整を適用
|
|
903
|
+
* @private
|
|
904
|
+
*/
|
|
905
|
+
_applyModification(modification) {
|
|
906
|
+
const goal = this.goals.get(modification.goalId);
|
|
907
|
+
if (!goal) {
|
|
908
|
+
throw new Error(`Goal not found: ${modification.goalId}`);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
const strategy = modification.strategy;
|
|
912
|
+
const previousState = { ...goal };
|
|
913
|
+
|
|
914
|
+
// タイプに応じた調整適用
|
|
915
|
+
switch (strategy.type) {
|
|
916
|
+
case ModificationType.PRIORITY_ADJUSTMENT:
|
|
917
|
+
goal.priority = strategy.newPriority;
|
|
918
|
+
break;
|
|
919
|
+
|
|
920
|
+
case ModificationType.SCOPE_REDUCTION:
|
|
921
|
+
goal.deliverables = goal.deliverables.filter(d =>
|
|
922
|
+
!strategy.reductionTargets?.some(t => t.id === d.id)
|
|
923
|
+
);
|
|
924
|
+
break;
|
|
925
|
+
|
|
926
|
+
case ModificationType.SCOPE_EXPANSION:
|
|
927
|
+
goal.deliverables = [
|
|
928
|
+
...goal.deliverables,
|
|
929
|
+
...(strategy.expansionItems || [])
|
|
930
|
+
];
|
|
931
|
+
break;
|
|
932
|
+
|
|
933
|
+
case ModificationType.TIMELINE_EXTENSION:
|
|
934
|
+
if (goal.targetDate) {
|
|
935
|
+
const date = new Date(goal.targetDate);
|
|
936
|
+
date.setDate(date.getDate() + (strategy.extensionDays || 7));
|
|
937
|
+
goal.targetDate = date.toISOString();
|
|
938
|
+
}
|
|
939
|
+
break;
|
|
940
|
+
|
|
941
|
+
case ModificationType.TIMELINE_COMPRESSION:
|
|
942
|
+
if (goal.targetDate) {
|
|
943
|
+
const date = new Date(goal.targetDate);
|
|
944
|
+
date.setDate(date.getDate() - (strategy.compressionDays || 3));
|
|
945
|
+
goal.targetDate = date.toISOString();
|
|
946
|
+
}
|
|
947
|
+
break;
|
|
948
|
+
|
|
949
|
+
case ModificationType.SUCCESS_CRITERIA_RELAXATION:
|
|
950
|
+
goal.successCriteria = goal.successCriteria.map(c => ({
|
|
951
|
+
...c,
|
|
952
|
+
threshold: c.threshold ? c.threshold * 0.8 : c.threshold
|
|
953
|
+
}));
|
|
954
|
+
break;
|
|
955
|
+
|
|
956
|
+
case ModificationType.SUCCESS_CRITERIA_TIGHTENING:
|
|
957
|
+
goal.successCriteria = goal.successCriteria.map(c => ({
|
|
958
|
+
...c,
|
|
959
|
+
threshold: c.threshold ? c.threshold * 1.2 : c.threshold
|
|
960
|
+
}));
|
|
961
|
+
break;
|
|
962
|
+
|
|
963
|
+
case ModificationType.GOAL_DECOMPOSITION:
|
|
964
|
+
// サブゴール作成
|
|
965
|
+
for (const subGoal of strategy.suggestedSubGoals || []) {
|
|
966
|
+
this.registerGoal({
|
|
967
|
+
...subGoal,
|
|
968
|
+
parentGoalId: goal.id
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
goal.status = 'decomposed';
|
|
972
|
+
break;
|
|
973
|
+
|
|
974
|
+
case ModificationType.GOAL_DEFERRAL:
|
|
975
|
+
goal.status = 'deferred';
|
|
976
|
+
goal.deferredAt = new Date().toISOString();
|
|
977
|
+
goal.deferralReason = strategy.deferralReason;
|
|
978
|
+
break;
|
|
979
|
+
|
|
980
|
+
case ModificationType.GOAL_CANCELLATION:
|
|
981
|
+
goal.status = 'cancelled';
|
|
982
|
+
goal.cancelledAt = new Date().toISOString();
|
|
983
|
+
break;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
// メタデータ更新
|
|
987
|
+
goal.modificationCount++;
|
|
988
|
+
goal.lastModifiedAt = new Date().toISOString();
|
|
989
|
+
|
|
990
|
+
// 履歴記録
|
|
991
|
+
this.historyManager.recordModification(
|
|
992
|
+
modification.goalId,
|
|
993
|
+
{ ...modification, previousState },
|
|
994
|
+
modification.impact
|
|
995
|
+
);
|
|
996
|
+
|
|
997
|
+
modification.status = 'applied';
|
|
998
|
+
modification.appliedAt = new Date().toISOString();
|
|
999
|
+
|
|
1000
|
+
this._emit('modification_applied', {
|
|
1001
|
+
modification,
|
|
1002
|
+
goal,
|
|
1003
|
+
previousState
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
return {
|
|
1007
|
+
status: 'applied',
|
|
1008
|
+
modification,
|
|
1009
|
+
goal,
|
|
1010
|
+
previousState,
|
|
1011
|
+
message: `Successfully applied ${strategy.type} to goal ${goal.id}`
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
* コンテキストを構築
|
|
1017
|
+
* @private
|
|
1018
|
+
*/
|
|
1019
|
+
_buildContext(goalId) {
|
|
1020
|
+
return {
|
|
1021
|
+
goals: Array.from(this.goals.values()),
|
|
1022
|
+
currentGoal: this.goals.get(goalId),
|
|
1023
|
+
history: this.historyManager.getHistory(goalId),
|
|
1024
|
+
patterns: this.historyManager.analyzePatterns(goalId)
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* 目標を取得
|
|
1030
|
+
* @param {string} goalId - 目標ID
|
|
1031
|
+
* @returns {Object|undefined} 目標
|
|
1032
|
+
*/
|
|
1033
|
+
getGoal(goalId) {
|
|
1034
|
+
return this.goals.get(goalId);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
/**
|
|
1038
|
+
* 全目標を取得
|
|
1039
|
+
* @returns {Array} 全目標リスト
|
|
1040
|
+
*/
|
|
1041
|
+
getAllGoals() {
|
|
1042
|
+
return Array.from(this.goals.values());
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
/**
|
|
1046
|
+
* 目標の調整履歴を取得
|
|
1047
|
+
* @param {string} goalId - 目標ID
|
|
1048
|
+
* @returns {Object} 履歴と分析
|
|
1049
|
+
*/
|
|
1050
|
+
getGoalHistory(goalId) {
|
|
1051
|
+
return {
|
|
1052
|
+
history: this.historyManager.getHistory(goalId),
|
|
1053
|
+
patterns: this.historyManager.analyzePatterns(goalId)
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* 保留中の調整を取得
|
|
1059
|
+
* @returns {Array} 保留中の調整リスト
|
|
1060
|
+
*/
|
|
1061
|
+
getPendingModifications() {
|
|
1062
|
+
return Array.from(this.pendingModifications.values());
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/**
|
|
1066
|
+
* イベントハンドラーを登録
|
|
1067
|
+
* @param {string} event - イベント名
|
|
1068
|
+
* @param {Function} handler - ハンドラー関数
|
|
1069
|
+
*/
|
|
1070
|
+
on(event, handler) {
|
|
1071
|
+
if (!this.eventHandlers.has(event)) {
|
|
1072
|
+
this.eventHandlers.set(event, []);
|
|
1073
|
+
}
|
|
1074
|
+
this.eventHandlers.get(event).push(handler);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
/**
|
|
1078
|
+
* イベントを発火
|
|
1079
|
+
* @private
|
|
1080
|
+
*/
|
|
1081
|
+
_emit(event, data) {
|
|
1082
|
+
const handlers = this.eventHandlers.get(event) || [];
|
|
1083
|
+
for (const handler of handlers) {
|
|
1084
|
+
try {
|
|
1085
|
+
handler(data);
|
|
1086
|
+
} catch (error) {
|
|
1087
|
+
console.error(`Error in event handler for ${event}:`, error);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* 調整提案を生成
|
|
1094
|
+
* @param {string} goalId - 目標ID
|
|
1095
|
+
* @param {Object} currentState - 現在の状態
|
|
1096
|
+
* @returns {Object} 調整提案
|
|
1097
|
+
*/
|
|
1098
|
+
generateSuggestions(goalId, currentState) {
|
|
1099
|
+
const goal = this.goals.get(goalId);
|
|
1100
|
+
if (!goal) {
|
|
1101
|
+
return { suggestions: [] };
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
const suggestions = [];
|
|
1105
|
+
const patterns = this.historyManager.analyzePatterns(goalId);
|
|
1106
|
+
|
|
1107
|
+
// 進捗ベースの提案
|
|
1108
|
+
if (currentState.progress < 0.3 && currentState.timeElapsed > 0.5) {
|
|
1109
|
+
suggestions.push({
|
|
1110
|
+
trigger: { reason: ModificationReason.TIME_CONSTRAINT, gap: 0.2 },
|
|
1111
|
+
urgency: 'high',
|
|
1112
|
+
description: 'Progress behind schedule - consider scope reduction or timeline extension'
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// リソースベースの提案
|
|
1117
|
+
if (currentState.resourceUtilization > 0.9) {
|
|
1118
|
+
suggestions.push({
|
|
1119
|
+
trigger: { reason: ModificationReason.RESOURCE_CONSTRAINT },
|
|
1120
|
+
urgency: 'medium',
|
|
1121
|
+
description: 'High resource utilization - consider prioritization'
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
// パターンベースの提案
|
|
1126
|
+
if (patterns.insights?.some(i => i.type === 'volatility')) {
|
|
1127
|
+
suggestions.push({
|
|
1128
|
+
trigger: { reason: ModificationReason.SCOPE_CREEP },
|
|
1129
|
+
urgency: 'medium',
|
|
1130
|
+
description: 'Goal volatility detected - consider stabilization or decomposition'
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
return {
|
|
1135
|
+
goalId,
|
|
1136
|
+
suggestions,
|
|
1137
|
+
patterns,
|
|
1138
|
+
generatedAt: new Date().toISOString()
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
module.exports = {
|
|
1144
|
+
AdaptiveGoalModifier,
|
|
1145
|
+
ImpactAnalyzer,
|
|
1146
|
+
ModificationStrategy,
|
|
1147
|
+
ModificationHistoryManager,
|
|
1148
|
+
ModificationReason,
|
|
1149
|
+
ModificationType
|
|
1150
|
+
};
|