principles-disciple 1.72.0 → 1.73.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/openclaw.plugin.json +10 -5
- package/package.json +17 -19
- package/scripts/acceptance-test.mjs +16 -73
- package/scripts/sync-plugin.mjs +382 -77
- package/src/commands/archive-impl.ts +2 -1
- package/src/commands/capabilities.ts +2 -2
- package/src/commands/context.ts +2 -2
- package/src/commands/disable-impl.ts +2 -1
- package/src/commands/evolution-status.ts +16 -16
- package/src/commands/export.ts +12 -67
- package/src/commands/pain.ts +91 -1
- package/src/commands/principle-rollback.ts +2 -1
- package/src/commands/promote-impl.ts +7 -43
- package/src/commands/rollback-impl.ts +2 -1
- package/src/commands/rollback.ts +2 -1
- package/src/commands/samples.ts +2 -1
- package/src/commands/thinking-os.ts +2 -1
- package/src/config/errors.ts +18 -2
- package/src/constants/diagnostician.ts +2 -2
- package/src/constants/tools.ts +2 -1
- package/src/core/__tests__/focus-history.test.ts +210 -0
- package/src/core/config.ts +1 -1
- package/src/core/confirm-first-gate.ts +255 -0
- package/src/core/correction-cue-learner.ts +2 -136
- package/src/core/correction-types.ts +16 -88
- package/src/core/dictionary.ts +19 -20
- package/src/core/empathy-keyword-matcher.ts +17 -289
- package/src/core/empathy-types.ts +18 -229
- package/src/core/event-log.ts +38 -132
- package/src/core/evolution-reducer.ts +21 -2
- package/src/core/evolution-types.ts +76 -464
- package/src/core/file-store.ts +80 -0
- package/src/core/focus-history.ts +228 -955
- package/src/core/local-worker-routing.ts +34 -314
- package/src/core/merge-gate-audit.ts +0 -195
- package/src/core/pain-diagnostic-gate.ts +154 -0
- package/src/core/pain-signal.ts +21 -138
- package/src/core/pain.ts +15 -88
- package/src/core/pd-task-reconciler.ts +26 -115
- package/src/core/pd-task-service.ts +9 -9
- package/src/core/pd-task-types.ts +23 -127
- package/src/core/principle-compiler/__tests__/compiler-replay-gate.test.ts +174 -0
- package/src/core/principle-compiler/code-validator.ts +15 -42
- package/src/core/principle-compiler/compiler.ts +100 -15
- package/src/core/principle-compiler/index.ts +5 -2
- package/src/core/principle-compiler/template-generator.ts +4 -104
- package/src/core/principle-injection.ts +10 -202
- package/src/core/principle-internalization/filesystem-lifecycle-datasource.ts +42 -0
- package/src/core/principle-internalization/lifecycle-read-model.ts +39 -242
- package/src/core/principle-internalization/principle-lifecycle-service.ts +12 -10
- package/src/core/principle-tree-ledger-adapter.ts +145 -0
- package/src/core/principle-tree-ledger.ts +8 -6
- package/src/core/reflection/reflection-context.ts +14 -109
- package/src/core/replay-engine.ts +8 -500
- package/src/core/rule-host-helpers.ts +5 -35
- package/src/core/rule-host-types.ts +10 -82
- package/src/core/rule-host.ts +6 -63
- package/src/core/runtime-v2-prompt-activation-reader.ts +231 -0
- package/src/core/session-tracker.ts +87 -101
- package/src/core/shadow-observation-registry.ts +19 -48
- package/src/core/trajectory.ts +3 -1
- package/src/core/workflow-funnel-loader.ts +62 -68
- package/src/core/workspace-context.ts +46 -0
- package/src/core/workspace-dir-service.ts +1 -1
- package/src/core/workspace-dir-validation.ts +18 -9
- package/src/hooks/AGENTS.md +1 -1
- package/src/hooks/gate-block-helper.ts +46 -44
- package/src/hooks/gate.ts +207 -7
- package/src/hooks/lifecycle.ts +30 -32
- package/src/hooks/llm.ts +60 -32
- package/src/hooks/pain.ts +297 -103
- package/src/hooks/prompt.ts +459 -439
- package/src/hooks/subagent.ts +2 -29
- package/src/i18n/commands.ts +2 -10
- package/src/index.ts +95 -85
- package/src/openclaw-sdk.ts +311 -0
- package/src/service/central-database.ts +8 -4
- package/src/service/evolution-queue-migration.ts +2 -1
- package/src/service/evolution-worker.ts +163 -1786
- package/src/service/internalization-trigger-adapter.ts +302 -0
- package/src/service/keyword-optimization-service.ts +4 -4
- package/src/service/monitoring-query-service.ts +1 -215
- package/src/service/queue-io.ts +60 -331
- package/src/service/runtime-summary-service.ts +59 -16
- package/src/service/subagent-workflow/index.ts +0 -41
- package/src/service/subagent-workflow/types.ts +9 -120
- package/src/service/subagent-workflow/workflow-store.ts +2 -119
- package/src/service/workflow-watchdog.ts +0 -43
- package/src/types/event-payload.ts +16 -74
- package/src/types/event-types.ts +39 -547
- package/src/types/hygiene-types.ts +7 -30
- package/src/types/principle-tree-schema.ts +20 -222
- package/src/types/queue.ts +15 -70
- package/src/types/runtime-summary.ts +5 -49
- package/src/utils/io.ts +10 -0
- package/src/utils/retry.ts +1 -1
- package/src/utils/shadow-fingerprint.ts +2 -2
- package/src/utils/workspace-resolver.ts +50 -0
- package/templates/langs/en/core/AGENTS.md +2 -2
- package/templates/langs/en/core/BOOT.md +1 -1
- package/templates/langs/en/core/HEARTBEAT.md +2 -2
- package/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
- package/templates/langs/en/skills/ai-sprint-orchestration/runtime/.gitignore +2 -2
- package/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
- package/templates/langs/en/skills/evolve-task/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-cli-operator/SKILL.md +67 -0
- package/templates/langs/en/skills/pd-diagnostician/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-mentor/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-pain-signal/SKILL.md +17 -39
- package/templates/langs/en/skills/pd-runtime-v2/SKILL.md +61 -0
- package/templates/langs/zh/core/AGENTS.md +2 -2
- package/templates/langs/zh/core/BOOT.md +1 -1
- package/templates/langs/zh/core/HEARTBEAT.md +2 -2
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +8 -8
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
- package/templates/langs/zh/skills/ai-sprint-orchestration/runtime/.gitignore +2 -2
- package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
- package/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +21 -5
- package/templates/langs/zh/skills/evolve-task/SKILL.md +2 -2
- package/templates/langs/zh/skills/pd-cli-operator/SKILL.md +67 -0
- package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +1 -1
- package/templates/langs/zh/skills/pd-mentor/SKILL.md +1 -1
- package/templates/langs/zh/skills/pd-pain-signal/SKILL.md +17 -38
- package/templates/langs/zh/skills/pd-runtime-v2/SKILL.md +61 -0
- package/tests/build-artifacts.test.ts +1 -3
- package/tests/commands/evolution-status.test.ts +0 -118
- package/tests/core/bootstrap-rules.test.ts +1 -1
- package/tests/core/config.test.ts +1 -1
- package/tests/core/event-log.test.ts +35 -0
- package/tests/core/evolution-engine.test.ts +610 -0
- package/tests/core/file-store.test.ts +102 -0
- package/tests/core/focus-history.test.ts +203 -11
- package/tests/core/merge-gate-audit.test.ts +2 -169
- package/tests/core/model-deployment-registry.test.ts +7 -1
- package/tests/core/model-training-registry.test.ts +19 -0
- package/tests/core/observability.test.ts +0 -1
- package/tests/core/pain-diagnostic-gate.test.ts +498 -0
- package/tests/core/pain.test.ts +0 -1
- package/tests/core/principle-internalization/deprecated-readiness.test.ts +2 -2
- package/tests/core/principle-internalization/lifecycle-metrics.test.ts +2 -2
- package/tests/core/principle-internalization/{internalization-routing-policy.test.ts → lifecycle-routing-policy.test.ts} +6 -6
- package/tests/core/principle-internalization/lineage-source-retired.test.ts +56 -0
- package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +1 -23
- package/tests/core/principle-tree-ledger-adapter.test.ts +253 -0
- package/tests/core/reflection-context.test.ts +0 -14
- package/tests/core/replay-engine.test.ts +127 -215
- package/tests/core/rule-host-helpers.test.ts +2 -2
- package/tests/core/rule-implementation-runtime.test.ts +0 -27
- package/tests/core/workflow-funnel-loader.test.ts +162 -0
- package/tests/core/workspace-dir-validation.test.ts +8 -1
- package/tests/core-anti-growth.test.ts +192 -0
- package/tests/hook-workspace-nextaction-contract.test.ts +42 -0
- package/tests/hooks/confirm-first-gate.test.ts +333 -0
- package/tests/hooks/gate-auto-correct-shadow.test.ts +310 -0
- package/tests/hooks/gate-auto-correct.test.ts +665 -0
- package/tests/hooks/gate-rule-host-pipeline.test.ts +2 -1
- package/tests/hooks/pain.test.ts +269 -12
- package/tests/hooks/prompt-characterization.test.ts +500 -0
- package/tests/hooks/prompt-size-guard.test.ts +32 -17
- package/tests/hooks/runtime-v2-prompt-activation.test.ts +869 -0
- package/tests/index.test.ts +94 -1
- package/tests/integration/auto-entry-gate.test.ts +248 -0
- package/tests/integration/internalization-trigger-guard.test.ts +69 -0
- package/tests/integration/m8-legacy-paths.test.ts +63 -0
- package/tests/integration/runtime-v2-pain-guard.test.ts +125 -0
- package/tests/plugin-config-resolution-cutover.test.ts +359 -0
- package/tests/runtime-v2-discovery-guard.test.ts +154 -0
- package/tests/service/central-database.test.ts +457 -0
- package/tests/service/evolution-worker.correction-observer.test.ts +173 -0
- package/tests/service/evolution-worker.timeout.test.ts +11 -129
- package/tests/service/internalization-trigger-adapter.test.ts +251 -0
- package/tests/service/monitoring-query-service.test.ts +1 -47
- package/tests/service/queue-io.test.ts +1 -62
- package/tests/service/runtime-summary-service.test.ts +3 -1
- package/tests/service/workflow-watchdog.test.ts +0 -91
- package/tests/utils/file-lock.test.ts +5 -3
- package/tests/utils/session-key.test.ts +52 -0
- package/tests/utils/subagent-probe.test.ts +48 -1
- package/vitest.config.ts +4 -11
- package/.planning/codebase/ARCHITECTURE.md +0 -157
- package/.planning/codebase/CONCERNS.md +0 -145
- package/.planning/codebase/CONVENTIONS.md +0 -148
- package/.planning/codebase/INTEGRATIONS.md +0 -81
- package/.planning/codebase/STACK.md +0 -87
- package/.planning/codebase/STRUCTURE.md +0 -193
- package/.planning/codebase/TESTING.md +0 -243
- package/.planning/phases/01-basic-visualization/01-GAP-CLOSURE-VERIFICATION.md +0 -113
- package/docs/COMMAND_REFERENCE.md +0 -76
- package/docs/COMMAND_REFERENCE_EN.md +0 -79
- package/scripts/build-web.mjs +0 -46
- package/scripts/diagnose-nocturnal.mjs +0 -537
- package/scripts/seed-nocturnal-scenarios.mjs +0 -384
- package/src/commands/nocturnal-review.ts +0 -322
- package/src/commands/nocturnal-rollout.ts +0 -790
- package/src/commands/nocturnal-train.ts +0 -986
- package/src/commands/pd-reflect.ts +0 -88
- package/src/core/adaptive-thresholds.ts +0 -478
- package/src/core/diagnostician-task-store.ts +0 -192
- package/src/core/nocturnal-arbiter.ts +0 -715
- package/src/core/nocturnal-artifact-lineage.ts +0 -116
- package/src/core/nocturnal-artificer.ts +0 -257
- package/src/core/nocturnal-candidate-scoring.ts +0 -530
- package/src/core/nocturnal-compliance.ts +0 -1146
- package/src/core/nocturnal-dataset.ts +0 -763
- package/src/core/nocturnal-executability.ts +0 -428
- package/src/core/nocturnal-export.ts +0 -499
- package/src/core/nocturnal-paths.ts +0 -240
- package/src/core/nocturnal-reasoning-deriver.ts +0 -343
- package/src/core/nocturnal-rule-implementation-validator.ts +0 -246
- package/src/core/nocturnal-snapshot-contract.ts +0 -99
- package/src/core/nocturnal-trajectory-extractor.ts +0 -512
- package/src/core/nocturnal-trinity-types.ts +0 -218
- package/src/core/nocturnal-trinity.ts +0 -2680
- package/src/core/principle-internalization/deprecated-readiness.ts +0 -93
- package/src/core/principle-internalization/internalization-routing-policy.ts +0 -208
- package/src/core/principle-internalization/lifecycle-metrics.ts +0 -152
- package/src/http/principles-console-route.ts +0 -709
- package/src/service/central-health-service.ts +0 -49
- package/src/service/central-overview-service.ts +0 -138
- package/src/service/control-ui-query-service.ts +0 -900
- package/src/service/cooldown-strategy.ts +0 -97
- package/src/service/evolution-pain-context.ts +0 -79
- package/src/service/evolution-query-service.ts +0 -407
- package/src/service/health-query-service.ts +0 -1038
- package/src/service/nocturnal-config.ts +0 -214
- package/src/service/nocturnal-runtime.ts +0 -734
- package/src/service/nocturnal-service.ts +0 -1605
- package/src/service/nocturnal-target-selector.ts +0 -545
- package/src/service/sleep-cycle.ts +0 -157
- package/src/service/startup-reconciler.ts +0 -112
- package/src/service/subagent-workflow/correction-observer-types.ts +0 -82
- package/src/service/subagent-workflow/correction-observer-workflow-manager.ts +0 -250
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +0 -1
- package/src/service/subagent-workflow/dynamic-timeout.ts +0 -30
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +0 -268
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +0 -795
- package/src/service/subagent-workflow/runtime-direct-driver.ts +0 -268
- package/src/service/subagent-workflow/workflow-manager-base.ts +0 -580
- package/src/tools/write-pain-flag.ts +0 -215
- package/tests/commands/nocturnal-review.test.ts +0 -448
- package/tests/commands/nocturnal-train.test.ts +0 -97
- package/tests/commands/pd-reflect.test.ts +0 -49
- package/tests/core/adaptive-thresholds.test.ts +0 -261
- package/tests/core/nocturnal-arbiter.test.ts +0 -559
- package/tests/core/nocturnal-artifact-lineage.test.ts +0 -53
- package/tests/core/nocturnal-artificer.test.ts +0 -241
- package/tests/core/nocturnal-candidate-scoring.test.ts +0 -532
- package/tests/core/nocturnal-compliance-p-principles.test.ts +0 -133
- package/tests/core/nocturnal-compliance.test.ts +0 -646
- package/tests/core/nocturnal-dataset.test.ts +0 -892
- package/tests/core/nocturnal-e2e.test.ts +0 -234
- package/tests/core/nocturnal-executability.test.ts +0 -357
- package/tests/core/nocturnal-export.test.ts +0 -517
- package/tests/core/nocturnal-reasoning-deriver.test.ts +0 -372
- package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +0 -428
- package/tests/core/nocturnal-rule-implementation-validator.test.ts +0 -127
- package/tests/core/nocturnal-snapshot-contract.test.ts +0 -121
- package/tests/core/nocturnal-trajectory-extractor.test.ts +0 -634
- package/tests/core/nocturnal-trinity.test.ts +0 -2053
- package/tests/core/pain-auto-repair.test.ts +0 -96
- package/tests/core/pain-integration.test.ts +0 -510
- package/tests/fixtures/nocturnal-reviewed-subset.json +0 -183
- package/tests/http/principles-console-route.test.ts +0 -162
- package/tests/integration/chaos-resilience.test.ts +0 -348
- package/tests/integration/empathy-workflow-integration.test.ts +0 -626
- package/tests/integration/pain-diagnostician-loop.e2e.test.ts +0 -380
- package/tests/service/control-ui-query-service.test.ts +0 -121
- package/tests/service/cooldown-strategy.test.ts +0 -164
- package/tests/service/data-endpoints-regression.test.ts +0 -834
- package/tests/service/empathy-observer-workflow-manager.test.ts +0 -175
- package/tests/service/evolution-worker.nocturnal.test.ts +0 -601
- package/tests/service/nocturnal-runtime-hardening.test.ts +0 -118
- package/tests/service/nocturnal-runtime.test.ts +0 -473
- package/tests/service/nocturnal-service-code-candidate.test.ts +0 -330
- package/tests/service/nocturnal-target-selector.test.ts +0 -615
- package/tests/service/startup-reconciler.test.ts +0 -148
- package/tests/tools/write-pain-flag.test.ts +0 -358
- package/ui/src/App.tsx +0 -45
- package/ui/src/api.ts +0 -220
- package/ui/src/charts.tsx +0 -955
- package/ui/src/components/ErrorState.tsx +0 -6
- package/ui/src/components/Loading.tsx +0 -13
- package/ui/src/components/ProtectedRoute.tsx +0 -12
- package/ui/src/components/Shell.tsx +0 -91
- package/ui/src/components/WorkspaceConfig.tsx +0 -178
- package/ui/src/components/index.ts +0 -5
- package/ui/src/context/auth.tsx +0 -80
- package/ui/src/context/theme.tsx +0 -66
- package/ui/src/hooks/useAutoRefresh.ts +0 -39
- package/ui/src/i18n/ui.ts +0 -473
- package/ui/src/main.tsx +0 -16
- package/ui/src/pages/EvolutionPage.tsx +0 -333
- package/ui/src/pages/FeedbackPage.tsx +0 -138
- package/ui/src/pages/GateMonitorPage.tsx +0 -136
- package/ui/src/pages/LoginPage.tsx +0 -89
- package/ui/src/pages/OverviewPage.tsx +0 -599
- package/ui/src/pages/SamplesPage.tsx +0 -174
- package/ui/src/pages/ThinkingModelsPage.tsx +0 -702
- package/ui/src/styles.css +0 -2020
- package/ui/src/types.ts +0 -384
- package/ui/src/utils/format.ts +0 -15
package/src/core/event-log.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import type {
|
|
@@ -17,36 +17,23 @@ import type {
|
|
|
17
17
|
PlanApprovalEventData,
|
|
18
18
|
EvolutionTaskEventData,
|
|
19
19
|
EmpathyRollbackEventData,
|
|
20
|
-
// C: New event data types
|
|
21
20
|
DiagnosisTaskEventData,
|
|
22
21
|
HeartbeatDiagnosisEventData,
|
|
23
22
|
DiagnosticianReportEventData,
|
|
24
23
|
PrincipleCandidateEventData,
|
|
25
24
|
RuleEnforcedEventData,
|
|
26
|
-
// C: Nocturnal funnel events (PD-FUNNEL-2.3)
|
|
27
|
-
NocturnalDreamerCompletedEventData,
|
|
28
|
-
NocturnalArtifactPersistedEventData,
|
|
29
|
-
NocturnalCodeCandidateCreatedEventData,
|
|
30
|
-
// C: RuleHost funnel events (PD-FUNNEL-2.4)
|
|
31
25
|
RuleHostEvaluatedEventData,
|
|
32
26
|
RuleHostBlockedEventData,
|
|
33
27
|
RuleHostRequireApprovalEventData,
|
|
28
|
+
RuleHostAutoCorrectProposedEventData,
|
|
29
|
+
RuleHostAutoCorrectAppliedEventData,
|
|
30
|
+
RuntimeV2PromptActivationsInjectedEventData,
|
|
31
|
+
RuntimeV2ConfirmFirstGateEventData,
|
|
34
32
|
} from '../types/event-types.js';
|
|
35
33
|
import { createEmptyDailyStats } from '../types/event-types.js';
|
|
36
34
|
import { atomicWriteFileSync } from '../utils/io.js';
|
|
37
35
|
import type { PluginLogger } from '../openclaw-sdk.js';
|
|
38
36
|
|
|
39
|
-
/**
|
|
40
|
-
* EventLog - Structured event logging with daily statistics aggregation.
|
|
41
|
-
*
|
|
42
|
-
* Log files are date-stamped: events_YYYY-MM-DD.jsonl
|
|
43
|
-
* Old event files are automatically cleaned up based on retention policy.
|
|
44
|
-
*/
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Event log retention in days.
|
|
48
|
-
* Files older than this are deleted on cleanup.
|
|
49
|
-
*/
|
|
50
37
|
const EVENT_LOG_RETENTION_DAYS = 7;
|
|
51
38
|
|
|
52
39
|
export class EventLog {
|
|
@@ -60,11 +47,9 @@ export class EventLog {
|
|
|
60
47
|
private readonly flushIntervalMs = 30000;
|
|
61
48
|
private flushTimer?: ReturnType<typeof setInterval>;
|
|
62
49
|
|
|
63
|
-
// Cached event file path for current date
|
|
64
50
|
private currentEventsFile: string | undefined;
|
|
65
51
|
private currentDate: string | undefined;
|
|
66
52
|
|
|
67
|
-
// Pain score sum per date (for avgScore calculation)
|
|
68
53
|
private readonly painScoreSums: Map<string, number> = new Map();
|
|
69
54
|
|
|
70
55
|
constructor(stateDir: string, logger?: PluginLogger) {
|
|
@@ -80,37 +65,24 @@ export class EventLog {
|
|
|
80
65
|
this.startFlushTimer();
|
|
81
66
|
}
|
|
82
67
|
|
|
83
|
-
/**
|
|
84
|
-
* Get the event file path for a given date.
|
|
85
|
-
*/
|
|
86
68
|
private getEventsFile(date: string): string {
|
|
87
69
|
return path.join(this.logsDir, `events_${date}.jsonl`);
|
|
88
70
|
}
|
|
89
71
|
|
|
90
|
-
/**
|
|
91
|
-
* Get today's date string (YYYY-MM-DD).
|
|
92
|
-
*/
|
|
93
72
|
private getTodayStr(): string {
|
|
94
73
|
return new Date().toISOString().split('T')[0];
|
|
95
74
|
}
|
|
96
75
|
|
|
97
|
-
/**
|
|
98
|
-
* Ensure we have the correct events file for today's date.
|
|
99
|
-
*/
|
|
100
76
|
private ensureEventsFile(): string {
|
|
101
77
|
const today = this.getTodayStr();
|
|
102
78
|
if (this.currentDate !== today || !this.currentEventsFile) {
|
|
103
79
|
this.currentDate = today;
|
|
104
80
|
this.currentEventsFile = this.getEventsFile(today);
|
|
105
|
-
// Run cleanup if date changed
|
|
106
81
|
this.cleanupOldEventFiles(today);
|
|
107
82
|
}
|
|
108
83
|
return this.currentEventsFile;
|
|
109
84
|
}
|
|
110
85
|
|
|
111
|
-
/**
|
|
112
|
-
* Clean up event files older than EVENT_LOG_RETENTION_DAYS.
|
|
113
|
-
*/
|
|
114
86
|
private cleanupOldEventFiles(_today: string): void {
|
|
115
87
|
if (EVENT_LOG_RETENTION_DAYS <= 0) return;
|
|
116
88
|
|
|
@@ -133,7 +105,7 @@ export class EventLog {
|
|
|
133
105
|
}
|
|
134
106
|
|
|
135
107
|
recordToolCall(sessionId: string | undefined, data: ToolCallEventData): void {
|
|
136
|
-
const category = data.error ? 'failure' : 'success';
|
|
108
|
+
const category = data.error || (data.exitCode !== undefined && data.exitCode !== 0) ? 'failure' : 'success';
|
|
137
109
|
this.record('tool_call', category, sessionId, data);
|
|
138
110
|
}
|
|
139
111
|
|
|
@@ -189,7 +161,6 @@ export class EventLog {
|
|
|
189
161
|
this.record('warn', 'failure', sessionId, { message, ...context });
|
|
190
162
|
}
|
|
191
163
|
|
|
192
|
-
// C: Diagnostician heartbeat chain event recorders
|
|
193
164
|
recordDiagnosisTask(data: DiagnosisTaskEventData): void {
|
|
194
165
|
this.record('diagnosis_task', 'written', undefined, data);
|
|
195
166
|
}
|
|
@@ -199,8 +170,6 @@ export class EventLog {
|
|
|
199
170
|
}
|
|
200
171
|
|
|
201
172
|
recordDiagnosticianReport(data: DiagnosticianReportEventData): void {
|
|
202
|
-
// Map three-state category to EventCategory
|
|
203
|
-
// Both missing_json and incomplete_fields map to 'failure' in EventCategory
|
|
204
173
|
const categoryMap: Record<DiagnosticianReportEventData['category'], EventCategory> = {
|
|
205
174
|
success: 'completed',
|
|
206
175
|
missing_json: 'failure',
|
|
@@ -217,20 +186,6 @@ export class EventLog {
|
|
|
217
186
|
this.record('rule_enforced', 'matched', undefined, data);
|
|
218
187
|
}
|
|
219
188
|
|
|
220
|
-
// C: Nocturnal funnel event recorders (PD-FUNNEL-2.3)
|
|
221
|
-
recordNocturnalDreamerCompleted(data: NocturnalDreamerCompletedEventData): void {
|
|
222
|
-
this.record('nocturnal_dreamer_completed', 'completed', undefined, data);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
recordNocturnalArtifactPersisted(data: NocturnalArtifactPersistedEventData): void {
|
|
226
|
-
this.record('nocturnal_artifact_persisted', 'completed', undefined, data);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
recordNocturnalCodeCandidateCreated(data: NocturnalCodeCandidateCreatedEventData): void {
|
|
230
|
-
this.record('nocturnal_code_candidate_created', 'created', undefined, data);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// C: RuleHost funnel event recorders (PD-FUNNEL-2.4)
|
|
234
189
|
recordRuleHostEvaluated(data: RuleHostEvaluatedEventData): void {
|
|
235
190
|
this.record('rulehost_evaluated', 'evaluated', undefined, data);
|
|
236
191
|
}
|
|
@@ -243,6 +198,26 @@ export class EventLog {
|
|
|
243
198
|
this.record('rulehost_requireApproval', 'requireApproval', undefined, data);
|
|
244
199
|
}
|
|
245
200
|
|
|
201
|
+
recordRuleHostAutoCorrectProposed(data: RuleHostAutoCorrectProposedEventData): void {
|
|
202
|
+
this.record('rulehost_auto_correct_proposed', 'auto_correct', undefined, data);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
recordRuleHostAutoCorrectApplied(data: RuleHostAutoCorrectAppliedEventData): void {
|
|
206
|
+
this.record('rulehost_auto_correct_applied', 'auto_correct', undefined, data);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
recordRuntimeV2ActivationsInjected(data: RuntimeV2PromptActivationsInjectedEventData): void {
|
|
210
|
+
this.record('runtime_v2_prompt_activations_injected', 'injected', data.sessionId, data);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
recordConfirmFirstGateBlocked(data: RuntimeV2ConfirmFirstGateEventData): void {
|
|
214
|
+
this.record('runtime_v2_confirm_first_gate_blocked', 'blocked', data.sessionId, data);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
recordConfirmFirstGateApproved(data: RuntimeV2ConfirmFirstGateEventData): void {
|
|
218
|
+
this.record('runtime_v2_confirm_first_gate_approved', 'approved', data.sessionId, data);
|
|
219
|
+
}
|
|
220
|
+
|
|
246
221
|
private record(
|
|
247
222
|
type: EventType,
|
|
248
223
|
category: EventCategory,
|
|
@@ -269,8 +244,6 @@ export class EventLog {
|
|
|
269
244
|
}
|
|
270
245
|
}
|
|
271
246
|
|
|
272
|
-
|
|
273
|
-
|
|
274
247
|
private formatDate(date: Date): string {
|
|
275
248
|
return date.toISOString().split('T')[0];
|
|
276
249
|
}
|
|
@@ -288,7 +261,6 @@ export class EventLog {
|
|
|
288
261
|
}
|
|
289
262
|
}
|
|
290
263
|
|
|
291
|
-
|
|
292
264
|
private updateStats(entry: EventLogEntry): void {
|
|
293
265
|
let stats = this.statsCache.get(entry.date);
|
|
294
266
|
if (!stats) {
|
|
@@ -300,24 +272,29 @@ export class EventLog {
|
|
|
300
272
|
stats.tools.total++;
|
|
301
273
|
if (entry.category === 'success') stats.tools.success++;
|
|
302
274
|
else stats.tools.failure++;
|
|
275
|
+
|
|
276
|
+
const tcData = entry.data as unknown as ToolCallEventData;
|
|
277
|
+
const observedGfi = tcData.gfiAfter ?? tcData.gfi;
|
|
278
|
+
if (observedGfi !== undefined) {
|
|
279
|
+
stats.gfi.samples++;
|
|
280
|
+
stats.gfi.total += observedGfi;
|
|
281
|
+
stats.gfi.peak = Math.max(stats.gfi.peak, observedGfi);
|
|
282
|
+
}
|
|
303
283
|
} else if (entry.type === 'pain_signal') {
|
|
304
284
|
const data = entry.data as unknown as PainSignalEventData;
|
|
305
285
|
stats.pain.signalsDetected++;
|
|
306
286
|
stats.pain.maxScore = Math.max(stats.pain.maxScore, data.score);
|
|
307
287
|
|
|
308
|
-
// Track signals by source
|
|
309
288
|
if (data.source) {
|
|
310
289
|
stats.pain.signalsBySource[data.source] = (stats.pain.signalsBySource[data.source] || 0) + 1;
|
|
311
290
|
}
|
|
312
291
|
|
|
313
|
-
// Accumulate score for avg calculation
|
|
314
292
|
const currentSum = this.painScoreSums.get(entry.date) ?? 0;
|
|
315
293
|
this.painScoreSums.set(entry.date, currentSum + (data.score || 0));
|
|
316
294
|
stats.pain.avgScore = stats.pain.signalsDetected > 0
|
|
317
295
|
? Math.round((currentSum + (data.score || 0)) / stats.pain.signalsDetected)
|
|
318
296
|
: 0;
|
|
319
297
|
|
|
320
|
-
// Update empathy stats for user_empathy source
|
|
321
298
|
if (data.source === 'user_empathy') {
|
|
322
299
|
if (data.deduped) {
|
|
323
300
|
stats.empathy.dedupedCount++;
|
|
@@ -325,23 +302,19 @@ export class EventLog {
|
|
|
325
302
|
stats.empathy.totalEvents++;
|
|
326
303
|
stats.empathy.totalPenaltyScore += data.score || 0;
|
|
327
304
|
|
|
328
|
-
// By severity
|
|
329
305
|
if (data.severity) {
|
|
330
306
|
stats.empathy.bySeverity[data.severity]++;
|
|
331
307
|
stats.empathy.scoreBySeverity[data.severity] += data.score || 0;
|
|
332
308
|
}
|
|
333
309
|
|
|
334
|
-
// By detection mode
|
|
335
310
|
if (data.detection_mode) {
|
|
336
311
|
stats.empathy.byDetectionMode[data.detection_mode]++;
|
|
337
312
|
}
|
|
338
313
|
|
|
339
|
-
// By origin
|
|
340
314
|
if (data.origin) {
|
|
341
315
|
stats.empathy.byOrigin[data.origin]++;
|
|
342
316
|
}
|
|
343
317
|
|
|
344
|
-
// Confidence distribution
|
|
345
318
|
const conf = data.confidence ?? 1;
|
|
346
319
|
if (conf >= 0.8) stats.empathy.confidenceDistribution.high++;
|
|
347
320
|
else if (conf >= 0.5) stats.empathy.confidenceDistribution.medium++;
|
|
@@ -357,7 +330,6 @@ export class EventLog {
|
|
|
357
330
|
if (entry.category === 'success') stats.hooks.success++;
|
|
358
331
|
else stats.hooks.failure++;
|
|
359
332
|
|
|
360
|
-
// Track by type
|
|
361
333
|
if (data.hook) {
|
|
362
334
|
if (!stats.hooks.byType[data.hook]) {
|
|
363
335
|
stats.hooks.byType[data.hook] = { total: 0, success: 0, failure: 0 };
|
|
@@ -385,19 +357,13 @@ export class EventLog {
|
|
|
385
357
|
stats.evolution.tasksEnqueued++;
|
|
386
358
|
}
|
|
387
359
|
}
|
|
388
|
-
// C: Diagnostician heartbeat chain event counters
|
|
389
360
|
else if (entry.type === 'diagnosis_task') {
|
|
390
361
|
stats.evolution.diagnosisTasksWritten++;
|
|
391
362
|
} else if (entry.type === 'heartbeat_diagnosis') {
|
|
392
363
|
stats.evolution.heartbeatsInjected++;
|
|
393
364
|
} else if (entry.type === 'diagnostician_report') {
|
|
394
|
-
// Backward compat: handle old events with success:boolean and new events with category:string
|
|
395
|
-
// Widen to Record<string, unknown> because DiagnosticianReportEventData requires
|
|
396
|
-
// category (new format) but legacy persisted events have { success: boolean }.
|
|
397
365
|
const raw = entry.data as unknown as Record<string, unknown>;
|
|
398
366
|
if (Object.prototype.hasOwnProperty.call(raw, 'category')) {
|
|
399
|
-
// New format: category is 'success' | 'missing_json' | 'incomplete_fields'
|
|
400
|
-
// All three categories mean diagnosis completed and attempted to produce a report
|
|
401
367
|
const cat = raw['category'] as string;
|
|
402
368
|
if (cat === 'success' || cat === 'missing_json' || cat === 'incomplete_fields') {
|
|
403
369
|
stats.evolution.diagnosticianReportsWritten++;
|
|
@@ -409,10 +375,6 @@ export class EventLog {
|
|
|
409
375
|
stats.evolution.reportsIncompleteFields++;
|
|
410
376
|
}
|
|
411
377
|
} else if (Object.prototype.hasOwnProperty.call(raw, 'success')) {
|
|
412
|
-
// Legacy format: { success: boolean }
|
|
413
|
-
// Agreed fix: count ALL legacy events in diagnosticianReportsWritten (+1 for both true and false).
|
|
414
|
-
// Sub-counters (reportsMissingJson/reportsIncompleteFields) stay untouched because
|
|
415
|
-
// legacy events lack enough information to distinguish sub-category — preserve total, don't fake breakdown.
|
|
416
378
|
stats.evolution.diagnosticianReportsWritten++;
|
|
417
379
|
}
|
|
418
380
|
} else if (entry.type === 'principle_candidate') {
|
|
@@ -420,32 +382,21 @@ export class EventLog {
|
|
|
420
382
|
} else if (entry.type === 'rule_enforced') {
|
|
421
383
|
stats.evolution.rulesEnforced++;
|
|
422
384
|
}
|
|
423
|
-
// C: Nocturnal funnel event counters (PD-FUNNEL-2.3)
|
|
424
|
-
else if (entry.type === 'nocturnal_dreamer_completed') {
|
|
425
|
-
const data = entry.data as unknown as NocturnalDreamerCompletedEventData;
|
|
426
|
-
stats.evolution.nocturnalDreamerCompleted++;
|
|
427
|
-
if (data.chainMode === 'trinity') {
|
|
428
|
-
stats.evolution.nocturnalTrinityCompleted++;
|
|
429
|
-
}
|
|
430
|
-
} else if (entry.type === 'nocturnal_artifact_persisted') {
|
|
431
|
-
stats.evolution.nocturnalArtifactPersisted++;
|
|
432
|
-
} else if (entry.type === 'nocturnal_code_candidate_created') {
|
|
433
|
-
stats.evolution.nocturnalCodeCandidateCreated++;
|
|
434
|
-
}
|
|
435
|
-
// C: RuleHost funnel event counters (PD-FUNNEL-2.4)
|
|
436
385
|
else if (entry.type === 'rulehost_evaluated') {
|
|
437
386
|
stats.evolution.rulehostEvaluated++;
|
|
438
387
|
} else if (entry.type === 'rulehost_blocked') {
|
|
439
388
|
stats.evolution.rulehostBlocked++;
|
|
440
389
|
} else if (entry.type === 'rulehost_requireApproval') {
|
|
441
390
|
stats.evolution.rulehostRequireApproval++;
|
|
391
|
+
} else if (entry.type === 'rulehost_auto_correct_proposed') {
|
|
392
|
+
stats.evolution.rulehostAutoCorrectProposed++;
|
|
393
|
+
} else if (entry.type === 'rulehost_auto_correct_applied') {
|
|
394
|
+
stats.evolution.rulehostAutoCorrectApplied++;
|
|
442
395
|
}
|
|
443
396
|
}
|
|
444
397
|
|
|
445
398
|
private startFlushTimer(): void {
|
|
446
399
|
this.flushTimer = setInterval(() => this.flush(), this.flushIntervalMs);
|
|
447
|
-
// Don't keep the process alive just for this timer
|
|
448
|
-
// This allows tests and CLI to exit without waiting for flush
|
|
449
400
|
this.flushTimer.unref();
|
|
450
401
|
}
|
|
451
402
|
|
|
@@ -454,16 +405,10 @@ export class EventLog {
|
|
|
454
405
|
this.flushStats();
|
|
455
406
|
}
|
|
456
407
|
|
|
457
|
-
/**
|
|
458
|
-
* Return in-memory buffered events that have not been flushed yet.
|
|
459
|
-
* Intended for live runtime summaries that should not lag behind disk snapshots.
|
|
460
|
-
*/
|
|
461
408
|
getBufferedEvents(): EventLogEntry[] {
|
|
462
409
|
return this.eventBuffer.map((entry) => ({ ...entry, data: { ...entry.data } }));
|
|
463
410
|
}
|
|
464
411
|
|
|
465
|
-
|
|
466
|
-
|
|
467
412
|
private getEventDedupKey(entry: EventLogEntry): string {
|
|
468
413
|
const eventId = typeof (entry.data as { eventId?: unknown } | undefined)?.eventId === 'string'
|
|
469
414
|
? String((entry.data as { eventId?: string }).eventId)
|
|
@@ -545,10 +490,6 @@ export class EventLog {
|
|
|
545
490
|
}
|
|
546
491
|
}
|
|
547
492
|
|
|
548
|
-
/**
|
|
549
|
-
* Get daily statistics for a specific date.
|
|
550
|
-
* Returns empty stats if no events recorded for that date.
|
|
551
|
-
*/
|
|
552
493
|
getDailyStats(date: string): DailyStats {
|
|
553
494
|
let stats = this.statsCache.get(date);
|
|
554
495
|
if (!stats) {
|
|
@@ -558,17 +499,10 @@ export class EventLog {
|
|
|
558
499
|
return stats;
|
|
559
500
|
}
|
|
560
501
|
|
|
561
|
-
/**
|
|
562
|
-
* Get aggregated empathy statistics for multiple time ranges.
|
|
563
|
-
* @param range 'today' | 'week' | 'session'
|
|
564
|
-
* @param sessionId Optional session ID for session-scoped stats
|
|
565
|
-
*/
|
|
566
|
-
|
|
567
502
|
getEmpathyStats(range: 'today' | 'week' | 'session', sessionId?: string): EmpathyEventStats {
|
|
568
503
|
const now = new Date();
|
|
569
504
|
const today = this.formatDate(now);
|
|
570
505
|
|
|
571
|
-
// Aggregate stats based on range
|
|
572
506
|
const result: EmpathyEventStats = {
|
|
573
507
|
totalEvents: 0,
|
|
574
508
|
dedupedCount: 0,
|
|
@@ -585,10 +519,8 @@ export class EventLog {
|
|
|
585
519
|
};
|
|
586
520
|
|
|
587
521
|
if (range === 'session' && sessionId) {
|
|
588
|
-
// For session range, scan event buffer and events file
|
|
589
522
|
this.aggregateSessionEmpathy(sessionId, result);
|
|
590
523
|
} else if (range === 'week') {
|
|
591
|
-
// For week range, aggregate last 7 days
|
|
592
524
|
for (let i = 0; i < 7; i++) {
|
|
593
525
|
const date = new Date(now);
|
|
594
526
|
date.setDate(date.getDate() - i);
|
|
@@ -626,7 +558,6 @@ export class EventLog {
|
|
|
626
558
|
}
|
|
627
559
|
}
|
|
628
560
|
} else {
|
|
629
|
-
// Today only
|
|
630
561
|
const stats = this.getDailyStats(today);
|
|
631
562
|
Object.assign(result, stats.empathy);
|
|
632
563
|
if (stats.empathy.totalEvents > 0 || stats.empathy.dedupedCount > 0) {
|
|
@@ -638,17 +569,12 @@ export class EventLog {
|
|
|
638
569
|
}
|
|
639
570
|
}
|
|
640
571
|
|
|
641
|
-
// Calculate dedupe hit rate
|
|
642
572
|
const total = result.totalEvents + result.dedupedCount;
|
|
643
573
|
result.dedupeHitRate = total > 0 ? result.dedupedCount / total : 0;
|
|
644
574
|
|
|
645
575
|
return result;
|
|
646
576
|
}
|
|
647
577
|
|
|
648
|
-
/**
|
|
649
|
-
* Aggregate empathy stats for a specific session.
|
|
650
|
-
*/
|
|
651
|
-
|
|
652
578
|
private aggregateSessionEmpathy(sessionId: string, result: EmpathyEventStats): void {
|
|
653
579
|
for (const entry of this.getMergedEvents()) {
|
|
654
580
|
if (entry.sessionId === sessionId && entry.type === 'pain_signal') {
|
|
@@ -677,15 +603,8 @@ export class EventLog {
|
|
|
677
603
|
result.rolledBackScore += data.originalScore || 0;
|
|
678
604
|
}
|
|
679
605
|
}
|
|
680
|
-
|
|
681
606
|
}
|
|
682
607
|
|
|
683
|
-
/**
|
|
684
|
-
* Rollback an empathy event by ID.
|
|
685
|
-
* Returns the rolled back score, or 0 if event not found.
|
|
686
|
-
*/
|
|
687
|
-
|
|
688
|
-
|
|
689
608
|
rollbackEmpathyEvent(eventId: string, sessionId: string | undefined, reason: string, triggeredBy: 'user_command' | 'natural_language' | 'system'): number {
|
|
690
609
|
const allEvents = this.getMergedEvents();
|
|
691
610
|
let foundEvent: { entry: EventLogEntry; data: PainSignalEventData } | null = null;
|
|
@@ -706,7 +625,6 @@ export class EventLog {
|
|
|
706
625
|
|
|
707
626
|
const originalScore = foundEvent.data.score || 0;
|
|
708
627
|
|
|
709
|
-
// Record the rollback event
|
|
710
628
|
this.recordEmpathyRollback(sessionId, {
|
|
711
629
|
eventId,
|
|
712
630
|
originalScore,
|
|
@@ -718,9 +636,6 @@ export class EventLog {
|
|
|
718
636
|
return originalScore;
|
|
719
637
|
}
|
|
720
638
|
|
|
721
|
-
/**
|
|
722
|
-
* Get the last empathy event ID for a session (for rollback).
|
|
723
|
-
*/
|
|
724
639
|
getLastEmpathyEventId(sessionId: string): string | null {
|
|
725
640
|
const allEvents = this.getMergedEvents();
|
|
726
641
|
for (let i = allEvents.length - 1; i >= 0; i--) {
|
|
@@ -736,9 +651,6 @@ export class EventLog {
|
|
|
736
651
|
return null;
|
|
737
652
|
}
|
|
738
653
|
|
|
739
|
-
/**
|
|
740
|
-
* Find the latest pain signal for a given session.
|
|
741
|
-
*/
|
|
742
654
|
findLatestPainSignal(sessionId: string | undefined): PainSignalEventData | null {
|
|
743
655
|
const allEvents = this.getMergedEvents();
|
|
744
656
|
for (let i = allEvents.length - 1; i >= 0; i--) {
|
|
@@ -750,9 +662,6 @@ export class EventLog {
|
|
|
750
662
|
return null;
|
|
751
663
|
}
|
|
752
664
|
|
|
753
|
-
/**
|
|
754
|
-
* Dispose of the EventLog, flushing pending data and clearing timer.
|
|
755
|
-
*/
|
|
756
665
|
dispose(): void {
|
|
757
666
|
if (this.flushTimer) {
|
|
758
667
|
clearInterval(this.flushTimer);
|
|
@@ -762,9 +671,6 @@ export class EventLog {
|
|
|
762
671
|
}
|
|
763
672
|
}
|
|
764
673
|
|
|
765
|
-
/**
|
|
766
|
-
* Service to manage multiple EventLog instances by stateDir.
|
|
767
|
-
*/
|
|
768
674
|
export class EventLogService {
|
|
769
675
|
private static readonly instances: Map<string, EventLog> = new Map();
|
|
770
676
|
|
|
@@ -105,6 +105,8 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
105
105
|
private readonly failureStreak = new Map<string, number>();
|
|
106
106
|
private lastPromotedAt: string | null = null;
|
|
107
107
|
private isReplaying = false;
|
|
108
|
+
/** Registered pain_detected callbacks (e.g., PainSignalBridge). */
|
|
109
|
+
private readonly _painCallbacks: Array<(event: EvolutionLoopEvent) => void> = [];
|
|
108
110
|
|
|
109
111
|
constructor(opts: { workspaceDir: string; stateDir?: string }) {
|
|
110
112
|
this.workspaceDir = opts.workspaceDir;
|
|
@@ -145,6 +147,15 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
145
147
|
// Performance: sweepExpiredProbation() moved to getProbationPrinciples() for lazy cleanup
|
|
146
148
|
}
|
|
147
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Register a callback for 'pain_detected' events.
|
|
152
|
+
* The callback is invoked synchronously within emitSync() after applyEvent completes.
|
|
153
|
+
* HG-4: Callbacks are fire-and-forget from the emitSync perspective.
|
|
154
|
+
*/
|
|
155
|
+
on(callback: (event: EvolutionLoopEvent) => void): void {
|
|
156
|
+
this._painCallbacks.push(callback);
|
|
157
|
+
}
|
|
158
|
+
|
|
148
159
|
getEventLog(): EvolutionLoopEvent[] {
|
|
149
160
|
return [...this.memoryEvents];
|
|
150
161
|
}
|
|
@@ -171,14 +182,14 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
171
182
|
const p = this.principles.get(principleId);
|
|
172
183
|
if (!p || p.status === 'active' || p.status === 'deprecated') return;
|
|
173
184
|
|
|
174
|
-
const
|
|
185
|
+
const toStatus = (p.status === 'candidate' ? 'probation' : p.status === 'probation' ? 'active' : p.status) as PrincipleStatus;
|
|
175
186
|
const event: EvolutionLoopEvent = {
|
|
176
187
|
ts: new Date().toISOString(),
|
|
177
188
|
type: 'principle_promoted',
|
|
178
189
|
data: {
|
|
179
190
|
principleId,
|
|
180
191
|
from: p.status,
|
|
181
|
-
to:
|
|
192
|
+
to: toStatus,
|
|
182
193
|
reason,
|
|
183
194
|
successCount: p.validation.successCount,
|
|
184
195
|
},
|
|
@@ -650,6 +661,14 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
650
661
|
if (!this.isReplaying) {
|
|
651
662
|
this.onPainDetected(event.data, event.ts);
|
|
652
663
|
}
|
|
664
|
+
// PainSignalBridge subscriptions: invoke registered callbacks (fire-and-forget)
|
|
665
|
+
for (const cb of this._painCallbacks) {
|
|
666
|
+
try {
|
|
667
|
+
cb(event);
|
|
668
|
+
} catch {
|
|
669
|
+
// Keep the evolution loop resilient — callback errors must not propagate
|
|
670
|
+
}
|
|
671
|
+
}
|
|
653
672
|
return;
|
|
654
673
|
case 'candidate_created':
|
|
655
674
|
this.onCandidateCreated(event.data, event.ts);
|