@h-rig/bundle-default-lifecycle 0.0.6-alpha.135 → 0.0.6-alpha.137

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/src/index.js CHANGED
@@ -96,6 +96,7 @@ function assertCloseoutShadowEquivalent(input) {
96
96
  }
97
97
  // packages/bundle-default-lifecycle/src/defaultPipeline.ts
98
98
  import { resolveKernelStages } from "@rig/kernel/resolver";
99
+ import { createDefaultKernelPlugin } from "@rig/kernel/default-kernel";
99
100
 
100
101
  // packages/bundle-default-lifecycle/src/stages/auto-merge.ts
101
102
  import { runRepoDefaultMerge } from "@rig/runtime/control-plane/native/pr-automation";
@@ -104,7 +105,6 @@ import { runRepoDefaultMerge } from "@rig/runtime/control-plane/native/pr-automa
104
105
  function defineDefaultLifecycleStage(input) {
105
106
  return {
106
107
  ...input,
107
- protected: input.protected ?? false,
108
108
  priority: input.priority ?? 0
109
109
  };
110
110
  }
@@ -138,8 +138,7 @@ var isolationStage = defineDefaultLifecycleStage({
138
138
  id: "isolation",
139
139
  kind: "transform",
140
140
  description: "Provision the isolated runtime worktree through the runtime isolation subsystem.",
141
- calls: ["ensureAgentRuntime"],
142
- protected: true
141
+ calls: ["ensureAgentRuntime"]
143
142
  });
144
143
  async function runIsolationStage(input) {
145
144
  return await ensureAgentRuntime(input);
@@ -150,21 +149,37 @@ var journalAppendStage = defineDefaultLifecycleStage({
150
149
  id: "journal-append",
151
150
  kind: "observe",
152
151
  description: "Record resolved pipeline and per-stage outcome entries through the kernel journal capability.",
153
- calls: ["journalCapability.append"],
154
- protected: true
152
+ calls: ["journalCapability.append"]
155
153
  });
156
154
  async function runJournalAppendStage(input) {
157
155
  await input.journal.append(input.event);
158
156
  }
159
157
 
160
158
  // packages/bundle-default-lifecycle/src/stages/merge-gate.ts
159
+ import {
160
+ runStrictPrMergeGate
161
+ } from "@rig/runtime/control-plane/native/pr-review-gate";
161
162
  var mergeGateStage = defineDefaultLifecycleStage({
162
163
  id: "merge-gate",
163
164
  kind: "gate",
164
165
  description: "Enforce GitHub review state, required checks, and configured review gates through runtime PR automation.",
165
- calls: ["runStrictPrMergeGate"],
166
- protected: true
166
+ calls: ["runStrictPrMergeGate"]
167
167
  });
168
+ async function runMergeGateStage(input) {
169
+ return await runStrictPrMergeGate({
170
+ projectRoot: input.projectRoot,
171
+ prUrl: input.prUrl,
172
+ taskId: input.taskId,
173
+ runId: input.runId,
174
+ cycle: input.cycle,
175
+ command: input.command,
176
+ ...input.artifactRoot !== undefined ? { artifactRoot: input.artifactRoot } : {},
177
+ ...input.allowedFailures !== undefined ? { allowedFailures: input.allowedFailures } : {},
178
+ ...input.greptileApi !== undefined ? { greptileApi: input.greptileApi } : {},
179
+ ...input.final !== undefined ? { final: input.final } : {},
180
+ ...input.requireGreptile !== undefined ? { requireGreptile: input.requireGreptile } : {}
181
+ });
182
+ }
168
183
 
169
184
  // packages/bundle-default-lifecycle/src/stages/open-pr.ts
170
185
  import { runPrAutomation } from "@rig/runtime/control-plane/native/pr-automation";
@@ -260,7 +275,21 @@ var DEFAULT_LIFECYCLE_STAGE_IDS = [
260
275
  "isolation",
261
276
  "journal-append"
262
277
  ];
263
- var PROTECTED_DEFAULT_LIFECYCLE_STAGE_IDS = ["merge-gate", "isolation", "journal-append"];
278
+ var DEFAULT_STAGE_AFTER = {
279
+ verify: ["validate"],
280
+ commit: ["verify"],
281
+ push: ["commit"],
282
+ "open-pr": ["push"],
283
+ "merge-gate": ["open-pr"],
284
+ "auto-merge": ["merge-gate"],
285
+ "source-closeout": ["auto-merge"],
286
+ isolation: ["source-closeout"],
287
+ "journal-append": ["isolation"]
288
+ };
289
+ function withDefaultAnchors(stage) {
290
+ const after = DEFAULT_STAGE_AFTER[stage.id];
291
+ return after ? { ...stage, after } : stage;
292
+ }
264
293
  var defaultLifecycleStages = [
265
294
  validateStage,
266
295
  verifyStage,
@@ -272,7 +301,7 @@ var defaultLifecycleStages = [
272
301
  sourceCloseoutStage,
273
302
  isolationStage,
274
303
  journalAppendStage
275
- ];
304
+ ].map(withDefaultAnchors);
276
305
  function defaultLifecycleStageById(id) {
277
306
  const stage = defaultLifecycleStages.find((candidate) => candidate.id === id);
278
307
  if (!stage)
@@ -280,8 +309,7 @@ function defaultLifecycleStageById(id) {
280
309
  return stage;
281
310
  }
282
311
  function resolveDefaultLifecycle(input = {}) {
283
- const grants = { protectedStageGrants: input.protectedStageGrants ?? [] };
284
- return resolveKernelStages(defaultLifecycleStages, input.mutations ?? [], grants);
312
+ return resolveKernelStages(defaultLifecycleStages, input.mutations ?? []);
285
313
  }
286
314
  function defaultPipelineShowData(input = {}) {
287
315
  const resolved = resolveDefaultLifecycle(input);
@@ -290,7 +318,6 @@ function defaultPipelineShowData(input = {}) {
290
318
  index: orderIndex.get(entry.stageId) ?? null,
291
319
  id: entry.stageId,
292
320
  contributedBy: entry.contributedBy,
293
- protected: entry.isProtected,
294
321
  ...entry.removedBy !== undefined ? { removedBy: entry.removedBy } : {},
295
322
  ...entry.replacedBy !== undefined ? { replacedBy: entry.replacedBy } : {},
296
323
  wrappedBy: entry.wrappedBy ?? [],
@@ -305,17 +332,41 @@ function defaultPipelineShowData(input = {}) {
305
332
  ...resolved.resolvedAt !== undefined ? { resolvedAt: resolved.resolvedAt } : {}
306
333
  };
307
334
  }
335
+ function defaultKernelStatusData() {
336
+ const plugin = createDefaultKernelPlugin();
337
+ const resolved = resolveDefaultLifecycle();
338
+ const capabilities = {};
339
+ for (const capability of plugin.provides)
340
+ capabilities[capability] = plugin.meta.id;
341
+ return {
342
+ kernelProviderId: plugin.meta.id,
343
+ kernelVersion: plugin.meta.version,
344
+ capabilities,
345
+ pipelineStageCount: resolved.order.length,
346
+ stageOrder: [...resolved.order]
347
+ };
348
+ }
349
+ function formatKernelStatus(data = defaultKernelStatusData()) {
350
+ const lines = [
351
+ `kernel: ${data.kernelProviderId} v${data.kernelVersion}`,
352
+ "capabilities (resolved provider):",
353
+ ...Object.entries(data.capabilities).map(([cap, provider]) => ` ${cap.padEnd(14)} ${provider}`),
354
+ `lifecycle stages: mutable`,
355
+ `resolved pipeline (${data.pipelineStageCount} stages): ${data.stageOrder.join(" -> ")}`
356
+ ];
357
+ return lines.join(`
358
+ `);
359
+ }
308
360
  function formatDefaultPipelineShow(input = {}) {
309
361
  const data = defaultPipelineShowData(input);
310
362
  const lines = [`${data.title}:`];
311
363
  for (const stage of data.stages) {
312
364
  if (stage.removedBy) {
313
- lines.push(` - ${stage.id.padEnd(20)} [${stage.contributedBy}] removed by [${stage.removedBy}]${stage.protected ? " PROTECTED" : ""}`);
365
+ lines.push(` - ${stage.id.padEnd(20)} [${stage.contributedBy}] removed by [${stage.removedBy}]`);
314
366
  continue;
315
367
  }
316
368
  const ordinal = stage.index === null ? " -." : `${String(stage.index).padStart(2)}.`;
317
369
  const annotations = [
318
- stage.protected ? "PROTECTED" : "",
319
370
  stage.replacedBy ? `replaced by [${stage.replacedBy}]` : "",
320
371
  stage.wrappedBy.length > 0 ? `wrapped by [${stage.wrappedBy.join(", ")}]` : ""
321
372
  ].filter(Boolean);
@@ -325,12 +376,125 @@ function formatDefaultPipelineShow(input = {}) {
325
376
  return lines.join(`
326
377
  `);
327
378
  }
328
- // packages/bundle-default-lifecycle/src/stagedCloseout.ts
379
+ // packages/bundle-default-lifecycle/src/pipelineCloseout.ts
329
380
  import { resolve as resolve2 } from "path";
330
381
  import { loadConfig } from "@rig/core/load-config";
382
+ import { createPluginHost } from "@rig/core";
383
+ import { createDefaultKernel } from "@rig/kernel/default-kernel";
331
384
  import { buildPluginHostContext } from "@rig/runtime/control-plane/plugin-host-context";
332
- import { CloseoutValidationError } from "@rig/runtime/control-plane/native/in-process-closeout";
385
+ import {
386
+ CloseoutValidationError
387
+ } from "@rig/runtime/control-plane/native/in-process-closeout";
333
388
  import { taskValidate } from "@rig/runtime/control-plane/native/task-ops";
389
+
390
+ // packages/bundle-default-lifecycle/src/plugin.ts
391
+ import { definePlugin } from "@rig/core";
392
+
393
+ // packages/bundle-default-lifecycle/src/cli.ts
394
+ var DEFAULT_PIPELINE_CLI_ID = "default-lifecycle.pipeline";
395
+ var DEFAULT_KERNEL_CLI_ID = "default-lifecycle.kernel";
396
+ function printJson(value) {
397
+ console.log(JSON.stringify(value, null, 2));
398
+ }
399
+ function takeFlag(args, flag) {
400
+ const rest = [...args];
401
+ const index = rest.indexOf(flag);
402
+ if (index < 0)
403
+ return { value: false, rest };
404
+ rest.splice(index, 1);
405
+ return { value: true, rest };
406
+ }
407
+ function requireNoExtraArgs(args, usage) {
408
+ if (args.length > 0)
409
+ throw new Error(`Unexpected argument: ${args[0]}
410
+ Usage: ${usage}`);
411
+ }
412
+ async function executePipeline(context, args) {
413
+ const [first = "show", ...rest] = args;
414
+ const command = first.startsWith("-") ? "show" : first;
415
+ const commandArgs = first.startsWith("-") ? args : rest;
416
+ if (command !== "show")
417
+ throw new Error(`Unknown pipeline command: ${command}`);
418
+ const json = takeFlag(commandArgs, "--json");
419
+ requireNoExtraArgs(json.rest, "rig pipeline show [--json]");
420
+ const details = defaultPipelineShowData();
421
+ if (context.outputMode === "text") {
422
+ if (json.value)
423
+ printJson(details);
424
+ else
425
+ console.log(formatDefaultPipelineShow());
426
+ }
427
+ return { ok: true, group: "pipeline", command: "show", details };
428
+ }
429
+ async function executeKernel(context, args) {
430
+ const [first = "status", ...rest] = args;
431
+ const command = first.startsWith("-") ? "status" : first;
432
+ const commandArgs = first.startsWith("-") ? args : rest;
433
+ if (command !== "status")
434
+ throw new Error(`Unknown kernel command: ${command}`);
435
+ const json = takeFlag(commandArgs, "--json");
436
+ requireNoExtraArgs(json.rest, "rig kernel status [--json]");
437
+ const details = defaultKernelStatusData();
438
+ if (context.outputMode === "text") {
439
+ if (json.value)
440
+ printJson(details);
441
+ else
442
+ console.log(formatKernelStatus(details));
443
+ }
444
+ return { ok: true, group: "kernel", command: "status", details };
445
+ }
446
+ var defaultLifecycleCliCommands = [
447
+ {
448
+ id: DEFAULT_PIPELINE_CLI_ID,
449
+ family: "pipeline",
450
+ command: "rig pipeline show [--json]",
451
+ description: "Show the resolved default lifecycle pipeline.",
452
+ usage: "rig pipeline show [--json]",
453
+ run: executePipeline
454
+ },
455
+ {
456
+ id: DEFAULT_KERNEL_CLI_ID,
457
+ family: "kernel",
458
+ command: "rig kernel status [--json]",
459
+ description: "Show default kernel provider and lifecycle status.",
460
+ usage: "rig kernel status [--json]",
461
+ run: executeKernel
462
+ }
463
+ ];
464
+
465
+ // packages/bundle-default-lifecycle/src/plugin.ts
466
+ var DEFAULT_LIFECYCLE_PLUGIN_ID = "@rig/bundle-default-lifecycle";
467
+ function createDefaultLifecyclePlugin(stages = {}) {
468
+ const plugin = definePlugin({
469
+ name: DEFAULT_LIFECYCLE_PLUGIN_ID,
470
+ version: "0.0.0-alpha.1",
471
+ provides: [],
472
+ contributes: {
473
+ stages: defaultLifecycleStages,
474
+ capabilities: [
475
+ { id: "default-lifecycle.pipeline", title: "Default lifecycle pipeline", commandId: DEFAULT_PIPELINE_CLI_ID },
476
+ { id: "default-lifecycle.kernel-status", title: "Default kernel status", commandId: DEFAULT_KERNEL_CLI_ID }
477
+ ],
478
+ cliCommands: defaultLifecycleCliCommands.map(({ run: _run, ...metadata }) => metadata)
479
+ }
480
+ }, {
481
+ stages,
482
+ featureCapabilities: [
483
+ { id: "default-lifecycle.pipeline", title: "Default lifecycle pipeline", commandId: DEFAULT_PIPELINE_CLI_ID },
484
+ { id: "default-lifecycle.kernel-status", title: "Default kernel status", commandId: DEFAULT_KERNEL_CLI_ID }
485
+ ],
486
+ cliCommands: defaultLifecycleCliCommands
487
+ });
488
+ return {
489
+ ...plugin,
490
+ meta: { id: DEFAULT_LIFECYCLE_PLUGIN_ID, name: "Default Rig Lifecycle", version: "0.0.0-alpha.1" },
491
+ contributes: { ...plugin.contributes ?? {}, stages: defaultLifecycleStages },
492
+ runtime: { stages }
493
+ };
494
+ }
495
+ var defaultLifecyclePlugin = createDefaultLifecyclePlugin();
496
+
497
+ // packages/bundle-default-lifecycle/src/pipelineCloseout.ts
334
498
  function cleanString(value) {
335
499
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
336
500
  }
@@ -346,9 +510,6 @@ function closeoutOutcome(status) {
346
510
  return "started";
347
511
  }
348
512
  }
349
- function normalizePrStatus(status) {
350
- return status === "needs_attention" ? "needs-attention" : status;
351
- }
352
513
  async function loadRigAutomationConfig(projectRoot) {
353
514
  try {
354
515
  return await loadConfig(projectRoot);
@@ -360,140 +521,233 @@ async function runRigProjectValidation({ projectRoot, taskId }) {
360
521
  const pluginHostCtx = await buildPluginHostContext(projectRoot);
361
522
  return taskValidate(projectRoot, taskId, pluginHostCtx?.validatorRegistry ?? undefined);
362
523
  }
363
- async function executeStagedCloseout(input) {
524
+ function shouldAttemptRigMerge(config) {
525
+ const mode = config.merge?.mode;
526
+ return mode !== "off" && mode !== "pr-ready";
527
+ }
528
+ async function loadPluginStageContributions(projectRoot) {
529
+ try {
530
+ const config = await loadConfig(projectRoot);
531
+ const host = createPluginHost(config.plugins ?? []);
532
+ return { executors: host.listStageExecutors(), mutations: host.listStageMutations() };
533
+ } catch {
534
+ return { executors: {}, mutations: [] };
535
+ }
536
+ }
537
+ async function runPipelineCloseout(input) {
364
538
  const taskId = cleanString(input.taskId);
365
539
  if (!taskId) {
366
- throw new Error("Staged closeout requires a task id.");
540
+ throw new Error("Pipeline closeout requires a task id.");
367
541
  }
368
542
  const loadedConfig = input.config ?? await loadRigAutomationConfig(input.projectRoot);
369
543
  const prMode = loadedConfig?.pr?.mode ?? "off";
370
544
  const reviewProvider = loadedConfig?.review?.provider ?? "github";
371
545
  const effectiveConfig = {
372
546
  ...loadedConfig ?? {},
373
- pr: {
374
- ...loadedConfig?.pr ?? {},
375
- mode: prMode
376
- },
377
- review: {
378
- ...loadedConfig?.review ?? {},
379
- provider: reviewProvider
380
- }
547
+ pr: { ...loadedConfig?.pr ?? {}, mode: prMode },
548
+ review: { ...loadedConfig?.review ?? {}, provider: reviewProvider }
381
549
  };
550
+ const openOnlyConfig = {
551
+ ...effectiveConfig,
552
+ merge: { ...effectiveConfig.merge ?? {}, mode: "pr-ready" }
553
+ };
554
+ const shouldMerge = shouldAttemptRigMerge(effectiveConfig);
382
555
  const workspace = input.workspace;
383
556
  const artifactRoot = input.artifactRoot ?? resolve2(input.projectRoot, "artifacts", taskId);
384
- let branch = input.branch;
385
- let currentPhase = "queued";
386
557
  const journal = async (phase, status, detail) => {
387
- currentPhase = phase;
388
558
  await input.journalPhase(phase, closeoutOutcome(status), detail ?? null);
389
559
  };
390
- const complete = async (result, detail) => {
391
- await journal("completed", "completed", detail);
392
- return result;
560
+ if (prMode === "off" || prMode === "ask") {
561
+ const reason = prMode === "ask" ? "PR creation awaits operator approval." : "PR automation disabled.";
562
+ await input.reflect("under_review", reason);
563
+ await journal("completed", "completed", reason);
564
+ return {
565
+ mode: "pipeline",
566
+ pipelineStageIds: [...resolveDefaultLifecycle().order],
567
+ result: { status: "skipped", iterations: 0, feedback: [] }
568
+ };
569
+ }
570
+ const state = {
571
+ branch: input.branch,
572
+ validationPassed: false,
573
+ committed: false,
574
+ pushed: false,
575
+ prUrl: null,
576
+ prReady: false,
577
+ pr: null,
578
+ gate: null,
579
+ mergeGate: null,
580
+ merged: false,
581
+ iterations: 0,
582
+ feedback: [],
583
+ blockedDetail: null
393
584
  };
394
- try {
395
- if (prMode === "off" || prMode === "ask") {
396
- const reason = prMode === "ask" ? "PR creation awaits operator approval." : "PR automation disabled.";
397
- await input.reflect("under_review", reason);
398
- return await complete({ status: "skipped", iterations: 0, feedback: [] }, reason);
399
- }
400
- await input.onValidationStart?.();
401
- await journal("queued", "running", `Validating task ${taskId} before closeout.`);
402
- let validationPassed = false;
403
- try {
404
- validationPassed = await runValidateStage({ projectRoot: input.projectRoot, taskId }, input.runValidation ?? runRigProjectValidation);
405
- } catch (error) {
406
- const detail2 = `Rig validation failed before closeout: ${error instanceof Error ? error.message : String(error)}`;
407
- await input.reflect("needs_attention", "Rig validation failed before closeout; commit/push/PR automation is blocked.", { errorText: detail2 });
408
- throw new CloseoutValidationError(detail2);
409
- }
410
- if (!validationPassed) {
411
- const detail2 = `Rig validation failed for task ${taskId}; closeout blocked before commit.`;
412
- await input.reflect("needs_attention", "Rig validation failed before closeout; commit/push/PR automation is blocked.", { errorText: detail2 });
413
- throw new CloseoutValidationError(detail2);
414
- }
415
- await journal("queued", "completed", `Validation passed for task ${taskId}.`);
416
- const workspaceBranch = await input.gitCommand(["rev-parse", "--abbrev-ref", "HEAD"], { cwd: workspace });
417
- const currentWorkspaceBranch = workspaceBranch.exitCode === 0 ? cleanString(workspaceBranch.stdout) : null;
418
- if (currentWorkspaceBranch && currentWorkspaceBranch !== "HEAD" && currentWorkspaceBranch !== branch) {
419
- branch = currentWorkspaceBranch;
420
- }
421
- await journal("commit", "running", `Committing changes in ${workspace}.`);
422
- await runCommitStage({ cwd: workspace, message: `rig: complete task ${taskId}`, command: input.gitCommand });
423
- await journal("push", "running", `Pushing branch ${branch}.`);
424
- await runPushStage({ projectRoot: workspace, branch, gitCommand: input.gitCommand });
425
- await journal("pr-review-merge", "running", `Opening and merging a pull request for ${branch}.`);
426
- const pr = await runOpenPrStage({
427
- ...input,
428
- taskId,
429
- branch,
430
- artifactRoot,
431
- config: effectiveConfig,
432
- sourceTask: { title: cleanString(input.sourceTask?.title) },
433
- lifecycle: {
434
- onPrOpened: async ({ prUrl }) => {
435
- await journal("pr-opened", "running", prUrl);
436
- await input.reflect("under_review", "Rig opened a pull request for this task.");
437
- },
438
- onFeedback: async ({ feedback }) => {
439
- await input.reflect("ci_fixing", "Rig is fixing CI/review feedback for this task.", { errorText: feedback.join(`
585
+ const cont = (ctx2) => ({ kind: "continue", ctx: ctx2 });
586
+ const executors = {
587
+ isolation: (ctx2) => cont(ctx2),
588
+ validate: async (ctx2) => {
589
+ await input.onValidationStart?.();
590
+ await journal("queued", "running", `Validating task ${taskId} before closeout.`);
591
+ let passed = false;
592
+ try {
593
+ passed = await runValidateStage({ projectRoot: input.projectRoot, taskId }, input.runValidation ?? runRigProjectValidation);
594
+ } catch (error) {
595
+ const detail = `Rig validation failed before closeout: ${error instanceof Error ? error.message : String(error)}`;
596
+ await input.reflect("needs_attention", "Rig validation failed before closeout; commit/push/PR automation is blocked.", { errorText: detail });
597
+ throw new CloseoutValidationError(detail);
598
+ }
599
+ if (!passed) {
600
+ const detail = `Rig validation failed for task ${taskId}; closeout blocked before commit.`;
601
+ await input.reflect("needs_attention", "Rig validation failed before closeout; commit/push/PR automation is blocked.", { errorText: detail });
602
+ throw new CloseoutValidationError(detail);
603
+ }
604
+ state.validationPassed = true;
605
+ await journal("queued", "completed", `Validation passed for task ${taskId}.`);
606
+ const workspaceBranch = await input.gitCommand(["rev-parse", "--abbrev-ref", "HEAD"], { cwd: workspace });
607
+ const currentWorkspaceBranch = workspaceBranch.exitCode === 0 ? cleanString(workspaceBranch.stdout) : null;
608
+ if (currentWorkspaceBranch && currentWorkspaceBranch !== "HEAD" && currentWorkspaceBranch !== state.branch) {
609
+ state.branch = currentWorkspaceBranch;
610
+ }
611
+ return cont(ctx2);
612
+ },
613
+ verify: () => state.validationPassed ? { kind: "allow" } : { kind: "block", reason: "validation did not pass" },
614
+ commit: async (ctx2) => {
615
+ await journal("commit", "running", `Committing changes in ${workspace}.`);
616
+ const committed = await runCommitStage({ cwd: workspace, message: `rig: complete task ${taskId}`, command: input.gitCommand });
617
+ state.committed = committed.committed;
618
+ return cont(ctx2);
619
+ },
620
+ push: async (ctx2) => {
621
+ await journal("push", "running", `Pushing branch ${state.branch}.`);
622
+ await runPushStage({ projectRoot: workspace, branch: state.branch, gitCommand: input.gitCommand });
623
+ state.pushed = true;
624
+ return cont(ctx2);
625
+ },
626
+ "open-pr": async (ctx2) => {
627
+ await journal("pr-review-merge", "running", `Opening a pull request for ${state.branch}.`);
628
+ const pr = await runOpenPrStage({
629
+ ...input,
630
+ taskId,
631
+ branch: state.branch,
632
+ artifactRoot,
633
+ config: openOnlyConfig,
634
+ sourceTask: { title: cleanString(input.sourceTask?.title) },
635
+ lifecycle: {
636
+ onPrOpened: async ({ prUrl }) => {
637
+ await journal("pr-opened", "running", prUrl);
638
+ await input.reflect("under_review", "Rig opened a pull request for this task.");
639
+ },
640
+ onFeedback: async ({ feedback }) => {
641
+ await input.reflect("ci_fixing", "Rig is fixing CI/review feedback for this task.", { errorText: feedback.join(`
440
642
  `) || null });
441
- },
442
- onMergeStarted: async ({ prUrl }) => {
443
- await journal("merge", "running", prUrl);
444
- await input.reflect("merging", "Rig is merging the pull request for this task.");
445
- },
446
- onMerged: async ({ prUrl }) => {
447
- await journal("close-source", "running", prUrl);
643
+ }
448
644
  }
645
+ });
646
+ state.pr = pr;
647
+ state.prUrl = pr.prUrl ?? null;
648
+ state.prReady = pr.status === "opened" || pr.status === "merged";
649
+ state.iterations = pr.iterations;
650
+ state.feedback = [...pr.actionableFeedback];
651
+ if (pr.status === "needs_attention") {
652
+ state.blockedDetail = pr.actionableFeedback.join(`
653
+ `) || "PR automation did not produce a mergeable PR.";
654
+ }
655
+ return cont(ctx2);
656
+ },
657
+ "merge-gate": async (ctx2) => {
658
+ if (!shouldMerge || !state.prReady || !state.prUrl) {
659
+ state.mergeGate = state.prReady ? "skipped" : null;
660
+ return state.prReady ? cont(ctx2) : { kind: "block", reason: state.blockedDetail ?? "no mergeable PR to gate" };
661
+ }
662
+ const gate = await runMergeGateStage({
663
+ projectRoot: workspace,
664
+ prUrl: state.prUrl,
665
+ taskId,
666
+ runId: input.runId,
667
+ cycle: 1,
668
+ command: input.command,
669
+ artifactRoot,
670
+ final: true,
671
+ ...effectiveConfig.merge?.allowedFailures ? { allowedFailures: effectiveConfig.merge.allowedFailures } : {},
672
+ ...input.greptileApi !== undefined ? { greptileApi: input.greptileApi } : {},
673
+ requireGreptile: reviewProvider === "greptile"
674
+ });
675
+ state.gate = gate;
676
+ if (gate.approved) {
677
+ state.mergeGate = "passed";
678
+ return cont(ctx2);
679
+ }
680
+ state.mergeGate = "blocked";
681
+ const detail = gate.actionableFeedback.join(`
682
+ `) || gate.reasons.join("; ") || "merge gate blocked the PR.";
683
+ state.blockedDetail = detail;
684
+ await input.reflect("needs_attention", "Rig needs operator attention before this task can merge.", { errorText: detail });
685
+ await journal("pr-review-merge", "needs-attention", detail);
686
+ return { kind: "block", reason: detail };
687
+ },
688
+ "auto-merge": async (ctx2) => {
689
+ if (!shouldMerge || state.mergeGate !== "passed" || !state.prUrl || !state.gate) {
690
+ return cont(ctx2);
449
691
  }
450
- });
451
- if (pr.status === "merged" && pr.prUrl) {
692
+ await journal("merge", "running", state.prUrl);
693
+ await input.reflect("merging", "Rig is merging the pull request for this task.");
694
+ await runAutoMergeStage({ prUrl: state.prUrl, config: effectiveConfig, command: input.command, cwd: workspace, strictGate: state.gate });
695
+ state.merged = true;
696
+ return cont(ctx2);
697
+ },
698
+ "source-closeout": async (ctx2) => {
699
+ if (!state.merged || !state.prUrl)
700
+ return cont(ctx2);
701
+ await journal("close-source", "running", state.prUrl);
702
+ const mergedPr = { ...state.pr ?? { iterations: state.iterations, actionableFeedback: state.feedback }, status: "merged", prUrl: state.prUrl, merged: true };
452
703
  await runSourceCloseoutStage({
453
704
  projectRoot: input.projectRoot,
454
705
  taskId,
455
706
  runId: input.runId,
456
- pr,
707
+ pr: mergedPr,
457
708
  ...input.sourceTask !== undefined ? { sourceTask: input.sourceTask } : {},
458
709
  updateTaskSource: async () => {
459
710
  await input.reflect("closed", "Rig merged the pull request and closed this task source.");
460
711
  return { updated: true, taskId, status: "closed", source: "runtime", sourceKind: "runtime" };
461
712
  }
462
713
  });
463
- return await complete({ status: "merged", prUrl: pr.prUrl, iterations: pr.iterations, feedback: pr.actionableFeedback }, `PR merged and issue closed: ${pr.prUrl}`);
464
- }
465
- if (pr.status === "opened" && pr.prUrl) {
466
- return await complete({ status: "opened", prUrl: pr.prUrl, iterations: pr.iterations, feedback: pr.actionableFeedback }, `PR ready without merge: ${pr.prUrl}`);
467
- }
468
- const detail = pr.actionableFeedback.join(`
469
- `) || "PR automation did not merge the PR.";
470
- await input.reflect("needs_attention", "Rig needs operator attention before this task can proceed.", { errorText: detail });
471
- await journal("pr-review-merge", "needs-attention", detail);
472
- return { status: normalizePrStatus(pr.status), ...pr.prUrl ? { prUrl: pr.prUrl } : {}, iterations: pr.iterations, feedback: pr.actionableFeedback };
473
- } catch (error) {
474
- const detail = error instanceof Error ? error.message : String(error);
475
- await journal(currentPhase, "failed", detail);
476
- throw error;
477
- }
478
- }
479
- async function runStagedCloseout(input) {
480
- const resolved = resolveDefaultLifecycle();
481
- const result = await executeStagedCloseout(input);
482
- return {
483
- mode: "staged",
484
- pipelineStageIds: [...resolved.order],
485
- result
714
+ return cont(ctx2);
715
+ },
716
+ "journal-append": (ctx2) => cont(ctx2)
486
717
  };
487
- }
488
- // packages/bundle-default-lifecycle/src/plugin.ts
489
- var DEFAULT_LIFECYCLE_PLUGIN_ID = "@rig/bundle-default-lifecycle";
490
- function createDefaultLifecyclePlugin() {
491
- return {
492
- meta: { id: DEFAULT_LIFECYCLE_PLUGIN_ID, name: "Default Rig Lifecycle", version: "0.0.0-alpha.1" },
493
- provides: new Set,
494
- contributes: { stages: defaultLifecycleStages },
495
- runtime: { stages: defaultLifecycleStages }
718
+ const defaultLifecyclePlugin2 = createDefaultLifecyclePlugin(executors);
719
+ const pluginStages = await loadPluginStageContributions(input.projectRoot);
720
+ const kernel = createDefaultKernel({ ...input.kernelJournal ? { journal: input.kernelJournal } : {}, stageExecutors: { ...defaultLifecyclePlugin2.runtime.stages, ...pluginStages.executors } });
721
+ const resolved = kernel.stageRunner.resolve(defaultLifecyclePlugin2.contributes.stages, pluginStages.mutations);
722
+ const ctx = {
723
+ runId: input.runId,
724
+ taskId,
725
+ state,
726
+ metadata: { projectRoot: input.projectRoot, workspace, closeoutState: state }
496
727
  };
728
+ await kernel.stageRunner.runPipeline(input.runId, resolved, ctx);
729
+ const result = mapStateToResult(state);
730
+ if (result.status === "merged" || result.status === "opened") {
731
+ await journal("completed", "completed", result.prUrl ? `${result.status === "merged" ? "PR merged and issue closed" : "PR ready without merge"}: ${result.prUrl}` : result.status);
732
+ }
733
+ return { mode: "pipeline", pipelineStageIds: [...resolved.order], result };
734
+ }
735
+ function mapStateToResult(state) {
736
+ if (state.merged && state.prUrl) {
737
+ return { status: "merged", prUrl: state.prUrl, iterations: state.iterations, feedback: state.feedback };
738
+ }
739
+ if (state.mergeGate === "blocked" || state.blockedDetail) {
740
+ return {
741
+ status: "needs-attention",
742
+ ...state.prUrl ? { prUrl: state.prUrl } : {},
743
+ iterations: state.iterations,
744
+ feedback: state.feedback
745
+ };
746
+ }
747
+ if (state.prReady && state.prUrl) {
748
+ return { status: "opened", prUrl: state.prUrl, iterations: state.iterations, feedback: state.feedback };
749
+ }
750
+ return { status: "needs-attention", ...state.prUrl ? { prUrl: state.prUrl } : {}, iterations: state.iterations, feedback: state.feedback };
497
751
  }
498
752
  export {
499
753
  verifyStage,
@@ -502,10 +756,11 @@ export {
502
756
  snapshotCloseoutArtifacts,
503
757
  runVerifyStage,
504
758
  runValidateStage,
505
- runStagedCloseout,
506
759
  runSourceCloseoutStage,
507
760
  runPushStage,
761
+ runPipelineCloseout,
508
762
  runOpenPrStage,
763
+ runMergeGateStage,
509
764
  runJournalAppendStage,
510
765
  runIsolationStage,
511
766
  runCommitStage,
@@ -516,20 +771,27 @@ export {
516
771
  mergeGateStage,
517
772
  journalAppendStage,
518
773
  isolationStage,
774
+ formatKernelStatus,
519
775
  formatDefaultPipelineShow,
776
+ executePipeline,
777
+ executeKernel,
520
778
  defineDefaultLifecycleStage,
521
779
  defaultPipelineShowData,
522
780
  defaultLifecycleStages,
523
781
  defaultLifecycleStageById,
782
+ defaultLifecyclePlugin,
783
+ defaultLifecycleCliCommands,
784
+ defaultKernelStatusData,
524
785
  createDefaultLifecyclePlugin,
525
786
  compareCloseoutShadowRuns,
526
787
  compareCloseoutEquivalence,
527
788
  commitStage,
528
789
  autoMergeStage,
529
790
  assertCloseoutShadowEquivalent,
530
- PROTECTED_DEFAULT_LIFECYCLE_STAGE_IDS,
791
+ DEFAULT_PIPELINE_CLI_ID,
531
792
  DEFAULT_LIFECYCLE_STAGE_IDS,
532
793
  DEFAULT_LIFECYCLE_PLUGIN_ID,
794
+ DEFAULT_KERNEL_CLI_ID,
533
795
  CLOSEOUT_EQUIVALENCE_STATE_FILES,
534
796
  CLOSEOUT_EQUIVALENCE_ARTIFACTS
535
797
  };