Skip to content

feat: full quality gate v2 — PHPStan, Pint, Rector, Publish Guard#73

Open
jordanpartridge wants to merge 5 commits into
masterfrom
feat/v2-full-quality-gate
Open

feat: full quality gate v2 — PHPStan, Pint, Rector, Publish Guard#73
jordanpartridge wants to merge 5 commits into
masterfrom
feat/v2-full-quality-gate

Conversation

@jordanpartridge
Copy link
Copy Markdown
Contributor

@jordanpartridge jordanpartridge commented Apr 10, 2026

Summary

Upgrades certify from 3 checks to 7 — making gate the universal quality standard across the ecosystem.

New checks

Check What it does Graceful skip
PhpStanAnalyzer Static analysis at configurable level Skips if phpstan not installed
PintFormatter Code style verification (--test mode) Skips if pint not installed
RectorAnalyzer Code hygiene dry-run Skips if no rector.php config
PublishGuard Blocks .env, .map, secrets, large binaries Always runs

New action input

- uses: synapse-sentinel/gate@v2
  with:
    coverage-threshold: 80
    phpstan-level: 5        # NEW — configurable per repo
    auto-merge: true

Certify order

Tests → Pint → PHPStan → Rector → Security → Syntax → Publish

Backward compatible

  • Existing consumers get Pint + PHPStan + Rector + Publish Guard for free
  • All new checks gracefully skip if the tool isn't installed
  • Default PHPStan level is 5 (repos can override to 8+)
  • No breaking changes to existing inputs or outputs

Consumer usage after merge

- uses: synapse-sentinel/gate@v2
  with:
    coverage-threshold: 100
    phpstan-level: 8
    auto-merge: true

Summary by CodeRabbit

  • New Features

    • Configurable PHPStan analysis level (0–9, or -1 to skip).
    • Added Pint style checker, Rector analyzer, and Publish Guard to the certification pipeline.
  • Tests

    • Updated unit tests and HTTP mocks to reflect the expanded checks and additional external interactions.
  • Chores

    • Various import/formatting and small prompt/format tweaks across the codebase.

…tify

Wire 4 new checks into CertifyCommand alongside existing Tests, Security, and Syntax:

- PhpStanAnalyzer: configurable level (--phpstan-level, default 5), JSON output parsing, graceful skip if not installed
- PintFormatter: --test mode, JSON output, reports files needing formatting
- RectorAnalyzer: --dry-run mode, graceful skip if no rector.php config
- PublishGuard: scans for .env files (excludes .testing/.example), .map files, large binaries (>1MB)

All checks follow existing CheckInterface pattern with pass/fail/skip semantics.
New action.yml input: phpstan-level (default 5, set -1 to skip).

Certify now runs 7 checks: Tests → Pint → PHPStan → Rector → Security → Syntax → Publish
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 10, 2026

📝 Walkthrough

Walkthrough

Adds four new checks (PhpStanAnalyzer, PintFormatter, RectorAnalyzer, PublishGuard), integrates them into the certify pipeline and GitHub Action via a new phpstan-level input, and applies minor formatting/instantiation adjustments across utilities and tests.

Changes

Cohort / File(s) Summary
New Check Classes
app/Checks/PhpStanAnalyzer.php, app/Checks/PintFormatter.php, app/Checks/PublishGuard.php, app/Checks/RectorAnalyzer.php
Added four checks implementing CheckInterface. PhpStanAnalyzer runs phpstan with configurable level and parses JSON; PintFormatter runs pint --test and parses results; RectorAnalyzer runs rector dry-run and summarizes diffs; PublishGuard flags tracked .env files, .map files, and large files.
Command & Action Integration
app/Commands/CertifyCommand.php, action.yml
CertifyCommand now accepts --phpstan-level (default 5) and instantiates the new checks (including PhpStanAnalyzer($phpstanLevel)). action.yml adds phpstan-level input and passes it into the gate certify command.
Process/Runner & Tests Adjustments
app/Checks/TestRunner.php, tests/.../*, tests/Unit/Commands/CertifyCommandTest.php
Type imports replaced fully-qualified class names; tests updated to use imported types and extended HTTP mock sequence for the actionable prompt response.
Utilities & Transformers
app/Services/PromptAssembler.php, app/Transformers/PhpStanPromptTransformer.php, app/Transformers/TestFailurePromptTransformer.php
Minor formatting and concatenation changes, instantiation syntax normalized (removed parentheses), and small string/pluralization formatting tweaks.
Clients & Config/bootstrap
app/GitHub/ChecksClient.php, bootstrap/app.php, config/app.php, config/commands.php
Whitespace/logging formatting tweaks; replaced fully-qualified references with imports for application and config files; updated hidden commands to use imported class names.

Sequence Diagram(s)

sequenceDiagram
    participant Certify as CertifyCommand
    participant PhpStan as PhpStanAnalyzer
    participant Pint as PintFormatter
    participant Rector as RectorAnalyzer
    participant Publish as PublishGuard
    participant Runner as ProcessRunner
    Certify->>PhpStan: run(workingDirectory, level)
    PhpStan->>Runner: execute php .../gate certify --phpstan-level
    Runner-->>PhpStan: JSON output / exitCode
    PhpStan-->>Certify: CheckResult
    Certify->>Pint: run(workingDirectory)
    Pint->>Runner: execute vendor/bin/pint --test --format=json
    Runner-->>Pint: JSON output / exitCode
    Pint-->>Certify: CheckResult
    Certify->>Rector: run(workingDirectory)
    Rector->>Runner: execute vendor/bin/rector --dry-run
    Runner-->>Rector: output / exitCode
    Rector-->>Certify: CheckResult
    Certify->>Publish: run(workingDirectory)
    Publish->>Runner: execute git ls-files / inspect files
    Runner-->>Publish: file listings
    Publish-->>Certify: CheckResult
    Certify->>Certify: aggregate & report results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 I hopped through code with nimble feet,
Added guards to keep checks neat,
PHPStan, Pint, Rector too,
Publish Guard spots what shouldn't view,
Quality carrots for us to eat! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: introducing four new quality gate checks (PHPStan, Pint, Rector, Publish Guard) to expand the certify step.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/v2-full-quality-gate

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 PHPStan (2.1.46)

/app/Branding.php
/app/Checks/CheckInterface.php
/app/Checks/CheckResult.php
/app/Checks/PestSyntaxValidator.php
/app/Checks/PhpStanAnalyzer.php
/app/Checks/PintFormatter.php
/app/Checks/PublishGuard.php
/app/Checks/RectorAnalyzer.php
/app/Checks/SecurityScanner.php
/app/Checks/TestRunner.php
/app/Commands/AnalyzeCommand.php
/app/Commands/CertifyCommand.php
/app/Commands/CheckCommand.php
/app/Commands/SecurityCommand.php
/app/Commands/SyntaxCommand.php
/app/Commands/TestsCommand.php
/app/Contracts/ProcessResult.php
/app/Contracts/ProcessRunner.php
/app/Contracts/PromptTransformerInterface.php
/app/GitHub/ChecksClient.php
/app/GitHub/CommentsClient.php
/app/Providers/AppServiceProvider.php
/app/Services/CloverParser.php
/app/Services/CoverageReporter.php
/app/Services/PestOutputParser.php
/app/Services/PromptAssembler.php
/app/Services/SymfonyProcessRunner.php
/app/Stages/TechnicalGate.php
/app/Transformers/PhpStanPromptTransformer.php
/app/Transformers/TestFailurePromptTransformer.php

... [truncated 95241 characters] ...

dor/nesbot/carbon/src/Carbon/Carbon.php
/vendor/nesbot/carbon/src/Carbon/CarbonConverterInterface.php
/vendor/nesbot/carbon/src/Carbon/CarbonImmutable.php
/vendor/nesbot/carbon/src/Carbon/CarbonInterface.php
/vendor/nesbot/carbon/src/Carbon/CarbonInterval.php
/vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php
PHP Fatal error: Allowed memory size of 8589934592 bytes exhausted (tried to allocate 16384 bytes) in phar:///usr/bin/phpstan/src/Type/TypehintHelper.php on line 55
Fatal error: Allowed memory size of 8589934592 bytes exhausted (tried to allocate 16384 bytes) in phar:///usr/bin/phpstan/src/Type/TypehintHelper.php on line 55

PHPStan process crashed because it reached configured PHP memory limit: 8192M
Increase your memory limit in php.ini or run PHPStan with --memory-limit CLI option.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 10, 2026

📊 Coverage Report

Metric Coverage Threshold Status
Lines 61.3% 100%

Files Below Threshold

File Coverage Uncovered Lines
app/Checks/PhpStanAnalyzer.php 0% 15, 19, 24, 26, 27... (+28 more)
app/Checks/PintFormatter.php 0% 14, 18, 23, 25, 26... (+28 more)
app/Checks/PublishGuard.php 0% 14, 18, 23, 26, 27... (+30 more)
app/Checks/RectorAnalyzer.php 0% 14, 18, 23, 25, 26... (+22 more)
app/Commands/AnalyzeCommand.php 0% 22, 23, 25, 26, 28... (+51 more)
app/Commands/CheckCommand.php 0% 30, 31, 33, 34, 37... (+91 more)
app/Transformers/PhpStanPromptTransformer.php 3.6% 49, 51, 52, 53, 54... (+76 more)
app/Transformers/TestFailurePromptTransformer.php 11.7% 47, 68, 69, 71, 72... (+101 more)
app/Services/PromptAssembler.php 88.9% 55, 56, 57, 58, 120
app/GitHub/ChecksClient.php 89.1% 222, 223, 226, 227, 230... (+8 more)

🏆 Synapse Sentinel Gate

…d JSON structures

Fix pre-existing pint style issues across gate codebase (4 files).
Guard PintFormatter::run() against missing 'path' key in JSON output
since different pint versions may structure the files array differently.
@github-actions
Copy link
Copy Markdown

🔧 Synapse Sentinel: 3 checks need attention

The following issues must be resolved before this PR can be merged:


Test Failures (1 total)

Fix these failing tests:

1. CertifyCommand → handle → it handles multiple failed checks 0.19s

FAIL at Unknown file:?

⨯ CertifyCommand → handle → it displays failure table when checks fai… 0.03s  
  ✓ CertifyCommand → handle → it accepts coverage option                 0.03s  
  ⨯ CertifyCommand → handle → it returns failure when any check fails    0.04s  
  ⨯ CertifyCommand → handle → it runs all checks without stop-on-failur… 0.04s  
  ✓ CertifyCommand → handle → it writes to GITHUB_STEP_SUMMARY when ava… 0.03s  
  ✓ CertifyCommand → handle → it outputs compact format... (truncated)

Fix: Review the test expectation vs actual behavior. Check the tested code logic.


Pint Style

{"about":"PHP CS Fixer 3.90.0 Folding Bike by Fabien Potencier, Dariusz Ruminski and contributors.","files":[],"time":{"total":0},"memory":0}

Review the output and fix any issues.

Security Audit


Review the output and fix any issues.

Quick Reference:

  • PHPStan errors → Fix type mismatches first, then missing types
  • Test failures → Read the assertion message, trace expected vs actual
  • Style issues → Run composer format to auto-fix

🤖 Generated by Synapse Sentinel - View Run

@github-actions
Copy link
Copy Markdown

🔧 Synapse Sentinel: 2 checks need attention

The following issues must be resolved before this PR can be merged:


Test Failures (1 total)

Fix these failing tests:

1. CertifyCommand → handle → it returns failure when any check fails 0.12s

FAIL at Unknown file:?

✓ CertifyCommand → handle → it returns success when all checks pass    0.03s  
  ⨯ CertifyCommand → handle → it stops at first failure when stop-on-fa… 0.03s  
  ⨯ CertifyCommand → handle → it handles multiple failed checks          0.04s  
  ✓ CertifyCommand → handle → it shortens check names in compact output  0.02s  
  ✓ CertifyCommand → handle → it writes to GITHUB_STEP_SUMMARY when ava… 0.03s  
  ⨯ CertifyCommand → handle → it displays failure table w... (truncated)

Fix: Review the test expectation vs actual behavior. Check the tested code logic.


Pint Style

{"about":"PHP CS Fixer 3.94.2 7th Gear by Fabien Potencier, Dariusz Ruminski and contributors.","files":[{"name":"tests\/Unit\/Checks\/TestRunnerTest.php"},{"name":"tests\/Unit\/Checks\/PestSyntaxValidatorTest.php"},{"name":"tests\/Unit\/Checks\/SecurityScannerTest.php"},{"name":"bootstrap\/app.php"},{"name":"config\/commands.php"},{"name":"config\/app.php"},{"name":"app\/Commands\/CertifyCommand.php"},{"name":"app\/Checks\/TestRunner.php"}],"time":{"total":0},"memory":0}

Review the output and fix any issues.

Quick Reference:

  • PHPStan errors → Fix type mismatches first, then missing types
  • Test failures → Read the assertion message, trace expected vs actual
  • Style issues → Run composer format to auto-fix

🤖 Generated by Synapse Sentinel - View Run

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
app/Commands/CertifyCommand.php (1)

223-225: Compact aliases are now inconsistent across new checks.

Consider adding short names for PHPStan and Rector as well, so compact output remains uniformly concise.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Commands/CertifyCommand.php` around lines 223 - 225, Add compact aliases
for the PHPStan and Rector checks in the same mapping where 'Pint Style' =>
'Pint' and 'Publish Guard' => 'Publish' are defined: add entries like 'PHPStan'
=> 'Stan' and 'Rector' => 'Rect' (or other preferred short labels) so the
compact output remains uniformly concise; update the associative array that
performs this name mapping to include these two new key=>value pairs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/Checks/PhpStanAnalyzer.php`:
- Around line 60-63: In PhpStanAnalyzer (inside the loop that builds $details
from $data['files']), guard the message fields before concatenation: check
$msg['line'] and $msg['message'] and use safe fallbacks (e.g., $msg['line'] ?? 0
and $msg['message'] ?? '') or skip entries that lack both, then build the detail
string (currently done with basename($file).':'.$msg['line'].' —
'.$msg['message']) using those guarded values so notices are avoided; update the
foreach that populates $details accordingly.
- Around line 22-34: The run method in PhpStanAnalyzer currently always executes
PHPStan even when configured with phpstan-level=-1 and doesn't validate the
level; update PhpStanAnalyzer::run to first check if $this->level === -1 and
immediately return CheckResult::pass('PHPStan skipped via configuration') to
implement the skip behavior, then validate that $this->level is an integer
within the allowed range (e.g., minimum and maximum allowed PHPStan levels
configured for your project) and return a CheckResult::fail with a clear message
if it's out of range before constructing and running the process; only then
build the $binary command and call $this->processRunner->run as before.

In `@app/Checks/PublishGuard.php`:
- Around line 21-63: The PublishGuard::run currently checks for .env, .map and
large files but lacks secret/artifact detection; update run to also scan tracked
files for common secret filenames/extensions (e.g., files ending with .key,
.pem, .crt, .p12, .jks, .pfx or names like id_rsa, private_key, secret, token)
and to search file contents for high-confidence secret patterns (e.g., base64
API keys, AWS keys, private key headers like "-----BEGIN PRIVATE KEY-----", or
strings like "API_KEY", "SECRET=", "PRIVATE_KEY"). Use the existing git ls-files
result ($lsResult->output) to iterate tracked files, skip binaries by size/type
if needed, read files safely (check file_exists and filesize) and add violations
to $violations with clear messages (e.g., "Tracked secret file: {file}" or
"Secret pattern in: {file}"). Ensure this logic is placed alongside the
large-file loop in PublishGuard::run so secrets are reported together.
- Around line 26-54: The checks call $this->processRunner->run (producing
$envResult, $mapResult, $lsResult) and then parse ->output without verifying the
command succeeded; if git fails the guard can incorrectly pass. After each run()
call (for $envResult, $mapResult and $lsResult) check the returned result for
failure (e.g. non-zero exit code or an isSuccessful()/successful flag) and if it
failed, treat it as a violation (or throw) to "fail closed" — add a violation
entry like "git ls-files failed: {stderr or exit code}" or throw a
PublishGuardException so the publish is blocked; ensure you reference
$envResult->errorOutput / $mapResult->errorOutput / $lsResult->errorOutput (or
->exitCode) when constructing the message.

In `@app/Checks/RectorAnalyzer.php`:
- Around line 57-59: The failure message in RectorAnalyzer uses
max($changeCount, 1) which forces a minimum of 1 even when no diffs were found;
change the logic in the return call that invokes CheckResult::fail to use the
actual $changeCount (not max(...,1)) and interpolate that value into the message
and pluralization, and ensure the array_slice($details, 0, 20) remains
unchanged; this will ensure the reported file count reflects the real number of
diffs found.

---

Nitpick comments:
In `@app/Commands/CertifyCommand.php`:
- Around line 223-225: Add compact aliases for the PHPStan and Rector checks in
the same mapping where 'Pint Style' => 'Pint' and 'Publish Guard' => 'Publish'
are defined: add entries like 'PHPStan' => 'Stan' and 'Rector' => 'Rect' (or
other preferred short labels) so the compact output remains uniformly concise;
update the associative array that performs this name mapping to include these
two new key=>value pairs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cb4e8e5c-74a0-41d8-90f8-400761c074b9

📥 Commits

Reviewing files that changed from the base of the PR and between 5691a60 and b3116c8.

📒 Files selected for processing (10)
  • action.yml
  • app/Checks/PhpStanAnalyzer.php
  • app/Checks/PintFormatter.php
  • app/Checks/PublishGuard.php
  • app/Checks/RectorAnalyzer.php
  • app/Commands/CertifyCommand.php
  • app/GitHub/ChecksClient.php
  • app/Services/PromptAssembler.php
  • app/Transformers/PhpStanPromptTransformer.php
  • app/Transformers/TestFailurePromptTransformer.php

Comment on lines +22 to +34
public function run(string $workingDirectory): CheckResult
{
$binary = $workingDirectory.'/vendor/bin/phpstan';

if (! file_exists($binary)) {
return CheckResult::pass('PHPStan not installed — skipped');
}

$result = $this->processRunner->run(
[$binary, 'analyse', '--no-progress', '--error-format=json', "--level={$this->level}", '--memory-limit=512M'],
$workingDirectory,
timeout: 300,
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

phpstan-level=-1 skip behavior is not implemented.

The analyzer always runs and forwards the level, so a configured skip value still invokes PHPStan. Add an explicit skip branch and validate allowed range before execution.

Suggested fix
     public function run(string $workingDirectory): CheckResult
     {
+        if ($this->level === -1) {
+            return CheckResult::pass('PHPStan skipped (level -1)');
+        }
+
+        if ($this->level < 0 || $this->level > 9) {
+            return CheckResult::fail(
+                "Invalid PHPStan level: {$this->level}. Expected -1 or 0-9."
+            );
+        }
+
         $binary = $workingDirectory.'/vendor/bin/phpstan';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function run(string $workingDirectory): CheckResult
{
$binary = $workingDirectory.'/vendor/bin/phpstan';
if (! file_exists($binary)) {
return CheckResult::pass('PHPStan not installed — skipped');
}
$result = $this->processRunner->run(
[$binary, 'analyse', '--no-progress', '--error-format=json', "--level={$this->level}", '--memory-limit=512M'],
$workingDirectory,
timeout: 300,
);
public function run(string $workingDirectory): CheckResult
{
if ($this->level === -1) {
return CheckResult::pass('PHPStan skipped (level -1)');
}
if ($this->level < 0 || $this->level > 9) {
return CheckResult::fail(
"Invalid PHPStan level: {$this->level}. Expected -1 or 0-9."
);
}
$binary = $workingDirectory.'/vendor/bin/phpstan';
if (! file_exists($binary)) {
return CheckResult::pass('PHPStan not installed — skipped');
}
$result = $this->processRunner->run(
[$binary, 'analyse', '--no-progress', '--error-format=json', "--level={$this->level}", '--memory-limit=512M'],
$workingDirectory,
timeout: 300,
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Checks/PhpStanAnalyzer.php` around lines 22 - 34, The run method in
PhpStanAnalyzer currently always executes PHPStan even when configured with
phpstan-level=-1 and doesn't validate the level; update PhpStanAnalyzer::run to
first check if $this->level === -1 and immediately return
CheckResult::pass('PHPStan skipped via configuration') to implement the skip
behavior, then validate that $this->level is an integer within the allowed range
(e.g., minimum and maximum allowed PHPStan levels configured for your project)
and return a CheckResult::fail with a clear message if it's out of range before
constructing and running the process; only then build the $binary command and
call $this->processRunner->run as before.

Comment on lines +60 to +63
foreach ($data['files'] ?? [] as $file => $fileData) {
foreach ($fileData['messages'] ?? [] as $msg) {
$details[] = basename($file).':'.$msg['line'].' — '.$msg['message'];
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard message fields before building detail lines.

Line 62 assumes line and message always exist. Use fallbacks to avoid notices on unexpected payloads.

Suggested fix
         foreach ($data['files'] ?? [] as $file => $fileData) {
             foreach ($fileData['messages'] ?? [] as $msg) {
-                $details[] = basename($file).':'.$msg['line'].' — '.$msg['message'];
+                $line = $msg['line'] ?? '?';
+                $message = $msg['message'] ?? 'Unknown PHPStan error';
+                $details[] = basename($file).':'.$line.' — '.$message;
             }
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
foreach ($data['files'] ?? [] as $file => $fileData) {
foreach ($fileData['messages'] ?? [] as $msg) {
$details[] = basename($file).':'.$msg['line'].''.$msg['message'];
}
foreach ($data['files'] ?? [] as $file => $fileData) {
foreach ($fileData['messages'] ?? [] as $msg) {
$line = $msg['line'] ?? '?';
$message = $msg['message'] ?? 'Unknown PHPStan error';
$details[] = basename($file).':'.$line.''.$message;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Checks/PhpStanAnalyzer.php` around lines 60 - 63, In PhpStanAnalyzer
(inside the loop that builds $details from $data['files']), guard the message
fields before concatenation: check $msg['line'] and $msg['message'] and use safe
fallbacks (e.g., $msg['line'] ?? 0 and $msg['message'] ?? '') or skip entries
that lack both, then build the detail string (currently done with
basename($file).':'.$msg['line'].' — '.$msg['message']) using those guarded
values so notices are avoided; update the foreach that populates $details
accordingly.

Comment on lines +21 to +63
public function run(string $workingDirectory): CheckResult
{
$violations = [];

// Check for .env files (excluding .env.testing and .env.example)
$envResult = $this->processRunner->run(
['git', 'ls-files', '*.env', '.env*'],
$workingDirectory,
timeout: 10,
);

foreach (array_filter(explode("\n", trim($envResult->output))) as $file) {
if (! str_ends_with($file, '.env.testing') && ! str_ends_with($file, '.env.example')) {
$violations[] = "Tracked .env file: {$file}";
}
}

// Check for source map files
$mapResult = $this->processRunner->run(
['git', 'ls-files', '*.map'],
$workingDirectory,
timeout: 10,
);

foreach (array_filter(explode("\n", trim($mapResult->output))) as $file) {
$violations[] = "Tracked .map file: {$file}";
}

// Check for large files (> 1MB)
$lsResult = $this->processRunner->run(
['git', 'ls-files'],
$workingDirectory,
timeout: 10,
);

foreach (array_filter(explode("\n", trim($lsResult->output))) as $file) {
$path = $workingDirectory.'/'.$file;
if (file_exists($path) && filesize($path) > 1_048_576) {
$size = round(filesize($path) / 1_048_576, 1);
$violations[] = "Large file ({$size}MB): {$file}";
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Secrets check is missing from Publish Guard.

This implementation enforces .env, .map, and size checks, but there is no explicit secret-artifact detection (e.g., key/cert files), which is part of the stated quality gate scope.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Checks/PublishGuard.php` around lines 21 - 63, The PublishGuard::run
currently checks for .env, .map and large files but lacks secret/artifact
detection; update run to also scan tracked files for common secret
filenames/extensions (e.g., files ending with .key, .pem, .crt, .p12, .jks, .pfx
or names like id_rsa, private_key, secret, token) and to search file contents
for high-confidence secret patterns (e.g., base64 API keys, AWS keys, private
key headers like "-----BEGIN PRIVATE KEY-----", or strings like "API_KEY",
"SECRET=", "PRIVATE_KEY"). Use the existing git ls-files result
($lsResult->output) to iterate tracked files, skip binaries by size/type if
needed, read files safely (check file_exists and filesize) and add violations to
$violations with clear messages (e.g., "Tracked secret file: {file}" or "Secret
pattern in: {file}"). Ensure this logic is placed alongside the large-file loop
in PublishGuard::run so secrets are reported together.

Comment on lines +26 to +54
$envResult = $this->processRunner->run(
['git', 'ls-files', '*.env', '.env*'],
$workingDirectory,
timeout: 10,
);

foreach (array_filter(explode("\n", trim($envResult->output))) as $file) {
if (! str_ends_with($file, '.env.testing') && ! str_ends_with($file, '.env.example')) {
$violations[] = "Tracked .env file: {$file}";
}
}

// Check for source map files
$mapResult = $this->processRunner->run(
['git', 'ls-files', '*.map'],
$workingDirectory,
timeout: 10,
);

foreach (array_filter(explode("\n", trim($mapResult->output))) as $file) {
$violations[] = "Tracked .map file: {$file}";
}

// Check for large files (> 1MB)
$lsResult = $this->processRunner->run(
['git', 'ls-files'],
$workingDirectory,
timeout: 10,
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fail closed when git ls-files fails.

Lines 26–54 parse command output without checking command success. A failed git call can bypass the guard and incorrectly report pass.

Suggested fix
         $envResult = $this->processRunner->run(
             ['git', 'ls-files', '*.env', '.env*'],
             $workingDirectory,
             timeout: 10,
         );
+        if (! $envResult->successful) {
+            return CheckResult::fail(
+                'Publish Guard failed to inspect tracked .env files',
+                [trim($envResult->output)],
+                $envResult->output,
+            );
+        }
@@
         $mapResult = $this->processRunner->run(
             ['git', 'ls-files', '*.map'],
             $workingDirectory,
             timeout: 10,
         );
+        if (! $mapResult->successful) {
+            return CheckResult::fail(
+                'Publish Guard failed to inspect tracked .map files',
+                [trim($mapResult->output)],
+                $mapResult->output,
+            );
+        }
@@
         $lsResult = $this->processRunner->run(
             ['git', 'ls-files'],
             $workingDirectory,
             timeout: 10,
         );
+        if (! $lsResult->successful) {
+            return CheckResult::fail(
+                'Publish Guard failed to inspect tracked files',
+                [trim($lsResult->output)],
+                $lsResult->output,
+            );
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Checks/PublishGuard.php` around lines 26 - 54, The checks call
$this->processRunner->run (producing $envResult, $mapResult, $lsResult) and then
parse ->output without verifying the command succeeded; if git fails the guard
can incorrectly pass. After each run() call (for $envResult, $mapResult and
$lsResult) check the returned result for failure (e.g. non-zero exit code or an
isSuccessful()/successful flag) and if it failed, treat it as a violation (or
throw) to "fail closed" — add a violation entry like "git ls-files failed:
{stderr or exit code}" or throw a PublishGuardException so the publish is
blocked; ensure you reference $envResult->errorOutput / $mapResult->errorOutput
/ $lsResult->errorOutput (or ->exitCode) when constructing the message.

Comment on lines +57 to +59
return CheckResult::fail(
'Rector found '.max($changeCount, 1).' file(s) needing refactoring',
array_slice($details, 0, 20),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Failure count/message can be incorrect when no diffs are detected.

Lines 57–59 force at least 1 changed file even when parsing finds zero diff markers, which can misreport tooling/config failures as refactoring suggestions.

Suggested fix
-        return CheckResult::fail(
-            'Rector found '.max($changeCount, 1).' file(s) needing refactoring',
-            array_slice($details, 0, 20),
-            $result->output,
-        );
+        $message = $changeCount > 0
+            ? "Rector found {$changeCount} file(s) needing refactoring"
+            : 'Rector failed to run cleanly; see raw output';
+
+        return CheckResult::fail(
+            $message,
+            array_slice($details, 0, 20),
+            $result->output,
+        );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return CheckResult::fail(
'Rector found '.max($changeCount, 1).' file(s) needing refactoring',
array_slice($details, 0, 20),
$message = $changeCount > 0
? "Rector found {$changeCount} file(s) needing refactoring"
: 'Rector failed to run cleanly; see raw output';
return CheckResult::fail(
$message,
array_slice($details, 0, 20),
$result->output,
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Checks/RectorAnalyzer.php` around lines 57 - 59, The failure message in
RectorAnalyzer uses max($changeCount, 1) which forces a minimum of 1 even when
no diffs were found; change the logic in the return call that invokes
CheckResult::fail to use the actual $changeCount (not max(...,1)) and
interpolate that value into the message and pluralization, and ensure the
array_slice($details, 0, 20) remains unchanged; this will ensure the reported
file count reflects the real number of diffs found.

@github-actions
Copy link
Copy Markdown

🔧 Synapse Sentinel: 2 checks need attention

The following issues must be resolved before this PR can be merged:


All tests passed.---

Pint Style

{"about":"PHP CS Fixer 3.94.2 7th Gear by Fabien Potencier, Dariusz Ruminski and contributors.","files":[{"name":"tests\/Unit\/Checks\/TestRunnerTest.php"},{"name":"tests\/Unit\/Checks\/PestSyntaxValidatorTest.php"},{"name":"tests\/Unit\/Checks\/SecurityScannerTest.php"},{"name":"bootstrap\/app.php"},{"name":"config\/commands.php"},{"name":"config\/app.php"},{"name":"app\/Commands\/CertifyCommand.php"},{"name":"app\/Checks\/TestRunner.php"}],"time":{"total":0},"memory":0}

Review the output and fix any issues.

Quick Reference:

  • PHPStan errors → Fix type mismatches first, then missing types
  • Test failures → Read the assertion message, trace expected vs actual
  • Style issues → Run composer format to auto-fix

🤖 Generated by Synapse Sentinel - View Run

@github-actions
Copy link
Copy Markdown

🔧 Synapse Sentinel: 2 checks need attention

The following issues must be resolved before this PR can be merged:


All tests passed.---

Pint Style

{"about":"PHP CS Fixer 3.94.2 7th Gear by Fabien Potencier, Dariusz Ruminski and contributors.","files":[],"time":{"total":0},"memory":0}

Review the output and fix any issues.

Quick Reference:

  • PHPStan errors → Fix type mismatches first, then missing types
  • Test failures → Read the assertion message, trace expected vs actual
  • Style issues → Run composer format to auto-fix

🤖 Generated by Synapse Sentinel - View Run

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/Unit/Commands/CertifyCommandTest.php (1)

59-67: ⚠️ Potential issue | 🟠 Major

Actionable-prompt path is still not exercised in these failure tests.

You added an extra mocked Response(201) for actionable prompt, but these cases instantiate ChecksClient without a PR number. postActionablePrompt() short-circuits when PR number is null, so the new mocked response is unused and the actionable-post flow remains untested.

Suggested fix
- $checksClient = new ChecksClient('token', $httpClient, 'owner/repo', 'sha123');
+ $checksClient = new ChecksClient('token', $httpClient, 'owner/repo', 'sha123', 1);

Apply this to each failure-path test updated with the extra actionable prompt response.

Also applies to: 81-88, 166-174, 226-233, 253-261, 316-324

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/Unit/Commands/CertifyCommandTest.php` around lines 59 - 67, The tests
add an extra mocked Response(201) intended for the actionable-prompt path but
instantiate ChecksClient without a PR number so postActionablePrompt()
short-circuits and never uses that mock; update the test instances that include
the extra mock to construct ChecksClient with a PR number (e.g., pass
'owner/repo', 'sha123', and a PR number) so postActionablePrompt() runs and
consumes the actionable-prompt Response(201); apply the same change to the other
affected tests mentioned (lines ~81-88, 166-174, 226-233, 253-261, 316-324) to
exercise the actionable-post flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@tests/Unit/Commands/CertifyCommandTest.php`:
- Around line 59-67: The tests add an extra mocked Response(201) intended for
the actionable-prompt path but instantiate ChecksClient without a PR number so
postActionablePrompt() short-circuits and never uses that mock; update the test
instances that include the extra mock to construct ChecksClient with a PR number
(e.g., pass 'owner/repo', 'sha123', and a PR number) so postActionablePrompt()
runs and consumes the actionable-prompt Response(201); apply the same change to
the other affected tests mentioned (lines ~81-88, 166-174, 226-233, 253-261,
316-324) to exercise the actionable-post flow.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 98875206-1a65-4b62-916e-ea945b535bc4

📥 Commits

Reviewing files that changed from the base of the PR and between b3116c8 and 507ef2b.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • app/Checks/TestRunner.php
  • app/Commands/CertifyCommand.php
  • bootstrap/app.php
  • config/app.php
  • config/commands.php
  • tests/Unit/Checks/PestSyntaxValidatorTest.php
  • tests/Unit/Checks/SecurityScannerTest.php
  • tests/Unit/Checks/TestRunnerTest.php
  • tests/Unit/Commands/CertifyCommandTest.php
✅ Files skipped from review due to trivial changes (6)
  • bootstrap/app.php
  • config/app.php
  • tests/Unit/Checks/PestSyntaxValidatorTest.php
  • tests/Unit/Checks/SecurityScannerTest.php
  • config/commands.php
  • app/Checks/TestRunner.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/Commands/CertifyCommand.php

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant