## Summary - add the first in-app support request flow with an immutable `SupportRequest` record, canonical context builder, submission service, and generated internal reference - expose contextual support-request actions from the tenant dashboard and operation run surfaces, including audit logging and support-safe diagnostic capture rules - add Pest coverage plus the `specs/246-support-request-context` artifacts for the new support-request slice ## Testing - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/SupportRequests/OperationRunSupportRequestActionTest.php tests/Feature/SupportRequests/SupportRequestAuditTest.php tests/Feature/SupportRequests/SupportRequestAuthorizationTest.php tests/Feature/SupportRequests/TenantSupportRequestActionTest.php tests/Unit/Support/SupportRequests/SupportRequestContextBuilderTest.php tests/Unit/Support/SupportRequests/SupportRequestReferenceTest.php` ## Notes - this PR supersedes the earlier session-branch PR opened from `246-support-request-context-session-1777289015` Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #285
18 KiB
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
SupportRequestproduct 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,
OperationRunside 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:
- Primary contexts:
tenantandoperation_runonly - Required submitted fields: severity, summary, creator identity, workspace, tenant, primary context
- Severity values:
low= Lownormal= Normal (default)high= Highblocking= Blocking
- Optional submitted fields: reproduction notes, contact name, contact email, attached diagnostic snapshot when allowed
- Attachment modes:
diagnostic_snapshot_attachedwhen the creator also passessupport_diagnostics.viewcanonical_context_onlywhen the creator can create the request but cannot attach support diagnostics
- 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
- Returned reference: one immutable internal support reference shown back immediately in success feedback, formatted as
SR-<ULID>
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 underMore. - 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, andApp\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
SupportRequestContextBuilderorSupportRequestSubmissionServiceis 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:assetsremains 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.phpexport 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
SupportRequestfactory; 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
OperationRunside 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-splitif 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)
specs/246-support-request-context/
├── checklists/
│ └── requirements.md
├── spec.md
├── plan.md
└── tasks.md
Source Code (repository root)
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
SupportRequestrecord 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
SupportRequestmodel, migration, and factory as the new persisted support-intake truth - Add
support_requests.createto 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
WorkspaceAuditLoggerpath for support-request creation - Add
Request supportaction forms toTenantDashboardandTenantlessOperationRunViewer - Return the generated support reference in success feedback and keep the action synchronous and DB-only
Implementation Phases
- Foundation: migration, model, factory, capability, audit action, internal reference generation, and shared context builder
- Tenant entry point: tenant dashboard action, reduced-attachment logic, tenant-context feature proof
- Run entry point: canonical operation detail action, entitled-tenant resolution, run-context feature proof
- 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:assetswhen registered Filament assets are present elsewhere. - Browser smoke confirmed both approved entry points on the live app: the tenant dashboard visible
Request supportaction and the tenantless operation viewer groupedMore>Request supportaction both submitted successfully and returnedSupport request submittedwithReference 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 underMoreto avoid competing with its primary navigation and utility actions.