update project

This commit is contained in:
root
2026-05-30 01:11:35 -04:00
parent 3a0628ecc7
commit 2225f6bc72
9743 changed files with 1122482 additions and 59 deletions
@@ -0,0 +1,51 @@
<?php declare(strict_types=1);
namespace App\Console\Commands;
use App\Support\Architecture\ArchitectureScanner;
use Illuminate\Console\Command;
final class ArchitectureScanCommand extends Command
{
protected $signature = 'app:architecture-scan {--format=console : console or json} {--fail-on=error : error, warning, or none}';
protected $description = 'Scan the codebase for modular architecture boundary violations.';
public function handle(ArchitectureScanner $scanner): int
{
$violations = $scanner->scan();
if ($this->option('format') === 'json') {
$this->line(json_encode(array_map(fn ($v) => $v->toArray(), $violations), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
} else {
if ($violations === []) {
$this->info('No architecture violations found. Suspiciously civilized.');
}
foreach ($violations as $violation) {
$this->line(sprintf(
'[%s] %s %s:%d %s',
strtoupper($violation->severity),
$violation->ruleId,
$violation->file,
$violation->line,
$violation->message
));
if ($violation->suggestedFix) {
$this->line(' Fix: '.$violation->suggestedFix);
}
}
}
$failOn = (string) $this->option('fail-on');
if ($failOn === 'none') {
return self::SUCCESS;
}
foreach ($violations as $violation) {
if ($violation->severity === 'error' || ($failOn === 'warning' && $violation->severity === 'warning')) {
return self::FAILURE;
}
}
return self::SUCCESS;
}
}
@@ -0,0 +1,36 @@
<?php declare(strict_types=1);
namespace App\Console\Commands;
use App\Support\Architecture\DependencyMapGenerator;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
final class DependencyMapCommand extends Command
{
protected $signature = 'app:dependency-map {--markdown : Also write docs/architecture/dependency-map.md}';
protected $description = 'Generate a module dependency map for governance review.';
public function handle(DependencyMapGenerator $generator): int
{
$map = $generator->generate();
Storage::disk('local')->put('generated/dependency-map.json', json_encode($map, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
if ($this->option('markdown')) {
$lines = ['# Dependency Map', '', 'Generated from PHP imports. Treat this as a smoke alarm, not a philosophical essay.', ''];
foreach ($map['files'] as $file => $imports) {
$lines[] = '## `'.$file.'`';
$lines[] = '';
foreach ($imports as $import) {
$lines[] = '- `'.$import.'`';
}
$lines[] = '';
}
@mkdir(base_path('docs/architecture'), 0777, true);
file_put_contents(base_path('docs/architecture/dependency-map.md'), implode(PHP_EOL, $lines).PHP_EOL);
}
$this->info('Dependency map generated. Now the imports have fewer places to hide.');
return self::SUCCESS;
}
}
@@ -0,0 +1,45 @@
<?php declare(strict_types=1);
namespace App\Console\Commands;
use Illuminate\Console\Command;
final class DocumentationCoverageCommand extends Command
{
protected $signature = 'app:docs-coverage {--inventory=storage/app/generated/api-route-inventory.json} {--openapi=docs/openapi.phase8.yaml}';
protected $description = 'Check that canonical routes and deprecated routes are represented in documentation artifacts.';
public function handle(): int
{
$inventoryPath = base_path((string) $this->option('inventory'));
$openApiPath = base_path((string) $this->option('openapi'));
$deprecationsPath = base_path('docs/deprecations.md');
if (! is_file($inventoryPath) || ! is_file($openApiPath)) {
$this->error('Inventory or OpenAPI file is missing. Documentation cannot be trusted by vibes alone.');
return self::FAILURE;
}
$rows = json_decode(file_get_contents($inventoryPath) ?: '[]', true) ?: [];
$openApi = file_get_contents($openApiPath) ?: '';
$deprecations = is_file($deprecationsPath) ? (file_get_contents($deprecationsPath) ?: '') : '';
$failures = [];
foreach ($rows as $row) {
$uri = '/'.ltrim((string) ($row['uri'] ?? ''), '/');
$status = (string) ($row['canonical_status'] ?? 'unknown');
if ($status === 'canonical' && ! str_contains($openApi, $uri)) {
$failures[] = $uri.' is canonical but missing from OpenAPI.';
}
if ($status === 'deprecated' && ! str_contains($deprecations, $uri)) {
$failures[] = $uri.' is deprecated but missing from docs/deprecations.md.';
}
}
foreach ($failures as $failure) {
$this->error($failure);
}
return $failures === [] ? self::SUCCESS : self::FAILURE;
}
}
@@ -0,0 +1,58 @@
<?php declare(strict_types=1);
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Routing\Route;
use Illuminate\Support\Facades\Route as RouteFacade;
use Illuminate\Support\Facades\Storage;
final class GenerateApiRouteInventoryCommand extends Command
{
protected $signature = 'api:route-inventory {--markdown : Also write docs/api-route-inventory.md}';
protected $description = 'Generate API route inventory for controller cleanup and OpenAPI coverage.';
public function handle(): int
{
$classification = config('api_controller_classification.controllers', []);
$rows = collect(RouteFacade::getRoutes())
->filter(fn (Route $route) => str_starts_with($route->uri(), 'api/'))
->map(function (Route $route) use ($classification): array {
$action = $route->getActionName();
[$controller, $method] = str_contains($action, '@') ? explode('@', $action, 2) : [$action, '__invoke'];
$meta = $classification[$controller] ?? [];
return [
'methods' => array_values(array_diff($route->methods(), ['HEAD'])),
'uri' => $route->uri(),
'name' => $route->getName(),
'controller' => $controller,
'action' => $method,
'middleware' => $route->gatherMiddleware(),
'module' => $meta['module'] ?? 'unknown',
'risk' => $meta['risk'] ?? 'unknown',
'canonical_status' => $meta['status'] ?? config('api_controller_classification.default_status', 'unknown'),
'contract' => $meta['contract'] ?? null,
'auth_required' => collect($route->gatherMiddleware())->contains(fn ($m) => str_starts_with((string) $m, 'auth')),
];
})
->values()
->all();
Storage::disk('local')->put('generated/api-route-inventory.json', json_encode($rows, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
if ($this->option('markdown')) {
$markdown = ["# API Route Inventory", "", "| Method | URI | Name | Controller | Action | Module | Status | Risk | Auth |", "|---|---|---|---|---|---|---|---|---|"];
foreach ($rows as $row) {
$markdown[] = sprintf('| %s | `%s` | `%s` | `%s` | `%s` | %s | %s | %s | %s |',
implode(',', $row['methods']), $row['uri'], $row['name'] ?? '', $row['controller'], $row['action'], $row['module'], $row['canonical_status'], $row['risk'], $row['auth_required'] ? 'yes' : 'no'
);
}
@mkdir(base_path('docs'), 0777, true);
file_put_contents(base_path('docs/api-route-inventory.md'), implode(PHP_EOL, $markdown).PHP_EOL);
}
$this->info('API route inventory written. Unknown routes are now visible, the bare minimum for civilization.');
return self::SUCCESS;
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
final class LegacyUnsafeRouteAuditCommand extends Command
{
protected $signature = 'app:legacy-unsafe-route-audit';
protected $description = 'Audit known unsafe legacy routes that must be disabled or removed before final rollout.';
public function handle(): int
{
$items = config('release_readiness.unsafe_legacy_routes', []);
$report = [
'generated_at' => now()->toIso8601String(),
'items' => array_map(fn (string $key): array => [
'key' => $key,
'status' => 'requires_project_mapping',
'note' => 'Map this key to actual legacy route names in the real app and disable/remove before production readiness signoff.',
], $items),
];
$path = storage_path('app/generated/legacy-unsafe-route-audit.json');
File::ensureDirectoryExists(dirname($path));
File::put($path, json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
$this->line(json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
return self::SUCCESS;
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Console\Commands;
use App\Support\Release\FeatureFlagRegistry;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
final class ReleaseFeatureFlagsCommand extends Command
{
protected $signature = 'app:release-feature-flags {--format=json}';
protected $description = 'Export modular rollout feature flag map.';
public function handle(FeatureFlagRegistry $registry): int
{
$data = ['flags' => $registry->export()];
$path = storage_path('app/generated/release-feature-flags.json');
File::ensureDirectoryExists(dirname($path));
File::put($path, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
$this->info('Feature flag rollout map written to '.$path);
$this->line(json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
return self::SUCCESS;
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\Console\Commands;
use App\Support\Release\MigrationValidationRunner;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
final class ReleaseMigrationValidateCommand extends Command
{
protected $signature = 'app:release-migration-validate {--module=all}';
protected $description = 'Run modular migration validation checks.';
public function handle(MigrationValidationRunner $runner): int
{
$results = array_map(fn ($result): array => $result->toArray(), $runner->runAll());
$summary = [
'generated_at' => now()->toIso8601String(),
'results' => $results,
'status' => collect($results)->every(fn ($result): bool => $result['status'] === 'pass') ? 'pass' : 'fail',
];
$path = storage_path('app/generated/release-validation/validation-summary.json');
File::ensureDirectoryExists(dirname($path));
File::put($path, json_encode($summary, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
$this->info('Migration validation summary written to '.$path);
$this->line(json_encode($summary, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
return $summary['status'] === 'pass' ? self::SUCCESS : self::FAILURE;
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Console\Commands;
use App\Support\Release\ReleaseReadinessGate;
use Illuminate\Console\Command;
final class ReleaseReadinessGateCommand extends Command
{
protected $signature = 'app:release-readiness-gate {--warning-mode}';
protected $description = 'Evaluate modular release readiness artifacts and gates.';
public function handle(ReleaseReadinessGate $gate): int
{
$result = $gate->evaluate();
$this->line(json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
if ($result['status'] !== 'pass') {
$this->warn('Release readiness gate failed. Fix artifacts or run in warning mode only for early rollout rehearsals.');
return $this->option('warning-mode') ? self::SUCCESS : self::FAILURE;
}
$this->info('Release readiness gate passed. Disturbingly competent.');
return self::SUCCESS;
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\Console\Commands;
use App\Support\Release\ShadowComparisonRunner;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
final class ReleaseShadowCompareCommand extends Command
{
protected $signature = 'app:release-shadow-compare {--module=all}';
protected $description = 'Run shadow comparisons between legacy and modular outputs.';
public function handle(ShadowComparisonRunner $runner): int
{
$results = array_map(fn ($result): array => $result->toArray(), $runner->runAll());
$summary = [
'generated_at' => now()->toIso8601String(),
'results' => $results,
'status' => collect($results)->contains(fn ($result): bool => $result['status'] === 'fail') ? 'fail' : 'warning',
];
$path = storage_path('app/generated/shadow-comparisons/shadow-summary.json');
File::ensureDirectoryExists(dirname($path));
File::put($path, json_encode($summary, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
$this->warn('Shadow comparisons are scaffolded. Wire legacy comparisons before production cutover.');
$this->line(json_encode($summary, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
return self::SUCCESS;
}
}
@@ -0,0 +1,43 @@
<?php declare(strict_types=1);
namespace App\Console\Commands;
use Illuminate\Console\Command;
final class RouteInventoryCheckCommand extends Command
{
protected $signature = 'app:route-inventory-check {--path=storage/app/generated/api-route-inventory.json}';
protected $description = 'Check generated route inventory for governance metadata gaps.';
public function handle(): int
{
$path = base_path((string) $this->option('path'));
if (! is_file($path)) {
$this->error('Route inventory missing. Run php artisan api:route-inventory first.');
return self::FAILURE;
}
$rows = json_decode(file_get_contents($path) ?: '[]', true) ?: [];
$failures = [];
foreach ($rows as $row) {
$uri = (string) ($row['uri'] ?? '');
$methods = $row['methods'] ?? [];
$mutation = count(array_intersect($methods, ['POST', 'PUT', 'PATCH', 'DELETE'])) > 0;
if (($row['module'] ?? 'unknown') === 'unknown') {
$failures[] = $uri.' is missing module classification.';
}
if ($mutation && empty($row['auth_required'])) {
$failures[] = $uri.' is a mutation route without auth metadata.';
}
if (str_contains($uri, 'islamic-sunday-school') && ! str_contains(json_encode($row['middleware'] ?? []), 'domain.profile')) {
$failures[] = $uri.' is an extension route without domain profile middleware.';
}
}
foreach ($failures as $failure) {
$this->error($failure);
}
return $failures === [] ? self::SUCCESS : self::FAILURE;
}
}