Skip to content

feat(issues): add Linear as a standalone issue tracker - #19274

Draft
juliusmarminge wants to merge 1 commit into
issues/5-threadsfrom
issues/5-linear
Draft

juliusmarminge wants to merge 1 commit into
issues/5-threadsfrom
issues/5-linear

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

Adds Linear, the first tracker that is not a git host.

How it works

  • The user connects Linear with an API key under Settings → Source Control, and binds a team to a project. That team's issues then list beside the project's git-host issues.
  • The key lives in the server's secret store; bindings are a small table (migration 092).
  • The client is generated with @effect/graphql-generator from the schema Linear publishes with its SDK, like the GitHub and Expo clients. CI checks the generated files are current.
  • Writes refuse an issue that is not in the project's bound team.

Not verified live: there is no Linear API key on the machine this was built on, so this is covered by tests against a fake Linear server only.

Opus 5.5 and Fable 5.2 in Claude Code, running in T3 Code.

🤖 Generated with Claude Code

@juliusmarminge juliusmarminge changed the title issues/5 linear feat(issues): add Linear as a standalone issue tracker Oct 14, 2026
@github-actions github-actions Bot added the vouch:trusted Author is trusted by repo permissions or the VOUCHED list. label Oct 14, 2026
@github-actions github-actions Bot added the size:XXL 1,000+ changed lines (additions + deletions). label Oct 14, 2026
@juliusmarminge juliusmarminge added the macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews label Oct 14, 2026
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire — 5.0 KiB — 6.8 KiB ✅
Codex Thread snapshot wire — 3.8 KiB — 4.9 KiB ✅
Codex Live turn WebSocket wire — 1.2 KiB — 2.0 KiB ✅
Codex Live turn WebSocket decoded — 21.5 KiB — 24.4 KiB ✅
Codex Live turn messages — 1 — 8 ✅
Claude Total thread wire — 5.0 KiB — 6.8 KiB ✅
Claude Thread snapshot wire — 3.8 KiB — 4.9 KiB ✅
Claude Live turn WebSocket wire — 1.2 KiB — 2.0 KiB ✅
Claude Live turn WebSocket decoded — 21.9 KiB — 24.4 KiB ✅
Claude Live turn messages — 1 — 8 ✅

Other scenarios

No main baseline exists yet for these 64 scenario measurements.

All 64 scenario measurements
Provider Scenario Metric Main baseline This PR Impact PR ceiling
Codex short turn resume WebSocket decoded — 10.5 KiB — 12.7 KiB ✅
Codex short turn resume WebSocket messages — 2 — 2 ✅
Codex long turn running, cold open HTTP decoded — 281.7 KiB — 302.7 KiB ✅
Codex long turn running, cold open rows — 75 — 75 ✅
Codex long turn running, history page HTTP decoded — 92.4 KiB — 107.4 KiB ✅
Codex long turn running, history page rows — 75 — 75 ✅
Codex long turn finished, cold open HTTP decoded — 381.5 KiB — 410.2 KiB ✅
Codex long turn finished, cold open rows — 151 — 151 ✅
Codex long turn finished, socket cold open WebSocket decoded — 381.6 KiB — 410.2 KiB ✅
Codex long turn finished, socket cold open WebSocket messages — 2 — 2 ✅
Codex long turn finished, socket cold open rows — 151 — 151 ✅
Codex long turn finished, work page HTTP decoded — 186.2 KiB — 200.2 KiB ✅
Codex long turn finished, work page rows — 150 — 150 ✅
Codex idle thread resume WebSocket decoded — 65 B — 200 B ✅
Codex idle thread resume WebSocket messages — 1 — 1 ✅
Codex fleet shell snapshot HTTP decoded — 42.4 KiB — 53.7 KiB ✅
Codex fleet shell snapshot rows — 27 — 27 ✅
Codex fleet shell subscribe WebSocket decoded — 65 B — 200 B ✅
Codex fleet shell subscribe WebSocket messages — 1 — 1 ✅
Codex fleet shell resume check HTTP decoded — 18 B — 100 B ✅
Codex fleet shell resume check rows — 0 — 0 ✅
Codex stale shell reload HTTP decoded — 42.4 KiB — 53.7 KiB ✅
Codex stale shell reload rows — 27 — 27 ✅
Codex stale shell resume WebSocket decoded — 140 B — 400 B ✅
Codex stale shell resume WebSocket messages — 2 — 2 ✅
Codex stale todo list cold open HTTP decoded — 72.4 KiB — 83.0 KiB ✅
Codex stale todo list cold open rows — 75 — 75 ✅
Codex stale todo list cold open nodes — 2 — 4 ✅
Codex stale todo list cold open subagents — 2 — 4 ✅
Codex stale todo list cold open plans — 1 — 2 ✅
Codex stale todo list cold open providerTurns — 1 — 2 ✅
Codex stale todo list cold open checkpoints — 1 — 2 ✅
Claude short turn resume WebSocket decoded — 10.6 KiB — 12.7 KiB ✅
Claude short turn resume WebSocket messages — 2 — 2 ✅
Claude long turn running, cold open HTTP decoded — 283.3 KiB — 302.7 KiB ✅
Claude long turn running, cold open rows — 75 — 75 ✅
Claude long turn running, history page HTTP decoded — 93.4 KiB — 107.4 KiB ✅
Claude long turn running, history page rows — 75 — 75 ✅
Claude long turn finished, cold open HTTP decoded — 384.2 KiB — 410.2 KiB ✅
Claude long turn finished, cold open rows — 151 — 151 ✅
Claude long turn finished, socket cold open WebSocket decoded — 384.3 KiB — 410.2 KiB ✅
Claude long turn finished, socket cold open WebSocket messages — 2 — 2 ✅
Claude long turn finished, socket cold open rows — 151 — 151 ✅
Claude long turn finished, work page HTTP decoded — 188.1 KiB — 200.2 KiB ✅
Claude long turn finished, work page rows — 150 — 150 ✅
Claude idle thread resume WebSocket decoded — 66 B — 200 B ✅
Claude idle thread resume WebSocket messages — 1 — 1 ✅
Claude fleet shell snapshot HTTP decoded — 43.0 KiB — 53.7 KiB ✅
Claude fleet shell snapshot rows — 27 — 27 ✅
Claude fleet shell subscribe WebSocket decoded — 66 B — 200 B ✅
Claude fleet shell subscribe WebSocket messages — 1 — 1 ✅
Claude fleet shell resume check HTTP decoded — 18 B — 100 B ✅
Claude fleet shell resume check rows — 0 — 0 ✅
Claude stale shell reload HTTP decoded — 43.0 KiB — 53.7 KiB ✅
Claude stale shell reload rows — 27 — 27 ✅
Claude stale shell resume WebSocket decoded — 140 B — 400 B ✅
Claude stale shell resume WebSocket messages — 2 — 2 ✅
Claude stale todo list cold open HTTP decoded — 73.4 KiB — 83.0 KiB ✅
Claude stale todo list cold open rows — 75 — 75 ✅
Claude stale todo list cold open nodes — 2 — 4 ✅
Claude stale todo list cold open subagents — 2 — 4 ✅
Claude stale todo list cold open plans — 1 — 2 ✅
Claude stale todo list cold open providerTurns — 1 — 2 ✅
Claude stale todo list cold open checkpoints — 1 — 2 ✅

Baseline: unavailable · PR result: 8f574c0 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@Bombatomica64

Copy link
Copy Markdown
Contributor

Multi-account question

As written, a server can only be connected to one account per standalone tracker, so one Linear workspace (and one Jira site once that lands):

  • issueTrackerSecretName(tracker) keys the secret by tracker kind only (issue-tracker-<kind>-token), so a second token for the same tracker overwrites the first.
  • issue_tracker_bindings (migration 063) has no connection or account id, and project_id is the primary key. A binding can't say which account's team it points at.
  • Disconnect runs bindings.removeTracker(kind), which unbinds every project bound to that tracker.

Linear API keys are per workspace, so anyone with two workspaces (say work and a client) can only use one. Per-project choice isn't possible either.

Is that an intentional v1 limit? If we want multi-account later, it's cheapest to add a connection_id to the binding and key the secret by (tracker, connection_id) before 063 ships. After that it needs a data migration. If it's deliberate, a line in the PR description or docs would be enough.

@nSimonFR-ai

Copy link
Copy Markdown

Two things from our fork that this stack doesn't cover yet, in case they're useful as reference: nSimonFR-ai#1

  • linking/reading a Linear issue by team key (e.g. CO-130) from a project with no Linear binding or no git remote
  • in thread details, a PR that a linked issue already lists is shown once, nested under that issue

Happy to tackle both as follow-up PRs on top of this stack once it settles, if that's welcome.

@juliusmarminge juliusmarminge self-assigned this Oct 19, 2026
@juliusmarminge

Copy link
Copy Markdown
Member Author

Taking over the misc-03 assessment of this draft. Its head still depends on issues/5-threads; I am checking the current main prerequisite boundary before changing code. The batch covers this Linear PR, so I will not silently import or rewrite the separate issue-tracker foundation. No merge is planned. I will record any prerequisite and live-workspace verification blockers here.

@juliusmarminge

Copy link
Copy Markdown
Member Author

misc-03 assessment complete: this draft is prerequisite-blocked, so I am leaving it open and unchanged rather than publishing an incomplete port.

The pinned head is 322107b, based on issues/5-threads. Current main at 92a6b43 lacks the existing issue contracts, tracker core, server service and issue UI that this 41-file layer extends. The prerequisite branch alone adds 101 files and 12,040 lines since the common ancestor. A full Linear implementation needs an agreed minimal foundation boundary/owner; importing the entire earlier stack would silently take over independent features outside this batch. A connection/probe-only extraction would not deliver this PR's promised CRUD, templates, or coexistence with repository issues. Migration 063 also collides with released DropUnusedV1Projections (064 is occupied too); a port must allocate a fresh identity and preserve existing migration history.

A fresh test agent read all 41 file diffs and rechecked the prerequisite boundary. Concrete items for the eventual rebuild:

  • Creation targets are derived only from repository identity. Linear is never offered; a bound project without a remote has no New issue action. Derive targets from bindings, including empty teams.
  • List mapping uses map(toSummary), whose second argument is commentCount; array indices become displayed counts. Extend the existing multi-item list fixture to catch this.
  • externalTemplates has a contract/adapter producer but no issue-composer consumer. Form/rich template links are not offered to users.
  • Credential replacement and binding overwrite the kind-keyed token while retaining old team bindings. The multi-account question remains unanswered. Choose a single-connection policy with defined replacement behavior or explicit connection identity before shipping persistence. Binding currently checks neither project existence nor connected-account scope membership.
  • The service has no shared sequencing for competing clients, and listing scans all bindings once per project. These need deterministic service-level ordering tests and a bounded lookup strategy, not client-local scheduling alone.

Minimum next proof: remote-less and dual-tracker create/read/update/close/reopen; plain/external template actions; connect/replace/bind/unbind/disconnect with the chosen identity policy; destination permission denial; Deferred-controlled concurrent clients; and fresh/upgrade SQLite migration fixtures preserving 063/064. Then verify the integrated flow against a disposable live Linear workspace and decide mobile/MCP scope explicitly.

No replacement code was written or pushed, no new implementation tests ran, and no formal adversarial implementation review round ran. Source-counted touched-test inventory remains 53 → 53 cases across six files; the original branch's historical CI is not proof against current main. No live account, GraphQL, browser or native client verification was performed. This assessment does not request credentials before the prerequisite scope is settled.

Assessed by GPT-6-Astra through the Codex harness in T3 Code; maintainer integration by GPT-6.1-Sol through Codex. Nothing merged.

peraautomations pushed a commit to Pera-Labs/t3code that referenced this pull request Oct 24, 2026
…ndalone tracker API

Decision: no fork-specific task system. Upstream's incoming Issues stack
(pingdotgg#19259 core, pingdotgg#19260 web, pingdotgg#19274 standalone trackers) is the base; Greater
becomes one standalone driver (kind gr8r) and the fork adds the generic
pieces upstream lacks: start a thread from an issue, issue to thread links,
an agent-side MCP toolkit, optional headless credential fallback.

The document records the upstream interface facts the design relies on,
the field, state and capability mapping, the write calls and 409 handling,
what v1 leaves out, the Jira portability check and the rollout order.
Nothing is implemented yet; waiting for the fork owner's go-ahead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NxWCnNRWCn2tyyoHUdcqM2
@jappyjan

Copy link
Copy Markdown
Contributor

I've been running a Linear integration on my fork for a while. Some of what it does isn't in this stack yet, so here are a few wishes to add on top of your tracker and link model. I'm not proposing a second Linear client. I'd happily rebuild these on top of #19274 and #19270 once they land, and drop mine.

Branch: https://github.com/jappyjan/t3code/tree/feat/linear (user guide: docs/user/linear.md)

1. Start work straight from an issue

  • An issue has Start chat and Run pipeline buttons.
  • T3 Code attaches the issue, its sub-issues and its comments as context.
  • By default it creates a new worktree on Linear's own branch name, so Linear links the PR when it's opened. If that branch already exists, the name gets -2, -3, and so on.
  • For me this is the main reason to have Linear in T3 Code, more than reading or editing issues.

2. Browse Linear's own structure

  • Drill down from team to project to milestone to issue.
  • Projects and milestones show their progress and issue counts. Team cards show "assigned to you".
  • Filters, Linear's saved views, and a jump search.
  • Linear is planned around projects and milestones more than one flat issue list per team.

3. Map at the Linear project level too

  • Mappings point a Linear team or a Linear project at a T3 Code project, and a project mapping beats its team's.
  • In our workspace one Linear team spans several repos, so a single team scope per project isn't enough.
  • Without a mapping, the launch asks which project to use.

4. Optional write-back when work starts

  • Move the issue to In Progress. This only happens from Backlog, Triage or an unstarted state.
  • Post a comment that links to the thread.
  • Both are off by default and can be switched off for a single launch.

5. Mobile

  • A Linear tab with the same drill-down and issue detail.
  • Start chat and Run pipeline work from the phone.
  • Deep links (linear/<env>/…).
  • Thread rows show the issue identifier, so you can find the work again from the sidebar.

Pipelines

Run pipeline starts a pipeline from #26694 with the issue as its input. A ticket goes through a multi-step chain: refine requirements, implement, prepare QA, QA, review, then a visual review guide. While the run moves, the pipeline keeps one Linear comment up to date. This has been the most effective way I've found to get ticket work done end to end. If that direction is interesting, issue → pipeline would be a natural entry point once both pieces exist.

Happy to split any of this into small PRs on top of your stack, or to drop the parts you don't want.

Linear is a tracker that is not a git host: the user connects it with an API key in Settings and binds a team to a project, whose issues then list beside the git host's. Its client is generated from the schema Linear publishes.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-context-increase Accepts agent context growth over main in the Agent context budget check. macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted Author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants