principles-disciple 1.28.2 → 1.28.3

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 (87) hide show
  1. package/openclaw.plugin.json +1 -1
  2. package/package.json +1 -1
  3. package/src/commands/archive-impl.ts +3 -0
  4. package/src/commands/context.ts +4 -1
  5. package/src/commands/disable-impl.ts +2 -2
  6. package/src/commands/evolution-status.ts +2 -2
  7. package/src/commands/focus.ts +4 -2
  8. package/src/commands/nocturnal-train.ts +9 -1
  9. package/src/commands/pain.ts +3 -1
  10. package/src/commands/pd-reflect.ts +2 -0
  11. package/src/commands/rollback-impl.ts +5 -1
  12. package/src/commands/rollback.ts +2 -1
  13. package/src/commands/samples.ts +1 -0
  14. package/src/commands/workflow-debug.ts +1 -0
  15. package/src/core/adaptive-thresholds.ts +2 -1
  16. package/src/core/code-implementation-storage.ts +2 -0
  17. package/src/core/config.ts +1 -0
  18. package/src/core/diagnostician-task-store.ts +2 -0
  19. package/src/core/empathy-keyword-matcher.ts +4 -1
  20. package/src/core/event-log.ts +6 -3
  21. package/src/core/evolution-engine.ts +4 -1
  22. package/src/core/evolution-logger.ts +1 -0
  23. package/src/core/external-training-contract.ts +2 -1
  24. package/src/core/focus-history.ts +15 -3
  25. package/src/core/init.ts +3 -1
  26. package/src/core/merge-gate-audit.ts +3 -0
  27. package/src/core/model-deployment-registry.ts +1 -0
  28. package/src/core/model-training-registry.ts +1 -0
  29. package/src/core/nocturnal-arbiter.ts +4 -3
  30. package/src/core/nocturnal-candidate-scoring.ts +5 -0
  31. package/src/core/nocturnal-compliance.ts +22 -1
  32. package/src/core/nocturnal-dataset.ts +3 -1
  33. package/src/core/nocturnal-export.ts +5 -0
  34. package/src/core/nocturnal-reasoning-deriver.ts +6 -1
  35. package/src/core/nocturnal-snapshot-contract.ts +1 -0
  36. package/src/core/nocturnal-trinity.ts +24 -3
  37. package/src/core/pain-context-extractor.ts +3 -1
  38. package/src/core/pain.ts +3 -1
  39. package/src/core/pd-task-reconciler.ts +3 -1
  40. package/src/core/pd-task-store.ts +1 -0
  41. package/src/core/principle-internalization/deprecated-readiness.ts +2 -1
  42. package/src/core/principle-training-state.ts +2 -0
  43. package/src/core/principle-tree-ledger.ts +4 -0
  44. package/src/core/principle-tree-migration.ts +2 -1
  45. package/src/core/promotion-gate.ts +7 -1
  46. package/src/core/replay-engine.ts +10 -4
  47. package/src/core/risk-calculator.ts +2 -1
  48. package/src/core/rule-host.ts +3 -2
  49. package/src/core/session-tracker.ts +5 -2
  50. package/src/core/shadow-observation-registry.ts +1 -0
  51. package/src/core/thinking-os-parser.ts +1 -0
  52. package/src/core/trajectory.ts +9 -5
  53. package/src/hooks/bash-risk.ts +2 -0
  54. package/src/hooks/edit-verification.ts +3 -0
  55. package/src/hooks/gate-block-helper.ts +3 -0
  56. package/src/hooks/gate.ts +8 -0
  57. package/src/hooks/gfi-gate.ts +2 -0
  58. package/src/hooks/lifecycle.ts +1 -0
  59. package/src/hooks/llm.ts +1 -0
  60. package/src/hooks/pain.ts +3 -1
  61. package/src/hooks/progressive-trust-gate.ts +3 -0
  62. package/src/hooks/prompt.ts +5 -2
  63. package/src/hooks/subagent.ts +1 -0
  64. package/src/hooks/thinking-checkpoint.ts +1 -0
  65. package/src/hooks/trajectory-collector.ts +2 -1
  66. package/src/http/principles-console-route.ts +5 -2
  67. package/src/index.ts +7 -0
  68. package/src/service/central-health-service.ts +1 -0
  69. package/src/service/central-overview-service.ts +2 -0
  70. package/src/service/evolution-query-service.ts +1 -0
  71. package/src/service/evolution-worker.ts +31 -1
  72. package/src/service/health-query-service.ts +6 -6
  73. package/src/service/monitoring-query-service.ts +4 -0
  74. package/src/service/nocturnal-runtime.ts +7 -5
  75. package/src/service/nocturnal-service.ts +21 -0
  76. package/src/service/nocturnal-target-selector.ts +2 -0
  77. package/src/service/runtime-summary-service.ts +6 -5
  78. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +2 -1
  79. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +2 -0
  80. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +3 -2
  81. package/src/service/subagent-workflow/workflow-manager-base.ts +6 -1
  82. package/src/service/subagent-workflow/workflow-store.ts +2 -0
  83. package/src/tools/deep-reflect.ts +9 -0
  84. package/src/tools/model-index.ts +1 -0
  85. package/src/tools/write-pain-flag.ts +1 -0
  86. package/src/utils/file-lock.ts +1 -0
  87. package/src/utils/io.ts +2 -1
@@ -155,6 +155,7 @@ export class EvolutionQueryService {
155
155
  * 注意:不关闭 trajectory,因为它是单例由 TrajectoryRegistry 管理
156
156
  */
157
157
 
158
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
158
159
  dispose(): void {
159
160
  // EvolutionQueryService 不拥有 trajectory,所以不关闭它
160
161
  // trajectory 是由 TrajectoryRegistry 管理的单例
@@ -427,6 +427,7 @@ export const LOCK_RETRY_DELAY_MS = 50;
427
427
  export const LOCK_STALE_MS = 30_000;
428
428
 
429
429
 
430
+ // eslint-disable-next-line @typescript-eslint/max-params
430
431
  export function createEvolutionTaskId(
431
432
  source: string,
432
433
  score: number,
@@ -460,6 +461,7 @@ export async function acquireQueueLock(resourcePath: string, logger: PluginLogge
460
461
  }
461
462
 
462
463
 
464
+ // eslint-disable-next-line @typescript-eslint/max-params
463
465
  async function requireQueueLock(resourcePath: string, logger: PluginLogger | { warn?: (message: string) => void; info?: (message: string) => void } | undefined, scope: string, lockSuffix: string = EVOLUTION_QUEUE_LOCK_SUFFIX): Promise<() => void> {
464
466
  try {
465
467
  return await acquireQueueLock(resourcePath, logger, lockSuffix);
@@ -475,6 +477,7 @@ export function extractEvolutionTaskId(task: string): string | null {
475
477
  }
476
478
 
477
479
 
480
+ // eslint-disable-next-line @typescript-eslint/max-params
478
481
  function findRecentDuplicateTask(
479
482
  queue: EvolutionQueueItem[],
480
483
  source: string,
@@ -483,12 +486,14 @@ function findRecentDuplicateTask(
483
486
  reason?: string
484
487
  ): EvolutionQueueItem | undefined {
485
488
 
489
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
486
490
  const key = normalizePainDedupKey(source, preview, reason);
487
491
  return queue.find((task) => {
488
492
  if (task.status === 'completed') return false;
489
493
  const taskTime = new Date(task.enqueued_at || task.timestamp).getTime();
490
494
  if (!Number.isFinite(taskTime) || (now - taskTime) > PAIN_QUEUE_DEDUP_WINDOW_MS) return false;
491
495
 
496
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
492
497
  return normalizePainDedupKey(task.source, task.trigger_text_preview || '', task.reason) === key;
493
498
  });
494
499
  }
@@ -542,6 +547,7 @@ function normalizePainDedupKey(source: string, preview: string, reason?: string)
542
547
  }
543
548
 
544
549
 
550
+ // eslint-disable-next-line @typescript-eslint/max-params
545
551
  export function hasRecentDuplicateTask(queue: EvolutionQueueItem[], source: string, preview: string, now: number, reason?: string): boolean {
546
552
  return !!findRecentDuplicateTask(queue, source, preview, now, reason);
547
553
  }
@@ -657,6 +663,7 @@ function shouldSkipForDedup(
657
663
  const recentSimilarReflection = hasRecentSimilarReflection(queue, painSourceKey, now);
658
664
 
659
665
  if (recentSimilarReflection) {
666
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
660
667
  const completedTime = new Date(recentSimilarReflection.completed_at!).getTime();
661
668
  logger?.debug?.(`[PD:EvolutionWorker] Skipping sleep_reflection — similar reflection completed ${Math.round((now - completedTime) / 60000)}min ago (same pain pattern: ${painSourceKey})`);
662
669
  return true;
@@ -668,6 +675,7 @@ function shouldSkipForDedup(
668
675
  * Load and migrate the evolution queue. Returns empty array if file doesn't exist.
669
676
  */
670
677
  function loadEvolutionQueue(queuePath: string): EvolutionQueueItem[] {
678
+ // eslint-disable-next-line no-useless-assignment
671
679
  let rawQueue: RawQueueItem[] = [];
672
680
  try {
673
681
  rawQueue = JSON.parse(fs.readFileSync(queuePath, 'utf8'));
@@ -681,6 +689,7 @@ function loadEvolutionQueue(queuePath: string): EvolutionQueueItem[] {
681
689
  /**
682
690
  * Build and persist a new sleep_reflection task.
683
691
  */
692
+ // eslint-disable-next-line @typescript-eslint/max-params
684
693
  function enqueueNewSleepReflectionTask(
685
694
  queue: EvolutionQueueItem[],
686
695
  recentPainContext: ReturnType<typeof readRecentPainContext>,
@@ -752,6 +761,7 @@ interface ParsedPainValues {
752
761
  }
753
762
 
754
763
 
764
+ // eslint-disable-next-line @typescript-eslint/max-params
755
765
  async function doEnqueuePainTask(
756
766
  wctx: WorkspaceContext, logger: PluginLogger, painFlagPath: string,
757
767
  result: WorkerStatusReport['pain_flag'], v: ParsedPainValues,
@@ -998,6 +1008,7 @@ async function checkPainFlag(wctx: WorkspaceContext, logger: PluginLogger): Prom
998
1008
  }
999
1009
 
1000
1010
 
1011
+ // eslint-disable-next-line @typescript-eslint/max-params
1001
1012
  async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogger, eventLog: EventLog, api?: OpenClawPluginApi) {
1002
1013
  const queuePath = wctx.resolve('EVOLUTION_QUEUE');
1003
1014
  if (!fs.existsSync(queuePath)) {
@@ -1595,10 +1606,13 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
1595
1606
  logger?.info?.(`[PD:EvolutionWorker] Processing sleep_reflection task ${sleepTask.id}`);
1596
1607
  }
1597
1608
 
1609
+ // eslint-disable-next-line @typescript-eslint/init-declarations
1598
1610
  let workflowId: string | undefined;
1599
1611
 
1612
+ // eslint-disable-next-line @typescript-eslint/init-declarations
1600
1613
  let nocturnalManager: NocturnalWorkflowManager;
1601
1614
 
1615
+ // eslint-disable-next-line @typescript-eslint/init-declarations
1602
1616
  let snapshotData: NocturnalSessionSnapshot | undefined;
1603
1617
 
1604
1618
  if (isPollingTask) {
@@ -1636,11 +1650,13 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
1636
1650
  s => s.failureCount > 0 || s.painEventCount > 0 || s.gateBlockCount > 0
1637
1651
  );
1638
1652
  if (sessionsWithViolations.length > 0) {
1653
+ // eslint-disable-next-line @typescript-eslint/prefer-destructuring
1639
1654
  const targetSession = sessionsWithViolations[0];
1640
1655
  logger?.info?.(`[PD:EvolutionWorker] Task ${sleepTask.id} using session with violations: ${targetSession.sessionId} (failed=${targetSession.failureCount}, pain=${targetSession.painEventCount}, gates=${targetSession.gateBlockCount})`);
1641
1656
  fullSnapshot = extractor.getNocturnalSessionSnapshot(targetSession.sessionId);
1642
1657
  } else if (recentSessions.length > 0) {
1643
1658
  // No sessions with violations, use most recent as last resort
1659
+ // eslint-disable-next-line @typescript-eslint/prefer-destructuring
1644
1660
  const latestSession = recentSessions[0];
1645
1661
  logger?.warn?.(`[PD:EvolutionWorker] Task ${sleepTask.id} no sessions with violations found, using most recent: ${latestSession.sessionId} (failed=${latestSession.failureCount}, pain=${latestSession.painEventCount}, gates=${latestSession.gateBlockCount})`);
1646
1662
  fullSnapshot = extractor.getNocturnalSessionSnapshot(latestSession.sessionId);
@@ -1707,6 +1723,7 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
1707
1723
  },
1708
1724
  });
1709
1725
  sleepTask.resultRef = workflowHandle.workflowId;
1726
+ // eslint-disable-next-line @typescript-eslint/prefer-destructuring
1710
1727
  workflowId = workflowHandle.workflowId;
1711
1728
  }
1712
1729
 
@@ -1739,13 +1756,20 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
1739
1756
  const errorReason = lastEvent?.reason ?? 'unknown';
1740
1757
  // #219: Include payload details for better diagnostics
1741
1758
  let detailedError = `Workflow terminal_error: ${errorReason}`;
1759
+
1742
1760
  let payload: unknown = {};
1761
+
1743
1762
  try {
1744
1763
  payload = lastEvent?.payload ?? {};
1764
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1745
1765
  if ((payload as any).skipReason) {
1766
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1746
1767
  detailedError += ` (skipReason: ${(payload as any).skipReason})`;
1768
+
1747
1769
  }
1770
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1748
1771
  if ((payload as any).failures && Array.isArray((payload as any).failures) && (payload as any).failures.length > 0) {
1772
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1749
1773
  detailedError += ` | failures: ${((payload as any).failures as string[]).slice(0, 3).join(', ')}`;
1750
1774
  }
1751
1775
  } catch { /* ignore parse errors */ }
@@ -1755,9 +1779,12 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
1755
1779
  if (isExpectedSubagentError(errorReason)) {
1756
1780
  // #237: Expected unavailability → stub fallback, not hard failure
1757
1781
  sleepTask.status = 'completed';
1782
+
1758
1783
  sleepTask.completed_at = new Date().toISOString();
1759
1784
  sleepTask.resolution = 'stub_fallback';
1785
+
1760
1786
  logger?.warn?.(`[PD:EvolutionWorker] sleep_reflection task ${sleepTask.id} background runtime unavailable, using stub fallback: ${errorReason}`);
1787
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1761
1788
  } else if ((payload as any).skipReason === 'no_violating_sessions') {
1762
1789
  // #244: No meaningful violations found (thin filter) → skip without failure
1763
1790
  sleepTask.status = 'completed';
@@ -1873,7 +1900,7 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
1873
1900
  }
1874
1901
  }
1875
1902
 
1876
- // eslint-disable-next-line complexity -- complexity 12, refactor candidate
1903
+
1877
1904
  async function processDetectionQueue(wctx: WorkspaceContext, api: OpenClawPluginApi, eventLog: EventLog) {
1878
1905
  const {logger} = api;
1879
1906
  try {
@@ -1929,6 +1956,7 @@ async function processDetectionQueue(wctx: WorkspaceContext, api: OpenClawPlugin
1929
1956
  // Evolution queue is now the single active pain→principle path
1930
1957
 
1931
1958
 
1959
+ // eslint-disable-next-line @typescript-eslint/max-params
1932
1960
  export async function registerEvolutionTaskSession(
1933
1961
  workspaceResolve: (key: string) => string,
1934
1962
  taskId: string,
@@ -1942,6 +1970,7 @@ export async function registerEvolutionTaskSession(
1942
1970
 
1943
1971
  try {
1944
1972
 
1973
+ // eslint-disable-next-line @typescript-eslint/init-declarations
1945
1974
  let rawQueue: RawQueueItem[];
1946
1975
  try {
1947
1976
  rawQueue = JSON.parse(fs.readFileSync(queuePath, 'utf8'));
@@ -2009,6 +2038,7 @@ function writeWorkerStatus(stateDir: string, report: WorkerStatusReport): void {
2009
2038
  }
2010
2039
 
2011
2040
 
2041
+ // eslint-disable-next-line @typescript-eslint/max-params
2012
2042
  async function processEvolutionQueueWithResult(
2013
2043
  wctx: WorkspaceContext,
2014
2044
  logger: PluginLogger,
@@ -337,7 +337,7 @@ export class HealthQueryService {
337
337
  }));
338
338
  }
339
339
 
340
- // eslint-disable-next-line complexity -- complexity 13, refactor candidate
340
+
341
341
  getGateStats(): {
342
342
  today: {
343
343
  gfiBlocks: number;
@@ -659,7 +659,7 @@ export class HealthQueryService {
659
659
  return null;
660
660
  }
661
661
 
662
- // eslint-disable-next-line complexity -- complexity 14, refactor candidate
662
+
663
663
  private readNocturnalTraining(): {
664
664
  queue: { pending: number; inProgress: number; completed: number };
665
665
  trinityRecords: { artifactId: string; status: string; createdAt: string }[];
@@ -787,8 +787,8 @@ export class HealthQueryService {
787
787
  }
788
788
 
789
789
 
790
- // eslint-disable-next-line @typescript-eslint/class-methods-use-this
791
- // eslint-disable-next-line complexity -- complexity 15, refactor candidate
790
+
791
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this -- complexity 15, refactor candidate
792
792
  private getEventDedupKey(entry: EventLogEntry): string {
793
793
  const eventId = typeof entry.data?.eventId === 'string' ? entry.data.eventId : null;
794
794
  if (eventId) {
@@ -859,7 +859,7 @@ export class HealthQueryService {
859
859
  }
860
860
 
861
861
 
862
- // eslint-disable-next-line complexity -- complexity 13, refactor candidate
862
+
863
863
  // eslint-disable-next-line @typescript-eslint/class-methods-use-this
864
864
  private resolveGateType(row: GateBlockRow): string {
865
865
  if (typeof row.gate_type === 'string' && row.gate_type.trim().length > 0) {
@@ -994,7 +994,7 @@ export class HealthQueryService {
994
994
  * Read the most recent session JSON file from disk.
995
995
  * Used to sync GFI from session-tracker's persistence into SQLite.
996
996
  */
997
- // eslint-disable-next-line complexity -- complexity 11, slightly over threshold
997
+
998
998
  private readLatestSessionFromFile(): SessionState | null {
999
999
  const sessionsDir = path.join(this.stateDir, 'sessions');
1000
1000
  if (!fs.existsSync(sessionsDir)) {
@@ -36,6 +36,7 @@ export class MonitoringQueryService {
36
36
  const now = Date.now();
37
37
  const workflowsWithStuckDetection = workflows.map(wf => {
38
38
  // Parse metadata for timeout configuration
39
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
39
40
  const metadata = parseWorkflowMetadata(wf.metadata_json);
40
41
  const timeoutMs = metadata.timeoutMs ?? 15 * 60 * 1000; // Default 15 minutes
41
42
 
@@ -84,8 +85,10 @@ export class MonitoringQueryService {
84
85
 
85
86
  // Determine status
86
87
 
88
+ // eslint-disable-next-line @typescript-eslint/init-declarations
87
89
  let status: 'pending' | 'running' | 'completed' | 'failed';
88
90
 
91
+ // eslint-disable-next-line @typescript-eslint/init-declarations
89
92
  let reason: string | undefined;
90
93
 
91
94
  if (!startEvent) {
@@ -107,6 +110,7 @@ export class MonitoringQueryService {
107
110
 
108
111
  // Calculate duration if stage started and completed/failed
109
112
 
113
+ // eslint-disable-next-line @typescript-eslint/init-declarations
110
114
  let duration: number | undefined;
111
115
  if (startEvent && (completeEvent || failedEvent)) {
112
116
  const endEvent = completeEvent || failedEvent;
@@ -40,7 +40,7 @@ import { withLockAsync } from '../utils/file-lock.js';
40
40
  * Excluded (NOT system sessions):
41
41
  * - User sessions like agent:main:feishu:user:xxx — third component is channel type
42
42
  */
43
- // eslint-disable-next-line complexity -- complexity 15, refactor candidate
43
+
44
44
  function isSystemSession(state: SessionState): boolean {
45
45
  const { sessionId, sessionKey, trigger } = state;
46
46
 
@@ -270,7 +270,7 @@ async function writeState(stateDir: string, state: NocturnalRuntimeState): Promi
270
270
  * @param trajectoryLastActivityAt - Optional trajectory timestamp as secondary guardrail
271
271
  * @returns IdleCheckResult with full diagnostic information
272
272
  */
273
- // eslint-disable-next-line complexity -- complexity 14, refactor candidate
273
+
274
274
  export function checkWorkspaceIdle(
275
275
  workspaceDir: string,
276
276
  options: {
@@ -321,6 +321,7 @@ export function checkWorkspaceIdle(
321
321
  }
322
322
 
323
323
 
324
+ // eslint-disable-next-line @typescript-eslint/init-declarations
324
325
  let reason: string;
325
326
  if (mostRecentActivityAt === 0) {
326
327
  reason = 'No active sessions found — workspace is idle';
@@ -357,7 +358,7 @@ export function checkWorkspaceIdle(
357
358
  * @param options - Cooldown configuration options
358
359
  * @returns CooldownCheckResult
359
360
  */
360
- // eslint-disable-next-line complexity -- complexity 11, slightly over threshold
361
+
361
362
  export function checkCooldown(
362
363
  stateDir: string,
363
364
  principleId?: string,
@@ -369,7 +370,7 @@ export function checkCooldown(
369
370
  } = {}
370
371
  ): CooldownCheckResult {
371
372
  const {
372
- /* eslint-disable @typescript-eslint/no-unused-vars -- Reason: Cooldown parameters reserved for future quota enforcement */
373
+
373
374
  globalCooldownMs: _globalCooldownMs = DEFAULT_GLOBAL_COOLDOWN_MS,
374
375
  principleCooldownMs: _principleCooldownMs = DEFAULT_PRINCIPLE_COOLDOWN_MS,
375
376
  maxRunsPerWindow = DEFAULT_MAX_RUNS_PER_WINDOW,
@@ -389,6 +390,7 @@ export function checkCooldown(
389
390
  if (cooldownEnd > now) {
390
391
  globalCooldownActive = true;
391
392
  globalCooldownRemainingMs = cooldownEnd - now;
393
+ // eslint-disable-next-line @typescript-eslint/prefer-destructuring
392
394
  globalCooldownUntil = state.globalCooldownUntil;
393
395
  }
394
396
  }
@@ -558,7 +560,7 @@ export interface PreflightCheckResult {
558
560
  * @param idleCheckOverride - Optional override for idle check result (for testing)
559
561
  */
560
562
 
561
- // eslint-disable-next-line complexity -- complexity 12, refactor candidate
563
+ // eslint-disable-next-line @typescript-eslint/max-params -- complexity 12, refactor candidate
562
564
  export function checkPreflight(
563
565
  workspaceDir: string,
564
566
  stateDir: string,
@@ -289,12 +289,16 @@ function invokeStubReflector(
289
289
  const hasPain = snapshot.stats.totalPainEvents > 0;
290
290
  const hasFailures = (snapshot.stats.failureCount ?? 0) > 0;
291
291
 
292
+ // eslint-disable-next-line @typescript-eslint/init-declarations
292
293
  let badDecision: string;
294
+ // eslint-disable-next-line @typescript-eslint/init-declarations
293
295
  let betterDecision: string;
296
+ // eslint-disable-next-line @typescript-eslint/init-declarations
294
297
  let rationale: string;
295
298
 
296
299
  if (hasGateBlocks && snapshot.gateBlocks.length > 0) {
297
300
  // Use actual gate block content
301
+ // eslint-disable-next-line @typescript-eslint/prefer-destructuring
298
302
  const block = snapshot.gateBlocks[0];
299
303
  const tool = block.toolName ?? 'a tool';
300
304
  const file = block.filePath ? ` on ${block.filePath}` : '';
@@ -303,6 +307,7 @@ function invokeStubReflector(
303
307
  rationale = `Gate blocks exist for a reason — bypassing them without understanding the underlying constraint risks unintended consequences. The block on ${tool}${file} indicates the operation exceeded allowed thresholds for the current evolution tier.`;
304
308
  } else if (hasPain && snapshot.painEvents.length > 0) {
305
309
  // Use actual pain event content
310
+ // eslint-disable-next-line @typescript-eslint/prefer-destructuring
306
311
  const pain = snapshot.painEvents[0];
307
312
  const painSource = pain.source ?? 'unknown';
308
313
  const painReason = pain.reason ? `: ${pain.reason}` : '';
@@ -398,6 +403,7 @@ function buildGateBlockRefs(snapshot: NocturnalSessionSnapshot): string[] {
398
403
  }
399
404
 
400
405
 
406
+ // eslint-disable-next-line @typescript-eslint/max-params
401
407
  function buildDefaultArtificerOutput(
402
408
  ruleId: string,
403
409
  artifact: NocturnalArtifact,
@@ -447,6 +453,7 @@ function buildDefaultArtificerOutput(
447
453
  }
448
454
 
449
455
 
456
+ // eslint-disable-next-line @typescript-eslint/max-params
450
457
  function persistCodeCandidate(
451
458
  workspaceDir: string,
452
459
  stateDir: string,
@@ -543,6 +550,7 @@ function persistCodeCandidate(
543
550
  }
544
551
 
545
552
 
553
+ // eslint-disable-next-line @typescript-eslint/max-params
546
554
  function maybePersistArtificerCandidate(
547
555
  workspaceDir: string,
548
556
  stateDir: string,
@@ -784,9 +792,11 @@ export function executeNocturnalReflection(
784
792
  // Step 5: Artifact generation (Trinity or single-reflector)
785
793
  // -------------------------------------------------------------------------
786
794
 
795
+ // eslint-disable-next-line no-useless-assignment
787
796
  let trinityArtifact: TrinityDraftArtifact | null = null;
788
797
  let trinityResult: TrinityResult | null = null;
789
798
 
799
+ // eslint-disable-next-line @typescript-eslint/init-declarations
790
800
  let rawJson: string;
791
801
 
792
802
  if (options.skipReflector) {
@@ -1011,6 +1021,7 @@ export function executeNocturnalReflection(
1011
1021
  };
1012
1022
 
1013
1023
 
1024
+ // eslint-disable-next-line @typescript-eslint/init-declarations
1014
1025
  let persistedPath: string;
1015
1026
  try {
1016
1027
  persistedPath = persistArtifact(workspaceDir, artifactWithBoundedAction);
@@ -1138,6 +1149,7 @@ export async function executeNocturnalReflectionAsync(
1138
1149
  // If runtime adapter is provided, use async Trinity path
1139
1150
  if (options.runtimeAdapter) {
1140
1151
 
1152
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
1141
1153
  return executeNocturnalReflectionWithAdapter(workspaceDir, stateDir, options);
1142
1154
  }
1143
1155
 
@@ -1188,10 +1200,13 @@ async function executeNocturnalReflectionWithAdapter(
1188
1200
 
1189
1201
  // Step 2: Target selection (or use override to skip)
1190
1202
 
1203
+ // eslint-disable-next-line @typescript-eslint/init-declarations
1191
1204
  let selectedPrincipleId: string | undefined;
1192
1205
 
1206
+ // eslint-disable-next-line @typescript-eslint/init-declarations
1193
1207
  let selectedSessionId: string | undefined;
1194
1208
 
1209
+ // eslint-disable-next-line no-useless-assignment
1195
1210
  let snapshot: NocturnalSessionSnapshot | null = null;
1196
1211
 
1197
1212
  if (options.principleIdOverride && options.snapshotOverride) {
@@ -1214,6 +1229,7 @@ async function executeNocturnalReflectionWithAdapter(
1214
1229
  // Skip Selector: use provided principleId and snapshot directly
1215
1230
  selectedPrincipleId = options.principleIdOverride;
1216
1231
  selectedSessionId = snapshotValidation.snapshot.sessionId;
1232
+ // eslint-disable-next-line @typescript-eslint/prefer-destructuring
1217
1233
  snapshot = snapshotValidation.snapshot;
1218
1234
  // Calculate violation density from snapshot stats for meaningful diagnostics
1219
1235
  const snapStats = snapshotValidation.snapshot.stats;
@@ -1270,8 +1286,10 @@ async function executeNocturnalReflectionWithAdapter(
1270
1286
  }
1271
1287
 
1272
1288
 
1289
+ // eslint-disable-next-line @typescript-eslint/prefer-destructuring
1273
1290
  selectedPrincipleId = selection.selectedPrincipleId;
1274
1291
 
1292
+ // eslint-disable-next-line @typescript-eslint/prefer-destructuring
1275
1293
  selectedSessionId = selection.selectedSessionId;
1276
1294
 
1277
1295
  if (!selectedPrincipleId || !selectedSessionId) {
@@ -1305,9 +1323,11 @@ async function executeNocturnalReflectionWithAdapter(
1305
1323
 
1306
1324
  // Step 4: Trinity execution via adapter (async)
1307
1325
 
1326
+ // eslint-disable-next-line no-useless-assignment
1308
1327
  let trinityArtifact: TrinityDraftArtifact | null = null;
1309
1328
  let trinityResult: TrinityResult | null = null;
1310
1329
 
1330
+ // eslint-disable-next-line @typescript-eslint/init-declarations
1311
1331
  let rawJson: string;
1312
1332
 
1313
1333
  if (options.skipReflector) {
@@ -1414,6 +1434,7 @@ async function executeNocturnalReflectionWithAdapter(
1414
1434
  // Step 7: Persist artifact
1415
1435
  const artifactWithBoundedAction = { ...arbiterResult.artifact, boundedAction: execResult.boundedAction };
1416
1436
 
1437
+ // eslint-disable-next-line @typescript-eslint/init-declarations
1417
1438
  let persistedPath: string;
1418
1439
  try {
1419
1440
  persistedPath = persistArtifact(workspaceDir, artifactWithBoundedAction);
@@ -287,6 +287,7 @@ export class NocturnalTargetSelector {
287
287
  };
288
288
 
289
289
 
290
+ // eslint-disable-next-line @typescript-eslint/max-params
290
291
  constructor(
291
292
  workspaceDir: string,
292
293
  stateDir: string,
@@ -528,6 +529,7 @@ export class NocturnalTargetSelector {
528
529
  * This is a convenience wrapper for the common case.
529
530
  */
530
531
 
532
+ // eslint-disable-next-line @typescript-eslint/max-params
531
533
  export function selectNocturnalTarget(
532
534
  workspaceDir: string,
533
535
  stateDir: string,
@@ -362,7 +362,7 @@ export class RuntimeSummaryService {
362
362
  return { session: sessions[0], reason: 'latest_active' };
363
363
  }
364
364
 
365
- // eslint-disable-next-line complexity -- complexity 11, slightly over threshold
365
+
366
366
  private static mergeSessionSnapshots(
367
367
  persistedSessions: PersistedSessionState[],
368
368
  workspaceDir: string
@@ -420,6 +420,7 @@ export class RuntimeSummaryService {
420
420
  * Queue is the only authoritative execution truth source.
421
421
  */
422
422
 
423
+ // eslint-disable-next-line @typescript-eslint/max-params
423
424
  private static buildDirectiveSummary(
424
425
  queue: QueueItem[] | null,
425
426
  directive: DirectiveFile | null,
@@ -511,9 +512,9 @@ export class RuntimeSummaryService {
511
512
  })
512
513
  .slice(-MAX_SOURCE_EVENTS)
513
514
  .reverse();
514
- // eslint-disable-next-line complexity -- complexity 11, slightly over threshold
515
+
515
516
 
516
- // eslint-disable-next-line complexity -- complexity 11
517
+
517
518
  return filtered.map((entry) => {
518
519
  if (entry.type === 'pain_signal') {
519
520
  return {
@@ -584,7 +585,7 @@ export class RuntimeSummaryService {
584
585
  return [...merged.values()].sort((a, b) => (a.ts || '').localeCompare(b.ts || ''));
585
586
  }
586
587
 
587
- // eslint-disable-next-line complexity -- complexity 15, refactor candidate
588
+
588
589
  private static getEventDedupKey(entry: EventLogEntry): string {
589
590
  const eventId = typeof entry.data?.eventId === 'string' ? entry.data.eventId : null;
590
591
  if (eventId) {
@@ -641,7 +642,7 @@ export class RuntimeSummaryService {
641
642
  return candidate ? new Date(candidate).getTime() : NaN;
642
643
  }
643
644
 
644
- // eslint-disable-next-line complexity -- complexity 14, refactor candidate
645
+
645
646
  private static buildDirectiveTaskPreview(item: QueueItem): string {
646
647
  const task = typeof item.task === 'string' ? item.task.trim() : '';
647
648
  if (task && task.toLowerCase() !== 'undefined') {
@@ -70,6 +70,7 @@ export class DeepReflectWorkflowManager extends WorkflowManagerBase {
70
70
  }
71
71
 
72
72
 
73
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
73
74
  protected override generateWorkflowId(): string {
74
75
  return `wf_dr_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
75
76
  }
@@ -150,7 +151,7 @@ export const deepReflectWorkflowSpec: SubagentWorkflowSpec<DeepReflectResult> =
150
151
  };
151
152
  },
152
153
 
153
- // eslint-disable-next-line complexity -- complexity 13, refactor candidate
154
+
154
155
  async persistResult(ctx: WorkflowPersistContext<DeepReflectResult>): Promise<void> {
155
156
  const { result, metadata, workspaceDir } = ctx;
156
157
 
@@ -71,6 +71,7 @@ export class EmpathyObserverWorkflowManager extends WorkflowManagerBase {
71
71
  }
72
72
 
73
73
 
74
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
74
75
  protected override createWorkflowMetadata<TResult>(
75
76
  spec: SubagentWorkflowSpec<TResult>,
76
77
  options: {
@@ -104,6 +105,7 @@ export class EmpathyObserverWorkflowManager extends WorkflowManagerBase {
104
105
  }
105
106
 
106
107
 
108
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
107
109
  protected override generateWorkflowId(): string {
108
110
  return `wf_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
109
111
  }
@@ -243,7 +243,7 @@ export class NocturnalWorkflowManager implements WorkflowManager {
243
243
  // #213: Wrap fire-and-forget Promise with .catch() to prevent
244
244
  // unhandled promise rejections if anything throws outside the try-catch
245
245
  // (e.g., during parameter construction or environment errors).
246
- // eslint-disable-next-line complexity -- complexity 15, refactor candidate
246
+
247
247
  Promise.resolve().then(async () => {
248
248
  try {
249
249
  const result = await executeNocturnalReflectionAsync(
@@ -372,6 +372,7 @@ export class NocturnalWorkflowManager implements WorkflowManager {
372
372
  }
373
373
 
374
374
 
375
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
375
376
  async notifyLifecycleEvent(
376
377
 
377
378
  _workflowId: string,
@@ -652,7 +653,7 @@ export class NocturnalWorkflowManager implements WorkflowManager {
652
653
  * Derives stage events from TrinityResult.telemetry and TrinityResult.failures.
653
654
  * Always records _start event for each stage that ran, plus _complete or _failed based on outcome.
654
655
  */
655
- // eslint-disable-next-line complexity -- complexity 12, refactor candidate
656
+
656
657
  private recordStageEvents(workflowId: string, result: TrinityResult): void {
657
658
  const { telemetry, failures } = result;
658
659
 
@@ -156,6 +156,7 @@ export abstract class WorkflowManagerBase implements WorkflowManager {
156
156
  * Subclasses override to add type-specific fields.
157
157
  */
158
158
 
159
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
159
160
  protected createWorkflowMetadata<TResult>(
160
161
  spec: SubagentWorkflowSpec<TResult>,
161
162
  options: {
@@ -182,6 +183,7 @@ export abstract class WorkflowManagerBase implements WorkflowManager {
182
183
  * Subclasses override to call store.createWorkflow() with type-specific metadata.
183
184
  */
184
185
 
186
+ // eslint-disable-next-line @typescript-eslint/max-params
185
187
  protected async createWorkflowRecord<TResult>(
186
188
  workflowId: string,
187
189
  childSessionKey: string,
@@ -214,6 +216,7 @@ export abstract class WorkflowManagerBase implements WorkflowManager {
214
216
  // ── Protected Helpers ────────────────────────────────────────────────────
215
217
 
216
218
 
219
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
217
220
  protected buildRunParams<TResult>(
218
221
  spec: SubagentWorkflowSpec<TResult>,
219
222
  options: {
@@ -312,6 +315,7 @@ export abstract class WorkflowManagerBase implements WorkflowManager {
312
315
  error?: string
313
316
  ): Promise<void> {
314
317
 
318
+ // eslint-disable-next-line @typescript-eslint/init-declarations
315
319
  let workflow;
316
320
  try {
317
321
  workflow = this.store.getWorkflow(workflowId);
@@ -449,7 +453,7 @@ export abstract class WorkflowManagerBase implements WorkflowManager {
449
453
  }
450
454
  }
451
455
 
452
- // eslint-disable-next-line complexity -- complexity 11, slightly over threshold
456
+
453
457
  async sweepExpiredWorkflows(maxAgeMs?: number): Promise<number> {
454
458
  const ttl = maxAgeMs ?? this.defaultTtlMs;
455
459
  const expired = this.store.getExpiredWorkflows(ttl);
@@ -523,6 +527,7 @@ export abstract class WorkflowManagerBase implements WorkflowManager {
523
527
 
524
528
  // ── Private Helpers ───────────────────────────────────────────────────────
525
529
 
530
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
526
531
  protected generateWorkflowId(): string {
527
532
  // Subclasses override the prefix part via wf_ prefix pattern
528
533
  return `wf_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
@@ -233,6 +233,7 @@ export class WorkflowStore {
233
233
  }
234
234
 
235
235
 
236
+ // eslint-disable-next-line @typescript-eslint/max-params
236
237
  recordEvent(
237
238
  workflowId: string,
238
239
  eventType: string,
@@ -269,6 +270,7 @@ export class WorkflowStore {
269
270
  * same idempotency_key already exists, this is a no-op (idempotent).
270
271
  */
271
272
 
273
+ // eslint-disable-next-line @typescript-eslint/max-params
272
274
  recordStageOutput(
273
275
  workflowId: string,
274
276
  stage: 'dreamer' | 'philosopher',