backbone_support/application/service/support_ports.rs
1//! Support's one outbound port (hand-authored, user-owned) — the optional seam where a ticket becomes
2//! delivery work. Support holds only this trait + its own DTOs; a composing service wires the real
3//! backbone-project behind it. **Zero normal Cargo edge** to project — the two Tier-4 modules stay
4//! independent (brief §7), and the DTOs are the wire contract, duplicated per consumer by design.
5//! Support posts no GL and has no ledger seam; escalation just spawns a delivery Project.
6
7use serde::{Deserialize, Serialize};
8use uuid::Uuid;
9
10/// Escalate a support ticket into a delivery project (open a backbone-project Project for the customer).
11///
12/// `company_id` is the SIBLING's domain key (backbone-project is not tenant-agnostic; its
13/// Project rows carry the legacy owner-company — composition-installed tenancy, ADR-0029).
14/// Support neither stores nor fences on it: the write path sources it from the ambient org
15/// scope and fails closed when the request carries no company-anchored scope.
16#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
17pub struct ProjectFromIssue {
18 pub company_id: Uuid,
19 pub issue_id: Uuid,
20 pub customer_id: Uuid,
21 pub subject: String,
22}
23
24/// The spawned delivery project's id.
25#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
26pub struct ProjectAck {
27 pub project_id: Uuid,
28}
29
30/// The project seam — a composing service implements it over backbone-project.
31#[async_trait::async_trait]
32pub trait ProjectPort: Send + Sync {
33 async fn open_delivery_project(
34 &self,
35 req: &ProjectFromIssue,
36 ) -> Result<ProjectAck, SupportRejected>;
37}
38
39/// A downstream rejection surfaced to support.
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
41pub struct SupportRejected {
42 pub code: String,
43 pub message: String,
44}