GUS Integration
GUS to Linear Integration Guide
Recommended Architecture for Salesforce DMT
This document provides a recommended architecture and implementation guide for transitioning Salesforce DMT teams from GUS to Linear. It covers entity mapping, a bi-directional sync engine, and a tiered migration strategy for GUS’s extensive integration surface. This is modeled after the Groove to Linear Integration Guide and informed by patterns in Linear’s existing Jira two-way sync and Salesforce integration.
This is a recommended approach, not a final spec. The entity mappings, sync boundaries, and tier classifications represent our current best recommendation and should be discussed and iterated on with Salesforce DMT stakeholders. Discussion points are called out explicitly throughout.
Scope
In Scope:
- Recommended entity hierarchy mapping (GUS → Linear)
- Bi-directional sync engine for core agile entities (Epics, Work Items, Sprints)
- Tiered migration strategy for all GUS use cases and integrations
- Linear API reference for all sync operations
- Initial migration/bootstrap strategy
- Guidance on GUS-resident capabilities that cannot fully migrate
Out of Scope:
- GUS API implementation details (kept API-agnostic on the GUS side)
- User provisioning and license management
- Salesforce org-level administration (OrgESC, Falcon infrastructure setup)
- Custom Salesforce LWC components (covered by Linear’s existing Salesforce integration)
Decision Matrix
Quick reference for every GUS use case. This table classifies each into a migration tier, identifies the Linear equivalent, and specifies the recommended sync direction.
| GUS Use Case | Tier | Linear Equivalent | Sync Direction | Notes |
| — | — | — | — | — |
| Agile Workflows (Epics, Stories, Sprints) | 1 | Projects, Issues, Cycles | Full migration | Core of the transition |
| Backlog / Kanban | 1 | Triage + Board views | Full migration | Native Linear feature |
| Retros / Metrics / Dashboards | 1 | Linear Analytics | Full migration | Supplement with Tableau via API if needed |
| SSO | 1 | Linear SAML/SCIM | Full migration | Standard enterprise setup |
| Slack Integration | 1 | Linear Slack app | Full migration | Native integration, replaces GUS Slack agents |
| Out of Office | 1 | Cycle capacity / time-off | Full migration | Confirm: Linear’s OOO handling meets needs |
| Chatter | 1 | Linear Inbox / Notifications | Full migration | |
| Agentforce | 1 | Linear AI (Ask, auto-triage) | Full migration | Different capabilities, same intent |
| Service Accounts | 1 | Linear API keys / OAuth apps | Full migration | |
| CODEOWNERS bug routing | 2 | Issue sync + team mapping | GUS → Linear | Bugs filed in GUS sync to Linear for triage |
| Git2GUS | 2 | Linear GitHub integration | Linear ← GitHub | Replace Git2GUS; backfill to GUS if legacy consumers exist |
| Incident/Problem Management | 2 | Issue sync with incident labels | GUS → Linear | Incidents stay in GUS; work items sync to Linear |
| CI/CD Bug Filing (SFCI, Fpolicy, Skywalker) | 2 | Issue sync with bug labels | GUS → Linear | Auto-created GUS bugs sync to Linear |
| HPS / Kaiju | 2 | External links + team mapping | GUS ↔ Linear | Maintain GUS Team mapping; track work in Linear |
| Intake Requests | 2 | Issue sync | Linear → GUS | Create in Linear, sync to GUS for service teams |
| PagerDuty | 2 | Linear PagerDuty integration | Evaluate | Native integration may eliminate GUS dependency |
| Falcon/Fedx (deployments, change mgmt) | 3 | External links in Linear | GUS → Linear (read-only) | Too deeply coupled to GUS |
| Change Cases / Templates | 3 | External links in Linear | GUS → Linear (read-only) | GUS workflow engine required |
| JIT Access | 3 | External links in Linear | No sync | Requires GUS Change Case |
| Certificate Management / CS Monitor | 3 | External links in Linear | GUS → Linear (read-only) | Automation tied to GUS |
| Release Management (OrgESC) | 3 | External links in Linear | GUS → Linear (read-only) | Discuss: can Linear replace OrgESC coupling? |
| Quip Integration | – | Linear Docs / external links | N/A | Low priority (moving away from Quip) |
| Tableau Integration | 1 | Linear API → Tableau | Full migration | Query Linear API for dashboards |
Discussion Point: Tier assignments are recommendations. If Salesforce identifies use cases that should move between tiers, the sync engine design accommodates this – the patterns are the same, only the sync direction configuration changes.
GUS Hierarchy Context
GUS manages a 6-level hierarchy with distinct time horizons. Linear has 4 levels. The mapping requires collapsing two levels.
GUS Hierarchy (from Salesforce documentation)
Portfolio/Cloud (long-living)
└─ Program (1-3 years, e.g., "Finance Cloud ERP Program Initiative")
└─ Project (1 quarter - 1 year, spans multiple Program Increments)
└─ Epic (1-4 sprints, within a single Program Increment)
└─ Work Item [Story/Bug/Investigation] (1 sprint)
└─ Task (hours to days)
Recommended Mapping to Linear
| GUS Entity | Linear Entity | Rationale | Alternative Considered |
| — | — | — | — |
| Portfolio/Cloud | Workspace | 1:1 organizational boundary | – |
| Program | Initiative | Both are strategic, multi-year efforts grouping deliverables | Could use Project Labels if Initiatives feel too heavy |
| Project | Metadata on Initiative | GUS Projects group Epics across PIs; represented as external links and labels on the parent Initiative | Could map to a separate Initiative (creates nesting complexity) |
| Epic | Project | Core synced entity. Epics are collections of work items with a delivery scope – same as Linear Projects | Could map to parent Issue (loses project-level features: status, milestones, lead) |
| Work Item (Story/Bug/Investigation) | Issue | Direct mapping. Core work unit sized to a sprint | – |
| Task | Sub-issue | Small divisions of a work item | Recommend NOT syncing – see below |
| Sprint | Cycle | Direct mapping. Time-boxed iteration | – |
| Team | Team | Direct mapping | – |
| Product Tag | Label | Categorization of work items by feature area | Could use Custom Fields if labels feel too flat |
| Team Dependency (TD) | Issue Relation (blocks/blocked-by) | Cross-team dependency tracking | Could use cross-team Project membership |
| Release | Project Milestone | Planned releases tied to builds | Discuss: dedicated Release-tracking Project may be cleaner |
Discussion Point: GUS Project mapping. GUS Projects span multiple PIs and contain Epics. Since Linear Initiatives already group Projects, we recommend representing GUS Projects as metadata (external links + labels) on the parent Initiative rather than creating a separate Linear entity. This avoids a 1:1:1 Initiative→Project→Issue chain that would feel over-nested. If DMT teams need GUS Projects as first-class trackable entities, we could map them to Linear Projects and bump Epics down to parent Issues – but this sacrifices project-level features for Epics.
Recommendation: Do not sync GUS Tasks. Tasks are sub-sprint divisions (hours to days) that teams should recreate natively in Linear as sub-issues. Syncing them adds complexity with minimal value – teams will naturally break down Issues into sub-issues during sprint planning. This keeps the sync surface manageable.
Entity and Field Mapping
GUS Epic ↔ Linear Project (Core Sync)
| GUS Field | Linear Field | Direction | Notes |
| — | — | — | — |
| Epic Name | name | ↔ Bi-directional | Required |
| Description | description | ↔ Bi-directional | Markdown format |
| Status | statusId | ↔ Bi-directional | See status mapping below |
| Owner | leadId | ↔ Bi-directional | Resolve via email lookup |
| Product Tag | labelIds | ↔ Bi-directional | Map to Project Labels |
| Start Date | startDate | ↔ Bi-directional | ISO date |
| Target Date | targetDate | ↔ Bi-directional | ISO date |
| Epic ID / URL | External Link | GUS → Linear | Back-reference to GUS |
| Parent Project ID / URL | External Link | GUS → Linear | Link to parent GUS Project |
| Parent Program ID / URL | External Link | GUS → Linear | Link to parent GUS Program |
GUS Work Item ↔ Linear Issue
| GUS Field | Linear Field | Direction | Notes |
| — | — | — | — |
| Title | title | ↔ Bi-directional | Required |
| Description | description | ↔ Bi-directional | Markdown |
| Status | stateId | ↔ Bi-directional | See status mapping below |
| Assignee | assigneeId | ↔ Bi-directional | Resolve via email |
| Type (Story/Bug/Investigation) | labelIds | GUS → Linear | Map to Issue Labels |
| Priority | priority | ↔ Bi-directional | Map GUS priority scale to Linear 0-4 |
| Story Points | estimate | ↔ Bi-directional | |
| Sprint | cycleId | ↔ Bi-directional | Resolve via Sprint ↔ Cycle mapping |
| Epic | projectId | ↔ Bi-directional | Resolve via Epic ↔ Project mapping |
| Product Tag | labelIds | ↔ Bi-directional | |
| Work Item ID / URL | External Link or attachment | GUS → Linear | Back-reference |
GUS Sprint ↔ Linear Cycle
| GUS Field | Linear Field | Direction | Notes |
| — | — | — | — |
| Sprint Name | name | ↔ Bi-directional | |
| Start Date | startsAt | ↔ Bi-directional | ISO datetime |
| End Date | endsAt | ↔ Bi-directional | ISO datetime |
| Team | teamId | Setup only | Set during initial configuration |
GUS Program → Linear Initiative
| GUS Field | Linear Field | Direction | Notes |
| — | — | — | — |
| Program Name | name | GUS → Linear | One-way recommended |
| Description | description | GUS → Linear | |
| Status | statusId | GUS → Linear | |
| Program ID / URL | External Link | GUS → Linear | Back-reference |
Discussion Point: Should Initiative sync be bi-directional? Programs are long-lived strategic entities that likely continue to be managed at a level above individual teams. We recommend one-way (GUS → Linear) unless DMT teams will actively manage Program-level planning in Linear.
Status Mapping
Status mapping uses the same generalized string comparison pattern as Linear’s Jira sync – normalize, lowercase, strip non-letter characters, and match:
function generalizedStringCompare(str1: string, str2: string): boolean {
const normalize = (s: string) => s.toLowerCase().replace(/[^\p{L}]/gu, "");
return normalize(str1) === normalize(str2);
}
Recommended GUS Epic → Linear Project Status mapping:
| GUS Epic Status | Linear Project Status | Status Type |
| — | — | — |
| Backlog | Planned | planned |
| In Progress | In Progress | started |
| Completed | Completed | completed |
| Cancelled | Canceled | canceled |
Recommended GUS Work Item → Linear Issue State mapping:
| GUS Work Item Status | Linear State | State Type |
| — | — | — |
| New | Triage / Backlog | triage / backlog |
| In Progress | In Progress | started |
| Fixed / Closed | Done | completed |
| Never Fix | Canceled | canceled |
To confirm with Salesforce: The GUS status values above are placeholders based on common Agile conventions. DMT should provide the actual status values per entity type so we can finalize these mapping tables.
For implementation, we recommend the category-based approach used in Linear’s Jira sync: map GUS status categories to Linear state types first, then match by name within that type. This is more resilient than exact name matching.
const gusStatusCategoryToLinearStateType: Record<string, string[]> = {
new: ["triage", "backlog", "unstarted"],
active: ["started"],
resolved: ["completed", "canceled"],
};
async function findMatchingState(
gusStatus: { name: string; category: string },
teamStates: WorkflowState[]
): Promise<WorkflowState | undefined> {
const candidateTypes = gusStatusCategoryToLinearStateType[gusStatus.category] ?? ["backlog"];
const candidates = teamStates.filter(s => candidateTypes.includes(s.type));
return (
candidates.find(s => s.name === gusStatus.name) ??
candidates.find(s => generalizedStringCompare(s.name, gusStatus.name)) ??
candidates[0]
);
}
Linear Workspace Architecture
Recommended structure for the DMT Linear workspace.
Teams
Create one Linear Team per GUS Team. Each team gets its own workflow states, cycles, and backlog. This is a 1:1 mapping – no consolidation or splitting recommended.
Labels
| Label Group | Labels | Scope | Purpose |
| — | — | — | — |
| Work Type | Story, Bug, Investigation | Workspace | Replaces GUS Work Item type field |
| Product Tags | Mirror GUS Product Tags | Team-level | Feature area categorization |
| Source | GUS Sync, SFCI Bug, Fpolicy Bug, Incident | Workspace | Tracks origin of synced items |
| Program | Mirror GUS Program names | Project Labels (workspace) | Groups Projects by parent Program |
Custom Fields
| Field Name | Type | Entity | Purpose |
| — | — | — | — |
| GUS ID | Text | Issue, Project | Stable cross-reference to GUS record |
| GUS Team | Text | Issue | Original GUS Team (for CODEOWNERS routing) |
| GUS Product Tag | Text | Issue | Preserved for CI/CD bug routing |
Cycles
Configure each team’s Cycle cadence to match their GUS Sprint cadence (typically 2 weeks). Linear Cycles map directly to GUS Sprints.
Workflow States
Each Linear Team should configure workflow states that align with the GUS status mapping above. Linear’s default states (Triage → Backlog → Todo → In Progress → Done → Canceled) map naturally to most GUS workflows.
Linear API Reference
All GraphQL operations used by the sync engine are defined here once. Later sections reference these by name.
Authentication
Use a Linear OAuth Application with scopes: read, write, and webhooks enabled.
API Endpoint: https://api.linear.app/graphql
Since GUS is built on Salesforce, the GUS side of the sync can use Salesforce REST APIs (the same pattern Linear’s existing Salesforce integration uses). GUS entities are Salesforce custom objects queryable via SOQL.
Project Operations (for GUS Epic sync)
Create:
mutation ProjectCreate($input: ProjectCreateInput!) {
projectCreate(input: $input) {
success
project { id name status { id name type } startDate targetDate lead { id email } }
}
}
ProjectCreateInput: name (required), teamIds (required), description, statusId, leadId, startDate, targetDate, labelIds, priority (0-4).
Update:
mutation ProjectUpdate($id: String!, $input: ProjectUpdateInput!) {
projectUpdate(id: $id, input: $input) {
success
project { id updatedAt }
}
}
Archive / Delete:
mutation ProjectArchive($id: String!) { projectArchive(id: $id) { success } }
mutation ProjectDelete($id: String!) { projectDelete(id: $id) { success } }
mutation ProjectUnarchive($id: String!) { projectUnarchive(id: $id) { success } }
Issue Operations (for GUS Work Items, Bugs, Incidents)
Create:
mutation IssueCreate($input: IssueCreateInput!) {
issueCreate(input: $input) {
success
issue { id identifier title state { id name type } assignee { id email } }
}
}
IssueCreateInput: title (required), teamId (required), description, stateId, assigneeId, priority (0-4), estimate, labelIds, projectId, cycleId, parentId (for sub-issues).
Update:
mutation IssueUpdate($id: String!, $input: IssueUpdateInput!) {
issueUpdate(id: $id, input: $input) {
success
issue { id updatedAt }
}
}
Cycle Operations (for GUS Sprint sync)
Create:
mutation CycleCreate($input: CycleCreateInput!) {
cycleCreate(input: $input) {
success
cycle { id name startsAt endsAt }
}
}
CycleCreateInput: teamId (required), startsAt (required), endsAt (required), name, description.
Initiative Operations (for GUS Program sync)
mutation InitiativeCreate($input: InitiativeCreateInput!) {
initiativeCreate(input: $input) {
success
initiative { id name status { id name type } }
}
}
Issue Relations (for GUS Team Dependencies)
mutation IssueRelationCreate($input: IssueRelationCreateInput!) {
issueRelationCreate(input: $input) {
success
issueRelation { id type relatedIssue { id identifier } }
}
}
IssueRelationCreateInput: issueId (required), relatedIssueId (required), type (blocks, duplicate, related).
External Links (for GUS back-references)
mutation EntityExternalLinkCreate($input: EntityExternalLinkCreateInput!) {
entityExternalLinkCreate(input: $input) {
success
entityExternalLink { id label url }
}
}
Attach to Projects (projectId), Issues (issueId), or Initiatives (initiativeId). Used to link back to GUS Epics, Work Items, Projects, and Programs.
Query Operations (for reconciliation)
query Projects($filter: ProjectFilter) {
projects(filter: $filter) {
nodes { id name description status { id name type } startDate targetDate updatedAt
lead { id email } labels { nodes { id name } } externalLinks { nodes { id label url } } }
}
}
query Issues($filter: IssueFilter) {
issues(filter: $filter) {
nodes { id identifier title description state { id name type } assignee { id email }
priority estimate updatedAt project { id } cycle { id } labels { nodes { id name } } }
}
}
query Cycles($filter: CycleFilter) {
cycles(filter: $filter) {
nodes { id name startsAt endsAt team { id } }
}
}
Webhook Payload
When creating the OAuth Application, enable webhooks. The action field is one of "create", "update", "remove".
{
"action": "update",
"type": "Issue",
"data": {
"id": "issue-uuid",
"title": "...",
"stateId": "state-uuid",
"assigneeId": "user-uuid",
"priority": 2,
"teamId": "team-uuid",
"projectId": "project-uuid",
"cycleId": "cycle-uuid",
"updatedAt": "2026-02-27T10:00:00.000Z"
},
"updatedFrom": { "stateId": "old-state-uuid" },
"organizationId": "org-uuid",
"webhookId": "webhook-uuid"
}
The same structure applies for Project and Cycle types. Use updatedFrom to determine which fields changed (avoids unnecessary GUS API calls).
User Resolution
Resolve GUS users to Linear users via email, following Linear’s existing pattern:
async function resolveLinearUserId(gusUserEmail: string): Promise<string | undefined> {
const { users } = await linearClient.rawRequest(`{
users(filter: { email: { eq: "${gusUserEmail}" } }) { nodes { id email } }
}`);
return users.nodes[0]?.id;
}
If the user isn’t found in Linear, log a warning and skip the assignment. Do not block the sync.
Core Sync Engine
Entity Mapping Table
A single mapping table design covers all entity pairs:
┌──────────────┬───────────────┬──────────────────┬──────────────────┬─────────────────────┐
│ entity_type │ gus_id │ linear_id │ linear_entity │ last_synced_at │
├──────────────┼───────────────┼──────────────────┼──────────────────┼─────────────────────┤
│ epic │ EPIC-12345 │ proj_a1b2c3d4 │ project │ 2026-02-27T10:00:00 │
│ work_item │ W-67890 │ iss_e5f6g7h8 │ issue │ 2026-02-27T10:00:00 │
│ sprint │ SPR-111 │ cyc_i9j0k1l2 │ cycle │ 2026-02-27T10:00:00 │
│ program │ PROG-222 │ ini_m3n4o5p6 │ initiative │ 2026-02-27T10:00:00 │
│ team_dep │ TD-333 │ rel_q7r8s9t0 │ issue_relation │ 2026-02-27T10:00:00 │
└──────────────┴───────────────┴──────────────────┴──────────────────┴─────────────────────┘
This follows the same ExternalEntityRelation pattern used in Linear’s Jira sync, where stable external IDs are stored with metadata for lookups.
Reconciliation Flow
Run hourly. Process entities in dependency order: Teams → Cycles → Projects → Issues → Relations.
NoYesGUS newerLinear newerSameFetch GUS entities by typeFetch Linear entities by typeLoad mapping tableFor each GUS entityExists in mapping?Create in LinearAdd GUS external linksStore mappingCompare updatedAt timestampsUpdate Linear from GUSUpdate GUS from LinearSkipUpdate mapping timestamp
Conflict Resolution
- Compare GUS
lastModifiedDatevs LinearupdatedAt - Most recently updated system wins for bi-directional fields
- GUS-only fields (IDs, URLs, hierarchy links) always flow GUS → Linear
- Log all conflicts for admin review
Sync Loop Prevention
Critical for bi-directional sync. Follow Linear’s Jira sync pattern:
interface SyncContext {
source: "gus" | "linear" | "reconciliation";
disableSyncBack: boolean;
}
async function handleLinearWebhook(payload: WebhookPayload) {
if (payload.type !== "Issue" && payload.type !== "Project") return;
const mapping = await mappingStore.findByLinearId(payload.data.id);
if (!mapping) return;
// Check if this change originated from our sync (avoid loops)
// Option 1: timestamp guard -- skip if last_synced_at is within a small window
// Option 2: track in-flight sync IDs in a short-lived cache
if (isRecentlySynced(mapping, payload.data.updatedAt)) return;
await syncToGus(mapping, payload.data, { source: "linear", disableSyncBack: true });
}
The disableSyncBack flag mirrors Linear’s disableSyncToSourceOnSave pattern from the Jira sync. When syncing from Linear → GUS, mark the GUS update so the next GUS → Linear reconciliation skips it.
Rate Limiting
Linear uses complexity-based rate limiting. For batch reconciliation:
- Query in batches of 50 entities
- Implement exponential backoff on
429responses - Cache frequently queried data (statuses, labels, users) for the duration of the batch run
Tier 1: Agile Workflow Migration
These move entirely to Linear. No ongoing GUS sync required after initial migration.
Epic → Project Migration
For each GUS Epic, use ProjectCreate (see API Reference) with:
teamIdsfrom the GUS Team → Linear Team mappingstatusIdresolved via the status mappingleadIdresolved via email lookuplabelIdsincluding the Product Tag label and Program label- External links back to the GUS Epic, parent Project, and parent Program
Work Item → Issue Migration
For each GUS Work Item, use IssueCreate (see API Reference) with:
teamIdfrom GUS Team mappingprojectIdfrom the Epic → Project mappingcycleIdfrom the Sprint → Cycle mappinglabelIdsincluding Work Type label (Story,Bug, orInvestigation) and Product TagstateIdresolved via the status mapping
Sprint → Cycle Migration
For each GUS Sprint, use CycleCreate (see API Reference). Cycles are team-scoped and date-bounded – direct mapping.
Linear Equivalents for Other Tier 1 Capabilities
| GUS Feature | Linear Equivalent | Setup |
| — | — | — |
| SSO | Linear SAML/SCIM | Configure in workspace settings; supports all major IdPs |
| Slack Integration | Linear Slack app | Install from Linear integrations; supports issue creation, notifications, and updates from Slack |
| Out of Office | Cycle capacity planning | Team members set availability per cycle; velocity adjusts accordingly |
| Chatter | Linear Inbox + Notifications | @mentions, issue comments, and activity feed replace Chatter functionality |
| Agentforce | Linear AI features | Auto-triage, AI-generated descriptions, natural language issue creation |
| Dashboards / Metrics | Linear Analytics | Sprint forecast, velocity, burn-down built in; use Linear API for custom Tableau dashboards |
| Service Accounts | Linear API keys / OAuth apps | Create dedicated OAuth apps for automation; sandbox available for testing |
| GUS User Profiles / Roles | Linear workspace roles | Admin, Member, Guest map to GUS roles; team-level permissions available |
| Retros | Linear integrations | Use Cycle wrap-up reports; integrate with dedicated retro tools if needed |
Tier 2: Bi-directional Integrations
Linear is the system of record. GUS stays alive as a shadow to feed infrastructure consumers. Each subsection below describes only the context-specific delta from the core sync patterns above.
CODEOWNERS Bug Routing
Problem: CODEOWNERS files contain #GUSINFO:GUS_Team_name,GUS_Product_tag. CI tools (SFCI, Fpolicy, Skywalker) file bugs against the GUS Team found in CODEOWNERS. These bugs must reach the right Linear team.
Recommended approach:
- Maintain a GUS Team → Linear Team mapping table as part of the sync engine configuration
- When a bug is created in GUS against a GUS Team, the sync engine routes it to the corresponding Linear Team
- Use
IssueCreate(see API Reference) with context-specific inputs:
{
"teamId": "<resolved from GUS Team → Linear Team mapping>",
"title": "<GUS bug title>",
"labelIds": ["<BUG_LABEL>", "<SOURCE_LABEL: e.g., SFCI Bug>"],
"priority": "<mapped from GUS priority>",
"description": "<GUS bug description + link back to GUS>"
}
- Do not modify CODEOWNERS files. Other Salesforce internal teams depend on GUS info in CODEOWNERS. The mapping table bridges the gap without disrupting existing CI infrastructure.
Discussion Point: Long-term, could CODEOWNERS support a
#LINEARINFOannotation alongside#GUSINFO? This would allow tools to file directly in Linear, eliminating the GUS hop.
Git Integration (replacing Git2GUS)
Current state: Git2GUS is a GitHub webhook hosted in Heroku that syncs commit details to GUS Work Items. It also serves as a PR check.
Recommended approach:
- Enable Linear’s native GitHub integration. When developers create branches using Linear’s suggested naming (
<team>-<issue-id>-<description>), commits and PRs are automatically linked to Linear Issues. - For existing workflows where developers reference GUS IDs in commit messages, add a lightweight adapter that parses commit messages for GUS Work Item IDs and creates corresponding Linear Issue comments or attachments via
IssueUpdate. - If legacy GUS consumers still need commit data, backfill from Linear to GUS using the sync engine: when a Linear Issue receives a Git attachment, sync that data to the mapped GUS Work Item.
Linear’s GitHub integration handles branch tracking, PR linking, and auto-closing – this replaces Git2GUS’s core functionality without custom infrastructure.
Incident and Problem Management
Current state: Incident Management teams remain on GUS. When an incident creates work items against DMT teams, those work items must reach Linear.
Recommended approach:
Incidents themselves do not sync (they’re managed by a separate GUS-based team). The sync engine picks up GUS Work Items created by the incident process and syncs them to Linear using IssueCreate with incident-specific inputs:
{
"labelIds": ["<BUG_LABEL>", "<INCIDENT_LABEL>"],
"priority": 1,
"description": "<GUS work item description>\n\n---\nLinked Incident: <GUS Incident URL>"
}
The INCIDENT_LABEL lets teams filter and prioritize incident-related work in Linear.
CI/CD Bug Sync (SFCI, SFBT, Fpolicy, Skywalker)
These tools auto-create bugs in GUS when they detect deviations. The sync engine picks them up and routes them to Linear using the CODEOWNERS bug routing pattern above. The only variation is the Source label:
| Tool | Source Label |
| — | — |
| SFCI | SFCI Bug |
| SFBT / Agentforce Pipeline | SFBT Bug |
| Fpolicy | Fpolicy Bug |
| Skywalker | Skywalker Bug |
HPS / Kaiju
These services require a GUS Team mapping for onboarding and tenant configuration. Maintain the GUS Team entity for these services; track monitoring-related work in Linear. Surface HPS/Kaiju details in Linear via external links on the relevant Team or Project.
Intake Requests
For intake workflows where DMT teams receive requests from other Salesforce service teams:
- Create the request as a Linear Issue (with an
Intakelabel) - Sync to GUS for service teams that still consume GUS Work Items
- Use
IssueCreatein the reverse direction (Linear → GUS) when the intake is triaged
PagerDuty
Linear has a native PagerDuty integration. Evaluate whether it can replace the GUS Team → PagerDuty auto-sync. If GUS Teams are maintained (for Tier 2/3 reasons), the existing PagerDuty sync can continue in parallel.
Tier 3: GUS-Resident Capabilities
These are too deeply coupled to Salesforce infrastructure to migrate. The recommended approach for all Tier 3 items: surface in Linear via external links so teams have visibility without leaving Linear, but continue to manage them in GUS.
Falcon/Fedx
Falcon applications are mapped to GUS Teams and GUS Product Tags. Deployment access (Spinnaker), FOX approvals, and change case workflows all require GUS entities. Maintain existing GUS Teams. Surface deployment status and change case links in Linear via external links on the relevant Project or Issue.
Long-term path: Work with the Falcon platform team to support Linear Team IDs as an alternative to GUS Team IDs for application mapping and access control.
Change Cases / Change Categories / Change Templates
Change management workflows (approval chains via Manager Review and Peer Review queues) are built on GUS. These cannot move to Linear without rebuilding the approval infrastructure. Link change cases to Linear Issues/Projects via external links so developers can reference them.
Discussion Point: Could a Linear automation create a GUS Change Case when a Linear Project reaches a “Ready for Release” status? This would let teams stay in Linear while the change approval flows through GUS.
JIT Access
Requires a GUS Change Case or Incident record for access to higher environments. No sync needed. Add a link to the GUS Change Case template in Linear team documentation or as a pinned resource.
Certificate Management / CS Monitor
Certificate request and expiry monitoring automation is tied to GUS. Bugs for expiring certificates are created against GUS Teams. Route these bugs to Linear via the Tier 2 CODEOWNERS bug sync pattern. The certificate request workflow itself stays in GUS.
Release Management (OrgESC)
GUS Builds are tied to Work Items and linked to OrgESC Release features. This has dependencies on both GUS and OrgESC.
Discussion Point: Can Linear’s project milestone tracking replace the GUS Build → OrgESC Release pipeline? If releases are primarily about tracking which Issues shipped, Linear’s built-in release tracking (filtering Issues by Cycle or Project) may suffice for DMT’s needs, with OrgESC integration handled separately.
FastLane
Depends on OrgESC records. Same approach as Release Management above.
Initial Migration / Bootstrap
Phased Rollout Strategy
Migrate team-by-team, not all at once. Recommended order:
- Pilot team – one DMT team with simple GUS usage (few integrations, no Falcon dependencies). Validate the sync engine end-to-end.
- Tier 1 teams – teams whose GUS usage is entirely agile workflows. Full migration, disable GUS access.
- Tier 2 teams – teams with CODEOWNERS / CI/CD dependencies. Enable bi-directional sync.
- Tier 3 teams – teams with Falcon/Change Management dependencies. Enable sync with GUS shadow entities.
Migration Script
Process entities in dependency order:
async function migrateTeam(gusTeamId: string, linearTeamId: string) {
// 1. Sync Programs → Initiatives (if not already created)
const programs = await gusClient.getProgramsForTeam(gusTeamId);
for (const program of programs) {
await syncProgramToInitiative(program);
}
// 2. Sync Epics → Projects
const epics = await gusClient.getEpicsForTeam(gusTeamId);
for (const epic of epics) {
const project = await createProjectFromEpic(epic, linearTeamId);
await addGusExternalLinks(project.id, epic);
await mappingStore.create("epic", epic.id, project.id, "project");
}
// 3. Sync Sprints → Cycles
const sprints = await gusClient.getSprintsForTeam(gusTeamId);
for (const sprint of sprints) {
const cycle = await createCycleFromSprint(sprint, linearTeamId);
await mappingStore.create("sprint", sprint.id, cycle.id, "cycle");
}
// 4. Sync Work Items → Issues
const workItems = await gusClient.getWorkItemsForTeam(gusTeamId);
for (const item of workItems) {
const projectId = await mappingStore.getLinearId("epic", item.epicId);
const cycleId = await mappingStore.getLinearId("sprint", item.sprintId);
const issue = await createIssueFromWorkItem(item, linearTeamId, projectId, cycleId);
await mappingStore.create("work_item", item.id, issue.id, "issue");
}
// 5. Sync Team Dependencies → Issue Relations
const deps = await gusClient.getTeamDependencies(gusTeamId);
for (const dep of deps) {
await createIssueRelationFromDependency(dep);
}
}
Data Validation
After migration, run a reconciliation pass comparing GUS and Linear entity counts and field values. Flag discrepancies for manual review before enabling bi-directional sync.
Error Handling and Monitoring
Error Handling
| Scenario | Handling |
| — | — |
| Linear rate limit (429) | Exponential backoff; cache query results during batch runs |
| User not found in Linear | Log warning, skip assignment, continue sync |
| Team not found in Linear | Fail sync for that entity, alert admin |
| Duplicate entity (already mapped) | Use existing mapping, log info |
| GUS API unavailable | Retry with backoff; alert if down > 15 minutes |
| Status not matched | Fall back to Backlog/Triage; log for admin review |
| Webhook signature invalid | Reject, log security alert |
Monitoring Alerts
- Sync job failure (any entity type)
- Sync lag (
last_synced_at> 2 hours for any mapping) - High unmapped entity count (entities in GUS without Linear counterparts)
- Rate limit approaching (> 80% of Linear’s complexity budget)
- Webhook delivery failures (5xx responses)
Audit Logging
interface SyncLog {
timestamp: string;
action: "create" | "update" | "archive" | "skip";
source: "gus" | "linear" | "reconciliation" | "webhook";
entityType: "epic" | "work_item" | "sprint" | "program" | "team_dep";
gusId?: string;
linearId?: string;
changes?: Record<string, { from: unknown; to: unknown }>;
error?: string;
}
Log all sync operations for debugging and compliance. Retain logs for at least 90 days.
Open Questions Summary
Items requiring Salesforce DMT input before finalizing:
- GUS Status values – Actual status names per entity type (Epic, Work Item) for status mapping tables
- GUS Project mapping – Should GUS Projects be metadata on Initiatives, or first-class Linear entities?
- Task sync – Confirm that GUS Tasks do not need to sync (teams recreate as sub-issues in Linear)
- Initiative sync direction – One-way (GUS → Linear) or bi-directional for Programs?
- Sprint cadence – Uniform 2-week sprints across all DMT teams, or variable?
- CODEOWNERS evolution – Interest in adding
#LINEARINFOalongside#GUSINFOlong-term? - Change Case automation – Could Linear trigger GUS Change Case creation for releases?
- Release Management – Can Linear milestones replace GUS Build → OrgESC Release?
- Sync service hosting – Heroku (like Git2GUS), AWS, or Salesforce-internal?
- Team rollout order – Which DMT team should pilot?