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.
Files changed (319) hide show
  1. package/INSTALL.md +1 -3
  2. package/openclaw.plugin.json +10 -5
  3. package/package.json +17 -19
  4. package/scripts/acceptance-test.mjs +16 -73
  5. package/scripts/sync-plugin.mjs +382 -77
  6. package/src/commands/archive-impl.ts +2 -1
  7. package/src/commands/capabilities.ts +2 -2
  8. package/src/commands/context.ts +2 -2
  9. package/src/commands/disable-impl.ts +2 -1
  10. package/src/commands/evolution-status.ts +16 -16
  11. package/src/commands/export.ts +12 -67
  12. package/src/commands/pain.ts +91 -1
  13. package/src/commands/principle-rollback.ts +2 -1
  14. package/src/commands/promote-impl.ts +7 -43
  15. package/src/commands/rollback-impl.ts +2 -1
  16. package/src/commands/rollback.ts +2 -1
  17. package/src/commands/samples.ts +2 -1
  18. package/src/commands/thinking-os.ts +2 -1
  19. package/src/config/errors.ts +18 -2
  20. package/src/constants/diagnostician.ts +2 -2
  21. package/src/constants/tools.ts +2 -1
  22. package/src/core/__tests__/focus-history.test.ts +210 -0
  23. package/src/core/config.ts +1 -1
  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 +29 -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/migration.ts +0 -1
  37. package/src/core/pain-diagnostic-gate.ts +154 -0
  38. package/src/core/pain-signal.ts +21 -138
  39. package/src/core/pain.ts +15 -88
  40. package/src/core/path-resolver.ts +0 -1
  41. package/src/core/paths.ts +0 -1
  42. package/src/core/pd-task-reconciler.ts +26 -115
  43. package/src/core/pd-task-service.ts +9 -9
  44. package/src/core/pd-task-types.ts +23 -127
  45. package/src/core/principle-compiler/__tests__/compiler-replay-gate.test.ts +174 -0
  46. package/src/core/principle-compiler/code-validator.ts +15 -42
  47. package/src/core/principle-compiler/compiler.ts +100 -15
  48. package/src/core/principle-compiler/index.ts +5 -2
  49. package/src/core/principle-compiler/template-generator.ts +4 -104
  50. package/src/core/principle-injection.ts +10 -202
  51. package/src/core/principle-internalization/filesystem-lifecycle-datasource.ts +42 -0
  52. package/src/core/principle-internalization/lifecycle-read-model.ts +39 -242
  53. package/src/core/principle-internalization/principle-lifecycle-service.ts +12 -10
  54. package/src/core/principle-tree-ledger-adapter.ts +145 -0
  55. package/src/core/principle-tree-ledger.ts +8 -6
  56. package/src/core/reflection/reflection-context.ts +14 -109
  57. package/src/core/replay-engine.ts +8 -500
  58. package/src/core/rule-host-helpers.ts +5 -35
  59. package/src/core/rule-host-types.ts +10 -82
  60. package/src/core/rule-host.ts +6 -63
  61. package/src/core/runtime-v2-prompt-activation-reader.ts +231 -0
  62. package/src/core/session-tracker.ts +87 -101
  63. package/src/core/shadow-observation-registry.ts +19 -48
  64. package/src/core/trajectory.ts +3 -1
  65. package/src/core/workflow-funnel-loader.ts +62 -68
  66. package/src/core/workspace-context.ts +46 -0
  67. package/src/core/workspace-dir-service.ts +1 -1
  68. package/src/core/workspace-dir-validation.ts +18 -9
  69. package/src/hooks/AGENTS.md +1 -1
  70. package/src/hooks/gate-block-helper.ts +71 -64
  71. package/src/hooks/gate.ts +183 -31
  72. package/src/hooks/lifecycle.ts +30 -32
  73. package/src/hooks/llm.ts +60 -32
  74. package/src/hooks/pain.ts +297 -103
  75. package/src/hooks/prompt.ts +400 -440
  76. package/src/hooks/subagent.ts +2 -29
  77. package/src/i18n/commands.ts +2 -10
  78. package/src/index.ts +95 -85
  79. package/src/openclaw-sdk.ts +311 -0
  80. package/src/service/central-database.ts +8 -4
  81. package/src/service/evolution-queue-migration.ts +2 -1
  82. package/src/service/evolution-worker.ts +163 -1786
  83. package/src/service/internalization-trigger-adapter.ts +302 -0
  84. package/src/service/keyword-optimization-service.ts +4 -4
  85. package/src/service/monitoring-query-service.ts +1 -215
  86. package/src/service/queue-io.ts +60 -331
  87. package/src/service/runtime-summary-service.ts +59 -16
  88. package/src/service/subagent-workflow/index.ts +0 -41
  89. package/src/service/subagent-workflow/types.ts +9 -120
  90. package/src/service/subagent-workflow/workflow-store.ts +2 -119
  91. package/src/service/workflow-watchdog.ts +0 -43
  92. package/src/types/event-payload.ts +16 -74
  93. package/src/types/event-types.ts +38 -547
  94. package/src/types/hygiene-types.ts +7 -30
  95. package/src/types/principle-tree-schema.ts +20 -222
  96. package/src/types/queue.ts +15 -70
  97. package/src/types/runtime-summary.ts +5 -49
  98. package/src/utils/io.ts +8 -20
  99. package/src/utils/retry.ts +1 -1
  100. package/src/utils/shadow-fingerprint.ts +2 -2
  101. package/src/utils/workspace-resolver.ts +50 -0
  102. package/templates/langs/en/core/AGENTS.md +7 -7
  103. package/templates/langs/en/core/BOOT.md +1 -1
  104. package/templates/langs/en/core/HEARTBEAT.md +2 -2
  105. package/templates/langs/en/principles/THINKING_OS.md +3 -2
  106. package/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
  107. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
  108. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
  109. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
  110. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
  111. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
  112. package/templates/langs/en/skills/evolve-task/SKILL.md +3 -3
  113. package/templates/langs/en/skills/pd-cli-operator/SKILL.md +67 -0
  114. package/templates/langs/en/skills/pd-diagnostician/SKILL.md +1 -1
  115. package/templates/langs/en/skills/pd-mentor/SKILL.md +2 -3
  116. package/templates/langs/en/skills/pd-pain-signal/SKILL.md +17 -39
  117. package/templates/langs/en/skills/pd-runtime-v2/SKILL.md +61 -0
  118. package/templates/langs/zh/core/AGENTS.md +7 -7
  119. package/templates/langs/zh/core/BOOT.md +1 -1
  120. package/templates/langs/zh/core/HEARTBEAT.md +2 -2
  121. package/templates/langs/zh/principles/THINKING_OS.md +3 -2
  122. package/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
  123. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
  124. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
  125. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +8 -8
  126. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
  127. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
  128. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
  129. package/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +21 -5
  130. package/templates/langs/zh/skills/evolve-task/SKILL.md +4 -4
  131. package/templates/langs/zh/skills/pd-cli-operator/SKILL.md +67 -0
  132. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +1 -1
  133. package/templates/langs/zh/skills/pd-mentor/SKILL.md +2 -3
  134. package/templates/langs/zh/skills/pd-pain-signal/SKILL.md +17 -38
  135. package/templates/langs/zh/skills/pd-runtime-v2/SKILL.md +61 -0
  136. package/tests/build-artifacts.test.ts +1 -3
  137. package/tests/commands/evolution-status.test.ts +0 -118
  138. package/tests/core/bootstrap-rules.test.ts +1 -1
  139. package/tests/core/config.test.ts +1 -1
  140. package/tests/core/event-log.test.ts +35 -0
  141. package/tests/core/evolution-engine.test.ts +610 -0
  142. package/tests/core/file-store.test.ts +102 -0
  143. package/tests/core/focus-history.test.ts +203 -11
  144. package/tests/core/merge-gate-audit.test.ts +2 -169
  145. package/tests/core/migration.test.ts +7 -7
  146. package/tests/core/model-deployment-registry.test.ts +7 -1
  147. package/tests/core/model-training-registry.test.ts +19 -0
  148. package/tests/core/observability.test.ts +0 -1
  149. package/tests/core/pain-diagnostic-gate.test.ts +498 -0
  150. package/tests/core/pain.test.ts +0 -1
  151. package/tests/core/path-resolver.test.ts +1 -1
  152. package/tests/core/paths-refactor.test.ts +0 -22
  153. package/tests/core/principle-internalization/deprecated-readiness.test.ts +2 -2
  154. package/tests/core/principle-internalization/lifecycle-metrics.test.ts +2 -2
  155. package/tests/core/principle-internalization/{internalization-routing-policy.test.ts → lifecycle-routing-policy.test.ts} +6 -6
  156. package/tests/core/principle-internalization/lineage-source-retired.test.ts +56 -0
  157. package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +1 -23
  158. package/tests/core/principle-tree-ledger-adapter.test.ts +253 -0
  159. package/tests/core/reflection-context.test.ts +0 -14
  160. package/tests/core/replay-engine.test.ts +127 -215
  161. package/tests/core/rule-host-helpers.test.ts +2 -2
  162. package/tests/core/rule-implementation-runtime.test.ts +0 -27
  163. package/tests/core/workflow-funnel-loader.test.ts +162 -0
  164. package/tests/core/workspace-context.test.ts +2 -2
  165. package/tests/core/workspace-dir-validation.test.ts +8 -1
  166. package/tests/core-anti-growth.test.ts +191 -0
  167. package/tests/hook-workspace-nextaction-contract.test.ts +42 -0
  168. package/tests/hooks/confirm-first-removal.test.ts +188 -0
  169. package/tests/hooks/gate-auto-correct-shadow.test.ts +310 -0
  170. package/tests/hooks/gate-auto-correct.test.ts +665 -0
  171. package/tests/hooks/gate-no-path-write-tool.test.ts +172 -0
  172. package/tests/hooks/gate-rule-host-pipeline.test.ts +2 -1
  173. package/tests/hooks/pain.test.ts +269 -12
  174. package/tests/hooks/prompt-characterization.test.ts +500 -0
  175. package/tests/hooks/prompt-size-guard.test.ts +32 -17
  176. package/tests/hooks/runtime-v2-prompt-activation.test.ts +869 -0
  177. package/tests/index.test.ts +94 -1
  178. package/tests/integration/auto-entry-gate.test.ts +248 -0
  179. package/tests/integration/internalization-trigger-guard.test.ts +69 -0
  180. package/tests/integration/m8-legacy-paths.test.ts +63 -0
  181. package/tests/integration/runtime-v2-pain-guard.test.ts +125 -0
  182. package/tests/plugin-config-resolution-cutover.test.ts +359 -0
  183. package/tests/runtime-v2-discovery-guard.test.ts +154 -0
  184. package/tests/service/central-database.test.ts +457 -0
  185. package/tests/service/evolution-worker.correction-observer.test.ts +173 -0
  186. package/tests/service/evolution-worker.timeout.test.ts +11 -129
  187. package/tests/service/internalization-trigger-adapter.test.ts +251 -0
  188. package/tests/service/monitoring-query-service.test.ts +1 -47
  189. package/tests/service/queue-io.test.ts +1 -62
  190. package/tests/service/runtime-summary-service.test.ts +3 -1
  191. package/tests/service/workflow-watchdog.test.ts +0 -91
  192. package/tests/utils/file-lock.test.ts +5 -3
  193. package/tests/utils/session-key.test.ts +52 -0
  194. package/tests/utils/subagent-probe.test.ts +48 -1
  195. package/vitest.config.ts +4 -11
  196. package/.planning/codebase/ARCHITECTURE.md +0 -157
  197. package/.planning/codebase/CONCERNS.md +0 -145
  198. package/.planning/codebase/CONVENTIONS.md +0 -148
  199. package/.planning/codebase/INTEGRATIONS.md +0 -81
  200. package/.planning/codebase/STACK.md +0 -87
  201. package/.planning/codebase/STRUCTURE.md +0 -193
  202. package/.planning/codebase/TESTING.md +0 -243
  203. package/.planning/phases/01-basic-visualization/01-GAP-CLOSURE-VERIFICATION.md +0 -113
  204. package/docs/COMMAND_REFERENCE.md +0 -76
  205. package/docs/COMMAND_REFERENCE_EN.md +0 -79
  206. package/scripts/build-web.mjs +0 -46
  207. package/scripts/diagnose-nocturnal.mjs +0 -537
  208. package/scripts/seed-nocturnal-scenarios.mjs +0 -384
  209. package/src/commands/nocturnal-review.ts +0 -322
  210. package/src/commands/nocturnal-rollout.ts +0 -790
  211. package/src/commands/nocturnal-train.ts +0 -986
  212. package/src/commands/pd-reflect.ts +0 -88
  213. package/src/core/adaptive-thresholds.ts +0 -478
  214. package/src/core/diagnostician-task-store.ts +0 -192
  215. package/src/core/nocturnal-arbiter.ts +0 -715
  216. package/src/core/nocturnal-artifact-lineage.ts +0 -116
  217. package/src/core/nocturnal-artificer.ts +0 -257
  218. package/src/core/nocturnal-candidate-scoring.ts +0 -530
  219. package/src/core/nocturnal-compliance.ts +0 -1146
  220. package/src/core/nocturnal-dataset.ts +0 -763
  221. package/src/core/nocturnal-executability.ts +0 -428
  222. package/src/core/nocturnal-export.ts +0 -499
  223. package/src/core/nocturnal-paths.ts +0 -240
  224. package/src/core/nocturnal-reasoning-deriver.ts +0 -343
  225. package/src/core/nocturnal-rule-implementation-validator.ts +0 -246
  226. package/src/core/nocturnal-snapshot-contract.ts +0 -99
  227. package/src/core/nocturnal-trajectory-extractor.ts +0 -512
  228. package/src/core/nocturnal-trinity-types.ts +0 -218
  229. package/src/core/nocturnal-trinity.ts +0 -2680
  230. package/src/core/principle-internalization/deprecated-readiness.ts +0 -93
  231. package/src/core/principle-internalization/internalization-routing-policy.ts +0 -208
  232. package/src/core/principle-internalization/lifecycle-metrics.ts +0 -152
  233. package/src/http/principles-console-route.ts +0 -709
  234. package/src/service/central-health-service.ts +0 -49
  235. package/src/service/central-overview-service.ts +0 -138
  236. package/src/service/control-ui-query-service.ts +0 -900
  237. package/src/service/cooldown-strategy.ts +0 -97
  238. package/src/service/evolution-pain-context.ts +0 -79
  239. package/src/service/evolution-query-service.ts +0 -407
  240. package/src/service/health-query-service.ts +0 -1038
  241. package/src/service/nocturnal-config.ts +0 -214
  242. package/src/service/nocturnal-runtime.ts +0 -734
  243. package/src/service/nocturnal-service.ts +0 -1605
  244. package/src/service/nocturnal-target-selector.ts +0 -545
  245. package/src/service/sleep-cycle.ts +0 -157
  246. package/src/service/startup-reconciler.ts +0 -112
  247. package/src/service/subagent-workflow/correction-observer-types.ts +0 -82
  248. package/src/service/subagent-workflow/correction-observer-workflow-manager.ts +0 -250
  249. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +0 -1
  250. package/src/service/subagent-workflow/dynamic-timeout.ts +0 -30
  251. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +0 -268
  252. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +0 -795
  253. package/src/service/subagent-workflow/runtime-direct-driver.ts +0 -268
  254. package/src/service/subagent-workflow/workflow-manager-base.ts +0 -580
  255. package/src/tools/write-pain-flag.ts +0 -215
  256. package/templates/langs/en/skills/plan-script/SKILL.md +0 -32
  257. package/templates/langs/zh/skills/plan-script/SKILL.md +0 -32
  258. package/tests/commands/nocturnal-review.test.ts +0 -448
  259. package/tests/commands/nocturnal-train.test.ts +0 -97
  260. package/tests/commands/pd-reflect.test.ts +0 -49
  261. package/tests/core/adaptive-thresholds.test.ts +0 -261
  262. package/tests/core/nocturnal-arbiter.test.ts +0 -559
  263. package/tests/core/nocturnal-artifact-lineage.test.ts +0 -53
  264. package/tests/core/nocturnal-artificer.test.ts +0 -241
  265. package/tests/core/nocturnal-candidate-scoring.test.ts +0 -532
  266. package/tests/core/nocturnal-compliance-p-principles.test.ts +0 -133
  267. package/tests/core/nocturnal-compliance.test.ts +0 -646
  268. package/tests/core/nocturnal-dataset.test.ts +0 -892
  269. package/tests/core/nocturnal-e2e.test.ts +0 -234
  270. package/tests/core/nocturnal-executability.test.ts +0 -357
  271. package/tests/core/nocturnal-export.test.ts +0 -517
  272. package/tests/core/nocturnal-reasoning-deriver.test.ts +0 -372
  273. package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +0 -428
  274. package/tests/core/nocturnal-rule-implementation-validator.test.ts +0 -127
  275. package/tests/core/nocturnal-snapshot-contract.test.ts +0 -121
  276. package/tests/core/nocturnal-trajectory-extractor.test.ts +0 -634
  277. package/tests/core/nocturnal-trinity.test.ts +0 -2053
  278. package/tests/core/pain-auto-repair.test.ts +0 -96
  279. package/tests/core/pain-integration.test.ts +0 -510
  280. package/tests/fixtures/nocturnal-reviewed-subset.json +0 -183
  281. package/tests/http/principles-console-route.test.ts +0 -162
  282. package/tests/integration/chaos-resilience.test.ts +0 -348
  283. package/tests/integration/empathy-workflow-integration.test.ts +0 -626
  284. package/tests/integration/pain-diagnostician-loop.e2e.test.ts +0 -380
  285. package/tests/service/control-ui-query-service.test.ts +0 -121
  286. package/tests/service/cooldown-strategy.test.ts +0 -164
  287. package/tests/service/data-endpoints-regression.test.ts +0 -834
  288. package/tests/service/empathy-observer-workflow-manager.test.ts +0 -175
  289. package/tests/service/evolution-worker.nocturnal.test.ts +0 -601
  290. package/tests/service/nocturnal-runtime-hardening.test.ts +0 -118
  291. package/tests/service/nocturnal-runtime.test.ts +0 -473
  292. package/tests/service/nocturnal-service-code-candidate.test.ts +0 -330
  293. package/tests/service/nocturnal-target-selector.test.ts +0 -615
  294. package/tests/service/startup-reconciler.test.ts +0 -148
  295. package/tests/tools/write-pain-flag.test.ts +0 -358
  296. package/ui/src/App.tsx +0 -45
  297. package/ui/src/api.ts +0 -220
  298. package/ui/src/charts.tsx +0 -955
  299. package/ui/src/components/ErrorState.tsx +0 -6
  300. package/ui/src/components/Loading.tsx +0 -13
  301. package/ui/src/components/ProtectedRoute.tsx +0 -12
  302. package/ui/src/components/Shell.tsx +0 -91
  303. package/ui/src/components/WorkspaceConfig.tsx +0 -178
  304. package/ui/src/components/index.ts +0 -5
  305. package/ui/src/context/auth.tsx +0 -80
  306. package/ui/src/context/theme.tsx +0 -66
  307. package/ui/src/hooks/useAutoRefresh.ts +0 -39
  308. package/ui/src/i18n/ui.ts +0 -473
  309. package/ui/src/main.tsx +0 -16
  310. package/ui/src/pages/EvolutionPage.tsx +0 -333
  311. package/ui/src/pages/FeedbackPage.tsx +0 -138
  312. package/ui/src/pages/GateMonitorPage.tsx +0 -136
  313. package/ui/src/pages/LoginPage.tsx +0 -89
  314. package/ui/src/pages/OverviewPage.tsx +0 -599
  315. package/ui/src/pages/SamplesPage.tsx +0 -174
  316. package/ui/src/pages/ThinkingModelsPage.tsx +0 -702
  317. package/ui/src/styles.css +0 -2020
  318. package/ui/src/types.ts +0 -384
  319. package/ui/src/utils/format.ts +0 -15
@@ -1,9 +1,9 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { assessDeprecatedReadiness } from '../../../src/core/principle-internalization/deprecated-readiness.js';
2
+ import { assessDeprecatedReadiness } from '@principles/core/runtime-v2';
3
3
  import type {
4
4
  PrincipleLifecycleEvidence,
5
5
  RuleLifecycleEvidence,
6
- } from '../../../src/core/principle-internalization/lifecycle-read-model.js';
6
+ } from '@principles/core/runtime-v2';
7
7
 
8
8
  function createRuleEvidence(
9
9
  id: string,
@@ -2,13 +2,13 @@ import { describe, expect, it } from 'vitest';
2
2
  import {
3
3
  computePrincipleAdherence,
4
4
  computeRuleMetrics,
5
- } from '../../../src/core/principle-internalization/lifecycle-metrics.js';
5
+ } from '@principles/core/runtime-v2';
6
6
  import type {
7
7
  PrincipleLifecycleEvidence,
8
8
  RuleLifecycleEvidence,
9
9
  } from '../../../src/core/principle-internalization/lifecycle-read-model.js';
10
10
  import type { ReplayReport } from '../../../src/core/replay-engine.js';
11
- import type { ArtifactLineageRecord } from '../../../src/core/nocturnal-artifact-lineage.js';
11
+ import type { ArtifactLineageRecord } from '@principles/core/runtime-v2';
12
12
 
13
13
  function createReplayReport(
14
14
  implementationId: string,
@@ -1,9 +1,9 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { recommendInternalizationRoute } from '../../../src/core/principle-internalization/internalization-routing-policy.js';
2
+ import { recommendLifecycleRoute } from '@principles/core/runtime-v2';
3
3
  import type {
4
4
  PrincipleLifecycleEvidence,
5
5
  RuleLifecycleEvidence,
6
- } from '../../../src/core/principle-internalization/lifecycle-read-model.js';
6
+ } from '@principles/core/runtime-v2';
7
7
 
8
8
  function createRuleEvidence(id: string, overrides: Partial<RuleLifecycleEvidence> = {}): RuleLifecycleEvidence {
9
9
  return {
@@ -94,9 +94,9 @@ function createPrincipleEvidence(
94
94
  };
95
95
  }
96
96
 
97
- describe('internalization-routing-policy', () => {
97
+ describe('recommendLifecycleRoute', () => {
98
98
  it('recommends code for deterministic high-risk principles with repeated failures and sufficient replay evidence', () => {
99
- const recommendation = recommendInternalizationRoute(
99
+ const recommendation = recommendLifecycleRoute(
100
100
  createPrincipleEvidence(
101
101
  [
102
102
  createRuleEvidence('R-001', {
@@ -156,7 +156,7 @@ describe('internalization-routing-policy', () => {
156
156
  });
157
157
 
158
158
  it('recommends skill when a cheaper non-code path is viable', () => {
159
- const recommendation = recommendInternalizationRoute(
159
+ const recommendation = recommendLifecycleRoute(
160
160
  createPrincipleEvidence([
161
161
  createRuleEvidence('R-001', {
162
162
  rule: {
@@ -201,7 +201,7 @@ describe('internalization-routing-policy', () => {
201
201
  });
202
202
 
203
203
  it('recommends defer when evidence is too sparse to choose a route confidently', () => {
204
- const recommendation = recommendInternalizationRoute(
204
+ const recommendation = recommendLifecycleRoute(
205
205
  createPrincipleEvidence([createRuleEvidence('R-001')]),
206
206
  );
207
207
 
@@ -0,0 +1,56 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { FilesystemLifecycleDatasource, LineageSourceRetiredError } from '../../../src/core/principle-internalization/filesystem-lifecycle-datasource.js';
3
+ import { buildLifecycleReadModel } from '@principles/core/runtime-v2';
4
+
5
+ describe('LineageSourceRetiredError regression guard (PRI-230)', () => {
6
+ it('listLineageRecords throws LineageSourceRetiredError, not return []', () => {
7
+ const ds = new FilesystemLifecycleDatasource('/tmp/nonexistent', '/tmp/nonexistent');
8
+ expect(() => ds.listLineageRecords('rule-implementation-candidate')).toThrow(LineageSourceRetiredError);
9
+ });
10
+
11
+ it('LineageSourceRetiredError message mentions PRI-230 and nocturnal-artifact-lineage', () => {
12
+ const ds = new FilesystemLifecycleDatasource('/tmp/nonexistent', '/tmp/nonexistent');
13
+ try {
14
+ ds.listLineageRecords('rule-implementation-candidate');
15
+ expect.unreachable('Should have thrown');
16
+ } catch (err) {
17
+ expect(err).toBeInstanceOf(LineageSourceRetiredError);
18
+ const message = (err as LineageSourceRetiredError).message;
19
+ expect(message).toContain('PRI-230');
20
+ expect(message).toContain('nocturnal-artifact-lineage');
21
+ }
22
+ });
23
+
24
+ it('LineageSourceRetiredError has correct name property', () => {
25
+ const err = new LineageSourceRetiredError();
26
+ expect(err.name).toBe('LineageSourceRetiredError');
27
+ });
28
+
29
+ it('retired datasource does not return empty array that could be misinterpreted as "no lineage"', () => {
30
+ const ds = new FilesystemLifecycleDatasource('/tmp/nonexistent', '/tmp/nonexistent');
31
+ let returnedEmptyArray = false;
32
+ try {
33
+ const result = ds.listLineageRecords('rule-implementation-candidate');
34
+ returnedEmptyArray = Array.isArray(result) && result.length === 0;
35
+ } catch {
36
+ // expected
37
+ }
38
+ expect(returnedEmptyArray).toBe(false);
39
+ });
40
+ });
41
+
42
+ describe('buildLifecycleReadModel with retired lineage source (PRI-230)', () => {
43
+ it('produces lineageEvidence with sourceRetired=true when datasource throws LineageSourceRetiredError', () => {
44
+ const ds = new FilesystemLifecycleDatasource('/tmp/nonexistent', '/tmp/nonexistent');
45
+ let model: ReturnType<typeof buildLifecycleReadModel>;
46
+ expect(() => {
47
+ model = buildLifecycleReadModel(ds);
48
+ }).not.toThrow();
49
+
50
+ const rules = model!.principles.flatMap((p) => p.rules);
51
+ for (const rule of rules) {
52
+ expect(rule.lineageEvidence.sourceRetired).toBe(true);
53
+ expect(rule.lineageEvidence.records).toEqual([]);
54
+ }
55
+ });
56
+ });
@@ -3,7 +3,6 @@ import * as fs from 'fs';
3
3
  import * as os from 'os';
4
4
  import * as path from 'path';
5
5
  import { PrincipleLifecycleService } from '../../../src/core/principle-internalization/principle-lifecycle-service.js';
6
- import { appendCandidateArtifactLineageRecord, listArtifactLineageRecords } from '../../../src/core/nocturnal-artifact-lineage.js';
7
6
  import { getImplementationAssetRoot } from '../../../src/core/code-implementation-storage.js';
8
7
  import { loadLedger, saveLedger, type LedgerPrinciple, type LedgerRule } from '../../../src/core/principle-tree-ledger.js';
9
8
  import type { Implementation } from '../../../src/types/principle-tree-schema.js';
@@ -142,19 +141,6 @@ describe('principle-lifecycle-service', () => {
142
141
  JSON.stringify(makeReplayReport(), null, 2),
143
142
  'utf-8',
144
143
  );
145
-
146
- appendCandidateArtifactLineageRecord(workspaceDir, {
147
- artifactId: 'artifact-1',
148
- principleId: 'P-001',
149
- ruleId: 'R-001',
150
- sessionId: 'session-1',
151
- sourceSnapshotRef: 'snapshot-1',
152
- sourcePainIds: ['pain-1'],
153
- sourceGateBlockIds: ['gate-1'],
154
- storagePath: getImplementationAssetRoot(stateDir, 'IMPL-001'),
155
- implementationId: 'IMPL-001',
156
- createdAt: '2026-04-08T00:00:00.000Z',
157
- });
158
144
  }
159
145
 
160
146
  it('recomputes lifecycle metrics and exposes assessments plus route recommendations through one service', () => {
@@ -185,13 +171,12 @@ describe('principle-lifecycle-service', () => {
185
171
  expect(context.getActivePrincipleSubtrees()).toEqual([]);
186
172
  });
187
173
 
188
- it('keeps replay classifications behavioral-only and preserves candidate lineage semantics', () => {
174
+ it('keeps replay classifications behavioral-only', () => {
189
175
  seedWorkspace();
190
176
  const service = new PrincipleLifecycleService(workspaceDir, stateDir);
191
177
 
192
178
  const assessments = service.listAssessments();
193
179
  const readModel = service.buildReadModel();
194
- const lineage = listArtifactLineageRecords(workspaceDir, 'rule-implementation-candidate');
195
180
  const latestReport = readModel.principles[0].rules[0].replayEvidence.latestReports[0];
196
181
 
197
182
  expect(assessments[0].routeRecommendation.route).toBe('skill');
@@ -200,12 +185,5 @@ describe('principle-lifecycle-service', () => {
200
185
  'successPositive',
201
186
  'principleAnchor',
202
187
  ]);
203
- expect(lineage).toHaveLength(1);
204
- expect(lineage[0]).toMatchObject({
205
- artifactKind: 'rule-implementation-candidate',
206
- sourcePainIds: ['pain-1'],
207
- sourceGateBlockIds: ['gate-1'],
208
- implementationId: 'IMPL-001',
209
- });
210
188
  });
211
189
  });
@@ -0,0 +1,253 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import * as fs from 'fs';
3
+ import * as os from 'os';
4
+ import * as path from 'path';
5
+ import { PrincipleTreeLedgerAdapter } from '../../src/core/principle-tree-ledger-adapter.js';
6
+ import { loadLedger } from '../../src/core/principle-tree-ledger.js';
7
+ import { CandidateIntakeError, INTAKE_ERROR_CODES } from '@principles/core/runtime-v2';
8
+ import type { LedgerPrincipleEntry } from '@principles/core/runtime-v2';
9
+ import { safeRmDir } from '../test-utils.js';
10
+
11
+ // Mock principle-tree-ledger.js module
12
+ // Use vi.mock with factory to provide controllable addPrincipleToLedger
13
+ vi.mock('../../src/core/principle-tree-ledger.js', async () => {
14
+ const actual = await vi.importActual<typeof import('../../src/core/principle-tree-ledger.js')>('../../src/core/principle-tree-ledger.js');
15
+ return {
16
+ ...actual,
17
+ addPrincipleToLedger: vi.fn(actual.addPrincipleToLedger),
18
+ };
19
+ });
20
+
21
+ describe('PrincipleTreeLedgerAdapter', () => {
22
+ let tempDir: string;
23
+ let stateDir: string;
24
+
25
+ beforeEach(() => {
26
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-adapter-'));
27
+ stateDir = path.join(tempDir, '.state');
28
+ fs.mkdirSync(stateDir, { recursive: true });
29
+ });
30
+
31
+ afterEach(() => {
32
+ safeRmDir(tempDir);
33
+ });
34
+
35
+ function createEntry(overrides: Partial<LedgerPrincipleEntry> = {}): LedgerPrincipleEntry {
36
+ return {
37
+ id: '550e8400-e29b-41d4-a716-446655440000',
38
+ title: 'Test Principle',
39
+ text: 'When file delete is attempted, then verify backup exists first.',
40
+ triggerPattern: 'file delete',
41
+ action: 'verify backup exists',
42
+ status: 'probation',
43
+ evaluability: 'weak_heuristic',
44
+ sourceRef: 'candidate://test-candidate-001',
45
+ artifactRef: 'artifact://test-artifact-001',
46
+ taskRef: 'task://test-task-001',
47
+ createdAt: '2026-04-26T10:00:00.000Z',
48
+ ...overrides,
49
+ };
50
+ }
51
+
52
+ describe('writeProbationEntry', () => {
53
+ it('writes a LedgerPrinciple to the ledger file with correct field expansion', () => {
54
+ const adapter = new PrincipleTreeLedgerAdapter({ stateDir });
55
+ const entry = createEntry();
56
+ const result = adapter.writeProbationEntry(entry);
57
+
58
+ expect(result).toBe(entry);
59
+
60
+ const store = loadLedger(stateDir);
61
+ const written = store.tree.principles[entry.id];
62
+ expect(written).toBeDefined();
63
+ expect(written.id).toBe(entry.id);
64
+ expect(written.text).toBe(entry.text);
65
+ expect(written.status).toBe('candidate');
66
+ expect(written.triggerPattern).toBe(entry.triggerPattern);
67
+ expect(written.action).toBe(entry.action);
68
+ });
69
+
70
+ it('is idempotent — second call with same candidateId returns existing entry, no double write', () => {
71
+ const adapter = new PrincipleTreeLedgerAdapter({ stateDir });
72
+ const entry = createEntry();
73
+ const first = adapter.writeProbationEntry(entry);
74
+ const second = adapter.writeProbationEntry(entry);
75
+
76
+ expect(second).toBe(first);
77
+
78
+ const store = loadLedger(stateDir);
79
+ expect(Object.keys(store.tree.principles).length).toBe(1);
80
+ });
81
+
82
+ it('different candidates produce different entries', () => {
83
+ const adapter = new PrincipleTreeLedgerAdapter({ stateDir });
84
+ const entry1 = createEntry({
85
+ id: '550e8400-e29b-41d4-a716-446655440000',
86
+ sourceRef: 'candidate://alpha',
87
+ });
88
+ const entry2 = createEntry({
89
+ id: '660e8400-e29b-41d4-a716-446655440001',
90
+ sourceRef: 'candidate://beta',
91
+ title: 'Another Principle',
92
+ text: 'When network fails, retry with backoff.',
93
+ });
94
+
95
+ adapter.writeProbationEntry(entry1);
96
+ adapter.writeProbationEntry(entry2);
97
+
98
+ const store = loadLedger(stateDir);
99
+ expect(Object.keys(store.tree.principles).length).toBe(2);
100
+ expect(store.tree.principles[entry1.id]).toBeDefined();
101
+ expect(store.tree.principles[entry2.id]).toBeDefined();
102
+
103
+ expect(adapter.existsForCandidate('alpha')).toBeDefined();
104
+ expect(adapter.existsForCandidate('beta')).toBeDefined();
105
+ });
106
+ });
107
+
108
+ describe('existsForCandidate', () => {
109
+ it('returns the entry for a previously written candidate', () => {
110
+ const adapter = new PrincipleTreeLedgerAdapter({ stateDir });
111
+ const entry = createEntry();
112
+ adapter.writeProbationEntry(entry);
113
+
114
+ const found = adapter.existsForCandidate('test-candidate-001');
115
+ expect(found).not.toBeNull();
116
+ expect(found?.id).toBe(entry.id);
117
+ expect(found?.title).toBe(entry.title);
118
+ expect(found?.sourceRef).toBe(entry.sourceRef);
119
+ });
120
+
121
+ it('returns null for an unknown candidate', () => {
122
+ const adapter = new PrincipleTreeLedgerAdapter({ stateDir });
123
+ const result = adapter.existsForCandidate('nonexistent-candidate');
124
+ expect(result).toBeNull();
125
+ });
126
+ });
127
+
128
+ describe('field expansion', () => {
129
+ it('maps status probation to candidate', () => {
130
+ const adapter = new PrincipleTreeLedgerAdapter({ stateDir });
131
+ const entry = createEntry();
132
+ adapter.writeProbationEntry(entry);
133
+
134
+ const store = loadLedger(stateDir);
135
+ const written = store.tree.principles[entry.id];
136
+ expect(written.status).toBe('candidate');
137
+ expect(written.status).not.toBe('probation');
138
+ });
139
+
140
+ it('applies default values correctly', () => {
141
+ const adapter = new PrincipleTreeLedgerAdapter({ stateDir });
142
+ const entry = createEntry({
143
+ triggerPattern: undefined,
144
+ action: undefined,
145
+ artifactRef: undefined,
146
+ taskRef: undefined,
147
+ });
148
+ adapter.writeProbationEntry(entry);
149
+
150
+ const store = loadLedger(stateDir);
151
+ const written = store.tree.principles[entry.id];
152
+ expect(written.version).toBe(1);
153
+ expect(written.priority).toBe('P1');
154
+ expect(written.scope).toBe('general');
155
+ expect(written.valueScore).toBe(0);
156
+ expect(written.adherenceRate).toBe(0);
157
+ expect(written.painPreventedCount).toBe(0);
158
+ expect(written.createdAt).toBe(entry.createdAt);
159
+ expect(written.updatedAt).toBe(entry.createdAt);
160
+ });
161
+
162
+ it('passes through triggerPattern and action (Decision B)', () => {
163
+ const adapter = new PrincipleTreeLedgerAdapter({ stateDir });
164
+ const entry1 = createEntry({ triggerPattern: 'file delete', action: 'verify backup' });
165
+ adapter.writeProbationEntry(entry1);
166
+
167
+ let store = loadLedger(stateDir);
168
+ let written = store.tree.principles[entry1.id];
169
+ expect(written.triggerPattern).toBe('file delete');
170
+ expect(written.action).toBe('verify backup');
171
+
172
+ const entry2 = createEntry({
173
+ id: '770e8400-e29b-41d4-a716-446655440002',
174
+ sourceRef: 'candidate://test-002',
175
+ triggerPattern: undefined,
176
+ action: undefined,
177
+ });
178
+ adapter.writeProbationEntry(entry2);
179
+
180
+ store = loadLedger(stateDir);
181
+ written = store.tree.principles[entry2.id];
182
+ expect(written.triggerPattern).toBe('');
183
+ expect(written.action).toBe('');
184
+ });
185
+
186
+ it('does NOT include sourceRef, artifactRef, taskRef in ledger (Decision C)', () => {
187
+ const adapter = new PrincipleTreeLedgerAdapter({ stateDir });
188
+ const entry = createEntry();
189
+ adapter.writeProbationEntry(entry);
190
+
191
+ const store = loadLedger(stateDir);
192
+ const written = store.tree.principles[entry.id];
193
+ expect('sourceRef' in written).toBe(false);
194
+ expect('artifactRef' in written).toBe(false);
195
+ expect('taskRef' in written).toBe(false);
196
+
197
+ // But existsForCandidate still works
198
+ expect(adapter.existsForCandidate('test-candidate-001')).not.toBeNull();
199
+ });
200
+
201
+ it('populates derivedFromPainIds with candidateId (Q1 resolved)', () => {
202
+ const adapter = new PrincipleTreeLedgerAdapter({ stateDir });
203
+ const entry = createEntry({ sourceRef: 'candidate://test-candidate-001' });
204
+ adapter.writeProbationEntry(entry);
205
+
206
+ const store = loadLedger(stateDir);
207
+ const written = store.tree.principles[entry.id];
208
+ expect(written.derivedFromPainIds).toEqual(['test-candidate-001']);
209
+ });
210
+ });
211
+
212
+ describe('error handling', () => {
213
+ it('throws LEDGER_WRITE_FAILED when addPrincipleToLedger fails', async () => {
214
+ const mockedModule = await import('../../src/core/principle-tree-ledger.js');
215
+ const mockAdd = vi.mocked(mockedModule.addPrincipleToLedger);
216
+ mockAdd.mockImplementation(() => {
217
+ throw new Error('Disk full');
218
+ });
219
+
220
+ const adapter = new PrincipleTreeLedgerAdapter({ stateDir });
221
+ const entry = createEntry();
222
+
223
+ expect(() => adapter.writeProbationEntry(entry)).toThrow(CandidateIntakeError);
224
+ try {
225
+ adapter.writeProbationEntry(entry);
226
+ } catch (err) {
227
+ expect(err).toBeInstanceOf(CandidateIntakeError);
228
+ expect((err as CandidateIntakeError).code).toBe(INTAKE_ERROR_CODES.LEDGER_WRITE_FAILED);
229
+ expect((err as Error).message).toContain(entry.id);
230
+ }
231
+
232
+ mockAdd.mockRestore();
233
+ });
234
+ });
235
+
236
+ describe('instance isolation', () => {
237
+ it('separate instances have separate Maps', () => {
238
+ const tempDir2 = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-adapter-'));
239
+ const stateDir2 = path.join(tempDir2, '.state');
240
+ fs.mkdirSync(stateDir2, { recursive: true });
241
+
242
+ const adapterA = new PrincipleTreeLedgerAdapter({ stateDir });
243
+ const adapterB = new PrincipleTreeLedgerAdapter({ stateDir: stateDir2 });
244
+
245
+ const entryA = createEntry({ sourceRef: 'candidate://candidate-A' });
246
+ adapterA.writeProbationEntry(entryA);
247
+
248
+ expect(adapterB.existsForCandidate('candidate-A')).toBeNull();
249
+
250
+ safeRmDir(tempDir2);
251
+ });
252
+ });
253
+ });
@@ -139,8 +139,6 @@ describe('ReflectionContextCollector', () => {
139
139
  expect(result!.principle.id).toBe('P_001');
140
140
  expect(result!.painEvents).toEqual([]);
141
141
  expect(result!.sessionSnapshot).toBeNull();
142
- expect(result!.lineage.sourcePainIds).toEqual(['pain_no_exist']);
143
- expect(result!.lineage.sessionId).toBeNull();
144
142
  });
145
143
 
146
144
  it('returns context with pain events and null snapshot when painIds match events but not a specific session', () => {
@@ -159,9 +157,6 @@ describe('ReflectionContextCollector', () => {
159
157
 
160
158
  expect(result).not.toBeNull();
161
159
  expect(result!.principle.id).toBe('P_002');
162
- expect(result!.lineage.sourcePainIds).toEqual(['pain_001']);
163
- // painId -> sessionId resolution is a known gap, so painEvents may be empty
164
- // and sessionSnapshot may be null
165
160
  });
166
161
 
167
162
  it('resolves pain events from a known session', () => {
@@ -181,9 +176,6 @@ describe('ReflectionContextCollector', () => {
181
176
 
182
177
  expect(result).not.toBeNull();
183
178
  expect(result!.principle.id).toBe('P_003');
184
- // painId -> sessionId gap: we attempt to find sessions containing pain events
185
- // The lineage sessionId should be populated if we can resolve it
186
- expect(result!.lineage.sourcePainIds).toEqual(['pain_from_sess_with_pain']);
187
179
  });
188
180
  });
189
181
 
@@ -258,8 +250,6 @@ describe('ReflectionContextCollector', () => {
258
250
  expect(results).toHaveLength(1);
259
251
 
260
252
  const ctx = results[0];
261
- expect(ctx.lineage.sourcePainIds).toEqual(['p1', 'p2', 'p3']);
262
- expect(ctx.lineage.sessionId).toBeNull();
263
253
  expect(ctx.sessionSnapshot).toBeNull();
264
254
  });
265
255
  });
@@ -297,7 +287,6 @@ describe('ReflectionContextCollector', () => {
297
287
  // Exact match: only ONE pain event should match
298
288
  expect(result!.painEvents).toHaveLength(1);
299
289
  expect(result!.painEvents[0].reason).toBe('exact_match_target');
300
- expect(result!.lineage.sessionId).toBe(sessionId);
301
290
  });
302
291
 
303
292
  it('falls back to substring heuristic when no exact ID match', () => {
@@ -340,17 +329,14 @@ describe('ReflectionContextCollector', () => {
340
329
  expect(results).toHaveLength(1);
341
330
 
342
331
  const ctx: ReflectionContext = results[0];
343
- // Verify all fields exist
344
332
  expect(ctx).toHaveProperty('principle');
345
333
  expect(ctx).toHaveProperty('painEvents');
346
334
  expect(ctx).toHaveProperty('sessionSnapshot');
347
- expect(ctx).toHaveProperty('lineage');
348
335
 
349
336
  expect(ctx.principle.id).toBe('P_SHAPE');
350
337
  expect(ctx.principle.text).toBe('Shape test principle');
351
338
  expect(ctx.principle.action).toBe('Do the thing');
352
339
  expect(Array.isArray(ctx.painEvents)).toBe(true);
353
- expect(ctx.lineage.sourcePainIds).toEqual(['pain_shape']);
354
340
  });
355
341
  });
356
342
  });