@loucompanion/forge-bridge 0.1.3-dev.b6be5e7f9668 → 0.1.3-dev.bca7d0ccee53

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/README.md +78 -0
  2. package/dist/bridge/bridge.base.d.ts +6 -1
  3. package/dist/bridge/bridge.service.d.ts +60 -3
  4. package/dist/bridge/bridge.service.js +970 -24
  5. package/dist/bridge/bridge.types.d.ts +172 -1
  6. package/dist/cleanup/cleanup.service.d.ts +13 -2
  7. package/dist/cleanup/cleanup.service.js +435 -3
  8. package/dist/cleanup/cleanup.types.d.ts +10 -0
  9. package/dist/cleanup/index.d.ts +1 -1
  10. package/dist/cleanup/index.js +1 -1
  11. package/dist/cleanup/internals.d.ts +2 -2
  12. package/dist/cleanup/internals.js +1 -1
  13. package/dist/cli/cli.help.d.ts +2 -0
  14. package/dist/cli/cli.help.js +217 -0
  15. package/dist/cli/cli.service.d.ts +42 -2
  16. package/dist/cli/cli.service.js +357 -206
  17. package/dist/cli/local-config.d.ts +6 -0
  18. package/dist/cli/local-config.js +47 -3
  19. package/dist/runtime/attempt-node-runtime-launcher.d.ts +17 -0
  20. package/dist/runtime/attempt-node-runtime-launcher.js +101 -0
  21. package/dist/runtime/attempt-process-supervisor-registry.d.ts +20 -0
  22. package/dist/runtime/attempt-process-supervisor-registry.js +64 -0
  23. package/dist/runtime/attempt-process-supervisor.d.ts +86 -0
  24. package/dist/runtime/attempt-process-supervisor.js +350 -0
  25. package/dist/runtime/generated-output-reset.d.ts +10 -0
  26. package/dist/runtime/generated-output-reset.js +103 -0
  27. package/dist/runtime/host-listener-inspector.d.ts +44 -0
  28. package/dist/runtime/host-listener-inspector.js +324 -0
  29. package/dist/runtime/host-port-broker.d.ts +76 -0
  30. package/dist/runtime/host-port-broker.js +333 -0
  31. package/dist/runtime/host-runtime-contract.d.ts +47 -0
  32. package/dist/runtime/host-runtime-contract.js +700 -0
  33. package/dist/runtime/host-runtime-lease.d.ts +133 -0
  34. package/dist/runtime/host-runtime-lease.js +1462 -0
  35. package/dist/runtime/host-runtime-promotion.d.ts +23 -0
  36. package/dist/runtime/host-runtime-promotion.js +159 -0
  37. package/dist/runtime/node-host-runtime-adapter.d.ts +51 -0
  38. package/dist/runtime/node-host-runtime-adapter.js +268 -0
  39. package/dist/runtime/playwright-host-runtime-adapter.d.ts +20 -0
  40. package/dist/runtime/playwright-host-runtime-adapter.js +220 -0
  41. package/dist/runtime/runtime-isolation-lease.d.ts +67 -0
  42. package/dist/runtime/runtime-isolation-lease.js +158 -0
  43. package/dist/sandbox/command-runner.d.ts +4 -0
  44. package/dist/sandbox/command-runner.js +47 -0
  45. package/dist/sandbox/contract.d.ts +5 -0
  46. package/dist/sandbox/contract.js +276 -0
  47. package/dist/sandbox/event-budget.d.ts +11 -0
  48. package/dist/sandbox/event-budget.js +27 -0
  49. package/dist/sandbox/image-policy.d.ts +35 -0
  50. package/dist/sandbox/image-policy.js +247 -0
  51. package/dist/sandbox/index.d.ts +5 -0
  52. package/dist/sandbox/index.js +5 -0
  53. package/dist/sandbox/lease-watchdog.d.ts +18 -0
  54. package/dist/sandbox/lease-watchdog.js +67 -0
  55. package/dist/sandbox/preflight.d.ts +54 -0
  56. package/dist/sandbox/preflight.js +335 -0
  57. package/dist/sandbox/process.d.ts +20 -0
  58. package/dist/sandbox/process.js +209 -0
  59. package/dist/sandbox/provider-auth/codex.d.ts +26 -0
  60. package/dist/sandbox/provider-auth/codex.js +212 -0
  61. package/dist/sandbox/repository-lock.d.ts +5 -0
  62. package/dist/sandbox/repository-lock.js +119 -0
  63. package/dist/sandbox/runtime.d.ts +297 -0
  64. package/dist/sandbox/runtime.js +2338 -0
  65. package/dist/sandbox/sandbox.types.d.ts +86 -0
  66. package/dist/sandbox/sandbox.types.js +1 -0
  67. package/dist/sandbox/smoke.d.ts +76 -0
  68. package/dist/sandbox/smoke.js +380 -0
  69. package/dist/sandbox/worker-supervisor.d.ts +13 -0
  70. package/dist/sandbox/worker-supervisor.js +36 -0
  71. package/dist/sandbox/worker.d.ts +1 -0
  72. package/dist/sandbox/worker.js +225 -0
  73. package/dist/sandbox/workspace.service.d.ts +13 -0
  74. package/dist/sandbox/workspace.service.js +72 -0
  75. package/dist/session/managed-attempt-tools.d.ts +17 -0
  76. package/dist/session/managed-attempt-tools.js +712 -0
  77. package/dist/session/phase-completion.artifacts.d.ts +10 -0
  78. package/dist/session/phase-completion.artifacts.js +627 -0
  79. package/dist/session/phase-completion.d.ts +43 -46
  80. package/dist/session/phase-completion.helpers.d.ts +117 -0
  81. package/dist/session/phase-completion.helpers.js +701 -0
  82. package/dist/session/phase-completion.js +40 -3488
  83. package/dist/session/phase-completion.prompts.d.ts +14 -0
  84. package/dist/session/phase-completion.prompts.js +235 -0
  85. package/dist/session/phase-completion.runtime.d.ts +48 -0
  86. package/dist/session/phase-completion.runtime.js +786 -0
  87. package/dist/session/phase-completion.schemas.d.ts +3 -0
  88. package/dist/session/phase-completion.schemas.js +812 -0
  89. package/dist/session/phase-completion.validation.d.ts +37 -0
  90. package/dist/session/phase-completion.validation.js +850 -0
  91. package/dist/session/provider-cli/ci-repair-readiness.d.ts +8 -0
  92. package/dist/session/provider-cli/ci-repair-readiness.js +95 -0
  93. package/dist/session/provider-cli/provider-cli.artifacts.d.ts +93 -0
  94. package/dist/session/provider-cli/provider-cli.artifacts.js +778 -0
  95. package/dist/session/provider-cli/provider-cli.base.d.ts +1 -0
  96. package/dist/session/provider-cli/provider-cli.deterministic-payloads.d.ts +12 -0
  97. package/dist/session/provider-cli/provider-cli.deterministic-payloads.js +279 -0
  98. package/dist/session/provider-cli/provider-cli.deterministic.d.ts +14 -0
  99. package/dist/session/provider-cli/provider-cli.deterministic.js +375 -0
  100. package/dist/session/provider-cli/provider-cli.process.d.ts +48 -0
  101. package/dist/session/provider-cli/provider-cli.process.js +474 -0
  102. package/dist/session/provider-cli/provider-cli.prompts.d.ts +47 -0
  103. package/dist/session/provider-cli/provider-cli.prompts.js +595 -0
  104. package/dist/session/provider-cli/provider-cli.qa-sidecars.d.ts +11 -0
  105. package/dist/session/provider-cli/provider-cli.qa-sidecars.js +66 -0
  106. package/dist/session/provider-cli/provider-cli.runtime.d.ts +9 -0
  107. package/dist/session/provider-cli/provider-cli.runtime.js +41 -0
  108. package/dist/session/provider-cli/provider-cli.service.d.ts +26 -53
  109. package/dist/session/provider-cli/provider-cli.service.js +524 -2571
  110. package/dist/session/provider-cli/provider-cli.types.d.ts +6 -0
  111. package/dist/session/provider-cli/provider-output-state.d.ts +22 -0
  112. package/dist/session/provider-cli/provider-output-state.js +473 -0
  113. package/dist/session/session-redaction.d.ts +3 -0
  114. package/dist/session/session-redaction.js +23 -0
  115. package/dist/session/session.base.d.ts +6 -1
  116. package/dist/session/session.service.d.ts +8 -2
  117. package/dist/session/session.service.js +480 -58
  118. package/dist/session/session.types.d.ts +161 -1
  119. package/dist/session/session.types.js +318 -3
  120. package/dist/shared/path/path.js +3 -1
  121. package/dist/shared/process/host-process-recovery.d.ts +12 -0
  122. package/dist/shared/process/host-process-recovery.js +78 -0
  123. package/dist/shared/process/host-process-supervisor.d.ts +57 -0
  124. package/dist/shared/process/host-process-supervisor.js +282 -0
  125. package/dist/shared/process/process.d.ts +1 -0
  126. package/dist/shared/process/process.js +1 -0
  127. package/dist/shared/process/windows-launch.d.ts +8 -0
  128. package/dist/shared/process/windows-launch.js +41 -0
  129. package/dist/shared/provider-capabilities.d.ts +1 -0
  130. package/dist/shared/provider-capabilities.js +3 -0
  131. package/dist/shared/redaction/redaction.js +10 -3
  132. package/dist/version.d.ts +1 -1
  133. package/dist/version.js +1 -1
  134. package/dist/worktree/artifact-injector.d.ts +3 -2
  135. package/dist/worktree/artifact-injector.js +82 -10
  136. package/dist/worktree/dependency-warmer.d.ts +21 -1
  137. package/dist/worktree/dependency-warmer.js +138 -5
  138. package/dist/worktree/index.d.ts +1 -0
  139. package/dist/worktree/index.js +1 -0
  140. package/dist/worktree/prior-phase-artifact-hydrator.d.ts +2 -0
  141. package/dist/worktree/prior-phase-artifact-hydrator.js +136 -0
  142. package/dist/worktree/worktree-paths.d.ts +4 -0
  143. package/dist/worktree/worktree-paths.js +30 -0
  144. package/dist/worktree/worktree.base.d.ts +3 -0
  145. package/dist/worktree/worktree.service.d.ts +9 -3
  146. package/dist/worktree/worktree.service.js +100 -52
  147. package/dist/worktree/worktree.types.d.ts +3 -1
  148. package/package.json +6 -3
  149. package/runtime/windows-job-supervisor.ps1 +356 -0
  150. package/runtime/windows-listener-owner.ps1 +33 -0
@@ -0,0 +1,375 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { GitClient } from "../../shared/git/git.js";
4
+ import { buildSpecBody, selectLaneCommand } from "../../worktree/artifact-injector.js";
5
+ import { completionPhaseFromDispatch, debugCompletionPhaseOrder, expectedCompletionToolName, expectedDebugCompletionToolName, expectedQaCompletionToolName, qaCompletionGateOrder, readCompletionArtifact, usesDebugCycleCompletion, usesDevelopCycleCompletion, writeCompletionArtifact } from "../phase-completion.js";
6
+ import { emitArchitectStructuredEvents, emitCompletionArtifactRecorded, emitQaArtifactRecorded, emitQaGateAccepted, emitQaStructuredEvents, isDebugCompletionPhaseForProvider, nextQaGate, phaseCompleteEventType, qaArtifactRootForDispatch, readQaCompletionArtifactBundle, readQaGateArtifact } from "./provider-cli.artifacts.js";
7
+ import { deterministicBrowserCheck, deterministicBrowserSidecar, hasLaunchTarget, writeQaSidecarFiles } from "./provider-cli.qa-sidecars.js";
8
+ import { buildProviderCliCommand, formatProviderCliCommand, isPullRequestCheckRepairDispatch, isPullRequestCommentsDispatch, singleLine } from "./provider-cli.prompts.js";
9
+ import { estimatedDeterministicUsage, exactRedactionSecrets } from "./provider-cli.runtime.js";
10
+ import { buildProviderUsageEvent } from "./provider-usage.js";
11
+ import { writeCiRepairReadinessManifest } from "./ci-repair-readiness.js";
12
+ import { deterministicArchitectPayload, deterministicCompletionPayload, deterministicQaGatePayload } from "./provider-cli.deterministic-payloads.js";
13
+ export class DeterministicProviderCliService {
14
+ git;
15
+ constructor(git = new GitClient()) {
16
+ this.git = git;
17
+ }
18
+ async runDraft(context) {
19
+ const content = JSON.stringify(deterministicDraftPayload(context.dispatch));
20
+ await context.emit({
21
+ role: "assistant",
22
+ kind: "message",
23
+ content,
24
+ eventType: "draft_complete"
25
+ });
26
+ await context.emit(buildProviderUsageEvent(context.dispatch, estimatedDeterministicUsage()));
27
+ return { summary: "forge-bridge generated deterministic draft." };
28
+ }
29
+ async runImplementation(context) {
30
+ const { dispatch, worktree } = context;
31
+ const phase = completionPhaseFromDispatch(dispatch);
32
+ const developCycle = usesDevelopCycleCompletion(dispatch);
33
+ const debugCycle = usesDebugCycleCompletion(dispatch);
34
+ const providerCommand = buildProviderCliCommand(dispatch);
35
+ const providerCommandText = formatProviderCliCommand(providerCommand);
36
+ const metadataRoot = path.join(worktree.worktreePath, ".forge", "work-items", dispatch.workItem.id);
37
+ const artifactRoot = path.join(metadataRoot, "artifacts");
38
+ await fs.mkdir(artifactRoot, { recursive: true });
39
+ const laneCommand = selectLaneCommand(dispatch.artifacts, dispatch.execution.lane, dispatch.cycleRun.phase);
40
+ if (!laneCommand) {
41
+ throw new Error(`Lane command for lane '${dispatch.execution.lane}' phase '${dispatch.cycleRun.phase}' was not resolved.`);
42
+ }
43
+ const specBody = `${buildSpecBody(laneCommand, dispatch.workItem, phase)}
44
+ ## Execution
45
+
46
+ - Provider: ${singleLine(dispatch.execution.provider)}
47
+ - Model: ${singleLine(dispatch.execution.model)}
48
+ - Command: ${providerCommandText}
49
+ `;
50
+ const specPath = path.join(metadataRoot, "spec.md");
51
+ await fs.writeFile(specPath, specBody, "utf8");
52
+ await fs.writeFile(path.join(metadataRoot, "spec.meta.json"), JSON.stringify({ schema_version: 1, status: "approved", produced_by: "forge-lane-command" }) + "\n", "utf8");
53
+ const changesetPath = path.join(worktree.worktreePath, "FORGE_PHASE5_CHANGESET.md");
54
+ const changesetBody = `# Forge Phase 5 bridge changeset\n\nWork item: ${dispatch.workItem.id}\nTitle: ${dispatch.workItem.title}\nBranch: ${worktree.branchName}\nProvider: ${singleLine(dispatch.execution.provider)}\nModel: ${singleLine(dispatch.execution.model)}\nCommand: ${providerCommandText}\n\nThis deterministic file is written by forge-bridge run-once through the shared session/worktree service path.\n`;
55
+ await fs.writeFile(changesetPath, changesetBody, "utf8");
56
+ await this.git.run(worktree.worktreePath, ["add", "FORGE_PHASE5_CHANGESET.md"]);
57
+ await this.git.run(worktree.worktreePath, ["commit", "--allow-empty", "-m", `forge bridge changeset for ${dispatch.workItem.id.slice(0, 8)}`]);
58
+ await context.emit({
59
+ role: "worker",
60
+ kind: "file_edit",
61
+ content: `Created bridge changeset at ${changesetPath}`,
62
+ eventType: "changeset"
63
+ });
64
+ const phases = debugCycle
65
+ ? debugCompletionPhaseOrder
66
+ : developCycle
67
+ ? ["implementation", "self_review", "validation"]
68
+ : [phase];
69
+ const completionArtifacts = [];
70
+ for (const completionPhase of phases) {
71
+ await writeCompletionArtifact({
72
+ worktreePath: worktree.worktreePath,
73
+ workItemId: dispatch.workItem.id,
74
+ phase: completionPhase,
75
+ producedBy: "forge-bridge",
76
+ redactionSecrets: exactRedactionSecrets(dispatch)
77
+ }, isDebugCompletionPhaseForProvider(completionPhase)
78
+ ? expectedDebugCompletionToolName(completionPhase)
79
+ : expectedCompletionToolName(completionPhase), deterministicCompletionPayload(completionPhase, dispatch, changesetBody));
80
+ const completionArtifact = await readCompletionArtifact(worktree.worktreePath, dispatch.workItem.id, completionPhase);
81
+ if (!completionArtifact) {
82
+ throw new Error(`Deterministic provider did not produce the Forge ${completionPhase} completion artifact.`);
83
+ }
84
+ completionArtifacts.push(completionArtifact);
85
+ await emitCompletionArtifactRecorded(context.emit, worktree.worktreePath, completionArtifact);
86
+ await context.emit({
87
+ role: "assistant",
88
+ kind: "message",
89
+ content: `${completionPhase.replace(/_/g, " ")} complete. forge-bridge produced deterministic phase artifacts.`,
90
+ eventType: phaseCompleteEventType(completionPhase)
91
+ });
92
+ }
93
+ await context.emit(buildProviderUsageEvent(dispatch, estimatedDeterministicUsage()));
94
+ return {
95
+ summary: `forge-bridge completed ${worktree.branchName} (${debugCycle ? "debug cycle" : developCycle ? "develop cycle" : phase}).`,
96
+ spec: {
97
+ repoPath: worktree.worktreePath,
98
+ path: specPath,
99
+ body: specBody,
100
+ producedBy: "forge-lane-command",
101
+ status: "approved"
102
+ },
103
+ artifact: completionArtifacts[0],
104
+ artifacts: completionArtifacts
105
+ };
106
+ }
107
+ async runArchitect(context) {
108
+ const { dispatch, worktree } = context;
109
+ const metadataRoot = path.join(worktree.worktreePath, ".forge", "work-items", dispatch.workItem.id);
110
+ await fs.mkdir(metadataRoot, { recursive: true });
111
+ const specBody = `---\ntitle: "${dispatch.workItem.title}"\nstatus: pending_approval\nproduced_by: forge-bridge\n---\n\n# ${dispatch.workItem.title}\n\nArchitect plan pending approval.\n`;
112
+ const specPath = path.join(metadataRoot, "spec.md");
113
+ await fs.writeFile(specPath, specBody, "utf8");
114
+ await fs.writeFile(path.join(metadataRoot, "spec.meta.json"), JSON.stringify({ schema_version: 1, status: "pending_approval", produced_by: "forge-bridge" }) + "\n", "utf8");
115
+ await writeCompletionArtifact({
116
+ worktreePath: worktree.worktreePath,
117
+ workItemId: dispatch.workItem.id,
118
+ phase: "architect",
119
+ producedBy: "forge-bridge",
120
+ redactionSecrets: exactRedactionSecrets(dispatch),
121
+ architectCycle: true
122
+ }, expectedCompletionToolName("architect"), deterministicArchitectPayload(dispatch));
123
+ const artifact = await readCompletionArtifact(worktree.worktreePath, dispatch.workItem.id, "architect");
124
+ if (!artifact) {
125
+ throw new Error("Deterministic provider did not produce the Forge Architect completion artifact.");
126
+ }
127
+ await emitCompletionArtifactRecorded(context.emit, worktree.worktreePath, artifact);
128
+ await emitArchitectStructuredEvents(context.emit, worktree.worktreePath, dispatch.workItem.id, artifact);
129
+ await context.emit({
130
+ role: "assistant",
131
+ kind: "message",
132
+ content: "Architect plan complete. forge-bridge produced deterministic planning artifacts.",
133
+ eventType: "architect_complete"
134
+ });
135
+ return {
136
+ summary: "forge-bridge completed Architect plan (deterministic).",
137
+ spec: {
138
+ repoPath: worktree.worktreePath,
139
+ path: specPath,
140
+ body: specBody,
141
+ producedBy: "forge-bridge",
142
+ status: "pending_approval"
143
+ },
144
+ artifact,
145
+ artifacts: [artifact]
146
+ };
147
+ }
148
+ async runQa(context) {
149
+ const { dispatch, worktree } = context;
150
+ await context.emit({
151
+ role: "worker",
152
+ kind: "message",
153
+ content: `Planning ${dispatch.execution.provider} QA checks through forge-bridge.`,
154
+ eventType: "qa_plan"
155
+ });
156
+ const qaBrowser = dispatch.qaBrowser;
157
+ const launchTarget = qaBrowser?.launchTarget ?? null;
158
+ const missingLaunchTarget = qaBrowser?.required === true && !hasLaunchTarget(launchTarget);
159
+ const browserSidecar = !missingLaunchTarget && qaBrowser?.required === true && launchTarget
160
+ ? deterministicBrowserSidecar(dispatch.workItem.id, launchTarget, dispatch.cycleRun.ownerType, dispatch.cycleRun.ownerId)
161
+ : null;
162
+ const browserChecks = browserSidecar && launchTarget
163
+ ? [deterministicBrowserCheck(launchTarget, browserSidecar.relativePath)]
164
+ : [];
165
+ const sidecars = await writeQaSidecarFiles(worktree.worktreePath, browserSidecar ? [browserSidecar] : []);
166
+ const summary = missingLaunchTarget
167
+ ? "QA blocked because browser testing is required but no app launch target was provided."
168
+ : "All checks passed. forge-bridge QA completed.";
169
+ const criterion = dispatch.workItem.acceptanceCriteria.trim() || "Forge QA evidence exists";
170
+ for (const gate of qaCompletionGateOrder) {
171
+ await writeCompletionArtifact({
172
+ worktreePath: worktree.worktreePath,
173
+ workItemId: dispatch.workItem.id,
174
+ phase: "qa",
175
+ producedBy: "forge-bridge",
176
+ redactionSecrets: exactRedactionSecrets(dispatch),
177
+ qaCycle: true,
178
+ ownerType: dispatch.cycleRun.ownerType ?? undefined,
179
+ ownerId: dispatch.cycleRun.ownerId ?? undefined
180
+ }, expectedQaCompletionToolName(gate), deterministicQaGatePayload({
181
+ gate,
182
+ criterion,
183
+ summary,
184
+ missingLaunchTarget,
185
+ browserChecks,
186
+ sidecars
187
+ }));
188
+ const gateArtifact = await readQaGateArtifact(qaArtifactRootForDispatch(worktree.worktreePath, dispatch.workItem.id, dispatch), gate);
189
+ if (gateArtifact) {
190
+ await emitQaGateAccepted(context.emit, worktree.worktreePath, gateArtifact, nextQaGate(gate));
191
+ }
192
+ }
193
+ const qaArtifact = await readQaCompletionArtifactBundle(worktree.worktreePath, dispatch.workItem.id, dispatch.cycleRun.ownerType, dispatch.cycleRun.ownerId);
194
+ if (!qaArtifact) {
195
+ throw new Error("Deterministic QA did not produce the complete durable QA artifact bundle.");
196
+ }
197
+ await emitQaArtifactRecorded(context.emit, worktree.worktreePath, qaArtifact);
198
+ await emitQaStructuredEvents(context.emit, qaArtifact);
199
+ await context.emit(buildProviderUsageEvent(dispatch, estimatedDeterministicUsage()));
200
+ return {
201
+ summary: missingLaunchTarget ? "forge-bridge QA blocked." : "forge-bridge QA passed.",
202
+ artifact: qaArtifact,
203
+ artifacts: [qaArtifact]
204
+ };
205
+ }
206
+ async runCiRepair(context) {
207
+ return await this.runPullRequestRepair(context, "ci_repair");
208
+ }
209
+ async runConflictResolve(context) {
210
+ return await this.runPullRequestRepair(context, "conflict_resolve");
211
+ }
212
+ async runPullRequestRepair(context, route) {
213
+ const { dispatch, worktree } = context;
214
+ const repairChecks = route === "ci_repair" || isPullRequestCheckRepairDispatch(dispatch);
215
+ const addressComments = isPullRequestCommentsDispatch(dispatch);
216
+ let repairArtifact = null;
217
+ let repairSpec = null;
218
+ if (repairChecks) {
219
+ const metadataRoot = path.join(worktree.worktreePath, ".forge", "work-items", dispatch.workItem.id);
220
+ await fs.mkdir(metadataRoot, { recursive: true });
221
+ const laneCommand = selectLaneCommand(dispatch.artifacts, dispatch.execution.lane, dispatch.cycleRun.phase);
222
+ if (!laneCommand)
223
+ throw new Error("Deterministic CI repair could not materialize the approved lane spec.");
224
+ const specBody = buildSpecBody(laneCommand, dispatch.workItem, "ci_repair");
225
+ const specPath = path.join(metadataRoot, "spec.md");
226
+ await fs.writeFile(specPath, specBody, "utf8");
227
+ await fs.writeFile(path.join(metadataRoot, "spec.meta.json"), `${JSON.stringify({ schema_version: 1, status: "approved", produced_by: "forge-lane-command" })}\n`, "utf8");
228
+ repairSpec = { repoPath: worktree.worktreePath, path: specPath, body: specBody, producedBy: "forge-lane-command", status: "approved" };
229
+ await writeCiRepairReadinessManifest(dispatch, worktree.worktreePath, specPath, context.runtimeEnvironment);
230
+ const reference = await writeCompletionArtifact({
231
+ worktreePath: worktree.worktreePath,
232
+ workItemId: dispatch.workItem.id,
233
+ phase: "ci_repair",
234
+ producedBy: "forge-deterministic-provider",
235
+ }, expectedCompletionToolName("ci_repair"), deterministicCiRepairPayload());
236
+ repairArtifact = await readCompletionArtifact(worktree.worktreePath, dispatch.workItem.id, "ci_repair");
237
+ if (!repairArtifact)
238
+ throw new Error(`Deterministic CI repair did not produce ${reference.path}.`);
239
+ await emitCompletionArtifactRecorded(context.emit, worktree.worktreePath, repairArtifact);
240
+ }
241
+ await this.git.run(worktree.worktreePath, ["commit", "--allow-empty", "-m", `forge bridge ${repairChecks ? "ci repair" : addressComments ? "pr comments" : "conflict resolve"} for ${dispatch.workItem.id.slice(0, 8)}`]);
242
+ await context.emit(buildProviderUsageEvent(dispatch, estimatedDeterministicUsage()));
243
+ await context.emit({
244
+ role: "assistant",
245
+ kind: "message",
246
+ content: repairChecks
247
+ ? `Deterministic CI repair completed for ${worktree.branchName}.`
248
+ : addressComments
249
+ ? `Deterministic PR comment addressing completed for ${worktree.branchName}.`
250
+ : `Deterministic conflict resolve completed for ${worktree.branchName}.`,
251
+ eventType: repairChecks ? "pr_checks_complete" : addressComments ? "pr_comments_complete" : "conflict_resolve_complete"
252
+ });
253
+ return {
254
+ summary: repairChecks
255
+ ? `forge-bridge repaired PR checks for ${worktree.branchName}.`
256
+ : addressComments
257
+ ? `forge-bridge addressed PR comments for ${worktree.branchName}.`
258
+ : `forge-bridge resolved conflicts for ${worktree.branchName}.`,
259
+ spec: repairSpec,
260
+ artifact: repairArtifact,
261
+ artifacts: repairArtifact ? [repairArtifact] : [],
262
+ };
263
+ }
264
+ }
265
+ function deterministicCiRepairPayload() {
266
+ const command = { command: "forge deterministic ci-repair validation", exit_code: 0, status: "pass", summary: "Deterministic CI repair fixture passed." };
267
+ return {
268
+ status: "pass",
269
+ summary: "Deterministic CI repair handoff is ready for provider checks.",
270
+ failed_checks: [{ name: "CI / deterministic", provider_state: "failure", workflow: ".github/workflows/ci.yml", job: "deterministic", step: "fixture" }],
271
+ root_cause: { classification: "test expectation mismatch", summary: "Deterministic fixture failure repaired.", evidence: ["Fixture provider reported the expected failed check."] },
272
+ changed_files: [],
273
+ no_code_action: { action: "deterministic_fixture", reason: "The deterministic provider models the repair without a product diff." },
274
+ narrow_validation: [command],
275
+ workflow_validation: [{ step: "deterministic fixture", equivalence: "supported", command: command.command, exit_code: 0, status: "pass", summary: command.summary }],
276
+ broader_validation: [command],
277
+ remaining_risks: [],
278
+ provider_state: "ready_for_rerun",
279
+ ready_for_push: true,
280
+ };
281
+ }
282
+ function deterministicDraftPayload(dispatch) {
283
+ const kind = dispatch.draft?.kind ?? "work_item";
284
+ if (kind === "work_order_decomposition") {
285
+ return {
286
+ summary: "Fake decompose proposed a Feature DAG for the Work Order.",
287
+ blockedReason: "",
288
+ candidates: [
289
+ {
290
+ origin: "agent",
291
+ key: "intake-submission",
292
+ title: "Customer intake submission",
293
+ summary: "Collect, validate, de-duplicate, and confirm incoming customer requests.",
294
+ planningPacket: "# Customer intake submission\n\nPlan Stories for request form validation, attachment support, duplicate prevention, and submission confirmation.",
295
+ sortOrder: 10,
296
+ dependsOn: []
297
+ },
298
+ {
299
+ origin: "agent",
300
+ key: "review-queue",
301
+ title: "Reviewer triage queue",
302
+ summary: "Provide reviewer triage, assignment, notes, status transitions, and audit trail.",
303
+ planningPacket: "# Reviewer triage queue\n\nPlan Stories for queue filtering, assignment, reviewer notes, status changes, and immutable audit trail.",
304
+ sortOrder: 20,
305
+ dependsOn: ["intake-submission"]
306
+ }
307
+ ]
308
+ };
309
+ }
310
+ if (kind === "feature_decomposition") {
311
+ return {
312
+ summary: "Fake decompose proposed a Story DAG for the Feature.",
313
+ blockedReason: "",
314
+ candidates: [
315
+ {
316
+ origin: "agent",
317
+ key: "foundation",
318
+ title: "Build the feature foundation",
319
+ description: "Create the persistence, API, and UI shell needed by the feature.",
320
+ acceptanceCriteria: "Scenario: Foundation exists\nGiven the Feature is selected\nWhen the foundation is delivered\nThen downstream Stories can build on stable contracts",
321
+ points: 3,
322
+ sortOrder: 10,
323
+ dependsOn: []
324
+ },
325
+ {
326
+ origin: "agent",
327
+ key: "workflow-behavior",
328
+ title: "Implement the primary workflow behavior",
329
+ description: "Implement the main user-visible behavior for the feature after the foundation is ready.",
330
+ acceptanceCriteria: "Scenario: Workflow behavior works\nGiven the foundation Story is complete\nWhen the user performs the primary workflow\nThen the expected state change and feedback are visible",
331
+ points: 5,
332
+ sortOrder: 20,
333
+ dependsOn: ["foundation"]
334
+ },
335
+ {
336
+ origin: "agent",
337
+ key: "audit-and-tests",
338
+ title: "Add audit coverage and regression tests",
339
+ description: "Add audit/evidence behavior and automated regression coverage for the feature.",
340
+ acceptanceCriteria: "Scenario: Feature is verifiable\nGiven the workflow behavior is complete\nWhen validation runs\nThen audit evidence and tests cover the expected outcomes",
341
+ points: 3,
342
+ sortOrder: 30,
343
+ dependsOn: ["workflow-behavior"]
344
+ }
345
+ ]
346
+ };
347
+ }
348
+ if (kind === "work_order") {
349
+ return {
350
+ title: "Deterministic runner Work Order draft",
351
+ summary: "Forge Runner generated this deterministic Work Order draft.",
352
+ objective: "Validate runner-backed Draft generation.",
353
+ planningPacket: "Runner-backed draft generation returned a Work Order JSON payload.",
354
+ decompositionContext: "Use this deterministic fixture only in bridge tests.",
355
+ assumptions: ["Forge Runner dispatch is available."],
356
+ warnings: []
357
+ };
358
+ }
359
+ if (kind === "story") {
360
+ return {
361
+ title: "Deterministic runner Story draft",
362
+ description: "Forge Runner generated this deterministic Story draft.",
363
+ acceptanceCriteria: "Given runner-backed Draft generation, when Forge requests a Story draft, then JSON is returned.",
364
+ points: 3
365
+ };
366
+ }
367
+ return {
368
+ title: "Deterministic runner Work Item draft",
369
+ description: "Forge Runner generated this deterministic Work Item draft.",
370
+ acceptanceCriteria: "Given runner-backed Draft generation, when Forge requests a draft, then JSON is returned.",
371
+ reproSteps: "Run Draft with a Forge Runner provider selected.",
372
+ systemInfo: "forge-bridge deterministic provider",
373
+ points: 1
374
+ };
375
+ }
@@ -0,0 +1,48 @@
1
+ import { type LaunchCommand } from "../../shared/process/windows-launch.js";
2
+ export type ProviderCliCommand = LaunchCommand;
3
+ export interface ProviderProcessRunRequest {
4
+ readonly command: string;
5
+ readonly args: readonly string[];
6
+ readonly cwd: string;
7
+ readonly env?: Readonly<Record<string, string>>;
8
+ readonly stdin?: string;
9
+ readonly captureOutput?: boolean;
10
+ }
11
+ export interface ProviderProcessRunResult {
12
+ readonly exitCode: number;
13
+ readonly stdout: string;
14
+ readonly stderr: string;
15
+ }
16
+ export interface ProviderProcessRunner {
17
+ run(request: ProviderProcessRunRequest, handlers?: ProviderProcessHandlers, signal?: AbortSignal): Promise<ProviderProcessRunResult>;
18
+ }
19
+ export interface ProviderProcessHandlers {
20
+ onStarted?(): Promise<void> | void;
21
+ onStdoutLine?(line: string): Promise<void> | void;
22
+ onStderrLine?(line: string): Promise<void> | void;
23
+ }
24
+ interface ProviderChildEnvironmentOptions {
25
+ readonly parentEnv?: NodeJS.ProcessEnv;
26
+ readonly platform?: NodeJS.Platform;
27
+ readonly homeDir?: string;
28
+ }
29
+ export declare function buildProviderChildEnvironment(requestEnv?: Readonly<Record<string, string>>, options?: ProviderChildEnvironmentOptions): NodeJS.ProcessEnv;
30
+ export declare class StreamingProviderProcessRunner implements ProviderProcessRunner {
31
+ run(request: ProviderProcessRunRequest, handlers?: ProviderProcessHandlers, signal?: AbortSignal): Promise<ProviderProcessRunResult>;
32
+ }
33
+ export declare function resolveCodexNpmDirectLaunch(commandPath: string, args: readonly string[]): LaunchCommand | null;
34
+ /**
35
+ * Avoids cmd.exe's nested-quote handling for package-manager shims. That
36
+ * quoting loses executable paths containing spaces after the command is
37
+ * wrapped by the Windows Job Object supervisor.
38
+ */
39
+ export declare function resolvePackageManagerDirectLaunch(commandPath: string, args: readonly string[]): LaunchCommand | null;
40
+ /**
41
+ * Resolves Windows commands without spawning where.exe. Runtime launches can
42
+ * happen while the machine is under enough process pressure for where.exe to
43
+ * miss its timeout; returning the unresolved package-manager name in that case
44
+ * makes Node fall back to a shell shim that cannot survive Job Object wrapping.
45
+ */
46
+ export declare function resolveWindowsCommandFromPath(command: string, pathValue?: string, pathExtValue?: string): string | null;
47
+ export declare function chooseWindowsCommandCandidate(candidates: readonly string[]): string | null;
48
+ export {};