@backburner/cli 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 (202) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +329 -0
  3. package/dist/src/agents/antigravity-launch.js +162 -0
  4. package/dist/src/agents/antigravity.js +251 -0
  5. package/dist/src/agents/claude-launch.js +117 -0
  6. package/dist/src/agents/claude.js +271 -0
  7. package/dist/src/agents/codex-launch.js +82 -0
  8. package/dist/src/agents/codex.js +261 -0
  9. package/dist/src/agents/composite-runner.js +13 -0
  10. package/dist/src/agents/extraction.js +442 -0
  11. package/dist/src/agents/failure-feedback.js +132 -0
  12. package/dist/src/agents/gemini-launch.js +96 -0
  13. package/dist/src/agents/gemini.js +392 -0
  14. package/dist/src/agents/job-agent-assignments.js +180 -0
  15. package/dist/src/agents/lifecycle-feedback.js +147 -0
  16. package/dist/src/agents/limit-detector.js +83 -0
  17. package/dist/src/agents/opencode-launch.js +138 -0
  18. package/dist/src/agents/opencode.js +156 -0
  19. package/dist/src/agents/plan-breakdown-feedback.js +97 -0
  20. package/dist/src/agents/pool.js +168 -0
  21. package/dist/src/agents/prompt.js +462 -0
  22. package/dist/src/agents/provider-sessions.js +105 -0
  23. package/dist/src/agents/review-feedback.js +255 -0
  24. package/dist/src/agents/selection.js +113 -0
  25. package/dist/src/agents/service.js +1087 -0
  26. package/dist/src/agents/types.js +1 -0
  27. package/dist/src/agents/wave-assessment-feedback.js +69 -0
  28. package/dist/src/attention/derive.js +337 -0
  29. package/dist/src/capabilities/index.js +2 -0
  30. package/dist/src/capabilities/projection.js +183 -0
  31. package/dist/src/capabilities/types.js +1 -0
  32. package/dist/src/capabilities/validator.js +87 -0
  33. package/dist/src/cli/commands/broker-smoke.js +96 -0
  34. package/dist/src/cli/commands/broker.js +56 -0
  35. package/dist/src/cli/commands/init.js +4 -0
  36. package/dist/src/cli/commands/journal.js +43 -0
  37. package/dist/src/cli/commands/run-loop.js +56 -0
  38. package/dist/src/cli/commands/run.js +1241 -0
  39. package/dist/src/cli/commands/tui.js +187 -0
  40. package/dist/src/cli/dispatcher.js +51 -0
  41. package/dist/src/cli/errors.js +2 -0
  42. package/dist/src/cli/init.js +92 -0
  43. package/dist/src/cli/options.js +86 -0
  44. package/dist/src/cli/output.js +231 -0
  45. package/dist/src/cli/run-journal.js +342 -0
  46. package/dist/src/cli/run-types.js +1 -0
  47. package/dist/src/cli/run.js +32 -0
  48. package/dist/src/cli/runtime/dispatch-lifecycle.js +772 -0
  49. package/dist/src/cli/runtime/provider-runtime-types.js +1 -0
  50. package/dist/src/cli/runtime/provider-runtime.js +152 -0
  51. package/dist/src/cli/runtime/provider-tools.js +73 -0
  52. package/dist/src/cli/runtime/providers/antigravity.js +26 -0
  53. package/dist/src/cli/runtime/providers/claude.js +20 -0
  54. package/dist/src/cli/runtime/providers/codex.js +20 -0
  55. package/dist/src/cli/runtime/providers/gemini.js +20 -0
  56. package/dist/src/cli/runtime/providers/opencode.js +20 -0
  57. package/dist/src/cli/runtime/run-context.js +131 -0
  58. package/dist/src/cli/tui/index.js +3 -0
  59. package/dist/src/cli/tui/screen.js +388 -0
  60. package/dist/src/cli/tui/tracker.js +558 -0
  61. package/dist/src/cli/tui/types.js +1 -0
  62. package/dist/src/cli/tui/views.js +1006 -0
  63. package/dist/src/config/loader.js +136 -0
  64. package/dist/src/config/schemas.js +403 -0
  65. package/dist/src/config/types.js +1 -0
  66. package/dist/src/context/types.js +331 -0
  67. package/dist/src/domain/entities.js +1 -0
  68. package/dist/src/git/backburner-git-tool-service.js +680 -0
  69. package/dist/src/git/gitops-lifecycle.js +56 -0
  70. package/dist/src/git/merge-prep-workspace.js +357 -0
  71. package/dist/src/git/types.js +484 -0
  72. package/dist/src/github/broker-model-content.js +215 -0
  73. package/dist/src/github/broker-server.js +237 -0
  74. package/dist/src/github/broker.js +638 -0
  75. package/dist/src/github/composed.js +53 -0
  76. package/dist/src/github/gateway.js +478 -0
  77. package/dist/src/github/normalize.js +222 -0
  78. package/dist/src/github/pr-classification.js +73 -0
  79. package/dist/src/github/scoped-broker.js +35 -0
  80. package/dist/src/github/security.js +126 -0
  81. package/dist/src/github/service.js +332 -0
  82. package/dist/src/github/types.js +1 -0
  83. package/dist/src/github/utils.js +16 -0
  84. package/dist/src/journal/index.js +4 -0
  85. package/dist/src/journal/reader.js +62 -0
  86. package/dist/src/journal/renderer.js +50 -0
  87. package/dist/src/journal/service.js +72 -0
  88. package/dist/src/journal/types.js +1 -0
  89. package/dist/src/journal/writer.js +19 -0
  90. package/dist/src/mcps/bridge.js +166 -0
  91. package/dist/src/mcps/index.js +5 -0
  92. package/dist/src/mcps/reconcile.js +113 -0
  93. package/dist/src/mcps/smithery-client.js +186 -0
  94. package/dist/src/mcps/smithery.js +164 -0
  95. package/dist/src/mcps/types.js +1 -0
  96. package/dist/src/memory/json-store.js +71 -0
  97. package/dist/src/memory/prompt.js +16 -0
  98. package/dist/src/memory/scope.js +31 -0
  99. package/dist/src/memory/store.js +1 -0
  100. package/dist/src/memory/tool-service.js +142 -0
  101. package/dist/src/memory/types.js +12 -0
  102. package/dist/src/onboarding/config-writer.js +96 -0
  103. package/dist/src/onboarding/engine.js +86 -0
  104. package/dist/src/onboarding/prompt.js +102 -0
  105. package/dist/src/onboarding/services/provider-discovery.js +58 -0
  106. package/dist/src/onboarding/services/repo-discovery.js +246 -0
  107. package/dist/src/onboarding/steps/check-github-auth.js +32 -0
  108. package/dist/src/onboarding/steps/check-requirements.js +34 -0
  109. package/dist/src/onboarding/steps/configure-models.js +105 -0
  110. package/dist/src/onboarding/steps/configure-paths.js +33 -0
  111. package/dist/src/onboarding/steps/discover-providers.js +38 -0
  112. package/dist/src/onboarding/steps/discover-repos.js +39 -0
  113. package/dist/src/onboarding/steps/generate-config.js +106 -0
  114. package/dist/src/onboarding/steps/readiness-validation.js +158 -0
  115. package/dist/src/onboarding/steps/select-repos.js +76 -0
  116. package/dist/src/onboarding/steps/show-summary.js +97 -0
  117. package/dist/src/onboarding/steps/welcome.js +27 -0
  118. package/dist/src/onboarding/types.js +1 -0
  119. package/dist/src/onboarding/validators/git-access.js +63 -0
  120. package/dist/src/onboarding/validators/github-auth.js +85 -0
  121. package/dist/src/onboarding/validators/tool-checker.js +153 -0
  122. package/dist/src/onboarding/validators/workspace.js +83 -0
  123. package/dist/src/prompts/resolver.js +106 -0
  124. package/dist/src/retrospectives/candidates.js +224 -0
  125. package/dist/src/retrospectives/derive.js +169 -0
  126. package/dist/src/retrospectives/ingest.js +321 -0
  127. package/dist/src/retrospectives/management-prs.js +380 -0
  128. package/dist/src/retrospectives/proposals.js +199 -0
  129. package/dist/src/retrospectives/scopes.js +272 -0
  130. package/dist/src/retrospectives/scoring.js +171 -0
  131. package/dist/src/retrospectives/types.js +1 -0
  132. package/dist/src/state/loader.js +473 -0
  133. package/dist/src/state/types.js +15 -0
  134. package/dist/src/tasks/derivation/builders.js +70 -0
  135. package/dist/src/tasks/derivation/handlers/comment-response.js +169 -0
  136. package/dist/src/tasks/derivation/handlers/implement-plan.js +103 -0
  137. package/dist/src/tasks/derivation/handlers/index.js +54 -0
  138. package/dist/src/tasks/derivation/handlers/packet-pr-control.js +92 -0
  139. package/dist/src/tasks/derivation/handlers/packet-worktree.js +29 -0
  140. package/dist/src/tasks/derivation/handlers/plan-breakdown.js +45 -0
  141. package/dist/src/tasks/derivation/handlers/prepare-for-merge.js +91 -0
  142. package/dist/src/tasks/derivation/handlers/product-discovery.js +36 -0
  143. package/dist/src/tasks/derivation/handlers/retry-failed-task.js +125 -0
  144. package/dist/src/tasks/derivation/handlers/review-pr.js +28 -0
  145. package/dist/src/tasks/derivation/handlers/shared.js +24 -0
  146. package/dist/src/tasks/derivation/handlers/sync-parent-branch.js +100 -0
  147. package/dist/src/tasks/derivation/handlers/wave-assessment.js +223 -0
  148. package/dist/src/tasks/derivation/handlers/write-plan.js +101 -0
  149. package/dist/src/tasks/derivation/handlers/write-product-spec.js +49 -0
  150. package/dist/src/tasks/derivation/runner.js +18 -0
  151. package/dist/src/tasks/derivation/types.js +1 -0
  152. package/dist/src/tasks/derive.js +810 -0
  153. package/dist/src/tasks/mcp-skip.js +10 -0
  154. package/dist/src/tasks/retry.js +10 -0
  155. package/dist/src/tasks/task.js +184 -0
  156. package/dist/src/tasks/types.js +1 -0
  157. package/dist/src/utils/command-runner.js +192 -0
  158. package/dist/src/utils/json.js +38 -0
  159. package/dist/src/utils/logger.js +43 -0
  160. package/dist/src/utils/paths.js +38 -0
  161. package/dist/src/utils/slug.js +10 -0
  162. package/dist/src/workflows/registry.js +174 -0
  163. package/dist/src/workflows/triage.js +90 -0
  164. package/dist/src/workflows/workstream-machine/derive-events.js +121 -0
  165. package/dist/src/workflows/workstream-machine/evaluate.js +24 -0
  166. package/dist/src/workflows/workstream-machine/events.js +1 -0
  167. package/dist/src/workflows/workstream-machine/machine.js +355 -0
  168. package/dist/src/workflows/workstream-machine/selectors.js +19 -0
  169. package/dist/src/workflows/workstream-machine/types.js +1 -0
  170. package/dist/src/workstreams/branch-sync.js +87 -0
  171. package/dist/src/workstreams/derive.js +629 -0
  172. package/dist/src/workstreams/mcp-blockers.js +420 -0
  173. package/dist/src/workstreams/packet-candidates.js +42 -0
  174. package/dist/src/workstreams/packet-lifecycle-feedback.js +60 -0
  175. package/dist/src/workstreams/packet-lifecycle-report.js +199 -0
  176. package/dist/src/workstreams/packet-plan.js +597 -0
  177. package/dist/src/workstreams/packet-projections.js +190 -0
  178. package/dist/src/workstreams/parent-branch-sync-action.js +312 -0
  179. package/dist/src/workstreams/parent-branch-sync.js +148 -0
  180. package/dist/src/workstreams/phases.js +4 -0
  181. package/dist/src/workstreams/plan-breakdown-generator/export.js +19 -0
  182. package/dist/src/workstreams/plan-breakdown-generator/index.js +6 -0
  183. package/dist/src/workstreams/plan-breakdown-generator/inspect.js +50 -0
  184. package/dist/src/workstreams/plan-breakdown-generator/patch.js +139 -0
  185. package/dist/src/workstreams/plan-breakdown-generator/session.js +19 -0
  186. package/dist/src/workstreams/plan-breakdown-generator/store.js +27 -0
  187. package/dist/src/workstreams/plan-breakdown-generator/tools.js +407 -0
  188. package/dist/src/workstreams/plan-breakdown-generator/types.js +6 -0
  189. package/dist/src/workstreams/plan-breakdown-generator/validate.js +212 -0
  190. package/dist/src/workstreams/plan-breakdown-schema.js +175 -0
  191. package/dist/src/workstreams/utils.js +15 -0
  192. package/dist/src/workstreams/wave-assessment/checkpoint.js +172 -0
  193. package/dist/src/workstreams/wave-assessment/index.js +3 -0
  194. package/dist/src/workstreams/wave-assessment/session.js +84 -0
  195. package/dist/src/workstreams/wave-assessment/store.js +51 -0
  196. package/dist/src/workstreams/wave-assessment/tools.js +419 -0
  197. package/dist/src/workstreams/wave-assessment/types.js +1 -0
  198. package/dist/src/workstreams/wave-assessment/validate.js +78 -0
  199. package/dist/src/worktrees/service.js +839 -0
  200. package/dist/src/worktrees/workspace-ownership.js +34 -0
  201. package/dist/src/worktrees/workspace-recovery.js +177 -0
  202. package/package.json +61 -0
@@ -0,0 +1,174 @@
1
+ const READ_ONLY_HANDOFF_POLICY = {
2
+ expectsPushableHandoff: false,
3
+ orchestratorOwnsPush: true,
4
+ remotePublishOwner: "orchestrator"
5
+ };
6
+ const WORKSPACE_WRITE_HANDOFF_POLICY = {
7
+ expectsPushableHandoff: true,
8
+ orchestratorOwnsPush: true,
9
+ remotePublishOwner: "orchestrator"
10
+ };
11
+ const WORKFLOW_TASK_DEFINITIONS = [
12
+ {
13
+ kind: "product_discovery",
14
+ role: "product",
15
+ modelClass: "strong",
16
+ executionPermission: "read-only",
17
+ handoffPolicy: READ_ONLY_HANDOFF_POLICY,
18
+ sourceKinds: ["issue"]
19
+ },
20
+ {
21
+ kind: "write_product_spec",
22
+ role: "product",
23
+ modelClass: "strong",
24
+ executionPermission: "workspace-write",
25
+ handoffPolicy: WORKSPACE_WRITE_HANDOFF_POLICY,
26
+ sourceKinds: ["issue"]
27
+ },
28
+ {
29
+ kind: "respond_issue_comment",
30
+ role: "discussion",
31
+ modelClass: "strong",
32
+ executionPermission: "read-only",
33
+ handoffPolicy: READ_ONLY_HANDOFF_POLICY,
34
+ sourceKinds: ["comment"]
35
+ },
36
+ {
37
+ kind: "review_pr",
38
+ role: "review",
39
+ modelClass: "strong",
40
+ executionPermission: "read-only",
41
+ handoffPolicy: READ_ONLY_HANDOFF_POLICY,
42
+ sourceKinds: ["pull_request"]
43
+ },
44
+ {
45
+ kind: "respond_pr_comment",
46
+ role: "discussion",
47
+ modelClass: "cheap",
48
+ executionPermission: "workspace-write",
49
+ handoffPolicy: WORKSPACE_WRITE_HANDOFF_POLICY,
50
+ sourceKinds: ["comment"]
51
+ },
52
+ {
53
+ kind: "write_plan",
54
+ role: "planning",
55
+ modelClass: "strong",
56
+ executionPermission: "workspace-write",
57
+ handoffPolicy: WORKSPACE_WRITE_HANDOFF_POLICY,
58
+ sourceKinds: ["issue"]
59
+ },
60
+ {
61
+ kind: "implement_plan",
62
+ role: "implementation",
63
+ modelClass: "cheap",
64
+ executionPermission: "workspace-write",
65
+ handoffPolicy: WORKSPACE_WRITE_HANDOFF_POLICY,
66
+ sourceKinds: ["pull_request"]
67
+ },
68
+ {
69
+ kind: "plan_breakdown",
70
+ role: "implementation_manager",
71
+ modelClass: "strong",
72
+ executionPermission: "read-only",
73
+ handoffPolicy: READ_ONLY_HANDOFF_POLICY,
74
+ sourceKinds: ["issue", "pull_request"]
75
+ },
76
+ {
77
+ kind: "wave_plan_assessment",
78
+ role: "planning",
79
+ modelClass: "strong",
80
+ executionPermission: "read-only",
81
+ handoffPolicy: READ_ONLY_HANDOFF_POLICY,
82
+ sourceKinds: ["pull_request"]
83
+ },
84
+ {
85
+ kind: "wave_plan_replanning",
86
+ role: "implementation_manager",
87
+ modelClass: "strong",
88
+ executionPermission: "workspace-write",
89
+ handoffPolicy: WORKSPACE_WRITE_HANDOFF_POLICY,
90
+ sourceKinds: ["pull_request"]
91
+ },
92
+ {
93
+ kind: "sync_parent_branch_preflight",
94
+ role: "implementation_manager",
95
+ modelClass: "strong",
96
+ executionPermission: "read-only",
97
+ handoffPolicy: READ_ONLY_HANDOFF_POLICY,
98
+ sourceKinds: ["issue"]
99
+ },
100
+ {
101
+ kind: "sync_parent_branch",
102
+ role: "implementation_manager",
103
+ modelClass: "strong",
104
+ executionPermission: "workspace-write",
105
+ handoffPolicy: WORKSPACE_WRITE_HANDOFF_POLICY,
106
+ sourceKinds: ["pull_request"]
107
+ },
108
+ {
109
+ kind: "prepare_pr_for_merge",
110
+ role: "implementation_manager",
111
+ modelClass: "strong",
112
+ executionPermission: "workspace-write",
113
+ handoffPolicy: WORKSPACE_WRITE_HANDOFF_POLICY,
114
+ sourceKinds: ["pull_request"]
115
+ },
116
+ {
117
+ kind: "extract_retrospective_problems",
118
+ role: "retrospective",
119
+ modelClass: "strong",
120
+ executionPermission: "read-only",
121
+ handoffPolicy: READ_ONLY_HANDOFF_POLICY,
122
+ sourceKinds: ["retrospective_candidate"]
123
+ },
124
+ {
125
+ kind: "investigate_learning_opportunity",
126
+ role: "retrospective",
127
+ modelClass: "strong",
128
+ executionPermission: "read-only",
129
+ handoffPolicy: READ_ONLY_HANDOFF_POLICY,
130
+ sourceKinds: ["retrospective_problem"]
131
+ },
132
+ {
133
+ kind: "draft_management_learning_proposal",
134
+ role: "retrospective",
135
+ modelClass: "strong",
136
+ executionPermission: "read-only",
137
+ handoffPolicy: READ_ONLY_HANDOFF_POLICY,
138
+ sourceKinds: ["learning_opportunity"]
139
+ }
140
+ ];
141
+ export class WorkflowRegistry {
142
+ definitions;
143
+ constructor(definitions = WORKFLOW_TASK_DEFINITIONS) {
144
+ this.definitions = new Map(definitions.map((def) => [def.kind, def]));
145
+ }
146
+ getDefinition(kind) {
147
+ const definition = this.definitions.get(kind);
148
+ if (!definition) {
149
+ throw new Error(`No workflow task definition registered for task kind "${kind}"`);
150
+ }
151
+ return definition;
152
+ }
153
+ resolveTaskExecutionPolicy(kind) {
154
+ const definition = this.getDefinition(kind);
155
+ return {
156
+ executionPermission: definition.executionPermission,
157
+ handoffPolicy: definition.handoffPolicy
158
+ };
159
+ }
160
+ get definitionsList() {
161
+ return Array.from(this.definitions.values());
162
+ }
163
+ }
164
+ export const workflowRegistry = new WorkflowRegistry();
165
+ export function getWorkflowTaskDefinition(kind) {
166
+ return workflowRegistry.getDefinition(kind);
167
+ }
168
+ /**
169
+ * Returns the list of capability names required by the given task kind.
170
+ * Returns an empty array when the kind has no required capabilities.
171
+ */
172
+ export function getRequiredCapabilityNames(kind) {
173
+ return workflowRegistry.getDefinition(kind).requiredCapabilityNames ?? [];
174
+ }
@@ -0,0 +1,90 @@
1
+ import { buildWorktreeProductSpecTaskRecord, ImplementationWorkflowService } from "../worktrees/service.js";
2
+ export class TriageFollowUpService {
3
+ implementationWorkflow;
4
+ constructor(gitGateway, gitHubGateway, now = () => new Date().toISOString()) {
5
+ this.implementationWorkflow = new ImplementationWorkflowService(gitGateway, gitHubGateway, now);
6
+ }
7
+ async materializeStartImplementation(input) {
8
+ const reposById = new Map(input.repos.map((repo) => [repo.id, repo]));
9
+ const tasksById = new Map(input.tasks.map((task) => [task.id, task]));
10
+ const worktrees = [...this.implementationWorkflow.linkPullRequests(input.state.worktrees, input.state.prs, input.repos)];
11
+ for (const issue of input.issues) {
12
+ const repo = reposById.get(issue.repoId);
13
+ if (!repo || !issue.labels.includes(repo.labels.agentProductApproved)) {
14
+ continue;
15
+ }
16
+ const repoPath = input.repoPaths.get(issue.repoId);
17
+ if (!repoPath) {
18
+ continue;
19
+ }
20
+ const alreadyHasSpecTask = [...tasksById.values()].some((task) => task.kind === "write_product_spec" && task.sourceId === issue.id);
21
+ const existingWorktree = worktrees.find((worktree) => worktree.repoId === repo.id && worktree.issueNumber === issue.number);
22
+ if (existingWorktree &&
23
+ existingWorktree.status !== "closed" &&
24
+ existingWorktree.prNumber !== undefined &&
25
+ existingWorktree.prUrl !== undefined &&
26
+ (existingWorktree.productSpecWrittenAt || alreadyHasSpecTask)) {
27
+ continue;
28
+ }
29
+ const prepared = await this.implementationWorkflow.ensureWorktreeForIssue({
30
+ repo,
31
+ repoPath,
32
+ codeRoot: input.codeRoot,
33
+ issue,
34
+ decision: {
35
+ decision: "start_implementation",
36
+ summary: "Product direction approved; prepare Product Spec before engineering planning."
37
+ },
38
+ existingWorktrees: worktrees
39
+ });
40
+ const existingIndex = worktrees.findIndex((worktree) => worktree.id === prepared.worktree.id);
41
+ if (existingIndex === -1) {
42
+ worktrees.push(prepared.worktree);
43
+ }
44
+ else {
45
+ worktrees[existingIndex] = prepared.worktree;
46
+ }
47
+ let withPr;
48
+ try {
49
+ const result = await this.implementationWorkflow.ensurePullRequestForWorktree(repo, issue, prepared.worktree);
50
+ withPr = result.worktree;
51
+ }
52
+ catch {
53
+ continue;
54
+ }
55
+ const pushedIndex = worktrees.findIndex((worktree) => worktree.id === withPr.id);
56
+ if (pushedIndex === -1) {
57
+ worktrees.push(withPr);
58
+ }
59
+ else {
60
+ worktrees[pushedIndex] = withPr;
61
+ }
62
+ if (!withPr.productSpecWrittenAt && !alreadyHasSpecTask) {
63
+ const writeProductSpecTask = buildWorktreeProductSpecTaskRecord(withPr, issue, input.state.issueMetadata.generatedAt ?? withPr.updatedAt);
64
+ tasksById.set(writeProductSpecTask.id, writeProductSpecTask);
65
+ }
66
+ }
67
+ return {
68
+ tasks: [...tasksById.values()].sort(compareTasks),
69
+ worktrees: worktrees.sort(compareWorktrees)
70
+ };
71
+ }
72
+ }
73
+ function compareTasks(left, right) {
74
+ const repoComparison = left.repoSlug.localeCompare(right.repoSlug);
75
+ if (repoComparison !== 0) {
76
+ return repoComparison;
77
+ }
78
+ const kindComparison = left.kind.localeCompare(right.kind);
79
+ if (kindComparison !== 0) {
80
+ return kindComparison;
81
+ }
82
+ return left.id.localeCompare(right.id);
83
+ }
84
+ function compareWorktrees(left, right) {
85
+ const repoComparison = left.repoSlug.localeCompare(right.repoSlug);
86
+ if (repoComparison !== 0) {
87
+ return repoComparison;
88
+ }
89
+ return left.id.localeCompare(right.id);
90
+ }
@@ -0,0 +1,121 @@
1
+ import { hasFeedback, isAbandoned, isReviewRequested, isResolved } from "./selectors.js";
2
+ export function deriveWorkstreamMachineEvents(input) {
3
+ // Repo reconciliation has already resolved the workstream projection; this layer
4
+ // only converts those resolved facts into ordered phase-machine events.
5
+ const events = derivePrimaryPhaseEvents(input);
6
+ for (const rule of eventRules) {
7
+ events.push(...rule.derive(input, events));
8
+ }
9
+ return events;
10
+ }
11
+ const eventRules = [
12
+ {
13
+ derive: (input) => input.previousPhase === undefined ? [{ type: "WORKSTREAM_INITIALIZED" }] : []
14
+ },
15
+ {
16
+ derive: (input) => input.attentionReasons.includes("contextual_feedback_received")
17
+ ? [{ type: "contextualFeedbackReceived" }]
18
+ : []
19
+ },
20
+ {
21
+ derive: (_input, events) => hasPhaseSignal(events) ? [] : [{ type: "NO_PHASE_SIGNAL_OBSERVED" }]
22
+ }
23
+ ];
24
+ function derivePrimaryPhaseEvents(input) {
25
+ if (isResolved(input)) {
26
+ return [{ type: "WORKSTREAM_MERGED" }];
27
+ }
28
+ if (isAbandoned(input)) {
29
+ return [{ type: "WORKSTREAM_CLOSED" }];
30
+ }
31
+ if (input.attentionReasons.includes("cancel_requested")) {
32
+ return [{ type: "CANCEL" }];
33
+ }
34
+ if (input.attentionReasons.includes("retry_requested")) {
35
+ return [{ type: "RETRY" }];
36
+ }
37
+ if (input.attentionReasons.includes("product_approval_observed") &&
38
+ input.activeWorktreeStatus === undefined) {
39
+ return [{ type: "PRODUCT_APPROVAL_OBSERVED" }];
40
+ }
41
+ if (isReviewRequested(input)) {
42
+ return [{ type: "ACTIVE_PR_REVIEW_REQUEST_OBSERVED" }];
43
+ }
44
+ const feedbackSource = hasFeedback(input)
45
+ ? deriveFeedbackSource(input.attentionReasons)
46
+ : undefined;
47
+ if (input.activeWorktreeStatus === "implementing") {
48
+ return feedbackSource === undefined
49
+ ? [{ type: "ACTIVE_WORKTREE_STATUS_OBSERVED", status: input.activeWorktreeStatus }]
50
+ : [{ type: "IMPLEMENTATION_FEEDBACK_OBSERVED", source: feedbackSource }];
51
+ }
52
+ if (input.activeWorktreeStatus === "implementation_done") {
53
+ return feedbackSource === undefined
54
+ ? [{ type: "ACTIVE_WORKTREE_STATUS_OBSERVED", status: input.activeWorktreeStatus }]
55
+ : [{ type: "IMPLEMENTATION_FEEDBACK_OBSERVED", source: feedbackSource }];
56
+ }
57
+ if (input.activeWorktreeStatus === "implementation_ready") {
58
+ return [{ type: "ACTIVE_WORKTREE_STATUS_OBSERVED", status: input.activeWorktreeStatus }];
59
+ }
60
+ if (input.activeWorktreeStatus === "product_specification") {
61
+ return [{ type: "ACTIVE_WORKTREE_STATUS_OBSERVED", status: input.activeWorktreeStatus }];
62
+ }
63
+ if (input.activeWorktreeStatus === "awaiting_plan_approval" &&
64
+ activePullRequestHasLabel(input, input.approvalLabel)) {
65
+ return [{ type: "ACTIVE_PR_PLAN_APPROVAL_OBSERVED" }];
66
+ }
67
+ if (input.activeWorktreeStatus === "planning" ||
68
+ input.activeWorktreeStatus === "awaiting_plan_approval") {
69
+ return feedbackSource === undefined
70
+ ? [{ type: "ACTIVE_WORKTREE_STATUS_OBSERVED", status: input.activeWorktreeStatus }]
71
+ : [{ type: "PLANNING_FEEDBACK_OBSERVED", source: feedbackSource }];
72
+ }
73
+ if (activePullRequestHasLabel(input, input.inProgressLabel)) {
74
+ return feedbackSource === undefined
75
+ ? [{ type: "ACTIVE_PR_PLANNING_LABEL_OBSERVED" }]
76
+ : [{ type: "PLANNING_FEEDBACK_OBSERVED", source: feedbackSource }];
77
+ }
78
+ if (input.activeWorktreeStatus !== undefined) {
79
+ if (feedbackSource !== undefined && isPriorPullRequestImplementationFallback(input)) {
80
+ return [{ type: "IMPLEMENTATION_FEEDBACK_OBSERVED", source: feedbackSource }];
81
+ }
82
+ if (feedbackSource !== undefined) {
83
+ return [{ type: "PLANNING_FEEDBACK_OBSERVED", source: feedbackSource }];
84
+ }
85
+ return [{ type: "ACTIVE_WORKTREE_STATUS_OBSERVED", status: input.activeWorktreeStatus }];
86
+ }
87
+ return feedbackSource === undefined
88
+ ? []
89
+ : [{ type: "WORKSTREAM_COMMENT_ATTENTION_OBSERVED", source: feedbackSource }];
90
+ }
91
+ function activePullRequestHasLabel(input, label) {
92
+ // Active PR/worktree events intentionally refer only to the selected active projection.
93
+ return label !== undefined && input.activePullRequestLabels.includes(label);
94
+ }
95
+ function deriveFeedbackSource(reasons) {
96
+ const hasIssueComment = reasons.some(isIssueCommentFeedback);
97
+ const hasPullRequestComment = reasons.some(isPullRequestCommentFeedback);
98
+ if (hasIssueComment && hasPullRequestComment) {
99
+ return "mixed";
100
+ }
101
+ return hasIssueComment ? "issue_comment" : "pr_comment";
102
+ }
103
+ function isIssueCommentFeedback(reason) {
104
+ return reason === "issue_comment_added" || reason === "issue_comment_edited";
105
+ }
106
+ function isPullRequestCommentFeedback(reason) {
107
+ return reason === "pr_comment_added" || reason === "pr_comment_edited";
108
+ }
109
+ function hasPhaseSignal(events) {
110
+ return events.some((event) => event.type !== "WORKSTREAM_INITIALIZED" &&
111
+ event.type !== "WORKSTREAM_COMMENT_ATTENTION_OBSERVED" &&
112
+ event.type !== "contextualFeedbackReceived");
113
+ }
114
+ function isPriorPullRequestImplementationFallback(input) {
115
+ return (input.originType === "pr" &&
116
+ input.activeWorktreeStatus !== undefined &&
117
+ (input.previousPhase === "implementation_ready" ||
118
+ input.previousPhase === "implementing" ||
119
+ input.previousPhase === "implementation_done" ||
120
+ input.previousPhase === "implementation_feedback"));
121
+ }
@@ -0,0 +1,24 @@
1
+ import { createActor } from "xstate";
2
+ import { deriveWorkstreamMachineEvents } from "./derive-events.js";
3
+ import { workstreamMachine } from "./machine.js";
4
+ export function evaluateWorkstreamMachine(input) {
5
+ const events = deriveWorkstreamMachineEvents(input);
6
+ const initialSnapshot = input.previousPhase === undefined
7
+ ? undefined
8
+ : workstreamMachine.resolveState({
9
+ value: input.previousPhase,
10
+ context: input
11
+ });
12
+ const actor = createActor(workstreamMachine, initialSnapshot === undefined
13
+ ? { input }
14
+ : {
15
+ input,
16
+ snapshot: initialSnapshot
17
+ }).start();
18
+ for (const event of events) {
19
+ actor.send(event);
20
+ }
21
+ const phase = actor.getSnapshot().value;
22
+ actor.stop();
23
+ return phase;
24
+ }
@@ -0,0 +1 @@
1
+ export {};