create-principles-disciple 1.102.0 → 1.103.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/console/dist/server/config/pd-config-store.d.ts +50 -1
- package/console/dist/server/config/pd-config-store.js +364 -1
- package/console/dist/server/models/ActivationsConsoleModel.d.ts +7 -4
- package/console/dist/server/models/ActivationsConsoleModel.js +7 -6
- package/console/dist/server/routes/config.js +92 -2
- package/console/dist/ui/api.d.ts +44 -3
- package/console/dist/ui/api.js +58 -2
- package/console/dist/ui/components/onboarding/DemoResultView.js +19 -1
- package/console/dist/ui/i18n/en.json +63 -2
- package/console/dist/ui/i18n/zh-CN.json +63 -2
- package/console/dist/ui/pages/activation/ActivationPage.js +11 -11
- package/console/dist/ui/pages/activation/ActivationValidators.js +14 -13
- package/console/dist/ui/pages/control-center/AgentCard.js +56 -7
- package/console/dist/ui/pages/control-center/ControlCenterPage.js +2 -1
- package/console/dist/ui/pages/control-center/RuntimeProfileManager.d.ts +31 -0
- package/console/dist/ui/pages/control-center/RuntimeProfileManager.js +234 -0
- package/console/dist/ui/pages/debt/DebtPage.js +2 -2
- package/console/dist/ui/utils/validators.d.ts +26 -3
- package/console/dist/ui/utils/validators.js +23 -6
- package/console/dist/web/assets/app.css +41 -0
- package/console/dist/web/assets/app.js +1918 -1160
- package/core/dist/runtime-v2/__tests__/resolve-runtime-config-from-pd-config.test.js +8 -10
- package/core/dist/runtime-v2/__tests__/resolve-runtime-config-from-pd-config.test.js.map +1 -1
- package/core/dist/runtime-v2/adapter/__tests__/l2-resolve-model-compat.test.d.ts +2 -0
- package/core/dist/runtime-v2/adapter/__tests__/l2-resolve-model-compat.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/adapter/__tests__/l2-resolve-model-compat.test.js +81 -0
- package/core/dist/runtime-v2/adapter/__tests__/l2-resolve-model-compat.test.js.map +1 -0
- package/core/dist/runtime-v2/adapter/__tests__/pi-ai-runtime-adapter.test.js +19 -0
- package/core/dist/runtime-v2/adapter/__tests__/pi-ai-runtime-adapter.test.js.map +1 -1
- package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts +8 -0
- package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts.map +1 -1
- package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js +4 -1
- package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js.map +1 -1
- package/core/dist/runtime-v2/candidate-intake-service.d.ts.map +1 -1
- package/core/dist/runtime-v2/candidate-intake-service.js +92 -6
- package/core/dist/runtime-v2/candidate-intake-service.js.map +1 -1
- package/core/dist/runtime-v2/candidate-intake.d.ts +38 -0
- package/core/dist/runtime-v2/candidate-intake.d.ts.map +1 -1
- package/core/dist/runtime-v2/candidate-intake.js +48 -0
- package/core/dist/runtime-v2/candidate-intake.js.map +1 -1
- package/core/dist/runtime-v2/config/__tests__/pd-config-agent-binding.test.js +26 -1
- package/core/dist/runtime-v2/config/__tests__/pd-config-agent-binding.test.js.map +1 -1
- package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.js +9 -5
- package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.js.map +1 -1
- package/core/dist/runtime-v2/config/__tests__/pd-config-validation-edge-cases.test.js +53 -33
- package/core/dist/runtime-v2/config/__tests__/pd-config-validation-edge-cases.test.js.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-agent-binding.d.ts +2 -0
- package/core/dist/runtime-v2/config/pd-config-agent-binding.d.ts.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-agent-binding.js +3 -0
- package/core/dist/runtime-v2/config/pd-config-agent-binding.js.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-defaults.d.ts +7 -3
- package/core/dist/runtime-v2/config/pd-config-defaults.d.ts.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-defaults.js +27 -3
- package/core/dist/runtime-v2/config/pd-config-defaults.js.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-redaction.d.ts.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-redaction.js +5 -2
- package/core/dist/runtime-v2/config/pd-config-redaction.js.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-types.d.ts +6 -0
- package/core/dist/runtime-v2/config/pd-config-types.d.ts.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-types.js.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-validate.d.ts.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-validate.js +11 -6
- package/core/dist/runtime-v2/config/pd-config-validate.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +2 -1
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +1 -0
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/artificer-prompt-builder-v2.test.js +7 -0
- package/core/dist/runtime-v2/internalization/__tests__/artificer-prompt-builder-v2.test.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/diag-rootcause-runner.test.js +7 -6
- package/core/dist/runtime-v2/internalization/__tests__/diag-rootcause-runner.test.js.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js +1 -1
- package/core/dist/runtime-v2/internalization/diag-distiller-runner.js +4 -4
- package/core/dist/runtime-v2/internalization/diag-distiller-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/diag-rootcause-runner.js +5 -5
- package/core/dist/runtime-v2/internalization/diag-rootcause-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/diag-router-runner.js +4 -4
- package/core/dist/runtime-v2/internalization/diag-router-runner.js.map +1 -1
- package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts +2 -0
- package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts.map +1 -1
- package/core/dist/runtime-v2/pain-signal-runtime-factory.js +2 -0
- package/core/dist/runtime-v2/pain-signal-runtime-factory.js.map +1 -1
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +5 -1
- package/dist/installer.js.map +1 -1
- package/dist/mvp-config.d.ts.map +1 -1
- package/dist/mvp-config.js +22 -10
- package/dist/mvp-config.js.map +1 -1
- package/package.json +1 -1
- package/pd-cli/dist/commands/__tests__/runtime-probe-config.test.js +2 -2
- package/pd-cli/dist/commands/__tests__/runtime-probe-config.test.js.map +1 -1
- package/pd-cli/dist/commands/demo-story-a.d.ts.map +1 -1
- package/pd-cli/dist/commands/demo-story-a.js +4 -2
- package/pd-cli/dist/commands/demo-story-a.js.map +1 -1
- package/pd-cli/dist/commands/proven-channel-baseline.d.ts.map +1 -1
- package/pd-cli/dist/commands/proven-channel-baseline.js +4 -2
- package/pd-cli/dist/commands/proven-channel-baseline.js.map +1 -1
- package/pd-cli/dist/commands/runtime-activation.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-activation.js +8 -2
- package/pd-cli/dist/commands/runtime-activation.js.map +1 -1
- package/pd-cli/dist/commands/runtime-canary.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-canary.js +4 -2
- package/pd-cli/dist/commands/runtime-canary.js.map +1 -1
- package/pd-cli/dist/commands/runtime-health-snapshot.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-health-snapshot.js +4 -2
- package/pd-cli/dist/commands/runtime-health-snapshot.js.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-integrity.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-integrity.js +4 -2
- package/pd-cli/dist/commands/runtime-internalization-integrity.js.map +1 -1
- package/pd-cli/dist/services/config-doctor.d.ts.map +1 -1
- package/pd-cli/dist/services/config-doctor.js +9 -2
- package/pd-cli/dist/services/config-doctor.js.map +1 -1
- package/pd-cli/dist/services/runtime-adapter-resolver.d.ts.map +1 -1
- package/pd-cli/dist/services/runtime-adapter-resolver.js +1 -0
- package/pd-cli/dist/services/runtime-adapter-resolver.js.map +1 -1
- package/plugin/dist/core/rule-host.d.ts +5 -2
- package/plugin/dist/core/rule-host.js +15 -2
- package/plugin/dist/hooks/gate.js +1 -1
- package/plugin/dist/service/evolution-dedup.d.ts +1 -1
- package/plugin/dist/service/evolution-worker.d.ts +7 -31
- package/plugin/dist/service/evolution-worker.js +17 -36
- package/plugin/dist/service/queue-io.js +14 -2
- package/plugin/dist/service/queue-migration.d.ts +27 -26
- package/plugin/dist/service/queue-migration.js +57 -0
- package/plugin/templates/langs/en/core/AGENTS.md +2 -2
- package/plugin/templates/langs/en/core/TOOLS.md +1 -1
- package/plugin/templates/langs/zh/core/AGENTS.md +2 -2
- package/plugin/templates/langs/zh/core/TOOLS.md +1 -1
- package/templates/langs/en/core/AGENTS.md +2 -2
- package/templates/langs/en/core/TOOLS.md +1 -1
- package/templates/langs/zh/core/AGENTS.md +2 -2
- package/templates/langs/zh/core/TOOLS.md +1 -1
- package/plugin/dist/constants/diagnostician.d.ts +0 -12
- package/plugin/dist/constants/diagnostician.js +0 -65
- package/plugin/dist/service/evolution-queue-migration.d.ts +0 -128
- package/plugin/dist/service/evolution-queue-migration.js +0 -53
- package/plugin/templates/langs/en/skills/pd-diagnostician/SKILL.md +0 -466
- package/plugin/templates/langs/zh/skills/pd-diagnostician/SKILL.md +0 -391
- package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +0 -287
|
@@ -161,7 +161,7 @@ export class RuleHost {
|
|
|
161
161
|
try {
|
|
162
162
|
const { loaded, skipped } = this._loadFromActivationsTable(this.workspaceDir, supportsContextV2);
|
|
163
163
|
if (loaded.length === 0) {
|
|
164
|
-
this._emitEmptyLoadWarn('armed but empty — 0 active code_tool_hook activations loaded (RuleHost will not block or require approval)', 'If this is unexpected, run `pd
|
|
164
|
+
this._emitEmptyLoadWarn('armed but empty — 0 active code_tool_hook activations loaded (RuleHost will not block or require approval)', 'If this is unexpected, run `pd activation list --channel code_tool_hook` to inspect activations, or `pd activation promote --activation-id <id> --confirm` to enable a live rule');
|
|
165
165
|
}
|
|
166
166
|
return { loaded, skipped };
|
|
167
167
|
}
|
|
@@ -258,7 +258,13 @@ export class RuleHost {
|
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
// Emit structured unhealthy evidence for duplicate groups; collect valid (non-duplicate) rows.
|
|
261
|
+
// PRI-497: duplicate activations are ALSO pushed to the `skipped` array
|
|
262
|
+
// so callers (gate.ts, CLI, Console) can observe them via
|
|
263
|
+
// skippedActivations — not just via rulehost_unhealthy event log.
|
|
264
|
+
// Both paths are populated (rc-9-no-silent-fallback): event log for
|
|
265
|
+
// telemetry, skipped array for structured caller-facing observability.
|
|
261
266
|
const validRows = [];
|
|
267
|
+
const skipped = [];
|
|
262
268
|
for (const [targetRef, group] of rowsByTargetRef) {
|
|
263
269
|
if (group.length > 1) {
|
|
264
270
|
const activationIds = [];
|
|
@@ -280,6 +286,14 @@ export class RuleHost {
|
|
|
280
286
|
const artifactId = typeof r['artifact_id'] === 'string' ? r['artifact_id'] : '';
|
|
281
287
|
if (activationId && artifactId) {
|
|
282
288
|
this._recordUnhealthy(activationId, artifactId, targetRef, `duplicate active activation for target_ref ${targetRef}`, 'Deactivate all but one activation for this target_ref');
|
|
289
|
+
// PRI-497: also surface in skippedActivations for caller observability
|
|
290
|
+
skipped.push({
|
|
291
|
+
activationId,
|
|
292
|
+
ruleId: targetRef,
|
|
293
|
+
mode: 'live',
|
|
294
|
+
reason: 'duplicate_active_activation',
|
|
295
|
+
nextAction: 'Deactivate all but one activation for this target_ref',
|
|
296
|
+
});
|
|
283
297
|
}
|
|
284
298
|
}
|
|
285
299
|
}
|
|
@@ -290,7 +304,6 @@ export class RuleHost {
|
|
|
290
304
|
}
|
|
291
305
|
}
|
|
292
306
|
const loaded = [];
|
|
293
|
-
const skipped = [];
|
|
294
307
|
for (const r of validRows) {
|
|
295
308
|
const activationId = typeof r['activation_id'] === 'string' ? r['activation_id'] : '';
|
|
296
309
|
const artifactId = typeof r['artifact_id'] === 'string' ? r['artifact_id'] : '';
|
|
@@ -80,7 +80,7 @@ export function handleBeforeToolCall(event, ctx) {
|
|
|
80
80
|
};
|
|
81
81
|
const report = typeof ruleHost.evaluateDetailed === 'function'
|
|
82
82
|
? ruleHost.evaluateDetailed(hostInput)
|
|
83
|
-
: { liveDecision: ruleHost.evaluate(hostInput), shadowDecisions: [] };
|
|
83
|
+
: { liveDecision: ruleHost.evaluate(hostInput), shadowDecisions: [], skippedActivations: [] };
|
|
84
84
|
const hostResult = report.liveDecision;
|
|
85
85
|
for (const shadowDecision of report.shadowDecisions) {
|
|
86
86
|
try {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Dedup logic for preventing duplicate pain tasks and redundant reflections.
|
|
5
5
|
* Extracted from evolution-worker.ts.
|
|
6
6
|
*/
|
|
7
|
-
import type { EvolutionQueueItem } from '
|
|
7
|
+
import type { EvolutionQueueItem } from '../core/evolution-types.js';
|
|
8
8
|
/**
|
|
9
9
|
* Dedup window for pain queue tasks (30 minutes).
|
|
10
10
|
*/
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { OpenClawPluginServiceContext, OpenClawPluginApi, PluginLogger } from '../openclaw-sdk.js';
|
|
2
2
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
3
|
-
import type { TaskKind, TaskPriority } from '../core/trajectory-types.js';
|
|
4
3
|
export type { TaskKind, TaskPriority } from '../core/trajectory-types.js';
|
|
5
4
|
export { loadEvolutionQueue, saveEvolutionQueue, withQueueLock, acquireQueueLock, requireQueueLock } from './queue-io.js';
|
|
6
5
|
export { EVOLUTION_QUEUE_LOCK_SUFFIX, LOCK_MAX_RETRIES, LOCK_RETRY_DELAY_MS, LOCK_STALE_MS } from './queue-io.js';
|
|
@@ -24,37 +23,14 @@ export type { WatchdogResult };
|
|
|
24
23
|
* Pain diagnosis is Runtime v2 only: after_tool_call / pd pain record ->
|
|
25
24
|
* PainSignalBridge -> SplitDiagnosticianRunner. EvolutionWorker does not read
|
|
26
25
|
* .pain_flag or process pain_diagnosis queue items.
|
|
26
|
+
*
|
|
27
|
+
* Types (QueueStatus / TaskResolution / EvolutionQueueItem) are re-exported
|
|
28
|
+
* from queue-migration.ts, which sources them from evolution-types.ts
|
|
29
|
+
* (canonical single source of truth).
|
|
27
30
|
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export
|
|
31
|
-
export interface EvolutionQueueItem {
|
|
32
|
-
id: string;
|
|
33
|
-
taskKind: TaskKind;
|
|
34
|
-
priority: TaskPriority;
|
|
35
|
-
source: string;
|
|
36
|
-
traceId?: string;
|
|
37
|
-
task?: string;
|
|
38
|
-
score: number;
|
|
39
|
-
reason: string;
|
|
40
|
-
timestamp: string;
|
|
41
|
-
enqueued_at?: string;
|
|
42
|
-
started_at?: string;
|
|
43
|
-
completed_at?: string;
|
|
44
|
-
assigned_session_key?: string;
|
|
45
|
-
trigger_text_preview?: string;
|
|
46
|
-
status: QueueStatus;
|
|
47
|
-
resolution?: TaskResolution;
|
|
48
|
-
session_id?: string;
|
|
49
|
-
agent_id?: string;
|
|
50
|
-
retryCount: number;
|
|
51
|
-
maxRetries: number;
|
|
52
|
-
lastError?: string;
|
|
53
|
-
resultRef?: string;
|
|
54
|
-
painEventId?: number;
|
|
55
|
-
}
|
|
56
|
-
import { migrateToV2, isLegacyQueueItem, migrateQueueToV2, LegacyEvolutionQueueItem, DEFAULT_TASK_KIND, DEFAULT_PRIORITY, DEFAULT_MAX_RETRIES, type RawQueueItem } from './queue-migration.js';
|
|
57
|
-
export { migrateToV2, isLegacyQueueItem, migrateQueueToV2, LegacyEvolutionQueueItem, DEFAULT_TASK_KIND, DEFAULT_PRIORITY, DEFAULT_MAX_RETRIES };
|
|
31
|
+
export type { QueueStatus, TaskResolution, EvolutionQueueItem } from './queue-migration.js';
|
|
32
|
+
import { migrateToV2, isLegacyQueueItem, migrateQueueToV2, LegacyEvolutionQueueItem, DEFAULT_TASK_KIND, DEFAULT_PRIORITY, DEFAULT_MAX_RETRIES, validateQueueItem, VALID_TASK_KINDS, isValidQueueItem, type RawQueueItem, type EvolutionQueueItem } from './queue-migration.js';
|
|
33
|
+
export { migrateToV2, isLegacyQueueItem, migrateQueueToV2, LegacyEvolutionQueueItem, DEFAULT_TASK_KIND, DEFAULT_PRIORITY, DEFAULT_MAX_RETRIES, validateQueueItem, VALID_TASK_KINDS, isValidQueueItem };
|
|
58
34
|
export type { RawQueueItem };
|
|
59
35
|
export declare function extractEvolutionTaskId(task: string): string | null;
|
|
60
36
|
export declare function purgeStaleFailedTasks(queue: EvolutionQueueItem[], logger: PluginLogger): {
|
|
@@ -11,7 +11,7 @@ import { atomicWriteFileSync } from '../utils/io.js';
|
|
|
11
11
|
// Re-export queue I/O (extracted to queue-io.ts)
|
|
12
12
|
export { loadEvolutionQueue, saveEvolutionQueue, withQueueLock, acquireQueueLock, requireQueueLock } from './queue-io.js';
|
|
13
13
|
export { EVOLUTION_QUEUE_LOCK_SUFFIX, LOCK_MAX_RETRIES, LOCK_RETRY_DELAY_MS, LOCK_STALE_MS } from './queue-io.js';
|
|
14
|
-
import { saveEvolutionQueue, requireQueueLock } from './queue-io.js';
|
|
14
|
+
import { saveEvolutionQueue, requireQueueLock, loadEvolutionQueue } from './queue-io.js';
|
|
15
15
|
import { WorkflowStore } from './subagent-workflow/workflow-store.js';
|
|
16
16
|
import { PrincipleCompiler } from '../core/principle-compiler/index.js';
|
|
17
17
|
import { loadLedger, updatePrinciple } from '../core/principle-tree-ledger.js';
|
|
@@ -58,8 +58,8 @@ import { runWorkflowWatchdog } from './workflow-watchdog.js';
|
|
|
58
58
|
export { runWorkflowWatchdog };
|
|
59
59
|
let timeoutId = null;
|
|
60
60
|
// ── Queue Migration (extracted to queue-migration.ts) ────────────────────────
|
|
61
|
-
import { migrateToV2, isLegacyQueueItem, migrateQueueToV2, DEFAULT_TASK_KIND, DEFAULT_PRIORITY, DEFAULT_MAX_RETRIES } from './queue-migration.js';
|
|
62
|
-
export { migrateToV2, isLegacyQueueItem, migrateQueueToV2, DEFAULT_TASK_KIND, DEFAULT_PRIORITY, DEFAULT_MAX_RETRIES };
|
|
61
|
+
import { migrateToV2, isLegacyQueueItem, migrateQueueToV2, DEFAULT_TASK_KIND, DEFAULT_PRIORITY, DEFAULT_MAX_RETRIES, validateQueueItem, VALID_TASK_KINDS, isValidQueueItem } from './queue-migration.js';
|
|
62
|
+
export { migrateToV2, isLegacyQueueItem, migrateQueueToV2, DEFAULT_TASK_KIND, DEFAULT_PRIORITY, DEFAULT_MAX_RETRIES, validateQueueItem, VALID_TASK_KINDS, isValidQueueItem };
|
|
63
63
|
// Queue lock constants and requireQueueLock are imported from queue-io.ts
|
|
64
64
|
export function extractEvolutionTaskId(task) {
|
|
65
65
|
if (!task)
|
|
@@ -302,30 +302,12 @@ async function processEvolutionQueue(wctx, logger, _eventLog, _api) {
|
|
|
302
302
|
logger?.info?.(`[PD:EvolutionWorker] Dropped ${beforeLegacyPainDrop - queue.length} legacy pain_diagnosis queue item(s); use PainSignalBridge/pd pain record`);
|
|
303
303
|
}
|
|
304
304
|
// Validate queue items — filter out malformed entries before processing.
|
|
305
|
-
//
|
|
305
|
+
// rc-1/rc-4 (ERR-007): consolidated in validateQueueItem (queue-migration.ts)
|
|
306
|
+
// so every load path applies the same filter. Malformed items are logged +
|
|
307
|
+
// skipped; they never crash the evolution cycle.
|
|
306
308
|
const beforeValidation = queue.length;
|
|
307
309
|
queue = queue.filter((item) => {
|
|
308
|
-
const errors =
|
|
309
|
-
if (!item.id || typeof item.id !== 'string')
|
|
310
|
-
errors.push('missing/invalid id');
|
|
311
|
-
if (!item.source || typeof item.source !== 'string')
|
|
312
|
-
errors.push('missing/invalid source');
|
|
313
|
-
if (typeof item.score !== 'number')
|
|
314
|
-
errors.push('missing/invalid score');
|
|
315
|
-
if (!item.status || typeof item.status !== 'string')
|
|
316
|
-
errors.push('missing/invalid status');
|
|
317
|
-
if (!item.taskKind || typeof item.taskKind !== 'string')
|
|
318
|
-
errors.push('missing/invalid taskKind');
|
|
319
|
-
else {
|
|
320
|
-
const validTaskKinds = ['model_eval'];
|
|
321
|
-
if (!validTaskKinds.includes(item.taskKind)) {
|
|
322
|
-
errors.push(`invalid taskKind value '${item.taskKind}' (expected one of: ${validTaskKinds.join(', ')})`);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
if (typeof item.retryCount !== 'number')
|
|
326
|
-
errors.push('missing/invalid retryCount');
|
|
327
|
-
if (typeof item.maxRetries !== 'number')
|
|
328
|
-
errors.push('missing/invalid maxRetries');
|
|
310
|
+
const errors = validateQueueItem(item);
|
|
329
311
|
if (errors.length > 0) {
|
|
330
312
|
logger?.warn?.(`[PD:EvolutionWorker] Skipping malformed queue item: ${errors.join(', ')} | ${JSON.stringify(item).slice(0, 200)}`);
|
|
331
313
|
SystemLogger.log(wctx.workspaceDir, 'QUEUE_ITEM_MALFORMED', `Skipped: ${errors.join(', ')} | id=${item.id || 'N/A'}`);
|
|
@@ -361,16 +343,11 @@ export async function registerEvolutionTaskSession(workspaceResolve, taskId, ses
|
|
|
361
343
|
return false;
|
|
362
344
|
const releaseLock = await requireQueueLock(queuePath, logger, 'registerEvolutionTaskSession');
|
|
363
345
|
try {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
logger?.warn?.(`[PD:EvolutionWorker] Failed to parse EVOLUTION_QUEUE for session registration: ${queuePath} - ${parseErr instanceof Error ? parseErr.message : String(parseErr)}`);
|
|
370
|
-
return false;
|
|
371
|
-
}
|
|
372
|
-
// V2: Migrate queue to current schema
|
|
373
|
-
const queue = migrateQueueToV2(rawQueue);
|
|
346
|
+
// loadEvolutionQueue handles JSON parse + migrate + validate (rc-1/rc-4).
|
|
347
|
+
// Previously this inlined JSON.parse + migrate cast without validating,
|
|
348
|
+
// so malformed queue items could reach the find() below. Reusing the
|
|
349
|
+
// shared loader keeps a single chokepoint for queue validation.
|
|
350
|
+
const queue = loadEvolutionQueue(queuePath);
|
|
374
351
|
const task = queue.find((item) => item.id === taskId && item.status === 'in_progress');
|
|
375
352
|
if (!task) {
|
|
376
353
|
logger?.warn?.(`[PD:EvolutionWorker] Could not find in-progress evolution task ${taskId} for session assignment`);
|
|
@@ -406,7 +383,11 @@ async function processEvolutionQueueWithResult(wctx, logger, eventLog, api) {
|
|
|
406
383
|
if (!fs.existsSync(queuePath)) {
|
|
407
384
|
return { queue: queueResult, errors };
|
|
408
385
|
}
|
|
409
|
-
|
|
386
|
+
// rc-1/rc-2/rc-4 (ERR-001/ERR-005/ERR-007): use the canonical loader
|
|
387
|
+
// instead of raw JSON.parse + cast. loadEvolutionQueue handles parse,
|
|
388
|
+
// migrate, and element-wise validation; malformed items are dropped
|
|
389
|
+
// with a console.warn (rc-9: no silent fallback).
|
|
390
|
+
const queue = loadEvolutionQueue(queuePath);
|
|
410
391
|
// Purge stale failed tasks before processing (keeps queue lean)
|
|
411
392
|
const purgeResult = purgeStaleFailedTasks(queue, logger);
|
|
412
393
|
if (purgeResult.purged > 0) {
|
|
@@ -11,7 +11,7 @@ import { createHash } from 'crypto';
|
|
|
11
11
|
import { acquireLockAsync, releaseLock as releaseImportedLock } from '../utils/file-lock.js';
|
|
12
12
|
import { atomicWriteFileSync } from '../utils/io.js';
|
|
13
13
|
import { LockUnavailableError } from '../config/errors.js';
|
|
14
|
-
import { migrateQueueToV2 } from './queue-migration.js';
|
|
14
|
+
import { migrateQueueToV2, validateQueueItem } from './queue-migration.js';
|
|
15
15
|
export const EVOLUTION_QUEUE_LOCK_SUFFIX = '.lock';
|
|
16
16
|
export const LOCK_MAX_RETRIES = 50;
|
|
17
17
|
export const LOCK_RETRY_DELAY_MS = 50;
|
|
@@ -72,7 +72,19 @@ export function loadEvolutionQueue(queuePath) {
|
|
|
72
72
|
rawQueue = [];
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
-
|
|
75
|
+
// rc-1/rc-4 (ERR-007): every element of the parsed array is untrusted disk
|
|
76
|
+
// JSON — validate before returning. Malformed items are filtered out and
|
|
77
|
+
// reported via console.warn (rc-9: no silent drop). This is the single
|
|
78
|
+
// chokepoint that loadEvolutionQueue callers inherit.
|
|
79
|
+
const migrated = migrateQueueToV2(rawQueue);
|
|
80
|
+
return migrated.filter((item) => {
|
|
81
|
+
const errors = validateQueueItem(item);
|
|
82
|
+
if (errors.length > 0) {
|
|
83
|
+
console.warn(`[queue-io] Dropping malformed queue item: ${errors.join(', ')} | id=${item?.id ?? 'N/A'}`);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
});
|
|
76
88
|
}
|
|
77
89
|
export function saveEvolutionQueue(queuePath, queue) {
|
|
78
90
|
atomicWriteFileSync(queuePath, JSON.stringify(queue, null, 2));
|
|
@@ -5,32 +5,8 @@
|
|
|
5
5
|
* to the V2 schema. Zero I/O, zero imports from evolution-worker.ts.
|
|
6
6
|
*/
|
|
7
7
|
import type { TaskKind, TaskPriority } from '../core/trajectory-types.js';
|
|
8
|
-
|
|
9
|
-
export type
|
|
10
|
-
export interface EvolutionQueueItem {
|
|
11
|
-
id: string;
|
|
12
|
-
taskKind: TaskKind;
|
|
13
|
-
priority: TaskPriority;
|
|
14
|
-
source: string;
|
|
15
|
-
traceId?: string;
|
|
16
|
-
task?: string;
|
|
17
|
-
score: number;
|
|
18
|
-
reason: string;
|
|
19
|
-
timestamp: string;
|
|
20
|
-
enqueued_at?: string;
|
|
21
|
-
started_at?: string;
|
|
22
|
-
completed_at?: string;
|
|
23
|
-
assigned_session_key?: string;
|
|
24
|
-
trigger_text_preview?: string;
|
|
25
|
-
status: QueueStatus;
|
|
26
|
-
resolution?: TaskResolution;
|
|
27
|
-
session_id?: string;
|
|
28
|
-
agent_id?: string;
|
|
29
|
-
retryCount: number;
|
|
30
|
-
maxRetries: number;
|
|
31
|
-
lastError?: string;
|
|
32
|
-
resultRef?: string;
|
|
33
|
-
}
|
|
8
|
+
import type { EvolutionQueueItem } from '../core/evolution-types.js';
|
|
9
|
+
export type { QueueStatus, TaskResolution, EvolutionQueueItem } from '../core/evolution-types.js';
|
|
34
10
|
/**
|
|
35
11
|
* Legacy queue item shape (pre-V2) for migration compatibility.
|
|
36
12
|
* These items lack taskKind, priority, retryCount, maxRetries, lastError fields.
|
|
@@ -81,3 +57,28 @@ export declare function isLegacyQueueItem(item: RawQueueItem): boolean;
|
|
|
81
57
|
* Returns a new array with all items migrated to V2 format.
|
|
82
58
|
*/
|
|
83
59
|
export declare function migrateQueueToV2(queue: RawQueueItem[]): EvolutionQueueItem[];
|
|
60
|
+
/**
|
|
61
|
+
* Canonical set of valid taskKind values. Single source of truth — mirrors
|
|
62
|
+
* TaskKind in trajectory-types.ts. Previously the inline validator in
|
|
63
|
+
* evolution-worker.ts hard-coded `['model_eval']`, which silently rejected
|
|
64
|
+
* three legitimate taskKind values (pain_diagnosis / sleep_reflection /
|
|
65
|
+
* keyword_optimization). Keep this in sync with TaskKind.
|
|
66
|
+
*/
|
|
67
|
+
export declare const VALID_TASK_KINDS: readonly TaskKind[];
|
|
68
|
+
/**
|
|
69
|
+
* Validate a single queue item loaded from untrusted disk JSON.
|
|
70
|
+
*
|
|
71
|
+
* Returns an array of human-readable error reasons; an empty array means the
|
|
72
|
+
* item is valid. Use {@link isValidQueueItem} for a type-guard form.
|
|
73
|
+
*
|
|
74
|
+
* This consolidates the inline validation that lived in processEvolutionQueue
|
|
75
|
+
* (evolution-worker.ts) so every queue load path (loadEvolutionQueue,
|
|
76
|
+
* registerEvolutionTaskSession) applies the same filter. rc-4 (ERR-007):
|
|
77
|
+
* array elements from parsed JSON must be element-wise validated.
|
|
78
|
+
*/
|
|
79
|
+
export declare function validateQueueItem(item: unknown): string[];
|
|
80
|
+
/**
|
|
81
|
+
* Type-guard convenience over {@link validateQueueItem}.
|
|
82
|
+
* Drops the reason strings (callers that need them should call validateQueueItem).
|
|
83
|
+
*/
|
|
84
|
+
export declare function isValidQueueItem(item: unknown): item is EvolutionQueueItem;
|
|
@@ -54,3 +54,60 @@ export function isLegacyQueueItem(item) {
|
|
|
54
54
|
export function migrateQueueToV2(queue) {
|
|
55
55
|
return queue.map(item => isLegacyQueueItem(item) ? migrateToV2(item) : item);
|
|
56
56
|
}
|
|
57
|
+
// ── Validation (rc-1/rc-2, rc-4: validate array elements from untrusted JSON) ─
|
|
58
|
+
/**
|
|
59
|
+
* Canonical set of valid taskKind values. Single source of truth — mirrors
|
|
60
|
+
* TaskKind in trajectory-types.ts. Previously the inline validator in
|
|
61
|
+
* evolution-worker.ts hard-coded `['model_eval']`, which silently rejected
|
|
62
|
+
* three legitimate taskKind values (pain_diagnosis / sleep_reflection /
|
|
63
|
+
* keyword_optimization). Keep this in sync with TaskKind.
|
|
64
|
+
*/
|
|
65
|
+
export const VALID_TASK_KINDS = [
|
|
66
|
+
'pain_diagnosis',
|
|
67
|
+
'sleep_reflection',
|
|
68
|
+
'model_eval',
|
|
69
|
+
'keyword_optimization',
|
|
70
|
+
];
|
|
71
|
+
/**
|
|
72
|
+
* Validate a single queue item loaded from untrusted disk JSON.
|
|
73
|
+
*
|
|
74
|
+
* Returns an array of human-readable error reasons; an empty array means the
|
|
75
|
+
* item is valid. Use {@link isValidQueueItem} for a type-guard form.
|
|
76
|
+
*
|
|
77
|
+
* This consolidates the inline validation that lived in processEvolutionQueue
|
|
78
|
+
* (evolution-worker.ts) so every queue load path (loadEvolutionQueue,
|
|
79
|
+
* registerEvolutionTaskSession) applies the same filter. rc-4 (ERR-007):
|
|
80
|
+
* array elements from parsed JSON must be element-wise validated.
|
|
81
|
+
*/
|
|
82
|
+
export function validateQueueItem(item) {
|
|
83
|
+
const errors = [];
|
|
84
|
+
if (item === null || typeof item !== 'object' || Array.isArray(item)) {
|
|
85
|
+
return ['item is not an object'];
|
|
86
|
+
}
|
|
87
|
+
const it = item;
|
|
88
|
+
if (!it.id || typeof it.id !== 'string')
|
|
89
|
+
errors.push('missing/invalid id');
|
|
90
|
+
if (!it.source || typeof it.source !== 'string')
|
|
91
|
+
errors.push('missing/invalid source');
|
|
92
|
+
if (typeof it.score !== 'number')
|
|
93
|
+
errors.push('missing/invalid score');
|
|
94
|
+
if (!it.status || typeof it.status !== 'string')
|
|
95
|
+
errors.push('missing/invalid status');
|
|
96
|
+
if (!it.taskKind || typeof it.taskKind !== 'string')
|
|
97
|
+
errors.push('missing/invalid taskKind');
|
|
98
|
+
else if (!VALID_TASK_KINDS.includes(it.taskKind)) {
|
|
99
|
+
errors.push(`invalid taskKind value '${it.taskKind}' (expected one of: ${VALID_TASK_KINDS.join(', ')})`);
|
|
100
|
+
}
|
|
101
|
+
if (typeof it.retryCount !== 'number')
|
|
102
|
+
errors.push('missing/invalid retryCount');
|
|
103
|
+
if (typeof it.maxRetries !== 'number')
|
|
104
|
+
errors.push('missing/invalid maxRetries');
|
|
105
|
+
return errors;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Type-guard convenience over {@link validateQueueItem}.
|
|
109
|
+
* Drops the reason strings (callers that need them should call validateQueueItem).
|
|
110
|
+
*/
|
|
111
|
+
export function isValidQueueItem(item) {
|
|
112
|
+
return validateQueueItem(item).length === 0;
|
|
113
|
+
}
|
|
@@ -200,6 +200,6 @@ When completing any coding task (via AI coding assistant or direct):
|
|
|
200
200
|
Use this to avoid confusing peer agents with Principles internal workers:
|
|
201
201
|
|
|
202
202
|
- **Peer agents / peer sessions**: `agents_list`, `sessions_list`, `sessions_send`, `sessions_spawn`
|
|
203
|
-
- **Internal workers** (for example `
|
|
203
|
+
- **Internal workers** (for example `explorer`): use `sessions_spawn with pd-explorer skill`
|
|
204
204
|
- **Inspect internal workers**: use `subagents`
|
|
205
|
-
- **Do not** treat `
|
|
205
|
+
- **Do not** treat `explorer` as a peer session target
|
|
@@ -10,6 +10,6 @@
|
|
|
10
10
|
## 4. Agent Routing Clarification
|
|
11
11
|
|
|
12
12
|
- `agents_list`, `sessions_list`, `sessions_send`, and `sessions_spawn` are for peer agents and peer sessions
|
|
13
|
-
- Use `sessions_spawn` with `pd-
|
|
13
|
+
- Use `sessions_spawn` with `pd-explorer` skill to start Principles internal workers (e.g., explorer)
|
|
14
14
|
- `subagents` inspects already-started internal workers and their outputs
|
|
15
15
|
- Do not use peer-session tools to pretend an internal worker is a peer agent
|
|
@@ -192,6 +192,6 @@ _This folder is home. Treat it that way._
|
|
|
192
192
|
用下面这几条避免把同级代理和 Principles 内部 worker 混淆:
|
|
193
193
|
|
|
194
194
|
- **同级代理 / 同级会话**:`agents_list`、`sessions_list`、`sessions_send`、`sessions_spawn`
|
|
195
|
-
- **内部 worker**(例如 `
|
|
195
|
+
- **内部 worker**(例如 `explorer`):使用 `sessions_spawn` 配合 `pd-explorer` skill 启动
|
|
196
196
|
- **查询内部 worker**:使用 `subagents`
|
|
197
|
-
- **不要**把 `
|
|
197
|
+
- **不要**把 `explorer` 当成同级 peer session 目标
|
|
@@ -10,6 +10,6 @@
|
|
|
10
10
|
## 4. 智能体路由澄清
|
|
11
11
|
|
|
12
12
|
- `agents_list`、`sessions_list`、`sessions_send`、`sessions_spawn` 用于同级代理和同级会话
|
|
13
|
-
- 使用 `sessions_spawn` 配合 `pd-
|
|
13
|
+
- 使用 `sessions_spawn` 配合 `pd-explorer` skill 启动 Principles 内部 worker,例如 `explorer`
|
|
14
14
|
- `subagents` 用于查看已启动内部 worker 的状态和输出
|
|
15
15
|
- 不要用同级会话工具把内部 worker 伪装成同级代理
|
|
@@ -200,6 +200,6 @@ When completing any coding task (via AI coding assistant or direct):
|
|
|
200
200
|
Use this to avoid confusing peer agents with Principles internal workers:
|
|
201
201
|
|
|
202
202
|
- **Peer agents / peer sessions**: `agents_list`, `sessions_list`, `sessions_send`, `sessions_spawn`
|
|
203
|
-
- **Internal workers** (for example `
|
|
203
|
+
- **Internal workers** (for example `explorer`): use `sessions_spawn with pd-explorer skill`
|
|
204
204
|
- **Inspect internal workers**: use `subagents`
|
|
205
|
-
- **Do not** treat `
|
|
205
|
+
- **Do not** treat `explorer` as a peer session target
|
|
@@ -53,6 +53,6 @@ Tool returns: Blind Spots → Risk Warnings → Alternative Approaches → Recom
|
|
|
53
53
|
## 4. Agent Routing Clarification
|
|
54
54
|
|
|
55
55
|
- `agents_list`, `sessions_list`, `sessions_send`, and `sessions_spawn` are for peer agents and peer sessions
|
|
56
|
-
- Use `sessions_spawn` with `pd-
|
|
56
|
+
- Use `sessions_spawn` with `pd-explorer` skill to start Principles internal workers (e.g., explorer)
|
|
57
57
|
- `subagents` inspects already-started internal workers and their outputs
|
|
58
58
|
- Do not use peer-session tools to pretend an internal worker is a peer agent
|
|
@@ -192,6 +192,6 @@ _This folder is home. Treat it that way._
|
|
|
192
192
|
用下面这几条避免把同级代理和 Principles 内部 worker 混淆:
|
|
193
193
|
|
|
194
194
|
- **同级代理 / 同级会话**:`agents_list`、`sessions_list`、`sessions_send`、`sessions_spawn`
|
|
195
|
-
- **内部 worker**(例如 `
|
|
195
|
+
- **内部 worker**(例如 `explorer`):使用 `sessions_spawn` 配合 `pd-explorer` skill 启动
|
|
196
196
|
- **查询内部 worker**:使用 `subagents`
|
|
197
|
-
- **不要**把 `
|
|
197
|
+
- **不要**把 `explorer` 当成同级 peer session 目标
|
|
@@ -53,6 +53,6 @@ deep_reflect(
|
|
|
53
53
|
## 4. 智能体路由澄清
|
|
54
54
|
|
|
55
55
|
- `agents_list`、`sessions_list`、`sessions_send`、`sessions_spawn` 用于同级代理和同级会话
|
|
56
|
-
- 使用 `sessions_spawn` 配合 `pd-
|
|
56
|
+
- 使用 `sessions_spawn` 配合 `pd-explorer` skill 启动 Principles 内部 worker,例如 `explorer`
|
|
57
57
|
- `subagents` 用于查看已启动内部 worker 的状态和输出
|
|
58
58
|
- 不要用同级会话工具把内部 worker 伪装成同级代理
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Diagnostician 协议常量
|
|
3
|
-
*
|
|
4
|
-
* 用于 Runtime V2 diagnostician prompt
|
|
5
|
-
* 与 templates/langs/zh/skills/pd-diagnostician/SKILL.md 保持一致
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* 诊断协议摘要(用于 HEARTBEAT.md 注入)
|
|
9
|
-
*
|
|
10
|
-
* 完整版见: templates/langs/zh/skills/pd-diagnostician/SKILL.md
|
|
11
|
-
*/
|
|
12
|
-
export declare const DIAGNOSTICIAN_PROTOCOL_SUMMARY = "## Diagnostic Protocol (5 Whys)\n\n**Phase 1 - Evidence Gathering**:\n- Use the task input/context provided by Runtime V2 for pain context\n- Read .state/logs/events.jsonl recent entries\n- Search codebase for error patterns from Reason field\n- Record all evidence sources (file:line)\n\n**Phase 2 - Causal Chain (5 Whys)**:\n- Why 1: Surface symptom (what you can see)\n- Why 2: Direct cause (what triggered it)\n- Why 3: Process gap (what should have caught it)\n- Why 4: Design flaw (why the gap exists)\n- Why 5: Root cause (systemic issue to fix)\n- Each Why MUST have evidence. Stop at actionable root cause.\n\n**Phase 3 - Root Cause Classification**:\nClassify into one of:\n- **People**: Human error, missing knowledge, communication gap\n- **Design**: Architecture flaw, missing validation, poor abstraction\n- **Assumption**: Invalid assumption, outdated context, edge case\n- **Tooling**: Tool limitation, environment issue, dependency problem\n- For Design: analyze why existing hooks/rules didn't catch this\n\n**Phase 4 - Principle Extraction**:\nExtract a HIGHLY ABSTRACTED principle (not an operational rule):\n- **trigger_pattern**: regex/keywords for when this occurs\n- **action**: what to do differently\n- **abstracted_principle**: ONE sentence, max 40 chars, cross-scenario applicable\n - \u274C Bad: \"\u5199\u5165\u524D\u68C0\u67E5\u76EE\u5F55\u662F\u5426\u5B58\u5728\" (too specific)\n - \u2705 Good: \"\u4EFB\u4F55\u5199\u5165\u64CD\u4F5C\u5FC5\u987B\u786E\u4FDD\u76EE\u6807\u73AF\u5883\u7684\u5B8C\u6574\u6027\" (abstract, reusable)\n\n**Output Format** (single JSON object):\n```json\n{\n \"diagnosis_report\": {\n \"task_id\": \"pain-xxx\",\n \"summary\": \"One-line root cause\",\n \"causal_chain\": [\n { \"why\": 1, \"answer\": \"...\", \"evidence\": \"file:line\" }\n ],\n \"root_cause\": {\n \"category\": \"Design|People|Assumption|Tooling\",\n \"description\": \"...\"\n },\n \"principle\": {\n \"trigger_pattern\": \"...\",\n \"action\": \"...\",\n \"abstracted_principle\": \"\u9AD8\u5EA6\u62BD\u8C61\u7684\u4E00\u53E5\u8BDD\u539F\u5219\"\n }\n }\n}\n```\n";
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Diagnostician 协议常量
|
|
3
|
-
*
|
|
4
|
-
* 用于 Runtime V2 diagnostician prompt
|
|
5
|
-
* 与 templates/langs/zh/skills/pd-diagnostician/SKILL.md 保持一致
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* 诊断协议摘要(用于 HEARTBEAT.md 注入)
|
|
9
|
-
*
|
|
10
|
-
* 完整版见: templates/langs/zh/skills/pd-diagnostician/SKILL.md
|
|
11
|
-
*/
|
|
12
|
-
export const DIAGNOSTICIAN_PROTOCOL_SUMMARY = `## Diagnostic Protocol (5 Whys)
|
|
13
|
-
|
|
14
|
-
**Phase 1 - Evidence Gathering**:
|
|
15
|
-
- Use the task input/context provided by Runtime V2 for pain context
|
|
16
|
-
- Read .state/logs/events.jsonl recent entries
|
|
17
|
-
- Search codebase for error patterns from Reason field
|
|
18
|
-
- Record all evidence sources (file:line)
|
|
19
|
-
|
|
20
|
-
**Phase 2 - Causal Chain (5 Whys)**:
|
|
21
|
-
- Why 1: Surface symptom (what you can see)
|
|
22
|
-
- Why 2: Direct cause (what triggered it)
|
|
23
|
-
- Why 3: Process gap (what should have caught it)
|
|
24
|
-
- Why 4: Design flaw (why the gap exists)
|
|
25
|
-
- Why 5: Root cause (systemic issue to fix)
|
|
26
|
-
- Each Why MUST have evidence. Stop at actionable root cause.
|
|
27
|
-
|
|
28
|
-
**Phase 3 - Root Cause Classification**:
|
|
29
|
-
Classify into one of:
|
|
30
|
-
- **People**: Human error, missing knowledge, communication gap
|
|
31
|
-
- **Design**: Architecture flaw, missing validation, poor abstraction
|
|
32
|
-
- **Assumption**: Invalid assumption, outdated context, edge case
|
|
33
|
-
- **Tooling**: Tool limitation, environment issue, dependency problem
|
|
34
|
-
- For Design: analyze why existing hooks/rules didn't catch this
|
|
35
|
-
|
|
36
|
-
**Phase 4 - Principle Extraction**:
|
|
37
|
-
Extract a HIGHLY ABSTRACTED principle (not an operational rule):
|
|
38
|
-
- **trigger_pattern**: regex/keywords for when this occurs
|
|
39
|
-
- **action**: what to do differently
|
|
40
|
-
- **abstracted_principle**: ONE sentence, max 40 chars, cross-scenario applicable
|
|
41
|
-
- ❌ Bad: "写入前检查目录是否存在" (too specific)
|
|
42
|
-
- ✅ Good: "任何写入操作必须确保目标环境的完整性" (abstract, reusable)
|
|
43
|
-
|
|
44
|
-
**Output Format** (single JSON object):
|
|
45
|
-
\`\`\`json
|
|
46
|
-
{
|
|
47
|
-
"diagnosis_report": {
|
|
48
|
-
"task_id": "pain-xxx",
|
|
49
|
-
"summary": "One-line root cause",
|
|
50
|
-
"causal_chain": [
|
|
51
|
-
{ "why": 1, "answer": "...", "evidence": "file:line" }
|
|
52
|
-
],
|
|
53
|
-
"root_cause": {
|
|
54
|
-
"category": "Design|People|Assumption|Tooling",
|
|
55
|
-
"description": "..."
|
|
56
|
-
},
|
|
57
|
-
"principle": {
|
|
58
|
-
"trigger_pattern": "...",
|
|
59
|
-
"action": "...",
|
|
60
|
-
"abstracted_principle": "高度抽象的一句话原则"
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
\`\`\`
|
|
65
|
-
`;
|