Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 2m6s
Implement external support desk handoff (spec 256). Created and pushed branch `256-external-support-desk-handoff`. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #301
167 lines
10 KiB
Markdown
167 lines
10 KiB
Markdown
# 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. |