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
@@ -27,11 +27,9 @@
27
27
  */
28
28
 
29
29
  import { randomUUID } from 'crypto';
30
- import * as fs from 'fs';
31
- import * as path from 'path';
32
- import * as url from 'url';
33
30
  import type { NocturnalSessionSnapshot } from './nocturnal-trajectory-extractor.js';
34
31
  import { computeThinkingModelDelta } from './nocturnal-trajectory-extractor.js';
32
+ import type { TrinityArtificerContext } from './nocturnal-artificer.js';
35
33
  import {
36
34
  runTournament,
37
35
  DEFAULT_SCORING_WEIGHTS,
@@ -45,22 +43,231 @@ import {
45
43
  } from './adaptive-thresholds.js';
46
44
 
47
45
  // ---------------------------------------------------------------------------
48
- // Role Prompt Loading
46
+ // Embedded Role Prompts
49
47
  // ---------------------------------------------------------------------------
48
+ // These prompts are embedded at build time to eliminate file system dependency.
49
+ // Previously loaded from src/agents/*.md at runtime — fragile because esbuild
50
+ // did not copy the agents/ directory into the bundle.
50
51
 
51
- const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
52
- const AGENTS_DIR = path.join(__dirname, '../agents');
52
+ const NOCTURNAL_DREAMER_PROMPT = `# Nocturnal Dreamer — Candidate Generation
53
53
 
54
- function loadRolePrompt(filename: string): string {
55
- const filePath = path.join(AGENTS_DIR, filename);
56
- try {
57
- return fs.readFileSync(filePath, 'utf-8');
58
- } catch {
59
- console.warn(`[Trinity] Could not load role prompt: ${filename}`);
60
- return '';
54
+ > System prompt for Trinity Dreamer stage.
55
+ > Role: Generate multiple alternative "better decision" candidates from a session snapshot.
56
+
57
+ ## Role
58
+
59
+ You are a principles analyst specializing in identifying decision alternatives.
60
+ Your task is to analyze a session trajectory and generate **multiple candidate corrections**,
61
+ each representing a different valid approach to the same problem.
62
+
63
+ ## Input
64
+
65
+ You will receive:
66
+ - A **target principle** (principle ID and description)
67
+ - A **session trajectory snapshot** containing:
68
+ - Assistant turns (sanitized text, no raw content)
69
+ - User turns (correction cues only, no raw content)
70
+ - Tool calls with outcomes and error messages
71
+ - Pain events and gate blocks
72
+ - Session metadata
73
+
74
+ ## Task
75
+
76
+ Analyze the session and generate **2-3 candidate corrections**, each capturing:
77
+
78
+ 1. **The bad decision**: What the agent decided or did that violated the target principle
79
+ 2. **The better decision**: What the agent should have done instead (unique per candidate)
80
+ 3. **The rationale**: Why this alternative is better
81
+ 4. **Confidence**: How confident you are this is a valid alternative (0.0-1.0)
82
+
83
+ ## Output Format
84
+
85
+ You MUST respond with ONLY a valid JSON object. No markdown, no explanation, no preamble.
86
+
87
+ {
88
+ "valid": true,
89
+ "candidates": [
90
+ {
91
+ "candidateIndex": 0,
92
+ "badDecision": "<what the agent did wrong>",
93
+ "betterDecision": "<what the agent should have done>",
94
+ "rationale": "<why this is better>",
95
+ "confidence": 0.95
96
+ }
97
+ ],
98
+ "generatedAt": "<ISO timestamp>"
99
+ }
100
+
101
+ ## Quality Standards
102
+
103
+ ### Each candidate MUST:
104
+ - Have a candidateIndex that is unique within the candidate list
105
+ - Describe a specific, concrete badDecision (not generic anti-patterns)
106
+ - Propose a specific, actionable betterDecision (contains an action verb)
107
+ - Provide a principle-grounded rationale (explicitly references the principle)
108
+ - Include a confidence score (0.0-1.0, higher = more confident)
109
+
110
+ ### Candidates should DIFFER from each other:
111
+ - Different candidates should represent genuinely different approaches
112
+ - Do not generate candidates with identical betterDecisions
113
+ - Vary the confidence scores to reflect genuine uncertainty
114
+
115
+ ### Candidates must NOT:
116
+ - Contain raw user text or private content
117
+ - Reference non-existent tools or impossible actions
118
+ - Propose vague improvements ("be more careful")
119
+ - Exceed the requested number of candidates
120
+
121
+ ## Validation
122
+
123
+ If you cannot generate valid candidates (e.g., no clear violation found, insufficient data), respond with:
124
+
125
+ {
126
+ "valid": false,
127
+ "candidates": [],
128
+ "reason": "<why valid candidates cannot be generated>",
129
+ "generatedAt": "<ISO timestamp>"
130
+ }`;
131
+
132
+ const NOCTURNAL_PHILOSOPHER_PROMPT = `# Nocturnal Philosopher — Candidate Evaluation and Ranking
133
+
134
+ > System prompt for Trinity Philosopher stage.
135
+ > Role: Evaluate Dreamer's candidates and rank them by principle alignment and quality.
136
+
137
+ ## Role
138
+
139
+ You are a principles analyst specializing in critical evaluation.
140
+ Your task is to evaluate Dreamer's candidate corrections and rank them
141
+ based on principle alignment, specificity, and actionability.
142
+
143
+ ## Input
144
+
145
+ You will receive:
146
+ - A **target principle** (principle ID and description)
147
+ - **Dreamer's candidates** — a list of alternative corrections to evaluate
148
+
149
+ ## Task
150
+
151
+ For each candidate, provide:
152
+ 1. **Critique**: A principle-grounded assessment of this candidate's strengths and weaknesses
153
+ 2. **Principle alignment**: Whether this candidate properly aligns with the target principle
154
+ 3. **Score**: Overall quality score (0.0-1.0, higher = better)
155
+ 4. **Rank**: Relative ranking among all candidates (1 = best)
156
+
157
+ Finally, provide an **overall assessment** of the candidate set.
158
+
159
+ ## Output Format
160
+
161
+ You MUST respond with ONLY a valid JSON object. No markdown, no explanation, no preamble.
162
+
163
+ {
164
+ "valid": true,
165
+ "judgments": [
166
+ {
167
+ "candidateIndex": 0,
168
+ "critique": "<principle-grounded critique>",
169
+ "principleAligned": true,
170
+ "score": 0.92,
171
+ "rank": 1
172
+ }
173
+ ],
174
+ "overallAssessment": "<summary of candidate set quality>",
175
+ "generatedAt": "<ISO timestamp>"
176
+ }
177
+
178
+ ## Evaluation Criteria
179
+
180
+ ### Score Components (0-1 scale each):
181
+ 1. **Principle Alignment** (weight: 0.4) — Does the betterDecision properly reflect the target principle?
182
+ 2. **Specificity** (weight: 0.3) — Is badDecision specific? Is betterDecision actionable?
183
+ 3. **Actionability** (weight: 0.3) — Does betterDecision describe a specific next step?
184
+
185
+ ### Ranking Rules:
186
+ - Candidates are ranked by score (highest = rank 1)
187
+ - Ties broken by: higher principle alignment, then lower candidateIndex
188
+
189
+ ## Validation
190
+
191
+ If you cannot judge the candidates, respond with:
192
+
193
+ {
194
+ "valid": false,
195
+ "judgments": [],
196
+ "overallAssessment": "",
197
+ "reason": "<why judgment cannot be produced>",
198
+ "generatedAt": "<ISO timestamp>"
199
+ }`;
200
+
201
+ const NOCTURNAL_SCRIBE_PROMPT = `# Nocturnal Scribe — Final Artifact Synthesis
202
+
203
+ > System prompt for Trinity Scribe stage.
204
+ > Role: Synthesize the best candidate into a final structured artifact.
205
+
206
+ ## Role
207
+
208
+ You are a principles analyst specializing in structured output.
209
+ Your task is to take the top-ranked candidate from Philosopher's evaluation
210
+ and synthesize it into a final decision-point artifact that passes arbiter validation.
211
+
212
+ ## Input
213
+
214
+ You will receive:
215
+ - A **target principle** (principle ID and description)
216
+ - A **session trajectory snapshot**
217
+ - **Philosopher's judgments** — ranked candidates with critiques
218
+ - **Dreamer's candidates** — the original candidate list
219
+
220
+ ## Task
221
+
222
+ Select the best candidate (Philosopher's rank 1) and synthesize it into
223
+ a final TrinityDraftArtifact.
224
+
225
+ ## Output Format
226
+
227
+ You MUST respond with ONLY a valid JSON object. No markdown, no explanation, no preamble.
228
+
229
+ {
230
+ "selectedCandidateIndex": 0,
231
+ "badDecision": "<final bad decision text>",
232
+ "betterDecision": "<final better decision text>",
233
+ "rationale": "<final rationale text>",
234
+ "sessionId": "<source session ID>",
235
+ "principleId": "<principle ID>",
236
+ "sourceSnapshotRef": "<snapshot reference>",
237
+ "telemetry": {
238
+ "chainMode": "trinity",
239
+ "dreamerPassed": true,
240
+ "philosopherPassed": true,
241
+ "scribePassed": true,
242
+ "candidateCount": 2,
243
+ "selectedCandidateIndex": 0,
244
+ "stageFailures": []
61
245
  }
62
246
  }
63
247
 
248
+ ## Validation
249
+
250
+ If you cannot synthesize an artifact:
251
+
252
+ {
253
+ "selectedCandidateIndex": -1,
254
+ "badDecision": "",
255
+ "betterDecision": "",
256
+ "rationale": "",
257
+ "sessionId": "<source session ID>",
258
+ "principleId": "<principle ID>",
259
+ "sourceSnapshotRef": "",
260
+ "telemetry": {
261
+ "chainMode": "trinity",
262
+ "dreamerPassed": true,
263
+ "philosopherPassed": false,
264
+ "scribePassed": false,
265
+ "candidateCount": 2,
266
+ "selectedCandidateIndex": -1,
267
+ "stageFailures": ["Philosopher: no valid judgments produced"]
268
+ }
269
+ }`;
270
+
64
271
  // ---------------------------------------------------------------------------
65
272
  // Trinity Runtime Adapter
66
273
  // ---------------------------------------------------------------------------
@@ -69,6 +276,7 @@ function loadRolePrompt(filename: string): string {
69
276
  * Interface for Trinity stage invocation.
70
277
  * Implementations can use real subagent runtimes or stubs.
71
278
  */
279
+ /* eslint-disable no-unused-vars -- Reason: interface method params are type signatures, not implementations */
72
280
  export interface TrinityRuntimeAdapter {
73
281
  /**
74
282
  * Invoke the Dreamer stage.
@@ -78,9 +286,9 @@ export interface TrinityRuntimeAdapter {
78
286
  * @returns Dreamer output JSON
79
287
  */
80
288
  invokeDreamer(
81
- snapshot: NocturnalSessionSnapshot,
82
- principleId: string,
83
- maxCandidates: number
289
+ _snapshot: NocturnalSessionSnapshot,
290
+ _principleId: string,
291
+ _maxCandidates: number
84
292
  ): Promise<DreamerOutput>;
85
293
 
86
294
  /**
@@ -90,8 +298,8 @@ export interface TrinityRuntimeAdapter {
90
298
  * @returns Philosopher output JSON
91
299
  */
92
300
  invokePhilosopher(
93
- dreamerOutput: DreamerOutput,
94
- principleId: string
301
+ _dreamerOutput: DreamerOutput,
302
+ _principleId: string
95
303
  ): Promise<PhilosopherOutput>;
96
304
 
97
305
  /**
@@ -105,12 +313,12 @@ export interface TrinityRuntimeAdapter {
105
313
  * @returns Scribe draft artifact or null if failed
106
314
  */
107
315
  invokeScribe(
108
- dreamerOutput: DreamerOutput,
109
- philosopherOutput: PhilosopherOutput,
110
- snapshot: NocturnalSessionSnapshot,
111
- principleId: string,
112
- telemetry: TrinityTelemetry,
113
- config: TrinityConfig
316
+ _dreamerOutput: DreamerOutput,
317
+ _philosopherOutput: PhilosopherOutput,
318
+ _snapshot: NocturnalSessionSnapshot,
319
+ _principleId: string,
320
+ _telemetry: TrinityTelemetry,
321
+ _config: TrinityConfig
114
322
  ): Promise<TrinityDraftArtifact | null>;
115
323
 
116
324
  /**
@@ -119,6 +327,7 @@ export interface TrinityRuntimeAdapter {
119
327
  */
120
328
  close?(): Promise<void>;
121
329
  }
330
+ /* eslint-enable no-unused-vars */
122
331
 
123
332
  // ---------------------------------------------------------------------------
124
333
  // OpenClaw Runtime Adapter
@@ -130,38 +339,40 @@ export interface TrinityRuntimeAdapter {
130
339
  * Does NOT depend on OpenClaw internals.
131
340
  */
132
341
  export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
342
+ /* eslint-disable no-unused-vars -- Reason: type-level function parameter names are documentation */
133
343
  private readonly api: {
134
344
  runtime: {
135
345
  subagent: {
136
- run: (opts: {
346
+ run: (_opts: {
137
347
  sessionKey: string;
138
348
  message: string;
139
349
  extraSystemPrompt?: string;
140
350
  deliver?: boolean;
141
351
  }) => Promise<{ runId: string }>;
142
- waitForRun: (opts: { runId: string; timeoutMs: number }) => Promise<{
352
+ waitForRun: (_opts: { runId: string; timeoutMs: number }) => Promise<{
143
353
  status: string;
144
354
  error?: string;
145
355
  }>;
146
- getSessionMessages: (opts: {
356
+ getSessionMessages: (_opts: {
147
357
  sessionKey: string;
148
358
  limit: number;
149
359
  }) => Promise<{
150
360
  messages: unknown[];
151
361
  }>;
152
- deleteSession: (opts: {
362
+ deleteSession: (_opts: {
153
363
  sessionKey: string;
154
364
  deleteTranscript?: boolean;
155
365
  }) => Promise<void>;
156
366
  };
157
367
  };
158
368
  };
369
+ /* eslint-enable no-unused-vars */
159
370
 
160
371
  private readonly stageTimeoutMs: number;
161
372
 
162
373
  constructor(
163
374
  api: OpenClawTrinityRuntimeAdapter['api'],
164
- stageTimeoutMs = 180_000
375
+ stageTimeoutMs = 300_000 // 5 min — increased from 3 min to accommodate slower LLM responses
165
376
  ) {
166
377
  this.api = api;
167
378
  this.stageTimeoutMs = stageTimeoutMs;
@@ -173,7 +384,7 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
173
384
  maxCandidates: number
174
385
  ): Promise<DreamerOutput> {
175
386
  const sessionKey = `agent:main:subagent:ne-dreamer-${randomUUID()}`;
176
- const systemPrompt = loadRolePrompt('nocturnal-dreamer.md');
387
+ const systemPrompt = NOCTURNAL_DREAMER_PROMPT;
177
388
 
178
389
  const prompt = this.buildDreamerPrompt(snapshot, principleId, maxCandidates);
179
390
 
@@ -204,13 +415,13 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
204
415
  limit: 5,
205
416
  });
206
417
 
207
- const outputText = this.extractAssistantText(messages.messages as Array<{ role: string; text?: string; content?: string }>);
418
+ const outputText = this.extractAssistantText(messages.messages as { role: string; text?: string; content?: string }[]);
208
419
  return this.parseDreamerOutput(outputText);
209
420
  } finally {
210
421
  await this.api.runtime.subagent.deleteSession({
211
422
  sessionKey,
212
423
  deleteTranscript: true,
213
- }).catch(() => {});
424
+ }).catch(() => { /* intentionally empty - fire-and-forget session cleanup */ });
214
425
  }
215
426
  }
216
427
 
@@ -219,7 +430,7 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
219
430
  principleId: string
220
431
  ): Promise<PhilosopherOutput> {
221
432
  const sessionKey = `agent:main:subagent:ne-philosopher-${randomUUID()}`;
222
- const systemPrompt = loadRolePrompt('nocturnal-philosopher.md');
433
+ const systemPrompt = NOCTURNAL_PHILOSOPHER_PROMPT;
223
434
 
224
435
  const prompt = this.buildPhilosopherPrompt(dreamerOutput, principleId);
225
436
 
@@ -251,26 +462,28 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
251
462
  limit: 5,
252
463
  });
253
464
 
254
- const outputText = this.extractAssistantText(messages.messages as Array<{ role: string; text?: string; content?: string }>);
465
+ const outputText = this.extractAssistantText(messages.messages as { role: string; text?: string; content?: string }[]);
255
466
  return this.parsePhilosopherOutput(outputText);
256
467
  } finally {
257
468
  await this.api.runtime.subagent.deleteSession({
258
469
  sessionKey,
259
470
  deleteTranscript: true,
260
- }).catch(() => {});
471
+ }).catch(() => { /* intentionally empty - fire-and-forget session cleanup */ });
261
472
  }
262
473
  }
263
474
 
475
+ // eslint-disable-next-line @typescript-eslint/max-params -- Reason: scribe invocation requires all context parameters - refactoring would break API
264
476
  async invokeScribe(
265
477
  dreamerOutput: DreamerOutput,
266
478
  philosopherOutput: PhilosopherOutput,
267
479
  snapshot: NocturnalSessionSnapshot,
268
480
  principleId: string,
269
481
  telemetry: TrinityTelemetry,
270
- config: TrinityConfig
482
+ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars -- Reason: interface requires this param but implementation doesn't use it
483
+ _config: TrinityConfig
271
484
  ): Promise<TrinityDraftArtifact | null> {
272
485
  const sessionKey = `agent:main:subagent:ne-scribe-${randomUUID()}`;
273
- const systemPrompt = loadRolePrompt('nocturnal-scribe.md');
486
+ const systemPrompt = NOCTURNAL_SCRIBE_PROMPT;
274
487
 
275
488
  const prompt = this.buildScribePrompt(dreamerOutput, philosopherOutput, snapshot, principleId);
276
489
 
@@ -296,16 +509,17 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
296
509
  limit: 5,
297
510
  });
298
511
 
299
- const outputText = this.extractAssistantText(messages.messages as Array<{ role: string; text?: string; content?: string }>);
512
+ const outputText = this.extractAssistantText(messages.messages as { role: string; text?: string; content?: string }[]);
300
513
  return this.parseScribeOutput(outputText, snapshot, principleId, telemetry);
301
514
  } finally {
302
515
  await this.api.runtime.subagent.deleteSession({
303
516
  sessionKey,
304
517
  deleteTranscript: true,
305
- }).catch(() => {});
518
+ }).catch(() => { /* intentionally empty - fire-and-forget session cleanup */ });
306
519
  }
307
520
  }
308
521
 
522
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this -- Reason: interface-required close() with no-op implementation
309
523
  async close(): Promise<void> {
310
524
  // Nothing to clean up in this implementation
311
525
  }
@@ -314,8 +528,9 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
314
528
  // Private Helper Methods
315
529
  // ---------------------------------------------------------------------------
316
530
 
531
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this -- Reason: pure utility function that doesn't need instance state
317
532
  private extractAssistantText(
318
- messages: Array<{ role: string; text?: string; content?: string }>
533
+ messages: { role: string; text?: string; content?: string }[]
319
534
  ): string {
320
535
  for (let i = messages.length - 1; i >= 0; i--) {
321
536
  const msg = messages[i] as { role: string; text?: string; content?: string };
@@ -326,6 +541,7 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
326
541
  return '';
327
542
  }
328
543
 
544
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this -- Reason: pure utility function that doesn't need instance state
329
545
  private buildDreamerPrompt(
330
546
  snapshot: NocturnalSessionSnapshot,
331
547
  principleId: string,
@@ -346,6 +562,7 @@ Please analyze this session and generate ${maxCandidates} candidate corrections.
346
562
  Respond with ONLY a valid JSON object matching the DreamerOutput contract.`;
347
563
  }
348
564
 
565
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this -- Reason: utility method doesn't require this - pure prompt building function
349
566
  private buildPhilosopherPrompt(
350
567
  dreamerOutput: DreamerOutput,
351
568
  principleId: string
@@ -359,6 +576,7 @@ ${candidatesJson}
359
576
  Please evaluate each candidate and rank them by principle alignment, specificity, and actionability. Respond with ONLY a valid JSON object matching the PhilosopherOutput contract.`;
360
577
  }
361
578
 
579
+ /* eslint-disable @typescript-eslint/class-methods-use-this, @typescript-eslint/max-params -- Reason: helper function needs all 4 context params and doesn't use instance state */
362
580
  private buildScribePrompt(
363
581
  dreamerOutput: DreamerOutput,
364
582
  philosopherOutput: PhilosopherOutput,
@@ -378,6 +596,7 @@ ${judgmentsJson}
378
596
 
379
597
  Select the best candidate (Philosopher's rank 1) and synthesize it into a final TrinityDraftArtifact. Respond with ONLY a valid JSON object.`;
380
598
  }
599
+ /* eslint-enable @typescript-eslint/class-methods-use-this, @typescript-eslint/max-params */
381
600
 
382
601
  private parseDreamerOutput(text: string): DreamerOutput {
383
602
  const json = this.extractJson(text);
@@ -475,11 +694,13 @@ Select the best candidate (Philosopher's rank 1) and synthesize it into a final
475
694
  }
476
695
  }
477
696
 
697
+ // eslint-disable-next-line @typescript-eslint/max-params -- Reason: output parsing requires text + snapshot + principleId + telemetry - refactoring would break API
478
698
  private parseScribeOutput(
479
699
  text: string,
480
700
  snapshot: NocturnalSessionSnapshot,
481
701
  principleId: string,
482
- telemetry: TrinityTelemetry
702
+ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars -- Reason: interface requires this param but implementation doesn't use it
703
+ _telemetry: TrinityTelemetry
483
704
  ): TrinityDraftArtifact | null {
484
705
  const json = this.extractJson(text);
485
706
  if (!json) {
@@ -519,6 +740,7 @@ Select the best candidate (Philosopher's rank 1) and synthesize it into a final
519
740
  /**
520
741
  * Extract JSON object from text that may contain markdown code blocks.
521
742
  */
743
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this -- Reason: pure utility function that doesn't need instance state
522
744
  private extractJson(text: string): string | null {
523
745
  // Try direct parse first
524
746
  try {
@@ -529,7 +751,7 @@ Select the best candidate (Philosopher's rank 1) and synthesize it into a final
529
751
  }
530
752
 
531
753
  // Match triple-backtick JSON blocks
532
- const codeBlockMatch = text.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
754
+ const codeBlockMatch = /```(?:json)?\s*\n?([\s\S]*?)\n?```/.exec(text);
533
755
  if (codeBlockMatch) {
534
756
  const extracted = codeBlockMatch[1].trim();
535
757
  try {
@@ -696,6 +918,8 @@ export interface TrinityDraftArtifact {
696
918
  thinkingModelDelta?: number;
697
919
  /** Reflection quality: gain in planning ratio (-1 to 1) */
698
920
  planningRatioGain?: number;
921
+ /** Optional routing context for a follow-on Artificer stage */
922
+ artificerContext?: TrinityArtificerContext;
699
923
  }
700
924
 
701
925
  export interface TrinityTelemetry {
@@ -749,19 +973,14 @@ export interface TrinityResult {
749
973
  failures: TrinityStageFailure[];
750
974
  /** Whether fallback to single-reflector occurred */
751
975
  fallbackOccurred: boolean;
976
+ /** Optional routing context for a follow-on Artificer stage */
977
+ artificerContext?: TrinityArtificerContext;
752
978
  }
753
979
 
754
980
  // ---------------------------------------------------------------------------
755
981
  // Internal Types for Trinity Execution
756
982
  // ---------------------------------------------------------------------------
757
983
 
758
- /**
759
- * Enriched candidate after Philosopher judgment.
760
- */
761
- interface EnrichedCandidate extends DreamerCandidate {
762
- judgment: PhilosopherJudgment;
763
- }
764
-
765
984
  // ---------------------------------------------------------------------------
766
985
  // Stub Stage Implementations (Phase 2 — no real subagent calls)
767
986
  // ---------------------------------------------------------------------------
@@ -781,6 +1000,10 @@ export function invokeStubDreamer(
781
1000
  const hasPain = snapshot.stats.totalPainEvents > 0;
782
1001
  const hasGateBlocks = snapshot.stats.totalGateBlocks > 0;
783
1002
 
1003
+ // #219: Detect fallback data source - stats may be incomplete
1004
+ const isFallback = snapshot._dataSource === 'pain_context_fallback';
1005
+ const fallbackWarning = isFallback ? ' [fallback data: stats may be incomplete]' : '';
1006
+
784
1007
  const candidates: DreamerCandidate[] = [];
785
1008
 
786
1009
  // Generate candidates based on available signals
@@ -879,11 +1102,19 @@ export function invokeStubDreamer(
879
1102
  // Ensure we don't exceed maxCandidates
880
1103
  const limitedCandidates = candidates.slice(0, Math.min(candidates.length, maxCandidates));
881
1104
 
1105
+ // #219: Add fallback warning to rationales if data source is fallback
1106
+ const annotatedCandidates = isFallback
1107
+ ? limitedCandidates.map((c) => ({
1108
+ ...c,
1109
+ rationale: c.rationale + fallbackWarning,
1110
+ }))
1111
+ : limitedCandidates;
1112
+
882
1113
  return {
883
- valid: limitedCandidates.length > 0,
884
- candidates: limitedCandidates,
1114
+ valid: annotatedCandidates.length > 0,
1115
+ candidates: annotatedCandidates,
885
1116
  generatedAt: new Date().toISOString(),
886
- reason: limitedCandidates.length === 0 ? 'No signal available for candidate generation' : undefined,
1117
+ reason: annotatedCandidates.length === 0 ? 'No signal available for candidate generation' : undefined,
887
1118
  };
888
1119
  }
889
1120
 
@@ -895,7 +1126,8 @@ export function invokeStubDreamer(
895
1126
  */
896
1127
  export function invokeStubPhilosopher(
897
1128
  dreamerOutput: DreamerOutput,
898
- principleId: string
1129
+ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars -- Reason: interface requires this param but stub implementation doesn't use it
1130
+ _principleId: string
899
1131
  ): PhilosopherOutput {
900
1132
  if (!dreamerOutput.valid || dreamerOutput.candidates.length === 0) {
901
1133
  return {
@@ -953,7 +1185,7 @@ export function invokeStubPhilosopher(
953
1185
  j.rank = idx + 1;
954
1186
  });
955
1187
 
956
- const topJudgment = judgments[0];
1188
+ const [topJudgment] = judgments;
957
1189
 
958
1190
  return {
959
1191
  valid: true,
@@ -969,6 +1201,7 @@ export function invokeStubPhilosopher(
969
1201
  * In production, this would call the actual Scribe subagent.
970
1202
  * The stub uses tournament selection (scoring + thresholds) to pick the winner.
971
1203
  */
1204
+ // eslint-disable-next-line @typescript-eslint/max-params -- Reason: stub scribe requires all context parameters - refactoring would break API
972
1205
  export function invokeStubScribe(
973
1206
  dreamerOutput: DreamerOutput,
974
1207
  philosopherOutput: PhilosopherOutput,
@@ -998,7 +1231,7 @@ export function invokeStubScribe(
998
1231
  return null;
999
1232
  }
1000
1233
 
1001
- const winner = tournamentResult.winner;
1234
+ const {winner} = tournamentResult;
1002
1235
 
1003
1236
  // Update telemetry with tournament info
1004
1237
  const updatedTelemetry: TrinityTelemetry = {
@@ -1046,6 +1279,7 @@ export function runTrinity(options: RunTrinityOptions): TrinityResult {
1046
1279
 
1047
1280
  // Stub path: use synchronous stub implementations
1048
1281
  if (config.useStubs) {
1282
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: function is defined later in file but callback order makes this necessary
1049
1283
  return runTrinityWithStubs(snapshot, principleId, config);
1050
1284
  }
1051
1285
 
@@ -1084,6 +1318,7 @@ export async function runTrinityAsync(options: RunTrinityOptions): Promise<Trini
1084
1318
 
1085
1319
  if (config.useStubs) {
1086
1320
  // Stub path: use synchronous stubs
1321
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: function is defined later in file but callback order makes this necessary
1087
1322
  return runTrinityWithStubs(snapshot, principleId, config);
1088
1323
  }
1089
1324
 
@@ -1125,14 +1360,19 @@ export async function runTrinityAsync(options: RunTrinityOptions): Promise<Trini
1125
1360
 
1126
1361
  try {
1127
1362
  // Step 1: Dreamer — generate candidates via real subagent
1363
+ const dreamerStart = Date.now();
1364
+ console.log(`[Trinity] Step 1/3: Invoking Dreamer (principleId=${principleId}, maxCandidates=${config.maxCandidates})`);
1128
1365
  const dreamerOutput = await adapter.invokeDreamer(snapshot, principleId, config.maxCandidates);
1366
+ console.log(`[Trinity] Dreamer completed in ${Date.now() - dreamerStart}ms, valid=${dreamerOutput.valid}, candidates=${dreamerOutput.candidates?.length ?? 0}`);
1129
1367
 
1130
1368
  if (!dreamerOutput.valid || dreamerOutput.candidates.length === 0) {
1369
+ const reason = dreamerOutput.reason ?? 'No valid candidates generated';
1370
+ console.warn(`[Trinity] Dreamer failed: ${reason}`);
1131
1371
  failures.push({
1132
1372
  stage: 'dreamer',
1133
- reason: dreamerOutput.reason ?? 'No valid candidates generated',
1373
+ reason,
1134
1374
  });
1135
- telemetry.stageFailures.push(`Dreamer: ${dreamerOutput.reason ?? 'failed'}`);
1375
+ telemetry.stageFailures.push(`Dreamer: ${reason}`);
1136
1376
  return { success: false, telemetry, failures, fallbackOccurred: false };
1137
1377
  }
1138
1378
 
@@ -1140,20 +1380,27 @@ export async function runTrinityAsync(options: RunTrinityOptions): Promise<Trini
1140
1380
  telemetry.candidateCount = dreamerOutput.candidates.length;
1141
1381
 
1142
1382
  // Step 2: Philosopher — rank candidates via real subagent
1383
+ const philosopherStart = Date.now();
1384
+ console.log(`[Trinity] Step 2/3: Invoking Philosopher (${dreamerOutput.candidates.length} candidates)`);
1143
1385
  const philosopherOutput = await adapter.invokePhilosopher(dreamerOutput, principleId);
1386
+ console.log(`[Trinity] Philosopher completed in ${Date.now() - philosopherStart}ms, valid=${philosopherOutput.valid}, judgments=${philosopherOutput.judgments?.length ?? 0}`);
1144
1387
 
1145
1388
  if (!philosopherOutput.valid || philosopherOutput.judgments.length === 0) {
1389
+ const reason = philosopherOutput.reason ?? 'No judgments produced';
1390
+ console.warn(`[Trinity] Philosopher failed: ${reason}`);
1146
1391
  failures.push({
1147
1392
  stage: 'philosopher',
1148
- reason: philosopherOutput.reason ?? 'No judgments produced',
1393
+ reason,
1149
1394
  });
1150
- telemetry.stageFailures.push(`Philosopher: ${philosopherOutput.reason ?? 'failed'}`);
1395
+ telemetry.stageFailures.push(`Philosopher: ${reason}`);
1151
1396
  return { success: false, telemetry, failures, fallbackOccurred: false };
1152
1397
  }
1153
1398
 
1154
1399
  telemetry.philosopherPassed = true;
1155
1400
 
1156
1401
  // Step 3: Scribe — synthesize final artifact via real subagent
1402
+ const scribeStart = Date.now();
1403
+ console.log(`[Trinity] Step 3/3: Invoking Scribe (synthesizing artifact)`);
1157
1404
  const draftArtifact = await adapter.invokeScribe(
1158
1405
  dreamerOutput,
1159
1406
  philosopherOutput,
@@ -1162,8 +1409,10 @@ export async function runTrinityAsync(options: RunTrinityOptions): Promise<Trini
1162
1409
  telemetry,
1163
1410
  config
1164
1411
  );
1412
+ console.log(`[Trinity] Scribe completed in ${Date.now() - scribeStart}ms, artifact=${!!draftArtifact}`);
1165
1413
 
1166
1414
  if (!draftArtifact) {
1415
+ console.warn(`[Trinity] Scribe failed: Failed to synthesize artifact from candidates`);
1167
1416
  failures.push({ stage: 'scribe', reason: 'Failed to synthesize artifact from candidates' });
1168
1417
  telemetry.stageFailures.push('Scribe: synthesis failed');
1169
1418
  return { success: false, telemetry, failures, fallbackOccurred: false };
@@ -1171,6 +1420,7 @@ export async function runTrinityAsync(options: RunTrinityOptions): Promise<Trini
1171
1420
 
1172
1421
  telemetry.scribePassed = true;
1173
1422
  telemetry.selectedCandidateIndex = draftArtifact.selectedCandidateIndex;
1423
+ console.log(`[Trinity] Trinity chain completed successfully: selectedCandidateIndex=${draftArtifact.selectedCandidateIndex}`);
1174
1424
 
1175
1425
  if (draftArtifact.telemetry) {
1176
1426
  telemetry.tournamentTrace = draftArtifact.telemetry.tournamentTrace;
@@ -1179,10 +1429,17 @@ export async function runTrinityAsync(options: RunTrinityOptions): Promise<Trini
1179
1429
  telemetry.eligibleCandidateCount = draftArtifact.telemetry.eligibleCandidateCount;
1180
1430
  }
1181
1431
 
1182
- return { success: true, artifact: draftArtifact, telemetry, failures: [], fallbackOccurred: false };
1432
+ return {
1433
+ success: true,
1434
+ artifact: draftArtifact,
1435
+ telemetry,
1436
+ failures: [],
1437
+ fallbackOccurred: false,
1438
+ artificerContext: draftArtifact.artificerContext,
1439
+ };
1183
1440
  } finally {
1184
1441
  if (adapter.close) {
1185
- await adapter.close().catch(() => {});
1442
+ await adapter.close().catch(() => { /* intentionally empty - adapter cleanup error ignored */ });
1186
1443
  }
1187
1444
  }
1188
1445
  }
@@ -1280,6 +1537,7 @@ function runTrinityWithStubs(
1280
1537
  telemetry,
1281
1538
  failures: [],
1282
1539
  fallbackOccurred: false,
1540
+ artificerContext: draftArtifact.artificerContext,
1283
1541
  };
1284
1542
  }
1285
1543