principles-disciple 1.71.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 +469 -339
  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 +115 -18
  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 +329 -0
  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 +184 -3
  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
@@ -6,27 +6,28 @@ import {
6
6
  } from '../principle-tree-ledger.js';
7
7
  import type { PrincipleValueMetrics } from '../../types/principle-tree-schema.js';
8
8
  import { buildLifecycleReadModel, type LifecycleReadModel, type PrincipleLifecycleEvidence } from './lifecycle-read-model.js';
9
+ import { FilesystemLifecycleDatasource } from './filesystem-lifecycle-datasource.js';
9
10
  import {
10
11
  computePrincipleAdherence,
11
12
  computeRuleMetrics,
12
13
  type PrincipleAdherenceResult,
13
14
  type RuleMetricResult,
14
- } from './lifecycle-metrics.js';
15
+ } from '@principles/core/runtime-v2';
15
16
  import {
16
17
  assessDeprecatedReadiness,
17
18
  type DeprecatedReadinessAssessment,
18
- } from './deprecated-readiness.js';
19
+ } from '@principles/core/runtime-v2';
19
20
  import {
20
- recommendInternalizationRoute,
21
- type InternalizationRouteRecommendation,
22
- } from './internalization-routing-policy.js';
21
+ recommendLifecycleRoute,
22
+ type LifecycleRouteRecommendation,
23
+ } from '@principles/core/runtime-v2';
23
24
 
24
25
  export interface RecomputedPrincipleLifecycle {
25
26
  principleId: string;
26
27
  ruleMetrics: Record<string, RuleMetricResult>;
27
28
  adherence: PrincipleAdherenceResult;
28
29
  deprecatedReadiness: DeprecatedReadinessAssessment;
29
- routeRecommendation: InternalizationRouteRecommendation;
30
+ routeRecommendation: LifecycleRouteRecommendation;
30
31
  }
31
32
 
32
33
  export interface PrincipleLifecycleAssessment {
@@ -35,7 +36,7 @@ export interface PrincipleLifecycleAssessment {
35
36
  ruleMetrics: Record<string, RuleMetricResult>;
36
37
  adherence: PrincipleAdherenceResult;
37
38
  deprecatedReadiness: DeprecatedReadinessAssessment;
38
- routeRecommendation: InternalizationRouteRecommendation;
39
+ routeRecommendation: LifecycleRouteRecommendation;
39
40
  }
40
41
 
41
42
 
@@ -83,7 +84,8 @@ export class PrincipleLifecycleService {
83
84
  }
84
85
 
85
86
  buildReadModel(): LifecycleReadModel {
86
- return buildLifecycleReadModel(this.workspaceDir, this.stateDir);
87
+ const datasource = new FilesystemLifecycleDatasource(this.workspaceDir, this.stateDir);
88
+ return buildLifecycleReadModel(datasource);
87
89
  }
88
90
 
89
91
  listAssessments(readModel = this.buildReadModel()): PrincipleLifecycleAssessment[] {
@@ -100,7 +102,7 @@ export class PrincipleLifecycleService {
100
102
  ruleMetrics,
101
103
  adherence,
102
104
  );
103
- const routeRecommendation = recommendInternalizationRoute(
105
+ const routeRecommendation = recommendLifecycleRoute(
104
106
  principleEvidence,
105
107
  ruleMetrics,
106
108
  adherence,
@@ -117,7 +119,7 @@ export class PrincipleLifecycleService {
117
119
  });
118
120
  }
119
121
 
120
- listRouteRecommendations(readModel = this.buildReadModel()): InternalizationRouteRecommendation[] {
122
+ listRouteRecommendations(readModel = this.buildReadModel()): LifecycleRouteRecommendation[] {
121
123
  return this.listAssessments(readModel).map((assessment) => assessment.routeRecommendation);
122
124
  }
123
125
 
@@ -0,0 +1,145 @@
1
+ import { CandidateIntakeError, INTAKE_ERROR_CODES } from '@principles/core/runtime-v2';
2
+ import type { LedgerAdapter, LedgerPrincipleEntry } from '@principles/core/runtime-v2';
3
+ import { addPrincipleToLedger, loadLedger } from './principle-tree-ledger.js';
4
+ import type { LedgerPrinciple } from './principle-tree-ledger.js';
5
+
6
+ const VALID_EVALUABILITIES = ['deterministic', 'weak_heuristic', 'manual_only'] as const;
7
+
8
+ /**
9
+ * PrincipleTreeLedgerAdapter — bridges 11-field LedgerPrincipleEntry to 18+ field LedgerPrinciple.
10
+ *
11
+ * This adapter maintains an in-memory idempotency map. Create once and reuse
12
+ * across calls within the same process lifetime. Do not create a new instance
13
+ * per intake call.
14
+ */
15
+ export class PrincipleTreeLedgerAdapter implements LedgerAdapter {
16
+ #stateDir: string;
17
+ #entryMap = new Map<string, LedgerPrincipleEntry>();
18
+
19
+ constructor(opts: { stateDir: string }) {
20
+ this.#stateDir = opts.stateDir;
21
+ }
22
+
23
+ /**
24
+ * Write a probation entry to the ledger.
25
+ *
26
+ * Idempotency: if the same candidateId was already written, returns the
27
+ * existing entry without writing again.
28
+ *
29
+ * @throws CandidateIntakeError with code LEDGER_WRITE_FAILED on write failure.
30
+ */
31
+ writeProbationEntry(entry: LedgerPrincipleEntry): LedgerPrincipleEntry {
32
+ const candidateId = this.#extractCandidateId(entry.sourceRef);
33
+ const existing = this.#entryMap.get(candidateId);
34
+ if (existing) return existing;
35
+
36
+ const ledgerPrinciple = this.#expandToLedgerPrinciple(entry, candidateId);
37
+
38
+ try {
39
+ addPrincipleToLedger(this.#stateDir, ledgerPrinciple);
40
+ } catch (err) {
41
+ throw new CandidateIntakeError(
42
+ INTAKE_ERROR_CODES.LEDGER_WRITE_FAILED,
43
+ `Failed to write principle ${entry.id} to ledger: ${String(err)}`,
44
+ { cause: err },
45
+ );
46
+ }
47
+
48
+ this.#entryMap.set(candidateId, entry);
49
+ return entry;
50
+ }
51
+
52
+ /**
53
+ * Check if a ledger entry already exists for a given candidate.
54
+ *
55
+ * Queries both the in-memory Map (fast path for same-process repeat calls)
56
+ * and the ledger file (for cross-process idempotency across CLI invocations).
57
+ *
58
+ * @returns The existing LedgerPrincipleEntry if found, null otherwise.
59
+ */
60
+ existsForCandidate(candidateId: string): LedgerPrincipleEntry | null {
61
+ // Fast path: check in-memory Map (covers same-process repeat calls)
62
+ const cached = this.#entryMap.get(candidateId);
63
+ if (cached) return cached;
64
+
65
+ // Cross-process path: check ledger file by derivedFromPainIds
66
+ const ledger = loadLedger(this.#stateDir);
67
+ const found = Object.values(ledger.tree.principles).find((p) =>
68
+ p.derivedFromPainIds.includes(candidateId),
69
+ );
70
+ if (!found) return null;
71
+
72
+ // Reconstruct LedgerPrincipleEntry from LedgerPrinciple fields
73
+ // Note: sourceRef, artifactRef, taskRef are not stored in LedgerPrinciple,
74
+ // so we cannot fully reconstruct the original LedgerPrincipleEntry.
75
+ // Return minimal entry sufficient for idempotency signaling.
76
+ return {
77
+ id: found.id,
78
+ title: '',
79
+ status: 'probation' as const,
80
+ sourceRef: `candidate://${candidateId}`,
81
+ artifactRef: '',
82
+ taskRef: '',
83
+ text: found.text,
84
+ triggerPattern: found.triggerPattern,
85
+ action: found.action,
86
+ evaluability: 'weak_heuristic' as const,
87
+ createdAt: found.createdAt,
88
+ } as LedgerPrincipleEntry;
89
+ }
90
+
91
+ /**
92
+ * Extract candidateId from sourceRef by stripping the 'candidate://' prefix.
93
+ *
94
+ * @internal
95
+ */
96
+ #extractCandidateId(sourceRef: string): string {
97
+ if (sourceRef.startsWith('candidate://')) {
98
+ return sourceRef.slice('candidate://'.length);
99
+ }
100
+ return sourceRef;
101
+ }
102
+
103
+ /**
104
+ * Expand an 11-field LedgerPrincipleEntry to an 18+ field LedgerPrinciple.
105
+ *
106
+ * Applies the field expansion table from CONTEXT.md:
107
+ * - status: 'probation' → 'candidate'
108
+ * - triggerPattern/action: pass through (empty string if absent)
109
+ * - All defaults applied per CONTEXT.md expansion table
110
+ * - sourceRef/artifactRef/taskRef are NOT written to LedgerPrinciple (not stored)
111
+ * - title is NOT written to LedgerPrinciple — intentionally excluded; title
112
+ * is available in LedgerPrincipleEntry but LedgerPrinciple has no title field
113
+ *
114
+ * @internal
115
+ */
116
+ #expandToLedgerPrinciple(entry: LedgerPrincipleEntry, candidateId: string): LedgerPrinciple {
117
+ if (!VALID_EVALUABILITIES.includes(entry.evaluability as (typeof VALID_EVALUABILITIES)[number])) {
118
+ throw new CandidateIntakeError(
119
+ INTAKE_ERROR_CODES.INPUT_INVALID,
120
+ `Invalid evaluability value: ${entry.evaluability}. Must be one of: ${VALID_EVALUABILITIES.join(', ')}`,
121
+ );
122
+ }
123
+
124
+ const result: LedgerPrinciple = {
125
+ id: entry.id,
126
+ version: 1,
127
+ text: entry.text,
128
+ triggerPattern: entry.triggerPattern ?? '',
129
+ action: entry.action ?? '',
130
+ status: 'candidate',
131
+ evaluability: entry.evaluability,
132
+ priority: 'P1',
133
+ scope: 'general',
134
+ valueScore: 0,
135
+ adherenceRate: 0,
136
+ painPreventedCount: 0,
137
+ derivedFromPainIds: [candidateId],
138
+ ruleIds: [],
139
+ conflictsWithPrincipleIds: [],
140
+ createdAt: entry.createdAt,
141
+ updatedAt: entry.createdAt,
142
+ };
143
+ return result;
144
+ }
145
+ }
@@ -294,7 +294,11 @@ function readLedgerFromFile(filePath: string): HybridLedgerStore {
294
294
  }
295
295
 
296
296
  try {
297
- const parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8')) as unknown;
297
+ const content = fs.readFileSync(filePath, 'utf-8');
298
+ if (!content || content.trim() === '') {
299
+ return { trainingStore: {}, tree: createEmptyTree() };
300
+ }
301
+ const parsed = JSON.parse(content) as unknown;
298
302
  const raw = isRecord(parsed) ? parsed : {};
299
303
  // #219: Handle both formats:
300
304
  // - New format: { trainingStore: {...}, tree: {...} }
@@ -305,11 +309,9 @@ function readLedgerFromFile(filePath: string): HybridLedgerStore {
305
309
  trainingStore: parseLegacyTrainingStore(trainingStoreRaw),
306
310
  tree: parseTree(treeRaw),
307
311
  };
308
- } catch {
309
- return {
310
- trainingStore: {},
311
- tree: createEmptyTree(),
312
- };
312
+ } catch (err) {
313
+ console.error(`[principle-tree-ledger] Failed to load ledger from ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
314
+ return { trainingStore: {}, tree: createEmptyTree() };
313
315
  }
314
316
  }
315
317
 
@@ -1,62 +1,20 @@
1
- /**
2
- * ReflectionContextCollector — Unified Pipeline Input
3
- * ====================================================
4
- *
5
- * PURPOSE: Collect all grounding context for a principle into a single
6
- * ReflectionContext object. This is the input to the nocturnal reflection
7
- * pipeline: principle + painEvents + sessionSnapshot + lineage.
8
- *
9
- * DESIGN DECISIONS:
10
- * - If a principle has no derivedFromPainIds, collect() returns null
11
- * (nothing to ground code on).
12
- * - painId -> sessionId resolution is a known gap. For now, we attempt
13
- * best-effort lookup but return what we have with sessionSnapshot = null
14
- * if we can't resolve.
15
- *
16
- * REUSES:
17
- * - principle-tree-ledger: loadLedger() for principle lookup
18
- * - nocturnal-trajectory-extractor: for session snapshots
19
- * - trajectory: TrajectoryDatabase for pain event queries
20
- */
21
-
22
1
  import { loadLedger, type LedgerPrinciple } from '../principle-tree-ledger.js';
23
- import {
24
- NocturnalTrajectoryExtractor,
25
- type NocturnalPainEvent,
26
- type NocturnalSessionSnapshot,
27
- } from '../nocturnal-trajectory-extractor.js';
28
2
  import type { TrajectoryDatabase } from '../trajectory.js';
29
3
  import type { Principle } from '../../types/principle-tree-schema.js';
30
4
 
31
- // ---------------------------------------------------------------------------
32
- // Types
33
- // ---------------------------------------------------------------------------
34
-
35
- /**
36
- * Unified reflection context for the nocturnal pipeline.
37
- */
38
5
  export interface ReflectionContext {
39
- /** The principle being reflected upon */
40
6
  principle: Principle;
41
- /** Pain events associated with this principle (via derivedFromPainIds) */
42
- painEvents: NocturnalPainEvent[];
43
- /** Session snapshot if resolvable, null otherwise */
44
- sessionSnapshot: NocturnalSessionSnapshot | null;
45
- /** Lineage metadata connecting principle to source pain signals */
46
- lineage: {
47
- sourcePainIds: string[];
48
- sessionId: string | null;
49
- };
7
+ painEvents: Array<{ source: string; score: number; severity: string | null; reason: string | null; createdAt: string }>;
8
+ sessionSnapshot: {
9
+ toolCalls: Array<{
10
+ toolName: string;
11
+ outcome: string;
12
+ filePath: string | null;
13
+ errorType: string | null;
14
+ }>;
15
+ } | null;
50
16
  }
51
17
 
52
- // ---------------------------------------------------------------------------
53
- // Collector
54
- // ---------------------------------------------------------------------------
55
-
56
- /**
57
- * Collects ReflectionContext for principles by joining ledger data with
58
- * trajectory data.
59
- */
60
18
  export class ReflectionContextCollector {
61
19
  private readonly stateDir: string;
62
20
  private readonly trajectory: TrajectoryDatabase;
@@ -66,13 +24,6 @@ export class ReflectionContextCollector {
66
24
  this.trajectory = trajectory;
67
25
  }
68
26
 
69
- /**
70
- * Collect full reflection context for a single principle.
71
- *
72
- * Returns null if:
73
- * - The principle is not found in the ledger
74
- * - The principle has no derivedFromPainIds (nothing to ground on)
75
- */
76
27
  collect(principleId: string): ReflectionContext | null {
77
28
  const ledger = loadLedger(this.stateDir);
78
29
  const principle = ledger.tree.principles[principleId];
@@ -88,11 +39,6 @@ export class ReflectionContextCollector {
88
39
  return this.buildContext(principle);
89
40
  }
90
41
 
91
- /**
92
- * Collect reflection contexts for multiple principles, optionally filtered.
93
- *
94
- * Skips principles without derivedFromPainIds.
95
- */
96
42
  collectBatch(filter?: { status?: string }): ReflectionContext[] {
97
43
  const ledger = loadLedger(this.stateDir);
98
44
  const principles = Object.values(ledger.tree.principles);
@@ -100,12 +46,10 @@ export class ReflectionContextCollector {
100
46
  const results: ReflectionContext[] = [];
101
47
 
102
48
  for (const principle of principles) {
103
- // Apply status filter if provided
104
49
  if (filter?.status && principle.status !== filter.status) {
105
50
  continue;
106
51
  }
107
52
 
108
- // Skip principles without pain grounding
109
53
  if (!principle.derivedFromPainIds || principle.derivedFromPainIds.length === 0) {
110
54
  continue;
111
55
  }
@@ -119,62 +63,26 @@ export class ReflectionContextCollector {
119
63
  return results;
120
64
  }
121
65
 
122
- // -----------------------------------------------------------------------
123
- // Private helpers
124
- // -----------------------------------------------------------------------
125
-
126
- /**
127
- * Build a ReflectionContext from a principle.
128
- *
129
- * Attempts to resolve painIds to sessions via best-effort lookup.
130
- * Since painId -> sessionId mapping is a known gap, we may return
131
- * empty painEvents and null sessionSnapshot.
132
- */
133
66
  private buildContext(principle: LedgerPrinciple): ReflectionContext {
134
67
  const sourcePainIds = principle.derivedFromPainIds;
135
-
136
- // Best-effort: try to find a session containing pain events related to this principle.
137
- // The pain_events table uses auto-increment IDs, not the string painIds stored in
138
- // derivedFromPainIds. This is the known gap — for now we attempt session resolution
139
- // but gracefully handle the case where we can't match.
140
- const { painEvents, sessionId } = this.resolvePainEvents(sourcePainIds);
141
-
142
- // If we found a session, get the snapshot
143
- let sessionSnapshot: NocturnalSessionSnapshot | null = null;
144
- if (sessionId) {
145
- const extractor = new NocturnalTrajectoryExtractor(this.trajectory);
146
- sessionSnapshot = extractor.getNocturnalSessionSnapshot(sessionId);
147
- }
68
+ const { painEvents } = this.resolvePainEvents(sourcePainIds);
148
69
 
149
70
  return {
150
71
  principle,
151
72
  painEvents,
152
- sessionSnapshot,
153
- lineage: {
154
- sourcePainIds,
155
- sessionId,
156
- },
73
+ sessionSnapshot: null,
157
74
  };
158
75
  }
159
76
 
160
- /**
161
- * Attempt to resolve painIds to actual pain events and a session.
162
- *
163
- * Two-phase strategy:
164
- * 1. Exact ID match: sourcePainIds are stringified pain_events row IDs.
165
- * If any match String(pe.id) exactly, use those and stop.
166
- * 2. Heuristic fallback: substring match on reason/origin fields.
167
- * Only used when no exact matches are found.
168
- */
169
77
  private resolvePainEvents(sourcePainIds: string[]): {
170
- painEvents: NocturnalPainEvent[];
78
+ painEvents: Array<{ source: string; score: number; severity: string | null; reason: string | null; createdAt: string }>;
171
79
  sessionId: string | null;
172
80
  } {
173
81
  const sessions = this.trajectory.listRecentSessions({ limit: 100 });
174
82
  const sourcePainIdSet = new Set(sourcePainIds);
175
83
 
176
- const exactMatches: NocturnalPainEvent[] = [];
177
- const heuristicMatches: NocturnalPainEvent[] = [];
84
+ const exactMatches: Array<{ source: string; score: number; severity: string | null; reason: string | null; createdAt: string }> = [];
85
+ const heuristicMatches: Array<{ source: string; score: number; severity: string | null; reason: string | null; createdAt: string }> = [];
178
86
  let exactSessionId: string | null = null;
179
87
  let heuristicSessionId: string | null = null;
180
88
 
@@ -182,7 +90,6 @@ export class ReflectionContextCollector {
182
90
  const sessionPainEvents = this.trajectory.listPainEventsForSession(session.sessionId);
183
91
 
184
92
  for (const pe of sessionPainEvents) {
185
- // Phase 1: exact ID match
186
93
  if (sourcePainIdSet.has(String(pe.id))) {
187
94
  exactMatches.push({
188
95
  source: pe.source,
@@ -197,7 +104,6 @@ export class ReflectionContextCollector {
197
104
  continue;
198
105
  }
199
106
 
200
- // Phase 2: heuristic substring match on reason/origin only
201
107
  const peText = [pe.reason, pe.origin].filter(Boolean);
202
108
  const isMatch = sourcePainIds.some((painId) =>
203
109
  peText.some((field) => field?.includes(painId)),
@@ -218,7 +124,6 @@ export class ReflectionContextCollector {
218
124
  }
219
125
  }
220
126
 
221
- // Prefer exact matches over heuristic matches
222
127
  if (exactMatches.length > 0) {
223
128
  return { painEvents: exactMatches, sessionId: exactSessionId };
224
129
  }