TenantAtlas/apps/platform/app/Support/Operations/LifecycleReconciliationReason.php
ahmido ce0615a9c1 Spec 182: relocate Laravel platform to apps/platform (#213)
## Summary
- move the Laravel application into `apps/platform` and keep the repository root for orchestration, docs, and tooling
- update the local command model, Sail/Docker wiring, runtime paths, and ignore rules around the new platform location
- add relocation quickstart/contracts plus focused smoke coverage for bootstrap, command model, routes, and runtime behavior

## Validation
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/PlatformRelocation`
- integrated browser smoke validated `/up`, `/`, `/admin`, `/admin/choose-workspace`, and tenant route semantics for `200`, `403`, and `404`

## Remaining Rollout Checks
- validate Dokploy build context and working-directory assumptions against the new `apps/platform` layout
- confirm web, queue, and scheduler processes all start from the expected working directory in staging/production
- verify no legacy volume mounts or asset-publish paths still point at the old root-level `public/` or `storage/` locations

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #213
2026-04-08 08:40:47 +00:00

84 lines
3.0 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Support\Operations;
use App\Support\ReasonTranslation\NextStepOption;
use App\Support\ReasonTranslation\ReasonResolutionEnvelope;
enum LifecycleReconciliationReason: string
{
case StaleQueued = 'run.stale_queued';
case StaleRunning = 'run.stale_running';
case InfrastructureTimeoutOrAbandonment = 'run.infrastructure_timeout_or_abandonment';
case QueueFailureBridge = 'run.queue_failure_bridge';
case AdapterOutOfSync = 'run.adapter_out_of_sync';
public function operatorLabel(): string
{
return match ($this) {
self::StaleQueued => 'Run never started',
self::StaleRunning => 'Run stopped reporting progress',
self::InfrastructureTimeoutOrAbandonment => 'Infrastructure ended the run',
self::QueueFailureBridge => 'Queue failure was reconciled',
self::AdapterOutOfSync => 'Lifecycle was reconciled from related records',
};
}
public function shortExplanation(): string
{
return match ($this) {
self::StaleQueued => 'The run stayed queued past its lifecycle window and was marked failed.',
self::StaleRunning => 'The run stayed active past its lifecycle window and was marked failed.',
self::InfrastructureTimeoutOrAbandonment => 'Queue infrastructure ended the job before normal completion could update the run.',
self::QueueFailureBridge => 'The platform bridged a queue failure back to the owning run and marked it failed.',
self::AdapterOutOfSync => 'A related restore record reached terminal truth before the operation run was updated.',
};
}
public function actionability(): string
{
return match ($this) {
self::AdapterOutOfSync => 'non_actionable',
default => 'retryable_transient',
};
}
/**
* @return array<int, NextStepOption>
*/
public function nextSteps(): array
{
return match ($this) {
self::AdapterOutOfSync => [
NextStepOption::instruction('Review the related restore record before deciding whether to run the workflow again.'),
],
default => [
NextStepOption::instruction('Review worker health and logs before retrying this operation.'),
],
};
}
public function defaultMessage(): string
{
return $this->shortExplanation();
}
/**
* @param array<string, mixed> $context
*/
public function toReasonResolutionEnvelope(string $surface = 'detail', array $context = []): ReasonResolutionEnvelope
{
return new ReasonResolutionEnvelope(
internalCode: $this->value,
operatorLabel: $this->operatorLabel(),
shortExplanation: $this->shortExplanation(),
actionability: $this->actionability(),
nextSteps: $this->nextSteps(),
showNoActionNeeded: false,
diagnosticCodeLabel: $this->value,
);
}
}