TenantAtlas/specs/256-external-support-desk-handoff/research.md
ahmido 7b394918ce
Some checks failed
Main Confidence / confidence (push) Failing after 1m48s
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m43s
chore(platform): merge platform-dev into dev (#302)
Integrates latest TenantPilot platform changes from `platform-dev` into `dev`.

This PR was created by agent on user request; do not merge automatically.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #302
2026-04-29 20:53:36 +00:00

10 KiB
Raw Blame History

Research — External Support Desk / PSA Handoff

Date: 2026-04-29
Spec: 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.