@substrate-ai/sdlc 0.19.54
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/dist/events.d.ts +336 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +11 -0
- package/dist/events.js.map +1 -0
- package/dist/gating/conflict-detector.d.ts +59 -0
- package/dist/gating/conflict-detector.d.ts.map +1 -0
- package/dist/gating/conflict-detector.js +101 -0
- package/dist/gating/conflict-detector.js.map +1 -0
- package/dist/gating/dispatch-gate.d.ts +42 -0
- package/dist/gating/dispatch-gate.d.ts.map +1 -0
- package/dist/gating/dispatch-gate.js +197 -0
- package/dist/gating/dispatch-gate.js.map +1 -0
- package/dist/gating/index.d.ts +9 -0
- package/dist/gating/index.d.ts.map +1 -0
- package/dist/gating/index.js +8 -0
- package/dist/gating/index.js.map +1 -0
- package/dist/gating/types.d.ts +98 -0
- package/dist/gating/types.d.ts.map +1 -0
- package/dist/gating/types.js +8 -0
- package/dist/gating/types.js.map +1 -0
- package/dist/handlers/event-bridge.d.ts +56 -0
- package/dist/handlers/event-bridge.d.ts.map +1 -0
- package/dist/handlers/event-bridge.js +140 -0
- package/dist/handlers/event-bridge.js.map +1 -0
- package/dist/handlers/index.d.ts +15 -0
- package/dist/handlers/index.d.ts.map +1 -0
- package/dist/handlers/index.js +14 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/handlers/sdlc-code-review-handler.d.ts +119 -0
- package/dist/handlers/sdlc-code-review-handler.d.ts.map +1 -0
- package/dist/handlers/sdlc-code-review-handler.js +131 -0
- package/dist/handlers/sdlc-code-review-handler.js.map +1 -0
- package/dist/handlers/sdlc-create-story-handler.d.ts +97 -0
- package/dist/handlers/sdlc-create-story-handler.d.ts.map +1 -0
- package/dist/handlers/sdlc-create-story-handler.js +91 -0
- package/dist/handlers/sdlc-create-story-handler.js.map +1 -0
- package/dist/handlers/sdlc-dev-story-handler.d.ts +121 -0
- package/dist/handlers/sdlc-dev-story-handler.d.ts.map +1 -0
- package/dist/handlers/sdlc-dev-story-handler.js +288 -0
- package/dist/handlers/sdlc-dev-story-handler.js.map +1 -0
- package/dist/handlers/sdlc-phase-handler.d.ts +32 -0
- package/dist/handlers/sdlc-phase-handler.d.ts.map +1 -0
- package/dist/handlers/sdlc-phase-handler.js +166 -0
- package/dist/handlers/sdlc-phase-handler.js.map +1 -0
- package/dist/handlers/types.d.ts +132 -0
- package/dist/handlers/types.d.ts.map +1 -0
- package/dist/handlers/types.js +10 -0
- package/dist/handlers/types.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/learning/failure-classifier.d.ts +23 -0
- package/dist/learning/failure-classifier.d.ts.map +1 -0
- package/dist/learning/failure-classifier.js +75 -0
- package/dist/learning/failure-classifier.js.map +1 -0
- package/dist/learning/finding-classifier.d.ts +25 -0
- package/dist/learning/finding-classifier.d.ts.map +1 -0
- package/dist/learning/finding-classifier.js +37 -0
- package/dist/learning/finding-classifier.js.map +1 -0
- package/dist/learning/finding-lifecycle.d.ts +69 -0
- package/dist/learning/finding-lifecycle.d.ts.map +1 -0
- package/dist/learning/finding-lifecycle.js +162 -0
- package/dist/learning/finding-lifecycle.js.map +1 -0
- package/dist/learning/finding-store.d.ts +16 -0
- package/dist/learning/finding-store.d.ts.map +1 -0
- package/dist/learning/finding-store.js +26 -0
- package/dist/learning/finding-store.js.map +1 -0
- package/dist/learning/findings-injector.d.ts +34 -0
- package/dist/learning/findings-injector.d.ts.map +1 -0
- package/dist/learning/findings-injector.js +140 -0
- package/dist/learning/findings-injector.js.map +1 -0
- package/dist/learning/index.d.ts +8 -0
- package/dist/learning/index.d.ts.map +1 -0
- package/dist/learning/index.js +10 -0
- package/dist/learning/index.js.map +1 -0
- package/dist/learning/relevance-scorer.d.ts +25 -0
- package/dist/learning/relevance-scorer.d.ts.map +1 -0
- package/dist/learning/relevance-scorer.js +49 -0
- package/dist/learning/relevance-scorer.js.map +1 -0
- package/dist/learning/types.d.ts +55 -0
- package/dist/learning/types.d.ts.map +1 -0
- package/dist/learning/types.js +36 -0
- package/dist/learning/types.js.map +1 -0
- package/dist/orchestrator/graph-orchestrator.d.ts +208 -0
- package/dist/orchestrator/graph-orchestrator.d.ts.map +1 -0
- package/dist/orchestrator/graph-orchestrator.js +213 -0
- package/dist/orchestrator/graph-orchestrator.js.map +1 -0
- package/dist/run-manifest/cli-flags.d.ts +11 -0
- package/dist/run-manifest/cli-flags.d.ts.map +1 -0
- package/dist/run-manifest/cli-flags.js +10 -0
- package/dist/run-manifest/cli-flags.js.map +1 -0
- package/dist/run-manifest/index.d.ts +10 -0
- package/dist/run-manifest/index.d.ts.map +1 -0
- package/dist/run-manifest/index.js +10 -0
- package/dist/run-manifest/index.js.map +1 -0
- package/dist/run-model/cli-flags.d.ts +27 -0
- package/dist/run-model/cli-flags.d.ts.map +1 -0
- package/dist/run-model/cli-flags.js +31 -0
- package/dist/run-model/cli-flags.js.map +1 -0
- package/dist/run-model/index.d.ts +21 -0
- package/dist/run-model/index.d.ts.map +1 -0
- package/dist/run-model/index.js +19 -0
- package/dist/run-model/index.js.map +1 -0
- package/dist/run-model/per-story-state.d.ts +62 -0
- package/dist/run-model/per-story-state.d.ts.map +1 -0
- package/dist/run-model/per-story-state.js +70 -0
- package/dist/run-model/per-story-state.js.map +1 -0
- package/dist/run-model/recovery-history.d.ts +56 -0
- package/dist/run-model/recovery-history.d.ts.map +1 -0
- package/dist/run-model/recovery-history.js +83 -0
- package/dist/run-model/recovery-history.js.map +1 -0
- package/dist/run-model/run-manifest.d.ts +146 -0
- package/dist/run-model/run-manifest.d.ts.map +1 -0
- package/dist/run-model/run-manifest.js +481 -0
- package/dist/run-model/run-manifest.js.map +1 -0
- package/dist/run-model/schemas.d.ts +117 -0
- package/dist/run-model/schemas.d.ts.map +1 -0
- package/dist/run-model/schemas.js +83 -0
- package/dist/run-model/schemas.js.map +1 -0
- package/dist/run-model/supervisor-lock.d.ts +104 -0
- package/dist/run-model/supervisor-lock.d.ts.map +1 -0
- package/dist/run-model/supervisor-lock.js +284 -0
- package/dist/run-model/supervisor-lock.js.map +1 -0
- package/dist/run-model/types.d.ts +74 -0
- package/dist/run-model/types.d.ts.map +1 -0
- package/dist/run-model/types.js +8 -0
- package/dist/run-model/types.js.map +1 -0
- package/dist/run-model/verification-result.d.ts +60 -0
- package/dist/run-model/verification-result.d.ts.map +1 -0
- package/dist/run-model/verification-result.js +55 -0
- package/dist/run-model/verification-result.js.map +1 -0
- package/dist/verification/checks/acceptance-criteria-evidence-check.d.ts +21 -0
- package/dist/verification/checks/acceptance-criteria-evidence-check.d.ts.map +1 -0
- package/dist/verification/checks/acceptance-criteria-evidence-check.js +159 -0
- package/dist/verification/checks/acceptance-criteria-evidence-check.js.map +1 -0
- package/dist/verification/checks/build-check.d.ts +52 -0
- package/dist/verification/checks/build-check.d.ts.map +1 -0
- package/dist/verification/checks/build-check.js +160 -0
- package/dist/verification/checks/build-check.js.map +1 -0
- package/dist/verification/checks/index.d.ts +15 -0
- package/dist/verification/checks/index.d.ts.map +1 -0
- package/dist/verification/checks/index.js +15 -0
- package/dist/verification/checks/index.js.map +1 -0
- package/dist/verification/checks/phantom-review-check.d.ts +29 -0
- package/dist/verification/checks/phantom-review-check.d.ts.map +1 -0
- package/dist/verification/checks/phantom-review-check.js +70 -0
- package/dist/verification/checks/phantom-review-check.js.map +1 -0
- package/dist/verification/checks/trivial-output-check.d.ts +47 -0
- package/dist/verification/checks/trivial-output-check.d.ts.map +1 -0
- package/dist/verification/checks/trivial-output-check.js +72 -0
- package/dist/verification/checks/trivial-output-check.js.map +1 -0
- package/dist/verification/index.d.ts +13 -0
- package/dist/verification/index.d.ts.map +1 -0
- package/dist/verification/index.js +13 -0
- package/dist/verification/index.js.map +1 -0
- package/dist/verification/types.d.ts +149 -0
- package/dist/verification/types.d.ts.map +1 -0
- package/dist/verification/types.js +12 -0
- package/dist/verification/types.js.map +1 -0
- package/dist/verification/verification-pipeline.d.ts +65 -0
- package/dist/verification/verification-pipeline.d.ts.map +1 -0
- package/dist/verification/verification-pipeline.js +149 -0
- package/dist/verification/verification-pipeline.js.map +1 -0
- package/graphs/sdlc-pipeline.dot +42 -0
- package/package.json +22 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SdlcDevStoryHandler — wraps the runDevStory compiled workflow
|
|
3
|
+
* as a graph NodeHandler for sdlc.dev-story nodes.
|
|
4
|
+
*
|
|
5
|
+
* Story 43-4.
|
|
6
|
+
*
|
|
7
|
+
* Architecture note (ADR-003): The SDLC package does not compile-time-depend on
|
|
8
|
+
* @substrate-ai/factory to avoid circular references. Compatible types are
|
|
9
|
+
* defined locally using TypeScript structural typing — they are assignable to
|
|
10
|
+
* the factory types when the CLI composition root wires them together at runtime.
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
13
|
+
import { classifyAndPersist } from '../learning/finding-classifier.js';
|
|
14
|
+
import { FindingsInjector, extractTargetFilesFromStoryContent } from '../learning/findings-injector.js';
|
|
15
|
+
import { FindingLifecycleManager } from '../learning/finding-lifecycle.js';
|
|
16
|
+
import { DispatchGate } from '../gating/dispatch-gate.js';
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Factory
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
/**
|
|
21
|
+
* Create an sdlc.dev-story node handler.
|
|
22
|
+
*
|
|
23
|
+
* The returned handler:
|
|
24
|
+
* 1. Validates storyKey and storyFilePath are present in GraphContext (AC6)
|
|
25
|
+
* 2. Reads optional retry remediation context from prior iteration (AC4)
|
|
26
|
+
* 3. Injects relevant prior-run findings into dispatch params (Story 53-8 AC2)
|
|
27
|
+
* 4. Emits orchestrator:story-phase-start telemetry (AC5)
|
|
28
|
+
* 5. Delegates to runDevStory(deps, params) (AC1)
|
|
29
|
+
* 6. Emits orchestrator:story-phase-complete telemetry in finally block (AC5)
|
|
30
|
+
* 7. Maps the DevStoryResult to an Outcome (AC2, AC3)
|
|
31
|
+
* 8. On failure: classifyAndPersist + emit pipeline:finding-captured (Story 53-8 AC1, AC6)
|
|
32
|
+
* 9. On success: retireContradictedFindings (Story 53-8 AC4)
|
|
33
|
+
*
|
|
34
|
+
* @param options - Handler configuration.
|
|
35
|
+
* @returns A NodeHandler function ready for registration under the 'sdlc.dev-story' key.
|
|
36
|
+
*/
|
|
37
|
+
export function createSdlcDevStoryHandler(options) {
|
|
38
|
+
return async (_node, context, _graph) => {
|
|
39
|
+
// AC6: Validate required context keys before calling runDevStory
|
|
40
|
+
const storyKey = context.getString('storyKey', '');
|
|
41
|
+
const storyFilePath = context.getString('storyFilePath', '');
|
|
42
|
+
if (!storyKey || !storyFilePath) {
|
|
43
|
+
const missingFields = [
|
|
44
|
+
!storyKey && 'storyKey',
|
|
45
|
+
!storyFilePath && 'storyFilePath',
|
|
46
|
+
].filter(Boolean);
|
|
47
|
+
return {
|
|
48
|
+
status: 'FAILURE',
|
|
49
|
+
failureReason: `Missing required context: ${missingFields.join(', ')}`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// AC1: Extract optional pipelineRunId — omit key when absent
|
|
53
|
+
// (exactOptionalPropertyTypes requires optional fields to be absent, not undefined)
|
|
54
|
+
const pipelineRunIdRaw = context.getString('pipelineRunId', '');
|
|
55
|
+
// AC4: Read prior iteration remediation context from context
|
|
56
|
+
const priorFiles = context.getList?.('devStoryFilesModified') ?? [];
|
|
57
|
+
const priorAcFailures = context.getList?.('devStoryAcFailures') ?? [];
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Read story content (used for findings injection and dispatch gating)
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
let storyContent = '';
|
|
62
|
+
try {
|
|
63
|
+
storyContent = readFileSync(storyFilePath, 'utf-8');
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Non-fatal: if file can't be read, use empty content
|
|
67
|
+
}
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Story 53-8 AC2: Pre-dispatch findings injection
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
let findingsPrompt = '';
|
|
72
|
+
if (options.db != null) {
|
|
73
|
+
try {
|
|
74
|
+
// Infer packageName from storyKey prefix (e.g., '53-8' → no package, or from path)
|
|
75
|
+
const pkgMatch = /packages\/([^/]+)\//.exec(storyFilePath);
|
|
76
|
+
const packageName = pkgMatch?.[1];
|
|
77
|
+
const injectionCtx = {
|
|
78
|
+
storyKey,
|
|
79
|
+
runId: pipelineRunIdRaw !== '' ? pipelineRunIdRaw : 'unknown',
|
|
80
|
+
targetFiles: extractTargetFilesFromStoryContent(storyContent),
|
|
81
|
+
...(packageName !== undefined ? { packageName } : {}),
|
|
82
|
+
};
|
|
83
|
+
findingsPrompt = await FindingsInjector.inject(options.db, injectionCtx);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// AC5: Non-fatal — DB errors must never block dispatch
|
|
87
|
+
console.warn('[sdlc-dev-story-handler] FindingsInjector.inject failed; proceeding without findings');
|
|
88
|
+
findingsPrompt = '';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Story 53-9: Dispatch pre-condition gating
|
|
93
|
+
// Runs after findings injection, before agent dispatch.
|
|
94
|
+
// Non-fatal: any gate error is caught and dispatch proceeds normally.
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
if (options.db != null) {
|
|
97
|
+
try {
|
|
98
|
+
const pendingFiles = extractTargetFilesFromStoryContent(storyContent);
|
|
99
|
+
const gateOptions = {
|
|
100
|
+
storyKey,
|
|
101
|
+
storyContent,
|
|
102
|
+
pendingFiles,
|
|
103
|
+
// completedStories: populated from run manifest per_story_state.
|
|
104
|
+
// Currently no modifiedFiles tracked in manifest — empty array is
|
|
105
|
+
// correct (gate degrades gracefully per AC7). Learning pre-emption
|
|
106
|
+
// (AC6) still functions via the DB query path.
|
|
107
|
+
completedStories: [],
|
|
108
|
+
db: options.db,
|
|
109
|
+
projectRoot: process.cwd(),
|
|
110
|
+
};
|
|
111
|
+
const gateResult = await DispatchGate.check(gateOptions);
|
|
112
|
+
if (gateResult.decision === 'warn' && gateResult.overlappingFiles !== undefined && gateResult.completedStoryKey !== undefined) {
|
|
113
|
+
// AC2: emit warning event; dispatch proceeds normally
|
|
114
|
+
options.eventBus.emit('pipeline:dispatch-warn', {
|
|
115
|
+
storyKey,
|
|
116
|
+
completedStoryKey: gateResult.completedStoryKey,
|
|
117
|
+
overlappingFiles: gateResult.overlappingFiles,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
else if (gateResult.decision === 'block' && gateResult.modifiedPrompt !== undefined) {
|
|
121
|
+
// AC4: auto-resolved; extract extension note from modifiedPrompt and inject
|
|
122
|
+
// into findingsPrompt so the agent receives the namespace extension guidance.
|
|
123
|
+
// modifiedPrompt = storyContent + '\n\n' + extensionNote
|
|
124
|
+
const extensionNote = gateResult.modifiedPrompt.slice(storyContent.length).trim();
|
|
125
|
+
if (extensionNote.length > 0) {
|
|
126
|
+
findingsPrompt = findingsPrompt !== ''
|
|
127
|
+
? `${findingsPrompt}\n\n${extensionNote}`
|
|
128
|
+
: extensionNote;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else if (gateResult.decision === 'gated') {
|
|
132
|
+
// AC5: non-resolvable conflict; place story in gated phase
|
|
133
|
+
options.eventBus.emit('pipeline:story-gated', {
|
|
134
|
+
storyKey,
|
|
135
|
+
conflictType: gateResult.conflictType ?? 'namespace-collision',
|
|
136
|
+
reason: gateResult.reason ?? 'dispatch gate: non-resolvable conflict',
|
|
137
|
+
...(gateResult.completedStoryKey !== undefined
|
|
138
|
+
? { completedStoryKey: gateResult.completedStoryKey }
|
|
139
|
+
: {}),
|
|
140
|
+
});
|
|
141
|
+
// Return early without dispatching — story stays in gated phase
|
|
142
|
+
return {
|
|
143
|
+
status: 'FAILURE',
|
|
144
|
+
failureReason: gateResult.reason ?? 'dispatch gate: story gated — operator review required',
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// AC7: gate error must never block dispatch
|
|
150
|
+
console.debug('[sdlc-dev-story-handler] DispatchGate.check failed; proceeding with original dispatch');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Build DevStoryParams with required + optional fields
|
|
154
|
+
const devStoryParams = {
|
|
155
|
+
storyKey,
|
|
156
|
+
storyFilePath,
|
|
157
|
+
...(pipelineRunIdRaw !== '' ? { pipelineRunId: pipelineRunIdRaw } : {}),
|
|
158
|
+
// AC4: Pass accumulated modified files from prior iteration as priorFiles
|
|
159
|
+
...(priorFiles.length > 0 ? { priorFiles } : {}),
|
|
160
|
+
// AC4: Construct taskScope note describing prior failures if present
|
|
161
|
+
...(priorAcFailures.length > 0
|
|
162
|
+
? { taskScope: `Prior attempt failed ACs: ${priorAcFailures.join(', ')}` }
|
|
163
|
+
: {}),
|
|
164
|
+
// Story 53-8 AC2 + Story 53-9 AC4: findings + gate extension note (if any)
|
|
165
|
+
...(findingsPrompt !== '' ? { findingsPrompt } : {}),
|
|
166
|
+
};
|
|
167
|
+
// AC5: Emit phase-start telemetry before calling runDevStory
|
|
168
|
+
options.eventBus.emit('orchestrator:story-phase-start', {
|
|
169
|
+
storyKey,
|
|
170
|
+
phase: 'dev-story',
|
|
171
|
+
...(pipelineRunIdRaw !== '' ? { pipelineRunId: pipelineRunIdRaw } : {}),
|
|
172
|
+
});
|
|
173
|
+
// Initialize outcome to a default so finally block always has a valid status
|
|
174
|
+
let outcome = { status: 'FAILURE', failureReason: 'unexpected error in dev-story handler' };
|
|
175
|
+
try {
|
|
176
|
+
// AC1: Delegate to runDevStory
|
|
177
|
+
const workflowResult = await options.runDevStory(options.deps, devStoryParams);
|
|
178
|
+
if (workflowResult.result === 'success') {
|
|
179
|
+
// Build verification gate: catch compile errors before code-review
|
|
180
|
+
if (options.buildVerifier) {
|
|
181
|
+
const projectRoot = context.getString('projectRoot', '');
|
|
182
|
+
if (projectRoot) {
|
|
183
|
+
const buildResult = options.buildVerifier(projectRoot);
|
|
184
|
+
if (buildResult.status === 'failed' || buildResult.status === 'timeout') {
|
|
185
|
+
outcome = {
|
|
186
|
+
status: 'FAILURE',
|
|
187
|
+
failureReason: `build verification failed after dev-story: ${buildResult.output?.slice(0, 500) ?? 'no output'}`,
|
|
188
|
+
contextUpdates: {
|
|
189
|
+
filesModified: workflowResult.files_modified,
|
|
190
|
+
devStoryFilesModified: workflowResult.files_modified,
|
|
191
|
+
devStoryAcFailures: ['build-verification'],
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
return outcome;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// AC2: Map success result to SUCCESS Outcome with implementation artifacts
|
|
199
|
+
outcome = {
|
|
200
|
+
status: 'SUCCESS',
|
|
201
|
+
contextUpdates: {
|
|
202
|
+
filesModified: workflowResult.files_modified,
|
|
203
|
+
acMet: workflowResult.ac_met,
|
|
204
|
+
// Persist for retry pass-through (AC4)
|
|
205
|
+
devStoryFilesModified: workflowResult.files_modified,
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
// Story 53-8 AC4: Retire contradicted findings on success
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
if (options.db != null) {
|
|
212
|
+
const successCtx = {
|
|
213
|
+
modifiedFiles: workflowResult.files_modified,
|
|
214
|
+
runId: pipelineRunIdRaw !== '' ? pipelineRunIdRaw : 'unknown',
|
|
215
|
+
};
|
|
216
|
+
try {
|
|
217
|
+
await FindingLifecycleManager.retireContradictedFindings(successCtx, options.db);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
// AC5: Non-fatal — DB errors must never block success outcome
|
|
221
|
+
console.warn('[sdlc-dev-story-handler] retireContradictedFindings failed; continuing');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
// AC3: Map failure result to FAILURE Outcome with remediation context
|
|
227
|
+
const failureReason = workflowResult.error ??
|
|
228
|
+
(workflowResult.ac_failures.length > 0
|
|
229
|
+
? `dev-story failed ACs: ${workflowResult.ac_failures.join(', ')}`
|
|
230
|
+
: 'dev-story workflow failed');
|
|
231
|
+
outcome = {
|
|
232
|
+
status: 'FAILURE',
|
|
233
|
+
failureReason,
|
|
234
|
+
contextUpdates: {
|
|
235
|
+
acFailures: workflowResult.ac_failures,
|
|
236
|
+
filesModified: workflowResult.files_modified,
|
|
237
|
+
// Persist for retry pass-through (AC4)
|
|
238
|
+
devStoryFilesModified: workflowResult.files_modified,
|
|
239
|
+
devStoryAcFailures: workflowResult.ac_failures,
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
// Story 53-8 AC1: Classify and persist failure finding
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
if (options.db != null) {
|
|
246
|
+
const failureCtx = {
|
|
247
|
+
storyKey,
|
|
248
|
+
runId: pipelineRunIdRaw !== '' ? pipelineRunIdRaw : 'unknown',
|
|
249
|
+
// exactOptionalPropertyTypes: omit 'error' when undefined rather than set to undefined
|
|
250
|
+
...(workflowResult.error !== undefined ? { error: workflowResult.error } : {}),
|
|
251
|
+
affectedFiles: workflowResult.files_modified,
|
|
252
|
+
buildFailed: workflowResult.ac_failures.includes('build-verification'),
|
|
253
|
+
testsFailed: workflowResult.tests === 'fail',
|
|
254
|
+
};
|
|
255
|
+
try {
|
|
256
|
+
const finding = await classifyAndPersist(failureCtx, options.db);
|
|
257
|
+
// AC6: Emit pipeline:finding-captured after successful persist
|
|
258
|
+
options.eventBus.emit('pipeline:finding-captured', {
|
|
259
|
+
storyKey,
|
|
260
|
+
runId: failureCtx.runId,
|
|
261
|
+
rootCause: finding.root_cause,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
// AC5: Non-fatal — DB errors must never block failure outcome
|
|
266
|
+
console.warn('[sdlc-dev-story-handler] classifyAndPersist failed; continuing');
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
// Handle unexpected throws from runDevStory
|
|
273
|
+
const failureReason = err instanceof Error ? err.message : String(err);
|
|
274
|
+
outcome = { status: 'FAILURE', failureReason };
|
|
275
|
+
}
|
|
276
|
+
finally {
|
|
277
|
+
// AC5: Emit phase-complete in finally block — guaranteed even on throw
|
|
278
|
+
options.eventBus.emit('orchestrator:story-phase-complete', {
|
|
279
|
+
storyKey,
|
|
280
|
+
phase: 'dev-story',
|
|
281
|
+
result: { status: outcome.status },
|
|
282
|
+
...(pipelineRunIdRaw !== '' ? { pipelineRunId: pipelineRunIdRaw } : {}),
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
return outcome;
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=sdlc-dev-story-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdlc-dev-story-handler.js","sourceRoot":"","sources":["../../src/handlers/sdlc-dev-story-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SdlcPhaseHandler — wraps SDLC pipeline phase execution as a graph NodeHandler.
|
|
3
|
+
*
|
|
4
|
+
* Story 43-2.
|
|
5
|
+
*
|
|
6
|
+
* Architecture note (ADR-003): This package does not import from @substrate-ai/factory
|
|
7
|
+
* or from the monolith source tree at compile time. All external dependencies
|
|
8
|
+
* (orchestrator, phaseDeps, phase runner functions) are injected via
|
|
9
|
+
* SdlcPhaseHandlerDeps at construction time.
|
|
10
|
+
*
|
|
11
|
+
* TypeScript structural typing ensures the returned SdlcNodeHandler is
|
|
12
|
+
* assignable to NodeHandler from @substrate-ai/factory at the CLI composition
|
|
13
|
+
* root — no sdlc→factory import required.
|
|
14
|
+
*/
|
|
15
|
+
import type { SdlcPhaseHandlerDeps, SdlcNodeHandler } from './types.js';
|
|
16
|
+
/**
|
|
17
|
+
* Create an sdlc.phase node handler.
|
|
18
|
+
*
|
|
19
|
+
* The returned handler:
|
|
20
|
+
* 1. Resolves the phase name from node.id (AC5)
|
|
21
|
+
* 2. Looks up the corresponding runner in the PHASE_RUNNERS map (AC7)
|
|
22
|
+
* 3. Calls the runner with phaseDeps and phase-specific params (AC1, AC2, AC5)
|
|
23
|
+
* 4. On runner error, returns FAILURE without re-throwing (AC3)
|
|
24
|
+
* 5. If advanceAfterRun !== false, calls orchestrator.advancePhase(runId) (AC4)
|
|
25
|
+
* 6. If gate check fails (advanced === false), returns FAILURE with gate messages (AC4)
|
|
26
|
+
* 7. On full success, returns SUCCESS with phase output in contextUpdates (AC1, AC2)
|
|
27
|
+
*
|
|
28
|
+
* @param deps - Injected dependencies: orchestrator, phaseDeps, phase runners.
|
|
29
|
+
* @returns A SdlcNodeHandler ready for registration under the 'sdlc.phase' key.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createSdlcPhaseHandler(deps: SdlcPhaseHandlerDeps): SdlcNodeHandler;
|
|
32
|
+
//# sourceMappingURL=sdlc-phase-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdlc-phase-handler.d.ts","sourceRoot":"","sources":["../../src/handlers/sdlc-phase-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAe,MAAM,YAAY,CAAA;AAMpF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,oBAAoB,GAAG,eAAe,CAiKlF"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SdlcPhaseHandler — wraps SDLC pipeline phase execution as a graph NodeHandler.
|
|
3
|
+
*
|
|
4
|
+
* Story 43-2.
|
|
5
|
+
*
|
|
6
|
+
* Architecture note (ADR-003): This package does not import from @substrate-ai/factory
|
|
7
|
+
* or from the monolith source tree at compile time. All external dependencies
|
|
8
|
+
* (orchestrator, phaseDeps, phase runner functions) are injected via
|
|
9
|
+
* SdlcPhaseHandlerDeps at construction time.
|
|
10
|
+
*
|
|
11
|
+
* TypeScript structural typing ensures the returned SdlcNodeHandler is
|
|
12
|
+
* assignable to NodeHandler from @substrate-ai/factory at the CLI composition
|
|
13
|
+
* root — no sdlc→factory import required.
|
|
14
|
+
*/
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Factory
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
/**
|
|
19
|
+
* Create an sdlc.phase node handler.
|
|
20
|
+
*
|
|
21
|
+
* The returned handler:
|
|
22
|
+
* 1. Resolves the phase name from node.id (AC5)
|
|
23
|
+
* 2. Looks up the corresponding runner in the PHASE_RUNNERS map (AC7)
|
|
24
|
+
* 3. Calls the runner with phaseDeps and phase-specific params (AC1, AC2, AC5)
|
|
25
|
+
* 4. On runner error, returns FAILURE without re-throwing (AC3)
|
|
26
|
+
* 5. If advanceAfterRun !== false, calls orchestrator.advancePhase(runId) (AC4)
|
|
27
|
+
* 6. If gate check fails (advanced === false), returns FAILURE with gate messages (AC4)
|
|
28
|
+
* 7. On full success, returns SUCCESS with phase output in contextUpdates (AC1, AC2)
|
|
29
|
+
*
|
|
30
|
+
* @param deps - Injected dependencies: orchestrator, phaseDeps, phase runners.
|
|
31
|
+
* @returns A SdlcNodeHandler ready for registration under the 'sdlc.phase' key.
|
|
32
|
+
*/
|
|
33
|
+
export function createSdlcPhaseHandler(deps) {
|
|
34
|
+
// Build the PHASE_RUNNERS map from the injected phase runner functions.
|
|
35
|
+
// Using a Map allows O(1) lookup and supports the unknown-phase check (AC7).
|
|
36
|
+
const PHASE_RUNNERS = new Map(Object.entries(deps.phases));
|
|
37
|
+
return async (node, context, _graph) => {
|
|
38
|
+
// AC5: Phase selection is driven by node.id
|
|
39
|
+
const phaseName = node.id;
|
|
40
|
+
// AC7: Unknown phase — no runner registered
|
|
41
|
+
const runner = PHASE_RUNNERS.get(phaseName);
|
|
42
|
+
if (runner === undefined) {
|
|
43
|
+
return {
|
|
44
|
+
status: 'FAILURE',
|
|
45
|
+
failureReason: `No phase runner registered for phase: ${phaseName}`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// Extract context values
|
|
49
|
+
const runId = context.getString('runId');
|
|
50
|
+
// AC1: concept is only needed for the analysis phase
|
|
51
|
+
const concept = phaseName === 'analysis' ? context.getString('concept', '') : '';
|
|
52
|
+
// Story-key skip: when --stories is provided (explicit story dispatch), the linear
|
|
53
|
+
// engine skips analysis/planning/solutioning entirely. Mirror that behavior in the
|
|
54
|
+
// graph engine by checking for storyKey in context — its presence means we're
|
|
55
|
+
// targeting implementation directly and early phases should be skipped.
|
|
56
|
+
const PRE_IMPL_PHASES = ['analysis', 'planning', 'solutioning'];
|
|
57
|
+
const storyKey = context.getString('storyKey', '');
|
|
58
|
+
if (storyKey && PRE_IMPL_PHASES.includes(phaseName)) {
|
|
59
|
+
return {
|
|
60
|
+
status: 'SUCCESS',
|
|
61
|
+
notes: `Phase ${phaseName} skipped — explicit story dispatch (storyKey=${storyKey})`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// Phase-skip check: if the phase's output artifact already exists (from a prior run or
|
|
65
|
+
// from the linear engine having completed it), skip dispatch and return SUCCESS.
|
|
66
|
+
// This gives the graph engine behavioral parity with the linear engine's detectStartPhase().
|
|
67
|
+
//
|
|
68
|
+
// When skipping, we also register the artifact(s) for the CURRENT pipeline run so that
|
|
69
|
+
// downstream entry gates (which filter by pipeline_run_id) can find them. Without this,
|
|
70
|
+
// the implementation phase's entry gate would fail because it only sees artifacts from
|
|
71
|
+
// the current run.
|
|
72
|
+
const PHASE_ARTIFACT_TYPES = {
|
|
73
|
+
analysis: ['product-brief'],
|
|
74
|
+
planning: ['prd'],
|
|
75
|
+
solutioning: ['architecture', 'stories'],
|
|
76
|
+
};
|
|
77
|
+
const artifactTypes = PHASE_ARTIFACT_TYPES[phaseName];
|
|
78
|
+
if (artifactTypes !== undefined) {
|
|
79
|
+
try {
|
|
80
|
+
const db = deps.phaseDeps.db;
|
|
81
|
+
if (db) {
|
|
82
|
+
// Check if ALL required artifacts for this phase exist globally
|
|
83
|
+
let allExist = true;
|
|
84
|
+
for (const at of artifactTypes) {
|
|
85
|
+
const rows = await db.query('SELECT id, path, content_hash, summary FROM artifacts WHERE phase = ? AND type = ? ORDER BY created_at DESC LIMIT 1', [phaseName, at]);
|
|
86
|
+
if (!Array.isArray(rows) || rows.length === 0) {
|
|
87
|
+
allExist = false;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (allExist) {
|
|
92
|
+
// Register each artifact for the current pipeline run so downstream entry gates pass.
|
|
93
|
+
// The pipelineRunId comes from context (set by the graph orchestrator).
|
|
94
|
+
const pipelineRunId = context.getString('pipelineRunId', '');
|
|
95
|
+
if (pipelineRunId) {
|
|
96
|
+
for (const at of artifactTypes) {
|
|
97
|
+
const existing = await db.query('SELECT id, path, content_hash, summary FROM artifacts WHERE phase = ? AND type = ? ORDER BY created_at DESC LIMIT 1', [phaseName, at]);
|
|
98
|
+
const src = existing[0];
|
|
99
|
+
if (src) {
|
|
100
|
+
// Check if we've already registered this for the current run
|
|
101
|
+
const alreadyRegistered = await db.query('SELECT id FROM artifacts WHERE pipeline_run_id = ? AND phase = ? AND type = ? LIMIT 1', [pipelineRunId, phaseName, at]);
|
|
102
|
+
if (!Array.isArray(alreadyRegistered) || alreadyRegistered.length === 0) {
|
|
103
|
+
const newId = crypto.randomUUID();
|
|
104
|
+
await db.query('INSERT INTO artifacts (id, pipeline_run_id, phase, type, path, content_hash, summary) VALUES (?, ?, ?, ?, ?, ?, ?)', [newId, pipelineRunId, phaseName, at, src.path, src.content_hash ?? null, src.summary ?? null]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
status: 'SUCCESS',
|
|
111
|
+
notes: `Phase ${phaseName} already complete — artifact(s) exist, skipping dispatch`,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// DB query failed — proceed with normal dispatch (don't block on skip check failure)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Build phase-specific params (analysis requires concept; others only runId)
|
|
121
|
+
const params = phaseName === 'analysis' ? { runId, concept } : { runId };
|
|
122
|
+
// Outer try/catch wraps the entry gate check, runner call, and advancePhase call.
|
|
123
|
+
// This boundary satisfies AC3 (runner error) and also handles the case
|
|
124
|
+
// where evaluateEntryGates or advancePhase itself throws (network/DB error).
|
|
125
|
+
try {
|
|
126
|
+
// Story 43-13 AC1, AC2, AC4: Evaluate entry gates before dispatching runner.
|
|
127
|
+
// evaluateEntryGates is always called regardless of advanceAfterRun flag.
|
|
128
|
+
const entryGateResult = await deps.orchestrator.evaluateEntryGates(runId);
|
|
129
|
+
if (!entryGateResult.passed) {
|
|
130
|
+
const failures = entryGateResult.failures
|
|
131
|
+
?.map((f) => `${f.gate}: ${f.error}`)
|
|
132
|
+
.join('; ') ?? 'no details';
|
|
133
|
+
return { status: 'FAILURE', failureReason: `entry gate failed: ${failures}` };
|
|
134
|
+
}
|
|
135
|
+
// AC1, AC2, AC5: Dispatch to the registered phase runner
|
|
136
|
+
const phaseOutput = await runner(deps.phaseDeps, params);
|
|
137
|
+
// AC4: Advance phase unless the caller has disabled it
|
|
138
|
+
if (deps.advanceAfterRun !== false) {
|
|
139
|
+
const advanceResult = await deps.orchestrator.advancePhase(runId);
|
|
140
|
+
if (!advanceResult.advanced) {
|
|
141
|
+
// Story 43-13 AC3, AC4: Exit gate failure — prefix with 'exit gate failed: '
|
|
142
|
+
const failures = advanceResult.gateFailures
|
|
143
|
+
?.map((f) => `${f.gate}: ${f.error}`)
|
|
144
|
+
.join('; ') ?? 'no details';
|
|
145
|
+
return { status: 'FAILURE', failureReason: `exit gate failed: ${failures}` };
|
|
146
|
+
}
|
|
147
|
+
// AC1, AC2: Full success — include phase output and advanced phase name
|
|
148
|
+
return {
|
|
149
|
+
status: 'SUCCESS',
|
|
150
|
+
contextUpdates: { ...phaseOutput, advancedPhase: advanceResult.phase },
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// advanceAfterRun === false: return success without advancing
|
|
154
|
+
return {
|
|
155
|
+
status: 'SUCCESS',
|
|
156
|
+
contextUpdates: phaseOutput,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
// AC3: runner threw, or evaluateEntryGates/advancePhase threw unexpectedly
|
|
161
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
162
|
+
return { status: 'FAILURE', failureReason: message };
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=sdlc-phase-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdlc-phase-handler.js","sourceRoot":"","sources":["../../src/handlers/sdlc-phase-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the SDLC handler module.
|
|
3
|
+
* Story 43-2.
|
|
4
|
+
*
|
|
5
|
+
* All types are defined locally — structurally compatible with monolith types
|
|
6
|
+
* but not compile-time coupled to the monolith or @substrate-ai/factory.
|
|
7
|
+
* See ADR-003: sdlc must NOT import from @substrate-ai/factory.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Structured outcome returned by every SDLC node handler.
|
|
11
|
+
*
|
|
12
|
+
* Structurally compatible with Outcome from @substrate-ai/factory via
|
|
13
|
+
* TypeScript duck-typing — the CLI composition root can assign one to
|
|
14
|
+
* the other without any sdlc→factory import.
|
|
15
|
+
*/
|
|
16
|
+
export interface SdlcOutcome {
|
|
17
|
+
status: 'SUCCESS' | 'FAILURE' | 'PARTIAL_SUCCESS' | 'NEEDS_RETRY' | 'ESCALATE';
|
|
18
|
+
failureReason?: string;
|
|
19
|
+
contextUpdates?: Record<string, unknown>;
|
|
20
|
+
notes?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Optional preferred edge label for the graph engine's edge selector.
|
|
23
|
+
* The graph engine uses this to match the DOT edge `label` attribute when
|
|
24
|
+
* choosing the next node after this handler returns.
|
|
25
|
+
* e.g., 'SHIP_IT' routes to exit, 'NEEDS_FIXES' routes back to dev_story.
|
|
26
|
+
*/
|
|
27
|
+
preferredLabel?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* SDLC node handler type alias.
|
|
31
|
+
*
|
|
32
|
+
* Structurally compatible with NodeHandler from @substrate-ai/factory:
|
|
33
|
+
* type NodeHandler = (node: GraphNode, context: IGraphContext, graph: Graph) => Promise<Outcome>
|
|
34
|
+
*
|
|
35
|
+
* TypeScript structural typing ensures the CLI composition root can assign
|
|
36
|
+
* SdlcNodeHandler to NodeHandler without any sdlc→factory import.
|
|
37
|
+
*/
|
|
38
|
+
export type SdlcNodeHandler = (node: {
|
|
39
|
+
id: string;
|
|
40
|
+
label: string;
|
|
41
|
+
prompt: string;
|
|
42
|
+
}, context: {
|
|
43
|
+
getString(key: string, defaultValue?: string): string;
|
|
44
|
+
}, graph: unknown) => Promise<SdlcOutcome>;
|
|
45
|
+
/** Gate failure detail from a phase advance attempt. */
|
|
46
|
+
export interface GateFailure {
|
|
47
|
+
gate: string;
|
|
48
|
+
error: string;
|
|
49
|
+
}
|
|
50
|
+
/** Result of attempting to advance the pipeline phase. */
|
|
51
|
+
interface AdvancePhaseResult {
|
|
52
|
+
advanced: boolean;
|
|
53
|
+
phase: string;
|
|
54
|
+
gateFailures?: GateFailure[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Result of evaluating entry gates before a phase dispatch.
|
|
58
|
+
* Story 43-13.
|
|
59
|
+
*/
|
|
60
|
+
export interface EntryGateResult {
|
|
61
|
+
passed: boolean;
|
|
62
|
+
failures?: GateFailure[];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Local minimal PhaseOrchestrator interface.
|
|
66
|
+
* Structurally compatible with the monolith PhaseOrchestrator.
|
|
67
|
+
* `advancePhase` handles post-runner exit gates; `evaluateEntryGates` handles
|
|
68
|
+
* pre-runner entry gates (added in story 43-13).
|
|
69
|
+
*/
|
|
70
|
+
export interface PhaseOrchestrator {
|
|
71
|
+
advancePhase(runId: string): Promise<AdvancePhaseResult>;
|
|
72
|
+
evaluateEntryGates(runId: string): Promise<EntryGateResult>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Phase runner function type.
|
|
76
|
+
*
|
|
77
|
+
* Structurally compatible with the monolith phase runner signatures
|
|
78
|
+
* (runAnalysisPhase, runPlanningPhase, runSolutioningPhase) when called
|
|
79
|
+
* with the monolith's PhaseDeps and phase-specific params.
|
|
80
|
+
*
|
|
81
|
+
* `deps` is typed as `unknown` because PhaseDeps is defined in the monolith
|
|
82
|
+
* and this package has no need to inspect or modify it — it is passed through
|
|
83
|
+
* as-is from SdlcPhaseHandlerDeps.phaseDeps.
|
|
84
|
+
*/
|
|
85
|
+
export type PhaseRunnerFn = (deps: unknown, params: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
86
|
+
/**
|
|
87
|
+
* Map of phase name → runner function.
|
|
88
|
+
* Index signature allows additional phases to be registered without
|
|
89
|
+
* modifying the type (e.g., ux-design in a follow-on story).
|
|
90
|
+
*/
|
|
91
|
+
export interface PhaseRunners {
|
|
92
|
+
analysis: PhaseRunnerFn;
|
|
93
|
+
planning: PhaseRunnerFn;
|
|
94
|
+
solutioning: PhaseRunnerFn;
|
|
95
|
+
[key: string]: PhaseRunnerFn;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Dependency injection container for createSdlcPhaseHandler.
|
|
99
|
+
*
|
|
100
|
+
* Architecture note (ADR-003): phaseDeps is typed as `unknown` because
|
|
101
|
+
* PhaseDeps is defined in the monolith. It is passed through opaquely to the
|
|
102
|
+
* injected phase runner functions — this package has no need to inspect or
|
|
103
|
+
* modify it. The CLI composition root injects the concrete PhaseDeps instance
|
|
104
|
+
* at runtime.
|
|
105
|
+
*
|
|
106
|
+
* Phase runner functions are provided via `phases` to allow test injection of
|
|
107
|
+
* mocks without vi.mock — preferred per ADR-003 (no monolith compile-time
|
|
108
|
+
* coupling). At runtime the CLI composition root injects the real runners.
|
|
109
|
+
*/
|
|
110
|
+
export interface SdlcPhaseHandlerDeps {
|
|
111
|
+
/** Phase orchestrator for gate evaluation and phase advancement. */
|
|
112
|
+
orchestrator: PhaseOrchestrator;
|
|
113
|
+
/**
|
|
114
|
+
* Phase dependencies — passed through to runner functions.
|
|
115
|
+
* Typed as `unknown` to avoid compile-time coupling to monolith PhaseDeps.
|
|
116
|
+
*/
|
|
117
|
+
phaseDeps: unknown;
|
|
118
|
+
/**
|
|
119
|
+
* Whether to call orchestrator.advancePhase() after each runner completes.
|
|
120
|
+
* Defaults to `true`. Pass `false` when managing phase advancement
|
|
121
|
+
* externally or in integration scenarios where only the runner is tested.
|
|
122
|
+
*/
|
|
123
|
+
advanceAfterRun?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Injectable phase runner functions — keyed by phase name (node.id).
|
|
126
|
+
* At runtime, the CLI composition root provides the real monolith runners.
|
|
127
|
+
* In tests, pass vi.fn() stubs directly (no vi.mock needed).
|
|
128
|
+
*/
|
|
129
|
+
phases: PhaseRunners;
|
|
130
|
+
}
|
|
131
|
+
export {};
|
|
132
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/handlers/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,iBAAiB,GAAG,aAAa,GAAG,UAAU,CAAA;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG,CAC5B,IAAI,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACnD,OAAO,EAAE;IAAE,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EAClE,KAAK,EAAE,OAAO,KACX,OAAO,CAAC,WAAW,CAAC,CAAA;AAMzB,wDAAwD;AACxD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd;AAED,0DAA0D;AAC1D,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,WAAW,EAAE,CAAA;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAA;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IACxD,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;CAC5D;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAErC;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,aAAa,CAAA;IACvB,QAAQ,EAAE,aAAa,CAAA;IACvB,WAAW,EAAE,aAAa,CAAA;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAA;CAC7B;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,oBAAoB;IACnC,oEAAoE;IACpE,YAAY,EAAE,iBAAiB,CAAA;IAC/B;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB;;;;OAIG;IACH,MAAM,EAAE,YAAY,CAAA;CACrB"}
|