TenantAtlas/specs/256-external-support-desk-handoff/data-model.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

6.6 KiB
Raw Blame History

Data Model — External Support Desk / PSA Handoff

Spec: spec.md

Spec 256 extends the existing support-request truth. No new support-ticket table, resource, or queue artifact is introduced.

Existing Canonical Entity Extended

SupportRequest (support_requests)

Purpose: Canonical tenant-owned support-request truth. Spec 256 extends it so the same row can carry one-way external handoff continuity.

Existing key fields (already in repo):

  • id
  • workspace_id
  • tenant_id
  • operation_run_id
  • initiated_by_user_id
  • internal_reference
  • primary_context_type
  • attachment_mode
  • severity
  • summary
  • reproduction_notes
  • contact_name
  • contact_email
  • context_envelope
  • created_at
  • updated_at

New fields (planned):

  • external_handoff_mode
    • type: string
    • required: yes
    • default: internal_only
    • allowed values:
      • internal_only
      • create_external_ticket
      • link_existing_ticket
  • external_ticket_reference
    • type: nullable string
    • stored when an external ticket was created or linked successfully
  • external_ticket_url
    • type: nullable text
    • stored only when the target returns or the operator provides a valid URL
  • external_handoff_failure_summary
    • type: nullable text
    • bounded human-readable failure summary for the current request only

Relationships (unchanged):

  • belongs to Workspace
  • belongs to Tenant
  • optionally belongs to OperationRun
  • optionally belongs to initiator User

Behavioral rules:

  • internal_reference remains the canonical TenantPilot support identifier even when an external ticket exists.
  • external_handoff_mode records the operators chosen path and replaces the need for a second persisted status family.
  • Spec 256 explicitly narrows Spec 246 immutability in one bounded way: after the internal request is created, the same row may receive exactly one synchronous finalization write limited to external_handoff_mode, external_ticket_reference, external_ticket_url, and external_handoff_failure_summary. No later edit, reopen, merge, or status workflow is introduced.
  • external_ticket_reference and external_ticket_url remain null for internal_only and for failed create attempts.
  • external_handoff_failure_summary remains null on successful create, successful link, and internal-only submissions.
  • On a failed external create, the row persists with:
    • external_handoff_mode = create_external_ticket
    • external_ticket_reference = null
    • external_ticket_url = null
    • external_handoff_failure_summary populated
  • When the failed external create was caused by timeout, external_handoff_failure_summary stores the same bounded timeout-oriented message that the UI and audit path use. Raw transport detail is never persisted.

Latest-summary query rules:

  • Tenant dashboard summary queries the latest support request for the current entitled tenant where primary_context_type = tenant.
  • Operation-run summary queries the latest support request for the current run where primary_context_type = operation_run and operation_run_id matches the viewed run.
  • Existing indexes on (tenant_id, created_at) and (operation_run_id, created_at) are sufficient. No new lookup path by external reference is planned.

Validation rules:

  • external_handoff_mode must be one of the three allowed values.
  • external_ticket_reference is required when external_handoff_mode = link_existing_ticket.
  • external_ticket_url is optional but must be a valid URL when present.
  • When no external target is configured for the application, the form must force or constrain the effective mode to internal_only.

Application-Configured External Target (Config Contract In Scope, Not New Persisted Truth)

External Support Desk Target

Purpose: Supplies the one configured outbound target for create or link normalization.

Status in Spec 256:

  • minimal application config contract in scope
  • not a new persisted entity in this slice
  • not a workspace settings domain or UI surface in this slice

Repo-grounded note:

  • The repo has no existing support settings domain, so Spec 256 makes the target seam explicit through one application config file: apps/platform/config/support_desk.php with environment-backed values for the single supported target.
  • This config contract may define availability, create endpoint settings, reference-link normalization defaults, and the five-second outbound timeout budget.
  • Per-workspace target selection, settings UI, or a second target remain follow-up scope.

Derived Runtime Entities

SupportRequestHandoffOutcome (computed, not persisted)

Purpose: Gives the Filament page actions one normalized outcome for notification copy and tests after submission completes.

Expected shape:

  • support_request_id
  • internal_reference
  • primary_context_type
  • handoff_mode
  • handoff_outcome
    • internal_only
    • external_ticket_created
    • external_ticket_linked
    • external_handoff_failed
  • external_ticket_reference
  • external_ticket_url
  • failure_summary

Why derived only:

  • The outcome is an execution summary for one request cycle.
  • Persisting it separately would duplicate the support-request truth and audit log.
  • The bounded synchronous finalization write on SupportRequest remains the only allowed post-create mutation for this slice.

LatestSupportRequestHandoffSummary (computed, not persisted)

Purpose: Supplies the existing tenant and run support actions with one scoped summary of the latest linkage for the current primary context.

Expected shape:

  • internal_reference
  • primary_context_type
  • primary_context_id
  • submitted_at
  • external_handoff_mode
  • external_ticket_reference
  • external_ticket_url
  • external_handoff_failure_summary
  • has_external_link
  • has_failure

Why derived only:

  • It is a read model over the latest support_requests row for one context.
  • A separate table or persisted summary would violate PERSIST-001 without solving a distinct lifecycle problem.

Audit Events (Persistent Audit Truth, Not Product Truth)

The implementation should add these stable audit actions in addition to the existing support_request.created event:

  • support_request.external_ticket_created
  • support_request.external_ticket_linked
  • support_request.external_handoff_failed

Audit context should include:

  • workspace_id
  • tenant_id
  • internal_reference
  • primary_context_type
  • primary_context_id
  • external_handoff_mode
  • external_ticket_reference when present

Audit context should not include:

  • raw provider request payloads
  • secrets or credentials
  • unrestricted provider response bodies