# Research — External Support Desk / PSA Handoff

**Date**: 2026-04-29  
**Spec**: [spec.md](spec.md)

This document records the repo-grounded decisions that make the Spec 256 plan implementation-ready without expanding into a generic helpdesk product.

## Decision 1 — Extend `support_requests` instead of adding a second support-ticket truth

**Decision**: Keep `App\Models\SupportRequest` as the only persisted truth for this slice and add the external linkage fields directly to `support_requests`.

**Rationale**:
- The repo already has one canonical support-request model, migration, factory, and submission service.
- The operator workflow needs one durable record that still carries the internal `SR-...` reference after create, link, or failure.
- Constitution `PERSIST-001` and `PROP-001` reject a second lifecycle unless it solves a distinct product problem. Spec 256 does not need one.

**Evidence**:
- Existing model: `apps/platform/app/Models/SupportRequest.php`
- Existing persistence: `apps/platform/database/migrations/2026_04_27_095518_create_support_requests_table.php`
- Existing write path: `apps/platform/app/Support/SupportRequests/SupportRequestSubmissionService.php`
- Candidate scope: `docs/product/spec-candidates.md`

**Alternatives considered**:
- Add a new `SupportTicket` or `SupportRequestLink` model.
  - Rejected: creates a second truth and encourages a support register or detail page the spec explicitly forbids.
- Keep external linkage entirely derived from audit logs.
  - Rejected: the current support context must show the latest linkage on revisit, which audit-only storage cannot do safely or cheaply.

## Decision 2 — Persist a bounded failure summary on the same row; keep detailed provider failure out of product truth

**Decision**: Store `external_handoff_failure_summary` on `support_requests` and keep detailed provider payloads or raw errors out of persisted support-request truth.

**Rationale**:
- The spec requires explicit, revisitable failure handling in the same support context.
- A purely audited failure would satisfy compliance but fail the operator need to reopen the action and see what happened.
- A bounded human-readable summary is enough for revisit. Provider-specific payloads remain provider-owned and redaction-sensitive.

**Evidence**:
- Existing audit path is already separate from product truth: `apps/platform/app/Services/Audit/WorkspaceAuditLogger.php`
- Current support-request row has no external linkage or failure fields, so the revisit contract is impossible without row-level extension.

**Alternatives considered**:
- Audit failure only.
  - Rejected: failure becomes invisible in the current support context.
- Persist raw provider response JSON.
  - Rejected: violates the spec’s minimal neutral truth and increases leakage risk.

## Decision 3 — Keep the flow synchronous, preserve internal durability, and document the one bounded finalization write

**Decision**: Preserve the existing synchronous submit flow, move any external create call outside the current internal-request creation transaction, enforce a five-second outbound timeout, and explicitly allow one bounded post-create finalization write on the same `SupportRequest` row.

**Rationale**:
- The current service wraps internal create plus audit in a transaction.
- Spec 256 explicitly requires the internal support request to survive external create failure.
- Spec 246 declared the row immutable after creation, so Spec 256 must make its one bounded finalization exception explicit instead of mutating the row silently.
- Holding a database transaction open across remote HTTP is unnecessary and increases latency and failure risk.
- A hard timeout budget is needed so the operator-visible submit path stays bounded and timeout behavior is testable.
- The repo truth does not require `OperationRun`, queueing, or retry scheduling for this slice.

**Evidence**:
- Existing transaction structure in `SupportRequestSubmissionService`
- Existing synchronous page actions on `TenantDashboard` and `TenantlessOperationRunViewer`
- The spec’s explicit non-goal for queues, retries, and `OperationRun`
- Spec 246 FR-246-011 immutability contract

**Alternatives considered**:
- Perform external HTTP inside the current DB transaction.
  - Rejected: risks long transactions and makes internal request durability harder to guarantee.
- Introduce queue work or `OperationRun`.
  - Rejected: broader than current-release truth and not required for one synchronous target.
- Keep Spec 246 immutability unchanged and infer final handoff state only from audit.
  - Rejected: the current support context must show revisitable success or failure on the canonical `SupportRequest`, so the one bounded finalization write has to be explicit.

## Decision 4 — Keep external linkage visibility inside the existing support request actions only

**Decision**: Show the latest linkage summary inside the existing `Request support` slide-overs and in submit feedback. Do not add a new support page, dashboard card, or run-detail history section.

**Rationale**:
- The spec says visibility must stay attached to the existing tenant and run support contexts.
- The acceptance criteria require reopening the action and seeing the latest linkage summary for that same context.
- A broader always-visible history surface would deepen support-product scope and duplicate truth.

**Evidence**:
- Existing support-aware surfaces: `apps/platform/app/Filament/Pages/TenantDashboard.php` and `apps/platform/app/Filament/Pages/Operations/TenantlessOperationRunViewer.php`
- No existing `SupportRequest` resource, list, or detail page exists in the repo today.

**Alternatives considered**:
- Add a support-request resource or detail page.
  - Rejected: explicitly out of scope.
- Add a new page-level widget or card for support linkage.
  - Rejected: broader than the acceptance requirement and would create duplicate visible truth.

## Decision 5 — Add one minimal application config contract; do not hide target resolution behind an undefined prerequisite

**Decision**: Bring one minimal application config contract into scope through `apps/platform/config/support_desk.php` and environment-backed values for the single supported target. Do not add workspace settings UI, a support settings domain, or provider-connection product work.

**Rationale**:
- The repo has no existing `support` settings domain, so leaving target resolution as an external prerequisite would create hidden implementation work.
- The product contract only needs one target for v1, so an application config contract is the narrowest explicit source of truth.
- Pulling workspace administration into Spec 256 would still expand scope from handoff to setup and administration.

**Evidence**:
- Existing repo truth has no support-target config seam yet, so a new app config file is the explicit minimal source of truth for one target.

**Alternatives considered**:
- Add a new `support` settings domain and UI in the same spec.
  - Rejected: becomes a second feature slice.
- Reuse `ProviderConnection` as the support target model.
  - Rejected: not justified by current repo truth for one external desk handoff target.
- Leave target resolution as an undefined prerequisite.
  - Rejected: the tasks and plan already depend on a concrete resolution seam, so the config contract must be explicit inside the package.

## Decision 6 — Use one concrete provider-owned handoff service, not a registry or interface framework

**Decision**: Add one concrete provider-owned handoff service under the support-request path for the single real external target.

**Rationale**:
- Both existing support surfaces need the same create-or-normalize behavior.
- Constitution `ABSTR-001` rejects a provider registry or interface framework before two real targets exist.
- Page-local HTTP logic would duplicate failure handling, normalization, and audit shape.

**Evidence**:
- One configured target only in the spec and roadmap candidate
- Existing shared write path already centralizes support-request submission across both surfaces

**Alternatives considered**:
- Add a provider interface plus registry.
  - Rejected: future-proofing without current-release variance.
- Duplicate HTTP logic inside both Filament pages.
  - Rejected: immediate drift risk and weaker audit consistency.

## Decision 7 — Keep queries context-scoped and avoid new search or indexing semantics

**Decision**: Derive the latest visible linkage from the latest support request for the same primary context, using the existing context indexes. Do not add cross-scope lookup or search by external ticket reference.

**Rationale**:
- Tenant summary and run summary have different scope rules in the spec.
- Existing indexes already support latest-by-tenant and latest-by-run queries.
- Cross-scope lookup by external reference is explicitly out of scope and would create a new leakage risk.

**Evidence**:
- Existing indexes on `support_requests(tenant_id, created_at)` and `support_requests(operation_run_id, created_at)`
- Context scoping in `SupportRequest::PRIMARY_CONTEXT_TENANT` and `SupportRequest::PRIMARY_CONTEXT_OPERATION_RUN`

**Alternatives considered**:
- Add an index and lookup flow for external ticket reference.
  - Rejected: no current surface needs it, and it would conflict with the no-cross-scope-shortcuts rule.

## Decision 8 — Proof stays in Unit + Feature lanes with manual smoke only

**Decision**: Keep the proving strategy in focused Pest unit and feature suites, then use a narrow manual smoke path after implementation.

**Rationale**:
- Business truth is server-side: branching, persistence, audit, and authorization.
- Existing support-request tests already cover the same two Filament entry surfaces.
- Browser coverage would mostly duplicate the existing action-form semantics.

**Evidence**:
- Existing test family:
  - `apps/platform/tests/Feature/SupportRequests/TenantSupportRequestActionTest.php`
  - `apps/platform/tests/Feature/SupportRequests/OperationRunSupportRequestActionTest.php`
  - `apps/platform/tests/Feature/SupportRequests/SupportRequestAuthorizationTest.php`
  - `apps/platform/tests/Feature/SupportRequests/SupportRequestAuditTest.php`

**Alternatives considered**:
- Add browser tests in the first slice.
  - Rejected: not required to prove the current business truth.