# Implementation Plan: In-App Support Request with Context **Branch**: `246-support-request-context` | **Date**: 2026-04-27 | **Spec**: `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/246-support-request-context/spec.md` **Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/246-support-request-context/spec.md` ## Summary - Add one bounded `SupportRequest` product truth that captures structured support intake from two existing support-aware surfaces: the tenant dashboard and the canonical operation detail viewer. - Reuse the existing support-diagnostics bundle to attach a redacted, machine-readable context envelope when the creator is allowed to view diagnostics, and fall back to a safe canonical reference set when they are not. - Keep the slice synchronous, Livewire v4-compatible, Filament v5-native, and free of external ticket adapters, `OperationRun` side effects, global-search changes, destructive actions, or asset changes. ## Technical Context **Language/Version**: PHP 8.4 (Laravel 12) **Primary Dependencies**: Laravel 12 + Filament v5 + Livewire v4 + Pest; existing `SupportDiagnosticBundleBuilder`, `WorkspaceAuditLogger`, `UiEnforcement`, `CapabilityResolver`, and canonical tenant/run support surfaces **Storage**: PostgreSQL tenant-owned `support_requests` table with required not-null `workspace_id` and `tenant_id`, plus immutable redacted context JSON **Testing**: Pest unit + feature tests **Validation Lanes**: fast-feedback, confidence **Target Platform**: Sail-backed Laravel admin panel under `/admin` **Project Type**: web **Performance Goals**: create the support request synchronously inside ordinary admin-request latency with no outbound HTTP and no background jobs **Constraints**: no external ticketing provider, no support inbox or resource, no new system panel, no raw payload persistence, no `OperationRun`, and no asset changes **Scale/Scope**: one migration, one model and factory, one bounded support-request context builder or submission service, one generated reference path, two header actions, and focused unit plus feature proof only ## First-Slice Request Contract The first slice is locked to the following request shape: 1. **Primary contexts**: `tenant` and `operation_run` only 2. **Required submitted fields**: severity, summary, creator identity, workspace, tenant, primary context 3. **Severity values**: - `low` = Low - `normal` = Normal (default) - `high` = High - `blocking` = Blocking 4. **Optional submitted fields**: reproduction notes, contact name, contact email, attached diagnostic snapshot when allowed 5. **Attachment modes**: - `diagnostic_snapshot_attached` when the creator also passes `support_diagnostics.view` - `canonical_context_only` when the creator can create the request but cannot attach support diagnostics 6. **Duplicate submissions**: repeated submits are intentionally allowed in v1 and must always create a fresh support request row plus a fresh internal support reference with no hidden merge or dedupe behavior 7. **Returned reference**: one immutable internal support reference shown back immediately in success feedback, formatted as `SR-` Any support inbox, lifecycle state machine, external ticket reference, file upload, or customer-facing support portal is deferred by design. ## UI / Surface Guardrail Plan - **Guardrail scope**: changed surfaces - **Native vs custom classification summary**: native Filament + shared support primitives - **Shared-family relevance**: header actions, support capture, support diagnostics, audit feedback - **State layers in scope**: page, detail, action form - **Audience modes in scope**: operator-MSP, support-platform - **Decision/diagnostic/raw hierarchy plan**: decision-first form, diagnostics-second, support-raw omitted from persistence - **Raw/support gating plan**: capability-gated attachment, raw payloads never persisted - **One-primary-action / duplicate-truth control**: the request form keeps one dominant action, `Submit support request`, and reuses the support-diagnostics bundle summary instead of introducing local case language. On the operation detail page, both support actions (`Open support diagnostics`, `Request support`) stay grouped in secondary placement under `More`. - **Handling modes by drift class or surface**: review-mandatory - **Repository-signal treatment**: review-mandatory - **Special surface test profiles**: standard-native-filament, monitoring-state-page - **Required tests or manual smoke**: functional-core, state-contract - **Exception path and spread control**: existing tenant dashboard action-surface exemption remains bounded to the two support-aware actions only - **Active feature PR close-out entry**: Guardrail ## Shared Pattern & System Fit - **Cross-cutting feature marker**: yes - **Systems touched**: `App\Filament\Pages\TenantDashboard`, `App\Filament\Pages\Operations\TenantlessOperationRunViewer`, `App\Support\SupportDiagnostics\SupportDiagnosticBundleBuilder`, `App\Support\Auth\Capabilities`, `App\Services\Auth\RoleCapabilityMap`, and `App\Services\Audit\WorkspaceAuditLogger` - **Shared abstractions reused**: support-diagnostics bundle composition, existing capability gating through `UiEnforcement`, existing audit-log writing, and the current support-aware tenant and run action surfaces - **New abstraction introduced? why?**: one bounded `SupportRequestContextBuilder` or `SupportRequestSubmissionService` is justified because the slice needs one canonical place to shape immutable request context and reference generation without duplicating tenant/run logic - **Why the existing abstraction was sufficient or insufficient**: the existing abstractions are sufficient for safe context and audit patterns, but insufficient for persisted support-request truth and immutable reference generation - **Bounded deviation / spread control**: no ticket-provider adapter, no support queue, no page-local context builders, and no second support-summary vocabulary ## OperationRun UX Impact - **Touches OperationRun start/completion/link UX?**: no - **Central contract reused**: N/A - **Delegated UX behaviors**: N/A - **Surface-owned behavior kept local**: the run-context action reads the current run only as request context - **Queued DB-notification policy**: N/A - **Terminal notification path**: N/A - **Exception path**: none ## Provider Boundary & Portability Fit - **Shared provider/platform boundary touched?**: yes - **Provider-owned seams**: translated provider reasons and provider-connection state already carried by the support-diagnostics bundle - **Platform-core seams**: support request record, support reference, severity, primary context labels, attachment mode - **Neutral platform terms / contracts preserved**: support request, support reference, attached context, redacted diagnostic snapshot, canonical reference set - **Retained provider-specific semantics and why**: provider-specific reasons remain inside the redacted support-diagnostics bundle because they are already modeled as provider-owned evidence - **Bounded extraction or follow-up path**: external ticketing remains a follow-up spec and must consume the neutral support-request truth instead of reshaping provider-specific evidence inline ## Constitution Check *GATE: Must pass before implementation begins. Re-check after design changes.* - Inventory-first / snapshots-second: PASS - the request stores immutable submitted context and does not replace canonical diagnostic truth - Read/write separation: PASS - the action form is the pre-submit preview surface, creation is explicit, and the write is audited and tested - Graph contract path: PASS - no new Graph calls are introduced - Deterministic capabilities: PASS - capability derivation stays in the canonical registry and role map - RBAC-UX / workspace isolation / tenant isolation: PASS - the feature remains on `/admin`, keeps non-member and non-entitled access as 404, and uses tenant-safe action surfaces only - Global search rule: PASS - no resource or global-search change is introduced - Run observability / Ops UX: PASS - request creation is DB-only and intentionally creates no `OperationRun` - Proportionality / `PROP-001`, `ABSTR-001`, `PERSIST-001`, `STATE-001`, `BLOAT-001`: PASS - one persisted request truth and one small severity family are justified by structured support intake and no narrower path preserves immutable request context - Shared pattern reuse / `XCUT-001`: PASS - support-diagnostics and audit seams are reused explicitly - Provider boundary / `PROV-001`: PASS - provider-specific semantics stay inside the existing redacted bundle only - Filament-native UI / `UI-FIL-001`: PASS - the slice uses native Filament action forms only - Livewire v4 / Filament v5 compliance: PASS - the plan stays entirely within the current Filament v5 + Livewire v4 stack - Provider registration location: PASS - no provider registration changes are introduced; Laravel 11+ provider registration remains in `bootstrap/providers.php` - Destructive actions: PASS - none added, so no `->requiresConfirmation()` path is introduced here - Asset strategy: PASS - no new assets are required, so deployment behavior for `cd apps/platform && php artisan filament:assets` remains unchanged - Test governance / `TEST-GOV-001`: PASS - proof remains in narrow unit plus feature lanes only ## Test Governance Check - **Test purpose / classification by changed surface**: Unit for context-shape and internal-reference rules; Feature for tenant and run action behavior, authorization, and audit - **Affected validation lanes**: fast-feedback, confidence - **Why this lane mix is the narrowest sufficient proof**: the feature is server-driven, synchronous, and action-form based; browser automation would only duplicate behavior already provable through unit and Filament feature tests - **Narrowest proving command(s)**: - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/SupportRequests/SupportRequestContextBuilderTest.php tests/Unit/Support/SupportRequests/SupportRequestReferenceTest.php` - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/SupportRequests/TenantSupportRequestActionTest.php tests/Feature/SupportRequests/OperationRunSupportRequestActionTest.php tests/Feature/SupportRequests/SupportRequestAuthorizationTest.php tests/Feature/SupportRequests/SupportRequestAuditTest.php` - **Fixture / helper / factory / seed / context cost risks**: add one `SupportRequest` factory; reuse existing workspace, tenant, run, provider, finding, report, review, and audit fixtures; keep support-request helpers local to the feature namespace - **Expensive defaults or shared helper growth introduced?**: no - **Heavy-family additions, promotions, or visibility changes**: none - **Surface-class relief / special coverage rule**: standard-native-filament relief applies to the tenant action; the operation detail action keeps the monitoring-state-page contract already established on the viewer - **Closing validation and reviewer handoff**: reviewers should re-run the listed unit and feature commands, verify immutable context persistence, verify 404 versus 403 boundaries, and verify that no outbound HTTP or `OperationRun` side effect occurs - **Budget / baseline / trend follow-up**: none expected beyond ordinary feature-local upkeep - **Review-stop questions**: did implementation add an outbound adapter, a request status workflow, a support inbox, or raw diagnostic persistence; did it broaden entry points beyond the two approved surfaces? - **Escalation path**: `reject-or-split` if the slice expands into external sync, a support queue, or lifecycle management - **Active feature PR close-out entry**: Guardrail - **Why no dedicated follow-up spec is needed**: the planned cost stays local to support-request creation; only external ticketing or request-lifecycle expansion would justify a follow-up spec ## Project Structure ### Documentation (this feature) ```text specs/246-support-request-context/ ├── checklists/ │ └── requirements.md ├── spec.md ├── plan.md └── tasks.md ``` ### Source Code (repository root) ```text apps/platform/ ├── app/ │ ├── Filament/ │ │ └── Pages/ │ │ ├── Operations/ │ │ │ └── TenantlessOperationRunViewer.php │ │ └── TenantDashboard.php │ ├── Models/ │ │ └── SupportRequest.php │ ├── Services/ │ │ ├── Audit/WorkspaceAuditLogger.php │ │ └── Auth/RoleCapabilityMap.php │ └── Support/ │ ├── Audit/AuditActionId.php │ ├── Auth/Capabilities.php │ ├── SupportDiagnostics/SupportDiagnosticBundleBuilder.php │ └── SupportRequests/ │ ├── SupportRequestContextBuilder.php │ └── SupportRequestReferenceGenerator.php ├── database/ │ ├── factories/SupportRequestFactory.php │ └── migrations/ └── tests/ ├── Feature/SupportRequests/ │ ├── OperationRunSupportRequestActionTest.php │ ├── SupportRequestAuditTest.php │ ├── SupportRequestAuthorizationTest.php │ └── TenantSupportRequestActionTest.php └── Unit/Support/SupportRequests/ ├── SupportRequestContextBuilderTest.php └── SupportRequestReferenceTest.php ``` **Structure Decision**: Single Laravel application. The implementation adds one bounded support-request model plus one small support namespace and reuses existing tenant and run surfaces. ## Complexity Tracking No additional constitution violations are required. The new persisted support-request truth and small severity family are already justified in the proportionality review and remain bounded to the first slice. ## Proportionality Review - **Current operator problem**: support intake still starts outside structured product context even though the product can already explain the current tenant or run problem safely - **Existing structure is insufficient because**: support diagnostics is read-only and cannot provide an immutable, auditable support request or internal support reference - **Narrowest correct implementation**: one immutable `SupportRequest` record plus one bounded context builder that reuses existing support diagnostics when allowed - **Ownership cost**: migration, model, factory, capability, audit action, bounded support-request support namespace, and focused tests - **Alternative intentionally rejected**: outbound-only ticket adapters, a multi-provider helpdesk registry, and a request status workflow were rejected because no concrete second use case or provider foundation exists yet - **Why this is current-release truth**: the support-request record is directly useful now even without external sync because it preserves structured intake, returns a support reference, and captures immutable context ## Rollout & Risk Controls - Start on exactly two existing surfaces only: the tenant dashboard and the canonical operation detail viewer - Keep request truth immutable after creation; lifecycle management is explicitly out of scope - Persist only redacted context JSON; raw payloads, secrets, and unrestricted provider bodies remain excluded - Keep the reference internal-only in v1; external ticket references are deferred until a later ticketing spec exists - Reuse the existing support-diagnostics capability when attaching diagnostics so the feature can safely support reduced attachment mode without creating a second diagnostic access path ## Implementation Outline - Add the `SupportRequest` model, migration, and factory as the new persisted support-intake truth - Add `support_requests.create` to the canonical capability registry and role map - Add a bounded support-request context builder plus internal reference generator that can build tenant-context and run-context envelopes from the existing support-diagnostics bundle and canonical references - Add an audit action identifier and `WorkspaceAuditLogger` path for support-request creation - Add `Request support` action forms to `TenantDashboard` and `TenantlessOperationRunViewer` - Return the generated support reference in success feedback and keep the action synchronous and DB-only ## Implementation Phases 1. **Foundation**: migration, model, factory, capability, audit action, internal reference generation, and shared context builder 2. **Tenant entry point**: tenant dashboard action, reduced-attachment logic, tenant-context feature proof 3. **Run entry point**: canonical operation detail action, entitled-tenant resolution, run-context feature proof 4. **Safety hardening**: immutable context persistence, authorization edge cases, audit proof, and no-side-effect verification ## Guardrail Close-Out - Livewire v4 compliance remained unchanged and the feature shipped through native Filament v5 action forms only. - Provider registration location stayed unchanged; Laravel 11+ provider registration remains in `bootstrap/providers.php`. - No global-search behavior changed because no resource or global-search surface was introduced. - No destructive action was added, so no new confirmation flow was required. - No asset strategy changed; deployment remains unchanged, including `cd apps/platform && php artisan filament:assets` when registered Filament assets are present elsewhere. - Browser smoke confirmed both approved entry points on the live app: the tenant dashboard visible `Request support` action and the tenantless operation viewer grouped `More` > `Request support` action both submitted successfully and returned `Support request submitted` with `Reference SR-...`. - The tenant dashboard action-surface exception stayed bounded to the existing support-aware pair (`Request support`, `Open support diagnostics`), while the operation viewer kept both support actions grouped under `More` to avoid competing with its primary navigation and utility actions.