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.
Files changed (309) hide show
  1. package/openclaw.plugin.json +10 -5
  2. package/package.json +17 -19
  3. package/scripts/acceptance-test.mjs +16 -73
  4. package/scripts/sync-plugin.mjs +382 -77
  5. package/src/commands/archive-impl.ts +2 -1
  6. package/src/commands/capabilities.ts +2 -2
  7. package/src/commands/context.ts +2 -2
  8. package/src/commands/disable-impl.ts +2 -1
  9. package/src/commands/evolution-status.ts +16 -16
  10. package/src/commands/export.ts +12 -67
  11. package/src/commands/pain.ts +91 -1
  12. package/src/commands/principle-rollback.ts +2 -1
  13. package/src/commands/promote-impl.ts +7 -43
  14. package/src/commands/rollback-impl.ts +2 -1
  15. package/src/commands/rollback.ts +2 -1
  16. package/src/commands/samples.ts +2 -1
  17. package/src/commands/thinking-os.ts +2 -1
  18. package/src/config/errors.ts +18 -2
  19. package/src/constants/diagnostician.ts +2 -2
  20. package/src/constants/tools.ts +2 -1
  21. package/src/core/__tests__/focus-history.test.ts +210 -0
  22. package/src/core/config.ts +1 -1
  23. package/src/core/confirm-first-gate.ts +255 -0
  24. package/src/core/correction-cue-learner.ts +2 -136
  25. package/src/core/correction-types.ts +16 -88
  26. package/src/core/dictionary.ts +19 -20
  27. package/src/core/empathy-keyword-matcher.ts +17 -289
  28. package/src/core/empathy-types.ts +18 -229
  29. package/src/core/event-log.ts +38 -132
  30. package/src/core/evolution-reducer.ts +21 -2
  31. package/src/core/evolution-types.ts +76 -464
  32. package/src/core/file-store.ts +80 -0
  33. package/src/core/focus-history.ts +228 -955
  34. package/src/core/local-worker-routing.ts +34 -314
  35. package/src/core/merge-gate-audit.ts +0 -195
  36. package/src/core/pain-diagnostic-gate.ts +154 -0
  37. package/src/core/pain-signal.ts +21 -138
  38. package/src/core/pain.ts +15 -88
  39. package/src/core/pd-task-reconciler.ts +26 -115
  40. package/src/core/pd-task-service.ts +9 -9
  41. package/src/core/pd-task-types.ts +23 -127
  42. package/src/core/principle-compiler/__tests__/compiler-replay-gate.test.ts +174 -0
  43. package/src/core/principle-compiler/code-validator.ts +15 -42
  44. package/src/core/principle-compiler/compiler.ts +100 -15
  45. package/src/core/principle-compiler/index.ts +5 -2
  46. package/src/core/principle-compiler/template-generator.ts +4 -104
  47. package/src/core/principle-injection.ts +10 -202
  48. package/src/core/principle-internalization/filesystem-lifecycle-datasource.ts +42 -0
  49. package/src/core/principle-internalization/lifecycle-read-model.ts +39 -242
  50. package/src/core/principle-internalization/principle-lifecycle-service.ts +12 -10
  51. package/src/core/principle-tree-ledger-adapter.ts +145 -0
  52. package/src/core/principle-tree-ledger.ts +8 -6
  53. package/src/core/reflection/reflection-context.ts +14 -109
  54. package/src/core/replay-engine.ts +8 -500
  55. package/src/core/rule-host-helpers.ts +5 -35
  56. package/src/core/rule-host-types.ts +10 -82
  57. package/src/core/rule-host.ts +6 -63
  58. package/src/core/runtime-v2-prompt-activation-reader.ts +231 -0
  59. package/src/core/session-tracker.ts +87 -101
  60. package/src/core/shadow-observation-registry.ts +19 -48
  61. package/src/core/trajectory.ts +3 -1
  62. package/src/core/workflow-funnel-loader.ts +62 -68
  63. package/src/core/workspace-context.ts +46 -0
  64. package/src/core/workspace-dir-service.ts +1 -1
  65. package/src/core/workspace-dir-validation.ts +18 -9
  66. package/src/hooks/AGENTS.md +1 -1
  67. package/src/hooks/gate-block-helper.ts +46 -44
  68. package/src/hooks/gate.ts +207 -7
  69. package/src/hooks/lifecycle.ts +30 -32
  70. package/src/hooks/llm.ts +60 -32
  71. package/src/hooks/pain.ts +297 -103
  72. package/src/hooks/prompt.ts +459 -439
  73. package/src/hooks/subagent.ts +2 -29
  74. package/src/i18n/commands.ts +2 -10
  75. package/src/index.ts +95 -85
  76. package/src/openclaw-sdk.ts +311 -0
  77. package/src/service/central-database.ts +8 -4
  78. package/src/service/evolution-queue-migration.ts +2 -1
  79. package/src/service/evolution-worker.ts +163 -1786
  80. package/src/service/internalization-trigger-adapter.ts +302 -0
  81. package/src/service/keyword-optimization-service.ts +4 -4
  82. package/src/service/monitoring-query-service.ts +1 -215
  83. package/src/service/queue-io.ts +60 -331
  84. package/src/service/runtime-summary-service.ts +59 -16
  85. package/src/service/subagent-workflow/index.ts +0 -41
  86. package/src/service/subagent-workflow/types.ts +9 -120
  87. package/src/service/subagent-workflow/workflow-store.ts +2 -119
  88. package/src/service/workflow-watchdog.ts +0 -43
  89. package/src/types/event-payload.ts +16 -74
  90. package/src/types/event-types.ts +39 -547
  91. package/src/types/hygiene-types.ts +7 -30
  92. package/src/types/principle-tree-schema.ts +20 -222
  93. package/src/types/queue.ts +15 -70
  94. package/src/types/runtime-summary.ts +5 -49
  95. package/src/utils/io.ts +10 -0
  96. package/src/utils/retry.ts +1 -1
  97. package/src/utils/shadow-fingerprint.ts +2 -2
  98. package/src/utils/workspace-resolver.ts +50 -0
  99. package/templates/langs/en/core/AGENTS.md +2 -2
  100. package/templates/langs/en/core/BOOT.md +1 -1
  101. package/templates/langs/en/core/HEARTBEAT.md +2 -2
  102. package/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
  103. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
  104. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
  105. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
  106. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
  107. package/templates/langs/en/skills/ai-sprint-orchestration/runtime/.gitignore +2 -2
  108. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
  109. package/templates/langs/en/skills/evolve-task/SKILL.md +1 -1
  110. package/templates/langs/en/skills/pd-cli-operator/SKILL.md +67 -0
  111. package/templates/langs/en/skills/pd-diagnostician/SKILL.md +1 -1
  112. package/templates/langs/en/skills/pd-mentor/SKILL.md +1 -1
  113. package/templates/langs/en/skills/pd-pain-signal/SKILL.md +17 -39
  114. package/templates/langs/en/skills/pd-runtime-v2/SKILL.md +61 -0
  115. package/templates/langs/zh/core/AGENTS.md +2 -2
  116. package/templates/langs/zh/core/BOOT.md +1 -1
  117. package/templates/langs/zh/core/HEARTBEAT.md +2 -2
  118. package/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
  119. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
  120. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
  121. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +8 -8
  122. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
  123. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
  124. package/templates/langs/zh/skills/ai-sprint-orchestration/runtime/.gitignore +2 -2
  125. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
  126. package/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +21 -5
  127. package/templates/langs/zh/skills/evolve-task/SKILL.md +2 -2
  128. package/templates/langs/zh/skills/pd-cli-operator/SKILL.md +67 -0
  129. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +1 -1
  130. package/templates/langs/zh/skills/pd-mentor/SKILL.md +1 -1
  131. package/templates/langs/zh/skills/pd-pain-signal/SKILL.md +17 -38
  132. package/templates/langs/zh/skills/pd-runtime-v2/SKILL.md +61 -0
  133. package/tests/build-artifacts.test.ts +1 -3
  134. package/tests/commands/evolution-status.test.ts +0 -118
  135. package/tests/core/bootstrap-rules.test.ts +1 -1
  136. package/tests/core/config.test.ts +1 -1
  137. package/tests/core/event-log.test.ts +35 -0
  138. package/tests/core/evolution-engine.test.ts +610 -0
  139. package/tests/core/file-store.test.ts +102 -0
  140. package/tests/core/focus-history.test.ts +203 -11
  141. package/tests/core/merge-gate-audit.test.ts +2 -169
  142. package/tests/core/model-deployment-registry.test.ts +7 -1
  143. package/tests/core/model-training-registry.test.ts +19 -0
  144. package/tests/core/observability.test.ts +0 -1
  145. package/tests/core/pain-diagnostic-gate.test.ts +498 -0
  146. package/tests/core/pain.test.ts +0 -1
  147. package/tests/core/principle-internalization/deprecated-readiness.test.ts +2 -2
  148. package/tests/core/principle-internalization/lifecycle-metrics.test.ts +2 -2
  149. package/tests/core/principle-internalization/{internalization-routing-policy.test.ts → lifecycle-routing-policy.test.ts} +6 -6
  150. package/tests/core/principle-internalization/lineage-source-retired.test.ts +56 -0
  151. package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +1 -23
  152. package/tests/core/principle-tree-ledger-adapter.test.ts +253 -0
  153. package/tests/core/reflection-context.test.ts +0 -14
  154. package/tests/core/replay-engine.test.ts +127 -215
  155. package/tests/core/rule-host-helpers.test.ts +2 -2
  156. package/tests/core/rule-implementation-runtime.test.ts +0 -27
  157. package/tests/core/workflow-funnel-loader.test.ts +162 -0
  158. package/tests/core/workspace-dir-validation.test.ts +8 -1
  159. package/tests/core-anti-growth.test.ts +192 -0
  160. package/tests/hook-workspace-nextaction-contract.test.ts +42 -0
  161. package/tests/hooks/confirm-first-gate.test.ts +333 -0
  162. package/tests/hooks/gate-auto-correct-shadow.test.ts +310 -0
  163. package/tests/hooks/gate-auto-correct.test.ts +665 -0
  164. package/tests/hooks/gate-rule-host-pipeline.test.ts +2 -1
  165. package/tests/hooks/pain.test.ts +269 -12
  166. package/tests/hooks/prompt-characterization.test.ts +500 -0
  167. package/tests/hooks/prompt-size-guard.test.ts +32 -17
  168. package/tests/hooks/runtime-v2-prompt-activation.test.ts +869 -0
  169. package/tests/index.test.ts +94 -1
  170. package/tests/integration/auto-entry-gate.test.ts +248 -0
  171. package/tests/integration/internalization-trigger-guard.test.ts +69 -0
  172. package/tests/integration/m8-legacy-paths.test.ts +63 -0
  173. package/tests/integration/runtime-v2-pain-guard.test.ts +125 -0
  174. package/tests/plugin-config-resolution-cutover.test.ts +359 -0
  175. package/tests/runtime-v2-discovery-guard.test.ts +154 -0
  176. package/tests/service/central-database.test.ts +457 -0
  177. package/tests/service/evolution-worker.correction-observer.test.ts +173 -0
  178. package/tests/service/evolution-worker.timeout.test.ts +11 -129
  179. package/tests/service/internalization-trigger-adapter.test.ts +251 -0
  180. package/tests/service/monitoring-query-service.test.ts +1 -47
  181. package/tests/service/queue-io.test.ts +1 -62
  182. package/tests/service/runtime-summary-service.test.ts +3 -1
  183. package/tests/service/workflow-watchdog.test.ts +0 -91
  184. package/tests/utils/file-lock.test.ts +5 -3
  185. package/tests/utils/session-key.test.ts +52 -0
  186. package/tests/utils/subagent-probe.test.ts +48 -1
  187. package/vitest.config.ts +4 -11
  188. package/.planning/codebase/ARCHITECTURE.md +0 -157
  189. package/.planning/codebase/CONCERNS.md +0 -145
  190. package/.planning/codebase/CONVENTIONS.md +0 -148
  191. package/.planning/codebase/INTEGRATIONS.md +0 -81
  192. package/.planning/codebase/STACK.md +0 -87
  193. package/.planning/codebase/STRUCTURE.md +0 -193
  194. package/.planning/codebase/TESTING.md +0 -243
  195. package/.planning/phases/01-basic-visualization/01-GAP-CLOSURE-VERIFICATION.md +0 -113
  196. package/docs/COMMAND_REFERENCE.md +0 -76
  197. package/docs/COMMAND_REFERENCE_EN.md +0 -79
  198. package/scripts/build-web.mjs +0 -46
  199. package/scripts/diagnose-nocturnal.mjs +0 -537
  200. package/scripts/seed-nocturnal-scenarios.mjs +0 -384
  201. package/src/commands/nocturnal-review.ts +0 -322
  202. package/src/commands/nocturnal-rollout.ts +0 -790
  203. package/src/commands/nocturnal-train.ts +0 -986
  204. package/src/commands/pd-reflect.ts +0 -88
  205. package/src/core/adaptive-thresholds.ts +0 -478
  206. package/src/core/diagnostician-task-store.ts +0 -192
  207. package/src/core/nocturnal-arbiter.ts +0 -715
  208. package/src/core/nocturnal-artifact-lineage.ts +0 -116
  209. package/src/core/nocturnal-artificer.ts +0 -257
  210. package/src/core/nocturnal-candidate-scoring.ts +0 -530
  211. package/src/core/nocturnal-compliance.ts +0 -1146
  212. package/src/core/nocturnal-dataset.ts +0 -763
  213. package/src/core/nocturnal-executability.ts +0 -428
  214. package/src/core/nocturnal-export.ts +0 -499
  215. package/src/core/nocturnal-paths.ts +0 -240
  216. package/src/core/nocturnal-reasoning-deriver.ts +0 -343
  217. package/src/core/nocturnal-rule-implementation-validator.ts +0 -246
  218. package/src/core/nocturnal-snapshot-contract.ts +0 -99
  219. package/src/core/nocturnal-trajectory-extractor.ts +0 -512
  220. package/src/core/nocturnal-trinity-types.ts +0 -218
  221. package/src/core/nocturnal-trinity.ts +0 -2680
  222. package/src/core/principle-internalization/deprecated-readiness.ts +0 -93
  223. package/src/core/principle-internalization/internalization-routing-policy.ts +0 -208
  224. package/src/core/principle-internalization/lifecycle-metrics.ts +0 -152
  225. package/src/http/principles-console-route.ts +0 -709
  226. package/src/service/central-health-service.ts +0 -49
  227. package/src/service/central-overview-service.ts +0 -138
  228. package/src/service/control-ui-query-service.ts +0 -900
  229. package/src/service/cooldown-strategy.ts +0 -97
  230. package/src/service/evolution-pain-context.ts +0 -79
  231. package/src/service/evolution-query-service.ts +0 -407
  232. package/src/service/health-query-service.ts +0 -1038
  233. package/src/service/nocturnal-config.ts +0 -214
  234. package/src/service/nocturnal-runtime.ts +0 -734
  235. package/src/service/nocturnal-service.ts +0 -1605
  236. package/src/service/nocturnal-target-selector.ts +0 -545
  237. package/src/service/sleep-cycle.ts +0 -157
  238. package/src/service/startup-reconciler.ts +0 -112
  239. package/src/service/subagent-workflow/correction-observer-types.ts +0 -82
  240. package/src/service/subagent-workflow/correction-observer-workflow-manager.ts +0 -250
  241. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +0 -1
  242. package/src/service/subagent-workflow/dynamic-timeout.ts +0 -30
  243. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +0 -268
  244. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +0 -795
  245. package/src/service/subagent-workflow/runtime-direct-driver.ts +0 -268
  246. package/src/service/subagent-workflow/workflow-manager-base.ts +0 -580
  247. package/src/tools/write-pain-flag.ts +0 -215
  248. package/tests/commands/nocturnal-review.test.ts +0 -448
  249. package/tests/commands/nocturnal-train.test.ts +0 -97
  250. package/tests/commands/pd-reflect.test.ts +0 -49
  251. package/tests/core/adaptive-thresholds.test.ts +0 -261
  252. package/tests/core/nocturnal-arbiter.test.ts +0 -559
  253. package/tests/core/nocturnal-artifact-lineage.test.ts +0 -53
  254. package/tests/core/nocturnal-artificer.test.ts +0 -241
  255. package/tests/core/nocturnal-candidate-scoring.test.ts +0 -532
  256. package/tests/core/nocturnal-compliance-p-principles.test.ts +0 -133
  257. package/tests/core/nocturnal-compliance.test.ts +0 -646
  258. package/tests/core/nocturnal-dataset.test.ts +0 -892
  259. package/tests/core/nocturnal-e2e.test.ts +0 -234
  260. package/tests/core/nocturnal-executability.test.ts +0 -357
  261. package/tests/core/nocturnal-export.test.ts +0 -517
  262. package/tests/core/nocturnal-reasoning-deriver.test.ts +0 -372
  263. package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +0 -428
  264. package/tests/core/nocturnal-rule-implementation-validator.test.ts +0 -127
  265. package/tests/core/nocturnal-snapshot-contract.test.ts +0 -121
  266. package/tests/core/nocturnal-trajectory-extractor.test.ts +0 -634
  267. package/tests/core/nocturnal-trinity.test.ts +0 -2053
  268. package/tests/core/pain-auto-repair.test.ts +0 -96
  269. package/tests/core/pain-integration.test.ts +0 -510
  270. package/tests/fixtures/nocturnal-reviewed-subset.json +0 -183
  271. package/tests/http/principles-console-route.test.ts +0 -162
  272. package/tests/integration/chaos-resilience.test.ts +0 -348
  273. package/tests/integration/empathy-workflow-integration.test.ts +0 -626
  274. package/tests/integration/pain-diagnostician-loop.e2e.test.ts +0 -380
  275. package/tests/service/control-ui-query-service.test.ts +0 -121
  276. package/tests/service/cooldown-strategy.test.ts +0 -164
  277. package/tests/service/data-endpoints-regression.test.ts +0 -834
  278. package/tests/service/empathy-observer-workflow-manager.test.ts +0 -175
  279. package/tests/service/evolution-worker.nocturnal.test.ts +0 -601
  280. package/tests/service/nocturnal-runtime-hardening.test.ts +0 -118
  281. package/tests/service/nocturnal-runtime.test.ts +0 -473
  282. package/tests/service/nocturnal-service-code-candidate.test.ts +0 -330
  283. package/tests/service/nocturnal-target-selector.test.ts +0 -615
  284. package/tests/service/startup-reconciler.test.ts +0 -148
  285. package/tests/tools/write-pain-flag.test.ts +0 -358
  286. package/ui/src/App.tsx +0 -45
  287. package/ui/src/api.ts +0 -220
  288. package/ui/src/charts.tsx +0 -955
  289. package/ui/src/components/ErrorState.tsx +0 -6
  290. package/ui/src/components/Loading.tsx +0 -13
  291. package/ui/src/components/ProtectedRoute.tsx +0 -12
  292. package/ui/src/components/Shell.tsx +0 -91
  293. package/ui/src/components/WorkspaceConfig.tsx +0 -178
  294. package/ui/src/components/index.ts +0 -5
  295. package/ui/src/context/auth.tsx +0 -80
  296. package/ui/src/context/theme.tsx +0 -66
  297. package/ui/src/hooks/useAutoRefresh.ts +0 -39
  298. package/ui/src/i18n/ui.ts +0 -473
  299. package/ui/src/main.tsx +0 -16
  300. package/ui/src/pages/EvolutionPage.tsx +0 -333
  301. package/ui/src/pages/FeedbackPage.tsx +0 -138
  302. package/ui/src/pages/GateMonitorPage.tsx +0 -136
  303. package/ui/src/pages/LoginPage.tsx +0 -89
  304. package/ui/src/pages/OverviewPage.tsx +0 -599
  305. package/ui/src/pages/SamplesPage.tsx +0 -174
  306. package/ui/src/pages/ThinkingModelsPage.tsx +0 -702
  307. package/ui/src/styles.css +0 -2020
  308. package/ui/src/types.ts +0 -384
  309. package/ui/src/utils/format.ts +0 -15
@@ -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 nextStatus: PrincipleStatus = p.status === 'candidate' ? 'probation' : 'active';
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: nextStatus,
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);