principles-disciple 1.72.0 → 1.74.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/INSTALL.md +1 -3
- 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/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 +29 -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/migration.ts +0 -1
- 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/path-resolver.ts +0 -1
- package/src/core/paths.ts +0 -1
- 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 +71 -64
- package/src/hooks/gate.ts +183 -31
- 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 +400 -440
- 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 +38 -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 +8 -20
- 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 +7 -7
- package/templates/langs/en/core/BOOT.md +1 -1
- package/templates/langs/en/core/HEARTBEAT.md +2 -2
- package/templates/langs/en/principles/THINKING_OS.md +3 -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/scripts/run.mjs +51 -15
- package/templates/langs/en/skills/evolve-task/SKILL.md +3 -3
- 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 +2 -3
- 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 +7 -7
- package/templates/langs/zh/core/BOOT.md +1 -1
- package/templates/langs/zh/core/HEARTBEAT.md +2 -2
- package/templates/langs/zh/principles/THINKING_OS.md +3 -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/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 +4 -4
- 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 +2 -3
- 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/migration.test.ts +7 -7
- 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/path-resolver.test.ts +1 -1
- package/tests/core/paths-refactor.test.ts +0 -22
- 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-context.test.ts +2 -2
- package/tests/core/workspace-dir-validation.test.ts +8 -1
- package/tests/core-anti-growth.test.ts +191 -0
- package/tests/hook-workspace-nextaction-contract.test.ts +42 -0
- package/tests/hooks/confirm-first-removal.test.ts +188 -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-no-path-write-tool.test.ts +172 -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/templates/langs/en/skills/plan-script/SKILL.md +0 -32
- package/templates/langs/zh/skills/plan-script/SKILL.md +0 -32
- 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,22 @@ 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,
|
|
34
31
|
} from '../types/event-types.js';
|
|
35
32
|
import { createEmptyDailyStats } from '../types/event-types.js';
|
|
36
33
|
import { atomicWriteFileSync } from '../utils/io.js';
|
|
37
34
|
import type { PluginLogger } from '../openclaw-sdk.js';
|
|
38
35
|
|
|
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
36
|
const EVENT_LOG_RETENTION_DAYS = 7;
|
|
51
37
|
|
|
52
38
|
export class EventLog {
|
|
@@ -60,11 +46,9 @@ export class EventLog {
|
|
|
60
46
|
private readonly flushIntervalMs = 30000;
|
|
61
47
|
private flushTimer?: ReturnType<typeof setInterval>;
|
|
62
48
|
|
|
63
|
-
// Cached event file path for current date
|
|
64
49
|
private currentEventsFile: string | undefined;
|
|
65
50
|
private currentDate: string | undefined;
|
|
66
51
|
|
|
67
|
-
// Pain score sum per date (for avgScore calculation)
|
|
68
52
|
private readonly painScoreSums: Map<string, number> = new Map();
|
|
69
53
|
|
|
70
54
|
constructor(stateDir: string, logger?: PluginLogger) {
|
|
@@ -80,37 +64,24 @@ export class EventLog {
|
|
|
80
64
|
this.startFlushTimer();
|
|
81
65
|
}
|
|
82
66
|
|
|
83
|
-
/**
|
|
84
|
-
* Get the event file path for a given date.
|
|
85
|
-
*/
|
|
86
67
|
private getEventsFile(date: string): string {
|
|
87
68
|
return path.join(this.logsDir, `events_${date}.jsonl`);
|
|
88
69
|
}
|
|
89
70
|
|
|
90
|
-
/**
|
|
91
|
-
* Get today's date string (YYYY-MM-DD).
|
|
92
|
-
*/
|
|
93
71
|
private getTodayStr(): string {
|
|
94
72
|
return new Date().toISOString().split('T')[0];
|
|
95
73
|
}
|
|
96
74
|
|
|
97
|
-
/**
|
|
98
|
-
* Ensure we have the correct events file for today's date.
|
|
99
|
-
*/
|
|
100
75
|
private ensureEventsFile(): string {
|
|
101
76
|
const today = this.getTodayStr();
|
|
102
77
|
if (this.currentDate !== today || !this.currentEventsFile) {
|
|
103
78
|
this.currentDate = today;
|
|
104
79
|
this.currentEventsFile = this.getEventsFile(today);
|
|
105
|
-
// Run cleanup if date changed
|
|
106
80
|
this.cleanupOldEventFiles(today);
|
|
107
81
|
}
|
|
108
82
|
return this.currentEventsFile;
|
|
109
83
|
}
|
|
110
84
|
|
|
111
|
-
/**
|
|
112
|
-
* Clean up event files older than EVENT_LOG_RETENTION_DAYS.
|
|
113
|
-
*/
|
|
114
85
|
private cleanupOldEventFiles(_today: string): void {
|
|
115
86
|
if (EVENT_LOG_RETENTION_DAYS <= 0) return;
|
|
116
87
|
|
|
@@ -133,7 +104,7 @@ export class EventLog {
|
|
|
133
104
|
}
|
|
134
105
|
|
|
135
106
|
recordToolCall(sessionId: string | undefined, data: ToolCallEventData): void {
|
|
136
|
-
const category = data.error ? 'failure' : 'success';
|
|
107
|
+
const category = data.error || (data.exitCode !== undefined && data.exitCode !== 0) ? 'failure' : 'success';
|
|
137
108
|
this.record('tool_call', category, sessionId, data);
|
|
138
109
|
}
|
|
139
110
|
|
|
@@ -189,7 +160,6 @@ export class EventLog {
|
|
|
189
160
|
this.record('warn', 'failure', sessionId, { message, ...context });
|
|
190
161
|
}
|
|
191
162
|
|
|
192
|
-
// C: Diagnostician heartbeat chain event recorders
|
|
193
163
|
recordDiagnosisTask(data: DiagnosisTaskEventData): void {
|
|
194
164
|
this.record('diagnosis_task', 'written', undefined, data);
|
|
195
165
|
}
|
|
@@ -199,8 +169,6 @@ export class EventLog {
|
|
|
199
169
|
}
|
|
200
170
|
|
|
201
171
|
recordDiagnosticianReport(data: DiagnosticianReportEventData): void {
|
|
202
|
-
// Map three-state category to EventCategory
|
|
203
|
-
// Both missing_json and incomplete_fields map to 'failure' in EventCategory
|
|
204
172
|
const categoryMap: Record<DiagnosticianReportEventData['category'], EventCategory> = {
|
|
205
173
|
success: 'completed',
|
|
206
174
|
missing_json: 'failure',
|
|
@@ -217,20 +185,6 @@ export class EventLog {
|
|
|
217
185
|
this.record('rule_enforced', 'matched', undefined, data);
|
|
218
186
|
}
|
|
219
187
|
|
|
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
188
|
recordRuleHostEvaluated(data: RuleHostEvaluatedEventData): void {
|
|
235
189
|
this.record('rulehost_evaluated', 'evaluated', undefined, data);
|
|
236
190
|
}
|
|
@@ -243,6 +197,18 @@ export class EventLog {
|
|
|
243
197
|
this.record('rulehost_requireApproval', 'requireApproval', undefined, data);
|
|
244
198
|
}
|
|
245
199
|
|
|
200
|
+
recordRuleHostAutoCorrectProposed(data: RuleHostAutoCorrectProposedEventData): void {
|
|
201
|
+
this.record('rulehost_auto_correct_proposed', 'auto_correct', undefined, data);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
recordRuleHostAutoCorrectApplied(data: RuleHostAutoCorrectAppliedEventData): void {
|
|
205
|
+
this.record('rulehost_auto_correct_applied', 'auto_correct', undefined, data);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
recordRuntimeV2ActivationsInjected(data: RuntimeV2PromptActivationsInjectedEventData): void {
|
|
209
|
+
this.record('runtime_v2_prompt_activations_injected', 'injected', data.sessionId, data);
|
|
210
|
+
}
|
|
211
|
+
|
|
246
212
|
private record(
|
|
247
213
|
type: EventType,
|
|
248
214
|
category: EventCategory,
|
|
@@ -269,8 +235,6 @@ export class EventLog {
|
|
|
269
235
|
}
|
|
270
236
|
}
|
|
271
237
|
|
|
272
|
-
|
|
273
|
-
|
|
274
238
|
private formatDate(date: Date): string {
|
|
275
239
|
return date.toISOString().split('T')[0];
|
|
276
240
|
}
|
|
@@ -288,7 +252,6 @@ export class EventLog {
|
|
|
288
252
|
}
|
|
289
253
|
}
|
|
290
254
|
|
|
291
|
-
|
|
292
255
|
private updateStats(entry: EventLogEntry): void {
|
|
293
256
|
let stats = this.statsCache.get(entry.date);
|
|
294
257
|
if (!stats) {
|
|
@@ -300,24 +263,29 @@ export class EventLog {
|
|
|
300
263
|
stats.tools.total++;
|
|
301
264
|
if (entry.category === 'success') stats.tools.success++;
|
|
302
265
|
else stats.tools.failure++;
|
|
266
|
+
|
|
267
|
+
const tcData = entry.data as unknown as ToolCallEventData;
|
|
268
|
+
const observedGfi = tcData.gfiAfter ?? tcData.gfi;
|
|
269
|
+
if (observedGfi !== undefined) {
|
|
270
|
+
stats.gfi.samples++;
|
|
271
|
+
stats.gfi.total += observedGfi;
|
|
272
|
+
stats.gfi.peak = Math.max(stats.gfi.peak, observedGfi);
|
|
273
|
+
}
|
|
303
274
|
} else if (entry.type === 'pain_signal') {
|
|
304
275
|
const data = entry.data as unknown as PainSignalEventData;
|
|
305
276
|
stats.pain.signalsDetected++;
|
|
306
277
|
stats.pain.maxScore = Math.max(stats.pain.maxScore, data.score);
|
|
307
278
|
|
|
308
|
-
// Track signals by source
|
|
309
279
|
if (data.source) {
|
|
310
280
|
stats.pain.signalsBySource[data.source] = (stats.pain.signalsBySource[data.source] || 0) + 1;
|
|
311
281
|
}
|
|
312
282
|
|
|
313
|
-
// Accumulate score for avg calculation
|
|
314
283
|
const currentSum = this.painScoreSums.get(entry.date) ?? 0;
|
|
315
284
|
this.painScoreSums.set(entry.date, currentSum + (data.score || 0));
|
|
316
285
|
stats.pain.avgScore = stats.pain.signalsDetected > 0
|
|
317
286
|
? Math.round((currentSum + (data.score || 0)) / stats.pain.signalsDetected)
|
|
318
287
|
: 0;
|
|
319
288
|
|
|
320
|
-
// Update empathy stats for user_empathy source
|
|
321
289
|
if (data.source === 'user_empathy') {
|
|
322
290
|
if (data.deduped) {
|
|
323
291
|
stats.empathy.dedupedCount++;
|
|
@@ -325,23 +293,19 @@ export class EventLog {
|
|
|
325
293
|
stats.empathy.totalEvents++;
|
|
326
294
|
stats.empathy.totalPenaltyScore += data.score || 0;
|
|
327
295
|
|
|
328
|
-
// By severity
|
|
329
296
|
if (data.severity) {
|
|
330
297
|
stats.empathy.bySeverity[data.severity]++;
|
|
331
298
|
stats.empathy.scoreBySeverity[data.severity] += data.score || 0;
|
|
332
299
|
}
|
|
333
300
|
|
|
334
|
-
// By detection mode
|
|
335
301
|
if (data.detection_mode) {
|
|
336
302
|
stats.empathy.byDetectionMode[data.detection_mode]++;
|
|
337
303
|
}
|
|
338
304
|
|
|
339
|
-
// By origin
|
|
340
305
|
if (data.origin) {
|
|
341
306
|
stats.empathy.byOrigin[data.origin]++;
|
|
342
307
|
}
|
|
343
308
|
|
|
344
|
-
// Confidence distribution
|
|
345
309
|
const conf = data.confidence ?? 1;
|
|
346
310
|
if (conf >= 0.8) stats.empathy.confidenceDistribution.high++;
|
|
347
311
|
else if (conf >= 0.5) stats.empathy.confidenceDistribution.medium++;
|
|
@@ -357,7 +321,6 @@ export class EventLog {
|
|
|
357
321
|
if (entry.category === 'success') stats.hooks.success++;
|
|
358
322
|
else stats.hooks.failure++;
|
|
359
323
|
|
|
360
|
-
// Track by type
|
|
361
324
|
if (data.hook) {
|
|
362
325
|
if (!stats.hooks.byType[data.hook]) {
|
|
363
326
|
stats.hooks.byType[data.hook] = { total: 0, success: 0, failure: 0 };
|
|
@@ -385,19 +348,13 @@ export class EventLog {
|
|
|
385
348
|
stats.evolution.tasksEnqueued++;
|
|
386
349
|
}
|
|
387
350
|
}
|
|
388
|
-
// C: Diagnostician heartbeat chain event counters
|
|
389
351
|
else if (entry.type === 'diagnosis_task') {
|
|
390
352
|
stats.evolution.diagnosisTasksWritten++;
|
|
391
353
|
} else if (entry.type === 'heartbeat_diagnosis') {
|
|
392
354
|
stats.evolution.heartbeatsInjected++;
|
|
393
355
|
} 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
356
|
const raw = entry.data as unknown as Record<string, unknown>;
|
|
398
357
|
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
358
|
const cat = raw['category'] as string;
|
|
402
359
|
if (cat === 'success' || cat === 'missing_json' || cat === 'incomplete_fields') {
|
|
403
360
|
stats.evolution.diagnosticianReportsWritten++;
|
|
@@ -409,10 +366,6 @@ export class EventLog {
|
|
|
409
366
|
stats.evolution.reportsIncompleteFields++;
|
|
410
367
|
}
|
|
411
368
|
} 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
369
|
stats.evolution.diagnosticianReportsWritten++;
|
|
417
370
|
}
|
|
418
371
|
} else if (entry.type === 'principle_candidate') {
|
|
@@ -420,32 +373,21 @@ export class EventLog {
|
|
|
420
373
|
} else if (entry.type === 'rule_enforced') {
|
|
421
374
|
stats.evolution.rulesEnforced++;
|
|
422
375
|
}
|
|
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
376
|
else if (entry.type === 'rulehost_evaluated') {
|
|
437
377
|
stats.evolution.rulehostEvaluated++;
|
|
438
378
|
} else if (entry.type === 'rulehost_blocked') {
|
|
439
379
|
stats.evolution.rulehostBlocked++;
|
|
440
380
|
} else if (entry.type === 'rulehost_requireApproval') {
|
|
441
381
|
stats.evolution.rulehostRequireApproval++;
|
|
382
|
+
} else if (entry.type === 'rulehost_auto_correct_proposed') {
|
|
383
|
+
stats.evolution.rulehostAutoCorrectProposed++;
|
|
384
|
+
} else if (entry.type === 'rulehost_auto_correct_applied') {
|
|
385
|
+
stats.evolution.rulehostAutoCorrectApplied++;
|
|
442
386
|
}
|
|
443
387
|
}
|
|
444
388
|
|
|
445
389
|
private startFlushTimer(): void {
|
|
446
390
|
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
391
|
this.flushTimer.unref();
|
|
450
392
|
}
|
|
451
393
|
|
|
@@ -454,16 +396,10 @@ export class EventLog {
|
|
|
454
396
|
this.flushStats();
|
|
455
397
|
}
|
|
456
398
|
|
|
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
399
|
getBufferedEvents(): EventLogEntry[] {
|
|
462
400
|
return this.eventBuffer.map((entry) => ({ ...entry, data: { ...entry.data } }));
|
|
463
401
|
}
|
|
464
402
|
|
|
465
|
-
|
|
466
|
-
|
|
467
403
|
private getEventDedupKey(entry: EventLogEntry): string {
|
|
468
404
|
const eventId = typeof (entry.data as { eventId?: unknown } | undefined)?.eventId === 'string'
|
|
469
405
|
? String((entry.data as { eventId?: string }).eventId)
|
|
@@ -545,10 +481,6 @@ export class EventLog {
|
|
|
545
481
|
}
|
|
546
482
|
}
|
|
547
483
|
|
|
548
|
-
/**
|
|
549
|
-
* Get daily statistics for a specific date.
|
|
550
|
-
* Returns empty stats if no events recorded for that date.
|
|
551
|
-
*/
|
|
552
484
|
getDailyStats(date: string): DailyStats {
|
|
553
485
|
let stats = this.statsCache.get(date);
|
|
554
486
|
if (!stats) {
|
|
@@ -558,17 +490,10 @@ export class EventLog {
|
|
|
558
490
|
return stats;
|
|
559
491
|
}
|
|
560
492
|
|
|
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
493
|
getEmpathyStats(range: 'today' | 'week' | 'session', sessionId?: string): EmpathyEventStats {
|
|
568
494
|
const now = new Date();
|
|
569
495
|
const today = this.formatDate(now);
|
|
570
496
|
|
|
571
|
-
// Aggregate stats based on range
|
|
572
497
|
const result: EmpathyEventStats = {
|
|
573
498
|
totalEvents: 0,
|
|
574
499
|
dedupedCount: 0,
|
|
@@ -585,10 +510,8 @@ export class EventLog {
|
|
|
585
510
|
};
|
|
586
511
|
|
|
587
512
|
if (range === 'session' && sessionId) {
|
|
588
|
-
// For session range, scan event buffer and events file
|
|
589
513
|
this.aggregateSessionEmpathy(sessionId, result);
|
|
590
514
|
} else if (range === 'week') {
|
|
591
|
-
// For week range, aggregate last 7 days
|
|
592
515
|
for (let i = 0; i < 7; i++) {
|
|
593
516
|
const date = new Date(now);
|
|
594
517
|
date.setDate(date.getDate() - i);
|
|
@@ -626,7 +549,6 @@ export class EventLog {
|
|
|
626
549
|
}
|
|
627
550
|
}
|
|
628
551
|
} else {
|
|
629
|
-
// Today only
|
|
630
552
|
const stats = this.getDailyStats(today);
|
|
631
553
|
Object.assign(result, stats.empathy);
|
|
632
554
|
if (stats.empathy.totalEvents > 0 || stats.empathy.dedupedCount > 0) {
|
|
@@ -638,17 +560,12 @@ export class EventLog {
|
|
|
638
560
|
}
|
|
639
561
|
}
|
|
640
562
|
|
|
641
|
-
// Calculate dedupe hit rate
|
|
642
563
|
const total = result.totalEvents + result.dedupedCount;
|
|
643
564
|
result.dedupeHitRate = total > 0 ? result.dedupedCount / total : 0;
|
|
644
565
|
|
|
645
566
|
return result;
|
|
646
567
|
}
|
|
647
568
|
|
|
648
|
-
/**
|
|
649
|
-
* Aggregate empathy stats for a specific session.
|
|
650
|
-
*/
|
|
651
|
-
|
|
652
569
|
private aggregateSessionEmpathy(sessionId: string, result: EmpathyEventStats): void {
|
|
653
570
|
for (const entry of this.getMergedEvents()) {
|
|
654
571
|
if (entry.sessionId === sessionId && entry.type === 'pain_signal') {
|
|
@@ -677,15 +594,8 @@ export class EventLog {
|
|
|
677
594
|
result.rolledBackScore += data.originalScore || 0;
|
|
678
595
|
}
|
|
679
596
|
}
|
|
680
|
-
|
|
681
597
|
}
|
|
682
598
|
|
|
683
|
-
/**
|
|
684
|
-
* Rollback an empathy event by ID.
|
|
685
|
-
* Returns the rolled back score, or 0 if event not found.
|
|
686
|
-
*/
|
|
687
|
-
|
|
688
|
-
|
|
689
599
|
rollbackEmpathyEvent(eventId: string, sessionId: string | undefined, reason: string, triggeredBy: 'user_command' | 'natural_language' | 'system'): number {
|
|
690
600
|
const allEvents = this.getMergedEvents();
|
|
691
601
|
let foundEvent: { entry: EventLogEntry; data: PainSignalEventData } | null = null;
|
|
@@ -706,7 +616,6 @@ export class EventLog {
|
|
|
706
616
|
|
|
707
617
|
const originalScore = foundEvent.data.score || 0;
|
|
708
618
|
|
|
709
|
-
// Record the rollback event
|
|
710
619
|
this.recordEmpathyRollback(sessionId, {
|
|
711
620
|
eventId,
|
|
712
621
|
originalScore,
|
|
@@ -718,9 +627,6 @@ export class EventLog {
|
|
|
718
627
|
return originalScore;
|
|
719
628
|
}
|
|
720
629
|
|
|
721
|
-
/**
|
|
722
|
-
* Get the last empathy event ID for a session (for rollback).
|
|
723
|
-
*/
|
|
724
630
|
getLastEmpathyEventId(sessionId: string): string | null {
|
|
725
631
|
const allEvents = this.getMergedEvents();
|
|
726
632
|
for (let i = allEvents.length - 1; i >= 0; i--) {
|
|
@@ -736,9 +642,6 @@ export class EventLog {
|
|
|
736
642
|
return null;
|
|
737
643
|
}
|
|
738
644
|
|
|
739
|
-
/**
|
|
740
|
-
* Find the latest pain signal for a given session.
|
|
741
|
-
*/
|
|
742
645
|
findLatestPainSignal(sessionId: string | undefined): PainSignalEventData | null {
|
|
743
646
|
const allEvents = this.getMergedEvents();
|
|
744
647
|
for (let i = allEvents.length - 1; i >= 0; i--) {
|
|
@@ -750,9 +653,6 @@ export class EventLog {
|
|
|
750
653
|
return null;
|
|
751
654
|
}
|
|
752
655
|
|
|
753
|
-
/**
|
|
754
|
-
* Dispose of the EventLog, flushing pending data and clearing timer.
|
|
755
|
-
*/
|
|
756
656
|
dispose(): void {
|
|
757
657
|
if (this.flushTimer) {
|
|
758
658
|
clearInterval(this.flushTimer);
|
|
@@ -762,9 +662,6 @@ export class EventLog {
|
|
|
762
662
|
}
|
|
763
663
|
}
|
|
764
664
|
|
|
765
|
-
/**
|
|
766
|
-
* Service to manage multiple EventLog instances by stateDir.
|
|
767
|
-
*/
|
|
768
665
|
export class EventLogService {
|
|
769
666
|
private static readonly instances: Map<string, EventLog> = new Map();
|
|
770
667
|
|
|
@@ -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);
|