@kbediako/codex-orchestrator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +238 -0
  3. package/dist/bin/codex-orchestrator.js +507 -0
  4. package/dist/orchestrator/src/agents/builder.js +16 -0
  5. package/dist/orchestrator/src/agents/index.js +4 -0
  6. package/dist/orchestrator/src/agents/planner.js +17 -0
  7. package/dist/orchestrator/src/agents/reviewer.js +13 -0
  8. package/dist/orchestrator/src/agents/tester.js +13 -0
  9. package/dist/orchestrator/src/cli/adapters/CommandBuilder.js +20 -0
  10. package/dist/orchestrator/src/cli/adapters/CommandPlanner.js +164 -0
  11. package/dist/orchestrator/src/cli/adapters/CommandReviewer.js +32 -0
  12. package/dist/orchestrator/src/cli/adapters/CommandTester.js +33 -0
  13. package/dist/orchestrator/src/cli/adapters/index.js +4 -0
  14. package/dist/orchestrator/src/cli/config/userConfig.js +28 -0
  15. package/dist/orchestrator/src/cli/doctor.js +48 -0
  16. package/dist/orchestrator/src/cli/events/runEvents.js +84 -0
  17. package/dist/orchestrator/src/cli/exec/command.js +56 -0
  18. package/dist/orchestrator/src/cli/exec/context.js +108 -0
  19. package/dist/orchestrator/src/cli/exec/experience.js +77 -0
  20. package/dist/orchestrator/src/cli/exec/finalization.js +140 -0
  21. package/dist/orchestrator/src/cli/exec/learning.js +62 -0
  22. package/dist/orchestrator/src/cli/exec/stageRunner.js +71 -0
  23. package/dist/orchestrator/src/cli/exec/summary.js +109 -0
  24. package/dist/orchestrator/src/cli/exec/telemetry.js +18 -0
  25. package/dist/orchestrator/src/cli/exec/tfgrpo.js +200 -0
  26. package/dist/orchestrator/src/cli/exec/tfgrpoArtifacts.js +19 -0
  27. package/dist/orchestrator/src/cli/exec/types.js +1 -0
  28. package/dist/orchestrator/src/cli/init.js +64 -0
  29. package/dist/orchestrator/src/cli/mcp.js +124 -0
  30. package/dist/orchestrator/src/cli/metrics/metricsAggregator.js +404 -0
  31. package/dist/orchestrator/src/cli/metrics/metricsRecorder.js +138 -0
  32. package/dist/orchestrator/src/cli/orchestrator.js +554 -0
  33. package/dist/orchestrator/src/cli/pipelines/defaultDiagnostics.js +32 -0
  34. package/dist/orchestrator/src/cli/pipelines/designReference.js +72 -0
  35. package/dist/orchestrator/src/cli/pipelines/hiFiDesignToolkit.js +71 -0
  36. package/dist/orchestrator/src/cli/pipelines/index.js +34 -0
  37. package/dist/orchestrator/src/cli/run/environment.js +24 -0
  38. package/dist/orchestrator/src/cli/run/manifest.js +367 -0
  39. package/dist/orchestrator/src/cli/run/manifestPersister.js +88 -0
  40. package/dist/orchestrator/src/cli/run/runPaths.js +30 -0
  41. package/dist/orchestrator/src/cli/selfCheck.js +12 -0
  42. package/dist/orchestrator/src/cli/services/commandRunner.js +420 -0
  43. package/dist/orchestrator/src/cli/services/controlPlaneService.js +107 -0
  44. package/dist/orchestrator/src/cli/services/execRuntime.js +69 -0
  45. package/dist/orchestrator/src/cli/services/pipelineResolver.js +47 -0
  46. package/dist/orchestrator/src/cli/services/runPreparation.js +82 -0
  47. package/dist/orchestrator/src/cli/services/runSummaryWriter.js +35 -0
  48. package/dist/orchestrator/src/cli/services/schedulerService.js +42 -0
  49. package/dist/orchestrator/src/cli/tasks/taskMetadata.js +19 -0
  50. package/dist/orchestrator/src/cli/telemetry/schema.js +8 -0
  51. package/dist/orchestrator/src/cli/types.js +1 -0
  52. package/dist/orchestrator/src/cli/ui/HudApp.js +112 -0
  53. package/dist/orchestrator/src/cli/ui/controller.js +26 -0
  54. package/dist/orchestrator/src/cli/ui/store.js +240 -0
  55. package/dist/orchestrator/src/cli/utils/enforcementMode.js +12 -0
  56. package/dist/orchestrator/src/cli/utils/fs.js +8 -0
  57. package/dist/orchestrator/src/cli/utils/interactive.js +25 -0
  58. package/dist/orchestrator/src/cli/utils/jsonlWriter.js +10 -0
  59. package/dist/orchestrator/src/cli/utils/optionalDeps.js +30 -0
  60. package/dist/orchestrator/src/cli/utils/packageInfo.js +25 -0
  61. package/dist/orchestrator/src/cli/utils/planFormatter.js +49 -0
  62. package/dist/orchestrator/src/cli/utils/runId.js +7 -0
  63. package/dist/orchestrator/src/cli/utils/specGuardRunner.js +26 -0
  64. package/dist/orchestrator/src/cli/utils/strings.js +8 -0
  65. package/dist/orchestrator/src/cli/utils/time.js +6 -0
  66. package/dist/orchestrator/src/control-plane/drift-reporter.js +109 -0
  67. package/dist/orchestrator/src/control-plane/index.js +3 -0
  68. package/dist/orchestrator/src/control-plane/request-builder.js +217 -0
  69. package/dist/orchestrator/src/control-plane/types.js +1 -0
  70. package/dist/orchestrator/src/control-plane/validator.js +50 -0
  71. package/dist/orchestrator/src/credentials/CredentialBroker.js +1 -0
  72. package/dist/orchestrator/src/events/EventBus.js +25 -0
  73. package/dist/orchestrator/src/learning/crystalizer.js +108 -0
  74. package/dist/orchestrator/src/learning/harvester.js +146 -0
  75. package/dist/orchestrator/src/learning/manifest.js +56 -0
  76. package/dist/orchestrator/src/learning/runner.js +177 -0
  77. package/dist/orchestrator/src/learning/validator.js +164 -0
  78. package/dist/orchestrator/src/logger.js +20 -0
  79. package/dist/orchestrator/src/manager.js +388 -0
  80. package/dist/orchestrator/src/persistence/ArtifactStager.js +95 -0
  81. package/dist/orchestrator/src/persistence/ExperienceStore.js +210 -0
  82. package/dist/orchestrator/src/persistence/PersistenceCoordinator.js +65 -0
  83. package/dist/orchestrator/src/persistence/RunManifestWriter.js +23 -0
  84. package/dist/orchestrator/src/persistence/TaskStateStore.js +172 -0
  85. package/dist/orchestrator/src/persistence/identifierGuards.js +1 -0
  86. package/dist/orchestrator/src/persistence/lockFile.js +26 -0
  87. package/dist/orchestrator/src/persistence/sanitizeIdentifier.js +26 -0
  88. package/dist/orchestrator/src/persistence/sanitizeRunId.js +8 -0
  89. package/dist/orchestrator/src/persistence/sanitizeTaskId.js +8 -0
  90. package/dist/orchestrator/src/persistence/writeAtomicFile.js +4 -0
  91. package/dist/orchestrator/src/privacy/guard.js +111 -0
  92. package/dist/orchestrator/src/scheduler/index.js +1 -0
  93. package/dist/orchestrator/src/scheduler/plan.js +171 -0
  94. package/dist/orchestrator/src/scheduler/types.js +1 -0
  95. package/dist/orchestrator/src/sync/CloudRunsClient.js +1 -0
  96. package/dist/orchestrator/src/sync/CloudRunsHttpClient.js +82 -0
  97. package/dist/orchestrator/src/sync/CloudSyncWorker.js +206 -0
  98. package/dist/orchestrator/src/sync/createCloudSyncWorker.js +15 -0
  99. package/dist/orchestrator/src/types.js +1 -0
  100. package/dist/orchestrator/src/utils/atomicWrite.js +15 -0
  101. package/dist/orchestrator/src/utils/errorMessage.js +14 -0
  102. package/dist/orchestrator/src/utils/executionMode.js +69 -0
  103. package/dist/packages/control-plane-schemas/src/index.js +1 -0
  104. package/dist/packages/control-plane-schemas/src/run-request.js +548 -0
  105. package/dist/packages/orchestrator/src/exec/handle-service.js +203 -0
  106. package/dist/packages/orchestrator/src/exec/session-manager.js +147 -0
  107. package/dist/packages/orchestrator/src/exec/unified-exec.js +432 -0
  108. package/dist/packages/orchestrator/src/index.js +3 -0
  109. package/dist/packages/orchestrator/src/instructions/loader.js +101 -0
  110. package/dist/packages/orchestrator/src/instructions/promptPacks.js +151 -0
  111. package/dist/packages/orchestrator/src/notifications/index.js +74 -0
  112. package/dist/packages/orchestrator/src/telemetry/otel-exporter.js +142 -0
  113. package/dist/packages/orchestrator/src/tool-orchestrator.js +161 -0
  114. package/dist/packages/sdk-node/src/orchestrator.js +195 -0
  115. package/dist/packages/shared/config/designConfig.js +495 -0
  116. package/dist/packages/shared/config/env.js +37 -0
  117. package/dist/packages/shared/config/index.js +2 -0
  118. package/dist/packages/shared/design-artifacts/writer.js +221 -0
  119. package/dist/packages/shared/events/serializer.js +84 -0
  120. package/dist/packages/shared/events/types.js +1 -0
  121. package/dist/packages/shared/manifest/artifactUtils.js +36 -0
  122. package/dist/packages/shared/manifest/designArtifacts.js +665 -0
  123. package/dist/packages/shared/manifest/fileIO.js +29 -0
  124. package/dist/packages/shared/manifest/toolRuns.js +78 -0
  125. package/dist/packages/shared/manifest/toolkitArtifacts.js +223 -0
  126. package/dist/packages/shared/manifest/types.js +5 -0
  127. package/dist/packages/shared/manifest/validator.js +73 -0
  128. package/dist/packages/shared/manifest/writer.js +2 -0
  129. package/dist/packages/shared/streams/stdio.js +112 -0
  130. package/dist/scripts/design/pipeline/advanced-assets.js +466 -0
  131. package/dist/scripts/design/pipeline/componentize.js +74 -0
  132. package/dist/scripts/design/pipeline/context.js +34 -0
  133. package/dist/scripts/design/pipeline/extract.js +249 -0
  134. package/dist/scripts/design/pipeline/optionalDeps.js +107 -0
  135. package/dist/scripts/design/pipeline/prepare.js +46 -0
  136. package/dist/scripts/design/pipeline/reference.js +94 -0
  137. package/dist/scripts/design/pipeline/state.js +206 -0
  138. package/dist/scripts/design/pipeline/toolkit/common.js +94 -0
  139. package/dist/scripts/design/pipeline/toolkit/extract.js +258 -0
  140. package/dist/scripts/design/pipeline/toolkit/publish.js +202 -0
  141. package/dist/scripts/design/pipeline/toolkit/publishActions.js +12 -0
  142. package/dist/scripts/design/pipeline/toolkit/reference.js +846 -0
  143. package/dist/scripts/design/pipeline/toolkit/snapshot.js +882 -0
  144. package/dist/scripts/design/pipeline/toolkit/tokens.js +456 -0
  145. package/dist/scripts/design/pipeline/visual-regression.js +137 -0
  146. package/dist/scripts/design/pipeline/write-artifacts.js +61 -0
  147. package/package.json +97 -0
  148. package/schemas/manifest.json +1064 -0
  149. package/templates/README.md +12 -0
  150. package/templates/codex/mcp-client.json +8 -0
@@ -0,0 +1,554 @@
1
+ import process from 'node:process';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { TaskManager } from '../manager.js';
5
+ import { CommandPlanner, CommandBuilder, CommandTester, CommandReviewer } from './adapters/index.js';
6
+ import { resolveEnvironment } from './run/environment.js';
7
+ import { bootstrapManifest, loadManifest, updateHeartbeat, finalizeStatus, appendSummary, ensureGuardrailStatus, resetForResume, recordResumeEvent } from './run/manifest.js';
8
+ import { ManifestPersister, persistManifest } from './run/manifestPersister.js';
9
+ import { generateRunId } from './utils/runId.js';
10
+ import { runCommandStage } from './services/commandRunner.js';
11
+ import { appendMetricsEntry } from './metrics/metricsRecorder.js';
12
+ import { isoTimestamp } from './utils/time.js';
13
+ import { resolveRunPaths, relativeToRepo } from './run/runPaths.js';
14
+ import { logger } from '../logger.js';
15
+ import { getPrivacyGuard } from './services/execRuntime.js';
16
+ import { PipelineResolver } from './services/pipelineResolver.js';
17
+ import { ControlPlaneService } from './services/controlPlaneService.js';
18
+ import { SchedulerService } from './services/schedulerService.js';
19
+ import { applyHandlesToRunSummary, applyPrivacyToRunSummary, persistRunSummary } from './services/runSummaryWriter.js';
20
+ import { prepareRun, resolvePipelineForResume, overrideTaskEnvironment } from './services/runPreparation.js';
21
+ import { loadUserConfig } from './config/userConfig.js';
22
+ import { RunEventPublisher, snapshotStages } from './events/runEvents.js';
23
+ import { CLI_EXECUTION_MODE_PARSER, resolveRequiresCloudPolicy } from '../utils/executionMode.js';
24
+ export class CodexOrchestrator {
25
+ baseEnv;
26
+ controlPlane = new ControlPlaneService();
27
+ scheduler = new SchedulerService();
28
+ constructor(baseEnv = resolveEnvironment()) {
29
+ this.baseEnv = baseEnv;
30
+ }
31
+ async start(options = {}) {
32
+ const preparation = await prepareRun({
33
+ baseEnv: this.baseEnv,
34
+ taskIdOverride: options.taskId,
35
+ pipelineId: options.pipelineId,
36
+ targetStageId: options.targetStageId ?? null,
37
+ planTargetFallback: null
38
+ });
39
+ const runId = generateRunId();
40
+ const { manifest, paths } = await bootstrapManifest(runId, {
41
+ env: preparation.env,
42
+ pipeline: preparation.pipeline,
43
+ parentRunId: options.parentRunId ?? null,
44
+ taskSlug: preparation.metadata.slug,
45
+ approvalPolicy: options.approvalPolicy ?? null,
46
+ planTargetId: preparation.planPreview?.targetId ?? preparation.plannerTargetId ?? null
47
+ });
48
+ const persister = new ManifestPersister({
49
+ manifest,
50
+ paths,
51
+ persistIntervalMs: Math.max(1000, manifest.heartbeat_interval_seconds * 1000)
52
+ });
53
+ const runEvents = this.createRunEventPublisher({
54
+ runId,
55
+ pipeline: preparation.pipeline,
56
+ manifest,
57
+ paths,
58
+ emitter: options.runEvents
59
+ });
60
+ return await this.performRunLifecycle({
61
+ env: preparation.env,
62
+ pipeline: preparation.pipeline,
63
+ manifest,
64
+ paths,
65
+ planner: preparation.planner,
66
+ taskContext: preparation.taskContext,
67
+ runId,
68
+ runEvents,
69
+ persister,
70
+ envOverrides: preparation.envOverrides
71
+ });
72
+ }
73
+ async resume(options) {
74
+ const env = this.baseEnv;
75
+ const { manifest, paths } = await loadManifest(env, options.runId);
76
+ const actualEnv = overrideTaskEnvironment(env, manifest.task_id);
77
+ const resolver = new PipelineResolver();
78
+ const designConfig = await resolver.loadDesignConfig(actualEnv.repoRoot);
79
+ const userConfig = await loadUserConfig(actualEnv);
80
+ const pipeline = resolvePipelineForResume(actualEnv, manifest, userConfig);
81
+ const envOverrides = resolver.resolveDesignEnvOverrides(designConfig, pipeline.id);
82
+ await this.validateResumeToken(paths, manifest, options.resumeToken ?? null);
83
+ recordResumeEvent(manifest, {
84
+ actor: options.actor ?? 'cli',
85
+ reason: options.reason ?? 'manual-resume',
86
+ outcome: 'accepted'
87
+ });
88
+ resetForResume(manifest);
89
+ updateHeartbeat(manifest);
90
+ const preparation = await prepareRun({
91
+ baseEnv: actualEnv,
92
+ pipeline,
93
+ resolver,
94
+ taskIdOverride: manifest.task_id,
95
+ targetStageId: options.targetStageId,
96
+ planTargetFallback: manifest.plan_target_id ?? null,
97
+ envOverrides
98
+ });
99
+ manifest.plan_target_id = preparation.planPreview?.targetId ?? preparation.plannerTargetId ?? null;
100
+ const persister = new ManifestPersister({
101
+ manifest,
102
+ paths,
103
+ persistIntervalMs: Math.max(1000, manifest.heartbeat_interval_seconds * 1000)
104
+ });
105
+ await persister.schedule({ manifest: true, heartbeat: true, force: true });
106
+ const runEvents = this.createRunEventPublisher({
107
+ runId: manifest.run_id,
108
+ pipeline,
109
+ manifest,
110
+ paths,
111
+ emitter: options.runEvents
112
+ });
113
+ return await this.performRunLifecycle({
114
+ env: preparation.env,
115
+ pipeline,
116
+ manifest,
117
+ paths,
118
+ planner: preparation.planner,
119
+ taskContext: preparation.taskContext,
120
+ runId: manifest.run_id,
121
+ runEvents,
122
+ persister,
123
+ envOverrides: preparation.envOverrides
124
+ });
125
+ }
126
+ async status(options) {
127
+ const env = this.baseEnv;
128
+ const { manifest, paths } = await loadManifest(env, options.runId);
129
+ if (options.format === 'json') {
130
+ const payload = this.buildStatusPayload(env, manifest, paths);
131
+ process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
132
+ return manifest;
133
+ }
134
+ this.renderStatus(manifest);
135
+ return manifest;
136
+ }
137
+ async plan(options = {}) {
138
+ const preparation = await prepareRun({
139
+ baseEnv: this.baseEnv,
140
+ taskIdOverride: options.taskId,
141
+ pipelineId: options.pipelineId,
142
+ targetStageId: options.targetStageId,
143
+ planTargetFallback: null
144
+ });
145
+ const plan = preparation.planPreview ?? (await preparation.planner.plan(preparation.taskContext));
146
+ const stages = preparation.pipeline.stages.map((stage, index) => {
147
+ if (stage.kind === 'command') {
148
+ return {
149
+ index: index + 1,
150
+ id: stage.id,
151
+ title: stage.title,
152
+ kind: stage.kind,
153
+ command: stage.command,
154
+ cwd: stage.cwd ?? null,
155
+ env: stage.env ?? null,
156
+ allowFailure: Boolean(stage.allowFailure),
157
+ summaryHint: stage.summaryHint ?? null
158
+ };
159
+ }
160
+ return {
161
+ index: index + 1,
162
+ id: stage.id,
163
+ title: stage.title,
164
+ kind: stage.kind,
165
+ pipeline: stage.pipeline,
166
+ optional: Boolean(stage.optional)
167
+ };
168
+ });
169
+ const pipelineSource = preparation.pipelineSource === 'user'
170
+ ? 'user'
171
+ : preparation.pipelineSource === 'default'
172
+ ? 'default'
173
+ : null;
174
+ return {
175
+ pipeline: {
176
+ id: preparation.pipeline.id,
177
+ title: preparation.pipeline.title,
178
+ description: preparation.pipeline.description ?? null,
179
+ source: pipelineSource
180
+ },
181
+ stages,
182
+ plan,
183
+ targetId: plan.targetId ?? null
184
+ };
185
+ }
186
+ createRunEventPublisher(params) {
187
+ if (!params.emitter) {
188
+ return undefined;
189
+ }
190
+ return new RunEventPublisher(params.emitter, {
191
+ taskId: params.manifest.task_id,
192
+ runId: params.runId,
193
+ pipelineId: params.pipeline.id,
194
+ pipelineTitle: params.pipeline.title,
195
+ manifestPath: params.paths.manifestPath,
196
+ logPath: params.paths.logPath
197
+ });
198
+ }
199
+ createTaskManager(runId, pipeline, executePipeline, getResult, plannerInstance) {
200
+ const planner = plannerInstance ?? new CommandPlanner(pipeline);
201
+ const builder = new CommandBuilder(executePipeline);
202
+ const tester = new CommandTester(getResult);
203
+ const reviewer = new CommandReviewer(getResult);
204
+ const options = {
205
+ planner,
206
+ builder,
207
+ tester,
208
+ reviewer,
209
+ runIdFactory: () => runId,
210
+ modePolicy: (task, subtask) => this.determineMode(task, subtask),
211
+ persistence: { autoStart: true }
212
+ };
213
+ return new TaskManager(options);
214
+ }
215
+ determineMode(task, subtask) {
216
+ if (this.requiresCloudExecution(task, subtask)) {
217
+ return 'cloud';
218
+ }
219
+ return 'mcp';
220
+ }
221
+ requiresCloudExecution(task, subtask) {
222
+ const requiresCloudFlag = resolveRequiresCloudPolicy({
223
+ boolFlags: [subtask.requires_cloud, subtask.requiresCloud],
224
+ metadata: {
225
+ executionMode: typeof subtask.metadata?.executionMode === 'string'
226
+ ? subtask.metadata.executionMode
227
+ : null,
228
+ mode: typeof subtask.metadata?.mode === 'string' ? subtask.metadata.mode : null
229
+ },
230
+ metadataOrder: ['executionMode', 'mode'],
231
+ parseMode: CLI_EXECUTION_MODE_PARSER
232
+ });
233
+ if (requiresCloudFlag !== null) {
234
+ return requiresCloudFlag;
235
+ }
236
+ return Boolean(task.metadata?.execution?.parallel);
237
+ }
238
+ async executePipeline(options) {
239
+ const { env, pipeline, manifest, paths, runEvents, envOverrides } = options;
240
+ const notes = [];
241
+ let success = true;
242
+ manifest.guardrail_status = undefined;
243
+ const persister = options.persister ??
244
+ new ManifestPersister({
245
+ manifest,
246
+ paths,
247
+ persistIntervalMs: Math.max(1000, manifest.heartbeat_interval_seconds * 1000)
248
+ });
249
+ const schedulePersist = (options = {}) => persister.schedule(options);
250
+ const pushHeartbeat = (forceManifest = false) => {
251
+ updateHeartbeat(manifest);
252
+ return schedulePersist({ manifest: forceManifest, heartbeat: true, force: forceManifest });
253
+ };
254
+ manifest.status = 'in_progress';
255
+ updateHeartbeat(manifest);
256
+ await schedulePersist({ manifest: true, heartbeat: true, force: true });
257
+ runEvents?.runStarted(snapshotStages(manifest, pipeline), manifest.status);
258
+ const heartbeatInterval = setInterval(() => {
259
+ void pushHeartbeat(false).catch((error) => {
260
+ logger.warn(`Heartbeat update failed for run ${manifest.run_id}: ${error?.message ?? String(error)}`);
261
+ });
262
+ }, manifest.heartbeat_interval_seconds * 1000);
263
+ try {
264
+ for (let i = 0; i < pipeline.stages.length; i += 1) {
265
+ const stage = pipeline.stages[i];
266
+ const entry = manifest.commands[i];
267
+ if (!entry) {
268
+ continue;
269
+ }
270
+ if (entry.status === 'succeeded' || entry.status === 'skipped') {
271
+ notes.push(`${stage.title}: ${entry.status}`);
272
+ continue;
273
+ }
274
+ entry.status = 'pending';
275
+ entry.started_at = isoTimestamp();
276
+ void schedulePersist({ manifest: true });
277
+ if (stage.kind === 'command') {
278
+ try {
279
+ const result = await runCommandStage({
280
+ env,
281
+ paths,
282
+ manifest,
283
+ stage,
284
+ index: entry.index,
285
+ events: runEvents,
286
+ persister,
287
+ envOverrides
288
+ });
289
+ notes.push(`${stage.title}: ${result.summary}`);
290
+ const updatedEntry = manifest.commands[i];
291
+ if (updatedEntry?.status === 'failed') {
292
+ manifest.status_detail = `stage:${stage.id}:failed`;
293
+ appendSummary(manifest, `Stage '${stage.title}' failed with exit code ${result.exitCode}.`);
294
+ success = false;
295
+ await schedulePersist({ manifest: true, force: true });
296
+ break;
297
+ }
298
+ }
299
+ catch (error) {
300
+ entry.status = 'failed';
301
+ entry.completed_at = isoTimestamp();
302
+ entry.summary = `Execution error: ${error?.message ?? String(error)}`;
303
+ manifest.status_detail = `stage:${stage.id}:error`;
304
+ appendSummary(manifest, entry.summary);
305
+ await schedulePersist({ manifest: true, force: true });
306
+ runEvents?.stageCompleted({
307
+ stageId: stage.id,
308
+ stageIndex: entry.index,
309
+ title: stage.title,
310
+ kind: 'command',
311
+ status: entry.status,
312
+ exitCode: entry.exit_code,
313
+ summary: entry.summary,
314
+ logPath: entry.log_path
315
+ });
316
+ success = false;
317
+ break;
318
+ }
319
+ }
320
+ else {
321
+ entry.status = 'running';
322
+ await schedulePersist({ manifest: true, force: true });
323
+ runEvents?.stageStarted({
324
+ stageId: stage.id,
325
+ stageIndex: entry.index,
326
+ title: stage.title,
327
+ kind: 'subpipeline',
328
+ logPath: entry.log_path,
329
+ status: entry.status
330
+ });
331
+ try {
332
+ const child = await this.start({
333
+ taskId: env.taskId,
334
+ pipelineId: stage.pipeline,
335
+ parentRunId: manifest.run_id,
336
+ format: 'json'
337
+ });
338
+ entry.completed_at = isoTimestamp();
339
+ entry.sub_run_id = child.manifest.run_id;
340
+ entry.summary = child.runSummary.review.summary ?? null;
341
+ entry.status = child.manifest.status === 'succeeded' ? 'succeeded' : stage.optional ? 'skipped' : 'failed';
342
+ entry.command = null;
343
+ manifest.child_runs.push({
344
+ run_id: child.manifest.run_id,
345
+ pipeline_id: stage.pipeline,
346
+ status: child.manifest.status,
347
+ manifest: relativeToRepo(env, resolveRunPaths(env, child.manifest.run_id).manifestPath)
348
+ });
349
+ notes.push(`${stage.title}: ${entry.status}`);
350
+ await schedulePersist({ manifest: true, force: true });
351
+ runEvents?.stageCompleted({
352
+ stageId: stage.id,
353
+ stageIndex: entry.index,
354
+ title: stage.title,
355
+ kind: 'subpipeline',
356
+ status: entry.status,
357
+ exitCode: entry.exit_code,
358
+ summary: entry.summary,
359
+ logPath: entry.log_path,
360
+ subRunId: entry.sub_run_id
361
+ });
362
+ if (!stage.optional && entry.status === 'failed') {
363
+ manifest.status_detail = `subpipeline:${stage.pipeline}:failed`;
364
+ appendSummary(manifest, `Sub-pipeline '${stage.pipeline}' failed.`);
365
+ await schedulePersist({ manifest: true, force: true });
366
+ success = false;
367
+ break;
368
+ }
369
+ }
370
+ catch (error) {
371
+ entry.completed_at = isoTimestamp();
372
+ entry.summary = `Sub-pipeline error: ${error?.message ?? String(error)}`;
373
+ entry.status = stage.optional ? 'skipped' : 'failed';
374
+ entry.command = null;
375
+ manifest.status_detail = `subpipeline:${stage.pipeline}:error`;
376
+ appendSummary(manifest, entry.summary);
377
+ notes.push(`${stage.title}: ${entry.status}`);
378
+ await schedulePersist({ manifest: true, force: true });
379
+ runEvents?.stageCompleted({
380
+ stageId: stage.id,
381
+ stageIndex: entry.index,
382
+ title: stage.title,
383
+ kind: 'subpipeline',
384
+ status: entry.status,
385
+ exitCode: entry.exit_code,
386
+ summary: entry.summary,
387
+ logPath: entry.log_path,
388
+ subRunId: entry.sub_run_id
389
+ });
390
+ if (!stage.optional) {
391
+ success = false;
392
+ break;
393
+ }
394
+ }
395
+ }
396
+ }
397
+ }
398
+ finally {
399
+ clearInterval(heartbeatInterval);
400
+ await schedulePersist({ force: true });
401
+ }
402
+ if (success) {
403
+ finalizeStatus(manifest, 'succeeded');
404
+ }
405
+ else {
406
+ finalizeStatus(manifest, 'failed', manifest.status_detail ?? 'pipeline-failed');
407
+ }
408
+ const guardrailStatus = ensureGuardrailStatus(manifest);
409
+ if (guardrailStatus.recommendation) {
410
+ appendSummary(manifest, guardrailStatus.recommendation);
411
+ }
412
+ updateHeartbeat(manifest);
413
+ await schedulePersist({ manifest: true, heartbeat: true, force: true }).catch((error) => {
414
+ logger.warn(`Heartbeat update failed for run ${manifest.run_id}: ${error?.message ?? String(error)}`);
415
+ });
416
+ await schedulePersist({ force: true });
417
+ await appendMetricsEntry(env, paths, manifest, persister);
418
+ return {
419
+ success,
420
+ notes,
421
+ manifest,
422
+ manifestPath: relativeToRepo(env, paths.manifestPath),
423
+ logPath: relativeToRepo(env, paths.logPath)
424
+ };
425
+ }
426
+ async performRunLifecycle(context) {
427
+ const { env, pipeline, manifest, paths, planner, taskContext, runId, persister, envOverrides } = context;
428
+ let pipelineResult = null;
429
+ let executing = null;
430
+ const executePipeline = async () => {
431
+ if (!executing) {
432
+ executing = this.executePipeline({
433
+ env,
434
+ pipeline,
435
+ manifest,
436
+ paths,
437
+ runEvents: context.runEvents,
438
+ persister,
439
+ envOverrides
440
+ }).then((result) => {
441
+ pipelineResult = result;
442
+ return result;
443
+ });
444
+ }
445
+ return executing;
446
+ };
447
+ const getResult = () => pipelineResult;
448
+ const manager = this.createTaskManager(runId, pipeline, executePipeline, getResult, planner);
449
+ this.attachPlanTargetTracker(manager, manifest, paths, persister);
450
+ getPrivacyGuard().reset();
451
+ const controlPlaneResult = await this.controlPlane.guard({
452
+ env,
453
+ manifest,
454
+ paths,
455
+ pipeline,
456
+ task: taskContext,
457
+ runId,
458
+ requestedBy: { actorId: 'codex-cli', channel: 'cli', name: 'Codex CLI' },
459
+ persister
460
+ });
461
+ const schedulerPlan = await this.scheduler.createPlanForRun({
462
+ env,
463
+ manifest,
464
+ paths,
465
+ controlPlaneResult,
466
+ persister
467
+ });
468
+ let runSummary;
469
+ try {
470
+ runSummary = await manager.execute(taskContext);
471
+ }
472
+ catch (error) {
473
+ context.runEvents?.runError({
474
+ pipelineId: pipeline.id,
475
+ message: error?.message ?? String(error),
476
+ stageId: null
477
+ });
478
+ throw error;
479
+ }
480
+ await this.scheduler.finalizePlan({
481
+ manifest,
482
+ paths,
483
+ plan: schedulerPlan,
484
+ persister
485
+ });
486
+ this.scheduler.applySchedulerToRunSummary(runSummary, schedulerPlan);
487
+ applyHandlesToRunSummary(runSummary, manifest);
488
+ applyPrivacyToRunSummary(runSummary, manifest);
489
+ this.controlPlane.applyControlPlaneToRunSummary(runSummary, controlPlaneResult);
490
+ await persistRunSummary(env, paths, manifest, runSummary, persister);
491
+ context.runEvents?.runCompleted({
492
+ pipelineId: pipeline.id,
493
+ status: manifest.status,
494
+ manifestPath: paths.manifestPath,
495
+ runSummaryPath: manifest.run_summary_path,
496
+ metricsPath: join(env.runsRoot, env.taskId, 'metrics.json'),
497
+ summary: manifest.summary ?? null
498
+ });
499
+ return { manifest, runSummary };
500
+ }
501
+ attachPlanTargetTracker(manager, manifest, paths, persister) {
502
+ manager.bus.on('plan:completed', (event) => {
503
+ const targetId = event.payload.plan.targetId ?? null;
504
+ if (manifest.plan_target_id === targetId) {
505
+ return;
506
+ }
507
+ manifest.plan_target_id = targetId;
508
+ void persistManifest(paths, manifest, persister, { force: true }).catch((error) => {
509
+ logger.warn(`Failed to persist plan target for run ${manifest.run_id}: ${error?.message ?? String(error)}`);
510
+ });
511
+ });
512
+ }
513
+ async validateResumeToken(paths, manifest, provided) {
514
+ let stored = manifest.resume_token;
515
+ if (!stored) {
516
+ try {
517
+ stored = (await readFile(paths.resumeTokenPath, 'utf8')).trim();
518
+ }
519
+ catch (error) {
520
+ throw new Error(`Resume token missing for run ${manifest.run_id}: ${error?.message ?? String(error)}`);
521
+ }
522
+ }
523
+ if (provided && stored !== provided) {
524
+ throw new Error('Resume token mismatch.');
525
+ }
526
+ }
527
+ buildStatusPayload(env, manifest, paths) {
528
+ return {
529
+ run_id: manifest.run_id,
530
+ status: manifest.status,
531
+ status_detail: manifest.status_detail,
532
+ started_at: manifest.started_at,
533
+ completed_at: manifest.completed_at,
534
+ manifest: relativeToRepo(env, paths.manifestPath),
535
+ artifact_root: manifest.artifact_root,
536
+ log_path: manifest.log_path,
537
+ heartbeat_at: manifest.heartbeat_at,
538
+ commands: manifest.commands,
539
+ child_runs: manifest.child_runs
540
+ };
541
+ }
542
+ renderStatus(manifest) {
543
+ logger.info(`Run: ${manifest.run_id}`);
544
+ logger.info(`Status: ${manifest.status}${manifest.status_detail ? ` (${manifest.status_detail})` : ''}`);
545
+ logger.info(`Started: ${manifest.started_at}`);
546
+ logger.info(`Completed: ${manifest.completed_at ?? 'in-progress'}`);
547
+ logger.info(`Manifest: ${manifest.artifact_root}/manifest.json`);
548
+ logger.info('Commands:');
549
+ for (const command of manifest.commands) {
550
+ const summary = command.summary ? ` — ${command.summary}` : '';
551
+ logger.info(` [${command.status}] ${command.title}${summary}`);
552
+ }
553
+ }
554
+ }
@@ -0,0 +1,32 @@
1
+ export const defaultDiagnosticsPipeline = {
2
+ id: 'diagnostics',
3
+ title: 'Diagnostics Pipeline',
4
+ description: 'Build, lint, test, and optionally run spec-guard for the repository.',
5
+ tags: ['diagnostics-primary', 'diagnostics-secondary'],
6
+ stages: [
7
+ {
8
+ kind: 'command',
9
+ id: 'build',
10
+ title: 'npm run build',
11
+ command: 'npm run build'
12
+ },
13
+ {
14
+ kind: 'command',
15
+ id: 'lint',
16
+ title: 'npm run lint',
17
+ command: 'npm run lint'
18
+ },
19
+ {
20
+ kind: 'command',
21
+ id: 'test',
22
+ title: 'npm run test',
23
+ command: 'npm run test'
24
+ },
25
+ {
26
+ kind: 'command',
27
+ id: 'spec-guard',
28
+ title: 'Optional spec-guard (if scripts/spec-guard.mjs exists)',
29
+ command: 'node "$CODEX_ORCHESTRATOR_PACKAGE_ROOT/dist/orchestrator/src/cli/utils/specGuardRunner.js" --dry-run'
30
+ }
31
+ ]
32
+ };
@@ -0,0 +1,72 @@
1
+ const DESIGN_PIPELINE_ENV = {
2
+ DESIGN_PIPELINE: '1'
3
+ };
4
+ export const designReferencePipeline = {
5
+ id: 'design-reference',
6
+ title: 'Design Reference Pipeline',
7
+ description: 'Extracts design reference assets, stages Storybook-ready components, and records manifest evidence.',
8
+ tags: ['design', 'reference'],
9
+ stages: [
10
+ {
11
+ kind: 'command',
12
+ id: 'design-config',
13
+ title: 'Resolve design configuration',
14
+ command: 'node "$CODEX_ORCHESTRATOR_PACKAGE_ROOT/dist/scripts/design/pipeline/prepare.js"',
15
+ env: { ...DESIGN_PIPELINE_ENV }
16
+ },
17
+ {
18
+ kind: 'command',
19
+ id: 'design-extract',
20
+ title: 'Run Playwright design extractor',
21
+ command: 'node "$CODEX_ORCHESTRATOR_PACKAGE_ROOT/dist/scripts/design/pipeline/extract.js"',
22
+ env: { ...DESIGN_PIPELINE_ENV }
23
+ },
24
+ {
25
+ kind: 'command',
26
+ id: 'design-reference',
27
+ title: 'Build motherduck reference page',
28
+ command: 'node "$CODEX_ORCHESTRATOR_PACKAGE_ROOT/dist/scripts/design/pipeline/reference.js"',
29
+ env: { ...DESIGN_PIPELINE_ENV }
30
+ },
31
+ {
32
+ kind: 'command',
33
+ id: 'design-componentize',
34
+ title: 'Componentize artifacts via packages/design-system',
35
+ command: 'node "$CODEX_ORCHESTRATOR_PACKAGE_ROOT/dist/scripts/design/pipeline/componentize.js"',
36
+ env: { ...DESIGN_PIPELINE_ENV }
37
+ },
38
+ {
39
+ kind: 'command',
40
+ id: 'design-advanced-assets',
41
+ title: 'Generate advanced design assets',
42
+ command: 'node "$CODEX_ORCHESTRATOR_PACKAGE_ROOT/dist/scripts/design/pipeline/advanced-assets.js"',
43
+ env: { ...DESIGN_PIPELINE_ENV },
44
+ allowFailure: true,
45
+ summaryHint: 'Optional Framer Motion and FFmpeg assets'
46
+ },
47
+ {
48
+ kind: 'command',
49
+ id: 'design-visual-regression',
50
+ title: 'Run visual regression tests',
51
+ command: 'node "$CODEX_ORCHESTRATOR_PACKAGE_ROOT/dist/scripts/design/pipeline/visual-regression.js"',
52
+ env: { ...DESIGN_PIPELINE_ENV },
53
+ allowFailure: true,
54
+ summaryHint: 'Visual regression diffs stored under design/visual-regression/'
55
+ },
56
+ {
57
+ kind: 'command',
58
+ id: 'design-spec-guard',
59
+ title: 'Optional spec-guard (if scripts/spec-guard.mjs exists)',
60
+ command: 'node "$CODEX_ORCHESTRATOR_PACKAGE_ROOT/dist/orchestrator/src/cli/utils/specGuardRunner.js" --dry-run',
61
+ env: { ...DESIGN_PIPELINE_ENV },
62
+ summaryHint: 'Ensures design specs are fresh before artifact write'
63
+ },
64
+ {
65
+ kind: 'command',
66
+ id: 'design-artifact-writer',
67
+ title: 'Persist design artifact manifests',
68
+ command: 'node "$CODEX_ORCHESTRATOR_PACKAGE_ROOT/dist/scripts/design/pipeline/write-artifacts.js"',
69
+ env: { ...DESIGN_PIPELINE_ENV }
70
+ }
71
+ ]
72
+ };