TenantAtlas/specs/157-reason-code-translation/plan.md
ahmido 92f39d9749 feat: add shared reason translation contract (#187)
## Summary
- introduce a shared reason-translation contract with envelopes, presenter helpers, fallback handling, and provider translation support
- adopt translated operator-facing reason presentation across operation runs, notifications, provider guidance, tenant operability, and RBAC-related surfaces
- add Spec 157 design artifacts and targeted regression coverage for translation quality, diagnostics retention, and authorization-safe guidance

## Validation
- `vendor/bin/sail bin pint --dirty --format agent`
- `vendor/bin/sail artisan test --compact tests/Architecture/ReasonTranslationPrimarySurfaceGuardTest.php tests/Unit/Support/ReasonTranslation/ReasonResolutionEnvelopeTest.php tests/Unit/Support/ReasonTranslation/ExecutionDenialReasonTranslationTest.php tests/Unit/Support/ReasonTranslation/TenantOperabilityReasonTranslationTest.php tests/Unit/Support/ReasonTranslation/RbacReasonTranslationTest.php tests/Unit/Support/ReasonTranslation/ProviderReasonTranslationTest.php tests/Feature/Notifications/OperationRunNotificationTest.php tests/Feature/Operations/OperationRunBlockedExecutionPresentationTest.php tests/Feature/Operations/TenantlessOperationRunViewerTest.php tests/Feature/ReasonTranslation/GovernanceReasonPresentationTest.php tests/Feature/Authorization/ReasonTranslationScopeSafetyTest.php tests/Feature/Monitoring/OperationRunBlockedSpec081Test.php tests/Feature/ProviderConnections/ProviderOperationBlockedGuidanceSpec081Test.php tests/Feature/ProviderConnections/ProviderGatewayRuntimeSmokeSpec081Test.php`

## Notes
- Livewire v4.0+ compliance remains unchanged within the existing Filament v5 stack.
- No new panel was added; provider registration remains in `bootstrap/providers.php`.
- No new globally searchable resource was introduced.
- No new destructive action family was introduced.
- No new assets were added; the existing `filament:assets` deployment behavior remains unchanged.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #187
2026-03-22 20:19:43 +00:00

175 lines
15 KiB
Markdown

# Implementation Plan: Operator Reason Code Translation and Humanization Contract
**Branch**: `157-reason-code-translation` | **Date**: 2026-03-22 | **Spec**: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/157-reason-code-translation/spec.md`
**Input**: Feature specification from `/specs/157-reason-code-translation/spec.md`
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts.
## Summary
Define one shared reason translation contract that converts stable internal reason codes into operator-facing labels, explanations, retryability classes, and next-step guidance. The implementation should preserve existing backend reason-code precision, avoid new business-domain storage, and adopt the contract first where the repo already centralizes reason-bearing UX: operations notifications and run detail, provider next-step flows, tenant-operability governance, and adopted system-console RBAC or onboarding health surfaces. Existing heuristic string matching in `RunFailureSanitizer` should shrink from being the primary explanation path on adopted surfaces to being a bounded fallback only.
## Technical Context
**Language/Version**: PHP 8.4.15
**Primary Dependencies**: Laravel 12, Filament v5, Livewire v4, PostgreSQL, Laravel Sail, Pest v4
**Storage**: PostgreSQL-backed existing records such as `operation_runs`, tenant governance records, onboarding workflow state, and provider connection state; no new business-domain table is required for the first slice
**Testing**: Pest feature tests, unit tests for reason-translation contracts and fallback behavior, existing architecture or guard-style tests, focused notification and Filament surface assertions
**Target Platform**: Laravel web application running locally via Sail and deployed via Dokploy
**Project Type**: Web application
**Performance Goals**: No render-time external calls; reason translation must remain constant-time and fit inside current presenter and badge paths; Monitoring and canonical views remain DB-only at render time; notification generation must not add query-heavy joins beyond existing run and tenant lookups
**Constraints**: Preserve stable internal reason-code contracts, preserve RBAC 404 versus 403 semantics, preserve existing Ops-UX lifecycle and notification rules, avoid page-local ad-hoc translation helpers, and keep the first slice bounded to central adoption seams rather than full-repo migration
**Scale/Scope**: Cross-domain contract foundation plus bounded first-slice adoption across operations and notifications, provider blocking and next-steps, tenant-operability governance, and adopted system-console RBAC or onboarding reason surfaces
### Filament v5 Implementation Notes
- **Livewire v4.0+ compliance**: Maintained. This work changes operator-facing explanation behavior inside the existing Filament v5 + Livewire v4 stack and introduces no incompatible Livewire pattern.
- **Provider registration location**: No new panel is introduced. Existing panel providers remain registered in `bootstrap/providers.php`.
- **Global search rule**: No new globally searchable resource is added. Existing reason-bearing labels and next-step hints must remain non-member-safe on canonical and tenant-context views.
- **Destructive actions**: No new destructive action family is introduced. Existing destructive actions on adopted surfaces remain confirmation-protected and capability-gated.
- **Asset strategy**: No new global or on-demand assets are planned. Deployment behavior remains unchanged, including `php artisan filament:assets` where already required.
- **Testing plan**: Add Pest coverage for reason translation envelopes, fallback behavior, notification wording, adopted surface rendering, and authorization-safe next-step guidance.
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
- Inventory-first: PASS. The feature changes explanation semantics only and does not alter inventory versus snapshot ownership or capture paths.
- Read/write separation: PASS. No new mutation flow is introduced. Existing write flows remain governed by their owning specs.
- Graph contract path: PASS. No new Graph path or contract registry change is required.
- Deterministic capabilities: PASS. Capability derivation remains unchanged and continues to gate adopted surfaces server-side.
- RBAC-UX plane separation: PASS. The feature spans tenant/admin and system surfaces while preserving 404 for non-members and 403 for in-scope capability denial.
- Workspace isolation: PASS. Humanized reason labels, summaries, and next-step hints must be derived only from entitled workspace scope.
- Tenant isolation: PASS. Shared reason wording must not leak unauthorized tenant state in canonical views, filters, summaries, or notifications.
- Destructive confirmation standard: PASS. No destructive semantics are changed.
- Global search tenant safety: PASS WITH WORK. Shared translated labels and next-step hints must remain non-member-safe; focused regression coverage is required.
- Run observability: PASS. Existing `OperationRun` usage remains canonical. This feature only changes how reasons are translated and displayed.
- Ops-UX 3-surface feedback: PASS WITH WORK. Adopted operation notifications and run details must stay within the existing toast, progress, and terminal notification contract.
- Ops-UX lifecycle: PASS. `OperationRun.status` and `OperationRun.outcome` remain service-owned. This feature only changes explanation paths.
- Ops-UX summary counts: PASS WITH WORK. `SummaryCountsNormalizer` already humanizes keys, but the first slice must improve reason-bearing summary language without changing the numeric contract.
- Ops-UX guards: PASS WITH WORK. Add guard coverage so adopted surfaces cannot fall back to raw or heuristic-only operator reason strings without an approved translation path.
- Ops-UX system runs: PASS. No change to initiator-null notification semantics.
- Automation: PASS. No queue, lock, or retry mechanism is changed by the contract itself.
- Data minimization: PASS. This feature reduces raw reason exposure on primary operator surfaces by pushing internal codes into diagnostics.
- Badge semantics (BADGE-001): PASS WITH WORK. Where translated reasons affect status-like wording, they must align with the existing outcome taxonomy and centralized badge semantics.
- UI naming (UI-NAMING-001): PASS WITH WORK. Shared reason labels become part of the operator-facing vocabulary and must stay consistent across run detail, notifications, banners, and guidance text.
- Operator surfaces (OPSURF-001): PASS WITH WORK. Default-visible content on adopted surfaces must show label, explanation, and next step while relegating raw codes and payload fragments to diagnostics.
- Filament UI Action Surface Contract: PASS. Existing action surfaces remain structurally unchanged; the rollout changes explanation copy and guidance only.
- Filament UI UX-001: PASS. No new screen category is introduced. Adopted surfaces keep their current layout while improving the operator-first information hierarchy.
**Phase 0 Gate Result**: PASS
- The feature is bounded to a shared translation contract plus a first-slice adoption set, not a full domain rewrite.
- Existing presenter, registry, and notification seams provide a practical implementation path.
- The main delivery risk is inconsistency between translated labels and raw-code fallbacks, which is addressable through contract tests and guard coverage.
## Project Structure
### Documentation (this feature)
```text
specs/157-reason-code-translation/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ ├── no-external-api-changes.md
│ ├── reason-resolution.logical.openapi.yaml
│ └── reason-translation-entry.schema.json
└── tasks.md
```
### Source Code (repository root)
```text
app/
├── Filament/
│ ├── Resources/
│ └── Widgets/
├── Notifications/
├── Services/
│ ├── Operations/
│ ├── Providers/
│ ├── Tenants/
│ ├── Intune/
│ └── Verification/
├── Support/
│ ├── Baselines/
│ ├── Operations/
│ ├── OpsUx/
│ ├── Providers/
│ └── Tenants/
└── Models/
tests/
├── Feature/
├── Unit/
└── Architecture/
```
**Structure Decision**: Use the existing Laravel web application structure. The documentation artifacts live under `specs/157-reason-code-translation/`, while the planned implementation targets concentrate in `app/Support/OpsUx`, `app/Support/Providers`, `app/Support/Operations`, `app/Support/Tenants`, `app/Notifications`, `app/Filament/Resources`, and focused Pest suites under `tests/Unit`, `tests/Feature`, and existing guard-oriented directories.
## Phase 0 — Research (complete)
- Output: [specs/157-reason-code-translation/research.md](research.md)
- Resolved key decisions:
- Preserve stable internal reason codes and translate them through a shared resolution envelope rather than renaming backend contracts.
- Use existing central seams as first-slice adoption points: `OperationUxPresenter`, `OperationRunCompleted`, `ProviderNextStepsRegistry`, and enum-backed reason families that already model reason semantics.
- Reduce `RunFailureSanitizer` from being the primary operator explanation path on adopted surfaces to being a bounded fallback or normalization seam only.
- Prefer domain-owned translations behind one common contract shape over a monolithic page-local formatting pattern.
- First-slice rollout order is contract foundation, enum-backed families, provider next-step flows, then operations notifications and run-detail wording.
## Phase 1 — Design & Contracts (complete)
- Output: [data-model.md](./data-model.md) defines the shared reason artifact, resolution envelope, translation entry, next-step option, and adoption-target model.
- Output: [contracts/reason-resolution.logical.openapi.yaml](./contracts/reason-resolution.logical.openapi.yaml) captures the logical request and response contract for resolving a raw reason into operator-facing presentation.
- Output: [contracts/reason-translation-entry.schema.json](./contracts/reason-translation-entry.schema.json) defines the documentation-first schema for a translation entry and its actionability constraints.
- Output: [contracts/no-external-api-changes.md](./contracts/no-external-api-changes.md) records that this feature is internal-contract work with no public transport API changes.
- Output: [quickstart.md](./quickstart.md) documents the recommended rollout order and focused validation commands.
### Post-design Constitution Re-check
- PASS: No new panel, Graph path, route family, or business-domain storage is introduced.
- PASS: The design preserves Filament v5 + Livewire v4 and keeps provider registration unchanged in `bootstrap/providers.php`.
- PASS WITH WORK: Operations notifications, run detail, and summary wording need focused validation so translated labels improve clarity without violating the existing Ops-UX contract.
- PASS WITH WORK: Canonical and tenant-context views need explicit non-member regression coverage because translated next-step hints and summary labels are part of the leak surface.
- PASS WITH WORK: Fallback behavior must remain understandable by meeting a minimum label, explanation, and action-guidance floor, and it must not become a loophole that reintroduces raw-code-as-primary-message patterns.
## Phase 2 — Implementation Planning
`tasks.md` should cover:
- Defining the shared reason resolution envelope and domain-facing translation contract in `app/Support` so adopted reason families return the same minimum shape: label, explanation, actionability class, and next-step guidance when applicable.
- Implementing the first enum-backed adoption slice for `ExecutionDenialReasonCode`, `TenantOperabilityReasonCode`, and `RbacReason`, reusing their existing semantics while standardizing their operator-facing output.
- Extending provider-domain flows so `ProviderReasonCodes` can be resolved through the shared contract and `ProviderNextStepsRegistry` becomes one domain implementation of that contract rather than the only next-step registry in the system.
- Updating `OperationUxPresenter` and `OperationRunCompleted` so terminal notifications and run detail wording consume translated reason envelopes instead of raw or heuristically sanitized fragments.
- Updating adopted summary and banner paths so humanized labels remain operator-first while raw internal reason codes stay available in diagnostics.
- Reducing adopted uses of heuristic string matching in `RunFailureSanitizer` so it no longer acts as the primary operator explanation path on first-slice surfaces.
- Adding unit and feature tests for translation envelopes, fallback behavior, retryability or actionability classes, entitlement-safe next-step guidance, and adopted notification wording.
- Adding guard coverage that fails when adopted surfaces expose raw internal reason codes as the primary operator-facing message or drift away from the canonical operator vocabulary for blocked, missing, denied, stale, unsupported, partial, and retry states.
### Contract Implementation Note
- The OpenAPI file is logical, not transport-prescriptive. It documents how existing presenters, notifications, badge mappers, and Filament surfaces should resolve raw reason state into operator-facing output.
- The JSON schema is documentation-first and guard-friendly. It can be enforced through fixtures, curated translation registries, or unit tests rather than a new runtime parser in the first slice.
- The no-external-api note makes the boundary explicit: this feature standardizes internal explanation behavior and transport payload composition, not external REST endpoints.
### Deployment Sequencing Note
- No migration is expected in the first slice.
- No asset publish change is expected.
- Recommended rollout order: shared contract foundation, enum-backed adoption slice, provider next-step slice, operations notification and run-detail slice, then broader domain adoption only after guards are green.
### Story Delivery Note
- User Story 1 and User Story 2 are both P1. The executable delivery order should start with User Story 2's contract and diagnostic-boundary requirements because preserving backend precision is the precondition for safe translation.
- User Story 1 follows immediately through operations and provider-facing wording because those are the highest-leverage operator surfaces.
- User Story 3 finishes the first slice by extending the common contract across additional reason families and guard coverage.
## Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| None | Not applicable | Not applicable |