Skip to content
This repository was archived by the owner on Mar 15, 2026. It is now read-only.

Commit eecb37f

Browse files
refactoring
1 parent 61a3594 commit eecb37f

20 files changed

+862
-633
lines changed

config/services.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,15 @@ services:
2727
arguments:
2828
- "%env(resolve:DATABASE_PRODUCT)%://%env(resolve:DATABASE_USER)%:%env(resolve:DATABASE_PASSWORD)%@%env(resolve:DATABASE_HOST)%:%env(resolve:DATABASE_PORT)%/%env(resolve:DATABASE_DB)%?serverVersion=%env(resolve:DATABASE_SERVERVERSION)%"
2929

30+
# LlmIntegration: bind interface to implementation per consumer (two preparators exist)
31+
App\LlmIntegration\Infrastructure\Service\Cursor\CursorCliBinaryManager:
32+
arguments:
33+
$configWriter: '@App\LlmIntegration\Infrastructure\Service\Cursor\CursorCliConfigWriter'
34+
$runtimePreparator: '@App\LlmIntegration\Infrastructure\Service\Cursor\CursorWorkspaceRuntimePreparator'
35+
36+
App\LlmIntegration\Infrastructure\Service\ClaudeCode\ClaudeCodeBinaryManager:
37+
arguments:
38+
$runtimePreparator: '@App\LlmIntegration\Infrastructure\Service\ClaudeCode\ClaudeCodeWorkspaceRuntimePreparator'
39+
3040
# add more service definitions when explicit configuration is needed
3141
# please note that last definitions always *replace* previous ones

src/ImplementationAgent/Infrastructure/Service/ImplementationRunExecutor.php

Lines changed: 92 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -96,126 +96,117 @@ public function execute(ImplementIssueMessage $message): void
9696

9797
private function doExecute(ImplementIssueMessage $message, ProductConfigDto $config): void
9898
{
99-
$runStarted = $this->workflowConcurrencyFacade->tryStartRun(
99+
$didRun = $this->workflowConcurrencyFacade->executeWithRunClaim(
100100
$message->productConfigId,
101101
$message->issueNumber,
102102
WorkflowRunPhase::Implementation,
103103
$message->runId,
104-
);
105-
106-
if (!$runStarted) {
107-
$this->logger->info('[ImplementationAgent] Skipping stale or duplicate implementation message', [
108-
'productConfigId' => $message->productConfigId,
109-
'issueNumber' => $message->issueNumber,
110-
'runId' => $message->runId,
111-
]);
112-
113-
return;
114-
}
104+
function () use ($message, $config): void {
105+
$issueDto = $this->findIssue($config->githubUrl, $config->githubToken, $message->issueNumber);
106+
if ($issueDto === null) {
107+
$this->logger->error('[ImplementationAgent] Could not fetch issue data', [
108+
'issueNumber' => $message->issueNumber,
109+
]);
110+
111+
return;
112+
}
115113

116-
try {
117-
$issueDto = $this->findIssue($config->githubUrl, $config->githubToken, $message->issueNumber);
118-
if ($issueDto === null) {
119-
$this->logger->error('[ImplementationAgent] Could not fetch issue data', [
120-
'issueNumber' => $message->issueNumber,
121-
]);
114+
$issueComments = $this->githubIntegrationFacade->getIssueComments(
115+
$config->githubUrl,
116+
$config->githubToken,
117+
$message->issueNumber,
118+
);
122119

123-
return;
124-
}
120+
$workspaceInfo = null;
121+
122+
try {
123+
if ($message->isRevision && $message->prBranchName !== null) {
124+
$workspaceInfo = $this->workspaceManagementFacade->acquireWorkspace(
125+
$config->id,
126+
$message->issueNumber,
127+
$config->githubUrl,
128+
$config->githubToken,
129+
$message->prBranchName,
130+
);
131+
132+
$prComments = $message->prNumber !== null
133+
? $this->githubIntegrationFacade->getPullRequestComments($config->githubUrl, $config->githubToken, $message->prNumber)
134+
: [];
135+
136+
$prompt = $this->buildRevisionPrompt($issueDto, $issueComments, $prComments, $message->prNumber);
137+
} else {
138+
$workspaceInfo = $this->workspaceManagementFacade->acquireWorkspace(
139+
$config->id,
140+
$message->issueNumber,
141+
$config->githubUrl,
142+
$config->githubToken,
143+
);
144+
145+
$prompt = $this->buildPrompt($issueDto, $issueComments);
146+
}
147+
148+
$agentApiKeyOverride = $message->agentApiKeyOverride;
149+
$credentials = $agentApiKeyOverride !== null
150+
? new AgentCredentialsDto($agentApiKeyOverride, $agentApiKeyOverride)
151+
: new AgentCredentialsDto($config->cursorAgentApiKey, $config->anthropicApiKey);
152+
153+
$agentResult = $this->llmIntegrationFacade->runAgent(
154+
AgentRole::Implementation,
155+
$prompt,
156+
$workspaceInfo->workspacePath,
157+
$credentials,
158+
$config->githubToken,
159+
$workspaceInfo->containerName,
160+
$message->agentProviderOverride,
161+
);
125162

126-
$issueComments = $this->githubIntegrationFacade->getIssueComments(
127-
$config->githubUrl,
128-
$config->githubToken,
129-
$message->issueNumber,
130-
);
163+
$outcome = ImplementationOutcome::fromAgentOutput($agentResult->success, $agentResult->resultText);
131164

132-
$workspaceInfo = null;
165+
$this->logger->info('[ImplementationAgent] Agent completed', [
166+
'issueNumber' => $message->issueNumber,
167+
'outcome' => $outcome->value,
168+
'isRevision' => $message->isRevision,
169+
'durationMs' => $agentResult->durationMs,
170+
]);
133171

134-
try {
135-
if ($message->isRevision && $message->prBranchName !== null) {
136-
$workspaceInfo = $this->workspaceManagementFacade->acquireWorkspace(
137-
$config->id,
138-
$message->issueNumber,
172+
$this->handleOutcome(
173+
$outcome,
174+
$agentResult->resultText,
139175
$config->githubUrl,
140176
$config->githubToken,
141-
$message->prBranchName,
177+
$message->issueNumber,
178+
$issueDto,
179+
$message->prNumber,
142180
);
181+
} catch (Throwable $e) {
182+
$this->logger->error('[ImplementationAgent] Unhandled exception', [
183+
'issueNumber' => $message->issueNumber,
184+
'isRevision' => $message->isRevision,
185+
'error' => $e->getMessage(),
186+
]);
143187

144-
$prComments = $message->prNumber !== null
145-
? $this->githubIntegrationFacade->getPullRequestComments($config->githubUrl, $config->githubToken, $message->prNumber)
146-
: [];
188+
$this->applyErrorLabels($config->githubUrl, $config->githubToken, $message->issueNumber);
147189

148-
$prompt = $this->buildRevisionPrompt($issueDto, $issueComments, $prComments, $message->prNumber);
149-
} else {
150-
$workspaceInfo = $this->workspaceManagementFacade->acquireWorkspace(
151-
$config->id,
152-
$message->issueNumber,
190+
$this->githubIntegrationFacade->postIssueComment(
153191
$config->githubUrl,
154192
$config->githubToken,
193+
$message->issueNumber,
194+
"**ProductBuilder Implementation Error**\n\nAn unexpected error occurred during implementation:\n\n> " . $e->getMessage(),
155195
);
156-
157-
$prompt = $this->buildPrompt($issueDto, $issueComments);
196+
} finally {
197+
if ($workspaceInfo !== null) {
198+
$this->workspaceManagementFacade->releaseWorkspace($workspaceInfo);
199+
}
158200
}
201+
},
202+
);
159203

160-
$agentApiKeyOverride = $message->agentApiKeyOverride;
161-
$credentials = $agentApiKeyOverride !== null
162-
? new AgentCredentialsDto($agentApiKeyOverride, $agentApiKeyOverride)
163-
: new AgentCredentialsDto($config->cursorAgentApiKey, $config->anthropicApiKey);
164-
165-
$agentResult = $this->llmIntegrationFacade->runAgent(
166-
AgentRole::Implementation,
167-
$prompt,
168-
$workspaceInfo->workspacePath,
169-
$credentials,
170-
$config->githubToken,
171-
$workspaceInfo->containerName,
172-
$message->agentProviderOverride,
173-
);
174-
175-
$outcome = ImplementationOutcome::fromAgentOutput($agentResult->success, $agentResult->resultText);
176-
177-
$this->logger->info('[ImplementationAgent] Agent completed', [
178-
'issueNumber' => $message->issueNumber,
179-
'outcome' => $outcome->value,
180-
'isRevision' => $message->isRevision,
181-
'durationMs' => $agentResult->durationMs,
182-
]);
183-
184-
$this->handleOutcome(
185-
$outcome,
186-
$agentResult->resultText,
187-
$config->githubUrl,
188-
$config->githubToken,
189-
$message->issueNumber,
190-
$issueDto,
191-
$message->prNumber,
192-
);
193-
} catch (Throwable $e) {
194-
$this->logger->error('[ImplementationAgent] Unhandled exception', [
195-
'issueNumber' => $message->issueNumber,
196-
'isRevision' => $message->isRevision,
197-
'error' => $e->getMessage(),
198-
]);
199-
200-
$this->applyErrorLabels($config->githubUrl, $config->githubToken, $message->issueNumber);
201-
202-
$this->githubIntegrationFacade->postIssueComment(
203-
$config->githubUrl,
204-
$config->githubToken,
205-
$message->issueNumber,
206-
"**ProductBuilder Implementation Error**\n\nAn unexpected error occurred during implementation:\n\n> " . $e->getMessage(),
207-
);
208-
} finally {
209-
if ($workspaceInfo !== null) {
210-
$this->workspaceManagementFacade->releaseWorkspace($workspaceInfo);
211-
}
212-
}
213-
} finally {
214-
$this->workflowConcurrencyFacade->releaseRunClaim(
215-
$message->productConfigId,
216-
$message->issueNumber,
217-
$message->runId,
218-
);
204+
if (!$didRun) {
205+
$this->logger->info('[ImplementationAgent] Skipping stale or duplicate implementation message', [
206+
'productConfigId' => $message->productConfigId,
207+
'issueNumber' => $message->issueNumber,
208+
'runId' => $message->runId,
209+
]);
219210
}
220211
}
221212

0 commit comments

Comments
 (0)