TenantAtlas/specs/256-external-support-desk-handoff/research.md
ahmido 52ebf63af1
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 2m6s
feat(specs/256): external support desk handoff (#301)
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
2026-04-29 20:16:40 +00:00

167 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 specs 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 specs 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.