principles-disciple 1.11.0 → 1.13.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 (233) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +66 -0
  3. package/esbuild.config.js +1 -1
  4. package/openclaw.plugin.json +4 -4
  5. package/package.json +2 -3
  6. package/run-nocturnal.mjs +30 -0
  7. package/scripts/db-migrate.mjs +170 -0
  8. package/scripts/sync-plugin.mjs +250 -6
  9. package/src/commands/archive-impl.ts +136 -0
  10. package/src/commands/capabilities.ts +4 -2
  11. package/src/commands/context.ts +5 -1
  12. package/src/commands/disable-impl.ts +151 -0
  13. package/src/commands/evolution-status.ts +64 -19
  14. package/src/commands/export.ts +8 -6
  15. package/src/commands/focus.ts +8 -20
  16. package/src/commands/nocturnal-review.ts +5 -7
  17. package/src/commands/nocturnal-rollout.ts +1 -12
  18. package/src/commands/nocturnal-train.ts +21 -47
  19. package/src/commands/pain.ts +10 -5
  20. package/src/commands/principle-rollback.ts +4 -2
  21. package/src/commands/promote-impl.ts +274 -0
  22. package/src/commands/rollback-impl.ts +234 -0
  23. package/src/commands/rollback.ts +6 -3
  24. package/src/commands/samples.ts +2 -0
  25. package/src/commands/thinking-os.ts +3 -4
  26. package/src/commands/workflow-debug.ts +2 -1
  27. package/src/config/errors.ts +1 -0
  28. package/src/core/AGENTS.md +34 -0
  29. package/src/core/adaptive-thresholds.ts +4 -3
  30. package/src/core/code-implementation-storage.ts +241 -0
  31. package/src/core/config.ts +5 -2
  32. package/src/core/control-ui-db.ts +29 -10
  33. package/src/core/detection-funnel.ts +12 -7
  34. package/src/core/diagnostician-task-store.ts +156 -0
  35. package/src/core/dictionary.ts +4 -4
  36. package/src/core/empathy-keyword-matcher.ts +7 -3
  37. package/src/core/empathy-types.ts +13 -2
  38. package/src/core/event-log.ts +14 -6
  39. package/src/core/evolution-engine.ts +27 -31
  40. package/src/core/evolution-logger.ts +3 -2
  41. package/src/core/evolution-reducer.ts +110 -31
  42. package/src/core/evolution-types.ts +10 -0
  43. package/src/core/external-training-contract.ts +1 -0
  44. package/src/core/focus-history.ts +38 -24
  45. package/src/core/hygiene/tracker.ts +10 -6
  46. package/src/core/init.ts +5 -2
  47. package/src/core/migration.ts +3 -3
  48. package/src/core/model-deployment-registry.ts +6 -4
  49. package/src/core/model-training-registry.ts +5 -3
  50. package/src/core/nocturnal-arbiter.ts +13 -14
  51. package/src/core/nocturnal-artifact-lineage.ts +117 -0
  52. package/src/core/nocturnal-artificer.ts +257 -0
  53. package/src/core/nocturnal-candidate-scoring.ts +4 -2
  54. package/src/core/nocturnal-compliance.ts +67 -19
  55. package/src/core/nocturnal-dataset.ts +95 -2
  56. package/src/core/nocturnal-executability.ts +2 -3
  57. package/src/core/nocturnal-export.ts +6 -3
  58. package/src/core/nocturnal-rule-implementation-validator.ts +245 -0
  59. package/src/core/nocturnal-trajectory-extractor.ts +10 -3
  60. package/src/core/nocturnal-trinity.ts +319 -61
  61. package/src/core/pain-context-extractor.ts +29 -15
  62. package/src/core/pain.ts +7 -5
  63. package/src/core/path-resolver.ts +16 -15
  64. package/src/core/paths.ts +2 -1
  65. package/src/core/pd-task-reconciler.ts +463 -0
  66. package/src/core/pd-task-service.ts +42 -0
  67. package/src/core/pd-task-store.ts +77 -0
  68. package/src/core/pd-task-types.ts +128 -0
  69. package/src/core/principle-internalization/deprecated-readiness.ts +91 -0
  70. package/src/core/principle-internalization/internalization-routing-policy.ts +208 -0
  71. package/src/core/principle-internalization/lifecycle-metrics.ts +149 -0
  72. package/src/core/principle-internalization/lifecycle-read-model.ts +243 -0
  73. package/src/core/principle-internalization/lifecycle-refresh.ts +11 -0
  74. package/src/core/principle-internalization/principle-lifecycle-service.ts +167 -0
  75. package/src/core/principle-training-state.ts +95 -370
  76. package/src/core/principle-tree-ledger.ts +733 -0
  77. package/src/core/principle-tree-migration.ts +195 -0
  78. package/src/core/profile.ts +3 -1
  79. package/src/core/promotion-gate.ts +14 -18
  80. package/src/core/replay-engine.ts +562 -0
  81. package/src/core/risk-calculator.ts +6 -4
  82. package/src/core/rule-host-helpers.ts +39 -0
  83. package/src/core/rule-host-types.ts +82 -0
  84. package/src/core/rule-host.ts +245 -0
  85. package/src/core/rule-implementation-runtime.ts +38 -0
  86. package/src/core/schema/db-types.ts +16 -0
  87. package/src/core/schema/index.ts +26 -0
  88. package/src/core/schema/migration-runner.ts +207 -0
  89. package/src/core/schema/migrations/001-init-trajectory.ts +211 -0
  90. package/src/core/schema/migrations/002-init-central.ts +122 -0
  91. package/src/core/schema/migrations/003-init-workflow.ts +55 -0
  92. package/src/core/schema/migrations/004-add-thinking-and-gfi.ts +74 -0
  93. package/src/core/schema/migrations/index.ts +31 -0
  94. package/src/core/schema/schema-definitions.ts +650 -0
  95. package/src/core/session-tracker.ts +6 -4
  96. package/src/core/shadow-observation-registry.ts +6 -3
  97. package/src/core/system-logger.ts +2 -2
  98. package/src/core/thinking-models.ts +182 -46
  99. package/src/core/thinking-os-parser.ts +156 -0
  100. package/src/core/training-program.ts +7 -7
  101. package/src/core/trajectory.ts +42 -36
  102. package/src/core/workspace-context.ts +77 -11
  103. package/src/core/workspace-dir-validation.ts +152 -0
  104. package/src/hooks/AGENTS.md +31 -0
  105. package/src/hooks/bash-risk.ts +3 -1
  106. package/src/hooks/edit-verification.ts +9 -5
  107. package/src/hooks/gate-block-helper.ts +5 -1
  108. package/src/hooks/gate.ts +152 -5
  109. package/src/hooks/gfi-gate.ts +9 -2
  110. package/src/hooks/lifecycle-routing.ts +124 -0
  111. package/src/hooks/lifecycle.ts +12 -12
  112. package/src/hooks/llm.ts +17 -109
  113. package/src/hooks/message-sanitize.ts +5 -3
  114. package/src/hooks/pain.ts +19 -15
  115. package/src/hooks/progressive-trust-gate.ts +7 -1
  116. package/src/hooks/prompt.ts +169 -60
  117. package/src/hooks/subagent.ts +5 -4
  118. package/src/hooks/thinking-checkpoint.ts +2 -0
  119. package/src/hooks/trajectory-collector.ts +15 -12
  120. package/src/http/principles-console-route.ts +31 -68
  121. package/src/i18n/commands.ts +2 -2
  122. package/src/index.ts +130 -40
  123. package/src/service/central-database.ts +131 -43
  124. package/src/service/central-health-service.ts +47 -0
  125. package/src/service/central-overview-service.ts +135 -0
  126. package/src/service/central-sync-service.ts +87 -0
  127. package/src/service/control-ui-query-service.ts +46 -36
  128. package/src/service/event-log-auditor.ts +261 -0
  129. package/src/service/evolution-query-service.ts +23 -22
  130. package/src/service/evolution-worker.ts +565 -261
  131. package/src/service/health-query-service.ts +213 -36
  132. package/src/service/nocturnal-runtime.ts +8 -4
  133. package/src/service/nocturnal-service.ts +503 -59
  134. package/src/service/nocturnal-target-selector.ts +5 -7
  135. package/src/service/runtime-summary-service.ts +2 -1
  136. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +25 -336
  137. package/src/service/subagent-workflow/dynamic-timeout.ts +30 -0
  138. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +48 -386
  139. package/src/service/subagent-workflow/index.ts +2 -0
  140. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +169 -284
  141. package/src/service/subagent-workflow/runtime-direct-driver.ts +114 -16
  142. package/src/service/subagent-workflow/subagent-error-utils.ts +25 -0
  143. package/src/service/subagent-workflow/types.ts +9 -4
  144. package/src/service/subagent-workflow/workflow-manager-base.ts +573 -0
  145. package/src/service/subagent-workflow/workflow-store.ts +71 -11
  146. package/src/service/trajectory-service.ts +2 -1
  147. package/src/tools/critique-prompt.ts +1 -1
  148. package/src/tools/deep-reflect.ts +175 -209
  149. package/src/tools/model-index.ts +2 -1
  150. package/src/types/event-types.ts +2 -2
  151. package/src/types/principle-tree-schema.ts +29 -23
  152. package/src/utils/file-lock.ts +5 -3
  153. package/src/utils/io.ts +5 -2
  154. package/src/utils/nlp.ts +5 -46
  155. package/src/utils/node-vm-polyfill.ts +11 -0
  156. package/src/utils/plugin-logger.ts +2 -0
  157. package/src/utils/retry.ts +572 -0
  158. package/src/utils/subagent-probe.ts +1 -1
  159. package/templates/langs/en/core/AGENTS.md +0 -13
  160. package/templates/langs/en/core/SOUL.md +1 -31
  161. package/templates/langs/en/core/TOOLS.md +0 -4
  162. package/templates/langs/en/principles/THINKING_OS.md +77 -0
  163. package/templates/langs/en/skills/admin/SKILL.md +0 -1
  164. package/templates/langs/en/skills/evolution-framework-update/SKILL.md +1 -1
  165. package/templates/langs/en/skills/pd-diagnostician/SKILL.md +18 -5
  166. package/templates/langs/zh/core/AGENTS.md +0 -22
  167. package/templates/langs/zh/core/SOUL.md +1 -31
  168. package/templates/langs/zh/core/TOOLS.md +0 -4
  169. package/templates/langs/zh/principles/THINKING_OS.md +77 -0
  170. package/templates/langs/zh/skills/admin/SKILL.md +0 -1
  171. package/templates/langs/zh/skills/evolution-framework-update/SKILL.md +1 -1
  172. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +25 -4
  173. package/tests/commands/evolution-status.test.ts +119 -0
  174. package/tests/commands/implementation-lifecycle.test.ts +362 -0
  175. package/tests/core/code-implementation-storage.test.ts +398 -0
  176. package/tests/core/evolution-reducer.detector-metadata.test.ts +28 -28
  177. package/tests/core/nocturnal-artifact-lineage.test.ts +53 -0
  178. package/tests/core/nocturnal-artificer.test.ts +241 -0
  179. package/tests/core/nocturnal-compliance-p-principles.test.ts +133 -0
  180. package/tests/core/nocturnal-rule-implementation-validator.test.ts +127 -0
  181. package/tests/core/pd-task-store.test.ts +126 -0
  182. package/tests/core/principle-internalization/deprecated-readiness.test.ts +193 -0
  183. package/tests/core/principle-internalization/internalization-routing-policy.test.ts +212 -0
  184. package/tests/core/principle-internalization/lifecycle-metrics.test.ts +350 -0
  185. package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +211 -0
  186. package/tests/core/principle-training-state.test.ts +228 -1
  187. package/tests/core/principle-tree-ledger.test.ts +423 -0
  188. package/tests/core/regression-v1-9-1.test.ts +265 -0
  189. package/tests/core/replay-engine.test.ts +234 -0
  190. package/tests/core/rule-host-helpers.test.ts +120 -0
  191. package/tests/core/rule-host.test.ts +389 -0
  192. package/tests/core/rule-implementation-runtime.test.ts +64 -0
  193. package/tests/core/workspace-context.test.ts +53 -0
  194. package/tests/core/workspace-dir-validation.test.ts +272 -0
  195. package/tests/hooks/gate-rule-host-pipeline.test.ts +385 -0
  196. package/tests/hooks/pain.test.ts +74 -10
  197. package/tests/hooks/prompt.test.ts +63 -1
  198. package/tests/integration/principle-lifecycle.e2e.test.ts +197 -0
  199. package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +211 -0
  200. package/tests/service/data-endpoints-regression.test.ts +834 -0
  201. package/tests/service/evolution-worker.test.ts +0 -123
  202. package/tests/service/nocturnal-service-code-candidate.test.ts +330 -0
  203. package/tests/utils/nlp.test.ts +1 -19
  204. package/tests/utils/retry.test.ts +327 -0
  205. package/ui/src/App.tsx +1 -1
  206. package/ui/src/api.ts +4 -0
  207. package/ui/src/charts.tsx +366 -0
  208. package/ui/src/components/WorkspaceConfig.tsx +107 -75
  209. package/ui/src/i18n/ui.ts +89 -31
  210. package/ui/src/pages/EvolutionPage.tsx +1 -1
  211. package/ui/src/pages/OverviewPage.tsx +441 -81
  212. package/ui/src/pages/ThinkingModelsPage.tsx +287 -69
  213. package/ui/src/styles.css +43 -0
  214. package/ui/src/types.ts +17 -1
  215. package/src/agents/nocturnal-dreamer.md +0 -152
  216. package/src/agents/nocturnal-philosopher.md +0 -138
  217. package/src/agents/nocturnal-reflector.md +0 -126
  218. package/src/agents/nocturnal-scribe.md +0 -164
  219. package/templates/workspace/.principles/00-kernel.md +0 -51
  220. package/templates/workspace/.principles/DECISION_POLICY.json +0 -44
  221. package/templates/workspace/.principles/PRINCIPLES.md +0 -20
  222. package/templates/workspace/.principles/PROFILE.json +0 -54
  223. package/templates/workspace/.principles/PROFILE.schema.json +0 -56
  224. package/templates/workspace/.principles/THINKING_OS.md +0 -64
  225. package/templates/workspace/.principles/THINKING_OS_ARCHIVE.md +0 -7
  226. package/templates/workspace/.principles/THINKING_OS_CANDIDATES.md +0 -9
  227. package/templates/workspace/.principles/models/_INDEX.md +0 -27
  228. package/templates/workspace/.principles/models/first_principles.md +0 -62
  229. package/templates/workspace/.principles/models/marketing_4p.md +0 -52
  230. package/templates/workspace/.principles/models/porter_five.md +0 -63
  231. package/templates/workspace/.principles/models/swot.md +0 -60
  232. package/templates/workspace/.principles/models/user_story_map.md +0 -63
  233. package/templates/workspace/.state/WORKBOARD.json +0 -4
@@ -0,0 +1,243 @@
1
+ import { loadLedger, type LedgerPrinciple, type LedgerRule } from '../principle-tree-ledger.js';
2
+ import { listArtifactLineageRecords, type ArtifactLineageRecord } from '../nocturnal-artifact-lineage.js';
3
+ import { ReplayEngine, type ClassificationSummary, type ReplayReport } from '../replay-engine.js';
4
+ import type { Implementation, ImplementationLifecycleState } from '../../types/principle-tree-schema.js';
5
+
6
+ export interface LifecycleClassificationTotals {
7
+ total: number;
8
+ passed: number;
9
+ failed: number;
10
+ }
11
+
12
+ export interface RuleReplayEvidence {
13
+ reportCount: number;
14
+ latestReports: ReplayReport[];
15
+ painNegative: LifecycleClassificationTotals;
16
+ successPositive: LifecycleClassificationTotals;
17
+ principleAnchor: LifecycleClassificationTotals;
18
+ passingImplementationIds: string[];
19
+ failingImplementationIds: string[];
20
+ needsReviewImplementationIds: string[];
21
+ }
22
+
23
+ export interface RuleLiveEvidence {
24
+ activeCount: number;
25
+ candidateCount: number;
26
+ disabledCount: number;
27
+ archivedCount: number;
28
+ durablePenaltyCount: number;
29
+ rollbackEvidenceCount: number;
30
+ hasActiveImplementation: boolean;
31
+ hasPassingActiveImplementation: boolean;
32
+ }
33
+
34
+ export interface RuleLineageEvidence {
35
+ records: ArtifactLineageRecord[];
36
+ distinctPainSignalCount: number;
37
+ distinctGateBlockCount: number;
38
+ repeatedErrorSignal: number;
39
+ latestCreatedAt?: string;
40
+ }
41
+
42
+ export interface ImplementationLifecycleEvidence {
43
+ implementation: Implementation;
44
+ latestReplayReport: ReplayReport | null;
45
+ replayHistoryCount: number;
46
+ lineageRecords: ArtifactLineageRecord[];
47
+ }
48
+
49
+ export interface RuleLifecycleEvidence {
50
+ rule: LedgerRule;
51
+ implementations: ImplementationLifecycleEvidence[];
52
+ replayEvidence: RuleReplayEvidence;
53
+ liveEvidence: RuleLiveEvidence;
54
+ lineageEvidence: RuleLineageEvidence;
55
+ }
56
+
57
+ export interface PrincipleLifecycleEvidence {
58
+ principle: LedgerPrinciple;
59
+ rules: RuleLifecycleEvidence[];
60
+ summary: {
61
+ replayReportCount: number;
62
+ activeImplementationCount: number;
63
+ candidateImplementationCount: number;
64
+ disabledImplementationCount: number;
65
+ archivedImplementationCount: number;
66
+ distinctPainSignalCount: number;
67
+ distinctGateBlockCount: number;
68
+ repeatedErrorSignal: number;
69
+ };
70
+ }
71
+
72
+ export interface LifecycleReadModel {
73
+ generatedAt: string;
74
+ principles: PrincipleLifecycleEvidence[];
75
+ }
76
+
77
+ function toClassificationTotals(summary: ClassificationSummary[]): LifecycleClassificationTotals {
78
+ return summary.reduce<LifecycleClassificationTotals>(
79
+ (totals, entry) => ({
80
+ total: totals.total + entry.total,
81
+ passed: totals.passed + entry.passed,
82
+ failed: totals.failed + entry.failed,
83
+ }),
84
+ { total: 0, passed: 0, failed: 0 },
85
+ );
86
+ }
87
+
88
+ function countByLifecycle(implementations: Implementation[], lifecycleState: ImplementationLifecycleState): number {
89
+ return implementations.filter((implementation) => implementation.lifecycleState === lifecycleState).length;
90
+ }
91
+
92
+ function hasDurablePenalty(implementation: Implementation): boolean {
93
+ if (implementation.lifecycleState === 'disabled' || implementation.lifecycleState === 'archived') {
94
+ return true;
95
+ }
96
+
97
+ return typeof implementation.disabledReason === 'string' && implementation.disabledReason.trim().length > 0;
98
+ }
99
+
100
+ function hasRollbackEvidence(implementation: Implementation): boolean {
101
+ return typeof implementation.previousActive === 'string' && implementation.previousActive.length > 0;
102
+ }
103
+
104
+ function createRuleReplayEvidence(reports: { implementationId: string; report: ReplayReport }[]): RuleReplayEvidence {
105
+ return {
106
+ reportCount: reports.length,
107
+ latestReports: reports.map((entry) => entry.report),
108
+ painNegative: toClassificationTotals(reports.map((entry) => entry.report.replayResults.painNegative)),
109
+ successPositive: toClassificationTotals(reports.map((entry) => entry.report.replayResults.successPositive)),
110
+ principleAnchor: toClassificationTotals(reports.map((entry) => entry.report.replayResults.principleAnchor)),
111
+ passingImplementationIds: reports
112
+ .filter((entry) => entry.report.overallDecision === 'pass')
113
+ .map((entry) => entry.implementationId),
114
+ failingImplementationIds: reports
115
+ .filter((entry) => entry.report.overallDecision === 'fail')
116
+ .map((entry) => entry.implementationId),
117
+ needsReviewImplementationIds: reports
118
+ .filter((entry) => entry.report.overallDecision === 'needs-review')
119
+ .map((entry) => entry.implementationId),
120
+ };
121
+ }
122
+
123
+ function createRuleLineageEvidence(records: ArtifactLineageRecord[]): RuleLineageEvidence {
124
+ const painIds = new Set<string>();
125
+ const gateBlockIds = new Set<string>();
126
+
127
+ for (const record of records) {
128
+ for (const painId of record.sourcePainIds) {
129
+ painIds.add(painId);
130
+ }
131
+ for (const gateBlockId of record.sourceGateBlockIds) {
132
+ gateBlockIds.add(gateBlockId);
133
+ }
134
+ }
135
+
136
+ const latestCreatedAt =
137
+ records.length > 0
138
+ ? records
139
+ .map((record) => record.createdAt)
140
+ .sort((left, right) => new Date(right).getTime() - new Date(left).getTime())[0]
141
+ : undefined;
142
+
143
+ return {
144
+ records,
145
+ distinctPainSignalCount: painIds.size,
146
+ distinctGateBlockCount: gateBlockIds.size,
147
+ repeatedErrorSignal: painIds.size + gateBlockIds.size,
148
+ latestCreatedAt,
149
+ };
150
+ }
151
+
152
+ function createRuleLiveEvidence(
153
+ implementations: Implementation[],
154
+ replayEvidence: RuleReplayEvidence,
155
+ ): RuleLiveEvidence {
156
+ const activeImplementations = implementations.filter((implementation) => implementation.lifecycleState === 'active');
157
+
158
+ return {
159
+ activeCount: countByLifecycle(implementations, 'active'),
160
+ candidateCount: countByLifecycle(implementations, 'candidate'),
161
+ disabledCount: countByLifecycle(implementations, 'disabled'),
162
+ archivedCount: countByLifecycle(implementations, 'archived'),
163
+ durablePenaltyCount: implementations.filter((implementation) => hasDurablePenalty(implementation)).length,
164
+ rollbackEvidenceCount: implementations.filter((implementation) => hasRollbackEvidence(implementation)).length,
165
+ hasActiveImplementation: activeImplementations.length > 0,
166
+ hasPassingActiveImplementation: activeImplementations.some((implementation) =>
167
+ replayEvidence.passingImplementationIds.includes(implementation.id),
168
+ ),
169
+ };
170
+ }
171
+
172
+ export function buildLifecycleReadModel(workspaceDir: string, stateDir: string): LifecycleReadModel {
173
+ const ledger = loadLedger(stateDir);
174
+ const replayEngine = new ReplayEngine(workspaceDir, stateDir);
175
+ const lineageRecords = listArtifactLineageRecords(workspaceDir, 'rule-implementation-candidate');
176
+
177
+ const principles = Object.values(ledger.tree.principles)
178
+ .map((principle): PrincipleLifecycleEvidence => {
179
+ const rules = principle.ruleIds
180
+ .map((ruleId) => ledger.tree.rules[ruleId])
181
+ .filter((rule): rule is LedgerRule => rule !== undefined)
182
+ .map((rule): RuleLifecycleEvidence => {
183
+ const implementations = rule.implementationIds
184
+ .map((implementationId) => ledger.tree.implementations[implementationId])
185
+ .filter((implementation): implementation is Implementation => implementation !== undefined);
186
+
187
+ const implementationEvidence: ImplementationLifecycleEvidence[] = implementations.map((implementation) => {
188
+ const reports = replayEngine.listReports(implementation.id);
189
+ const implementationLineage = lineageRecords.filter(
190
+ (record) => record.ruleId === rule.id || record.implementationId === implementation.id,
191
+ );
192
+
193
+ return {
194
+ implementation,
195
+ latestReplayReport: reports[0] ?? null,
196
+ replayHistoryCount: reports.length,
197
+ lineageRecords: implementationLineage,
198
+ };
199
+ });
200
+
201
+ const replayEvidence = createRuleReplayEvidence(
202
+ implementationEvidence
203
+ .filter((entry) => entry.latestReplayReport !== null)
204
+ .map((entry) => ({
205
+ implementationId: entry.implementation.id,
206
+ report: entry.latestReplayReport as ReplayReport,
207
+ })),
208
+ );
209
+ const ruleLineageRecords = lineageRecords.filter((record) => record.ruleId === rule.id);
210
+ const lineageEvidence = createRuleLineageEvidence(ruleLineageRecords);
211
+ const liveEvidence = createRuleLiveEvidence(implementations, replayEvidence);
212
+
213
+ return {
214
+ rule,
215
+ implementations: implementationEvidence,
216
+ replayEvidence,
217
+ liveEvidence,
218
+ lineageEvidence,
219
+ };
220
+ });
221
+
222
+ return {
223
+ principle,
224
+ rules,
225
+ summary: {
226
+ replayReportCount: rules.reduce((sum, rule) => sum + rule.replayEvidence.reportCount, 0),
227
+ activeImplementationCount: rules.reduce((sum, rule) => sum + rule.liveEvidence.activeCount, 0),
228
+ candidateImplementationCount: rules.reduce((sum, rule) => sum + rule.liveEvidence.candidateCount, 0),
229
+ disabledImplementationCount: rules.reduce((sum, rule) => sum + rule.liveEvidence.disabledCount, 0),
230
+ archivedImplementationCount: rules.reduce((sum, rule) => sum + rule.liveEvidence.archivedCount, 0),
231
+ distinctPainSignalCount: rules.reduce((sum, rule) => sum + rule.lineageEvidence.distinctPainSignalCount, 0),
232
+ distinctGateBlockCount: rules.reduce((sum, rule) => sum + rule.lineageEvidence.distinctGateBlockCount, 0),
233
+ repeatedErrorSignal: rules.reduce((sum, rule) => sum + rule.lineageEvidence.repeatedErrorSignal, 0),
234
+ },
235
+ };
236
+ })
237
+ .sort((left, right) => left.principle.id.localeCompare(right.principle.id));
238
+
239
+ return {
240
+ generatedAt: new Date().toISOString(),
241
+ principles,
242
+ };
243
+ }
@@ -0,0 +1,11 @@
1
+ import {
2
+ PrincipleLifecycleService,
3
+ type RecomputedPrincipleLifecycle,
4
+ } from './principle-lifecycle-service.js';
5
+
6
+ export function refreshPrincipleLifecycle(
7
+ workspaceDir: string,
8
+ stateDir: string,
9
+ ): RecomputedPrincipleLifecycle[] {
10
+ return new PrincipleLifecycleService(workspaceDir, stateDir).recomputeAll();
11
+ }
@@ -0,0 +1,167 @@
1
+ import {
2
+ loadLedger,
3
+ updatePrinciple,
4
+ updatePrincipleValueMetrics,
5
+ updateRule,
6
+ } from '../principle-tree-ledger.js';
7
+ import type { PrincipleValueMetrics } from '../../types/principle-tree-schema.js';
8
+ import { buildLifecycleReadModel, type LifecycleReadModel, type PrincipleLifecycleEvidence } from './lifecycle-read-model.js';
9
+ import {
10
+ computePrincipleAdherence,
11
+ computeRuleMetrics,
12
+ type PrincipleAdherenceResult,
13
+ type RuleMetricResult,
14
+ } from './lifecycle-metrics.js';
15
+ import {
16
+ assessDeprecatedReadiness,
17
+ type DeprecatedReadinessAssessment,
18
+ } from './deprecated-readiness.js';
19
+ import {
20
+ recommendInternalizationRoute,
21
+ type InternalizationRouteRecommendation,
22
+ } from './internalization-routing-policy.js';
23
+
24
+ export interface RecomputedPrincipleLifecycle {
25
+ principleId: string;
26
+ ruleMetrics: Record<string, RuleMetricResult>;
27
+ adherence: PrincipleAdherenceResult;
28
+ deprecatedReadiness: DeprecatedReadinessAssessment;
29
+ routeRecommendation: InternalizationRouteRecommendation;
30
+ }
31
+
32
+ export interface PrincipleLifecycleAssessment {
33
+ principle: PrincipleLifecycleEvidence['principle'];
34
+ summary: PrincipleLifecycleEvidence['summary'];
35
+ ruleMetrics: Record<string, RuleMetricResult>;
36
+ adherence: PrincipleAdherenceResult;
37
+ deprecatedReadiness: DeprecatedReadinessAssessment;
38
+ routeRecommendation: InternalizationRouteRecommendation;
39
+ }
40
+
41
+ function createValueMetrics(
42
+ principle: PrincipleLifecycleEvidence,
43
+ adherence: PrincipleAdherenceResult,
44
+ current: PrincipleValueMetrics | undefined,
45
+ ): PrincipleValueMetrics {
46
+ const violatedCount = principle.summary.repeatedErrorSignal;
47
+ const totalOpportunities = Math.max(
48
+ violatedCount + principle.summary.replayReportCount,
49
+ current?.totalOpportunities ?? 0,
50
+ );
51
+ const adheredCount = Math.max(0, Math.round((adherence.adherenceRate / 100) * totalOpportunities));
52
+
53
+ return {
54
+ principleId: principle.principle.id,
55
+ painPreventedCount: current?.painPreventedCount ?? principle.principle.painPreventedCount,
56
+ avgPainSeverityPrevented: current?.avgPainSeverityPrevented ?? 0,
57
+ lastPainPreventedAt: current?.lastPainPreventedAt ?? principle.principle.lastPainPreventedAt,
58
+ totalOpportunities,
59
+ adheredCount,
60
+ violatedCount,
61
+ implementationCost: current?.implementationCost ?? 0,
62
+ benefitScore: Number(
63
+ (
64
+ (current?.painPreventedCount ?? principle.principle.painPreventedCount) *
65
+ Math.max(current?.avgPainSeverityPrevented ?? 0, 1) *
66
+ (adherence.adherenceRate / 100)
67
+ ).toFixed(2)
68
+ ),
69
+ calculatedAt: new Date().toISOString(),
70
+ };
71
+ }
72
+
73
+ export class PrincipleLifecycleService {
74
+ private readonly workspaceDir: string;
75
+ private readonly stateDir: string;
76
+ constructor(
77
+ workspaceDir: string,
78
+ stateDir: string,
79
+ ) {
80
+ this.workspaceDir = workspaceDir;
81
+ this.stateDir = stateDir;
82
+ }
83
+
84
+ buildReadModel(): LifecycleReadModel {
85
+ return buildLifecycleReadModel(this.workspaceDir, this.stateDir);
86
+ }
87
+
88
+ listAssessments(readModel = this.buildReadModel()): PrincipleLifecycleAssessment[] {
89
+ return readModel.principles.map((principleEvidence) => {
90
+ const ruleMetrics = Object.fromEntries(
91
+ principleEvidence.rules.map((ruleEvidence) => [
92
+ ruleEvidence.rule.id,
93
+ computeRuleMetrics(ruleEvidence),
94
+ ]),
95
+ ) as Record<string, RuleMetricResult>;
96
+ const adherence = computePrincipleAdherence(principleEvidence, ruleMetrics);
97
+ const deprecatedReadiness = assessDeprecatedReadiness(
98
+ principleEvidence,
99
+ ruleMetrics,
100
+ adherence,
101
+ );
102
+ const routeRecommendation = recommendInternalizationRoute(
103
+ principleEvidence,
104
+ ruleMetrics,
105
+ adherence,
106
+ );
107
+
108
+ return {
109
+ principle: principleEvidence.principle,
110
+ summary: principleEvidence.summary,
111
+ ruleMetrics,
112
+ adherence,
113
+ deprecatedReadiness,
114
+ routeRecommendation,
115
+ };
116
+ });
117
+ }
118
+
119
+ listRouteRecommendations(readModel = this.buildReadModel()): InternalizationRouteRecommendation[] {
120
+ return this.listAssessments(readModel).map((assessment) => assessment.routeRecommendation);
121
+ }
122
+
123
+ recomputeAll(): RecomputedPrincipleLifecycle[] {
124
+ const ledger = loadLedger(this.stateDir);
125
+ const readModel = this.buildReadModel();
126
+ const assessments = this.listAssessments(readModel);
127
+
128
+ return assessments.map((assessment) => {
129
+ const principleEvidence = readModel.principles.find(
130
+ (principle) => principle.principle.id === assessment.principle.id,
131
+ );
132
+ if (!principleEvidence) {
133
+ throw new Error(`Missing lifecycle evidence for principle "${assessment.principle.id}".`);
134
+ }
135
+
136
+ for (const [ruleId, metrics] of Object.entries(assessment.ruleMetrics)) {
137
+ updateRule(this.stateDir, ruleId, {
138
+ coverageRate: metrics.coverageRate,
139
+ falsePositiveRate: metrics.falsePositiveRate,
140
+ updatedAt: readModel.generatedAt,
141
+ });
142
+ }
143
+
144
+ updatePrinciple(this.stateDir, principleEvidence.principle.id, {
145
+ adherenceRate: assessment.adherence.adherenceRate,
146
+ updatedAt: readModel.generatedAt,
147
+ });
148
+ updatePrincipleValueMetrics(
149
+ this.stateDir,
150
+ principleEvidence.principle.id,
151
+ createValueMetrics(
152
+ principleEvidence,
153
+ assessment.adherence,
154
+ ledger.tree.metrics[principleEvidence.principle.id],
155
+ ),
156
+ );
157
+
158
+ return {
159
+ principleId: principleEvidence.principle.id,
160
+ ruleMetrics: assessment.ruleMetrics,
161
+ adherence: assessment.adherence,
162
+ deprecatedReadiness: assessment.deprecatedReadiness,
163
+ routeRecommendation: assessment.routeRecommendation,
164
+ };
165
+ });
166
+ }
167
+ }