feat: follow an epic with everything under it

«Seguir todo» on a ticket with subtasks (and on each epic of the Epics
page) tells you of it and of every ticket hanging from it, at any depth,
also the ones added later: their moves, assignments, comments and plan
changes, as a follower of each, plus when a ticket is placed under it.
Guests of a private project are left out. Personal, no history (Mongo
`tree_follows`).

API: followTree / unfollowTree, Issue.followingTree, PUT/DELETE
/api/issues/{issue}/follow-tree. MCP: watch_issue with tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 19:59:04 +02:00
parent 179d6a5e94
commit d79ef418d3
31 changed files with 1072 additions and 62 deletions

View File

@@ -688,6 +688,7 @@ export type IssueSummaryFieldsFragment = {
export type IssueFieldsFragment = {
pinned: boolean;
followingTree: boolean;
description: string | null;
figmaUrl: string | null;
createdAt: string;
@@ -933,6 +934,7 @@ export type IssueQueryVariables = Exact<{
export type IssueQuery = {
issue: {
pinned: boolean;
followingTree: boolean;
description: string | null;
figmaUrl: string | null;
createdAt: string;
@@ -1151,6 +1153,18 @@ export type MyPinsQuery = {
}>;
};
export type FollowTreeMutationVariables = Exact<{
issueId: string;
}>;
export type FollowTreeMutation = { followTree: boolean };
export type UnfollowTreeMutationVariables = Exact<{
issueId: string;
}>;
export type UnfollowTreeMutation = { unfollowTree: boolean };
export type PinIssueMutationVariables = Exact<{
issueId: string;
}>;
@@ -1321,6 +1335,7 @@ export type EpicsQueryVariables = Exact<{
export type EpicsQuery = {
epics: Array<{
issue: {
followingTree: boolean;
id: string;
projectId: string;
key: string;
@@ -3820,6 +3835,7 @@ export const IssueFieldsFragmentDoc = {
name: { kind: 'Name', value: 'IssueSummaryFields' },
},
{ kind: 'Field', name: { kind: 'Name', value: 'pinned' } },
{ kind: 'Field', name: { kind: 'Name', value: 'followingTree' } },
{
kind: 'Field',
name: { kind: 'Name', value: 'decisions' },
@@ -6420,6 +6436,7 @@ export const IssueDocument = {
name: { kind: 'Name', value: 'IssueSummaryFields' },
},
{ kind: 'Field', name: { kind: 'Name', value: 'pinned' } },
{ kind: 'Field', name: { kind: 'Name', value: 'followingTree' } },
{
kind: 'Field',
name: { kind: 'Name', value: 'decisions' },
@@ -7452,6 +7469,93 @@ export const MyPinsDocument = {
},
],
} as unknown as DocumentNode<MyPinsQuery, MyPinsQueryVariables>;
export const FollowTreeDocument = {
kind: 'Document',
definitions: [
{
kind: 'OperationDefinition',
operation: 'mutation',
name: { kind: 'Name', value: 'FollowTree' },
variableDefinitions: [
{
kind: 'VariableDefinition',
variable: {
kind: 'Variable',
name: { kind: 'Name', value: 'issueId' },
},
type: {
kind: 'NonNullType',
type: { kind: 'NamedType', name: { kind: 'Name', value: 'ID' } },
},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: { kind: 'Name', value: 'followTree' },
arguments: [
{
kind: 'Argument',
name: { kind: 'Name', value: 'issueId' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'issueId' },
},
},
],
},
],
},
},
],
} as unknown as DocumentNode<FollowTreeMutation, FollowTreeMutationVariables>;
export const UnfollowTreeDocument = {
kind: 'Document',
definitions: [
{
kind: 'OperationDefinition',
operation: 'mutation',
name: { kind: 'Name', value: 'UnfollowTree' },
variableDefinitions: [
{
kind: 'VariableDefinition',
variable: {
kind: 'Variable',
name: { kind: 'Name', value: 'issueId' },
},
type: {
kind: 'NonNullType',
type: { kind: 'NamedType', name: { kind: 'Name', value: 'ID' } },
},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: { kind: 'Name', value: 'unfollowTree' },
arguments: [
{
kind: 'Argument',
name: { kind: 'Name', value: 'issueId' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'issueId' },
},
},
],
},
],
},
},
],
} as unknown as DocumentNode<
UnfollowTreeMutation,
UnfollowTreeMutationVariables
>;
export const PinIssueDocument = {
kind: 'Document',
definitions: [
@@ -8633,6 +8737,10 @@ export const EpicsDocument = {
kind: 'FragmentSpread',
name: { kind: 'Name', value: 'IssueSummaryFields' },
},
{
kind: 'Field',
name: { kind: 'Name', value: 'followingTree' },
},
{
kind: 'Field',
name: { kind: 'Name', value: 'links' },