principles-disciple 1.10.0 → 1.12.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.
- package/CHANGELOG.md +26 -0
- package/README.md +66 -0
- package/esbuild.config.js +1 -1
- package/openclaw.plugin.json +4 -4
- package/package.json +2 -3
- package/run-nocturnal.mjs +30 -0
- package/scripts/db-migrate.mjs +170 -0
- package/scripts/sync-plugin.mjs +94 -5
- package/src/commands/archive-impl.ts +136 -0
- package/src/commands/capabilities.ts +4 -2
- package/src/commands/context.ts +5 -1
- package/src/commands/disable-impl.ts +151 -0
- package/src/commands/evolution-status.ts +64 -19
- package/src/commands/export.ts +8 -6
- package/src/commands/focus.ts +8 -20
- package/src/commands/nocturnal-review.ts +5 -7
- package/src/commands/nocturnal-rollout.ts +1 -12
- package/src/commands/nocturnal-train.ts +17 -42
- package/src/commands/pain.ts +10 -5
- package/src/commands/principle-rollback.ts +4 -2
- package/src/commands/promote-impl.ts +274 -0
- package/src/commands/rollback-impl.ts +234 -0
- package/src/commands/rollback.ts +6 -3
- package/src/commands/samples.ts +2 -0
- package/src/commands/thinking-os.ts +3 -4
- package/src/commands/workflow-debug.ts +2 -1
- package/src/config/errors.ts +1 -0
- package/src/core/AGENTS.md +34 -0
- package/src/core/adaptive-thresholds.ts +4 -3
- package/src/core/code-implementation-storage.ts +241 -0
- package/src/core/config.ts +5 -2
- package/src/core/control-ui-db.ts +29 -10
- package/src/core/detection-funnel.ts +12 -7
- package/src/core/diagnostician-task-store.ts +156 -0
- package/src/core/dictionary.ts +4 -4
- package/src/core/empathy-keyword-matcher.ts +7 -3
- package/src/core/empathy-types.ts +13 -2
- package/src/core/event-log.ts +14 -6
- package/src/core/evolution-engine.ts +27 -31
- package/src/core/evolution-logger.ts +3 -2
- package/src/core/evolution-reducer.ts +83 -31
- package/src/core/evolution-types.ts +10 -0
- package/src/core/external-training-contract.ts +1 -0
- package/src/core/focus-history.ts +38 -24
- package/src/core/hygiene/tracker.ts +10 -6
- package/src/core/init.ts +5 -2
- package/src/core/migration.ts +3 -3
- package/src/core/model-deployment-registry.ts +6 -4
- package/src/core/model-training-registry.ts +5 -3
- package/src/core/nocturnal-arbiter.ts +13 -14
- package/src/core/nocturnal-artifact-lineage.ts +117 -0
- package/src/core/nocturnal-artificer.ts +257 -0
- package/src/core/nocturnal-candidate-scoring.ts +4 -2
- package/src/core/nocturnal-compliance.ts +67 -19
- package/src/core/nocturnal-dataset.ts +95 -2
- package/src/core/nocturnal-executability.ts +2 -3
- package/src/core/nocturnal-export.ts +6 -3
- package/src/core/nocturnal-rule-implementation-validator.ts +245 -0
- package/src/core/nocturnal-trajectory-extractor.ts +10 -3
- package/src/core/nocturnal-trinity.ts +300 -57
- package/src/core/pain-context-extractor.ts +29 -15
- package/src/core/pain.ts +7 -5
- package/src/core/path-resolver.ts +16 -15
- package/src/core/paths.ts +2 -1
- package/src/core/pd-task-reconciler.ts +463 -0
- package/src/core/pd-task-service.ts +42 -0
- package/src/core/pd-task-store.ts +77 -0
- package/src/core/pd-task-types.ts +128 -0
- package/src/core/principle-internalization/deprecated-readiness.ts +91 -0
- package/src/core/principle-internalization/internalization-routing-policy.ts +208 -0
- package/src/core/principle-internalization/lifecycle-metrics.ts +149 -0
- package/src/core/principle-internalization/lifecycle-read-model.ts +243 -0
- package/src/core/principle-internalization/lifecycle-refresh.ts +11 -0
- package/src/core/principle-internalization/principle-lifecycle-service.ts +167 -0
- package/src/core/principle-training-state.ts +95 -370
- package/src/core/principle-tree-ledger.ts +713 -0
- package/src/core/profile.ts +3 -1
- package/src/core/promotion-gate.ts +14 -18
- package/src/core/replay-engine.ts +562 -0
- package/src/core/risk-calculator.ts +6 -4
- package/src/core/rule-host-helpers.ts +39 -0
- package/src/core/rule-host-types.ts +82 -0
- package/src/core/rule-host.ts +245 -0
- package/src/core/rule-implementation-runtime.ts +38 -0
- package/src/core/schema/db-types.ts +16 -0
- package/src/core/schema/index.ts +26 -0
- package/src/core/schema/migration-runner.ts +207 -0
- package/src/core/schema/migrations/001-init-trajectory.ts +211 -0
- package/src/core/schema/migrations/002-init-central.ts +122 -0
- package/src/core/schema/migrations/003-init-workflow.ts +55 -0
- package/src/core/schema/migrations/004-add-thinking-and-gfi.ts +74 -0
- package/src/core/schema/migrations/index.ts +31 -0
- package/src/core/schema/schema-definitions.ts +650 -0
- package/src/core/session-tracker.ts +6 -4
- package/src/core/shadow-observation-registry.ts +6 -3
- package/src/core/system-logger.ts +2 -2
- package/src/core/thinking-models.ts +182 -46
- package/src/core/thinking-os-parser.ts +164 -0
- package/src/core/training-program.ts +7 -7
- package/src/core/trajectory.ts +42 -36
- package/src/core/workspace-context.ts +77 -11
- package/src/core/workspace-dir-validation.ts +152 -0
- package/src/hooks/AGENTS.md +31 -0
- package/src/hooks/bash-risk.ts +3 -1
- package/src/hooks/edit-verification.ts +9 -5
- package/src/hooks/gate-block-helper.ts +5 -1
- package/src/hooks/gate.ts +152 -5
- package/src/hooks/gfi-gate.ts +9 -2
- package/src/hooks/lifecycle-routing.ts +124 -0
- package/src/hooks/lifecycle.ts +12 -12
- package/src/hooks/llm.ts +17 -109
- package/src/hooks/message-sanitize.ts +5 -3
- package/src/hooks/pain.ts +19 -15
- package/src/hooks/progressive-trust-gate.ts +7 -1
- package/src/hooks/prompt.ts +169 -60
- package/src/hooks/subagent.ts +5 -4
- package/src/hooks/thinking-checkpoint.ts +2 -0
- package/src/hooks/trajectory-collector.ts +15 -12
- package/src/http/principles-console-route.ts +31 -68
- package/src/i18n/commands.ts +2 -2
- package/src/index.ts +126 -40
- package/src/service/central-database.ts +131 -43
- package/src/service/central-health-service.ts +47 -0
- package/src/service/central-overview-service.ts +135 -0
- package/src/service/central-sync-service.ts +87 -0
- package/src/service/control-ui-query-service.ts +46 -36
- package/src/service/event-log-auditor.ts +261 -0
- package/src/service/evolution-query-service.ts +23 -22
- package/src/service/evolution-worker.ts +565 -261
- package/src/service/health-query-service.ts +213 -36
- package/src/service/nocturnal-runtime.ts +8 -4
- package/src/service/nocturnal-service.ts +499 -59
- package/src/service/nocturnal-target-selector.ts +5 -7
- package/src/service/runtime-summary-service.ts +2 -1
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +25 -336
- package/src/service/subagent-workflow/dynamic-timeout.ts +30 -0
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +48 -386
- package/src/service/subagent-workflow/index.ts +2 -0
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +155 -285
- package/src/service/subagent-workflow/runtime-direct-driver.ts +114 -16
- package/src/service/subagent-workflow/subagent-error-utils.ts +25 -0
- package/src/service/subagent-workflow/types.ts +9 -4
- package/src/service/subagent-workflow/workflow-manager-base.ts +573 -0
- package/src/service/subagent-workflow/workflow-store.ts +71 -11
- package/src/service/trajectory-service.ts +2 -1
- package/src/tools/critique-prompt.ts +1 -1
- package/src/tools/deep-reflect.ts +175 -209
- package/src/tools/model-index.ts +2 -1
- package/src/types/event-types.ts +2 -2
- package/src/types/principle-tree-schema.ts +29 -23
- package/src/utils/file-lock.ts +5 -3
- package/src/utils/io.ts +5 -2
- package/src/utils/nlp.ts +5 -46
- package/src/utils/node-vm-polyfill.ts +11 -0
- package/src/utils/plugin-logger.ts +2 -0
- package/src/utils/retry.ts +572 -0
- package/src/utils/subagent-probe.ts +1 -1
- package/templates/langs/en/core/AGENTS.md +0 -13
- package/templates/langs/en/core/SOUL.md +1 -31
- package/templates/langs/en/core/TOOLS.md +0 -4
- package/templates/langs/en/principles/THINKING_OS.md +64 -0
- package/templates/langs/en/skills/admin/SKILL.md +0 -1
- package/templates/langs/en/skills/evolution-framework-update/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-diagnostician/SKILL.md +18 -5
- package/templates/langs/zh/core/AGENTS.md +0 -22
- package/templates/langs/zh/core/SOUL.md +1 -31
- package/templates/langs/zh/core/TOOLS.md +0 -4
- package/templates/langs/zh/principles/THINKING_OS.md +64 -0
- package/templates/langs/zh/skills/admin/SKILL.md +0 -1
- package/templates/langs/zh/skills/evolution-framework-update/SKILL.md +1 -1
- package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +25 -4
- package/tests/commands/evolution-status.test.ts +119 -0
- package/tests/commands/implementation-lifecycle.test.ts +362 -0
- package/tests/core/code-implementation-storage.test.ts +398 -0
- package/tests/core/evolution-reducer.detector-metadata.test.ts +28 -28
- package/tests/core/nocturnal-artifact-lineage.test.ts +53 -0
- package/tests/core/nocturnal-artificer.test.ts +241 -0
- package/tests/core/nocturnal-compliance-p-principles.test.ts +133 -0
- package/tests/core/nocturnal-rule-implementation-validator.test.ts +127 -0
- package/tests/core/pd-task-store.test.ts +126 -0
- package/tests/core/principle-internalization/deprecated-readiness.test.ts +193 -0
- package/tests/core/principle-internalization/internalization-routing-policy.test.ts +212 -0
- package/tests/core/principle-internalization/lifecycle-metrics.test.ts +350 -0
- package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +211 -0
- package/tests/core/principle-training-state.test.ts +228 -1
- package/tests/core/principle-tree-ledger.test.ts +423 -0
- package/tests/core/regression-v1-9-1.test.ts +265 -0
- package/tests/core/replay-engine.test.ts +234 -0
- package/tests/core/rule-host-helpers.test.ts +120 -0
- package/tests/core/rule-host.test.ts +389 -0
- package/tests/core/rule-implementation-runtime.test.ts +64 -0
- package/tests/core/workspace-context.test.ts +53 -0
- package/tests/core/workspace-dir-validation.test.ts +272 -0
- package/tests/hooks/gate-rule-host-pipeline.test.ts +385 -0
- package/tests/hooks/pain.test.ts +74 -10
- package/tests/hooks/prompt.test.ts +63 -1
- package/tests/integration/principle-lifecycle.e2e.test.ts +197 -0
- package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +211 -0
- package/tests/service/data-endpoints-regression.test.ts +834 -0
- package/tests/service/evolution-worker.test.ts +0 -123
- package/tests/service/nocturnal-service-code-candidate.test.ts +330 -0
- package/tests/utils/nlp.test.ts +1 -19
- package/tests/utils/retry.test.ts +327 -0
- package/ui/src/App.tsx +1 -1
- package/ui/src/api.ts +4 -0
- package/ui/src/charts.tsx +366 -0
- package/ui/src/components/WorkspaceConfig.tsx +107 -75
- package/ui/src/i18n/ui.ts +55 -22
- package/ui/src/pages/OverviewPage.tsx +441 -81
- package/ui/src/styles.css +43 -0
- package/ui/src/types.ts +17 -1
- package/src/agents/nocturnal-dreamer.md +0 -152
- package/src/agents/nocturnal-philosopher.md +0 -138
- package/src/agents/nocturnal-reflector.md +0 -126
- package/src/agents/nocturnal-scribe.md +0 -164
- package/templates/workspace/.principles/00-kernel.md +0 -51
- package/templates/workspace/.principles/DECISION_POLICY.json +0 -44
- package/templates/workspace/.principles/PRINCIPLES.md +0 -20
- package/templates/workspace/.principles/PROFILE.json +0 -54
- package/templates/workspace/.principles/PROFILE.schema.json +0 -56
- package/templates/workspace/.principles/THINKING_OS.md +0 -64
- package/templates/workspace/.principles/THINKING_OS_ARCHIVE.md +0 -7
- package/templates/workspace/.principles/THINKING_OS_CANDIDATES.md +0 -9
- package/templates/workspace/.principles/models/_INDEX.md +0 -27
- package/templates/workspace/.principles/models/first_principles.md +0 -62
- package/templates/workspace/.principles/models/marketing_4p.md +0 -52
- package/templates/workspace/.principles/models/porter_five.md +0 -63
- package/templates/workspace/.principles/models/swot.md +0 -60
- package/templates/workspace/.principles/models/user_story_map.md +0 -63
- package/templates/workspace/.state/WORKBOARD.json +0 -4
|
@@ -31,20 +31,18 @@
|
|
|
31
31
|
import * as fs from 'fs';
|
|
32
32
|
import * as path from 'path';
|
|
33
33
|
import { randomUUID } from 'crypto';
|
|
34
|
+
import type { RecentPainContext } from './evolution-worker.js';
|
|
34
35
|
import {
|
|
35
|
-
NocturnalTrajectoryExtractor,
|
|
36
36
|
createNocturnalTrajectoryExtractor,
|
|
37
37
|
computeThinkingModelDelta,
|
|
38
38
|
type NocturnalSessionSnapshot,
|
|
39
39
|
} from '../core/nocturnal-trajectory-extractor.js';
|
|
40
40
|
import {
|
|
41
41
|
NocturnalTargetSelector,
|
|
42
|
-
selectNocturnalTarget,
|
|
43
42
|
type NocturnalSelectionResult,
|
|
44
43
|
type SkipReason,
|
|
45
44
|
} from './nocturnal-target-selector.js';
|
|
46
45
|
import {
|
|
47
|
-
validateArtifact,
|
|
48
46
|
parseAndValidateArtifact,
|
|
49
47
|
validateTrinityDraft,
|
|
50
48
|
type NocturnalArtifact,
|
|
@@ -69,7 +67,29 @@ import {
|
|
|
69
67
|
type ThresholdSignals,
|
|
70
68
|
} from '../core/adaptive-thresholds.js';
|
|
71
69
|
import {
|
|
72
|
-
|
|
70
|
+
parseArtificerOutput,
|
|
71
|
+
resolveArtificerTargetRule,
|
|
72
|
+
shouldRunArtificer,
|
|
73
|
+
type ArtificerOutput,
|
|
74
|
+
type ArtificerTargetRuleResolution,
|
|
75
|
+
} from '../core/nocturnal-artificer.js';
|
|
76
|
+
import { validateRuleImplementationCandidate } from '../core/nocturnal-rule-implementation-validator.js';
|
|
77
|
+
import { refreshPrincipleLifecycle } from '../core/principle-internalization/lifecycle-refresh.js';
|
|
78
|
+
import {
|
|
79
|
+
createImplementationAssetDir,
|
|
80
|
+
deleteImplementationAssetDir,
|
|
81
|
+
getImplementationAssetRoot,
|
|
82
|
+
type CodeImplementationLineageMetadata,
|
|
83
|
+
} from '../core/code-implementation-storage.js';
|
|
84
|
+
import {
|
|
85
|
+
appendCandidateArtifactLineageRecord,
|
|
86
|
+
appendArtifactLineageRecord,
|
|
87
|
+
} from '../core/nocturnal-artifact-lineage.js';
|
|
88
|
+
import {
|
|
89
|
+
createImplementation,
|
|
90
|
+
deleteImplementation,
|
|
91
|
+
} from '../core/principle-tree-ledger.js';
|
|
92
|
+
import {
|
|
73
93
|
checkPreflight,
|
|
74
94
|
recordRunStart,
|
|
75
95
|
recordRunEnd,
|
|
@@ -78,6 +98,7 @@ import {
|
|
|
78
98
|
} from './nocturnal-runtime.js';
|
|
79
99
|
import { NocturnalPathResolver } from '../core/nocturnal-paths.js';
|
|
80
100
|
import { registerSample } from '../core/nocturnal-dataset.js';
|
|
101
|
+
import type { Implementation } from '../types/principle-tree-schema.js';
|
|
81
102
|
|
|
82
103
|
// ---------------------------------------------------------------------------
|
|
83
104
|
// Types
|
|
@@ -131,6 +152,27 @@ export interface NocturnalRunDiagnostics {
|
|
|
131
152
|
persisted: boolean;
|
|
132
153
|
/** Persistence path (if persisted) */
|
|
133
154
|
persistedPath?: string;
|
|
155
|
+
/** Code-candidate sidecar diagnostics */
|
|
156
|
+
artificer: NocturnalArtificerDiagnostics;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface NocturnalArtificerDiagnostics {
|
|
160
|
+
status: 'skipped' | 'validation_failed' | 'persisted_candidate';
|
|
161
|
+
reason?:
|
|
162
|
+
| 'behavioral_artifact_unavailable'
|
|
163
|
+
| 'no_deterministic_rule'
|
|
164
|
+
| 'insufficient_signal_density'
|
|
165
|
+
| 'missing_scribe_input'
|
|
166
|
+
| 'parse_failed'
|
|
167
|
+
| 'rule_mismatch'
|
|
168
|
+
| 'validator_rejected'
|
|
169
|
+
| 'persistence_failed';
|
|
170
|
+
ruleResolution: ArtificerTargetRuleResolution | null;
|
|
171
|
+
validationFailures: string[];
|
|
172
|
+
implementationId?: string;
|
|
173
|
+
artifactId?: string;
|
|
174
|
+
ruleId?: string;
|
|
175
|
+
persistedPath?: string;
|
|
134
176
|
}
|
|
135
177
|
|
|
136
178
|
/**
|
|
@@ -179,7 +221,27 @@ export interface NocturnalServiceOptions {
|
|
|
179
221
|
* When provided, the target selector uses it for ranking bias and diagnostics enrichment.
|
|
180
222
|
* This threads recent pain signals into sleep_reflection targeting without merging task kinds.
|
|
181
223
|
*/
|
|
182
|
-
painContext?:
|
|
224
|
+
painContext?: RecentPainContext;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Override the principleId (skip Selector stage).
|
|
228
|
+
* When provided with snapshotOverride, the Selector stage is skipped and the provided
|
|
229
|
+
* principleId and snapshot are used directly for Trinity execution.
|
|
230
|
+
* This unifies NocturnalWorkflowManager with executeNocturnalReflectionAsync.
|
|
231
|
+
*/
|
|
232
|
+
principleIdOverride?: string;
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Override the snapshot (skip Selector stage).
|
|
236
|
+
* Must be provided together with principleIdOverride to skip Selector.
|
|
237
|
+
*/
|
|
238
|
+
snapshotOverride?: NocturnalSessionSnapshot;
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Override the Artificer JSON output (for testing).
|
|
242
|
+
* When omitted, a deterministic local candidate is synthesized.
|
|
243
|
+
*/
|
|
244
|
+
artificerOutputOverride?: string;
|
|
183
245
|
}
|
|
184
246
|
|
|
185
247
|
// ---------------------------------------------------------------------------
|
|
@@ -209,8 +271,11 @@ function invokeStubReflector(
|
|
|
209
271
|
const hasGateBlocks = snapshot.stats.totalGateBlocks > 0;
|
|
210
272
|
|
|
211
273
|
// Detect what kind of signal is available and craft appropriate artifact
|
|
274
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in all if/else branches
|
|
212
275
|
let badDecision: string;
|
|
276
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in all if/else branches
|
|
213
277
|
let betterDecision: string;
|
|
278
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in all if/else branches
|
|
214
279
|
let rationale: string;
|
|
215
280
|
|
|
216
281
|
if (hasGateBlocks) {
|
|
@@ -283,6 +348,272 @@ function persistArtifact(
|
|
|
283
348
|
return artifactPath;
|
|
284
349
|
}
|
|
285
350
|
|
|
351
|
+
function buildPainRefs(snapshot: NocturnalSessionSnapshot): string[] {
|
|
352
|
+
return snapshot.painEvents.map(
|
|
353
|
+
(painEvent) =>
|
|
354
|
+
`pain:${painEvent.source}:${painEvent.createdAt}:${(painEvent.reason ?? '').trim()}`
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function buildGateBlockRefs(snapshot: NocturnalSessionSnapshot): string[] {
|
|
359
|
+
return snapshot.gateBlocks.map(
|
|
360
|
+
(gateBlock) =>
|
|
361
|
+
`gate:${gateBlock.toolName}:${gateBlock.createdAt}:${gateBlock.reason.trim()}`
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Function signature requires all parameters for type-safe artifact construction */
|
|
366
|
+
function buildDefaultArtificerOutput(
|
|
367
|
+
ruleId: string,
|
|
368
|
+
artifact: NocturnalArtifact,
|
|
369
|
+
sourceSnapshotRef: string,
|
|
370
|
+
sourcePainIds: string[],
|
|
371
|
+
sourceGateBlockIds: string[]
|
|
372
|
+
): ArtificerOutput {
|
|
373
|
+
return {
|
|
374
|
+
ruleId,
|
|
375
|
+
implementationType: 'code',
|
|
376
|
+
candidateSource: [
|
|
377
|
+
'export const meta = {',
|
|
378
|
+
` name: ${JSON.stringify(`nocturnal-${ruleId.toLowerCase()}`)},`,
|
|
379
|
+
' version: "1.0.0",',
|
|
380
|
+
` ruleId: ${JSON.stringify(ruleId)},`,
|
|
381
|
+
` coversCondition: ${JSON.stringify(artifact.betterDecision)},`,
|
|
382
|
+
'};',
|
|
383
|
+
'',
|
|
384
|
+
'export function evaluate(input, helpers) {',
|
|
385
|
+
' const riskPath = helpers.isRiskPath();',
|
|
386
|
+
' const toolName = helpers.getToolName();',
|
|
387
|
+
' const planStatus = helpers.getPlanStatus();',
|
|
388
|
+
" if (riskPath && toolName === 'write' && planStatus !== 'READY') {",
|
|
389
|
+
' return {',
|
|
390
|
+
" decision: 'requireApproval',",
|
|
391
|
+
' matched: true,',
|
|
392
|
+
` reason: ${JSON.stringify(artifact.rationale)},`,
|
|
393
|
+
' };',
|
|
394
|
+
' }',
|
|
395
|
+
' return {',
|
|
396
|
+
" decision: 'allow',",
|
|
397
|
+
' matched: false,',
|
|
398
|
+
" reason: 'not-applicable',",
|
|
399
|
+
' };',
|
|
400
|
+
'}',
|
|
401
|
+
].join('\n'),
|
|
402
|
+
helperUsage: ['isRiskPath', 'getToolName', 'getPlanStatus'],
|
|
403
|
+
expectedDecision: 'requireApproval',
|
|
404
|
+
rationale: artifact.rationale,
|
|
405
|
+
lineage: {
|
|
406
|
+
artifactKind: 'rule-implementation-candidate',
|
|
407
|
+
sourceSnapshotRef,
|
|
408
|
+
sourcePainIds,
|
|
409
|
+
sourceGateBlockIds,
|
|
410
|
+
},
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Function signature requires all parameters for type-safe candidate persistence */
|
|
415
|
+
function persistCodeCandidate(
|
|
416
|
+
workspaceDir: string,
|
|
417
|
+
stateDir: string,
|
|
418
|
+
artifact: NocturnalArtifact,
|
|
419
|
+
selectedPrincipleId: string,
|
|
420
|
+
selectedSessionId: string,
|
|
421
|
+
parsedArtificer: ArtificerOutput
|
|
422
|
+
): NocturnalArtificerDiagnostics {
|
|
423
|
+
const implementationId = `IMPL-${randomUUID()}`;
|
|
424
|
+
const artifactId = `artifact-${randomUUID()}`;
|
|
425
|
+
const now = new Date().toISOString();
|
|
426
|
+
const assetRoot = getImplementationAssetRoot(stateDir, implementationId);
|
|
427
|
+
const entryPath = path.join(assetRoot, 'entry.js');
|
|
428
|
+
const lineage: CodeImplementationLineageMetadata = {
|
|
429
|
+
principleId: selectedPrincipleId,
|
|
430
|
+
ruleId: parsedArtificer.ruleId,
|
|
431
|
+
sourceSnapshotRef: artifact.sourceSnapshotRef,
|
|
432
|
+
sourcePainIds: [...parsedArtificer.lineage.sourcePainIds],
|
|
433
|
+
sourceGateBlockIds: [...parsedArtificer.lineage.sourceGateBlockIds],
|
|
434
|
+
sourceSessionId: selectedSessionId,
|
|
435
|
+
artificerArtifactId: artifactId,
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
const implementation: Implementation = {
|
|
439
|
+
id: implementationId,
|
|
440
|
+
ruleId: parsedArtificer.ruleId,
|
|
441
|
+
type: 'code',
|
|
442
|
+
path: entryPath,
|
|
443
|
+
version: now,
|
|
444
|
+
coversCondition: parsedArtificer.rationale,
|
|
445
|
+
coveragePercentage: 0,
|
|
446
|
+
lifecycleState: 'candidate',
|
|
447
|
+
createdAt: now,
|
|
448
|
+
updatedAt: now,
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
try {
|
|
452
|
+
createImplementation(stateDir, implementation);
|
|
453
|
+
createImplementationAssetDir(stateDir, implementationId, now, {
|
|
454
|
+
entrySource: parsedArtificer.candidateSource,
|
|
455
|
+
lineage,
|
|
456
|
+
});
|
|
457
|
+
appendCandidateArtifactLineageRecord(workspaceDir, {
|
|
458
|
+
artifactId,
|
|
459
|
+
principleId: selectedPrincipleId,
|
|
460
|
+
ruleId: parsedArtificer.ruleId,
|
|
461
|
+
sessionId: selectedSessionId,
|
|
462
|
+
sourceSnapshotRef: artifact.sourceSnapshotRef,
|
|
463
|
+
sourcePainIds: lineage.sourcePainIds,
|
|
464
|
+
sourceGateBlockIds: lineage.sourceGateBlockIds,
|
|
465
|
+
storagePath: assetRoot,
|
|
466
|
+
implementationId,
|
|
467
|
+
createdAt: now,
|
|
468
|
+
});
|
|
469
|
+
refreshPrincipleLifecycle(workspaceDir, stateDir);
|
|
470
|
+
return {
|
|
471
|
+
status: 'persisted_candidate',
|
|
472
|
+
ruleResolution: {
|
|
473
|
+
status: 'selected',
|
|
474
|
+
ruleId: parsedArtificer.ruleId,
|
|
475
|
+
reason: 'evidence-winner',
|
|
476
|
+
scores: [],
|
|
477
|
+
},
|
|
478
|
+
validationFailures: [],
|
|
479
|
+
implementationId,
|
|
480
|
+
artifactId,
|
|
481
|
+
ruleId: parsedArtificer.ruleId,
|
|
482
|
+
persistedPath: assetRoot,
|
|
483
|
+
};
|
|
484
|
+
} catch (error: unknown) {
|
|
485
|
+
deleteImplementationAssetDir(stateDir, implementationId);
|
|
486
|
+
try {
|
|
487
|
+
deleteImplementation(stateDir, implementationId);
|
|
488
|
+
} catch {
|
|
489
|
+
// Best effort cleanup to avoid leaving a half-created candidate discoverable.
|
|
490
|
+
}
|
|
491
|
+
return {
|
|
492
|
+
status: 'validation_failed',
|
|
493
|
+
reason: 'persistence_failed',
|
|
494
|
+
ruleResolution: {
|
|
495
|
+
status: 'selected',
|
|
496
|
+
ruleId: parsedArtificer.ruleId,
|
|
497
|
+
reason: 'evidence-winner',
|
|
498
|
+
scores: [],
|
|
499
|
+
},
|
|
500
|
+
validationFailures: [String(error)],
|
|
501
|
+
ruleId: parsedArtificer.ruleId,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Function signature requires all parameters for type-safe candidate persistence */
|
|
507
|
+
function maybePersistArtificerCandidate(
|
|
508
|
+
workspaceDir: string,
|
|
509
|
+
stateDir: string,
|
|
510
|
+
selectedPrincipleId: string,
|
|
511
|
+
selectedSessionId: string,
|
|
512
|
+
snapshot: NocturnalSessionSnapshot,
|
|
513
|
+
artifact: NocturnalArtifact,
|
|
514
|
+
options: NocturnalServiceOptions
|
|
515
|
+
): NocturnalArtificerDiagnostics {
|
|
516
|
+
const ruleResolution = resolveArtificerTargetRule(
|
|
517
|
+
stateDir,
|
|
518
|
+
selectedPrincipleId,
|
|
519
|
+
snapshot
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
if (ruleResolution.status !== 'selected') {
|
|
523
|
+
return {
|
|
524
|
+
status: 'skipped',
|
|
525
|
+
reason: 'no_deterministic_rule',
|
|
526
|
+
ruleResolution,
|
|
527
|
+
validationFailures: [],
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// #219: Detect fallback data source and warn about potential signal inaccuracy
|
|
532
|
+
const validationFailures: string[] = [];
|
|
533
|
+
if (snapshot._dataSource === 'pain_context_fallback') {
|
|
534
|
+
validationFailures.push('fallback_snapshot: stats derived from pain context only (trajectory extractor failed) - signal counts may be undercounted');
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (!shouldRunArtificer(snapshot, ruleResolution)) {
|
|
538
|
+
return {
|
|
539
|
+
status: 'skipped',
|
|
540
|
+
reason: 'insufficient_signal_density',
|
|
541
|
+
ruleResolution,
|
|
542
|
+
validationFailures,
|
|
543
|
+
ruleId: ruleResolution.ruleId,
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (!artifact.betterDecision || !artifact.rationale) {
|
|
548
|
+
return {
|
|
549
|
+
status: 'skipped',
|
|
550
|
+
reason: 'missing_scribe_input',
|
|
551
|
+
ruleResolution,
|
|
552
|
+
validationFailures: [],
|
|
553
|
+
ruleId: ruleResolution.ruleId,
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const sourcePainIds = buildPainRefs(snapshot);
|
|
558
|
+
const sourceGateBlockIds = buildGateBlockRefs(snapshot);
|
|
559
|
+
const parsedArtificer =
|
|
560
|
+
options.artificerOutputOverride !== undefined
|
|
561
|
+
? parseArtificerOutput(options.artificerOutputOverride)
|
|
562
|
+
: buildDefaultArtificerOutput(
|
|
563
|
+
ruleResolution.ruleId,
|
|
564
|
+
artifact,
|
|
565
|
+
artifact.sourceSnapshotRef,
|
|
566
|
+
sourcePainIds,
|
|
567
|
+
sourceGateBlockIds
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
if (!parsedArtificer) {
|
|
571
|
+
return {
|
|
572
|
+
status: 'validation_failed',
|
|
573
|
+
reason: 'parse_failed',
|
|
574
|
+
ruleResolution,
|
|
575
|
+
validationFailures: ['Artificer output could not be parsed.'],
|
|
576
|
+
ruleId: ruleResolution.ruleId,
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (parsedArtificer.ruleId !== ruleResolution.ruleId) {
|
|
581
|
+
return {
|
|
582
|
+
status: 'validation_failed',
|
|
583
|
+
reason: 'rule_mismatch',
|
|
584
|
+
ruleResolution,
|
|
585
|
+
validationFailures: [
|
|
586
|
+
`Resolved rule ${ruleResolution.ruleId} did not match candidate rule ${parsedArtificer.ruleId}.`,
|
|
587
|
+
],
|
|
588
|
+
ruleId: ruleResolution.ruleId,
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const validation = validateRuleImplementationCandidate(parsedArtificer.candidateSource);
|
|
593
|
+
if (!validation.passed) {
|
|
594
|
+
return {
|
|
595
|
+
status: 'validation_failed',
|
|
596
|
+
reason: 'validator_rejected',
|
|
597
|
+
ruleResolution,
|
|
598
|
+
validationFailures: validation.failures.map((failure) => failure.message),
|
|
599
|
+
ruleId: ruleResolution.ruleId,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const persisted = persistCodeCandidate(
|
|
604
|
+
workspaceDir,
|
|
605
|
+
stateDir,
|
|
606
|
+
artifact,
|
|
607
|
+
selectedPrincipleId,
|
|
608
|
+
selectedSessionId,
|
|
609
|
+
parsedArtificer
|
|
610
|
+
);
|
|
611
|
+
return {
|
|
612
|
+
...persisted,
|
|
613
|
+
ruleResolution,
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
|
|
286
617
|
// ---------------------------------------------------------------------------
|
|
287
618
|
// Main Orchestrator
|
|
288
619
|
// ---------------------------------------------------------------------------
|
|
@@ -320,6 +651,11 @@ export function executeNocturnalReflection(
|
|
|
320
651
|
arbiterResult: null,
|
|
321
652
|
executabilityResult: null,
|
|
322
653
|
persisted: false,
|
|
654
|
+
artificer: {
|
|
655
|
+
status: 'skipped',
|
|
656
|
+
ruleResolution: null,
|
|
657
|
+
validationFailures: [],
|
|
658
|
+
},
|
|
323
659
|
};
|
|
324
660
|
|
|
325
661
|
// -------------------------------------------------------------------------
|
|
@@ -407,10 +743,11 @@ export function executeNocturnalReflection(
|
|
|
407
743
|
// -------------------------------------------------------------------------
|
|
408
744
|
// Step 5: Artifact generation (Trinity or single-reflector)
|
|
409
745
|
// -------------------------------------------------------------------------
|
|
746
|
+
// eslint-disable-next-line no-useless-assignment -- Reason: initial value unused due to immediate reassignment in all branches
|
|
410
747
|
let trinityArtifact: TrinityDraftArtifact | null = null;
|
|
411
748
|
let trinityResult: TrinityResult | null = null;
|
|
749
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in all branches before use at line 884
|
|
412
750
|
let rawJson: string;
|
|
413
|
-
let chainModeUsed: 'trinity' | 'single-reflector' = 'single-reflector';
|
|
414
751
|
|
|
415
752
|
if (options.skipReflector) {
|
|
416
753
|
// Caller provided explicit artifact — used for testing arbiter/executability
|
|
@@ -430,7 +767,6 @@ export function executeNocturnalReflection(
|
|
|
430
767
|
diagnostics.trinityAttempted = true;
|
|
431
768
|
diagnostics.trinityResult = trinityResult;
|
|
432
769
|
diagnostics.chainModeUsed = trinityResult.success ? 'trinity' : 'single-reflector';
|
|
433
|
-
chainModeUsed = trinityResult.success ? 'trinity' : 'single-reflector';
|
|
434
770
|
|
|
435
771
|
if (!trinityResult.success) {
|
|
436
772
|
// Trinity failed — fail closed (same semantics as production)
|
|
@@ -457,7 +793,7 @@ export function executeNocturnalReflection(
|
|
|
457
793
|
// Validate Trinity draft
|
|
458
794
|
const draftValidation = validateTrinityDraft(trinityResult.artifact);
|
|
459
795
|
if (!draftValidation.valid) {
|
|
460
|
-
const failures = draftValidation
|
|
796
|
+
const {failures} = draftValidation;
|
|
461
797
|
void recordRunEnd(stateDir, 'failed', { reason: `Trinity draft invalid: ${failures.join('; ')}` }).catch((err) => {
|
|
462
798
|
console.warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
|
|
463
799
|
});
|
|
@@ -477,7 +813,7 @@ export function executeNocturnalReflection(
|
|
|
477
813
|
diagnostics,
|
|
478
814
|
};
|
|
479
815
|
}
|
|
480
|
-
trinityArtifact = trinityResult.artifact!;
|
|
816
|
+
trinityArtifact = trinityResult.artifact!; // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Reason: artifact is validated by validateTrinityDraft which returns valid: true when artifact exists
|
|
481
817
|
// Convert Trinity draft to arbiter-compatible artifact
|
|
482
818
|
const artifactData = draftToArtifact(trinityArtifact);
|
|
483
819
|
rawJson = JSON.stringify(artifactData);
|
|
@@ -502,14 +838,13 @@ export function executeNocturnalReflection(
|
|
|
502
838
|
trinityResult = runTrinity({ snapshot, principleId: selectedPrincipleId, config: effectiveConfig });
|
|
503
839
|
diagnostics.trinityResult = trinityResult;
|
|
504
840
|
diagnostics.chainModeUsed = trinityResult.success ? 'trinity' : 'single-reflector';
|
|
505
|
-
chainModeUsed = trinityResult.success ? 'trinity' : 'single-reflector';
|
|
506
841
|
|
|
507
842
|
if (trinityResult.success) {
|
|
508
843
|
// Validate Trinity draft
|
|
509
844
|
const draftValidation = validateTrinityDraft(trinityResult.artifact);
|
|
510
845
|
if (!draftValidation.valid) {
|
|
511
846
|
// Trinity draft invalid — fail closed
|
|
512
|
-
const failures = draftValidation
|
|
847
|
+
const {failures} = draftValidation;
|
|
513
848
|
void recordRunEnd(stateDir, 'failed', { reason: `Trinity draft invalid: ${failures.join('; ')}` }).catch((err) => {
|
|
514
849
|
console.warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
|
|
515
850
|
});
|
|
@@ -529,7 +864,7 @@ export function executeNocturnalReflection(
|
|
|
529
864
|
diagnostics,
|
|
530
865
|
};
|
|
531
866
|
}
|
|
532
|
-
trinityArtifact = trinityResult.artifact!;
|
|
867
|
+
trinityArtifact = trinityResult.artifact!; // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Reason: artifact is validated by validateTrinityDraft which returns valid: true when artifact exists
|
|
533
868
|
// Convert Trinity draft to arbiter-compatible artifact
|
|
534
869
|
const artifactData = draftToArtifact(trinityArtifact);
|
|
535
870
|
rawJson = JSON.stringify(artifactData);
|
|
@@ -631,6 +966,7 @@ export function executeNocturnalReflection(
|
|
|
631
966
|
boundedAction: execResult.boundedAction,
|
|
632
967
|
};
|
|
633
968
|
|
|
969
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in try, catch has early return
|
|
634
970
|
let persistedPath: string;
|
|
635
971
|
try {
|
|
636
972
|
persistedPath = persistArtifact(workspaceDir, artifactWithBoundedAction);
|
|
@@ -663,6 +999,34 @@ export function executeNocturnalReflection(
|
|
|
663
999
|
console.warn(`[nocturnal-service] Failed to register sample in dataset registry: ${String(err)}`);
|
|
664
1000
|
}
|
|
665
1001
|
|
|
1002
|
+
try {
|
|
1003
|
+
appendArtifactLineageRecord(workspaceDir, {
|
|
1004
|
+
artifactKind: 'behavioral-sample',
|
|
1005
|
+
artifactId: arbiterResult.artifact.artifactId,
|
|
1006
|
+
principleId: selectedPrincipleId,
|
|
1007
|
+
ruleId: null,
|
|
1008
|
+
sessionId: selectedSessionId,
|
|
1009
|
+
sourceSnapshotRef: arbiterResult.artifact.sourceSnapshotRef,
|
|
1010
|
+
sourcePainIds: buildPainRefs(snapshot),
|
|
1011
|
+
sourceGateBlockIds: buildGateBlockRefs(snapshot),
|
|
1012
|
+
storagePath: persistedPath,
|
|
1013
|
+
implementationId: null,
|
|
1014
|
+
createdAt: arbiterResult.artifact.createdAt,
|
|
1015
|
+
});
|
|
1016
|
+
} catch (err) {
|
|
1017
|
+
console.warn(`[nocturnal-service] Failed to append behavioral artifact lineage: ${String(err)}`);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
diagnostics.artificer = maybePersistArtificerCandidate(
|
|
1021
|
+
workspaceDir,
|
|
1022
|
+
stateDir,
|
|
1023
|
+
selectedPrincipleId,
|
|
1024
|
+
selectedSessionId,
|
|
1025
|
+
snapshot,
|
|
1026
|
+
arbiterResult.artifact,
|
|
1027
|
+
options
|
|
1028
|
+
);
|
|
1029
|
+
|
|
666
1030
|
// -------------------------------------------------------------------------
|
|
667
1031
|
// Step 9: Record run success
|
|
668
1032
|
// -------------------------------------------------------------------------
|
|
@@ -726,6 +1090,7 @@ export async function executeNocturnalReflectionAsync(
|
|
|
726
1090
|
|
|
727
1091
|
// If runtime adapter is provided, use async Trinity path
|
|
728
1092
|
if (options.runtimeAdapter) {
|
|
1093
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
729
1094
|
return executeNocturnalReflectionWithAdapter(workspaceDir, stateDir, options);
|
|
730
1095
|
}
|
|
731
1096
|
|
|
@@ -752,6 +1117,11 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
752
1117
|
arbiterResult: null,
|
|
753
1118
|
executabilityResult: null,
|
|
754
1119
|
persisted: false,
|
|
1120
|
+
artificer: {
|
|
1121
|
+
status: 'skipped',
|
|
1122
|
+
ruleResolution: null,
|
|
1123
|
+
validationFailures: [],
|
|
1124
|
+
},
|
|
755
1125
|
};
|
|
756
1126
|
|
|
757
1127
|
// Step 1: Pre-flight check
|
|
@@ -768,51 +1138,93 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
768
1138
|
return { success: false, noTargetSelected: false, validationFailed: false, validationFailures: [], diagnostics };
|
|
769
1139
|
}
|
|
770
1140
|
|
|
771
|
-
// Step 2: Target selection
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
1141
|
+
// Step 2: Target selection (or use override to skip)
|
|
1142
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- Reason: assigned immediately in all branches before use
|
|
1143
|
+
let selectedPrincipleId: string | undefined;
|
|
1144
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- Reason: assigned immediately in all branches before use
|
|
1145
|
+
let selectedSessionId: string | undefined;
|
|
1146
|
+
// eslint-disable-next-line no-useless-assignment -- Reason: initial value unused due to immediate reassignment in all branches
|
|
1147
|
+
let snapshot: NocturnalSessionSnapshot | null = null;
|
|
1148
|
+
|
|
1149
|
+
if (options.principleIdOverride && options.snapshotOverride) {
|
|
1150
|
+
// Skip Selector: use provided principleId and snapshot directly
|
|
1151
|
+
selectedPrincipleId = options.principleIdOverride;
|
|
1152
|
+
selectedSessionId = options.snapshotOverride.sessionId;
|
|
1153
|
+
snapshot = options.snapshotOverride;
|
|
1154
|
+
// Calculate violation density from snapshot stats for meaningful diagnostics
|
|
1155
|
+
const snapStats = options.snapshotOverride.stats;
|
|
1156
|
+
const totalToolCalls = snapStats?.totalToolCalls ?? 0;
|
|
1157
|
+
const failureCount = snapStats?.failureCount ?? 0;
|
|
1158
|
+
const violationDensity = totalToolCalls > 0 ? failureCount / totalToolCalls : 0;
|
|
1159
|
+
diagnostics.selection = {
|
|
1160
|
+
decision: 'selected',
|
|
1161
|
+
selectedPrincipleId,
|
|
1162
|
+
selectedSessionId,
|
|
1163
|
+
skipReason: undefined,
|
|
1164
|
+
diagnostics: {
|
|
1165
|
+
totalEvaluablePrinciples: 1, // We provided one principle via override
|
|
1166
|
+
filteredByCooldown: 0,
|
|
1167
|
+
passedPrinciples: [selectedPrincipleId],
|
|
1168
|
+
violatingSessionCount: 1, // The session we're using
|
|
1169
|
+
selectedSessionViolationDensity: violationDensity,
|
|
1170
|
+
selectedPrincipleScore: 100, // Override means high priority
|
|
1171
|
+
scoringBreakdown: { override: 100 },
|
|
1172
|
+
idleCheckPassed: true,
|
|
1173
|
+
cooldownCheckPassed: true,
|
|
1174
|
+
quotaCheckPassed: true,
|
|
1175
|
+
},
|
|
1176
|
+
};
|
|
1177
|
+
diagnostics.idle = { isIdle: true, mostRecentActivityAt: 0, idleForMs: 0, userActiveSessions: 0, abandonedSessionIds: [], trajectoryGuardrailConfirmsIdle: true, reason: 'selector skipped (override provided)' };
|
|
1178
|
+
} else {
|
|
1179
|
+
// Normal Selector path
|
|
1180
|
+
const extractor = createNocturnalTrajectoryExtractor(workspaceDir, stateDir);
|
|
1181
|
+
const selector = new NocturnalTargetSelector(workspaceDir, stateDir, extractor, {
|
|
1182
|
+
idleCheckOverride: options.idleCheckOverride,
|
|
1183
|
+
recentPainContext: options.painContext,
|
|
1184
|
+
});
|
|
777
1185
|
|
|
778
|
-
|
|
779
|
-
|
|
1186
|
+
const selection = selector.select();
|
|
1187
|
+
diagnostics.selection = selection;
|
|
780
1188
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
1189
|
+
if (selection.decision === 'skip') {
|
|
1190
|
+
return {
|
|
1191
|
+
success: false,
|
|
1192
|
+
noTargetSelected: true,
|
|
1193
|
+
skipReason: selection.skipReason,
|
|
1194
|
+
validationFailed: false,
|
|
1195
|
+
validationFailures: [],
|
|
1196
|
+
diagnostics,
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
791
1199
|
|
|
792
|
-
|
|
1200
|
+
// eslint-disable-next-line @typescript-eslint/prefer-destructuring -- Reason: selectedPrincipleId/selectedSessionId are reassignable outer lets - destructuring would shadow
|
|
1201
|
+
selectedPrincipleId = selection.selectedPrincipleId;
|
|
1202
|
+
// eslint-disable-next-line @typescript-eslint/prefer-destructuring -- Reason: selectedPrincipleId/selectedSessionId are reassignable outer lets - destructuring would shadow
|
|
1203
|
+
selectedSessionId = selection.selectedSessionId;
|
|
793
1204
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
1205
|
+
if (!selectedPrincipleId || !selectedSessionId) {
|
|
1206
|
+
return {
|
|
1207
|
+
success: false,
|
|
1208
|
+
noTargetSelected: true,
|
|
1209
|
+
validationFailed: false,
|
|
1210
|
+
validationFailures: [],
|
|
1211
|
+
diagnostics,
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
803
1214
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
1215
|
+
snapshot = extractor.getNocturnalSessionSnapshot(selectedSessionId);
|
|
1216
|
+
if (!snapshot) {
|
|
1217
|
+
return {
|
|
1218
|
+
success: false,
|
|
1219
|
+
noTargetSelected: true,
|
|
1220
|
+
skipReason: 'insufficient_snapshot_data',
|
|
1221
|
+
validationFailed: false,
|
|
1222
|
+
validationFailures: [],
|
|
1223
|
+
diagnostics,
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
diagnostics.idle = { isIdle: true, mostRecentActivityAt: 0, idleForMs: 0, userActiveSessions: 0, abandonedSessionIds: [], trajectoryGuardrailConfirmsIdle: true, reason: 'preflight passed' };
|
|
814
1227
|
}
|
|
815
|
-
diagnostics.idle = { isIdle: true, mostRecentActivityAt: 0, idleForMs: 0, userActiveSessions: 0, abandonedSessionIds: [], trajectoryGuardrailConfirmsIdle: true, reason: 'preflight passed' };
|
|
816
1228
|
|
|
817
1229
|
// Step 3: Record run start
|
|
818
1230
|
void recordRunStart(stateDir, selectedPrincipleId).catch((err) => {
|
|
@@ -820,10 +1232,11 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
820
1232
|
});
|
|
821
1233
|
|
|
822
1234
|
// Step 4: Trinity execution via adapter (async)
|
|
1235
|
+
// eslint-disable-next-line no-useless-assignment -- Reason: initial value unused due to immediate reassignment in all branches
|
|
823
1236
|
let trinityArtifact: TrinityDraftArtifact | null = null;
|
|
824
1237
|
let trinityResult: TrinityResult | null = null;
|
|
1238
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in all branches before use
|
|
825
1239
|
let rawJson: string;
|
|
826
|
-
let chainModeUsed: 'trinity' | 'single-reflector' = 'single-reflector';
|
|
827
1240
|
|
|
828
1241
|
if (options.skipReflector) {
|
|
829
1242
|
if (!options.reflectorOutputOverride) {
|
|
@@ -841,7 +1254,6 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
841
1254
|
diagnostics.trinityAttempted = true;
|
|
842
1255
|
diagnostics.trinityResult = trinityResult;
|
|
843
1256
|
diagnostics.chainModeUsed = trinityResult.success ? 'trinity' : 'single-reflector';
|
|
844
|
-
chainModeUsed = trinityResult.success ? 'trinity' : 'single-reflector';
|
|
845
1257
|
|
|
846
1258
|
if (!trinityResult.success) {
|
|
847
1259
|
const failures = trinityResult.failures.map((f) => `${f.stage}: ${f.reason}`);
|
|
@@ -851,7 +1263,7 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
851
1263
|
adjustThresholdsFromSignals(stateDir, { malformedRate: 1.0, arbiterRejectRate: 0.0, executabilityRejectRate: 0.0, qualityDelta: 0.0 });
|
|
852
1264
|
return { success: false, noTargetSelected: false, validationFailed: true, validationFailures: [`Trinity override failed: ${failures.join('; ')}`], snapshot, diagnostics };
|
|
853
1265
|
}
|
|
854
|
-
trinityArtifact = trinityResult.artifact!;
|
|
1266
|
+
trinityArtifact = trinityResult.artifact!; // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Reason: artifact is validated by validateTrinityDraft which returns valid: true when artifact exists
|
|
855
1267
|
const artifactData = draftToArtifact(trinityArtifact);
|
|
856
1268
|
rawJson = JSON.stringify(artifactData);
|
|
857
1269
|
} else {
|
|
@@ -867,19 +1279,18 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
867
1279
|
trinityResult = await runTrinityAsync({ snapshot, principleId: selectedPrincipleId, config: trinityConfig });
|
|
868
1280
|
diagnostics.trinityResult = trinityResult;
|
|
869
1281
|
diagnostics.chainModeUsed = trinityResult.success ? 'trinity' : 'single-reflector';
|
|
870
|
-
chainModeUsed = trinityResult.success ? 'trinity' : 'single-reflector';
|
|
871
1282
|
|
|
872
1283
|
if (trinityResult.success) {
|
|
873
1284
|
const draftValidation = validateTrinityDraft(trinityResult.artifact);
|
|
874
1285
|
if (!draftValidation.valid) {
|
|
875
|
-
const failures = draftValidation
|
|
1286
|
+
const {failures} = draftValidation;
|
|
876
1287
|
void recordRunEnd(stateDir, 'failed', { reason: `Trinity draft invalid: ${failures.join('; ')}` }).catch((err) => {
|
|
877
1288
|
console.warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
|
|
878
1289
|
});
|
|
879
1290
|
adjustThresholdsFromSignals(stateDir, { malformedRate: 1.0, arbiterRejectRate: 0.0, executabilityRejectRate: 0.0, qualityDelta: 0.0 });
|
|
880
1291
|
return { success: false, noTargetSelected: false, validationFailed: true, validationFailures: failures, snapshot, diagnostics };
|
|
881
1292
|
}
|
|
882
|
-
trinityArtifact = trinityResult.artifact!;
|
|
1293
|
+
trinityArtifact = trinityResult.artifact!; // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Reason: artifact is validated by validateTrinityDraft which returns valid: true when artifact exists
|
|
883
1294
|
const artifactData = draftToArtifact(trinityArtifact);
|
|
884
1295
|
rawJson = JSON.stringify(artifactData);
|
|
885
1296
|
} else {
|
|
@@ -929,6 +1340,7 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
929
1340
|
|
|
930
1341
|
// Step 7: Persist artifact
|
|
931
1342
|
const artifactWithBoundedAction = { ...arbiterResult.artifact, boundedAction: execResult.boundedAction };
|
|
1343
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in try, catch has early return
|
|
932
1344
|
let persistedPath: string;
|
|
933
1345
|
try {
|
|
934
1346
|
persistedPath = persistArtifact(workspaceDir, artifactWithBoundedAction);
|
|
@@ -948,6 +1360,34 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
948
1360
|
console.warn(`[nocturnal-service] Failed to register sample in dataset registry: ${String(err)}`);
|
|
949
1361
|
}
|
|
950
1362
|
|
|
1363
|
+
try {
|
|
1364
|
+
appendArtifactLineageRecord(workspaceDir, {
|
|
1365
|
+
artifactKind: 'behavioral-sample',
|
|
1366
|
+
artifactId: arbiterResult.artifact.artifactId,
|
|
1367
|
+
principleId: selectedPrincipleId,
|
|
1368
|
+
ruleId: null,
|
|
1369
|
+
sessionId: selectedSessionId,
|
|
1370
|
+
sourceSnapshotRef: arbiterResult.artifact.sourceSnapshotRef,
|
|
1371
|
+
sourcePainIds: buildPainRefs(snapshot),
|
|
1372
|
+
sourceGateBlockIds: buildGateBlockRefs(snapshot),
|
|
1373
|
+
storagePath: persistedPath,
|
|
1374
|
+
implementationId: null,
|
|
1375
|
+
createdAt: arbiterResult.artifact.createdAt,
|
|
1376
|
+
});
|
|
1377
|
+
} catch (err) {
|
|
1378
|
+
console.warn(`[nocturnal-service] Failed to append behavioral artifact lineage: ${String(err)}`);
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
diagnostics.artificer = maybePersistArtificerCandidate(
|
|
1382
|
+
workspaceDir,
|
|
1383
|
+
stateDir,
|
|
1384
|
+
selectedPrincipleId,
|
|
1385
|
+
selectedSessionId,
|
|
1386
|
+
snapshot,
|
|
1387
|
+
arbiterResult.artifact,
|
|
1388
|
+
options
|
|
1389
|
+
);
|
|
1390
|
+
|
|
951
1391
|
// Step 9: Record run success
|
|
952
1392
|
void recordRunEnd(stateDir, 'success', { sampleCount: 1 }).catch((err) => {
|
|
953
1393
|
console.warn(`[nocturnal-service] Failed to record run end (success): ${String(err)}`);
|
|
@@ -982,9 +1422,9 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
982
1422
|
*/
|
|
983
1423
|
export function listApprovedNocturnalArtifacts(
|
|
984
1424
|
workspaceDir: string
|
|
985
|
-
):
|
|
1425
|
+
): (NocturnalArtifact & { persistedAt: string; boundedAction?: BoundedAction })[] {
|
|
986
1426
|
const samplePaths = NocturnalPathResolver.listApprovedSamples(workspaceDir);
|
|
987
|
-
const artifacts:
|
|
1427
|
+
const artifacts: (NocturnalArtifact & { persistedAt: string; boundedAction?: BoundedAction })[] = [];
|
|
988
1428
|
|
|
989
1429
|
for (const samplePath of samplePaths) {
|
|
990
1430
|
try {
|