20+ CHANGES_REQUESTED dispositions across PHP MCP services, Go pkg/agentic, hermes_runner_mcp Python server, plugin shell scripts. Highlights: - DatabaseSchema.php: identifier quoting - AwardCredits.php: task row locking order - CreditTransaction.php: fail-fast row decoding - OpenApiGenerator.php: YAML parse handling + uri query params - CaptureDispatchResultJob.php: AgentProfile namespace fix - CreditsController.php: missing workspace_id fail-closed - QueryAuditService.php: prose query false positives + unbounded aggregation - McpHealthService.php: proc_close after timeout + env var resolution - CreditLedger.php + FleetOverview.php: workspace agent + dispatch target validation - McpAgentServerCommand.php: quota burn on failed tool calls - McpMetricsService.php: N-day window consistency - hermes_runner_mcp: API key off command line + invalid method+id + run_id encoding - CircuitBreaker.php: extracted CircuitOpenException class with autoload-correct placement - pkg/agentic + brain + flow: SonarCloud sendMessage/fetchLoopRepoRefs/commitWorkspace/Connect annotations - shell scripts: removed [[ usage for portability 43 files modified, 1 new (CircuitOpenException.php). Verification: gofmt -w + php -l + python3 -m py_compile + bash -n all clean. Touched-package go test passes (pkg/lib/flow, pkg/lib). Full go test ./... blocked by pre-existing dappco.re module graph drift, out of scope. Parked for separate work: - Mantis #1062: go.mod local replace removal (cross-repo architectural) - Mantis #1063: Sonar residual line-length / duplication quality-gate cluster Closes findings on https://github.com/dAppCore/agent/pull/6 Co-authored-by: Codex <noreply@openai.com>
116 lines
3 KiB
PHP
116 lines
3 KiB
PHP
<?php
|
|
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
/*
|
|
* Core PHP Framework
|
|
*
|
|
* Licensed under the European Union Public Licence (EUPL) v1.2.
|
|
* See LICENSE file for details.
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Core\Mod\Agentic\Actions\Forge;
|
|
|
|
use Core\Actions\Action;
|
|
use Core\Mod\Agentic\Pipeline\ForgejoMetaReader;
|
|
use Core\Mod\Agentic\Pipeline\MetaReader;
|
|
use Core\Mod\Agentic\Services\ForgejoService;
|
|
|
|
/**
|
|
* Evaluate and merge a Forgejo pull request when ready.
|
|
*
|
|
* Checks the PR state, mergeability, and CI status from MetaReader
|
|
* before attempting the merge. Returns a result array describing the
|
|
* outcome.
|
|
*
|
|
* Usage:
|
|
* $result = ManagePullRequest::run('core', 'app', 10);
|
|
*/
|
|
class ManagePullRequest
|
|
{
|
|
use Action;
|
|
|
|
public function __construct(
|
|
private ?MetaReader $metaReader = null,
|
|
) {}
|
|
|
|
/**
|
|
* @return array{merged: bool, pr_number?: int, reason?: string}
|
|
*/
|
|
public function handle(string $owner, string $repo, int $prNumber): array
|
|
{
|
|
$forge = app(ForgejoService::class);
|
|
$metaReader = $this->resolveMetaReader($owner, $repo);
|
|
|
|
try {
|
|
$prMeta = $metaReader->getPRMeta($prNumber);
|
|
} catch (\Throwable $exception) {
|
|
return [
|
|
'merged' => false,
|
|
'reason' => 'meta_unavailable',
|
|
];
|
|
}
|
|
|
|
if ($prMeta->state !== 'open') {
|
|
return ['merged' => false, 'reason' => 'not_open'];
|
|
}
|
|
|
|
if ($prMeta->mergeability !== 'mergeable') {
|
|
return ['merged' => false, 'reason' => 'conflicts'];
|
|
}
|
|
|
|
if (! $this->checksHavePassed($prMeta->checkStatuses)) {
|
|
return ['merged' => false, 'reason' => 'checks_pending'];
|
|
}
|
|
|
|
try {
|
|
$forge->mergePullRequest($owner, $repo, $prNumber);
|
|
} catch (\Throwable $exception) {
|
|
return [
|
|
'merged' => false,
|
|
'reason' => 'merge_failed',
|
|
];
|
|
}
|
|
|
|
return ['merged' => true, 'pr_number' => $prNumber];
|
|
}
|
|
|
|
/**
|
|
* @param array<int, array{name: string, conclusion: string|null, status: string|null}> $checkStatuses
|
|
*/
|
|
private function checksHavePassed(array $checkStatuses): bool
|
|
{
|
|
if ($checkStatuses === []) {
|
|
return false;
|
|
}
|
|
|
|
foreach ($checkStatuses as $checkStatus) {
|
|
if (($checkStatus['status'] ?? null) !== 'completed') {
|
|
return false;
|
|
}
|
|
|
|
if (($checkStatus['conclusion'] ?? null) !== 'success') {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private function resolveMetaReader(string $owner, string $repo): MetaReader
|
|
{
|
|
if ($this->metaReader instanceof MetaReader) {
|
|
return $this->metaReader;
|
|
}
|
|
|
|
/** @var MetaReader $metaReader */
|
|
$metaReader = app()->makeWith(ForgejoMetaReader::class, [
|
|
'owner' => $owner,
|
|
'repo' => $repo,
|
|
]);
|
|
|
|
return $metaReader;
|
|
}
|
|
}
|