Skip to main content
Generalmarkus41

Harness Code ↔ Linear Bridge

This skill should be used when implementing or debugging two-way sync between Linear issues and Harness Code (branches, PRs, deploys, Git Experience, custom approvals, triggers). Activates on "harness sync", "harness bridge", "linear-harness", "harness code linear".

Stars
12
Source
markus41/claude
Updated
2026-05-11
Slug
markus41--claude--harness-bridge
View on GitHubRaw SKILL.md

// install — copy + paste into any project

mkdir -p .claude/skills && curl -fsSL https://raw.githubusercontent.com/markus41/claude/HEAD/plugins/linear-orchestrator/skills/harness-bridge/SKILL.md -o .claude/skills/harness-bridge.md

Drops the SKILL.md into .claude/skills/harness-bridge.md. Works with Claude Code, Cursor, and any agent that loads SKILL.md files from .claude/skills/.

Harness Code ↔ Linear Bridge

References:

Architecture

┌──────────────┐   webhook   ┌──────────────┐   webhook   ┌────────────┐
│   Linear     │───────────▶ │ Bridge HTTP  │ ◀──────────│ Harness    │
│   API        │ ◀───────────│   handler    │───────────▶│ Code       │
└──────────────┘  GraphQL    └──────┬───────┘  REST API  └────────────┘
                                    │
                              SQLite state
                              (lib/state.ts)

The bridge holds:

  • Linear issue ID ↔ Harness PR number
  • Linear team ↔ Harness repo
  • Linear cycle ↔ Harness pipeline tag
  • Last successful sync cursor (per resource type)
  • Webhook DLQ entries

Mapping rules

Linear Issue → Harness

  • Branch creation on issue with branch: label → POST /v1/repos/{repo}/branches with name = linear/{key}-{slug}
  • Move to "In Progress" → if PR exists, mark non-draft via PATCH /v1/repos/{repo}/pulls/{n}
  • Move to "Done" → add label linear:ready-to-merge via POST /v1/repos/{repo}/pulls/{n}/labels
  • Archived → close PR

Harness → Linear Issue

  • PR opened with ENG-123 in title → look up issue, link via attachmentLinkCreate, set state to "In Review" if currently "In Progress"
  • PR review changes → comment on issue
  • PR merged → state to "Done"
  • Deploy succeeded/failed → comment on issue + apply label deployed:<env> or reopen on failure

Idempotency

Every Harness webhook event has eventId. The bridge:

  1. Reads eventId first
  2. Checks SQLite events_processed table
  3. Inserts on success; on conflict, returns 200 (already processed)

Linear webhooks key off delivery.id similarly.

Reconciliation loop

Every 6h the bridge runs reconcile():

  1. Page through Linear issues in mapped teams (last 30 days, via updatedAt filter)
  2. For each, check linked PR exists in Harness
  3. Page through Harness PRs in mapped repos
  4. For each PR mentioning a Linear issue, check link exists in Linear
  5. Report drift; auto-heal if safe (links only, not state changes)

Custom approvals as Linear gates

Pattern: Harness pipeline has a Custom Approval step that calls back to the bridge:

- step:
    type: CustomApproval
    name: Linear Approval
    spec:
      url: https://your-bridge.example.com/harness/approval
      method: POST
      requestBody: |
        { "issue": "ENG-123", "executionId": "<+execution.id>" }

The bridge:

  1. Posts a comment on ENG-123 with [Approve via Harness] [Reject] rendered as Linear smart-buttons
  2. Waits for the user's reaction (reactionCreate event) or the assignee's status change
  3. Calls back Harness with { "approved": true } or { "rejected": true, "reason": "..." }

Tags propagation

Linear labels → Harness tags (1:1, lowercase, hyphenated). On sync the bridge:

  • Adds Harness tag linear:bug for issues with bug label
  • Adds Harness tag linear:p1 for priority 1
  • These tags filter pipeline executions, deploy timelines, etc.

Connector auto-provisioning

On /linear:setup --mode bridges, the bridge:

  1. Creates a Harness "Custom" connector pointing at the bridge URL
  2. Stores Linear API key as a Harness Secret (linear_api_key)
  3. Generates a YAML connector definition and posts via POST /ng/api/connectors
  4. Linear reference is now available in pipelines as <+connectors.linear>

Failure modes

Failure Behaviour
Harness 401 Refresh token; if still 401 alert and pause bridge
Harness 5xx Backoff up to 3 retries → DLQ
Linear rate-limited Wait for budget reset, retry
Webhook signature mismatch Reject + log + alert (potential attack)
State store corruption Halt bridge, require manual /linear:harness-sync reconcile --apply

Performance

  • Webhook processing target: P95 < 200ms (most work happens async via queue)
  • Reconcile job: ~30s for 1k issues + 200 PRs
  • Bulk import: paced at 10 ops/sec to stay under both Linear and Harness rate limits