Skip to content

feat(issues): add issue tracking core with GitHub issues - #19259

Draft
juliusmarminge wants to merge 7 commits into
mainfrom
issues/1-core
Draft

juliusmarminge wants to merge 7 commits into
mainfrom
issues/1-core

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

Problem

T3 Code can track pull requests but not issues, and non-git trackers such as Linear and Jira have nowhere to plug in.

Change

Adds issue tracking as its own extension point beside source control.

  • New @t3tools/issue-tracker-core package: the IssueTrackerApi port, IssueTrackerError, and defineIssueTrackerDriver. Each tracker declares capabilities (create, edit, comment, labels, assignees, reactions, templates, linked PRs, states, close reasons); clients only offer what a tracker declares.
  • Contracts: issues are { tracker, host?, scope, key } (owner/repo + 123, or a team key + ENG-123 later), and state is { category: open | closed, name } so tracker-specific states keep their own names. Ten issues.* RPCs; writes require the source-control write scope.
  • Server: IssueTrackerRegistry and IssueService. Project trackers resolve from the project's remote; issues on a repository no project owns are read-only.
  • GitHub tracker in source-control-github, over the existing GitHubApi.
  • RPC surface: the issue RPCs live in their own WsIssuesRpcGroup, merged into WsRpcGroup, with a separate handler layer in ws.ts. With Effect 4.0.4, adding them to the single core group pushed ServerWsRpcGroup.toLayer past TypeScript's instantiation depth (a hidden TS2589 that surfaced as a missing-any-service error in binCli.ts). Wire format, client, and authorization are unchanged. New domains should follow the same pattern.

Bitbucket retired its issue tracker and registers none.

Stack

  1. This PR: core, contracts, service, GitHub
  2. #19260: Issues page and detail panel
  3. #19261: GitLab, Azure DevOps, Forgejo trackers
  4. #19270: thread issue links and MCP issue tools
  5. #19274: Linear as a standalone tracker

Verification

Targeted typechecks and the new service, GitHub tracker, and RPC authorization tests pass.

Rebuilt from #6315 by @Bil0000, which this stack uses as its reference for host APIs and UX.

Implemented with Claude Opus 5.5 and Claude Fable 5.1 in T3 Code, using the Claude Code harness.

🤖 Generated with Claude Code

@juliusmarminge
juliusmarminge added this pull request to stack #19262 October 14, 2026 05:52
@github-actions github-actions Bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Oct 14, 2026
@juliusmarminge juliusmarminge changed the title issues/1 core feat(issues): add issue tracking core with GitHub issues 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.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB +7 B (+0.1%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB +7 B (+0.6%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 20.9 KiB 20.9 KiB 0 B (0.0%) 29.3 KiB ✅
Codex Live turn messages 2 2 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.2 KiB 21.2 KiB 0 B (0.0%) 29.3 KiB ✅
Claude Live turn messages 2 2 0 (0.0%) 8 ✅

Baseline: 173a970 · PR result: b72ded8 · 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.

juliusmarminge and others added 3 commits October 13, 2026 23:50
Issue tracking gets its own extension point beside source control so non-git
trackers slot in without posing as source-control hosts. This layer adds the
`@t3tools/issue-tracker-core` port and driver shape, the `issues.*` contracts
and RPCs, an `IssueService` that resolves a project's tracker from its git
remote and treats issues outside the project's own scope as read-only, and a
GitHub tracker over the existing `GitHubApi`.

Web UI, MCP tools, thread links, and the other trackers follow in later layers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With Effect 4.0.4 and the ten issue RPCs, typing `toLayer` over the whole
WebSocket RPC group hits TypeScript's instantiation depth limit (TS2589,
only reported when a single checker sees ws.ts after the orchestration
runtime). The checker then gives up and types the handler layer's
requirements as `any`, which spreads through the route and server layers
into binCli.ts and the CLI tests.

The issue RPCs now form `WsIssuesRpcGroup`, implemented by their own
`toLayer`, and `WsRpcGroup` merges it with the rest. The wire surface,
authorization, and instrumentation are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
...(input.state === "all" ? [] : [`is:${input.state}`]),
...(qualifier === null ? [] : [`${qualifier}:${input.viewer}`]),
"sort:updated-desc",
...(text.length === 0 ? [] : [searchPhrase(text)]),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small one on search: quoting the whole input keeps is:closed from being read as a qualifier, but it also turns the search into a phrase match. On this repo, is:issue "pull request panel" finds 32 issues, while "panel" "pull" "request" finds 68. Quoting each word on its own is just as safe against qualifiers and still finds issues where the words aren't next to each other. Ignore this if the phrase match is intended.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, the phrase match wasn't intended. Each word is now quoted on its own in c306735, so is:closed still can't act as a qualifier but the words match apart.

juliusmarminge and others added 2 commits October 14, 2026 01:43
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
repository(owner: $owner, name: $name) {
viewerPermission
issue(number: $number) {
${ISSUE_ROW_FIELDS}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opening any GitHub issue in the detail panel fails. GitHub rejects the detail query:

Field 'comments' has an argument conflict: {} or {last:"100"}?

ISSUE_DETAIL_GRAPHQL_QUERY interpolates ISSUE_ROW_FIELDS here, which already selects comments { totalCount }. Line 173 then selects comments(last: 100) { … } again. GraphQL won't merge the same field selected twice with different arguments, so the whole request errors. The issue list still works because the search query only uses the row fields.

The detail's comments(last: …) block already asks for totalCount, and RawDetailSchema overrides comments with that page. So the detail query can drop the row's count-only selection:

const ISSUE_ROW_BASE_FIELDS = `number title url state stateReason createdAt updatedAt closedAt
  author { login avatarUrl ... on User { name } }
  assignees(first: 20) { nodes { login name avatarUrl } }
  labels(first: 20) { nodes { name color } }`;

const ISSUE_ROW_FIELDS = `${ISSUE_ROW_BASE_FIELDS}
  comments { totalCount }`;

Then use ${ISSUE_ROW_BASE_FIELDS} in ISSUE_DETAIL_GRAPHQL_QUERY.

The tests didn't catch this because the tracker test stubs graphql with canned responses and never validates the query text.

Found by running the whole stack (#19259–#19274) merged onto main at 614b432a79 in a dev desktop build. All 17 issues.detail calls failed with this error, while issues.list worked.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for running the whole stack. That was a real blocker. Fixed in b72ded8 the way you suggested: the detail query uses ISSUE_ROW_BASE_FIELDS, so comments is selected once. I ran the detail, permission and search queries against a live issue here and GitHub accepts all three. The tracker test now also checks that the detail query names comments once, since its stubbed responses couldn't catch this.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed in a dev build of the full stack: opened an issue, commented and closed it without errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Artic0din

Copy link
Copy Markdown

Question on forks: issue scope follows the canonical repository identity, which prefers the upstream remote (#16353). A fork project therefore lists the upstream repo's issues and never its own. Here that hides 73 open issues across three forks that track their own work. Is following upstream intended for issues, or should issues use the checkout's origin (which #16353 already reports), or list both?

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

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 PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants