15 KiB
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:assetswhere 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
OperationRunusage 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.statusandOperationRun.outcomeremain service-owned. This feature only changes explanation paths. - Ops-UX summary counts: PASS WITH WORK.
SummaryCountsNormalizeralready 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)
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)
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
- 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
RunFailureSanitizerfrom 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 defines the shared reason artifact, resolution envelope, translation entry, next-step option, and adoption-target model.
- Output: 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 defines the documentation-first schema for a translation entry and its actionability constraints.
- Output: contracts/no-external-api-changes.md records that this feature is internal-contract work with no public transport API changes.
- Output: 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/Supportso 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, andRbacReason, reusing their existing semantics while standardizing their operator-facing output. - Extending provider-domain flows so
ProviderReasonCodescan be resolved through the shared contract andProviderNextStepsRegistrybecomes one domain implementation of that contract rather than the only next-step registry in the system. - Updating
OperationUxPresenterandOperationRunCompletedso 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
RunFailureSanitizerso 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 |