bosun 0.41.8 → 0.41.10

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 (58) hide show
  1. package/.env.example +1 -1
  2. package/README.md +23 -1
  3. package/agent/agent-event-bus.mjs +31 -2
  4. package/agent/agent-pool.mjs +275 -40
  5. package/agent/agent-prompts.mjs +9 -1
  6. package/agent/agent-supervisor.mjs +22 -0
  7. package/agent/autofix.mjs +1 -1
  8. package/agent/primary-agent.mjs +115 -5
  9. package/cli.mjs +3 -2
  10. package/config/config.mjs +47 -33
  11. package/config/context-shredding-config.mjs +1 -1
  12. package/config/repo-root.mjs +41 -33
  13. package/desktop/main.mjs +350 -25
  14. package/desktop/preload.cjs +8 -0
  15. package/desktop/preload.mjs +19 -0
  16. package/entrypoint.mjs +332 -0
  17. package/git/sdk-conflict-resolver.mjs +1 -1
  18. package/infra/health-status.mjs +72 -0
  19. package/infra/library-manager.mjs +58 -1
  20. package/infra/maintenance.mjs +1 -2
  21. package/infra/monitor.mjs +26 -8
  22. package/infra/session-tracker.mjs +30 -3
  23. package/package.json +12 -4
  24. package/server/bosun-mcp-server.mjs +1004 -0
  25. package/server/setup-web-server.mjs +288 -259
  26. package/server/ui-server.mjs +1323 -26
  27. package/shell/claude-shell.mjs +14 -1
  28. package/shell/codex-config.mjs +1 -1
  29. package/shell/codex-model-profiles.mjs +170 -30
  30. package/shell/codex-shell.mjs +63 -18
  31. package/shell/opencode-providers.mjs +20 -8
  32. package/task/task-executor.mjs +28 -0
  33. package/task/task-store.mjs +13 -4
  34. package/telegram/telegram-sentinel.mjs +54 -3
  35. package/tools/list-todos.mjs +7 -1
  36. package/ui/app.js +3 -2
  37. package/ui/components/agent-selector.js +127 -0
  38. package/ui/components/session-list.js +15 -10
  39. package/ui/demo-defaults.js +334 -336
  40. package/ui/modules/router.js +2 -0
  41. package/ui/modules/state.js +13 -5
  42. package/ui/tabs/chat.js +3 -0
  43. package/ui/tabs/library.js +284 -52
  44. package/ui/tabs/tasks.js +5 -13
  45. package/ui/tabs/workflows.js +766 -3
  46. package/workflow/workflow-engine.mjs +246 -5
  47. package/workflow/workflow-nodes/definitions.mjs +37 -0
  48. package/workflow/workflow-nodes.mjs +1014 -184
  49. package/workflow/workflow-templates.mjs +0 -5
  50. package/workflow-templates/_helpers.mjs +253 -0
  51. package/workflow-templates/agents.mjs +199 -226
  52. package/workflow-templates/github.mjs +106 -16
  53. package/workflow-templates/sub-workflows.mjs +233 -0
  54. package/workflow-templates/task-execution.mjs +125 -471
  55. package/workflow-templates/task-lifecycle.mjs +11 -48
  56. package/workspace/command-diagnostics.mjs +460 -0
  57. package/workspace/context-cache.mjs +396 -28
  58. package/workspace/worktree-manager.mjs +1 -1
@@ -11,7 +11,8 @@
11
11
  * - Meeting Orchestrator + Subworkflow Chain
12
12
  */
13
13
 
14
- import { node, edge, resetLayout } from "./_helpers.mjs";
14
+ import { node, edge, resetLayout, embedSubWorkflow, wire } from "./_helpers.mjs";
15
+ import { VALIDATION_GATE_SUB, PR_CHECK_HANDOFF_SUB } from "./sub-workflows.mjs";
15
16
 
16
17
  const AGENT_SESSION_MONITOR_COMMAND = [
17
18
  'node -e "',
@@ -495,39 +496,48 @@ export const AGENT_SESSION_MONITOR_TEMPLATE = {
495
496
 
496
497
  resetLayout();
497
498
 
498
- export const BACKEND_AGENT_TEMPLATE = {
499
- id: "template-backend-agent",
500
- name: "Task Completion Agent",
501
- description:
502
- "General-purpose task completion agent with a test-first methodology. " +
503
- "Writes tests first, implements the feature, validates with build + lint, " +
504
- "then creates a PR. Works with any language/framework — commands are " +
505
- "auto-detected from your project or fully customizable.",
506
- category: "agents",
507
- enabled: true,
508
- recommended: true,
509
- trigger: "trigger.task_assigned",
510
- variables: {
511
- testCommand: "npm test",
512
- buildCommand: "npm run build",
513
- lintCommand: "",
514
- baseBranch: "main",
515
- protectedBranches: ["main", "master", "develop", "production"],
516
- agentSdk: "auto",
517
- timeoutMs: 3600000,
518
- autoFixTimeoutMs: 1200000,
519
- },
520
- nodes: [
521
- node("trigger", "trigger.task_assigned", "Task Assigned", {
522
- }, { x: 400, y: 50 }),
499
+ export const BACKEND_AGENT_TEMPLATE = (() => {
500
+ resetLayout();
501
+
502
+ // ── Embed sub-workflows ────────────────────────────────────────────────
503
+ const mainValidation = embedSubWorkflow(VALIDATION_GATE_SUB, "main-");
504
+ const retryValidation = embedSubWorkflow(VALIDATION_GATE_SUB, "retry-");
505
+ const mainPrHandoff = embedSubWorkflow(PR_CHECK_HANDOFF_SUB, "main-");
506
+ const retryPrHandoff = embedSubWorkflow(PR_CHECK_HANDOFF_SUB, "retry-");
507
+
508
+ return {
509
+ id: "template-backend-agent",
510
+ name: "Task Completion Agent",
511
+ description:
512
+ "General-purpose task completion agent with a test-first methodology. " +
513
+ "Writes tests first, implements the feature, validates with build + lint, " +
514
+ "then creates a PR. Works with any language/framework — commands are " +
515
+ "auto-detected from your project or fully customizable.",
516
+ category: "agents",
517
+ enabled: true,
518
+ recommended: true,
519
+ trigger: "trigger.task_assigned",
520
+ variables: {
521
+ testCommand: "npm test",
522
+ buildCommand: "npm run build",
523
+ lintCommand: "",
524
+ baseBranch: "main",
525
+ protectedBranches: ["main", "master", "develop", "production"],
526
+ agentSdk: "auto",
527
+ timeoutMs: 3600000,
528
+ autoFixTimeoutMs: 1200000,
529
+ },
530
+ nodes: [
531
+ node("trigger", "trigger.task_assigned", "Task Assigned", {
532
+ }, { x: 400, y: 50 }),
523
533
 
524
- node("plan-work", "agent.run_planner", "Plan Implementation", {
525
- prompt: "Analyze the task requirements and create a step-by-step implementation plan. Identify which files need to be modified, what tests need to be written, and any API contracts to maintain.",
526
- outputVariable: "plan",
527
- }, { x: 400, y: 180 }),
534
+ node("plan-work", "agent.run_planner", "Plan Implementation", {
535
+ prompt: "Analyze the task requirements and create a step-by-step implementation plan. Identify which files need to be modified, what tests need to be written, and any API contracts to maintain.",
536
+ outputVariable: "plan",
537
+ }, { x: 400, y: 180 }),
528
538
 
529
- node("write-tests", "action.run_agent", "Write Tests First", {
530
- prompt: `# Test-First Development
539
+ node("write-tests", "action.run_agent", "Write Tests First", {
540
+ prompt: `# Test-First Development
531
541
 
532
542
  Based on the plan:
533
543
  {{plan}}
@@ -539,12 +549,12 @@ Write comprehensive tests FIRST before any implementation:
539
549
 
540
550
  Use the project's test command: {{testCommand}}
541
551
  Commit with message "test: add tests for [feature]"`,
542
- sdk: "{{agentSdk}}",
543
- timeoutMs: "{{timeoutMs}}",
544
- }, { x: 400, y: 330 }),
552
+ sdk: "{{agentSdk}}",
553
+ timeoutMs: "{{timeoutMs}}",
554
+ }, { x: 400, y: 330 }),
545
555
 
546
- node("implement", "action.run_agent", "Implement Feature", {
547
- prompt: `# Implement Feature
556
+ node("implement", "action.run_agent", "Implement Feature", {
557
+ prompt: `# Implement Feature
548
558
 
549
559
  The tests have been written. Now implement the feature to make them pass:
550
560
  1. Follow existing code conventions
@@ -554,79 +564,60 @@ The tests have been written. Now implement the feature to make them pass:
554
564
 
555
565
  Run \`{{testCommand}}\` after implementation.
556
566
  Commit with message "feat: implement [feature]"`,
557
- sdk: "{{agentSdk}}",
558
- timeoutMs: "{{timeoutMs}}",
559
- }, { x: 400, y: 490 }),
560
-
561
- node("build", "validation.build", "Build Check", {
562
- command: "{{buildCommand}}",
563
- zeroWarnings: true,
564
- }, { x: 400, y: 650 }),
565
-
566
- node("test-final", "validation.tests", "Final Test Run", {
567
- command: "{{testCommand}}",
568
- }, { x: 400, y: 780 }),
569
-
570
- node("lint", "validation.lint", "Lint Check", {
571
- command: "{{lintCommand}}",
572
- }, { x: 400, y: 910 }),
573
-
574
- node("all-passed", "condition.expression", "All Checks Passed?", {
575
- expression: "$ctx.getNodeOutput('build')?.passed === true && $ctx.getNodeOutput('test-final')?.passed === true && $ctx.getNodeOutput('lint')?.passed === true",
576
- }, { x: 400, y: 1040, outputs: ["yes", "no"] }),
577
-
578
- node("push-branch", "action.push_branch", "Push Branch", {
579
- worktreePath: "{{worktreePath}}",
580
- branch: "{{branch}}",
581
- baseBranch: "{{baseBranch}}",
582
- rebaseBeforePush: true,
583
- emptyDiffGuard: true,
584
- protectedBranches: "{{protectedBranches}}",
585
- }, { x: 250, y: 1110 }),
586
-
587
- node("push-ok", "condition.expression", "Push OK?", {
588
- expression: "$ctx.getNodeOutput('push-branch')?.pushed === true",
589
- }, { x: 250, y: 1175, outputs: ["yes", "no"] }),
590
-
591
- node("create-pr", "action.create_pr", "Handoff PR Lifecycle", {
592
- title: "feat: {{taskTitle}}",
593
- body: "Implements backend task with test-first methodology.\n\n**Plan:**\n{{plan}}\n\nAll tests passing. Bosun lifecycle handoff ready.",
594
- branch: "{{branch}}",
595
- baseBranch: "{{baseBranch}}",
596
- failOnError: true,
597
- maxRetries: 3,
598
- retryDelayMs: 15000,
599
- continueOnError: true,
600
- }, { x: 250, y: 1170 }),
601
-
602
- node("pr-created", "condition.expression", "Handoff Recorded?", {
603
- expression: "Boolean($ctx.getNodeOutput('create-pr')?.prNumber || $ctx.getNodeOutput('create-pr')?.prUrl)",
604
- }, { x: 250, y: 1240, outputs: ["yes", "no"] }),
605
-
606
- node("set-inreview", "action.update_task_status", "Set In-Review", {
607
- taskId: "{{taskId}}",
608
- status: "inreview",
609
- taskTitle: "{{taskTitle}}",
610
- }, { x: 180, y: 1320 }),
611
-
612
- node("notify-done", "notify.log", "Task Complete", {
613
- message: "Task completion agent finished task — PR lifecycle handoff recorded",
614
- level: "info",
615
- }, { x: 180, y: 1320 }),
616
-
617
- node("notify-pr-failed", "notify.telegram", "Escalate Lifecycle Handoff Failure", {
618
- message: ":alert: Task completion agent passed validation for {{taskTitle}} but failed to record Bosun PR lifecycle handoff after retries. Manual follow-up required.",
619
- }, { x: 420, y: 1320 }),
620
-
621
- node("set-validation-summary", "action.set_variable", "Summarize Validation Output", {
622
- key: "validationSummary",
623
- value:
624
- "(() => { const implement = $ctx.getNodeOutput('implement') || {}; const build = $ctx.getNodeOutput('build') || {}; const test = $ctx.getNodeOutput('test-final') || {}; const lint = $ctx.getNodeOutput('lint') || {}; return ['- implement.success: ' + (implement.success === true), '- build.passed: ' + (build.passed === true), '- test-final.passed: ' + (test.passed === true), '- lint.passed: ' + (lint.passed === true), '', 'Build output:', String(build.output || '').slice(0, 6000), '', 'Test output:', String(test.output || '').slice(0, 6000), '', 'Lint output:', String(lint.output || '').slice(0, 6000)].join('\\n'); })()",
625
- isExpression: true,
626
- }, { x: 620, y: 1090 }),
627
-
628
- node("auto-fix", "action.run_agent", "Auto-Fix Validation Failures", {
629
- prompt: `# Fix Validation Failures
567
+ sdk: "{{agentSdk}}",
568
+ timeoutMs: "{{timeoutMs}}",
569
+ }, { x: 400, y: 490 }),
570
+
571
+ // ── Main Validation Gate (build test lint) via sub-workflow ────
572
+ ...mainValidation.nodes,
573
+
574
+ node("all-passed", "condition.expression", "All Checks Passed?", {
575
+ expression: `$ctx.getNodeOutput('main-build')?.passed === true && $ctx.getNodeOutput('main-test')?.passed === true && $ctx.getNodeOutput('main-lint')?.passed === true`,
576
+ }, { x: 400, y: 1040, outputs: ["yes", "no"] }),
577
+
578
+ // ── Main Push + PR path ────────────────────────────────────────────
579
+ node("push-branch", "action.push_branch", "Push Branch", {
580
+ worktreePath: "{{worktreePath}}",
581
+ branch: "{{branch}}",
582
+ baseBranch: "{{baseBranch}}",
583
+ rebaseBeforePush: true,
584
+ emptyDiffGuard: true,
585
+ protectedBranches: "{{protectedBranches}}",
586
+ }, { x: 250, y: 1110 }),
587
+
588
+ node("create-pr", "action.create_pr", "Handoff PR Lifecycle", {
589
+ title: "feat: {{taskTitle}}",
590
+ body: "Implements backend task with test-first methodology.\n\n**Plan:**\n{{plan}}\n\nAll tests passing. Bosun lifecycle handoff ready.",
591
+ branch: "{{branch}}",
592
+ baseBranch: "{{baseBranch}}",
593
+ failOnError: true,
594
+ maxRetries: 3,
595
+ retryDelayMs: 15000,
596
+ continueOnError: true,
597
+ }, { x: 250, y: 1170 }),
598
+
599
+ // ── Main PR handoff (pr-ok? set-inreview dispatch) via sub-wf ─
600
+ ...mainPrHandoff.nodes,
601
+
602
+ node("notify-done", "notify.log", "Task Complete", {
603
+ message: "Task completion agent finished task PR lifecycle handoff recorded",
604
+ level: "info",
605
+ }, { x: 180, y: 1320 }),
606
+
607
+ node("notify-pr-failed", "notify.telegram", "Escalate Lifecycle Handoff Failure", {
608
+ message: ":alert: Task completion agent passed validation for {{taskTitle}} but failed to record Bosun PR lifecycle handoff after retries. Manual follow-up required.",
609
+ }, { x: 420, y: 1320 }),
610
+
611
+ // ── Retry path (validation failed → auto-fix → re-validate) ───────
612
+ node("set-validation-summary", "action.set_variable", "Summarize Validation Output", {
613
+ key: "validationSummary",
614
+ value:
615
+ "(() => { const implement = $ctx.getNodeOutput('implement') || {}; const build = $ctx.getNodeOutput('main-build') || {}; const test = $ctx.getNodeOutput('main-test') || {}; const lint = $ctx.getNodeOutput('main-lint') || {}; return ['- implement.success: ' + (implement.success === true), '- build.passed: ' + (build.passed === true), '- test-final.passed: ' + (test.passed === true), '- lint.passed: ' + (lint.passed === true), '', 'Build output:', String(build.output || '').slice(0, 6000), '', 'Test output:', String(test.output || '').slice(0, 6000), '', 'Lint output:', String(lint.output || '').slice(0, 6000)].join('\\n'); })()",
616
+ isExpression: true,
617
+ }, { x: 620, y: 1090 }),
618
+
619
+ node("auto-fix", "action.run_agent", "Auto-Fix Validation Failures", {
620
+ prompt: `# Fix Validation Failures
630
621
 
631
622
  The first validation pass failed for task **{{taskTitle}}**.
632
623
 
@@ -642,123 +633,105 @@ Keep the original task scope.
642
633
 
643
634
  Run build + tests + lint locally before finishing.
644
635
  Commit with message "fix: address validation failures"`,
645
- sdk: "{{agentSdk}}",
646
- timeoutMs: "{{autoFixTimeoutMs}}",
647
- }, { x: 620, y: 1170 }),
648
-
649
- node("build-retry", "validation.build", "Build Check (Retry)", {
650
- command: "{{buildCommand}}",
651
- zeroWarnings: true,
652
- }, { x: 620, y: 1300 }),
653
-
654
- node("test-retry", "validation.tests", "Final Test Run (Retry)", {
655
- command: "{{testCommand}}",
656
- }, { x: 620, y: 1430 }),
657
-
658
- node("lint-retry", "validation.lint", "Lint Check (Retry)", {
659
- command: "{{lintCommand}}",
660
- }, { x: 620, y: 1560 }),
661
-
662
- node("retry-passed", "condition.expression", "Retry Checks Passed?", {
663
- expression: "$ctx.getNodeOutput('build-retry')?.passed === true && $ctx.getNodeOutput('test-retry')?.passed === true && $ctx.getNodeOutput('lint-retry')?.passed === true",
664
- }, { x: 620, y: 1690, outputs: ["yes", "no"] }),
665
-
666
- node("push-branch-retry", "action.push_branch", "Push Branch (Retry)", {
667
- worktreePath: "{{worktreePath}}",
668
- branch: "{{branch}}",
669
- baseBranch: "{{baseBranch}}",
670
- rebaseBeforePush: true,
671
- emptyDiffGuard: true,
672
- protectedBranches: "{{protectedBranches}}",
673
- }, { x: 450, y: 1760 }),
674
-
675
- node("push-ok-retry", "condition.expression", "Push OK? (Retry)", {
676
- expression: "$ctx.getNodeOutput('push-branch-retry')?.pushed === true",
677
- }, { x: 450, y: 1825, outputs: ["yes", "no"] }),
678
-
679
- node("create-pr-retry", "action.create_pr", "Handoff PR Lifecycle (After Retry)", {
680
- title: "feat: {{taskTitle}}",
681
- body: "Implements backend task after auto-fix retry.\n\n**Plan:**\n{{plan}}\n\nValidation passed after remediation. Bosun lifecycle handoff ready.",
682
- branch: "{{branch}}",
683
- baseBranch: "{{baseBranch}}",
684
- failOnError: true,
685
- maxRetries: 3,
686
- retryDelayMs: 15000,
687
- continueOnError: true,
688
- }, { x: 450, y: 1820 }),
689
-
690
- node("pr-created-retry", "condition.expression", "Handoff Recorded (Retry Path)?", {
691
- expression: "Boolean($ctx.getNodeOutput('create-pr-retry')?.prNumber || $ctx.getNodeOutput('create-pr-retry')?.prUrl)",
692
- }, { x: 450, y: 1890, outputs: ["yes", "no"] }),
693
-
694
- node("set-inreview-retry", "action.update_task_status", "Set In-Review (Retry)", {
695
- taskId: "{{taskId}}",
696
- status: "inreview",
697
- taskTitle: "{{taskTitle}}",
698
- }, { x: 360, y: 1980 }),
699
-
700
- node("notify-done-retry", "notify.log", "Task Complete (After Retry)", {
701
- message: "Task completion agent finished task after retry — PR lifecycle handoff recorded",
702
- level: "info",
703
- }, { x: 360, y: 1980 }),
704
-
705
- node("notify-fail", "notify.telegram", "Checks Failed", {
706
- message: ":alert: Task completion agent: validation failed for task {{taskTitle}} even after remediation pass. Manual review needed.",
707
- }, { x: 820, y: 1820 }),
708
-
709
- node("notify-pr-failed-retry", "notify.telegram", "Escalate Lifecycle Failure (Retry Path)", {
710
- message: ":alert: Task completion agent remediation passed for {{taskTitle}} but Bosun PR lifecycle handoff failed after retries. Manual follow-up required.",
711
- }, { x: 620, y: 1980 }),
712
- ],
713
- edges: [
714
- edge("trigger", "plan-work"),
715
- edge("plan-work", "write-tests"),
716
- edge("write-tests", "implement"),
717
- edge("implement", "build"),
718
- edge("build", "test-final"),
719
- edge("test-final", "lint"),
720
- edge("lint", "all-passed"),
721
- edge("all-passed", "push-branch", { condition: "$output?.result === true", port: "yes" }),
722
- edge("all-passed", "set-validation-summary", { condition: "$output?.result !== true", port: "no" }),
723
- edge("set-validation-summary", "auto-fix"),
724
- edge("push-branch", "push-ok"),
725
- edge("push-ok", "create-pr", { condition: "$output?.result === true", port: "yes" }),
726
- edge("push-ok", "notify-pr-failed", { condition: "$output?.result !== true", port: "no" }),
727
- edge("create-pr", "pr-created"),
728
- edge("pr-created", "set-inreview", { condition: "$output?.result === true", port: "yes" }),
729
- edge("set-inreview", "notify-done"),
730
- edge("pr-created", "notify-pr-failed", { condition: "$output?.result !== true", port: "no" }),
731
- edge("auto-fix", "build-retry"),
732
- edge("build-retry", "test-retry"),
733
- edge("test-retry", "lint-retry"),
734
- edge("lint-retry", "retry-passed"),
735
- edge("retry-passed", "push-branch-retry", { condition: "$output?.result === true", port: "yes" }),
736
- edge("retry-passed", "notify-fail", { condition: "$output?.result !== true", port: "no" }),
737
- edge("push-branch-retry", "push-ok-retry"),
738
- edge("push-ok-retry", "create-pr-retry", { condition: "$output?.result === true", port: "yes" }),
739
- edge("push-ok-retry", "notify-pr-failed-retry", { condition: "$output?.result !== true", port: "no" }),
740
- edge("create-pr-retry", "pr-created-retry"),
741
- edge("pr-created-retry", "set-inreview-retry", { condition: "$output?.result === true", port: "yes" }),
742
- edge("set-inreview-retry", "notify-done-retry"),
743
- edge("pr-created-retry", "notify-pr-failed-retry", { condition: "$output?.result !== true", port: "no" }),
744
- ],
745
- metadata: {
746
- author: "bosun",
747
- version: 2,
748
- createdAt: "2025-02-25T00:00:00Z",
749
- templateVersion: "2.0.0",
750
- tags: ["agent", "task-completion", "test-first", "tdd", "multi-language"],
751
- replaces: {
752
- module: "primary-agent.mjs",
753
- functions: ["runAgentWithTask"],
754
- calledFrom: ["task-executor.mjs:executeTask"],
755
- description:
756
- "Replaces generic agent task execution with a structured " +
757
- "workflow. Test-first methodology, build/lint gates, and Bosun-managed PR lifecycle handoff " +
758
- "are enforced as distinct workflow stages. Works with any language/framework.",
636
+ sdk: "{{agentSdk}}",
637
+ timeoutMs: "{{autoFixTimeoutMs}}",
638
+ }, { x: 620, y: 1170 }),
639
+
640
+ // ── Retry Validation Gate (build test lint) via sub-workflow ───
641
+ ...retryValidation.nodes,
642
+
643
+ node("retry-passed", "condition.expression", "Retry Checks Passed?", {
644
+ expression: `$ctx.getNodeOutput('retry-build')?.passed === true && $ctx.getNodeOutput('retry-test')?.passed === true && $ctx.getNodeOutput('retry-lint')?.passed === true`,
645
+ }, { x: 620, y: 1690, outputs: ["yes", "no"] }),
646
+
647
+ // ── Retry Push + PR path ───────────────────────────────────────────
648
+ node("push-branch-retry", "action.push_branch", "Push Branch (Retry)", {
649
+ worktreePath: "{{worktreePath}}",
650
+ branch: "{{branch}}",
651
+ baseBranch: "{{baseBranch}}",
652
+ rebaseBeforePush: true,
653
+ emptyDiffGuard: true,
654
+ protectedBranches: "{{protectedBranches}}",
655
+ }, { x: 450, y: 1760 }),
656
+
657
+ node("create-pr-retry", "action.create_pr", "Handoff PR Lifecycle (After Retry)", {
658
+ title: "feat: {{taskTitle}}",
659
+ body: "Implements backend task after auto-fix retry.\n\n**Plan:**\n{{plan}}\n\nValidation passed after remediation. Bosun lifecycle handoff ready.",
660
+ branch: "{{branch}}",
661
+ baseBranch: "{{baseBranch}}",
662
+ failOnError: true,
663
+ maxRetries: 3,
664
+ retryDelayMs: 15000,
665
+ continueOnError: true,
666
+ }, { x: 450, y: 1820 }),
667
+
668
+ // ── Retry PR handoff via sub-workflow ──────────────────────────────
669
+ ...retryPrHandoff.nodes,
670
+
671
+ node("notify-done-retry", "notify.log", "Task Complete (After Retry)", {
672
+ message: "Task completion agent finished task after retry PR lifecycle handoff recorded",
673
+ level: "info",
674
+ }, { x: 360, y: 1980 }),
675
+
676
+ node("notify-fail", "notify.telegram", "Checks Failed", {
677
+ message: ":alert: Task completion agent: validation failed for task {{taskTitle}} even after remediation pass. Manual review needed.",
678
+ }, { x: 820, y: 1820 }),
679
+
680
+ node("notify-pr-failed-retry", "notify.telegram", "Escalate Lifecycle Failure (Retry Path)", {
681
+ message: ":alert: Task completion agent remediation passed for {{taskTitle}} but Bosun PR lifecycle handoff failed after retries. Manual follow-up required.",
682
+ }, { x: 620, y: 1980 }),
683
+ ],
684
+ edges: [
685
+ edge("trigger", "plan-work"),
686
+ edge("plan-work", "write-tests"),
687
+ edge("write-tests", "implement"),
688
+
689
+ // implement main validation gate
690
+ wire("implement", mainValidation.entryNodeId),
691
+ ...mainValidation.edges,
692
+ wire(mainValidation.exitNodeId, "all-passed"),
693
+
694
+ // Main pass push PR → handoff
695
+ edge("all-passed", "push-branch", { condition: "$output?.result === true", port: "yes" }),
696
+ edge("all-passed", "set-validation-summary", { condition: "$output?.result !== true", port: "no" }),
697
+ edge("push-branch", "create-pr"),
698
+ wire("create-pr", mainPrHandoff.entryNodeId),
699
+ ...mainPrHandoff.edges,
700
+ wire(mainPrHandoff.exitNodeId, "notify-done"),
701
+ edge(mainPrHandoff.entryNodeId, "notify-pr-failed", { condition: "$output?.result !== true", port: "no" }),
702
+
703
+ // Retry path: auto-fix → retry validation → push → PR → handoff
704
+ edge("set-validation-summary", "auto-fix"),
705
+ wire("auto-fix", retryValidation.entryNodeId),
706
+ ...retryValidation.edges,
707
+ wire(retryValidation.exitNodeId, "retry-passed"),
708
+
709
+ edge("retry-passed", "push-branch-retry", { condition: "$output?.result === true", port: "yes" }),
710
+ edge("retry-passed", "notify-fail", { condition: "$output?.result !== true", port: "no" }),
711
+ edge("push-branch-retry", "create-pr-retry"),
712
+ wire("create-pr-retry", retryPrHandoff.entryNodeId),
713
+ ...retryPrHandoff.edges,
714
+ wire(retryPrHandoff.exitNodeId, "notify-done-retry"),
715
+ edge(retryPrHandoff.entryNodeId, "notify-pr-failed-retry", { condition: "$output?.result !== true", port: "no" }),
716
+ ],
717
+ metadata: {
718
+ author: "bosun",
719
+ version: 2,
720
+ createdAt: "2025-02-25T00:00:00Z",
721
+ templateVersion: "2.0.0",
722
+ tags: ["agent", "task-completion", "test-first", "tdd", "multi-language"],
723
+ replaces: {
724
+ module: "primary-agent.mjs",
725
+ functions: ["runAgentWithTask"],
726
+ calledFrom: ["task-executor.mjs:executeTask"],
727
+ description:
728
+ "Replaces generic agent task execution with a structured " +
729
+ "workflow. Test-first methodology, build/lint gates, and Bosun-managed PR lifecycle handoff " +
730
+ "are enforced as distinct workflow stages. Works with any language/framework.",
731
+ },
759
732
  },
760
- },
761
- };
733
+ };
734
+ })();
762
735
 
763
736
  // ═══════════════════════════════════════════════════════════════════════════
764
737
  // Voice + Video Rollout (Parallel Lanes)