feat(tasks): browse Jira and start workspaces on mobile (#28616)

Browse Jira on mobile and create linked workspaces through the paired desktop, retaining shared task queries and protecting account/session changes.

Co-authored-by: Minho Yi <tisohjung@gmail.com>
This commit is contained in:
Neil
2026-10-14 03:28:11 -07:00
committed by GitHub
co-authored by Minho Yi
parent 5ae088a24b
commit cacce1790d
124 changed files with 20633 additions and 10266 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -265,6 +265,9 @@
}
}
},
"868d9c7eed63": {
"host-1": ["github", "jira"]
},
"8fce29bab175": {
"name": "settings.get#1",
"ordinal": 1,
@@ -391,6 +394,13 @@
}
}
},
"a940d6fc7d14": {
"name": "providers",
"ordinal": 7,
"value": {
"host-1": ["github", "jira"]
}
},
"aa3490f8f6f8": {
"name": "linear.status#1",
"ordinal": 6,
@@ -626,8 +636,8 @@
"settlements": {
"load": "eb79a9b3682a"
},
"state": "79b8c1b0d1d1",
"effects": ["9037ac778ace"]
"state": "868d9c7eed63",
"effects": ["a940d6fc7d14"]
}
},
{
@@ -638,8 +648,8 @@
"settlements": {
"load": "eb79a9b3682a"
},
"state": "79b8c1b0d1d1",
"effects": ["9037ac778ace"]
"state": "868d9c7eed63",
"effects": ["a940d6fc7d14"]
}
},
{
@@ -650,8 +660,8 @@
"settlements": {
"load": "eb79a9b3682a"
},
"state": "79b8c1b0d1d1",
"effects": ["9037ac778ace"]
"state": "868d9c7eed63",
"effects": ["a940d6fc7d14"]
}
},
{
@@ -662,8 +672,8 @@
"settlements": {
"load": "eb79a9b3682a"
},
"state": "79b8c1b0d1d1",
"effects": ["9037ac778ace"]
"state": "868d9c7eed63",
"effects": ["a940d6fc7d14"]
}
},
{
@@ -674,8 +684,8 @@
"settlements": {
"load": "eb79a9b3682a"
},
"state": "79b8c1b0d1d1",
"effects": ["9037ac778ace"]
"state": "868d9c7eed63",
"effects": ["a940d6fc7d14"]
}
},
{
@@ -686,8 +696,8 @@
"settlements": {
"load": "eb79a9b3682a"
},
"state": "79b8c1b0d1d1",
"effects": ["9037ac778ace"]
"state": "868d9c7eed63",
"effects": ["a940d6fc7d14"]
}
},
{
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -41,18 +41,13 @@
},
"trust": {}
},
"4083eac25622": {
"3d249e57942b": {
"name": "ui.get#1",
"ordinal": 8,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"41a2214deb19": {
"name": "settings.get#1",
"ordinal": 3,
"ordinal": 5,
"args": [
{
"name": "method",
"value": "settings.get"
"value": "ui.get"
},
{
"name": "params",
@@ -141,19 +136,43 @@
}
}
},
"5ce265803a9c": {
"name": "settings.get#1",
"ordinal": 7,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
"65e027dec060": {
"name": "ui.get#1",
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-5",
"ok": true,
"result": {
"ui": {}
}
}
}
},
"6953830b50a0": {
"name": "linear.status#1",
"ordinal": 6,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"843edb309e61": {
"7c5e5635081d": {
"name": "settings.get#1",
"ordinal": 3,
"ordinal": 4,
"args": [
{
"name": "method",
@@ -172,6 +191,63 @@
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"82132b79b8f7": {
"name": "settings.get#1",
"ordinal": 9,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"83e12a6721ec": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "jira.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"8566e2463d90": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "jira.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
@@ -180,13 +256,7 @@
"id": "frame-3",
"ok": true,
"result": {
"settings": {
"defaultTuiAgent": "codex",
"disabledTuiAgents": ["claude"],
"hostSettingOverrides": {},
"prBotAuthorOverrides": ["bot-user"],
"visibleTaskProviders": ["github", "linear"]
}
"connected": false
}
}
}
@@ -296,6 +366,11 @@
}
}
},
"a2fd5d449008": {
"name": "jira.status#1",
"ordinal": 8,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"jira.status\"}"
},
"a5904993256c": {
"name": "linear.status#1",
"ordinal": 2,
@@ -331,6 +406,11 @@
}
}
},
"ab92af857792": {
"name": "linear.status#1",
"ordinal": 7,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"ae4e5de4f5c3": {
"name": "linear.status#1",
"ordinal": 2,
@@ -423,13 +503,18 @@
"startedAt": 0
}
},
"d57386932daa": {
"name": "ui.get#1",
"cc61428a1b82": {
"name": "preflight.check#1",
"ordinal": 6,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
},
"d13ca77439fc": {
"name": "settings.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
"value": "settings.get"
},
{
"name": "params",
@@ -452,7 +537,13 @@
"id": "frame-4",
"ok": true,
"result": {
"ui": {}
"settings": {
"defaultTuiAgent": "codex",
"disabledTuiAgents": ["claude"],
"hostSettingOverrides": {},
"prBotAuthorOverrides": ["bot-user"],
"visibleTaskProviders": ["github", "linear"]
}
}
}
}
@@ -491,32 +582,6 @@
}
}
},
"e1a668fc41b0": {
"name": "ui.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"e7ae27078017": {
"name": "linear.status#1",
"ordinal": 2,
@@ -560,6 +625,11 @@
"$rpc": "undefined"
}
},
"edb8f0fe6906": {
"name": "ui.get#1",
"ordinal": 10,
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"f6a09f8c5b85": {
"providers": [],
"settings": {
@@ -632,11 +702,6 @@
"ok": true
}
}
},
"ff2933c7056d": {
"name": "preflight.check#1",
"ordinal": 5,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
}
},
"recording": {
@@ -645,8 +710,20 @@
{
"id": "settings-workspace-context-fulfilled.prelude:settings-pending",
"observation": {
"sender": ["b69f454508d6", "1e7f9a45facb", "41a2214deb19", "e1a668fc41b0"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"b69f454508d6",
"1e7f9a45facb",
"83e12a6721ec",
"7c5e5635081d",
"3d249e57942b"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -657,8 +734,20 @@
{
"id": "settings-workspace-context-fulfilled.normal:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -669,8 +758,20 @@
{
"id": "settings-workspace-context-fulfilled.result-absent:settled",
"observation": {
"sender": ["903ba5a79900", "fb159a391069", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"fb159a391069",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -681,8 +782,20 @@
{
"id": "settings-workspace-context-fulfilled.result-null:settled",
"observation": {
"sender": ["903ba5a79900", "ae4e5de4f5c3", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"ae4e5de4f5c3",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -693,8 +806,20 @@
{
"id": "settings-workspace-context-fulfilled.inner-ok-missing:settled",
"observation": {
"sender": ["903ba5a79900", "db838df1b33f", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"db838df1b33f",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -705,8 +830,20 @@
{
"id": "settings-workspace-context-fulfilled.inner-false-string-error:settled",
"observation": {
"sender": ["903ba5a79900", "9933bcbce0d0", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9933bcbce0d0",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -717,8 +854,20 @@
{
"id": "settings-workspace-context-fulfilled.inner-false-object-error:settled",
"observation": {
"sender": ["903ba5a79900", "4d17d8c28bae", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"4d17d8c28bae",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -729,8 +878,20 @@
{
"id": "settings-workspace-context-fulfilled.outer-refused:settled",
"observation": {
"sender": ["903ba5a79900", "fa98ce663de2", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"fa98ce663de2",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -741,8 +902,20 @@
{
"id": "settings-workspace-context-fulfilled.outer-refused-no-message:settled",
"observation": {
"sender": ["903ba5a79900", "e7ae27078017", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"e7ae27078017",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -753,8 +926,20 @@
{
"id": "settings-workspace-context-fulfilled.method-not-found:settled",
"observation": {
"sender": ["903ba5a79900", "a5904993256c", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"a5904993256c",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -765,8 +950,20 @@
{
"id": "settings-workspace-context-fulfilled.transport-rejection:settled",
"observation": {
"sender": ["903ba5a79900", "43071d87f881", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"43071d87f881",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -777,8 +974,20 @@
{
"id": "settings-workspace-context-fulfilled.transport-rejection-no-message:settled",
"observation": {
"sender": ["903ba5a79900", "b4d0a3f6b29d", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"b4d0a3f6b29d",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -112,18 +112,13 @@
},
"trust": {}
},
"4083eac25622": {
"3d249e57942b": {
"name": "ui.get#1",
"ordinal": 8,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"41a2214deb19": {
"name": "settings.get#1",
"ordinal": 3,
"ordinal": 5,
"args": [
{
"name": "method",
"value": "settings.get"
"value": "ui.get"
},
{
"name": "params",
@@ -143,15 +138,39 @@
"startedAt": 0
}
},
"5ce265803a9c": {
"name": "settings.get#1",
"ordinal": 7,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"6953830b50a0": {
"name": "linear.status#1",
"ordinal": 6,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
"65e027dec060": {
"name": "ui.get#1",
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-5",
"ok": true,
"result": {
"ui": {}
}
}
}
},
"7c1703d13d10": {
"name": "preflight.check#1",
@@ -188,9 +207,9 @@
}
}
},
"843edb309e61": {
"7c5e5635081d": {
"name": "settings.get#1",
"ordinal": 3,
"ordinal": 4,
"args": [
{
"name": "method",
@@ -209,6 +228,63 @@
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"82132b79b8f7": {
"name": "settings.get#1",
"ordinal": 9,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"83e12a6721ec": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "jira.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"8566e2463d90": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "jira.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
@@ -217,13 +293,7 @@
"id": "frame-3",
"ok": true,
"result": {
"settings": {
"defaultTuiAgent": "codex",
"disabledTuiAgents": ["claude"],
"hostSettingOverrides": {},
"prBotAuthorOverrides": ["bot-user"],
"visibleTaskProviders": ["github", "linear"]
}
"connected": false
}
}
}
@@ -368,6 +438,16 @@
}
}
},
"a2fd5d449008": {
"name": "jira.status#1",
"ordinal": 8,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"jira.status\"}"
},
"ab92af857792": {
"name": "linear.status#1",
"ordinal": 7,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"ad7164511835": {
"name": "preflight.check#1",
"ordinal": 1,
@@ -461,6 +541,11 @@
}
}
},
"cc61428a1b82": {
"name": "preflight.check#1",
"ordinal": 6,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
},
"d0d877eb4118": {
"name": "preflight.check#1",
"ordinal": 1,
@@ -492,6 +577,46 @@
}
}
},
"d13ca77439fc": {
"name": "settings.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "settings.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-4",
"ok": true,
"result": {
"settings": {
"defaultTuiAgent": "codex",
"disabledTuiAgents": ["claude"],
"hostSettingOverrides": {},
"prBotAuthorOverrides": ["bot-user"],
"visibleTaskProviders": ["github", "linear"]
}
}
}
}
},
"d1af7ef29aa6": {
"name": "preflight.check#1",
"ordinal": 1,
@@ -526,66 +651,6 @@
}
}
},
"d57386932daa": {
"name": "ui.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-4",
"ok": true,
"result": {
"ui": {}
}
}
}
},
"e1a668fc41b0": {
"name": "ui.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"eb79a9b3682a": {
"status": "fulfilled",
"startedAt": 0,
@@ -594,6 +659,11 @@
"$rpc": "undefined"
}
},
"edb8f0fe6906": {
"name": "ui.get#1",
"ordinal": 10,
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"f6a09f8c5b85": {
"providers": [],
"settings": {
@@ -632,11 +702,6 @@
"isRpcDeliveryUnknown": true
}
}
},
"ff2933c7056d": {
"name": "preflight.check#1",
"ordinal": 5,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
}
},
"recording": {
@@ -645,8 +710,20 @@
{
"id": "settings-workspace-context-fulfilled.prelude:settings-pending",
"observation": {
"sender": ["b69f454508d6", "1e7f9a45facb", "41a2214deb19", "e1a668fc41b0"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"b69f454508d6",
"1e7f9a45facb",
"83e12a6721ec",
"7c5e5635081d",
"3d249e57942b"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -657,8 +734,20 @@
{
"id": "settings-workspace-context-fulfilled.normal:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -669,8 +758,20 @@
{
"id": "settings-workspace-context-fulfilled.result-absent:settled",
"observation": {
"sender": ["d0d877eb4118", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"d0d877eb4118",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -681,8 +782,20 @@
{
"id": "settings-workspace-context-fulfilled.result-null:settled",
"observation": {
"sender": ["d1af7ef29aa6", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"d1af7ef29aa6",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -693,8 +806,20 @@
{
"id": "settings-workspace-context-fulfilled.inner-ok-missing:settled",
"observation": {
"sender": ["19d55040ae04", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"19d55040ae04",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -705,8 +830,20 @@
{
"id": "settings-workspace-context-fulfilled.inner-false-string-error:settled",
"observation": {
"sender": ["7c1703d13d10", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"7c1703d13d10",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -717,8 +854,20 @@
{
"id": "settings-workspace-context-fulfilled.inner-false-object-error:settled",
"observation": {
"sender": ["0e109980fe2a", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"0e109980fe2a",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -729,8 +878,20 @@
{
"id": "settings-workspace-context-fulfilled.outer-refused:settled",
"observation": {
"sender": ["925a9a6755cc", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"925a9a6755cc",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -741,8 +902,20 @@
{
"id": "settings-workspace-context-fulfilled.outer-refused-no-message:settled",
"observation": {
"sender": ["9b031957c39f", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"9b031957c39f",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -753,8 +926,20 @@
{
"id": "settings-workspace-context-fulfilled.method-not-found:settled",
"observation": {
"sender": ["c4022214e035", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"c4022214e035",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -765,8 +950,20 @@
{
"id": "settings-workspace-context-fulfilled.transport-rejection:settled",
"observation": {
"sender": ["f9b8f7e735b9", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"f9b8f7e735b9",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -777,8 +974,20 @@
{
"id": "settings-workspace-context-fulfilled.transport-rejection-no-message:settled",
"observation": {
"sender": ["ad7164511835", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"ad7164511835",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
File diff suppressed because it is too large Load Diff
@@ -4,9 +4,9 @@
"family": "settings.workspace-context",
"namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"],
"values": {
"06691dc6622b": {
"13671907ef4b": {
"name": "ui.get#1",
"ordinal": 4,
"ordinal": 5,
"args": [
{
"name": "method",
@@ -30,8 +30,12 @@
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-4",
"ok": true
"error": {
"code": "refused",
"message": ""
},
"id": "frame-5",
"ok": false
}
}
},
@@ -61,6 +65,41 @@
"startedAt": 0
}
},
"22cba9e551ea": {
"name": "ui.get#1",
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-5",
"ok": true,
"result": {
"error": "inner refused",
"ok": false
}
}
}
},
"2a7485a88169": {
"providers": ["github"],
"settings": {
@@ -72,9 +111,9 @@
},
"trust": {}
},
"324e59f7e794": {
"2b9cf944a5e5": {
"name": "ui.get#1",
"ordinal": 4,
"ordinal": 5,
"args": [
{
"name": "method",
@@ -98,44 +137,7 @@
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-4",
"ok": true,
"result": {
"error": {
"message": "inner refused"
},
"ok": false
}
}
}
},
"37c54ca9bc9e": {
"name": "ui.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-4",
"id": "frame-5",
"ok": true,
"result": {
"$rpc": "null"
@@ -143,75 +145,9 @@
}
}
},
"407d3a18d6c9": {
"30c7a0e1beac": {
"name": "ui.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"error": {
"code": "refused",
"message": ""
},
"id": "frame-4",
"ok": false
}
}
},
"4083eac25622": {
"name": "ui.get#1",
"ordinal": 8,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"41a2214deb19": {
"name": "settings.get#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "settings.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"4bbb94b5112a": {
"name": "ui.get#1",
"ordinal": 4,
"ordinal": 5,
"args": [
{
"name": "method",
@@ -239,14 +175,14 @@
"code": "method_not_found",
"message": "Unknown method"
},
"id": "frame-4",
"id": "frame-5",
"ok": false
}
}
},
"550b57bd70a0": {
"3d249e57942b": {
"name": "ui.get#1",
"ordinal": 4,
"ordinal": 5,
"args": [
{
"name": "method",
@@ -266,29 +202,82 @@
}
],
"settlement": {
"status": "rejected",
"status": "pending",
"startedAt": 0
}
},
"65e027dec060": {
"name": "ui.get#1",
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"error": {
"category": "Error",
"message": "transport failure",
"isRpcDeliveryUnknown": true
"value": {
"id": "frame-5",
"ok": true,
"result": {
"ui": {}
}
}
}
},
"5ce265803a9c": {
"name": "settings.get#1",
"ordinal": 7,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
"6f51f4002cd1": {
"name": "ui.get#1",
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"error": {
"code": "refused",
"message": "outer refused"
},
"id": "frame-5",
"ok": false
}
}
},
"6953830b50a0": {
"name": "linear.status#1",
"ordinal": 6,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"843edb309e61": {
"7c5e5635081d": {
"name": "settings.get#1",
"ordinal": 3,
"ordinal": 4,
"args": [
{
"name": "method",
@@ -307,6 +296,63 @@
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"82132b79b8f7": {
"name": "settings.get#1",
"ordinal": 9,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"83e12a6721ec": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "jira.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"8566e2463d90": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "jira.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
@@ -315,13 +361,7 @@
"id": "frame-3",
"ok": true,
"result": {
"settings": {
"defaultTuiAgent": "codex",
"disabledTuiAgents": ["claude"],
"hostSettingOverrides": {},
"prBotAuthorOverrides": ["bot-user"],
"visibleTaskProviders": ["github", "linear"]
}
"connected": false
}
}
}
@@ -362,9 +402,9 @@
}
}
},
"95c172db2861": {
"9b200288ea2b": {
"name": "ui.get#1",
"ordinal": 4,
"ordinal": 5,
"args": [
{
"name": "method",
@@ -384,13 +424,15 @@
}
],
"settlement": {
"status": "rejected",
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"error": {
"category": "Error",
"message": "",
"isRpcDeliveryUnknown": true
"value": {
"id": "frame-5",
"ok": true,
"result": {
"error": "refused"
}
}
}
},
@@ -428,6 +470,48 @@
}
}
},
"a0c220ccb9d7": {
"name": "ui.get#1",
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "rejected",
"startedAt": 0,
"settledAt": 0,
"error": {
"category": "Error",
"message": "",
"isRpcDeliveryUnknown": true
}
}
},
"a2fd5d449008": {
"name": "jira.status#1",
"ordinal": 8,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"jira.status\"}"
},
"ab92af857792": {
"name": "linear.status#1",
"ordinal": 7,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"b69f454508d6": {
"name": "preflight.check#1",
"ordinal": 1,
@@ -454,9 +538,41 @@
"startedAt": 0
}
},
"c18fa2dd43dc": {
"bb3d43ca38e2": {
"name": "ui.get#1",
"ordinal": 4,
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "rejected",
"startedAt": 0,
"settledAt": 0,
"error": {
"category": "Error",
"message": "transport failure",
"isRpcDeliveryUnknown": true
}
}
},
"bd326dadc78a": {
"name": "ui.get#1",
"ordinal": 5,
"args": [
{
"name": "method",
@@ -480,17 +596,20 @@
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-4",
"id": "frame-5",
"ok": true,
"result": {
"error": "refused"
"error": {
"message": "inner refused"
},
"ok": false
}
}
}
},
"d57386932daa": {
"cbd33dd63b3b": {
"name": "ui.get#1",
"ordinal": 4,
"ordinal": 5,
"args": [
{
"name": "method",
@@ -514,21 +633,23 @@
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-4",
"ok": true,
"result": {
"ui": {}
}
"id": "frame-5",
"ok": true
}
}
},
"e1a668fc41b0": {
"name": "ui.get#1",
"cc61428a1b82": {
"name": "preflight.check#1",
"ordinal": 6,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
},
"d13ca77439fc": {
"name": "settings.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
"value": "settings.get"
},
{
"name": "params",
@@ -544,8 +665,22 @@
}
],
"settlement": {
"status": "pending",
"startedAt": 0
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-4",
"ok": true,
"result": {
"settings": {
"defaultTuiAgent": "codex",
"disabledTuiAgents": ["claude"],
"hostSettingOverrides": {},
"prBotAuthorOverrides": ["bot-user"],
"visibleTaskProviders": ["github", "linear"]
}
}
}
}
},
"eb79a9b3682a": {
@@ -556,87 +691,17 @@
"$rpc": "undefined"
}
},
"edb8f0fe6906": {
"name": "ui.get#1",
"ordinal": 10,
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"f6a09f8c5b85": {
"providers": [],
"settings": {
"$rpc": "null"
},
"trust": {}
},
"f7dfd8b22a88": {
"name": "ui.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"error": {
"code": "refused",
"message": "outer refused"
},
"id": "frame-4",
"ok": false
}
}
},
"fda507c7ee8e": {
"name": "ui.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-4",
"ok": true,
"result": {
"error": "inner refused",
"ok": false
}
}
}
},
"ff2933c7056d": {
"name": "preflight.check#1",
"ordinal": 5,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
}
},
"recording": {
@@ -645,8 +710,20 @@
{
"id": "settings-workspace-context-fulfilled.prelude:settings-pending",
"observation": {
"sender": ["b69f454508d6", "1e7f9a45facb", "41a2214deb19", "e1a668fc41b0"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"b69f454508d6",
"1e7f9a45facb",
"83e12a6721ec",
"7c5e5635081d",
"3d249e57942b"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -657,8 +734,20 @@
{
"id": "settings-workspace-context-fulfilled.normal:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -669,8 +758,20 @@
{
"id": "settings-workspace-context-fulfilled.result-absent:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "06691dc6622b"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"cbd33dd63b3b"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -681,8 +782,20 @@
{
"id": "settings-workspace-context-fulfilled.result-null:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "37c54ca9bc9e"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"2b9cf944a5e5"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -693,8 +806,20 @@
{
"id": "settings-workspace-context-fulfilled.inner-ok-missing:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "c18fa2dd43dc"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"9b200288ea2b"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -705,8 +830,20 @@
{
"id": "settings-workspace-context-fulfilled.inner-false-string-error:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "fda507c7ee8e"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"22cba9e551ea"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -717,8 +854,20 @@
{
"id": "settings-workspace-context-fulfilled.inner-false-object-error:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "324e59f7e794"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"bd326dadc78a"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -729,8 +878,20 @@
{
"id": "settings-workspace-context-fulfilled.outer-refused:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "f7dfd8b22a88"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"6f51f4002cd1"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -741,8 +902,20 @@
{
"id": "settings-workspace-context-fulfilled.outer-refused-no-message:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "407d3a18d6c9"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"13671907ef4b"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -753,8 +926,20 @@
{
"id": "settings-workspace-context-fulfilled.method-not-found:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "4bbb94b5112a"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"30c7a0e1beac"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -765,8 +950,20 @@
{
"id": "settings-workspace-context-fulfilled.transport-rejection:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "550b57bd70a0"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"bb3d43ca38e2"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -777,8 +974,20 @@
{
"id": "settings-workspace-context-fulfilled.transport-rejection-no-message:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "95c172db2861"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"a0c220ccb9d7"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
File diff suppressed because it is too large Load Diff
@@ -30,6 +30,38 @@
"startedAt": 0
}
},
"21ac0fbb969f": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "jira.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "rejected",
"startedAt": 0,
"settledAt": 30000,
"error": {
"category": "Error",
"message": "Request timed out: jira.status",
"isRpcDeliveryUnknown": true
}
}
},
"21b939d94e15": {
"name": "preflight.check#1",
"ordinal": 1,
@@ -67,6 +99,70 @@
}
}
},
"232e57d66a75": {
"name": "settings.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "settings.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "rejected",
"startedAt": 0,
"settledAt": 30000,
"error": {
"category": "Error",
"message": "Request timed out: settings.get",
"isRpcDeliveryUnknown": true
}
}
},
"2703952e1ca3": {
"name": "ui.get#1",
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "rejected",
"startedAt": 0,
"settledAt": 0,
"error": {
"category": "Error",
"message": "ui.get#1 rejected",
"isRpcDeliveryUnknown": true
}
}
},
"273d1f7fd962": {
"name": "preflight.check#1",
"ordinal": 1,
@@ -142,48 +238,9 @@
},
"trust": {}
},
"2b0924eeed31": {
"name": "ui.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "rejected",
"startedAt": 0,
"settledAt": 0,
"error": {
"category": "Error",
"message": "Connection lost",
"isRpcDeliveryUnknown": true
}
}
},
"3a834cb85dd8": {
"providers": ["github"],
"settings": {
"$rpc": "null"
},
"trust": {}
},
"3d34ae2b099f": {
"37c4e321f6ed": {
"name": "settings.get#1",
"ordinal": 3,
"ordinal": 4,
"args": [
{
"name": "method",
@@ -213,18 +270,13 @@
}
}
},
"4083eac25622": {
"3d249e57942b": {
"name": "ui.get#1",
"ordinal": 8,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"41a2214deb19": {
"name": "settings.get#1",
"ordinal": 3,
"ordinal": 5,
"args": [
{
"name": "method",
"value": "settings.get"
"value": "ui.get"
},
{
"name": "params",
@@ -244,14 +296,9 @@
"startedAt": 0
}
},
"5ce265803a9c": {
"name": "settings.get#1",
"ordinal": 7,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"60fc6227fc5c": {
"527c048244cc": {
"name": "ui.get#1",
"ordinal": 4,
"ordinal": 5,
"args": [
{
"name": "method",
@@ -276,19 +323,137 @@
"settledAt": 0,
"error": {
"category": "Error",
"message": "ui.get#1 rejected",
"message": "Connection lost",
"isRpcDeliveryUnknown": true
}
}
},
"6953830b50a0": {
"name": "linear.status#1",
"ordinal": 6,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
"5f38435ef2d3": {
"name": "ui.get#1",
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "rejected",
"startedAt": 0,
"settledAt": 30000,
"error": {
"category": "Error",
"message": "Request timed out: ui.get",
"isRpcDeliveryUnknown": true
}
}
},
"81ce3736f772": {
"65e027dec060": {
"name": "ui.get#1",
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-5",
"ok": true,
"result": {
"ui": {}
}
}
}
},
"7c5e5635081d": {
"name": "settings.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "settings.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"82132b79b8f7": {
"name": "settings.get#1",
"ordinal": 9,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"83e12a6721ec": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "jira.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"84863e6a7119": {
"name": "settings.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
@@ -323,13 +488,13 @@
}
}
},
"843edb309e61": {
"name": "settings.get#1",
"8566e2463d90": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "settings.get"
"value": "jira.status"
},
{
"name": "params",
@@ -352,13 +517,7 @@
"id": "frame-3",
"ok": true,
"result": {
"settings": {
"defaultTuiAgent": "codex",
"disabledTuiAgents": ["claude"],
"hostSettingOverrides": {},
"prBotAuthorOverrides": ["bot-user"],
"visibleTaskProviders": ["github", "linear"]
}
"connected": false
}
}
}
@@ -431,6 +590,13 @@
}
}
},
"9523a5ec7949": {
"providers": ["github", "jira"],
"settings": {
"$rpc": "null"
},
"trust": {}
},
"9cf8484d8b20": {
"name": "linear.status#1",
"ordinal": 2,
@@ -465,13 +631,55 @@
}
}
},
"a3092128f66b": {
"name": "ui.get#1",
"ordinal": 4,
"a2fd5d449008": {
"name": "jira.status#1",
"ordinal": 8,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"jira.status\"}"
},
"aa5197e9af84": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "ui.get"
"value": "jira.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "rejected",
"startedAt": 0,
"settledAt": 0,
"error": {
"category": "Error",
"message": "Connection lost",
"isRpcDeliveryUnknown": true
}
}
},
"ab92af857792": {
"name": "linear.status#1",
"ordinal": 7,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"ad559d2e55ab": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "jira.status"
},
{
"name": "params",
@@ -528,38 +736,6 @@
"startedAt": 0
}
},
"bde13f51dd1e": {
"name": "settings.get#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "settings.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "rejected",
"startedAt": 0,
"settledAt": 0,
"error": {
"category": "Error",
"message": "Connection lost",
"isRpcDeliveryUnknown": true
}
}
},
"c02caa4460cf": {
"name": "linear.status#1",
"ordinal": 2,
@@ -597,13 +773,50 @@
}
}
},
"d57386932daa": {
"name": "ui.get#1",
"c46f950058f0": {
"name": "settings.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
"value": "settings.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "rejected",
"startedAt": 0,
"settledAt": 0,
"error": {
"category": "Error",
"message": "Connection lost",
"isRpcDeliveryUnknown": true
}
}
},
"cc61428a1b82": {
"name": "preflight.check#1",
"ordinal": 6,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
},
"d13ca77439fc": {
"name": "settings.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "settings.get"
},
{
"name": "params",
@@ -626,72 +839,20 @@
"id": "frame-4",
"ok": true,
"result": {
"ui": {}
"settings": {
"defaultTuiAgent": "codex",
"disabledTuiAgents": ["claude"],
"hostSettingOverrides": {},
"prBotAuthorOverrides": ["bot-user"],
"visibleTaskProviders": ["github", "linear"]
}
}
}
}
},
"e1a668fc41b0": {
"e3468ef861b0": {
"name": "ui.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"e982045ef351": {
"name": "settings.get#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "settings.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "rejected",
"startedAt": 0,
"settledAt": 30000,
"error": {
"category": "Error",
"message": "Request timed out: settings.get",
"isRpcDeliveryUnknown": true
}
}
},
"eb25c4a58533": {
"name": "ui.get#1",
"ordinal": 4,
"ordinal": 5,
"args": [
{
"name": "method",
@@ -713,11 +874,16 @@
"settlement": {
"status": "rejected",
"startedAt": 0,
"settledAt": 30000,
"settledAt": 0,
"error": {
"category": "Error",
"message": "Request timed out: ui.get",
"isRpcDeliveryUnknown": true
"category": "LogicalClientCutoverError",
"message": "RPC interrupted by connection migration",
"isRpcDeliveryUnknown": true,
"cause": {
"category": "Error",
"message": "Connection closed",
"isRpcDeliveryUnknown": true
}
}
}
},
@@ -761,17 +927,17 @@
}
}
},
"edb8f0fe6906": {
"name": "ui.get#1",
"ordinal": 10,
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"f6a09f8c5b85": {
"providers": [],
"settings": {
"$rpc": "null"
},
"trust": {}
},
"ff2933c7056d": {
"name": "preflight.check#1",
"ordinal": 5,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
}
},
"recording": {
@@ -780,8 +946,20 @@
{
"id": "settings-workspace-context-fulfilled.forward:sibling-pending",
"observation": {
"sender": ["b69f454508d6", "1e7f9a45facb", "843edb309e61", "e1a668fc41b0"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"b69f454508d6",
"1e7f9a45facb",
"83e12a6721ec",
"d13ca77439fc",
"3d249e57942b"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -792,8 +970,20 @@
{
"id": "settings-workspace-context-fulfilled.forward:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -804,8 +994,20 @@
{
"id": "settings-workspace-context-fulfilled.reverse:sibling-pending",
"observation": {
"sender": ["b69f454508d6", "1e7f9a45facb", "41a2214deb19", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"b69f454508d6",
"1e7f9a45facb",
"83e12a6721ec",
"7c5e5635081d",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -816,8 +1018,20 @@
{
"id": "settings-workspace-context-fulfilled.reverse:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -828,8 +1042,20 @@
{
"id": "settings-workspace-context-fulfilled.both-reject-forward:sibling-pending",
"observation": {
"sender": ["b69f454508d6", "1e7f9a45facb", "3d34ae2b099f", "e1a668fc41b0"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"b69f454508d6",
"1e7f9a45facb",
"83e12a6721ec",
"37c4e321f6ed",
"3d249e57942b"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -840,20 +1066,44 @@
{
"id": "settings-workspace-context-fulfilled.both-reject-forward:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "3d34ae2b099f", "60fc6227fc5c"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"37c4e321f6ed",
"2703952e1ca3"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "3a834cb85dd8",
"state": "9523a5ec7949",
"effects": []
}
},
{
"id": "settings-workspace-context-fulfilled.both-reject-reverse:sibling-pending",
"observation": {
"sender": ["b69f454508d6", "1e7f9a45facb", "41a2214deb19", "60fc6227fc5c"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"b69f454508d6",
"1e7f9a45facb",
"83e12a6721ec",
"7c5e5635081d",
"2703952e1ca3"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -864,20 +1114,44 @@
{
"id": "settings-workspace-context-fulfilled.both-reject-reverse:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "3d34ae2b099f", "60fc6227fc5c"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"37c4e321f6ed",
"2703952e1ca3"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "3a834cb85dd8",
"state": "9523a5ec7949",
"effects": []
}
},
{
"id": "settings-workspace-context-fulfilled.reject-peer-pending:sibling-pending",
"observation": {
"sender": ["b69f454508d6", "1e7f9a45facb", "3d34ae2b099f", "e1a668fc41b0"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"b69f454508d6",
"1e7f9a45facb",
"83e12a6721ec",
"37c4e321f6ed",
"3d249e57942b"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -888,8 +1162,20 @@
{
"id": "settings-workspace-context-fulfilled.reject-peer-pending:settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "3d34ae2b099f", "e1a668fc41b0"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"37c4e321f6ed",
"3d249e57942b"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -900,38 +1186,74 @@
{
"id": "settings-workspace-context-fulfilled.timeout:settled",
"observation": {
"sender": ["273d1f7fd962", "eda20c48bd66", "e982045ef351", "eb25c4a58533"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"273d1f7fd962",
"eda20c48bd66",
"21ac0fbb969f",
"232e57d66a75",
"5f38435ef2d3"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "3a834cb85dd8",
"state": "9523a5ec7949",
"effects": []
}
},
{
"id": "settings-workspace-context-fulfilled.disconnect:settled",
"observation": {
"sender": ["8bec70b5cc2c", "28d96d1b6f24", "bde13f51dd1e", "2b0924eeed31"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"8bec70b5cc2c",
"28d96d1b6f24",
"aa5197e9af84",
"c46f950058f0",
"527c048244cc"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a",
"disconnect": "eb79a9b3682a"
},
"state": "3a834cb85dd8",
"state": "9523a5ec7949",
"effects": []
}
},
{
"id": "settings-workspace-context-fulfilled.client-cutover:settled",
"observation": {
"sender": ["21b939d94e15", "c02caa4460cf", "81ce3736f772", "a3092128f66b"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"21b939d94e15",
"c02caa4460cf",
"ad559d2e55ab",
"84863e6a7119",
"e3468ef861b0"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a",
"cutover": "eb79a9b3682a"
},
"state": "3a834cb85dd8",
"state": "9523a5ec7949",
"effects": []
}
}
@@ -113,13 +113,6 @@
}
}
},
"2bb81a9fc867": {
"name": "providers",
"ordinal": 14,
"value": {
"host-1": ["github"]
}
},
"44136fa355b3": {},
"44e1776d841d": {
"name": "settings.get#2",
@@ -199,6 +192,13 @@
"startedAt": 0
}
},
"56227393d82f": {
"name": "providers",
"ordinal": 14,
"value": {
"host-1": ["github", "jira"]
}
},
"58995bbfcb17": {
"name": "linear.status#1",
"ordinal": 3,
@@ -325,6 +325,9 @@
"79b8c1b0d1d1": {
"host-1": ["github"]
},
"868d9c7eed63": {
"host-1": ["github", "jira"]
},
"8fce29bab175": {
"name": "settings.get#1",
"ordinal": 1,
@@ -521,8 +524,8 @@
"load": "eb79a9b3682a",
"reload": "eb79a9b3682a"
},
"state": "79b8c1b0d1d1",
"effects": ["9037ac778ace", "2bb81a9fc867"]
"state": "868d9c7eed63",
"effects": ["9037ac778ace", "56227393d82f"]
}
}
]
@@ -127,15 +127,8 @@
}
}
},
"79b8c1b0d1d1": {
"host-1": ["github"]
},
"9037ac778ace": {
"name": "providers",
"ordinal": 7,
"value": {
"host-1": ["github"]
}
"868d9c7eed63": {
"host-1": ["github", "jira"]
},
"908e43fdefae": {
"name": "preflight.check#1",
@@ -147,6 +140,13 @@
"ordinal": 4,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"a940d6fc7d14": {
"name": "providers",
"ordinal": 7,
"value": {
"host-1": ["github", "jira"]
}
},
"aa3490f8f6f8": {
"name": "linear.status#1",
"ordinal": 6,
@@ -245,8 +245,8 @@
"settlements": {
"load": "eb79a9b3682a"
},
"state": "79b8c1b0d1d1",
"effects": ["9037ac778ace"]
"state": "868d9c7eed63",
"effects": ["a940d6fc7d14"]
}
}
]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -4,6 +4,16 @@
"family": "settings.task-hydration",
"namedDeltas": [],
"values": {
"00a3ee0646c6": {
"name": "visibleProviders",
"ordinal": 62,
"value": ["github", "jira", "linear"]
},
"00bb6f59b349": {
"name": "linearTeams",
"ordinal": 58,
"value": []
},
"01236e463cd4": {
"name": "linearStatusPickerItem",
"ordinal": 32,
@@ -11,19 +21,43 @@
"$rpc": "null"
}
},
"0557cf6e117f": {
"name": "linearFilter",
"ordinal": 66,
"value": "all"
"0615469bfcf0": {
"name": "preflight.check#1",
"ordinal": 51,
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
},
"08c3599aa1ff": {
"name": "showGitHubKindPicker",
"ordinal": 11,
"value": false
},
"0cff3cfd4bb2": {
"0ebb7f0660a0": {
"name": "showLinearTeamPicker",
"ordinal": 4,
"value": false
},
"1221fd1c3ae9": {
"name": "mergeMethodProjectRow",
"ordinal": 37,
"value": {
"$rpc": "null"
}
},
"12d69a5555de": {
"name": "jira.status#1",
"ordinal": 48,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"jira.status\"}"
},
"16e4f4209bcf": {
"name": "projectRowDetail",
"ordinal": 29,
"value": {
"$rpc": "null"
}
},
"179ed1933972": {
"name": "preflight.check#1",
"ordinal": 45,
"ordinal": 46,
"args": [
{
"name": "method",
@@ -47,25 +81,6 @@
"startedAt": 0
}
},
"0ebb7f0660a0": {
"name": "showLinearTeamPicker",
"ordinal": 4,
"value": false
},
"1221fd1c3ae9": {
"name": "mergeMethodProjectRow",
"ordinal": 37,
"value": {
"$rpc": "null"
}
},
"16e4f4209bcf": {
"name": "projectRowDetail",
"ordinal": 29,
"value": {
"$rpc": "null"
}
},
"1f75fb0d92bd": {
"name": "projectRowItem",
"ordinal": 26,
@@ -73,31 +88,11 @@
"$rpc": "null"
}
},
"265e84a16ebd": {
"name": "githubKind",
"ordinal": 65,
"value": "issues"
},
"294741680522": {
"name": "error",
"ordinal": 42,
"value": ""
},
"2a56620ac048": {
"name": "githubPreset",
"ordinal": 64,
"value": "issues"
},
"2ae77d454712": {
"name": "ui.get#1",
"ordinal": 48,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"2ae9c5d06de2": {
"name": "linearConnected",
"ordinal": 54,
"value": false
},
"2d1b83d7dc85": {
"name": "mergeMethodTaskItem",
"ordinal": 36,
@@ -112,23 +107,58 @@
"$rpc": "null"
}
},
"2db932a72b8a": {
"name": "trustedOrcaHooks",
"ordinal": 54,
"value": {}
},
"2f9b87c56373": {
"name": "selectedLinearWorkspaceId",
"ordinal": 60,
"value": {
"$rpc": "null"
}
},
"301bdbdf1dec": {
"name": "taskStateHydrated",
"ordinal": 1,
"value": false
},
"3186562055fb": {
"name": "provider",
"ordinal": 63,
"value": "github"
},
"37580b6fcf9d": {
"name": "showLinearViewPicker",
"ordinal": 5,
"value": false
},
"3b5e5f172050": {
"name": "linear.status#1",
"ordinal": 46,
"3d60f87d082e": {
"name": "tasksSupportState",
"ordinal": 41,
"value": {
"client": "logical-client",
"kind": "supported"
}
},
"3f5a8bf8fa28": {
"name": "showLinearConnect",
"ordinal": 9,
"value": false
},
"45e466d86a07": {
"name": "showLinearWorkspacePicker",
"ordinal": 3,
"value": false
},
"45ee7e34a0c6": {
"name": "jira.status#1",
"ordinal": 43,
"args": [
{
"name": "method",
"value": "linear.status"
"value": "jira.status"
},
{
"name": "params",
@@ -148,7 +178,7 @@
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-5",
"id": "frame-2",
"ok": true,
"result": {
"connected": false
@@ -156,39 +186,21 @@
}
}
},
"3d60f87d082e": {
"name": "tasksSupportState",
"ordinal": 41,
"value": {
"client": "logical-client",
"kind": "supported"
}
},
"3f5a8bf8fa28": {
"name": "showLinearConnect",
"ordinal": 9,
"value": false
},
"4121b85bfa08": {
"name": "provider",
"ordinal": 61,
"value": "github"
},
"45df0a2741dc": {
"name": "query",
"ordinal": 68,
"value": "is:issue is:open"
},
"45e466d86a07": {
"name": "showLinearWorkspacePicker",
"ordinal": 3,
"value": false
},
"476118adaa02": {
"name": "showGitHubProjectFieldsPicker",
"ordinal": 23,
"value": false
},
"47d8fb4dc2fa": {
"name": "jiraFilter",
"ordinal": 69,
"value": "assigned"
},
"4e99f4f15c83": {
"name": "selectedRepoIds",
"ordinal": 61,
"value": []
},
"506d5254f01c": {
"name": "pendingHostedStateChange",
"ordinal": 35,
@@ -201,9 +213,21 @@
"ordinal": 7,
"value": false
},
"50e8f8e79bbf": {
"52b5f68c6d97": {
"name": "detailPayload",
"ordinal": 28,
"value": {
"$rpc": "null"
}
},
"56cd9b635a03": {
"name": "settings.get#1",
"ordinal": 49,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"57564581f448": {
"name": "ui.get#1",
"ordinal": 44,
"ordinal": 45,
"args": [
{
"name": "method",
@@ -222,125 +246,26 @@
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-3",
"ok": true,
"result": {
"ui": {}
}
}
}
},
"52b5f68c6d97": {
"name": "detailPayload",
"ordinal": 28,
"value": {
"$rpc": "null"
}
},
"558df6e44ef7": {
"name": "settings.get#1",
"ordinal": 47,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"5746a2b64cdf": {
"name": "runtimeTaskSettings",
"ordinal": 51,
"value": {}
},
"575605e669c4": {
"name": "selectedLinearTeamIds",
"ordinal": 57,
"value": []
},
"5a33c7e88b8e": {
"name": "defaultGitHubPreset",
"ordinal": 63,
"value": "issues"
},
"644df42439c4": {
"name": "linearWorkspaces",
"ordinal": 55,
"value": []
},
"6ffbb1794410": {
"name": "showLinearFilterPicker",
"ordinal": 15,
"value": false
},
"7197da94df94": {
"name": "showSortPicker",
"ordinal": 16,
"value": false
},
"796a64aabbc9": {
"name": "showGitLabViewPicker",
"ordinal": 13,
"value": false
},
"7b0ad7b958dd": {
"name": "tasksSupportState",
"ordinal": 2,
"value": {
"client": "logical-client",
"kind": "unknown"
}
},
"7d496ccb65c6": {
"name": "linear.status#1",
"ordinal": 46,
"args": [
{
"name": "method",
"value": "linear.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"7d8029e889cf": {
"name": "projectRepoNotInOrca",
"ordinal": 27,
"59d693e0473a": {
"name": "githubProjectSettings",
"ordinal": 70,
"value": {
"$rpc": "null"
"activeProject": {
"$rpc": "null"
},
"lastViewByProject": {},
"pinned": [],
"recent": []
}
},
"8515b9b6e08f": {
"name": "linear.status#1",
"ordinal": 50,
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"8616eb8e398b": {
"name": "showGitHubProjectSortPicker",
"ordinal": 22,
"value": false
},
"89a5d2a4b349": {
"name": "showProviderPicker",
"ordinal": 10,
"value": false
},
"8a1c7137714e": {
"619882ebba08": {
"name": "settings.get#1",
"ordinal": 43,
"ordinal": 44,
"args": [
{
"name": "method",
@@ -364,18 +289,103 @@
"startedAt": 0
}
},
"8d124d8922c8": {
"name": "showGitHubProjectViewPicker",
"ordinal": 21,
"6e8e6076a1ea": {
"name": "linear.status#1",
"ordinal": 52,
"json": "{\"id\":\"frame-6\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"6ffbb1794410": {
"name": "showLinearFilterPicker",
"ordinal": 15,
"value": false
},
"9055388dbe30": {
"name": "preflight.check#1",
"7197da94df94": {
"name": "showSortPicker",
"ordinal": 16,
"value": false
},
"71ac87a5a2f4": {
"name": "githubMode",
"ordinal": 64,
"value": "items"
},
"7256d0e13440": {
"name": "runtimeTaskSettings",
"ordinal": 53,
"value": {}
},
"796a64aabbc9": {
"name": "showGitLabViewPicker",
"ordinal": 13,
"value": false
},
"7b0ad7b958dd": {
"name": "tasksSupportState",
"ordinal": 2,
"value": {
"client": "logical-client",
"kind": "unknown"
}
},
"7d8029e889cf": {
"name": "projectRepoNotInOrca",
"ordinal": 27,
"value": {
"$rpc": "null"
}
},
"80faddbc5268": {
"name": "settings.get#1",
"ordinal": 44,
"args": [
{
"name": "method",
"value": "settings.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"error": {
"code": "refused",
"message": "settings refused"
},
"id": "frame-3",
"ok": false
}
}
},
"8616eb8e398b": {
"name": "showGitHubProjectSortPicker",
"ordinal": 22,
"value": false
},
"86cfb41d0cb8": {
"name": "linearFilter",
"ordinal": 68,
"value": "all"
},
"86ed8e03ac41": {
"name": "ui.get#1",
"ordinal": 45,
"args": [
{
"name": "method",
"value": "preflight.check"
"value": "ui.get"
},
{
"name": "params",
@@ -398,23 +408,41 @@
"id": "frame-4",
"ok": true,
"result": {
"glab": {
"installed": false
}
"ui": {}
}
}
}
},
"89a5d2a4b349": {
"name": "showProviderPicker",
"ordinal": 10,
"value": false
},
"8a48acc3bcf6": {
"name": "query",
"ordinal": 71,
"value": "is:issue is:open"
},
"8b6f54f81646": {
"name": "linearConnected",
"ordinal": 56,
"value": false
},
"8d124d8922c8": {
"name": "showGitHubProjectViewPicker",
"ordinal": 21,
"value": false
},
"8ef027c71140": {
"name": "githubKind",
"ordinal": 67,
"value": "issues"
},
"90ca04055722": {
"name": "showLinearGroupPicker",
"ordinal": 6,
"value": false
},
"90d9c563300c": {
"name": "taskStateHydrated",
"ordinal": 70,
"value": true
},
"917e8342c71e": {
"name": "showCreateTask",
"ordinal": 30,
@@ -454,52 +482,22 @@
}
}
},
"9490fcb6d06d": {
"name": "trustedOrcaHooks",
"ordinal": 52,
"value": {}
},
"963a91c532c8": {
"hydrated": true,
"settings": {}
},
"9c3cbdd83e1d": {
"name": "appliedQuery",
"ordinal": 69,
"value": "is:issue is:open"
},
"a5110db5951d": {
"name": "linearTeams",
"ordinal": 56,
"value": []
},
"ace3d3315687": {
"name": "showGitLabFilterPicker",
"ordinal": 14,
"value": false
},
"b00bd02317c1": {
"name": "showRepoPicker",
"ordinal": 17,
"value": false
},
"b1714fb6ea51": {
"name": "status.get#1",
"ordinal": 40,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"status.get\"}"
},
"b39bd3d3c40b": {
"name": "selectedRepoIds",
"96952d24ad82": {
"name": "selectedLinearTeamIds",
"ordinal": 59,
"value": []
},
"b3f3f6a6069b": {
"name": "ui.get#1",
"ordinal": 44,
"9704b1bab4cd": {
"name": "jira.status#1",
"ordinal": 43,
"args": [
{
"name": "method",
"value": "ui.get"
"value": "jira.status"
},
{
"name": "params",
@@ -519,6 +517,62 @@
"startedAt": 0
}
},
"9b52e4fef056": {
"name": "preflight.check#1",
"ordinal": 46,
"args": [
{
"name": "method",
"value": "preflight.check"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-5",
"ok": true,
"result": {
"glab": {
"installed": false
}
}
}
}
},
"a57d68d030ff": {
"name": "appliedQuery",
"ordinal": 72,
"value": "is:issue is:open"
},
"ace3d3315687": {
"name": "showGitLabFilterPicker",
"ordinal": 14,
"value": false
},
"b00bd02317c1": {
"name": "showRepoPicker",
"ordinal": 17,
"value": false
},
"b1714fb6ea51": {
"name": "status.get#1",
"ordinal": 40,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"status.get\"}"
},
"b7c96dd7bb6a": {
"name": "pendingGitHubProjectViewSelection",
"ordinal": 24,
@@ -531,6 +585,45 @@
"ordinal": 8,
"value": false
},
"bc3e027e3643": {
"name": "ui.get#1",
"ordinal": 50,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"bde7860d3963": {
"name": "linear.status#1",
"ordinal": 47,
"args": [
{
"name": "method",
"value": "linear.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-6",
"ok": true,
"result": {
"connected": false
}
}
}
},
"c4b6b5ed0b10": {
"name": "showCreateTargetPicker",
"ordinal": 31,
@@ -540,16 +633,52 @@
"hydrated": false,
"settings": {}
},
"c967fcbd2446": {
"name": "githubProjectSettings",
"ordinal": 67,
"c82c2272344f": {
"name": "linearWorkspaces",
"ordinal": 57,
"value": []
},
"d00ff103cdc6": {
"name": "linear.status#1",
"ordinal": 47,
"args": [
{
"name": "method",
"value": "linear.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"d495531e67be": {
"name": "jiraConnection",
"ordinal": 74,
"value": {
"activeProject": {
"connected": false,
"credentialError": {
"$rpc": "null"
},
"lastViewByProject": {},
"pinned": [],
"recent": []
"selection": {
"$rpc": "null"
},
"sites": [],
"unavailableReason": {
"$rpc": "null"
}
}
},
"d4db291a4c50": {
@@ -571,10 +700,10 @@
"$rpc": "null"
}
},
"e0887f269af4": {
"name": "preflight.check#1",
"ordinal": 49,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
"dc148a8a8aa1": {
"name": "taskStateHydrated",
"ordinal": 73,
"value": true
},
"e5a4abb9d94f": {
"name": "showGitHubPagePicker",
@@ -586,11 +715,6 @@
"ordinal": 20,
"value": false
},
"e715718a7e8c": {
"name": "githubProjectHiddenFieldIdsByView",
"ordinal": 53,
"value": {}
},
"eb79a9b3682a": {
"status": "fulfilled",
"startedAt": 0,
@@ -599,40 +723,10 @@
"$rpc": "undefined"
}
},
"f2557c836246": {
"name": "settings.get#1",
"ordinal": 43,
"args": [
{
"name": "method",
"value": "settings.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"error": {
"code": "refused",
"message": "settings refused"
},
"id": "frame-2",
"ok": false
}
}
"ed9151884cb7": {
"name": "defaultGitHubPreset",
"ordinal": 65,
"value": "issues"
},
"f3dafd073b87": {
"name": "actionItem",
@@ -641,22 +735,15 @@
"$rpc": "null"
}
},
"f62cf435afca": {
"name": "githubMode",
"ordinal": 62,
"value": "items"
"f4841617ad9d": {
"name": "githubPreset",
"ordinal": 66,
"value": "issues"
},
"f7b209507189": {
"name": "visibleProviders",
"ordinal": 60,
"value": ["github", "linear"]
},
"f7c2d2661209": {
"name": "selectedLinearWorkspaceId",
"ordinal": 58,
"value": {
"$rpc": "null"
}
"f54965c37b48": {
"name": "githubProjectHiddenFieldIdsByView",
"ordinal": 55,
"value": {}
},
"fe7472dae00e": {
"name": "showGitHubPresetPicker",
@@ -672,17 +759,19 @@
"observation": {
"sender": [
"92d285d3f3f0",
"8a1c7137714e",
"b3f3f6a6069b",
"0cff3cfd4bb2",
"7d496ccb65c6"
"9704b1bab4cd",
"619882ebba08",
"57564581f448",
"179ed1933972",
"d00ff103cdc6"
],
"payloads": [
"b1714fb6ea51",
"558df6e44ef7",
"2ae77d454712",
"e0887f269af4",
"8515b9b6e08f"
"12d69a5555de",
"56cd9b635a03",
"bc3e027e3643",
"0615469bfcf0",
"6e8e6076a1ea"
],
"settlements": {
"mount": "eb79a9b3682a"
@@ -737,17 +826,19 @@
"observation": {
"sender": [
"92d285d3f3f0",
"f2557c836246",
"50e8f8e79bbf",
"9055388dbe30",
"3b5e5f172050"
"45ee7e34a0c6",
"80faddbc5268",
"86ed8e03ac41",
"9b52e4fef056",
"bde7860d3963"
],
"payloads": [
"b1714fb6ea51",
"558df6e44ef7",
"2ae77d454712",
"e0887f269af4",
"8515b9b6e08f"
"12d69a5555de",
"56cd9b635a03",
"bc3e027e3643",
"0615469bfcf0",
"6e8e6076a1ea"
],
"settlements": {
"mount": "eb79a9b3682a"
@@ -794,26 +885,28 @@
"da3527bc5f58",
"3d60f87d082e",
"294741680522",
"5746a2b64cdf",
"9490fcb6d06d",
"e715718a7e8c",
"2ae9c5d06de2",
"644df42439c4",
"a5110db5951d",
"575605e669c4",
"f7c2d2661209",
"b39bd3d3c40b",
"f7b209507189",
"4121b85bfa08",
"f62cf435afca",
"5a33c7e88b8e",
"2a56620ac048",
"265e84a16ebd",
"0557cf6e117f",
"c967fcbd2446",
"45df0a2741dc",
"9c3cbdd83e1d",
"90d9c563300c"
"7256d0e13440",
"2db932a72b8a",
"f54965c37b48",
"8b6f54f81646",
"c82c2272344f",
"00bb6f59b349",
"96952d24ad82",
"2f9b87c56373",
"4e99f4f15c83",
"00a3ee0646c6",
"3186562055fb",
"71ac87a5a2f4",
"ed9151884cb7",
"f4841617ad9d",
"8ef027c71140",
"86cfb41d0cb8",
"47d8fb4dc2fa",
"59d693e0473a",
"8a48acc3bcf6",
"a57d68d030ff",
"dc148a8a8aa1",
"d495531e67be"
]
}
}
@@ -11,14 +11,43 @@
"$rpc": "null"
}
},
"0615469bfcf0": {
"name": "preflight.check#1",
"ordinal": 51,
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
},
"08c3599aa1ff": {
"name": "showGitHubKindPicker",
"ordinal": 11,
"value": false
},
"0cff3cfd4bb2": {
"0ebb7f0660a0": {
"name": "showLinearTeamPicker",
"ordinal": 4,
"value": false
},
"1221fd1c3ae9": {
"name": "mergeMethodProjectRow",
"ordinal": 37,
"value": {
"$rpc": "null"
}
},
"12d69a5555de": {
"name": "jira.status#1",
"ordinal": 48,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"jira.status\"}"
},
"16e4f4209bcf": {
"name": "projectRowDetail",
"ordinal": 29,
"value": {
"$rpc": "null"
}
},
"179ed1933972": {
"name": "preflight.check#1",
"ordinal": 45,
"ordinal": 46,
"args": [
{
"name": "method",
@@ -42,25 +71,6 @@
"startedAt": 0
}
},
"0ebb7f0660a0": {
"name": "showLinearTeamPicker",
"ordinal": 4,
"value": false
},
"1221fd1c3ae9": {
"name": "mergeMethodProjectRow",
"ordinal": 37,
"value": {
"$rpc": "null"
}
},
"16e4f4209bcf": {
"name": "projectRowDetail",
"ordinal": 29,
"value": {
"$rpc": "null"
}
},
"1f75fb0d92bd": {
"name": "projectRowItem",
"ordinal": 26,
@@ -73,153 +83,9 @@
"ordinal": 42,
"value": ""
},
"2ae77d454712": {
"name": "ui.get#1",
"ordinal": 48,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"2d1b83d7dc85": {
"name": "mergeMethodTaskItem",
"ordinal": 36,
"value": {
"$rpc": "null"
}
},
"2d7be5d82221": {
"name": "pendingHostedMerge",
"ordinal": 33,
"value": {
"$rpc": "null"
}
},
"301bdbdf1dec": {
"name": "taskStateHydrated",
"ordinal": 1,
"value": false
},
"37580b6fcf9d": {
"name": "showLinearViewPicker",
"ordinal": 5,
"value": false
},
"3b5e5f172050": {
"name": "linear.status#1",
"ordinal": 46,
"args": [
{
"name": "method",
"value": "linear.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-5",
"ok": true,
"result": {
"connected": false
}
}
}
},
"3d60f87d082e": {
"name": "tasksSupportState",
"ordinal": 41,
"value": {
"client": "logical-client",
"kind": "supported"
}
},
"3f5a8bf8fa28": {
"name": "showLinearConnect",
"ordinal": 9,
"value": false
},
"45e466d86a07": {
"name": "showLinearWorkspacePicker",
"ordinal": 3,
"value": false
},
"476118adaa02": {
"name": "showGitHubProjectFieldsPicker",
"ordinal": 23,
"value": false
},
"506d5254f01c": {
"name": "pendingHostedStateChange",
"ordinal": 35,
"value": {
"$rpc": "null"
}
},
"50a55bcbc6c4": {
"name": "showLinearOrderPicker",
"ordinal": 7,
"value": false
},
"50e8f8e79bbf": {
"name": "ui.get#1",
"2a687d29d320": {
"name": "settings.get#1",
"ordinal": 44,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-3",
"ok": true,
"result": {
"ui": {}
}
}
}
},
"52b5f68c6d97": {
"name": "detailPayload",
"ordinal": 28,
"value": {
"$rpc": "null"
}
},
"558df6e44ef7": {
"name": "settings.get#1",
"ordinal": 47,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"5f1ccfa1d4a0": {
"name": "settings.get#1",
"ordinal": 43,
"args": [
{
"name": "method",
@@ -249,10 +115,167 @@
}
}
},
"6be6eaa0cf4d": {
"name": "error",
"ordinal": 51,
"value": "settings disconnected"
"2d1b83d7dc85": {
"name": "mergeMethodTaskItem",
"ordinal": 36,
"value": {
"$rpc": "null"
}
},
"2d7be5d82221": {
"name": "pendingHostedMerge",
"ordinal": 33,
"value": {
"$rpc": "null"
}
},
"301bdbdf1dec": {
"name": "taskStateHydrated",
"ordinal": 1,
"value": false
},
"37580b6fcf9d": {
"name": "showLinearViewPicker",
"ordinal": 5,
"value": false
},
"3d60f87d082e": {
"name": "tasksSupportState",
"ordinal": 41,
"value": {
"client": "logical-client",
"kind": "supported"
}
},
"3f5a8bf8fa28": {
"name": "showLinearConnect",
"ordinal": 9,
"value": false
},
"45e466d86a07": {
"name": "showLinearWorkspacePicker",
"ordinal": 3,
"value": false
},
"45ee7e34a0c6": {
"name": "jira.status#1",
"ordinal": 43,
"args": [
{
"name": "method",
"value": "jira.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-2",
"ok": true,
"result": {
"connected": false
}
}
}
},
"476118adaa02": {
"name": "showGitHubProjectFieldsPicker",
"ordinal": 23,
"value": false
},
"506d5254f01c": {
"name": "pendingHostedStateChange",
"ordinal": 35,
"value": {
"$rpc": "null"
}
},
"50a55bcbc6c4": {
"name": "showLinearOrderPicker",
"ordinal": 7,
"value": false
},
"52b5f68c6d97": {
"name": "detailPayload",
"ordinal": 28,
"value": {
"$rpc": "null"
}
},
"56cd9b635a03": {
"name": "settings.get#1",
"ordinal": 49,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"57564581f448": {
"name": "ui.get#1",
"ordinal": 45,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"619882ebba08": {
"name": "settings.get#1",
"ordinal": 44,
"args": [
{
"name": "method",
"value": "settings.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"6e8e6076a1ea": {
"name": "linear.status#1",
"ordinal": 52,
"json": "{\"id\":\"frame-6\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"6ffbb1794410": {
"name": "showLinearFilterPicker",
@@ -277,32 +300,6 @@
"kind": "unknown"
}
},
"7d496ccb65c6": {
"name": "linear.status#1",
"ordinal": 46,
"args": [
{
"name": "method",
"value": "linear.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"7d8029e889cf": {
"name": "projectRepoNotInOrca",
"ordinal": 27,
@@ -310,59 +307,23 @@
"$rpc": "null"
}
},
"8515b9b6e08f": {
"name": "linear.status#1",
"ordinal": 50,
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
"8413a71cd880": {
"name": "error",
"ordinal": 53,
"value": "settings disconnected"
},
"8616eb8e398b": {
"name": "showGitHubProjectSortPicker",
"ordinal": 22,
"value": false
},
"89a5d2a4b349": {
"name": "showProviderPicker",
"ordinal": 10,
"value": false
},
"8a1c7137714e": {
"name": "settings.get#1",
"ordinal": 43,
"args": [
{
"name": "method",
"value": "settings.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"8d124d8922c8": {
"name": "showGitHubProjectViewPicker",
"ordinal": 21,
"value": false
},
"9055388dbe30": {
"name": "preflight.check#1",
"86ed8e03ac41": {
"name": "ui.get#1",
"ordinal": 45,
"args": [
{
"name": "method",
"value": "preflight.check"
"value": "ui.get"
},
{
"name": "params",
@@ -385,13 +346,21 @@
"id": "frame-4",
"ok": true,
"result": {
"glab": {
"installed": false
}
"ui": {}
}
}
}
},
"89a5d2a4b349": {
"name": "showProviderPicker",
"ordinal": 10,
"value": false
},
"8d124d8922c8": {
"name": "showGitHubProjectViewPicker",
"ordinal": 21,
"value": false
},
"90ca04055722": {
"name": "showLinearGroupPicker",
"ordinal": 6,
@@ -436,28 +405,13 @@
}
}
},
"ace3d3315687": {
"name": "showGitLabFilterPicker",
"ordinal": 14,
"value": false
},
"b00bd02317c1": {
"name": "showRepoPicker",
"ordinal": 17,
"value": false
},
"b1714fb6ea51": {
"name": "status.get#1",
"ordinal": 40,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"status.get\"}"
},
"b3f3f6a6069b": {
"name": "ui.get#1",
"ordinal": 44,
"9704b1bab4cd": {
"name": "jira.status#1",
"ordinal": 43,
"args": [
{
"name": "method",
"value": "ui.get"
"value": "jira.status"
},
{
"name": "params",
@@ -477,6 +431,62 @@
"startedAt": 0
}
},
"9792eb12d563": {
"name": "taskStateHydrated",
"ordinal": 54,
"value": false
},
"9b52e4fef056": {
"name": "preflight.check#1",
"ordinal": 46,
"args": [
{
"name": "method",
"value": "preflight.check"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-5",
"ok": true,
"result": {
"glab": {
"installed": false
}
}
}
}
},
"ace3d3315687": {
"name": "showGitLabFilterPicker",
"ordinal": 14,
"value": false
},
"b00bd02317c1": {
"name": "showRepoPicker",
"ordinal": 17,
"value": false
},
"b1714fb6ea51": {
"name": "status.get#1",
"ordinal": 40,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"status.get\"}"
},
"b7c96dd7bb6a": {
"name": "pendingGitHubProjectViewSelection",
"ordinal": 24,
@@ -489,6 +499,45 @@
"ordinal": 8,
"value": false
},
"bc3e027e3643": {
"name": "ui.get#1",
"ordinal": 50,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"bde7860d3963": {
"name": "linear.status#1",
"ordinal": 47,
"args": [
{
"name": "method",
"value": "linear.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-6",
"ok": true,
"result": {
"connected": false
}
}
}
},
"c4b6b5ed0b10": {
"name": "showCreateTargetPicker",
"ordinal": 31,
@@ -498,6 +547,32 @@
"hydrated": false,
"settings": {}
},
"d00ff103cdc6": {
"name": "linear.status#1",
"ordinal": 47,
"args": [
{
"name": "method",
"value": "linear.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"d4db291a4c50": {
"name": "pendingProjectGitHubMerge",
"ordinal": 34,
@@ -517,15 +592,22 @@
"$rpc": "null"
}
},
"e0887f269af4": {
"name": "preflight.check#1",
"ordinal": 49,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
},
"e0da5ae2a468": {
"name": "taskStateHydrated",
"ordinal": 52,
"value": false
"daff1d33bec7": {
"name": "jiraConnection",
"ordinal": 55,
"value": {
"connected": false,
"credentialError": {
"$rpc": "null"
},
"selection": {
"$rpc": "null"
},
"sites": [],
"unavailableReason": {
"$rpc": "null"
}
}
},
"e5a4abb9d94f": {
"name": "showGitHubPagePicker",
@@ -566,17 +648,19 @@
"observation": {
"sender": [
"92d285d3f3f0",
"8a1c7137714e",
"b3f3f6a6069b",
"0cff3cfd4bb2",
"7d496ccb65c6"
"9704b1bab4cd",
"619882ebba08",
"57564581f448",
"179ed1933972",
"d00ff103cdc6"
],
"payloads": [
"b1714fb6ea51",
"558df6e44ef7",
"2ae77d454712",
"e0887f269af4",
"8515b9b6e08f"
"12d69a5555de",
"56cd9b635a03",
"bc3e027e3643",
"0615469bfcf0",
"6e8e6076a1ea"
],
"settlements": {
"mount": "eb79a9b3682a"
@@ -631,17 +715,19 @@
"observation": {
"sender": [
"92d285d3f3f0",
"5f1ccfa1d4a0",
"50e8f8e79bbf",
"9055388dbe30",
"3b5e5f172050"
"45ee7e34a0c6",
"2a687d29d320",
"86ed8e03ac41",
"9b52e4fef056",
"bde7860d3963"
],
"payloads": [
"b1714fb6ea51",
"558df6e44ef7",
"2ae77d454712",
"e0887f269af4",
"8515b9b6e08f"
"12d69a5555de",
"56cd9b635a03",
"bc3e027e3643",
"0615469bfcf0",
"6e8e6076a1ea"
],
"settlements": {
"mount": "eb79a9b3682a"
@@ -688,8 +774,9 @@
"da3527bc5f58",
"3d60f87d082e",
"294741680522",
"6be6eaa0cf4d",
"e0da5ae2a468"
"8413a71cd880",
"9792eb12d563",
"daff1d33bec7"
]
}
}
@@ -41,14 +41,69 @@
},
"trust": {}
},
"4083eac25622": {
"3d249e57942b": {
"name": "ui.get#1",
"ordinal": 8,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"41a2214deb19": {
"65e027dec060": {
"name": "ui.get#1",
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-5",
"ok": true,
"result": {
"ui": {}
}
}
}
},
"7c5e5635081d": {
"name": "settings.get#1",
"ordinal": 3,
"ordinal": 4,
"args": [
{
"name": "method",
@@ -72,23 +127,44 @@
"startedAt": 0
}
},
"5ce265803a9c": {
"82132b79b8f7": {
"name": "settings.get#1",
"ordinal": 7,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
"ordinal": 9,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"6953830b50a0": {
"name": "linear.status#1",
"ordinal": 6,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"843edb309e61": {
"name": "settings.get#1",
"83e12a6721ec": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "settings.get"
"value": "jira.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"8566e2463d90": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "jira.status"
},
{
"name": "params",
@@ -111,13 +187,7 @@
"id": "frame-3",
"ok": true,
"result": {
"settings": {
"defaultTuiAgent": "codex",
"disabledTuiAgents": ["claude"],
"hostSettingOverrides": {},
"prBotAuthorOverrides": ["bot-user"],
"visibleTaskProviders": ["github", "linear"]
}
"connected": false
}
}
}
@@ -192,6 +262,16 @@
}
}
},
"a2fd5d449008": {
"name": "jira.status#1",
"ordinal": 8,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"jira.status\"}"
},
"ab92af857792": {
"name": "linear.status#1",
"ordinal": 7,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"b69f454508d6": {
"name": "preflight.check#1",
"ordinal": 1,
@@ -218,13 +298,18 @@
"startedAt": 0
}
},
"d57386932daa": {
"name": "ui.get#1",
"cc61428a1b82": {
"name": "preflight.check#1",
"ordinal": 6,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
},
"d13ca77439fc": {
"name": "settings.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
"value": "settings.get"
},
{
"name": "params",
@@ -247,37 +332,17 @@
"id": "frame-4",
"ok": true,
"result": {
"ui": {}
"settings": {
"defaultTuiAgent": "codex",
"disabledTuiAgents": ["claude"],
"hostSettingOverrides": {},
"prBotAuthorOverrides": ["bot-user"],
"visibleTaskProviders": ["github", "linear"]
}
}
}
}
},
"e1a668fc41b0": {
"name": "ui.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"eb79a9b3682a": {
"status": "fulfilled",
"startedAt": 0,
@@ -286,17 +351,17 @@
"$rpc": "undefined"
}
},
"edb8f0fe6906": {
"name": "ui.get#1",
"ordinal": 10,
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"f6a09f8c5b85": {
"providers": [],
"settings": {
"$rpc": "null"
},
"trust": {}
},
"ff2933c7056d": {
"name": "preflight.check#1",
"ordinal": 5,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
}
},
"recording": {
@@ -305,8 +370,20 @@
{
"id": "settings-pending",
"observation": {
"sender": ["b69f454508d6", "1e7f9a45facb", "41a2214deb19", "e1a668fc41b0"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"b69f454508d6",
"1e7f9a45facb",
"83e12a6721ec",
"7c5e5635081d",
"3d249e57942b"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -317,8 +394,20 @@
{
"id": "settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -4,13 +4,44 @@
"family": "settings.workspace-context",
"namedDeltas": [],
"values": {
"1b3e4be6609f": {
"name": "settings.get#2",
"048c28b9a17c": {
"name": "ui.get#2",
"ordinal": 15,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"15e6052de76e": {
"name": "linear.status#2",
"ordinal": 17,
"json": "{\"id\":\"frame-7\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"1aa8c05700c5": {
"name": "preflight.check#2",
"ordinal": 11,
"args": [
{
"name": "method",
"value": "settings.get"
"value": "preflight.check"
},
{
"name": "params",
@@ -67,9 +98,217 @@
},
"trust": {}
},
"3c4258332df3": {
"name": "settings.get#2",
"3335872ee1f8": {
"name": "preflight.check#2",
"ordinal": 11,
"args": [
{
"name": "method",
"value": "preflight.check"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-6",
"ok": true,
"result": {
"glab": {
"installed": false
}
}
}
}
},
"3d249e57942b": {
"name": "ui.get#1",
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"403a5a128034": {
"name": "jira.status#2",
"ordinal": 13,
"args": [
{
"name": "method",
"value": "jira.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-8",
"ok": true,
"result": {
"connected": false
}
}
}
},
"4ab811f6ef4b": {
"name": "linear.status#2",
"ordinal": 12,
"args": [
{
"name": "method",
"value": "linear.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-7",
"ok": true,
"result": {
"connected": false
}
}
}
},
"52f9aae379dd": {
"name": "preflight.check#2",
"ordinal": 16,
"json": "{\"id\":\"frame-6\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
},
"60227e762a73": {
"name": "settings.get#2",
"ordinal": 19,
"json": "{\"id\":\"frame-9\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"65e027dec060": {
"name": "ui.get#1",
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-5",
"ok": true,
"result": {
"ui": {}
}
}
}
},
"65f8ab1a49c2": {
"name": "ui.get#2",
"ordinal": 15,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-10",
"ok": true,
"result": {
"ui": {}
}
}
}
},
"706b8ee96dc7": {
"name": "settings.get#2",
"ordinal": 14,
"args": [
{
"name": "method",
@@ -97,24 +336,14 @@
"code": "refused",
"message": "settings refused"
},
"id": "frame-7",
"id": "frame-9",
"ok": false
}
}
},
"4083eac25622": {
"name": "ui.get#1",
"ordinal": 8,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"40a094bd83cc": {
"name": "linear.status#2",
"71b482600863": {
"name": "settings.get#2",
"ordinal": 14,
"json": "{\"id\":\"frame-6\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"41a2214deb19": {
"name": "settings.get#1",
"ordinal": 3,
"args": [
{
"name": "method",
@@ -138,13 +367,13 @@
"startedAt": 0
}
},
"4339fefe25aa": {
"name": "ui.get#2",
"ordinal": 12,
"7b2ffb4aa065": {
"name": "jira.status#2",
"ordinal": 13,
"args": [
{
"name": "method",
"value": "ui.get"
"value": "jira.status"
},
{
"name": "params",
@@ -160,30 +389,48 @@
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-8",
"ok": true,
"result": {
"ui": {}
}
}
"status": "pending",
"startedAt": 0
}
},
"5ce265803a9c": {
"7c5e5635081d": {
"name": "settings.get#1",
"ordinal": 7,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
"ordinal": 4,
"args": [
{
"name": "method",
"value": "settings.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"65f80b73db4d": {
"name": "preflight.check#2",
"82132b79b8f7": {
"name": "settings.get#1",
"ordinal": 9,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"83e12a6721ec": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "preflight.check"
"value": "jira.status"
},
{
"name": "params",
@@ -203,52 +450,13 @@
"startedAt": 0
}
},
"6953830b50a0": {
"name": "linear.status#1",
"ordinal": 6,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"7f0539cbdaa7": {
"name": "linear.status#2",
"ordinal": 10,
"args": [
{
"name": "method",
"value": "linear.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-6",
"ok": true,
"result": {
"connected": false
}
}
}
},
"843edb309e61": {
"name": "settings.get#1",
"8566e2463d90": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "settings.get"
"value": "jira.status"
},
{
"name": "params",
@@ -271,17 +479,21 @@
"id": "frame-3",
"ok": true,
"result": {
"settings": {
"defaultTuiAgent": "codex",
"disabledTuiAgents": ["claude"],
"hostSettingOverrides": {},
"prBotAuthorOverrides": ["bot-user"],
"visibleTaskProviders": ["github", "linear"]
}
"connected": false
}
}
}
},
"880ac4e9285d": {
"name": "jira.status#2",
"ordinal": 18,
"json": "{\"id\":\"frame-8\",\"deviceToken\":\"recording-device\",\"method\":\"jira.status\"}"
},
"8b8f728ca210": {
"name": "ui.get#2",
"ordinal": 20,
"json": "{\"id\":\"frame-10\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"903ba5a79900": {
"name": "preflight.check#1",
"ordinal": 1,
@@ -318,32 +530,6 @@
}
}
},
"9b0a7d2f1b01": {
"name": "linear.status#2",
"ordinal": 10,
"args": [
{
"name": "method",
"value": "linear.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"9cf8484d8b20": {
"name": "linear.status#1",
"ordinal": 2,
@@ -378,72 +564,15 @@
}
}
},
"9d0800b7c562": {
"name": "preflight.check#2",
"ordinal": 13,
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
"a2fd5d449008": {
"name": "jira.status#1",
"ordinal": 8,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"jira.status\"}"
},
"a6e51130744a": {
"name": "ui.get#2",
"ordinal": 12,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"b386e4f028b6": {
"name": "preflight.check#2",
"ordinal": 9,
"args": [
{
"name": "method",
"value": "preflight.check"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-5",
"ok": true,
"result": {
"glab": {
"installed": false
}
}
}
}
"ab92af857792": {
"name": "linear.status#1",
"ordinal": 7,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"b69f454508d6": {
"name": "preflight.check#1",
@@ -471,23 +600,18 @@
"startedAt": 0
}
},
"bc6d038b0149": {
"name": "ui.get#2",
"ordinal": 16,
"json": "{\"id\":\"frame-8\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
"cc61428a1b82": {
"name": "preflight.check#1",
"ordinal": 6,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
},
"d3f6b4014b96": {
"name": "settings.get#2",
"ordinal": 15,
"json": "{\"id\":\"frame-7\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"d57386932daa": {
"name": "ui.get#1",
"d13ca77439fc": {
"name": "settings.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
"value": "settings.get"
},
{
"name": "params",
@@ -510,18 +634,55 @@
"id": "frame-4",
"ok": true,
"result": {
"ui": {}
"settings": {
"defaultTuiAgent": "codex",
"disabledTuiAgents": ["claude"],
"hostSettingOverrides": {},
"prBotAuthorOverrides": ["bot-user"],
"visibleTaskProviders": ["github", "linear"]
}
}
}
}
},
"e1a668fc41b0": {
"e5fc4ab0cac2": {
"providers": ["github", "jira"],
"settings": {
"defaultTuiAgent": "codex",
"disabledTuiAgents": ["claude"],
"hostSettingOverrides": {},
"prBotAuthorOverrides": ["bot-user"],
"visibleTaskProviders": ["github", "linear"]
},
"trust": {}
},
"eb79a9b3682a": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"$rpc": "undefined"
}
},
"edb8f0fe6906": {
"name": "ui.get#1",
"ordinal": 4,
"ordinal": 10,
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"f6a09f8c5b85": {
"providers": [],
"settings": {
"$rpc": "null"
},
"trust": {}
},
"f76a2bd9afc4": {
"name": "linear.status#2",
"ordinal": 12,
"args": [
{
"name": "method",
"value": "ui.get"
"value": "linear.status"
},
{
"name": "params",
@@ -540,26 +701,6 @@
"status": "pending",
"startedAt": 0
}
},
"eb79a9b3682a": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"$rpc": "undefined"
}
},
"f6a09f8c5b85": {
"providers": [],
"settings": {
"$rpc": "null"
},
"trust": {}
},
"ff2933c7056d": {
"name": "preflight.check#1",
"ordinal": 5,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
}
},
"recording": {
@@ -568,8 +709,20 @@
{
"id": "settings-pending",
"observation": {
"sender": ["b69f454508d6", "1e7f9a45facb", "41a2214deb19", "e1a668fc41b0"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"b69f454508d6",
"1e7f9a45facb",
"83e12a6721ec",
"7c5e5635081d",
"3d249e57942b"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -580,8 +733,20 @@
{
"id": "settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -592,8 +757,20 @@
{
"id": "data-present",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "843edb309e61", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"d13ca77439fc",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -607,22 +784,26 @@
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"843edb309e61",
"d57386932daa",
"65f80b73db4d",
"9b0a7d2f1b01",
"1b3e4be6609f",
"a6e51130744a"
"8566e2463d90",
"d13ca77439fc",
"65e027dec060",
"1aa8c05700c5",
"f76a2bd9afc4",
"7b2ffb4aa065",
"71b482600863",
"048c28b9a17c"
],
"payloads": [
"ff2933c7056d",
"6953830b50a0",
"5ce265803a9c",
"4083eac25622",
"9d0800b7c562",
"40a094bd83cc",
"d3f6b4014b96",
"bc6d038b0149"
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906",
"52f9aae379dd",
"15e6052de76e",
"880ac4e9285d",
"60227e762a73",
"8b8f728ca210"
],
"settlements": {
"mount": "eb79a9b3682a",
@@ -639,29 +820,33 @@
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"843edb309e61",
"d57386932daa",
"b386e4f028b6",
"7f0539cbdaa7",
"3c4258332df3",
"4339fefe25aa"
"8566e2463d90",
"d13ca77439fc",
"65e027dec060",
"3335872ee1f8",
"4ab811f6ef4b",
"403a5a128034",
"706b8ee96dc7",
"65f8ab1a49c2"
],
"payloads": [
"ff2933c7056d",
"6953830b50a0",
"5ce265803a9c",
"4083eac25622",
"9d0800b7c562",
"40a094bd83cc",
"d3f6b4014b96",
"bc6d038b0149"
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906",
"52f9aae379dd",
"15e6052de76e",
"880ac4e9285d",
"60227e762a73",
"8b8f728ca210"
],
"settlements": {
"mount": "eb79a9b3682a",
"blur": "eb79a9b3682a",
"revisit": "eb79a9b3682a"
},
"state": "2a7485a88169",
"state": "e5fc4ab0cac2",
"effects": []
}
}
@@ -30,21 +30,104 @@
"startedAt": 0
}
},
"3a834cb85dd8": {
"providers": ["github"],
"settings": {
"$rpc": "null"
},
"trust": {}
},
"4083eac25622": {
"3d249e57942b": {
"name": "ui.get#1",
"ordinal": 8,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"41a2214deb19": {
"65e027dec060": {
"name": "ui.get#1",
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-5",
"ok": true,
"result": {
"ui": {}
}
}
}
},
"6b4a482f098c": {
"name": "settings.get#1",
"ordinal": 3,
"ordinal": 4,
"args": [
{
"name": "method",
"value": "settings.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"error": {
"code": "refused",
"message": "settings refused"
},
"id": "frame-4",
"ok": false
}
}
},
"7c5e5635081d": {
"name": "settings.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
@@ -68,15 +151,70 @@
"startedAt": 0
}
},
"5ce265803a9c": {
"82132b79b8f7": {
"name": "settings.get#1",
"ordinal": 7,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
"ordinal": 9,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"6953830b50a0": {
"name": "linear.status#1",
"ordinal": 6,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
"83e12a6721ec": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "jira.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"8566e2463d90": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "jira.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-3",
"ok": true,
"result": {
"connected": false
}
}
}
},
"903ba5a79900": {
"name": "preflight.check#1",
@@ -114,6 +252,13 @@
}
}
},
"9523a5ec7949": {
"providers": ["github", "jira"],
"settings": {
"$rpc": "null"
},
"trust": {}
},
"9cf8484d8b20": {
"name": "linear.status#1",
"ordinal": 2,
@@ -148,40 +293,15 @@
}
}
},
"a0909a6f7087": {
"name": "settings.get#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "settings.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"error": {
"code": "refused",
"message": "settings refused"
},
"id": "frame-3",
"ok": false
}
}
"a2fd5d449008": {
"name": "jira.status#1",
"ordinal": 8,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"jira.status\"}"
},
"ab92af857792": {
"name": "linear.status#1",
"ordinal": 7,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"b69f454508d6": {
"name": "preflight.check#1",
@@ -209,65 +329,10 @@
"startedAt": 0
}
},
"d57386932daa": {
"name": "ui.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-4",
"ok": true,
"result": {
"ui": {}
}
}
}
},
"e1a668fc41b0": {
"name": "ui.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
"cc61428a1b82": {
"name": "preflight.check#1",
"ordinal": 6,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
},
"eb79a9b3682a": {
"status": "fulfilled",
@@ -277,17 +342,17 @@
"$rpc": "undefined"
}
},
"edb8f0fe6906": {
"name": "ui.get#1",
"ordinal": 10,
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"f6a09f8c5b85": {
"providers": [],
"settings": {
"$rpc": "null"
},
"trust": {}
},
"ff2933c7056d": {
"name": "preflight.check#1",
"ordinal": 5,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
}
},
"recording": {
@@ -296,8 +361,20 @@
{
"id": "settings-pending",
"observation": {
"sender": ["b69f454508d6", "1e7f9a45facb", "41a2214deb19", "e1a668fc41b0"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"b69f454508d6",
"1e7f9a45facb",
"83e12a6721ec",
"7c5e5635081d",
"3d249e57942b"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -308,12 +385,24 @@
{
"id": "settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "a0909a6f7087", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"6b4a482f098c",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "3a834cb85dd8",
"state": "9523a5ec7949",
"effects": []
}
}
@@ -30,21 +30,69 @@
"startedAt": 0
}
},
"3a834cb85dd8": {
"providers": ["github"],
"settings": {
"$rpc": "null"
},
"trust": {}
},
"4083eac25622": {
"3d249e57942b": {
"name": "ui.get#1",
"ordinal": 8,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
},
"41a2214deb19": {
"65e027dec060": {
"name": "ui.get#1",
"ordinal": 5,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-5",
"ok": true,
"result": {
"ui": {}
}
}
}
},
"7c5e5635081d": {
"name": "settings.get#1",
"ordinal": 3,
"ordinal": 4,
"args": [
{
"name": "method",
@@ -68,18 +116,18 @@
"startedAt": 0
}
},
"5ce265803a9c": {
"82132b79b8f7": {
"name": "settings.get#1",
"ordinal": 7,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
"ordinal": 9,
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
},
"5deea0bbb564": {
"name": "settings.get#1",
"83e12a6721ec": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "settings.get"
"value": "jira.status"
},
{
"name": "params",
@@ -95,20 +143,43 @@
}
],
"settlement": {
"status": "rejected",
"startedAt": 0,
"settledAt": 0,
"error": {
"category": "Error",
"message": "settings disconnected",
"isRpcDeliveryUnknown": true
}
"status": "pending",
"startedAt": 0
}
},
"6953830b50a0": {
"name": "linear.status#1",
"ordinal": 6,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
"8566e2463d90": {
"name": "jira.status#1",
"ordinal": 3,
"args": [
{
"name": "method",
"value": "jira.status"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-3",
"ok": true,
"result": {
"connected": false
}
}
}
},
"903ba5a79900": {
"name": "preflight.check#1",
@@ -146,6 +217,13 @@
}
}
},
"9523a5ec7949": {
"providers": ["github", "jira"],
"settings": {
"$rpc": "null"
},
"trust": {}
},
"9cf8484d8b20": {
"name": "linear.status#1",
"ordinal": 2,
@@ -180,6 +258,16 @@
}
}
},
"a2fd5d449008": {
"name": "jira.status#1",
"ordinal": 8,
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"jira.status\"}"
},
"ab92af857792": {
"name": "linear.status#1",
"ordinal": 7,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
},
"b69f454508d6": {
"name": "preflight.check#1",
"ordinal": 1,
@@ -206,65 +294,10 @@
"startedAt": 0
}
},
"d57386932daa": {
"name": "ui.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "fulfilled",
"startedAt": 0,
"settledAt": 0,
"value": {
"id": "frame-4",
"ok": true,
"result": {
"ui": {}
}
}
}
},
"e1a668fc41b0": {
"name": "ui.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "ui.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "pending",
"startedAt": 0
}
"cc61428a1b82": {
"name": "preflight.check#1",
"ordinal": 6,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
},
"eb79a9b3682a": {
"status": "fulfilled",
@@ -274,17 +307,49 @@
"$rpc": "undefined"
}
},
"edb8f0fe6906": {
"name": "ui.get#1",
"ordinal": 10,
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"ui.get\"}"
},
"ef1c6ea901db": {
"name": "settings.get#1",
"ordinal": 4,
"args": [
{
"name": "method",
"value": "settings.get"
},
{
"name": "params",
"value": {
"$rpc": "absent"
}
},
{
"name": "options",
"value": {
"$rpc": "absent"
}
}
],
"settlement": {
"status": "rejected",
"startedAt": 0,
"settledAt": 0,
"error": {
"category": "Error",
"message": "settings disconnected",
"isRpcDeliveryUnknown": true
}
}
},
"f6a09f8c5b85": {
"providers": [],
"settings": {
"$rpc": "null"
},
"trust": {}
},
"ff2933c7056d": {
"name": "preflight.check#1",
"ordinal": 5,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
}
},
"recording": {
@@ -293,8 +358,20 @@
{
"id": "settings-pending",
"observation": {
"sender": ["b69f454508d6", "1e7f9a45facb", "41a2214deb19", "e1a668fc41b0"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"b69f454508d6",
"1e7f9a45facb",
"83e12a6721ec",
"7c5e5635081d",
"3d249e57942b"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
@@ -305,12 +382,24 @@
{
"id": "settled",
"observation": {
"sender": ["903ba5a79900", "9cf8484d8b20", "5deea0bbb564", "d57386932daa"],
"payloads": ["ff2933c7056d", "6953830b50a0", "5ce265803a9c", "4083eac25622"],
"sender": [
"903ba5a79900",
"9cf8484d8b20",
"8566e2463d90",
"ef1c6ea901db",
"65e027dec060"
],
"payloads": [
"cc61428a1b82",
"ab92af857792",
"a2fd5d449008",
"82132b79b8f7",
"edb8f0fe6906"
],
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "3a834cb85dd8",
"state": "9523a5ec7949",
"effects": []
}
}
+120
View File
@@ -500,6 +500,18 @@
}
}
},
{
"complete": "jira.status#1",
"params": {
"$undefined": true
},
"reply": {
"ok": true,
"result": {
"connected": false
}
}
},
{
"checkpoint": "settled"
}
@@ -571,6 +583,18 @@
}
}
},
{
"complete": "jira.status#1",
"params": {
"$undefined": true
},
"reply": {
"ok": true,
"result": {
"connected": false
}
}
},
{
"checkpoint": "settled"
}
@@ -639,6 +663,18 @@
}
}
},
{
"complete": "jira.status#1",
"params": {
"$undefined": true
},
"reply": {
"ok": true,
"result": {
"connected": false
}
}
},
{
"checkpoint": "settled"
}
@@ -1525,6 +1561,18 @@
}
}
},
{
"complete": "jira.status#1",
"params": {
"$undefined": true
},
"reply": {
"ok": true,
"result": {
"connected": false
}
}
},
{
"checkpoint": "settled"
}
@@ -1607,6 +1655,18 @@
}
}
},
{
"complete": "jira.status#1",
"params": {
"$undefined": true
},
"reply": {
"ok": true,
"result": {
"connected": false
}
}
},
{
"checkpoint": "settled"
}
@@ -1686,6 +1746,18 @@
}
}
},
{
"complete": "jira.status#1",
"params": {
"$undefined": true
},
"reply": {
"ok": true,
"result": {
"connected": false
}
}
},
{
"checkpoint": "settled"
}
@@ -2808,6 +2880,18 @@
}
}
},
{
"complete": "jira.status#1",
"params": {
"$undefined": true
},
"reply": {
"ok": true,
"result": {
"connected": false
}
}
},
{
"checkpoint": "settled"
},
@@ -2876,6 +2960,18 @@
}
}
},
{
"complete": "jira.status#2",
"params": {
"$undefined": true
},
"reply": {
"ok": true,
"result": {
"connected": false
}
}
},
{
"checkpoint": "refused-after-data"
}
@@ -3410,6 +3506,18 @@
}
}
},
{
"complete": "jira.status#1",
"params": {
"$undefined": true
},
"reply": {
"ok": true,
"result": {
"connected": false
}
}
},
{
"checkpoint": "settled"
},
@@ -3486,6 +3594,18 @@
}
}
},
{
"complete": "jira.status#2",
"params": {
"$undefined": true
},
"reply": {
"ok": true,
"result": {
"connected": false
}
}
},
{
"checkpoint": "refused-after-data"
}
@@ -0,0 +1,79 @@
import { Pressable, StyleSheet, Text, View } from 'react-native'
import { colors, radii, spacing, typography } from '../theme/mobile-theme'
import { TaskProviderLogo } from './TaskProviderLogo'
type Props = {
credentialError: string | null
unavailableReason: string | null
disabled: boolean
onCheckAgain: () => void
}
// Jira credentials are managed on the paired desktop.
export function JiraConnectPrompt({
credentialError,
unavailableReason,
disabled,
onCheckAgain
}: Props) {
return (
<View style={styles.centered}>
<TaskProviderLogo provider="jira" size={32} color={colors.textSecondary} />
<Text style={styles.emptyText}>
{unavailableReason ? 'Update Orca desktop' : 'Connect Jira on your desktop'}
</Text>
<Text style={styles.centeredHint}>
{unavailableReason ??
(credentialError
? `Jira is saved but unreadable: ${credentialError}`
: 'Add your Jira site in Orca desktop under Settings → Integrations, then check again.')}
</Text>
<Pressable
style={styles.checkAgainButton}
disabled={disabled}
onPress={() => {
if (!disabled) {
onCheckAgain()
}
}}
>
<Text style={styles.checkAgainText}>Check again</Text>
</Pressable>
</View>
)
}
const styles = StyleSheet.create({
centered: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
emptyText: {
color: colors.textSecondary,
fontSize: typography.bodySize
},
centeredHint: {
color: colors.textMuted,
fontSize: 12,
lineHeight: 17,
marginTop: spacing.sm,
maxWidth: 280,
textAlign: 'center'
},
checkAgainButton: {
alignItems: 'center',
borderWidth: 1,
borderColor: colors.borderSubtle,
backgroundColor: colors.bgPanel,
borderRadius: radii.input,
paddingHorizontal: spacing.md,
paddingVertical: spacing.sm + 2,
marginTop: spacing.md,
minWidth: 160
},
checkAgainText: {
color: colors.textPrimary,
fontSize: typography.bodySize
}
})
+46
View File
@@ -0,0 +1,46 @@
import { StyleSheet, Text, View } from 'react-native'
import { colors } from '../theme/mobile-theme'
type Props = {
assignee?: string
priorityName?: string
projectName: string
}
// The detail sheet owns the surrounding metadata grid.
export function JiraDetailMeta({ assignee, priorityName, projectName }: Props) {
const rows: [string, string | undefined][] = [
['Assignee', assignee],
['Priority', priorityName],
['Project', projectName]
]
return (
<>
{rows.map(([label, value]) =>
value ? (
<View key={label} style={styles.item}>
<Text style={styles.label}>{label}</Text>
<Text style={styles.value}>{value}</Text>
</View>
) : null
)}
</>
)
}
const styles = StyleSheet.create({
item: {
minWidth: 96,
flexGrow: 1
},
label: {
fontSize: 11,
color: colors.textMuted,
marginBottom: 2
},
value: {
fontSize: 13,
color: colors.textPrimary,
fontWeight: '600'
}
})
+4 -1
View File
@@ -162,7 +162,8 @@ function NewWorktreeModalContent(props: NewWorktreeModalProps) {
hasRepo: selectedRepo != null,
githubAvailable: runtime.availableProviders.includes('github'),
gitlabAvailable: runtime.availableProviders.includes('gitlab'),
linearAvailable: runtime.availableProviders.includes('linear')
linearAvailable: runtime.availableProviders.includes('linear'),
jiraAvailable: runtime.jiraConnection.connected && runtime.availableProviders.includes('jira')
}
// Why one client's: a source's repo switch is looked up through the picked repo's client.
const hostRepos = useMemo(
@@ -273,6 +274,8 @@ function NewWorktreeModalContent(props: NewWorktreeModalProps) {
selectedRepo={selectedRepo}
repos={hostRepos}
pasteRepos={pasteRepos}
jiraSiteId={runtime.jiraConnection.selection}
jiraSites={runtime.jiraConnection.sites}
sshReady={!executionTarget.sshGate.requiresConnection}
projectPickerItems={projectPickerItems}
selectedProjectId={selectedProjectId}
@@ -16,6 +16,7 @@ import { getMobileWorkspaceRepoBadgeColor } from './new-worktree-modal-types'
import { PickerListDrawer } from './PickerListDrawer'
import { SetupHookTrustDrawer, type SetupTrustPrompt } from './SetupHookTrustDrawer'
import { SmartWorkspaceSourceDrawer } from './SmartWorkspaceSourceDrawer'
import type { JiraSite, JiraSiteSelection } from '../../../src/shared/jira-types'
import type { NewWorktreeDrawerView } from './use-new-worktree-drawer-navigation'
type Composer = ReturnType<typeof useMobileComposerSource>
@@ -30,6 +31,8 @@ export function NewWorktreeModalDrawers(props: {
/** The picked repo's host's repos: a source can switch repos only within that host. */
repos: MobileWorkspaceRepo[]
pasteRepos: PasteRepoCandidate[]
jiraSiteId: JiraSiteSelection | null
jiraSites: readonly JiraSite[]
sshReady: boolean
projectPickerItems: NewWorkspaceProjectOption<MobileWorkspaceRepo>[]
selectedProjectId: string | null
@@ -55,6 +58,8 @@ export function NewWorktreeModalDrawers(props: {
availability={props.sourceAvailability}
repoId={props.selectedRepo?.id ?? null}
repos={props.pasteRepos}
jiraSiteId={props.jiraSiteId}
jiraSites={props.jiraSites}
sshReady={props.sshReady}
onRepoChange={(repoId) => {
const nextRepo = props.repos.find((repo) => repo.id === repoId)
@@ -10,6 +10,8 @@ import {
} from 'react-native'
import type { RpcClient } from '../transport/rpc-client'
import type { SmartWorkspaceSourceRow as SourceRow } from '../../../src/shared/new-workspace/smart-workspace-source-results'
import type { JiraSite, JiraSiteSelection } from '../../../src/shared/jira-types'
import { isBlockingJiraUrlIntent } from '../../../src/shared/new-workspace/smart-workspace-source-results'
import {
MR_STATE_FILTER_OPTIONS,
resolveAvailableSmartModes,
@@ -43,11 +45,15 @@ type Props = {
repoId: string | null
repos: readonly PasteRepoCandidate[]
linearWorkspaceId?: string | null
jiraSiteId?: JiraSiteSelection | null
jiraSites?: readonly JiraSite[]
sshReady: boolean
onRepoChange: (repoId: string) => void
onClose: () => void
}
const NO_JIRA_SITES: readonly JiraSite[] = []
export function SmartWorkspaceSourceDrawer({
visible,
client,
@@ -56,6 +62,8 @@ export function SmartWorkspaceSourceDrawer({
repoId,
repos,
linearWorkspaceId,
jiraSiteId,
jiraSites = NO_JIRA_SITES,
sshReady,
onRepoChange,
onClose
@@ -100,9 +108,14 @@ export function SmartWorkspaceSourceDrawer({
// Snap the chosen mode back into the available set if availability changes.
const effectiveMode = availableModes.includes(mode) ? mode : (availableModes[0] ?? 'text')
// Linear searches without a repo; every other provider/branch search needs a
// connected repo-backed target.
const searchEnabled = visible && (effectiveMode === 'linear' || sshReady)
// A pasted Jira URL resolves to one issue without touching the repo, so it must
// stay reachable from Smart mode even when the repo is not connected.
const jiraUrlIntent =
availability.jiraAvailable && isBlockingJiraUrlIntent(effectiveMode, composer.name)
// Linear and Jira search without a repo; every other provider/branch search
// needs a connected repo-backed target.
const searchEnabled =
visible && (effectiveMode === 'linear' || effectiveMode === 'jira' || jiraUrlIntent || sshReady)
const {
rows,
@@ -121,8 +134,11 @@ export function SmartWorkspaceSourceDrawer({
githubAvailable: availability.githubAvailable,
gitlabAvailable: availability.gitlabAvailable,
linearAvailable: availability.linearAvailable,
jiraAvailable: availability.jiraAvailable,
mrStateFilter,
linearWorkspaceId,
jiraSiteId,
jiraSites,
repos
})
@@ -146,6 +162,9 @@ export function SmartWorkspaceSourceDrawer({
case 'linear':
composer.handleSmartLinearIssueSelect(row.issue)
break
case 'jira':
composer.handleSmartJiraIssueSelect(row.issue)
break
}
onClose()
}
@@ -225,7 +244,11 @@ export function SmartWorkspaceSourceDrawer({
</View>
) : null}
{!sshReady && effectiveMode !== 'text' && effectiveMode !== 'linear' ? (
{!sshReady &&
effectiveMode !== 'text' &&
effectiveMode !== 'linear' &&
effectiveMode !== 'jira' &&
!jiraUrlIntent ? (
<Text style={styles.notice}>Connect the repository to search sources.</Text>
) : needsGitHubRemote ? (
<Text style={styles.notice}>
@@ -57,6 +57,13 @@ function resolveRowContent(row: SourceRow): RowContent {
subtitle: `${row.issue.identifier} · ${row.issue.team?.key ?? 'Linear'}`,
status: row.issue.state?.name
}
case 'jira':
return {
icon: <TaskProviderLogo provider="jira" size={16} color={colors.textSecondary} />,
title: row.issue.title,
subtitle: `${row.issue.key} · ${row.issue.project?.key ?? 'Jira'}`,
status: row.issue.status?.name
}
default:
return { icon: <CaseSensitive size={16} color={colors.textSecondary} />, title: '' }
}
+18 -1
View File
@@ -1,6 +1,6 @@
import Svg, { Path } from 'react-native-svg'
export type TaskProviderLogoKind = 'github' | 'gitlab' | 'linear'
export type TaskProviderLogoKind = 'github' | 'gitlab' | 'linear' | 'jira'
type Props = {
provider: TaskProviderLogoKind
@@ -46,6 +46,23 @@ export function TaskProviderLogo({ provider, size = 16, color }: Props) {
)
}
if (provider === 'jira') {
return (
<Svg
width={size}
height={size}
viewBox="0 -30.632388516510233 255.324 285.95638851651023"
fill={color}
>
{/* Why: flattened Jira product mark, matching the desktop JiraIcon so the
provider row stays monochrome instead of rendering as a branded tile. */}
<Path d="M244.658 0H121.707a55.502 55.502 0 0 0 55.502 55.502h22.649V77.37c.02 30.625 24.841 55.447 55.466 55.467V10.666C255.324 4.777 250.55 0 244.658 0z" />
<Path d="M183.822 61.262H60.872c.019 30.625 24.84 55.447 55.466 55.467h22.649v21.938c.039 30.625 24.877 55.43 55.502 55.43V71.93c0-5.891-4.776-10.667-10.667-10.667z" />
<Path d="M122.951 122.489H0c0 30.653 24.85 55.502 55.502 55.502h22.72v21.867c.02 30.597 24.798 55.408 55.396 55.466V133.156c0-5.891-4.776-10.667-10.667-10.667z" />
</Svg>
)
}
return (
<Svg width={size} height={size} viewBox="0 0 24 24" fill={color}>
{/* Why: keep Linear's mobile glyph aligned with the desktop sidebar logo. */}
@@ -51,6 +51,13 @@ describe('useNewWorkspaceRuntimeContext', () => {
context = null
})
function currentContext(): RuntimeContext {
if (!context) {
throw new Error('Runtime context did not render')
}
return context
}
function Harness({ client }: { client: RpcClient }): null {
context = useNewWorkspaceRuntimeContext(client, true)
return null
@@ -71,6 +78,35 @@ describe('useNewWorkspaceRuntimeContext', () => {
return { runtimeSettings, trustedOrcaHooks, availableProviders }
}
it('publishes other source providers while the optional Jira probe is pending', async () => {
const client = new FakeSession('connected')
let answerJira: (response: RpcResponse) => void = () => {}
const pending = new Promise<RpcResponse>((resolve) => {
answerJira = resolve
})
client.sendRequest.mockImplementation((method: string) => {
if (method === 'jira.status') {
return pending
}
if (method === 'settings.get') {
return Promise.resolve(reply({ settings: SETTINGS }))
}
if (method === 'ui.get') {
return Promise.resolve(reply(UI_WITH_TRUST))
}
if (method === 'preflight.check') {
return Promise.resolve(reply({ glab: { installed: true } }))
}
return Promise.resolve(reply({ connected: true }))
})
await act(async () => {
renderer = create(createElement(Harness, { client }))
})
expect(currentContext().availableProviders).toEqual(['github', 'linear'])
await act(async () => answerJira(reply({ connected: true })))
expect(currentContext().jiraConnection.connected).toBe(true)
})
// A null result used to throw the `settings` property read out of the effect, skipping the
// provider commit the absent case still reached.
it.each([
@@ -78,10 +114,12 @@ describe('useNewWorkspaceRuntimeContext', () => {
['absent', undefined],
['without a settings member', {}]
])('degrades a %s settings result to absent settings', async (_label, settingsResult) => {
// Jira rides the default provider list: unlike GitLab and Linear it has no
// availability gate here, so an absent settings result still offers it.
expect(await answer(settingsResult, UI_WITH_TRUST)).toEqual({
runtimeSettings: null,
trustedOrcaHooks: TRUSTED_HOOKS,
availableProviders: ['github']
availableProviders: ['github', 'jira']
})
})
@@ -9,7 +9,13 @@ import {
filterAvailableTaskProviders,
normalizeVisibleTaskProviders,
type TaskProvider
} from '../tasks/mobile-task-providers'
} from '../../../src/shared/task-providers'
import {
readJiraConnectionProbeReply,
type MobileJiraConnection
} from '../tasks/jira-mobile-connection'
import { jiraConnectionStatusProbe } from '../tasks/mobile-jira-operations'
import { useMobileJiraConnectionState } from '../tasks/use-mobile-jira-connection-state'
import type { NewWorktreeRuntimeSettings } from './new-worktree-agent-selection'
import { newWorkspaceUiStateRead } from './new-workspace-operations'
@@ -40,10 +46,16 @@ export function useNewWorkspaceRuntimeContext(
trustedOrcaHooks: PersistedTrustedOrcaHooks
setTrustedOrcaHooks: (trust: PersistedTrustedOrcaHooks) => void
availableProviders: TaskProvider[]
jiraConnection: MobileJiraConnection
} {
const [runtimeSettings, setRuntimeSettings] = useState<NewWorktreeRuntimeSettings | null>(null)
const [trustedOrcaHooks, setTrustedOrcaHooks] = useState<PersistedTrustedOrcaHooks>({})
const [availableProviders, setAvailableProviders] = useState<TaskProvider[]>([])
// Tasks can offer Jira setup; the composer requires a connected site.
const [jiraConnection, setJiraConnection] = useMobileJiraConnectionState(
client,
visible ? `visible:${hostId ?? ''}` : 'hidden'
)
useEffect(() => {
if (!visible || !client) {
@@ -55,6 +67,7 @@ export function useNewWorkspaceRuntimeContext(
taskPreflightRead.request(client),
taskLinearStatusRead.request(client)
])
const jiraProbe = Promise.allSettled([jiraConnectionStatusProbe.request(client)])
const [settingsRes, uiRes] = await Promise.allSettled([
optionalSettingsRead.request(client),
newWorkspaceUiStateRead.request(client)
@@ -100,6 +113,12 @@ export function useNewWorkspaceRuntimeContext(
linearConnected
}).filter((provider) => visibleProviders.includes(provider))
)
const [jiraRes] = await jiraProbe
if (!stale) {
setJiraConnection(
readJiraConnectionProbeReply(jiraRes.status === 'fulfilled' ? jiraRes.value : null)
)
}
})()
return () => {
stale = true
@@ -111,6 +130,7 @@ export function useNewWorkspaceRuntimeContext(
setRuntimeSettings,
trustedOrcaHooks,
setTrustedOrcaHooks,
availableProviders
availableProviders,
jiraConnection
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
import { StyleSheet, Text, View } from 'react-native'
import type { AccountsSnapshot } from '../components/AccountUsage'
import { MobileHomeQuickActions } from '../components/MobileHomeQuickActions'
import type { TaskProvider } from '../tasks/mobile-task-providers'
import type { TaskProvider } from '../../../src/shared/task-providers'
import { colors, spacing } from '../theme/mobile-theme'
import type { HostProfile } from '../transport/types'
import type { HomeResumeCard } from '../worktree/home-resume-card'
+1 -1
View File
@@ -11,7 +11,7 @@ import { hostRouteWithNotice } from '../host-route-notice'
import { useResponsiveLayout } from '../layout/responsive-layout'
import { triggerMediumImpact } from '../platform/haptics'
import { useOpenMobileSession } from '../session/use-open-mobile-session'
import type { TaskProvider } from '../tasks/mobile-task-providers'
import type { TaskProvider } from '../../../src/shared/task-providers'
import { useOpenMobileTasks } from '../tasks/use-open-mobile-tasks'
import { colors } from '../theme/mobile-theme'
import {
+3 -2
View File
@@ -1,13 +1,14 @@
import { ChevronRight, ListTodo } from 'lucide-react-native'
import { Pressable, StyleSheet, Text, View } from 'react-native'
import { TaskProviderLogo } from '../components/TaskProviderLogo'
import type { TaskProvider } from '../tasks/mobile-task-providers'
import type { TaskProvider } from '../../../src/shared/task-providers'
import { colors, radii, spacing } from '../theme/mobile-theme'
const TASK_PROVIDER_LABELS: Record<TaskProvider, string> = {
github: 'GitHub',
gitlab: 'GitLab',
linear: 'Linear'
linear: 'Linear',
jira: 'Jira'
}
export function MobileHomeTasksCard(props: {
+1 -1
View File
@@ -6,7 +6,7 @@ import {
filterAvailableTaskProviders,
normalizeVisibleTaskProviders,
type TaskProvider
} from '../tasks/mobile-task-providers'
} from '../../../src/shared/task-providers'
import type { RpcClient } from '../transport/rpc-client'
import { homeHostAccountsRead, homeHostStatsRead } from './mobile-home-host-operations'
+1 -1
View File
@@ -10,7 +10,7 @@ import {
mobileOnboardingDestination
} from '../onboarding/mobile-onboarding-plan'
import { totalHomeStats, type HomeStatsRow } from '../stats/home-stats-total'
import type { TaskProvider } from '../tasks/mobile-task-providers'
import type { TaskProvider } from '../../../src/shared/task-providers'
import {
selectConnectableHostProfiles,
sortHostsByLastConnected
@@ -38,6 +38,8 @@ import {
renderMobileTasksGitHubProjectFieldsPicker,
renderMobileTasksGitLabViewPicker,
renderMobileTasksGitLabFilterPicker,
renderMobileTasksJiraFilterPicker,
renderMobileTasksJiraSitePicker,
renderMobileTasksLinearFilterPicker,
renderMobileTasksLinearWorkspacePicker,
renderMobileTasksLinearTeamPicker,
@@ -203,6 +205,8 @@ export function MobileTasksLegacySurface({ model }: { model: ConnectionPresentat
{renderMobileTasksGitLabFilterPicker(model)}
{renderMobileTasksLinearFilterPicker(model)}
{renderMobileTasksJiraFilterPicker(model)}
{renderMobileTasksJiraSitePicker(model)}
{renderMobileTasksLinearWorkspacePicker(model)}
@@ -2,14 +2,18 @@ import type { GitHubWorkItem } from '../../../src/shared/github/work-item-types'
import type { GitLabWorkItem } from '../../../src/shared/gitlab-types'
import type { LinearIssue } from '../../../src/shared/linear/issue-types'
import { getLinearIssueWorkspaceName } from '../../../src/shared/workspace-name'
import type { JiraIssue } from '../../../src/shared/jira-types'
import {
buildGitHubWorkspaceSource,
buildGitLabWorkspaceSource,
buildJiraWorkspaceSource,
buildLinearWorkspaceSource,
buildWorkspaceSourceSelection,
getWorkspaceSourceName,
shouldApplyWorkspaceSourceAutoName
} from '../../../src/shared/new-workspace/workspace-source'
type JiraIssueSource = Pick<JiraIssue, 'key' | 'title' | 'url'>
import { resolveComposerBranchPick as resolveSharedComposerBranchPick } from '../../../src/shared/composer-branch-selection'
import type {
MobileComposerCreateSelection,
@@ -72,6 +76,14 @@ export function resolveLinearAutoName(issue: { identifier: string; title: string
return getLinearIssueWorkspaceName(issue)
}
export function buildJiraLinkedWorkItem(issue: JiraIssueSource): MobileLinkedWorkItem {
return buildJiraWorkspaceSource(issue)
}
export function resolveJiraAutoName(issue: JiraIssueSource): string {
return getWorkspaceSourceName(buildJiraWorkspaceSource(issue)).seedName
}
// Derives the pill descriptor from the linked item (or a plain branch base),
// mirroring desktop's smartNameSelection memo.
export function buildSmartNameSelection(args: {
@@ -1,6 +1,7 @@
import type { RpcClient } from '../transport/rpc-client'
import type { GitHubPrStartPoint } from '../../../src/shared/worktree/types'
import { worktreeMrBaseResolve, worktreePrBaseResolve } from './mobile-workspace-create-operations'
import type { ComposerBaseState } from './mobile-composer-source-types'
// The resolved start point for a linked PR/MR: the base branch to create from
// plus the optional review-compare ref, push target, and exact branch name.
@@ -9,6 +10,16 @@ export type ComposerHostedBase = Pick<
'baseBranch' | 'compareBaseRef' | 'pushTarget' | 'branchNameOverride' | 'maintainerCanModify'
>
// The composer stores branch fields; permissions only inform its fork warning.
export function toComposerBaseState(result: ComposerHostedBase): ComposerBaseState {
return {
baseBranch: result.baseBranch,
compareBaseRef: result.compareBaseRef,
pushTarget: result.pushTarget,
branchNameOverride: result.branchNameOverride
}
}
// Resolves a GitHub PR's base via worktree.resolvePrBase, mirroring desktop's
// select-time resolution. The runtime returns a soft { error } payload rather
// than an RPC error for provider failures.
@@ -40,6 +51,44 @@ export async function resolveComposerPrBase(args: {
return result as GitHubPrStartPoint
}
// Issues have no review branch to resolve.
export function resolveComposerHostedItemBase(args: {
client: RpcClient
repoId: string
provider: 'github' | 'gitlab'
type: 'issue' | 'pr' | 'mr'
number: number
branchName?: string
baseRefName?: string
isCrossRepository?: boolean
}): Promise<ComposerHostedBase> | null {
const { client, repoId, provider, type, number, branchName, baseRefName, isCrossRepository } =
args
const crossRepo = isCrossRepository === undefined ? {} : { isCrossRepository }
if (provider === 'github') {
return type === 'pr'
? resolveComposerPrBase({
client,
repoId,
prNumber: number,
...(branchName ? { headRefName: branchName } : {}),
...(baseRefName ? { baseRefName } : {}),
...crossRepo
})
: null
}
return type === 'mr'
? resolveComposerMrBase({
client,
repoId,
mrIid: number,
...(branchName ? { sourceBranch: branchName } : {}),
...(baseRefName ? { targetBranch: baseRefName } : {}),
...crossRepo
})
: null
}
// Resolves a GitLab MR's base via worktree.resolveMrBase.
export async function resolveComposerMrBase(args: {
client: RpcClient
@@ -0,0 +1,174 @@
import { describe, expect, it } from 'vitest'
import type { RpcResponse } from '../transport/types'
import {
extractJiraConnection,
jiraSiteLabel,
readJiraConnection,
readJiraConnectionProbeReply,
reconcileJiraConnection
} from './jira-mobile-connection'
const siteA = {
id: 'site-a',
siteUrl: 'https://a.atlassian.net',
email: 'me@example.com',
displayName: 'Site A',
accountId: 'acc-1'
}
const siteB = { ...siteA, id: 'site-b', siteUrl: 'https://b.atlassian.net', displayName: 'Site B' }
describe('extractJiraConnection', () => {
it('explains an unreadable status without blocking the remaining providers', () => {
// A host predating the Jira RPCs answers with an error, which reaches us as null.
for (const value of [null, undefined, 'nope', 42]) {
expect(extractJiraConnection(value)).toEqual({
connected: false,
sites: [],
selection: null,
credentialError: null,
unavailableReason: 'Update Orca desktop to browse Jira on mobile.'
})
}
})
it('keeps the credential error when a saved connection cannot be decrypted', () => {
expect(
extractJiraConnection({
connected: false,
viewer: null,
credentialError: ' keychain locked '
})
).toEqual({
connected: false,
sites: [],
selection: null,
credentialError: 'keychain locked',
unavailableReason: null
})
})
it('honors an explicit all-sites selection', () => {
const result = extractJiraConnection({
connected: true,
viewer: null,
sites: [siteA, siteB],
selectedSiteId: 'all'
})
expect(result.selection).toBe('all')
expect(result.sites).toHaveLength(2)
})
it('falls back to the active site when the saved selection was disconnected', () => {
const result = extractJiraConnection({
connected: true,
viewer: null,
sites: [siteB],
selectedSiteId: 'site-a',
activeSiteId: 'site-b'
})
expect(result.selection).toBe('site-b')
})
it('falls back to the first site when neither selection nor active site resolves', () => {
const result = extractJiraConnection({
connected: true,
viewer: null,
sites: [siteA, siteB],
selectedSiteId: 'gone',
activeSiteId: 'also-gone'
})
expect(result.selection).toBe('site-a')
})
it('reports connected with no selection when the site list is empty', () => {
const result = extractJiraConnection({ connected: true, viewer: null, sites: [] })
expect(result).toMatchObject({ connected: true, selection: null })
})
})
describe('jiraSiteLabel', () => {
it('prefers the display name', () => {
expect(jiraSiteLabel(siteA)).toBe('Site A')
})
it('falls back to a bare host when the display name is blank', () => {
expect(jiraSiteLabel({ ...siteA, displayName: ' ' })).toBe('a.atlassian.net')
})
})
describe('readJiraConnection', () => {
it('reports a refused status read instead of showing it as disconnected', async () => {
// A host that does not authorize jira.* for mobile clients answers with an
// error; treating that as "no Jira connected" strands the user on a setup
// prompt they cannot satisfy.
const client = {
sendRequest: async (): Promise<RpcResponse> => ({
id: 'jira-test',
ok: false,
error: {
code: 'UNAVAILABLE',
message: "Method 'jira.status' is not available to mobile clients"
}
})
}
await expect(readJiraConnection(client)).rejects.toThrow(
"Method 'jira.status' is not available to mobile clients"
)
})
it('returns the parsed connection on success', async () => {
const client = {
sendRequest: async (): Promise<RpcResponse> => ({
id: 'jira-test',
ok: true,
result: { connected: true, viewer: null, sites: [siteA], selectedSiteId: 'site-a' }
})
}
await expect(readJiraConnection(client)).resolves.toMatchObject({
connected: true,
selection: 'site-a'
})
})
})
describe('Jira connection compatibility', () => {
it('distinguishes an older host refusal from an actual disconnected site', () => {
expect(
readJiraConnectionProbeReply({
id: 'jira-test',
ok: false,
error: { code: 'METHOD_NOT_FOUND', message: 'Not found' }
})
).toMatchObject({
connected: false,
unavailableReason: 'Update Orca desktop to browse Jira on mobile.'
})
expect(
readJiraConnectionProbeReply({ id: 'jira-test', ok: true, result: { connected: false } })
).toMatchObject({
connected: false,
unavailableReason: null
})
})
it('salvages optional site fields while dropping sites that cannot be selected', () => {
const connection = extractJiraConnection({
connected: true,
sites: [null, { id: 'bad' }, { ...siteA, email: 42, authType: 'future' }]
})
expect(connection.sites).toEqual([{ ...siteA, email: '', authType: undefined }])
})
it('keeps the phone selection when desktop status is refreshed', () => {
const next = extractJiraConnection({
connected: true,
sites: [siteA, siteB],
selectedSiteId: 'site-a'
})
expect(reconcileJiraConnection({ ...next, selection: 'site-b' }, next).selection).toBe('site-b')
expect(reconcileJiraConnection({ ...next, selection: 'all' }, next).selection).toBe('all')
expect(reconcileJiraConnection({ ...next, selection: 'gone' }, next).selection).toBe('site-a')
})
})
+104
View File
@@ -0,0 +1,104 @@
import type { JiraSite, JiraSiteSelection } from '../../../src/shared/jira-types'
import type { RpcOperationSender } from '../transport/rpc-operation-sender'
import type { RpcResponse } from '../transport/types'
import { jiraConnectionStatusSchema } from './jira-reply-schema'
import { jiraConnectionStatusProbe, jiraConnectionStatusRead } from './mobile-jira-operations'
export type MobileJiraConnection = {
connected: boolean
sites: JiraSite[]
selection: JiraSiteSelection | null
credentialError: string | null
unavailableReason: string | null
}
const DISCONNECTED: MobileJiraConnection = {
connected: false,
sites: [],
selection: null,
credentialError: null,
unavailableReason: null
}
export function extractJiraConnection(result: unknown): MobileJiraConnection {
const decoded = jiraConnectionStatusSchema.safeParse(result)
if (!decoded.success) {
return { ...DISCONNECTED, unavailableReason: 'Update Orca desktop to browse Jira on mobile.' }
}
const status = decoded.data
const credentialError = status.credentialError?.trim() || null
if (!status.connected) {
return { ...DISCONNECTED, credentialError }
}
const sites = status.sites ?? []
return {
connected: true,
sites,
selection: resolveJiraSiteSelection(status, sites),
credentialError,
unavailableReason: null
}
}
// Older hosts can refuse Jira without taking the other task providers down.
export function readJiraConnectionProbeReply(reply: RpcResponse | null): MobileJiraConnection {
try {
if (reply) {
const result = jiraConnectionStatusProbe.interpret(reply)
if (result.accepted) {
return extractJiraConnection(result.value)
}
}
} catch {
// An incompatible advisory reply leaves the remaining providers usable.
}
return extractJiraConnection(null)
}
export function reconcileJiraConnection(
current: MobileJiraConnection,
next: MobileJiraConnection
): MobileJiraConnection {
const selected = current.selection
return selected === 'all' || next.sites.some((site) => site.id === selected)
? { ...next, selection: selected }
: next
}
function resolveJiraSiteSelection(
status: { selectedSiteId?: string | null; activeSiteId?: string | null },
sites: readonly JiraSite[]
): JiraSiteSelection | null {
const selected = status.selectedSiteId
if (selected === 'all') {
return 'all'
}
if (typeof selected === 'string' && sites.some((site) => site.id === selected)) {
return selected
}
// A disconnected selection falls back to an available site.
if (
typeof status.activeSiteId === 'string' &&
sites.some((site) => site.id === status.activeSiteId)
) {
return status.activeSiteId
}
return sites[0]?.id ?? null
}
// Explicit refreshes surface failures instead of presenting a setup prompt.
export async function readJiraConnection(
client: RpcOperationSender
): Promise<MobileJiraConnection> {
return extractJiraConnection(
jiraConnectionStatusRead.interpret(await jiraConnectionStatusRead.request(client))
)
}
export function jiraSiteLabel(site: JiraSite): string {
const name = site.displayName.trim()
if (name) {
return name
}
return site.siteUrl.replace(/^https?:\/\//, '').replace(/\/+$/, '')
}
@@ -0,0 +1,38 @@
import { describe, expect, it } from 'vitest'
import { toJiraDetailComments } from './jira-mobile-issue-read'
describe('toJiraDetailComments', () => {
it('maps the author and avatar off the nested user', () => {
expect(
toJiraDetailComments([
{
id: '10',
body: 'hi',
createdAt: '2026-01-01T00:00:00Z',
user: { accountId: 'a', displayName: 'Dana', avatarUrl: 'https://x/y.png' }
}
])
).toEqual([
{
id: '10',
author: 'Dana',
authorAvatarUrl: 'https://x/y.png',
body: 'hi',
createdAt: '2026-01-01T00:00:00Z'
}
])
})
it('degrades to an empty list instead of breaking the detail sheet', () => {
expect(toJiraDetailComments(null)).toEqual([])
expect(toJiraDetailComments({ error: 'nope' })).toEqual([])
})
it('drops entries without a usable id', () => {
expect(
toJiraDetailComments([{ id: 5, body: 'x' }, { body: 'y' }, { id: '7', body: 'z' }])
).toEqual([
{ id: '7', author: undefined, authorAvatarUrl: undefined, body: 'z', createdAt: undefined }
])
})
})
@@ -0,0 +1,24 @@
import { jiraCommentListSchema } from './jira-reply-schema'
export type JiraDetailComment = {
id: string
author?: string
authorAvatarUrl?: string
body: string
createdAt?: string
}
// Optional comments must not hide a readable issue.
export function toJiraDetailComments(result: unknown): JiraDetailComment[] {
const decoded = jiraCommentListSchema.safeParse(result)
if (!decoded.success) {
return []
}
return decoded.data.map((comment) => ({
id: comment.id,
author: comment.user?.displayName,
authorAvatarUrl: comment.user?.avatarUrl,
body: comment.body ?? '',
createdAt: comment.createdAt
}))
}
@@ -0,0 +1,95 @@
import { describe, expect, it, vi } from 'vitest'
import type { JiraIssue, JiraSite } from '../../../src/shared/jira-types'
import type { RpcOperationSender } from '../transport/rpc-operation-sender'
import { lookupJiraIssueByUrl } from './jira-mobile-url-lookup'
const acme: JiraSite = {
id: 'site-acme',
siteUrl: 'https://acme.atlassian.net',
email: 'me@acme.com',
displayName: 'Acme',
accountId: 'acc-1'
}
const other: JiraSite = { ...acme, id: 'site-other', siteUrl: 'https://other.atlassian.net' }
function issue(overrides: Partial<JiraIssue> = {}): JiraIssue {
return {
id: '1',
key: 'ORCA-123',
siteId: 'site-acme',
title: 'Add Jira to mobile',
url: 'https://acme.atlassian.net/browse/ORCA-123',
project: { id: 'p1', key: 'ORCA', name: 'Orca' },
issueType: { id: 't1', name: 'Task' },
status: { id: 's1', name: 'To Do', categoryKey: 'new', categoryName: 'To Do' },
labels: [],
updatedAt: '2026-01-01T00:00:00Z',
createdAt: '2026-01-01T00:00:00Z',
...overrides
}
}
function clientReturning(result: unknown, sendRequest = vi.fn()): RpcOperationSender {
sendRequest.mockResolvedValue({ ok: true, result })
return { sendRequest }
}
describe('lookupJiraIssueByUrl', () => {
it('resolves an issue through the matching connected site', async () => {
const sendRequest = vi.fn()
const client = clientReturning(issue(), sendRequest)
await expect(
lookupJiraIssueByUrl(client, 'https://acme.atlassian.net/browse/ORCA-123', [acme, other])
).resolves.toMatchObject({ key: 'ORCA-123' })
expect(sendRequest).toHaveBeenCalledWith('jira.getIssue', {
key: 'ORCA-123',
siteId: 'site-acme'
})
})
it('never queries a site the pasted URL does not belong to', async () => {
const sendRequest = vi.fn()
const client = clientReturning(issue(), sendRequest)
// The user is connected to `other` only; a link to acme must not be answered
// with `other`'s same-keyed issue.
await expect(
lookupJiraIssueByUrl(client, 'https://acme.atlassian.net/browse/ORCA-123', [other])
).resolves.toBeNull()
expect(sendRequest).not.toHaveBeenCalled()
})
it('rejects a response whose issue does not canonically match the link', async () => {
const client = clientReturning(
issue({ url: 'https://other.atlassian.net/browse/ORCA-123', siteId: 'site-acme' })
)
await expect(
lookupJiraIssueByUrl(client, 'https://acme.atlassian.net/browse/ORCA-123', [acme])
).resolves.toBeNull()
})
it('ignores non-Jira and non-browse URLs without a round trip', async () => {
const sendRequest = vi.fn()
const client = clientReturning(issue(), sendRequest)
for (const value of [
'not a url',
'https://acme.atlassian.net/jira/software/ORCA-1',
'ORCA-1'
]) {
await expect(lookupJiraIssueByUrl(client, value, [acme])).resolves.toBeNull()
}
expect(sendRequest).not.toHaveBeenCalled()
})
it('tries the next matching site when one errors', async () => {
const twin: JiraSite = { ...acme, id: 'site-acme-2' }
const sendRequest = vi
.fn()
.mockResolvedValueOnce({ ok: false, error: { message: 'boom' } })
.mockResolvedValueOnce({ ok: true, result: issue({ siteId: 'site-acme-2' }) })
const client = { sendRequest }
await expect(
lookupJiraIssueByUrl(client, 'https://acme.atlassian.net/browse/ORCA-123', [acme, twin])
).resolves.toMatchObject({ siteId: 'site-acme-2' })
expect(sendRequest).toHaveBeenCalledTimes(2)
})
})
@@ -0,0 +1,31 @@
import type { JiraIssue, JiraSite } from '../../../src/shared/jira-types'
import {
getMatchingJiraSites,
isResolvedJiraIssueMatch,
parseJiraIssueUrl
} from '../../../src/shared/jira-issue-url'
import type { RpcOperationSender } from '../transport/rpc-operation-sender'
import { jiraIssueRead } from './mobile-jira-operations'
// Match a connected site before resolving keys that may exist on several sites.
export async function lookupJiraIssueByUrl(
client: RpcOperationSender,
value: string,
sites: readonly JiraSite[]
): Promise<JiraIssue | null> {
const parsed = parseJiraIssueUrl(value)
if (!parsed) {
return null
}
for (const site of getMatchingJiraSites(parsed, sites)) {
// Another connected account for this origin may have access.
const issue = await jiraIssueRead
.request(client, { key: parsed.issueKey, siteId: site.id })
.then((reply) => jiraIssueRead.interpret(reply))
.catch(() => null)
if (issue && isResolvedJiraIssueMatch(parsed, site, issue)) {
return issue
}
}
return null
}
+84
View File
@@ -0,0 +1,84 @@
import { z } from 'zod'
import { salvagedOptional, salvagingArray } from '../../../src/shared/zod-salvage'
// Required identity fields reject unusable rows; optional fields salvage older-host replies.
const text = (name: string) => salvagedOptional(name, z.string())
const jiraSiteRow = z.looseObject({
id: z.string(),
siteUrl: z.string(),
displayName: z.string(),
email: text('email').transform((value) => value ?? ''),
accountId: text('accountId').transform((value) => value ?? ''),
authType: salvagedOptional('authType', z.enum(['cloud', 'server']))
})
export const jiraConnectionStatusSchema = z.looseObject({
connected: z.boolean(),
sites: salvagedOptional('sites', salvagingArray(jiraSiteRow)),
activeSiteId: salvagedOptional('activeSiteId', z.string().nullable()),
selectedSiteId: salvagedOptional('selectedSiteId', z.string().nullable()),
credentialError: text('credentialError'),
viewer: z.unknown().optional()
})
const jiraProject = z.looseObject({
id: z.string(),
key: z.string(),
name: z.string(),
siteId: text('siteId'),
siteName: text('siteName')
})
const jiraNamed = z.looseObject({ id: z.string(), name: z.string() })
const jiraUser = z.looseObject({
accountId: z.string(),
displayName: z.string(),
avatarUrl: text('avatarUrl')
})
export const jiraIssueRowSchema = z.looseObject({
id: z.string(),
key: z.string(),
title: z.string(),
url: z.string(),
updatedAt: z.string(),
createdAt: z.string(),
description: text('description'),
siteId: text('siteId'),
siteName: text('siteName'),
project: jiraProject,
issueType: jiraNamed,
status: z.looseObject({
id: z.string(),
name: z.string(),
categoryKey: z.string(),
categoryName: z.string(),
colorName: text('colorName')
}),
labels: salvagingArray(z.string()),
assignee: salvagedOptional('assignee', jiraUser),
reporter: salvagedOptional('reporter', jiraUser),
priority: salvagedOptional('priority', jiraNamed)
})
export const jiraIssueListSchema = z.union([
salvagingArray(jiraIssueRowSchema),
z
.looseObject({ items: salvagingArray(jiraIssueRowSchema) })
.transform((envelope) => envelope.items),
z
.looseObject({ issues: salvagingArray(jiraIssueRowSchema) })
.transform((envelope) => envelope.issues)
])
export const jiraCommentListSchema = salvagingArray(
z.looseObject({
id: z.string(),
body: z.string(),
createdAt: text('createdAt'),
user: salvagedOptional('user', jiraUser)
})
)
@@ -17,9 +17,7 @@ export type ComposerBaseState = {
// Mirrors the desktop composer's `linkedWorkItem` (a FolderWorkspaceLinkedTask
// superset): the one work item a Smart selection pins the workspace to. Linear
// items carry the workspace/org routing the runtime needs to relink the issue.
export type MobileLinkedWorkItem = Omit<WorkspaceSourceLinkedItem, 'provider'> & {
provider: Exclude<WorkspaceSourceLinkedItem['provider'], 'jira'>
}
export type MobileLinkedWorkItem = WorkspaceSourceLinkedItem
export type SmartNameSelectionKind =
| 'github-pr'
@@ -28,6 +26,7 @@ export type SmartNameSelectionKind =
| 'gitlab-issue'
| 'branch'
| 'linear'
| 'jira'
// The pill descriptor the field renders once a source is selected. Same shape
// as desktop's `SmartWorkspaceNameSelection`.
@@ -0,0 +1,14 @@
import type { JiraIssueFilter } from '../../../src/shared/jira-types'
export const JIRA_FILTER_LABELS: Record<JiraIssueFilter, string> = {
assigned: 'My Issues',
reported: 'Reported',
all: 'All',
done: 'Done'
}
export function normalizeJiraFilter(value: unknown): JiraIssueFilter {
return value === 'assigned' || value === 'reported' || value === 'done' || value === 'all'
? value
: 'assigned'
}
@@ -0,0 +1,74 @@
import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc-operation'
import { rpcResultVariant } from '../transport/rpc-operation-result-reader'
import {
jiraCommentListSchema,
jiraConnectionStatusSchema,
jiraIssueListSchema,
jiraIssueRowSchema
} from './jira-reply-schema'
// Credentials and issue mutations stay on desktop.
const jiraStatusReader = rpcResultVariant('jira-status', jiraConnectionStatusSchema)
export const jiraConnectionStatusRead = bindDeferredRpcOperation(
defineRpcOperation({
name: 'jira.connection-status',
method: 'jira.status',
acceptance: 'require-result-or-throw-message',
barrier: 'after-caller-barrier',
read: jiraStatusReader
})
)
export const jiraConnectionStatusProbe = bindDeferredRpcOperation(
defineRpcOperation({
name: 'jira.connection-status-or-skip',
method: 'jira.status',
acceptance: 'success-result-or-skip',
barrier: 'after-caller-barrier',
read: jiraStatusReader
})
)
const jiraIssueListReader = rpcResultVariant('jira-issues', jiraIssueListSchema)
export const jiraIssueListRead = bindDeferredRpcOperation(
defineRpcOperation({
name: 'jira.issue-list',
method: 'jira.listIssues',
acceptance: 'require-result-or-throw-message',
barrier: 'after-caller-barrier',
read: jiraIssueListReader
})
)
export const jiraIssueSearchRead = bindDeferredRpcOperation(
defineRpcOperation({
name: 'jira.issue-search',
method: 'jira.searchIssues',
acceptance: 'require-result-or-throw-message',
barrier: 'after-caller-barrier',
read: jiraIssueListReader
})
)
export const jiraIssueRead = bindDeferredRpcOperation(
defineRpcOperation({
name: 'jira.issue',
method: 'jira.getIssue',
acceptance: 'require-result-or-throw-message',
barrier: 'after-caller-barrier',
read: rpcResultVariant('jira-issue', jiraIssueRowSchema)
})
)
export const jiraIssueCommentsRead = bindDeferredRpcOperation(
defineRpcOperation({
name: 'jira.issue-comments-or-skip',
method: 'jira.issueComments',
acceptance: 'success-result-or-skip',
barrier: 'after-caller-barrier',
read: rpcResultVariant('jira-comments', jiraCommentListSchema)
})
)
@@ -0,0 +1,98 @@
import { describe, expect, it, vi } from 'vitest'
import type { JiraIssue } from '../../../src/shared/jira-types'
import type { RpcResponse } from '../transport/types'
import { fetchJiraIssueDetail, fetchJiraTaskIssues } from './mobile-jira-task-source'
const issue: JiraIssue = {
id: '1',
key: 'ORCA-7',
title: 'Read Jira on mobile',
url: 'https://acme.atlassian.net/browse/ORCA-7',
siteId: 'site-a',
description: 'A description',
labels: ['mobile'],
project: { id: 'p', key: 'ORCA', name: 'Orca' },
issueType: { id: 'task', name: 'Task' },
status: { id: 'open', name: 'Open', categoryKey: 'new', categoryName: 'To Do' },
updatedAt: '2026-01-01T00:00:00Z',
createdAt: '2026-01-01T00:00:00Z'
}
describe('mobile Jira task requests', () => {
it('targets the phone-selected site, caps reads, and salvages malformed list rows', async () => {
const sendRequest = vi
.fn()
.mockResolvedValue({ id: 'jira-test', ok: true, result: [null, { key: 'bad' }, issue] })
await expect(
fetchJiraTaskIssues({ sendRequest }, { jql: '', filter: 'reported', siteId: 'site-b' })
).resolves.toEqual([issue])
expect(sendRequest).toHaveBeenCalledWith('jira.listIssues', {
filter: 'reported',
limit: 50,
siteId: 'site-b'
})
})
it('uses JQL instead of combining it with the named preset', async () => {
const sendRequest = vi
.fn()
.mockResolvedValue({ id: 'jira-test', ok: true, result: { issues: [issue] } })
await expect(
fetchJiraTaskIssues(
{ sendRequest },
{ jql: ' project = ORCA ', filter: 'assigned', siteId: 'all' }
)
).resolves.toEqual([issue])
expect(sendRequest).toHaveBeenCalledWith('jira.searchIssues', {
jql: 'project = ORCA',
limit: 50,
siteId: 'all'
})
})
it('rejects an incompatible list envelope instead of presenting it as empty', async () => {
const sendRequest = vi
.fn()
.mockResolvedValue({ id: 'jira-test', ok: true, result: { error: 'bad response' } })
await expect(
fetchJiraTaskIssues({ sendRequest }, { jql: '', filter: 'assigned', siteId: null })
).rejects.toThrow(/jira.listIssues/)
})
it('keeps the issue readable when optional comments refuse, disconnect, or fail validation', async () => {
const commentReplies: (RpcResponse | null)[] = [
null,
{ id: 'jira-test', ok: false, error: { code: 'UNAVAILABLE', message: 'Unavailable' } },
{ id: 'jira-test', ok: true, result: 'bad comments' }
]
for (const response of commentReplies) {
const sendRequest = vi.fn(async (method: string) => {
if (method === 'jira.getIssue') {
return { id: 'jira-test', ok: true as const, result: issue }
}
if (response === null) {
throw new Error('Connection lost')
}
return response
})
await expect(
fetchJiraIssueDetail({ sendRequest }, { key: issue.key, siteId: issue.siteId })
).resolves.toEqual({ issue, comments: [] })
}
})
it('surfaces issue failures even when comments can be read', async () => {
const sendRequest = vi.fn(async (method: string) =>
method === 'jira.getIssue'
? {
id: 'jira-test',
ok: false as const,
error: { code: 'UNAVAILABLE', message: 'Issue permission denied' }
}
: { id: 'jira-test', ok: true as const, result: [] }
)
await expect(fetchJiraIssueDetail({ sendRequest }, { key: issue.key })).rejects.toThrow(
'Issue permission denied'
)
})
})
@@ -0,0 +1,64 @@
import type { JiraIssue, JiraIssueFilter, JiraSiteSelection } from '../../../src/shared/jira-types'
import type { RpcOperationSender } from '../transport/rpc-operation-sender'
import { toJiraDetailComments } from './jira-mobile-issue-read'
import {
jiraIssueCommentsRead,
jiraIssueListRead,
jiraIssueRead,
jiraIssueSearchRead
} from './mobile-jira-operations'
export const JIRA_TASK_LIMIT = 50
const DETAIL_TIMEOUT_MS = 30_000
// Raw JQL replaces the selected preset, matching desktop.
export async function fetchJiraTaskIssues(
client: RpcOperationSender,
args: { jql: string; filter: JiraIssueFilter; siteId: JiraSiteSelection | null }
): Promise<JiraIssue[]> {
const jql = args.jql.trim()
const issues = jql
? jiraIssueSearchRead.interpret(
await jiraIssueSearchRead.request(client, {
jql,
limit: JIRA_TASK_LIMIT,
siteId: args.siteId ?? undefined
})
)
: jiraIssueListRead.interpret(
await jiraIssueListRead.request(client, {
filter: args.filter,
limit: JIRA_TASK_LIMIT,
siteId: args.siteId ?? undefined
})
)
return [...issues].sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt))
}
export async function fetchJiraIssueDetail(
client: RpcOperationSender,
args: { key: string; siteId?: string }
): Promise<{ issue: JiraIssue; comments: ReturnType<typeof toJiraDetailComments> }> {
const params = { key: args.key, siteId: args.siteId }
const [issueReply, commentsReply] = await Promise.all([
jiraIssueRead.request(client, params, { timeoutMs: DETAIL_TIMEOUT_MS }),
jiraIssueCommentsRead
.request(client, params, { timeoutMs: DETAIL_TIMEOUT_MS })
.catch(() => null)
])
const issue = jiraIssueRead.interpret(issueReply)
if (!issue) {
throw new Error('Details not found')
}
let comments: ReturnType<typeof toJiraDetailComments> = []
try {
if (commentsReply) {
const decoded = jiraIssueCommentsRead.interpret(commentsReply)
comments = toJiraDetailComments(decoded.accepted ? decoded.value : [])
}
} catch {
// Optional comments must not hide a readable issue.
}
return { issue, comments }
}
@@ -13,7 +13,8 @@ const fullyAvailable = {
hasRepo: true,
githubAvailable: true,
gitlabAvailable: true,
linearAvailable: true
linearAvailable: true,
jiraAvailable: true
}
describe('resolveAvailableSmartModes', () => {
@@ -22,6 +23,7 @@ describe('resolveAvailableSmartModes', () => {
'smart',
'github',
'linear',
'jira',
'gitlab',
'branches',
'text'
@@ -46,13 +48,21 @@ describe('resolveAvailableSmartModes', () => {
githubAvailable: false,
gitlabAvailable: false
})
).toEqual(['smart', 'linear', 'branches', 'text'])
).toEqual(['smart', 'linear', 'jira', 'branches', 'text'])
expect(resolveAvailableSmartModes({ ...fullyAvailable, hasRepo: false })).toEqual([
'smart',
'linear',
'jira',
'text'
])
})
it('keeps the Jira tab repo-independent but hides it when Jira is disconnected', () => {
expect(resolveAvailableSmartModes({ ...fullyAvailable, hasRepo: false })).toContain('jira')
expect(resolveAvailableSmartModes({ ...fullyAvailable, jiraAvailable: false })).not.toContain(
'jira'
)
})
})
describe('resolveDefaultSmartMode', () => {
@@ -5,7 +5,7 @@ import type { MrStateFilter, SmartNameMode } from './mobile-composer-source-type
// icons.
export type SmartModeIcon =
| { type: 'lucide'; name: 'sparkles' | 'git-branch' | 'case-sensitive' }
| { type: 'provider'; provider: 'github' | 'gitlab' | 'linear' }
| { type: 'provider'; provider: 'github' | 'gitlab' | 'linear' | 'jira' }
export type SmartModeOption = {
id: SmartNameMode
@@ -14,11 +14,12 @@ export type SmartModeOption = {
}
// Order + labels + icons mirror desktop getSmartWorkspaceNameModes():
// Smart · GitHub · Linear · GitLab · Branch · Name.
// Smart · GitHub · Linear · Jira · GitLab · Branch · Name.
export const SMART_MODE_OPTIONS: readonly SmartModeOption[] = [
{ id: 'smart', label: 'Smart', icon: { type: 'lucide', name: 'sparkles' } },
{ id: 'github', label: 'GitHub', icon: { type: 'provider', provider: 'github' } },
{ id: 'linear', label: 'Linear', icon: { type: 'provider', provider: 'linear' } },
{ id: 'jira', label: 'Jira', icon: { type: 'provider', provider: 'jira' } },
{ id: 'gitlab', label: 'GitLab', icon: { type: 'provider', provider: 'gitlab' } },
{ id: 'branches', label: 'Branch', icon: { type: 'lucide', name: 'git-branch' } },
{ id: 'text', label: 'Name', icon: { type: 'lucide', name: 'case-sensitive' } }
@@ -31,6 +32,7 @@ export type SmartModeAvailabilityInput = {
githubAvailable: boolean
gitlabAvailable: boolean
linearAvailable: boolean
jiraAvailable: boolean
}
// Faithful port of the desktop availableModes filter. Non-git repos collapse to
@@ -51,6 +53,9 @@ export function resolveAvailableSmartModes(input: SmartModeAvailabilityInput): S
return input.tasksSupported && input.hasRepo && input.gitlabAvailable
case 'linear':
return input.tasksSupported && input.linearAvailable
case 'jira':
// Jira issues are not repo-scoped, so the tab needs no selected repo.
return input.tasksSupported && input.jiraAvailable
case 'branches':
return input.hasRepo
case 'text':
+1 -1
View File
@@ -1,5 +1,5 @@
import type { HostStackRouteTarget } from '../navigation/host-stack-navigation'
import type { TaskProvider } from './mobile-task-providers'
import type { TaskProvider } from '../../../src/shared/task-providers'
export function mobileTasksRouteTarget(
hostId: string,
-57
View File
@@ -1,57 +0,0 @@
export type TaskProvider = 'github' | 'gitlab' | 'linear'
const MOBILE_TASK_PROVIDERS: readonly TaskProvider[] = ['github', 'gitlab', 'linear']
const TASK_PROVIDER_SET = new Set<TaskProvider>(MOBILE_TASK_PROVIDERS)
export function normalizeVisibleTaskProviders(value: unknown): TaskProvider[] {
if (!Array.isArray(value)) {
return [...MOBILE_TASK_PROVIDERS]
}
const normalized: TaskProvider[] = []
for (const provider of value) {
if (!TASK_PROVIDER_SET.has(provider as TaskProvider)) {
continue
}
if (!normalized.includes(provider as TaskProvider)) {
normalized.push(provider as TaskProvider)
}
}
// Why: at least one provider must remain visible so the Tasks surface always
// has a valid source to select after settings hydration or manual edits.
return normalized.length > 0 ? normalized : [...MOBILE_TASK_PROVIDERS]
}
export type TaskProviderAvailability = {
gitlabInstalled: boolean
linearConnected: boolean
}
export function filterAvailableTaskProviders(
visibleProviders: readonly TaskProvider[],
availability: TaskProviderAvailability
): TaskProvider[] {
const available = visibleProviders.filter((provider) => {
if (provider === 'github') {
return true
}
if (provider === 'gitlab') {
return availability.gitlabInstalled
}
return availability.linearConnected
})
return available.length > 0 ? available : ['github']
}
export function resolveVisibleTaskProvider(
preferred: TaskProvider | null | undefined,
visibleProviders: readonly TaskProvider[]
): TaskProvider {
if (preferred && visibleProviders.includes(preferred)) {
return preferred
}
return visibleProviders[0] ?? 'github'
}
@@ -0,0 +1,38 @@
import type { JiraIssueFilter } from '../../../src/shared/jira-types'
import type { TaskProvider } from '../../../src/shared/task-providers'
import type { PickerOption } from '../components/PickerModal'
import { JIRA_FILTER_LABELS } from './mobile-jira-issue-filters'
export const PROVIDER_LABELS: Record<TaskProvider, string> = {
github: 'GitHub',
gitlab: 'GitLab',
linear: 'Linear',
jira: 'Jira'
}
// Records require a label for every task provider.
export const PROVIDER_EMPTY_LABELS: Record<TaskProvider, string> = {
github: 'No GitHub tasks',
gitlab: 'No GitLab tasks',
linear: 'No Linear tasks',
jira: 'No Jira tasks'
}
export const JIRA_FILTER_OPTIONS: PickerOption<JiraIssueFilter>[] = [
{
value: 'assigned',
label: JIRA_FILTER_LABELS.assigned,
subtitle: 'Unresolved issues assigned to you'
},
{
value: 'reported',
label: JIRA_FILTER_LABELS.reported,
subtitle: 'Unresolved issues you reported'
},
{
value: 'all',
label: JIRA_FILTER_LABELS.all,
subtitle: 'Unresolved issues across the selected sites'
},
{ value: 'done', label: JIRA_FILTER_LABELS.done, subtitle: 'Your recently resolved issues' }
]
@@ -96,6 +96,9 @@ export const mobileTasksChromeStyles = StyleSheet.create({
fontSize: 12,
color: colors.textSecondary
},
segmentDisabledText: {
color: colors.textMuted
},
searchBar: {
paddingHorizontal: spacing.md,
paddingVertical: spacing.sm,
@@ -143,8 +143,8 @@ export {
filterAvailableTaskProviders,
normalizeVisibleTaskProviders,
resolveVisibleTaskProvider
} from './mobile-task-providers'
export type { TaskProvider } from './mobile-task-providers'
} from '../../../src/shared/task-providers'
export type { TaskProvider } from '../../../src/shared/task-providers'
export { hasSettledHostRepoList } from './host-repo-list'
export { useHostRepoList } from './use-host-repo-list'
export { isHostedTaskRepo, reconcileRepoSelection } from './hosted-repo-selection'
@@ -11,6 +11,7 @@ import {
} from './mobile-tasks-dependencies'
import { linearWorkspaceSelect } from './mobile-task-runtime-operations'
import { styles } from './mobile-tasks-legacy-styles'
import { JIRA_FILTER_OPTIONS } from './mobile-task-view-options'
import {
GITLAB_VIEW_OPTIONS,
GITLAB_FILTER_OPTIONS,
@@ -333,3 +334,56 @@ export function renderMobileTasksLinearStatusPicker(model: ConnectionPresentatio
</BottomDrawer>
)
}
export function renderMobileTasksJiraFilterPicker(model: ConnectionPresentationModel) {
const {
jiraFilter,
persistTaskResumeState,
setAppliedQuery,
setJiraFilter,
setQuery,
setShowJiraFilterPicker,
showJiraFilterPicker,
taskUiReady
} = model
return (
<PickerModal
visible={taskUiReady && showJiraFilterPicker}
title="Jira Filter"
options={JIRA_FILTER_OPTIONS}
selected={jiraFilter}
onSelect={(filter) => {
setJiraFilter(filter)
setQuery('')
setAppliedQuery('')
persistTaskResumeState({ jiraPreset: filter, jiraQuery: '' })
}}
onClose={() => setShowJiraFilterPicker(false)}
/>
)
}
export function renderMobileTasksJiraSitePicker(model: ConnectionPresentationModel) {
const {
jiraConnection,
jiraSiteOptions,
setItems,
setJiraConnection,
setShowJiraSitePicker,
showJiraSitePicker,
taskUiReady
} = model
return (
<PickerModal
visible={taskUiReady && showJiraSitePicker}
title="Jira Site"
options={jiraSiteOptions}
selected={jiraConnection.selection ?? ''}
onSelect={(siteId) => {
setJiraConnection((current) => ({ ...current, selection: siteId }))
setItems([])
}}
onClose={() => setShowJiraSitePicker(false)}
/>
)
}
@@ -0,0 +1,69 @@
import { isTaskProvider, type TaskProvider } from '../../../src/shared/task-providers'
import { resolveVisibleTaskProvider } from './mobile-tasks-dependencies'
import { normalizeJiraFilter } from './mobile-jira-issue-filters'
import {
type RuntimeTaskSettings,
type TaskResumeState,
getTaskPresetQuery,
githubKindFromQuery,
normalizeGitHubPreset,
normalizeLinearFilter,
scopeGitHubTaskSearch
} from './mobile-tasks-legacy-foundation'
export type HydratedTaskViewState = {
provider: TaskProvider
githubPreset: ReturnType<typeof normalizeGitHubPreset>
defaultGithubPreset: ReturnType<typeof normalizeGitHubPreset>
githubKind: ReturnType<typeof githubKindFromQuery>
linearFilter: ReturnType<typeof normalizeLinearFilter>
jiraFilter: ReturnType<typeof normalizeJiraFilter>
query: string
appliedQuery: string
}
export function resolveHydratedTaskViewState(args: {
settings: RuntimeTaskSettings
resume: TaskResumeState
requestedTaskSource: TaskProvider | undefined
visibleProviders: TaskProvider[]
}): HydratedTaskViewState {
const { settings, resume, requestedTaskSource, visibleProviders } = args
const provider =
requestedTaskSource && visibleProviders.includes(requestedTaskSource)
? requestedTaskSource
: resolveVisibleTaskProvider(
isTaskProvider(settings.defaultTaskSource) ? settings.defaultTaskSource : undefined,
visibleProviders
)
// A cleared preset preserves the typed query; an absent preset uses the host default.
const githubPreset =
resume.githubItemsPreset === null
? normalizeGitHubPreset(settings.defaultTaskViewPreset)
: normalizeGitHubPreset(resume.githubItemsPreset ?? settings.defaultTaskViewPreset)
const githubQuery =
resume.githubItemsPreset === null
? (resume.githubItemsQuery ?? '')
: getTaskPresetQuery(githubPreset)
const query =
provider === 'github'
? githubQuery
: provider === 'linear'
? (resume.linearQuery ?? '')
: provider === 'jira'
? (resume.jiraQuery ?? '')
: ''
return {
provider,
githubPreset,
defaultGithubPreset: normalizeGitHubPreset(settings.defaultTaskViewPreset),
githubKind: githubKindFromQuery(githubQuery, githubPreset),
linearFilter: normalizeLinearFilter(resume.linearPreset),
jiraFilter: normalizeJiraFilter(resume.jiraPreset),
query,
appliedQuery:
provider === 'github'
? scopeGitHubTaskSearch(githubQuery, githubKindFromQuery(githubQuery, githubPreset))
: query
}
}
@@ -44,7 +44,10 @@ export function renderMobileTasksItemActions(model: ConnectionPresentationModel)
style={styles.actionRow}
disabled={creatingKey === actionItem.key}
onPress={() => {
if (actionItem.provider === 'linear' && workspaceRepos.length > 1) {
// Jira, like Linear, is not repo-scoped, so the target repo has to be asked for
// rather than defaulted to the first one.
const needsRepoChoice = actionItem.provider === 'linear' || actionItem.provider === 'jira'
if (needsRepoChoice && workspaceRepos.length > 1) {
setWorkspaceRepoPickerItem(actionItem)
return
}
@@ -16,6 +16,9 @@ export function taskKindLabel(item: TaskItem): string {
if (item.provider === 'gitlabTodo') {
return `${gitLabTodoTargetLabel(item.source)} todo`
}
if (item.provider === 'jira') {
return 'Jira issue'
}
return 'Linear ticket'
}
@@ -26,6 +29,9 @@ export function taskExternalOpenLabel(item: TaskItem): string {
if (item.provider === 'gitlab' || item.provider === 'gitlabTodo') {
return 'Open in GitLab'
}
if (item.provider === 'jira') {
return 'Open in Jira'
}
return 'Open in Linear'
}
@@ -25,6 +25,7 @@ import {
renderMobileTasksItemFiles
} from './mobile-tasks-item-review-sections'
import { renderMobileTasksItemFieldEditors } from './mobile-tasks-item-field-editors'
import { JiraDetailMeta } from '../components/JiraDetailMeta'
export function renderMobileTasksItemDetailContent(model: ConnectionPresentationModel) {
const {
@@ -81,6 +82,13 @@ export function renderMobileTasksItemDetailContent(model: ConnectionPresentation
<Text style={styles.detailMetaValue}>{detailPayload.project.name}</Text>
</View>
) : null}
{detailPayload.provider === 'jira' ? (
<JiraDetailMeta
assignee={detailPayload.assignee}
priorityName={detailPayload.priorityName}
projectName={detailPayload.projectName}
/>
) : null}
{(detailPayload.provider === 'github' || detailPayload.provider === 'gitlab') &&
detailPayload.assignees.length > 0 ? (
<View style={styles.detailMetaItem}>
+18 -9
View File
@@ -1,12 +1,7 @@
import { getGitHubTaskKind } from '../../../src/shared/task-github-search'
export { scopeGitHubTaskSearch } from '../../../src/shared/task-github-search'
export { getTaskPresetQuery } from '../../../src/shared/task-preset-query'
import {
type RpcSuccess,
type TaskProvider,
type GitHubOwnerRepo,
colors
} from './mobile-tasks-dependencies'
import { type RpcSuccess, type GitHubOwnerRepo, colors } from './mobile-tasks-dependencies'
import type {
GitHubPreset,
GitHubProjectRow,
@@ -14,6 +9,7 @@ import type {
LinearFilter
} from './mobile-tasks-view-state-types'
import type { TaskItem } from './mobile-tasks-project-workspace-types'
import type { JiraIssue } from '../../../src/shared/jira-types'
import type {
GitHubWorkItem,
GitLabTodo,
@@ -47,9 +43,7 @@ export function formatUpdatedAt(value: string): string {
return `${Math.floor(hours / 24)}d`
}
export function isTaskProvider(value: unknown): value is TaskProvider {
return value === 'github' || value === 'gitlab' || value === 'linear'
}
export { isTaskProvider } from '../../../src/shared/task-providers'
export function normalizeGitHubPreset(value: unknown): GitHubPreset {
return value === 'my-issues' ||
@@ -256,6 +250,21 @@ export async function mapWithConcurrency<T, R>(
return results
}
export function createJiraTask(issue: JiraIssue): Extract<TaskItem, { provider: 'jira' }> {
const project = issue.project.name.trim() || issue.project.key
return {
// Keys are site-qualified because the 'all' selection fans out across sites
// and two sites can hand back the same issue id.
key: `jira:${issue.siteId ?? 'site'}:${issue.id}`,
provider: 'jira',
title: issue.title,
subtitle: `${issue.key} · ${project}`,
status: issue.status.name,
updatedAt: issue.updatedAt,
source: issue
}
}
export function createLinearTask(issue: LinearIssue): TaskItem {
return {
key: `linear:${issue.workspaceId ?? 'workspace'}:${issue.id}`,
@@ -18,6 +18,7 @@ import {
Check
} from './mobile-tasks-dependencies'
import { styles } from './mobile-tasks-legacy-styles'
import { detailPayloadBody } from './mobile-tasks-provider-detail-types'
export function renderMobileTasksItemBodyEditor(model: ConnectionPresentationModel) {
const {
@@ -81,10 +82,7 @@ export function renderMobileTasksItemBodyEditor(model: ConnectionPresentationMod
<MobileMarkdown content={itemBodyDraft} fallback="No description." />
</>
) : (
<MobileMarkdown
content={detailPayload.provider === 'linear' ? detailPayload.description : detailPayload.body}
fallback="No description."
/>
<MobileMarkdown content={detailPayloadBody(detailPayload)} fallback="No description." />
)
}
@@ -0,0 +1,56 @@
import type { ConnectionPresentationModel } from './use-mobile-tasks-connection-presentation'
import { Pressable, Text } from './mobile-tasks-dependencies'
import { styles } from './mobile-tasks-legacy-styles'
// Multiple connected sites add a scope picker beside the issue filter.
export function renderMobileTasksJiraViewControls(model: ConnectionPresentationModel) {
const {
provider,
jiraConnection,
jiraFilterLabel,
jiraSiteChipLabel,
appliedQuery,
taskUiReady,
setShowJiraSitePicker,
setShowJiraFilterPicker
} = model
if (provider !== 'jira' || !jiraConnection.connected) {
return null
}
const queryOverridesPreset = appliedQuery.trim().length > 0
return (
<>
{jiraConnection.sites.length > 1 ? (
<Pressable
style={styles.segmentButton}
disabled={!taskUiReady}
onPress={() => {
if (taskUiReady) {
setShowJiraSitePicker(true)
}
}}
>
<Text style={styles.segmentSecondaryText}>{jiraSiteChipLabel}</Text>
</Pressable>
) : null}
<Pressable
style={styles.segmentButton}
disabled={!taskUiReady || queryOverridesPreset}
onPress={() => {
if (taskUiReady) {
setShowJiraFilterPicker(true)
}
}}
>
<Text
style={[
styles.segmentSecondaryText,
queryOverridesPreset ? styles.segmentDisabledText : null
]}
>
{jiraFilterLabel}
</Text>
</Pressable>
</>
)
}
@@ -11,11 +11,15 @@ import { styles } from './mobile-tasks-legacy-styles'
import { renderMobileTasksGitHubProjectList } from './mobile-tasks-github-project-list'
import { renderMobileTasksLinearList } from './mobile-tasks-linear-list'
import { renderMobileTasksProviderItemList } from './mobile-tasks-provider-item-list'
import { JiraConnectPrompt } from '../components/JiraConnectPrompt'
export function renderMobileTasksListSurface(model: ConnectionPresentationModel) {
const {
githubMode,
linearConnected,
jiraConnection,
refreshJiraConnection,
setError,
provider,
setLinearApiKeyDraft,
setLinearConnectError,
@@ -38,6 +42,17 @@ export function renderMobileTasksListSurface(model: ConnectionPresentationModel)
<ActivityIndicator size="small" color={colors.textSecondary} />
</View>
)
) : provider === 'jira' && !jiraConnection.connected ? (
<JiraConnectPrompt
credentialError={jiraConnection.credentialError}
unavailableReason={jiraConnection.unavailableReason}
disabled={!taskUiReady}
onCheckAgain={() => {
void refreshJiraConnection().catch((err: unknown) => {
setError(err instanceof Error ? err.message : 'Failed to read Jira status')
})
}}
/>
) : provider === 'linear' && !linearConnected ? (
<View style={styles.centered}>
<TaskProviderLogo provider="linear" size={32} color={colors.textSecondary} />
+15
View File
@@ -60,6 +60,18 @@ export const PROVIDER_OPTIONS: PickerOption<TaskProvider>[] = [
color={selected ? colors.textPrimary : colors.textSecondary}
/>
)
},
{
value: 'jira',
label: 'Jira',
subtitle: 'Assigned and reported issues',
renderIcon: (selected) => (
<TaskProviderLogo
provider="jira"
size={16}
color={selected ? colors.textPrimary : colors.textSecondary}
/>
)
}
]
@@ -86,6 +98,9 @@ export function taskWorkspaceFallback(item: ActionableTaskItem): string {
if (item.provider === 'github' || item.provider === 'gitlab') {
return `${item.source.type}-${item.source.number}`
}
if (item.provider === 'jira') {
return item.source.key.toLowerCase()
}
return item.source.identifier.toLowerCase()
}
@@ -7,6 +7,7 @@ import type {
} from './mobile-tasks-provider-detail-types'
import type { WorkspaceAgentChoice, SparsePreset } from './mobile-tasks-dependencies'
import type { GitHubProjectRow } from './mobile-tasks-view-state-types'
import type { JiraIssue } from '../../../src/shared/jira-types'
export type TaskItem =
| {
@@ -45,6 +46,15 @@ export type TaskItem =
updatedAt: string
source: LinearIssue
}
| {
key: string
provider: 'jira'
title: string
subtitle: string
status: string
updatedAt: string
source: JiraIssue
}
export type ActionableTaskItem = Exclude<TaskItem, { provider: 'gitlabTodo' }>
@@ -12,6 +12,7 @@ import { styles } from './mobile-tasks-legacy-styles'
import { getRepoBadgeColor } from './mobile-tasks-legacy-foundation'
import { renderMobileTasksGitHubViewControls } from './mobile-tasks-github-view-controls'
import { renderMobileTasksLinearViewControls } from './mobile-tasks-linear-view-controls'
import { renderMobileTasksJiraViewControls } from './mobile-tasks-jira-view-controls'
export function renderMobileTasksProviderControls(model: ConnectionPresentationModel) {
const {
@@ -116,6 +117,8 @@ export function renderMobileTasksProviderControls(model: ConnectionPresentationM
{renderMobileTasksLinearViewControls(model)}
{renderMobileTasksJiraViewControls(model)}
{provider !== 'linear' && !(provider === 'github' && githubMode === 'project') ? (
<Pressable
style={styles.segmentButton}
@@ -277,3 +277,19 @@ export type DetailPayload =
project?: LinearProject
children: LinearIssueChild[]
}
| {
provider: 'jira'
description: string
comments: DetailComment[]
labels: string[]
assignee?: string
projectName: string
issueTypeName: string
priorityName?: string
}
export function detailPayloadBody(payload: DetailPayload): string {
return payload.provider === 'linear' || payload.provider === 'jira'
? payload.description
: payload.body
}
@@ -22,6 +22,7 @@ import {
GITHUB_KIND_OPTIONS
} from './mobile-tasks-legacy-foundation'
import { styles } from './mobile-tasks-legacy-styles'
import { normalizeJiraFilter } from './mobile-jira-issue-filters'
export function renderMobileTasksProviderPicker(model: ConnectionPresentationModel) {
const {
@@ -35,6 +36,7 @@ export function renderMobileTasksProviderPicker(model: ConnectionPresentationMod
setGithubPreset,
setItems,
setLinearFilter,
setJiraFilter,
setProvider,
setQuery,
setShowProviderPicker,
@@ -79,6 +81,11 @@ export function renderMobileTasksProviderPicker(model: ConnectionPresentationMod
setLinearFilter(normalizeLinearFilter(resume.linearPreset))
setQuery(nextQuery)
setAppliedQuery(nextQuery.trim())
} else if (next === 'jira') {
const nextQuery = resume.jiraQuery ?? ''
setJiraFilter(normalizeJiraFilter(resume.jiraPreset))
setQuery(nextQuery)
setAppliedQuery(nextQuery.trim())
} else {
setQuery('')
setAppliedQuery('')
@@ -81,6 +81,15 @@ export function taskRepositoryMeta(
color: repoColor(item.source.projectPath)
}
}
if (item.provider === 'jira') {
// Jira has no per-issue color, so derive a stable one from the project the
// way GitLab todos do from their project path.
return {
key: item.source.project.id,
label: item.source.project.name || item.source.project.key,
color: repoColor(item.source.project.key)
}
}
return {
key: item.source.team.id,
label: item.source.team.name,
@@ -14,6 +14,7 @@ export function renderMobileTasksSearchControl(model: ConnectionPresentationMode
gitlabView,
isGithubProjectSearch,
linearConnected,
jiraConnection,
persistTaskResumeState,
provider,
providerLabel,
@@ -25,13 +26,18 @@ export function renderMobileTasksSearchControl(model: ConnectionPresentationMode
taskUiReady
} = model
return provider === 'gitlab' && gitlabView === 'todos' ? null : provider === 'linear' &&
!linearConnected ? null : (
!linearConnected ? null : provider === 'jira' && !jiraConnection.connected ? null : (
<View style={styles.searchBar}>
<MobileSearchField
value={isGithubProjectSearch ? githubProjectSearch : query}
onChangeText={isGithubProjectSearch ? setGithubProjectSearch : setQuery}
placeholder={
isGithubProjectSearch ? 'Search project view...' : `Search ${providerLabel} tasks...`
isGithubProjectSearch
? 'Search project view...'
: provider === 'jira'
? // Matches desktop: the Jira box takes raw JQL, not free text.
'Jira JQL, e.g. project = ABC AND statusCategory != Done'
: `Search ${providerLabel} tasks...`
}
// Why: GitHub items seed the field with a preset query, so a bare
// value.length check would always show clear. Project mode shows clear
@@ -6,6 +6,7 @@ import type {
GitHubProjectRef
} from './mobile-tasks-dependencies'
import type { GitHubProjectSortDirection } from '../../../src/shared/github/project-types'
import type { JiraIssueFilter } from '../../../src/shared/jira-types'
export type GitHubTaskKind = 'issues' | 'prs'
@@ -46,6 +47,8 @@ export type TaskResumeState = {
githubProjectHiddenFieldIdsByView?: Record<string, string[]>
linearPreset?: LinearFilter
linearQuery?: string
jiraPreset?: JiraIssueFilter
jiraQuery?: string
}
export type RuntimeTaskSettings = {
@@ -46,6 +46,8 @@ const smartArgs = {
githubAvailable: true,
gitlabAvailable: true,
linearAvailable: true,
jiraAvailable: false,
jiraSiteId: null,
mrStateFilter: 'opened' as const,
linearWorkspaceId: null
}
+21 -1
View File
@@ -9,10 +9,12 @@ import {
import type { RpcClient } from '../transport/rpc-client'
import { isGitHubWorkItemsSshRemoteRequiredError } from './mobile-work-items'
import type { MrStateFilter } from './mobile-composer-source-types'
import type { JiraIssue, JiraSiteSelection } from '../../../src/shared/jira-types'
import {
searchBranches,
searchGitHubItems,
searchGitLabItems,
searchJiraIssues,
searchLinearIssues
} from './smart-source-search-requests'
@@ -20,6 +22,7 @@ export type SmartFanOutResult = {
githubItems: GitHubWorkItem[]
gitlabItems: GitLabWorkItem[]
linearIssues: LinearIssue[]
jiraIssues: JiraIssue[]
branches: BaseRefSearchResult[]
needsGitHubRemote: boolean
error: string
@@ -29,6 +32,7 @@ const EMPTY: Omit<SmartFanOutResult, 'needsGitHubRemote' | 'error'> = {
githubItems: [],
gitlabItems: [],
linearIssues: [],
jiraIssues: [],
branches: []
}
@@ -44,6 +48,11 @@ function shouldSearchLinear(mode: SmartNameMode, linearAvailable: boolean): bool
return linearAvailable && (mode === 'smart' || mode === 'linear')
}
// The shared row builder only shows Jira search results in the Jira tab.
function shouldSearchJira(mode: SmartNameMode, jiraAvailable: boolean): boolean {
return jiraAvailable && mode === 'jira'
}
function shouldSearchBranches(mode: SmartNameMode, query: string): boolean {
return mode === 'branches' || (mode === 'smart' && query.trim().length > 0)
}
@@ -56,8 +65,10 @@ type FanOutArgs = {
githubAvailable: boolean
gitlabAvailable: boolean
linearAvailable: boolean
jiraAvailable: boolean
mrStateFilter: MrStateFilter
linearWorkspaceId: string | null | undefined
jiraSiteId: JiraSiteSelection | null | undefined
}
// Runs every provider search the active mode needs, concurrently. Smart mode is
@@ -78,6 +89,7 @@ export async function fanOutSmartSearch(args: FanOutArgs): Promise<SmartFanOutRe
githubAvailable,
gitlabAvailable,
linearAvailable,
jiraAvailable,
mrStateFilter
} = args
const isSmart = mode === 'smart'
@@ -93,13 +105,17 @@ export async function fanOutSmartSearch(args: FanOutArgs): Promise<SmartFanOutRe
linear: shouldSearchLinear(mode, linearAvailable)
? searchLinearIssues(client, query, args.linearWorkspaceId)
: null,
jira: shouldSearchJira(mode, jiraAvailable)
? searchJiraIssues(client, query, args.jiraSiteId)
: null,
branches:
shouldSearchBranches(mode, query) && repoId ? searchBranches(client, repoId, query) : null
}
const [github, gitlab, linear, branches] = await Promise.allSettled([
const [github, gitlab, linear, jira, branches] = await Promise.allSettled([
tasks.github ?? Promise.resolve<GitHubWorkItem[]>([]),
tasks.gitlab ?? Promise.resolve<GitLabWorkItem[]>([]),
tasks.linear ?? Promise.resolve<LinearIssue[]>([]),
tasks.jira ?? Promise.resolve<JiraIssue[]>([]),
tasks.branches ?? Promise.resolve<BaseRefSearchResult[]>([])
])
@@ -123,6 +139,9 @@ export async function fanOutSmartSearch(args: FanOutArgs): Promise<SmartFanOutRe
if (linear.status === 'rejected') {
fail(linear.reason)
}
if (jira.status === 'rejected') {
fail(jira.reason)
}
if (branches.status === 'rejected') {
fail(branches.reason)
}
@@ -132,6 +151,7 @@ export async function fanOutSmartSearch(args: FanOutArgs): Promise<SmartFanOutRe
githubItems: github.status === 'fulfilled' ? github.value : [],
gitlabItems: gitlab.status === 'fulfilled' ? gitlab.value : [],
linearIssues: linear.status === 'fulfilled' ? linear.value : [],
jiraIssues: jira.status === 'fulfilled' ? jira.value : [],
branches: branches.status === 'fulfilled' ? branches.value : [],
needsGitHubRemote,
error
@@ -14,6 +14,8 @@ function Probe(props: { client: RpcClient; query: string }) {
githubAvailable: true,
gitlabAvailable: false,
linearAvailable: false,
jiraAvailable: false,
jiraSites: [],
mrStateFilter: 'opened',
repos: [{ id: 'repo-1', displayName: 'orca', slug: { owner: 'stablyai', repo: 'orca' } }]
})
@@ -3,6 +3,13 @@ import type { GitLabWorkItem } from '../../../src/shared/gitlab-types'
import type { LinearIssue } from '../../../src/shared/linear/issue-types'
import type { BaseRefSearchResult } from '../../../src/shared/repo-types'
import type { RpcClient } from '../transport/rpc-client'
import type { JiraIssue, JiraSiteSelection } from '../../../src/shared/jira-types'
import { getJiraIssueSearchQuery } from '../../../src/shared/new-workspace/smart-workspace-source-results'
import {
buildJiraIssueKeyJql,
buildJiraTextMatchJql
} from '../../../src/shared/jira-search-input-jql'
import { jiraIssueSearchRead } from './mobile-jira-operations'
import { repoBaseRefSearchRead } from './mobile-workspace-source-operations'
import {
githubWorkItemSearchRead,
@@ -15,6 +22,7 @@ import type { MrStateFilter } from './mobile-composer-source-types'
const GITLAB_PER_PAGE = 50
const LINEAR_LIMIT = 50
const JIRA_LIMIT = 50
const BRANCH_LIMIT = 20
// Why: the desktop Smart picker returns BOTH issues and PRs — the runtime's
@@ -93,6 +101,27 @@ export async function searchLinearIssues(
return issues as LinearIssue[]
}
// The composer accepts key/text searches; Tasks accepts raw JQL.
export async function searchJiraIssues(
client: RpcClient,
query: string,
siteId: JiraSiteSelection | null | undefined
): Promise<JiraIssue[]> {
const searchable = getJiraIssueSearchQuery(query)
if (!searchable) {
return []
}
// Key first, as the desktop does: an issue key is an exact lookup, not a text match.
const jql = buildJiraIssueKeyJql(searchable) ?? buildJiraTextMatchJql(searchable)
return jiraIssueSearchRead.interpret(
await jiraIssueSearchRead.request(client, {
jql,
limit: JIRA_LIMIT,
siteId: siteId ?? undefined
})
)
}
export async function searchBranches(
client: RpcClient,
repoId: string,
@@ -82,6 +82,16 @@ function toTaskItem(item: MobileLinkedWorkItem, targetRepoId: string): Workspace
}
}
}
if (item.provider === 'jira') {
return {
provider: 'jira',
source: {
key: item.jiraIdentifier ?? '',
title: item.title,
url: item.url
}
}
}
return {
provider: 'linear',
source: {
+100 -99
View File
@@ -6,20 +6,22 @@ import { resolveComposerManualBranchNameChange } from '../../../src/shared/compo
import { resolveGitHubWorkItemIdentity } from '../../../src/shared/new-workspace/github-work-item-identity'
import { getForkPushWarning } from '../../../src/shared/new-workspace/fork-push-warning'
import type { RpcClient } from '../transport/rpc-client'
import type { JiraIssue } from '../../../src/shared/jira-types'
import {
buildGitHubLinkedWorkItem,
buildGitLabLinkedWorkItem,
buildJiraLinkedWorkItem,
buildLinearLinkedWorkItem,
buildSmartNameSelection,
resolveComposerBranchPick,
resolveComposerCreateSelection,
resolveJiraAutoName,
resolveLinearAutoName,
resolveWorkItemAutoName,
shouldApplyAutoName
resolveWorkItemAutoName
} from './composer-linked-work-item'
import {
resolveComposerMrBase,
resolveComposerPrBase,
resolveComposerHostedItemBase,
toComposerBaseState,
type ComposerHostedBase
} from './composer-source-base-resolve'
import type {
@@ -80,12 +82,7 @@ export function useMobileComposerSource(args: UseMobileComposerSourceArgs) {
if (resolveTokenRef.current !== token) {
return
}
setBase({
baseBranch: result.baseBranch,
compareBaseRef: result.compareBaseRef,
pushTarget: result.pushTarget,
branchNameOverride: result.branchNameOverride
})
setBase(toComposerBaseState(result))
setForkPushWarning(getForkPushWarning(result))
})
.catch((error: unknown) => {
@@ -104,113 +101,120 @@ export function useMobileComposerSource(args: UseMobileComposerSourceArgs) {
[onError]
)
// A cross-repo selection can precede the selectedRepoId update.
const selectHostedItem = useCallback(
(args: {
item: {
repoId?: string
type: 'issue' | 'pr' | 'mr'
number: number
branchName?: string
baseRefName?: string
isCrossRepository?: boolean
}
provider: 'github' | 'gitlab'
linked: MobileLinkedWorkItem
autoName: string
}) => {
const token = (resolveTokenRef.current += 1)
const repoId = args.item.repoId || selectedRepoId
setLinkedWorkItem(args.linked)
applyAutoName(args.autoName, name)
clearBaseAndBranch()
if (!client || !repoId) {
return
}
const pending = resolveComposerHostedItemBase({
...args.item,
client,
repoId,
provider: args.provider
})
if (pending) {
runBaseResolve(token, pending)
}
},
[applyAutoName, clearBaseAndBranch, client, name, runBaseResolve, selectedRepoId]
)
const handleSmartGitHubItemSelect = useCallback(
(item: GitHubWorkItem) => {
const token = (resolveTokenRef.current += 1)
const identity = resolveGitHubWorkItemIdentity(item)
// Resolve the PR base against the item's OWN repo — a cross-repo accept
// switches repos then selects synchronously, so selectedRepoId is stale.
const repoId = item.repoId || selectedRepoId
setLinkedWorkItem(
buildGitHubLinkedWorkItem({
type: identity.type,
number: identity.number,
selectHostedItem({
item: { ...item, ...identity },
provider: 'github',
linked: buildGitHubLinkedWorkItem({
...identity,
title: item.title,
url: item.url,
repoId: item.repoId
})
)
applyAutoName(
resolveWorkItemAutoName({ ...identity, title: item.title, provider: 'github' }),
name
)
clearBaseAndBranch()
if (identity.type !== 'pr' || !client || !repoId) {
return
}
runBaseResolve(
token,
resolveComposerPrBase({
client,
repoId,
prNumber: identity.number,
...(item.branchName ? { headRefName: item.branchName } : {}),
...(item.baseRefName ? { baseRefName: item.baseRefName } : {}),
...(item.isCrossRepository !== undefined
? { isCrossRepository: item.isCrossRepository }
: {})
})
)
}),
autoName: resolveWorkItemAutoName({ ...identity, title: item.title, provider: 'github' })
})
},
[applyAutoName, clearBaseAndBranch, client, name, runBaseResolve, selectedRepoId]
[selectHostedItem]
)
const handleSmartGitLabItemSelect = useCallback(
(item: GitLabWorkItem) => {
const token = (resolveTokenRef.current += 1)
// Resolve the MR base against the item's OWN repo (see the GitHub handler).
const repoId = item.repoId || selectedRepoId
setLinkedWorkItem(
buildGitLabLinkedWorkItem({
selectHostedItem({
item,
provider: 'gitlab',
linked: buildGitLabLinkedWorkItem({
type: item.type,
number: item.number,
title: item.title,
url: item.url,
repoId: item.repoId
})
)
applyAutoName(
resolveWorkItemAutoName({
}),
autoName: resolveWorkItemAutoName({
type: item.type,
number: item.number,
title: item.title,
provider: 'gitlab'
}),
name
)
clearBaseAndBranch()
if (item.type !== 'mr' || !client || !repoId) {
return
}
runBaseResolve(
token,
resolveComposerMrBase({
client,
repoId,
mrIid: item.number,
...(item.branchName ? { sourceBranch: item.branchName } : {}),
...(item.baseRefName ? { targetBranch: item.baseRefName } : {}),
...(item.isCrossRepository !== undefined
? { isCrossRepository: item.isCrossRepository }
: {})
})
)
})
},
[applyAutoName, clearBaseAndBranch, client, name, runBaseResolve, selectedRepoId]
[selectHostedItem]
)
// Jira and Linear issues share the same naming and base-reset rules.
const selectIdentifiedIssue = useCallback(
(args: { linked: MobileLinkedWorkItem; suggested: string; identifier: string }) => {
resolveTokenRef.current += 1
setLinkedWorkItem(args.linked)
// A bare issue key is lookup text, so it can adopt the issue's name.
const identifierTyped =
!linkedWorkItem && name.trim().toLowerCase() === args.identifier.toLowerCase()
if (identifierTyped && args.suggested) {
setNameState(args.suggested)
lastAutoNameRef.current = args.suggested
} else {
applyAutoName(args.suggested, name)
}
clearBaseAndBranch()
},
[applyAutoName, clearBaseAndBranch, lastAutoNameRef, linkedWorkItem, name, setNameState]
)
const handleSmartLinearIssueSelect = useCallback(
(issue: LinearIssue) => {
resolveTokenRef.current += 1
setLinkedWorkItem(buildLinearLinkedWorkItem(issue))
const suggested = resolveLinearAutoName(issue)
const identifierTyped =
!linkedWorkItem && name.trim().toLowerCase() === issue.identifier.toLowerCase()
if (
suggested &&
(identifierTyped ||
shouldApplyAutoName({
currentName: name,
lastAutoName: lastAutoNameRef.current,
lookupTextIsQuery: !linkedWorkItem
}))
) {
setNameState(suggested)
lastAutoNameRef.current = suggested
}
clearBaseAndBranch()
},
[clearBaseAndBranch, linkedWorkItem, name]
(issue: LinearIssue) =>
selectIdentifiedIssue({
linked: buildLinearLinkedWorkItem(issue),
suggested: resolveLinearAutoName(issue),
identifier: issue.identifier
}),
[selectIdentifiedIssue]
)
const handleSmartJiraIssueSelect = useCallback(
(issue: JiraIssue) =>
selectIdentifiedIssue({
linked: buildJiraLinkedWorkItem(issue),
suggested: resolveJiraAutoName(issue),
identifier: issue.key
}),
[selectIdentifiedIssue]
)
const handleSmartBranchSelect = useCallback(
@@ -266,17 +270,13 @@ export function useMobileComposerSource(args: UseMobileComposerSourceArgs) {
const handleBranchNameOverrideChange = useCallback(
(value: string) => {
const next = resolveComposerManualBranchNameChange({
const { forkPushWarning: nextWarning, ...nextBase } = resolveComposerManualBranchNameChange({
value,
pushTarget: base.pushTarget,
forkPushWarning
})
setBase({
...base,
branchNameOverride: next.branchNameOverride,
pushTarget: next.pushTarget
})
setForkPushWarning(next.forkPushWarning)
setBase({ ...base, ...nextBase })
setForkPushWarning(nextWarning)
},
[base, forkPushWarning]
)
@@ -317,6 +317,7 @@ export function useMobileComposerSource(args: UseMobileComposerSourceArgs) {
handleSmartGitHubItemSelect,
handleSmartGitLabItemSelect,
handleSmartLinearIssueSelect,
handleSmartJiraIssueSelect,
handleSmartBranchSelect,
handleSmartCreateBranch,
handleClearSmartNameSelection
@@ -0,0 +1,79 @@
import { createElement } from 'react'
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
import { afterEach, describe, expect, it } from 'vitest'
import type { MobileJiraConnection } from './jira-mobile-connection'
import { useMobileJiraConnectionState } from './use-mobile-jira-connection-state'
const mounted: ReactTestRenderer[] = []
afterEach(() => {
act(() => {
for (const renderer of mounted.splice(0)) {
renderer.unmount()
}
})
})
function mountConnection(client: object) {
let value: ReturnType<typeof useMobileJiraConnectionState> | undefined
function Probe(props: { client: object; state: string }) {
value = useMobileJiraConnectionState(props.client, props.state)
return null
}
let renderer: ReactTestRenderer | undefined
act(() => {
renderer = create(createElement(Probe, { client, state: 'connected' }))
mounted.push(renderer)
})
return {
current() {
if (!value) {
throw new Error('Connection did not render')
}
return value
},
update(nextClient: object, state = 'connected') {
act(() => {
renderer?.update(createElement(Probe, { client: nextClient, state }))
})
}
}
}
const connected: MobileJiraConnection = {
connected: true,
sites: [],
selection: 'all',
credentialError: null,
unavailableReason: null
}
describe('mobile Jira connection session ownership', () => {
it('discards the previous connection on a host switch before another status arrives', () => {
const fixture = mountConnection({})
act(() => fixture.current()[1](connected))
fixture.update({})
expect(fixture.current()[0]).toMatchObject({ connected: false, selection: null })
})
it('retires old setters even after switching back to the same client', () => {
const client = {}
const fixture = mountConnection(client)
const oldSet = fixture.current()[1]
fixture.update({})
fixture.update(client)
act(() => fixture.current()[1](connected))
act(() => oldSet({ ...connected, connected: false }))
expect(fixture.current()[0].connected).toBe(true)
})
it('requires fresh status after disconnecting and reconnecting the same client', () => {
const client = {}
const fixture = mountConnection(client)
const oldSet = fixture.current()[1]
act(() => oldSet(connected))
fixture.update(client, 'disconnected')
fixture.update(client)
act(() => oldSet(connected))
expect(fixture.current()[0]).toMatchObject({ connected: false, selection: null })
})
})
@@ -0,0 +1,34 @@
import { useCallback, useLayoutEffect, useMemo, useRef, useState, type SetStateAction } from 'react'
import type { MobileJiraConnection } from './jira-mobile-connection'
export const EMPTY_MOBILE_JIRA_CONNECTION: MobileJiraConnection = {
connected: false,
sites: [],
selection: null,
credentialError: null,
unavailableReason: null
}
export function useMobileJiraConnectionState(client: unknown, connState: string | undefined) {
const owner = useMemo(() => ({ client, connState }), [client, connState])
const committedOwner = useRef(owner)
const [state, setState] = useState({ owner, value: EMPTY_MOBILE_JIRA_CONNECTION })
useLayoutEffect(() => {
committedOwner.current = owner
}, [owner])
const setConnection = useCallback(
(update: SetStateAction<MobileJiraConnection>): void => {
setState((current) => {
if (committedOwner.current !== owner) {
return current
}
const previous = current.owner === owner ? current.value : EMPTY_MOBILE_JIRA_CONNECTION
return { owner, value: typeof update === 'function' ? update(previous) : update }
})
},
[owner]
)
// A reused screen must not read another desktop's sites before its status arrives.
const connection = state.owner === owner ? state.value : EMPTY_MOBILE_JIRA_CONNECTION
return [connection, setConnection] as const
}
@@ -1,5 +1,6 @@
import type { ProviderViewProjectionModel } from './use-mobile-tasks-provider-view-projection'
import { classifyConnection } from './mobile-tasks-dependencies'
import { PROVIDER_EMPTY_LABELS } from './mobile-task-view-options'
export function useMobileTasksConnectionPresentation(model: ProviderViewProjectionModel) {
const {
@@ -22,11 +23,7 @@ export function useMobileTasksConnectionPresentation(model: ProviderViewProjecti
? 'Connect to a host to load tasks'
: query
? 'No matching tasks'
: provider === 'github'
? 'No GitHub tasks'
: provider === 'gitlab'
? 'No GitLab tasks'
: 'No Linear tasks'
: PROVIDER_EMPTY_LABELS[provider]
const isGithubProjectSearch = provider === 'github' && githubMode === 'project'
return Object.assign(model, { headerVerdict, emptyLabel, isGithubProjectSearch })
}
@@ -1,6 +1,8 @@
import type { ProviderCheckSummary } from '../../../src/shared/github/pull-request-types'
import type { GitLabWorkItem } from './mobile-tasks-provider-detail-types'
import type { ItemDetailMetadataEffectsModel } from './use-mobile-tasks-item-detail-metadata-effects'
import { fetchJiraIssueDetail } from './mobile-jira-task-source'
import { createJiraTask } from './mobile-tasks-item-mapping'
import {
type HostedReviewDecision,
buildGitLabCheckSummary,
@@ -182,6 +184,43 @@ export function useMobileTasksItemDetailLoading<Model extends ItemDetailLoadingI
return
}
if (actionItem.provider === 'jira') {
const { issue, comments } = await fetchJiraIssueDetail(client, {
key: actionItem.source.key,
siteId: actionItem.source.siteId
})
if (!stale) {
setDetailPayload({
provider: 'jira',
description: issue.description ?? '',
comments,
labels: issue.labels ?? [],
assignee: issue.assignee?.displayName,
projectName: issue.project.name || issue.project.key,
issueTypeName: issue.issueType.name,
priorityName: issue.priority?.name
})
setActionItem((current) => {
if (
current?.provider !== 'jira' ||
current.source.key !== issue.key ||
current.source.siteId !== issue.siteId
) {
return current
}
// Keep identity stable so hydrating details does not fetch them again.
const alreadyHydrated =
current.title === issue.title &&
current.status === issue.status.name &&
current.source.description === issue.description &&
(current.source.labels ?? []).length === (issue.labels ?? []).length &&
current.source.assignee?.accountId === issue.assignee?.accountId
return alreadyHydrated ? current : createJiraTask(issue)
})
}
return
}
// Interpretation is deferred past the group on purpose: this Promise.all rejects as soon as
// one leg's transport does, and interpreting only after both settled is what makes the issue
// error win over the comments error. startRpcOperation would wait for the slower peer.
@@ -4,6 +4,7 @@ import {
githubAssignableUserListRead,
githubRepoLabelListRead
} from './mobile-task-item-detail-operations'
import { detailPayloadBody } from './mobile-tasks-provider-detail-types'
export function useMobileTasksItemDetailMetadataEffects(model: ListAndDetailEffectsModel) {
const {
@@ -24,9 +25,7 @@ export function useMobileTasksItemDetailMetadataEffects(model: ListAndDetailEffe
setItemBodyDraft('')
return
}
setItemBodyDraft(
detailPayload.provider === 'linear' ? detailPayload.description : detailPayload.body
)
setItemBodyDraft(detailPayloadBody(detailPayload))
}, [detailPayload])
useEffect(() => {
@@ -0,0 +1,146 @@
import { createElement } from 'react'
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { JiraIssue } from '../../../src/shared/jira-types'
import { mountFixture } from '../test-support/rpc-recording/recorder-fixture-shape'
import { FakeSession } from '../transport/mobile-endpoint-supervisor-test-fakes'
import type { RepoSummary } from './mobile-tasks-provider-detail-types'
import { useHostRepoList, type HostRepoListResource } from './use-host-repo-list'
import { useMobileTasksTaskListLoading } from './use-mobile-tasks-task-list-loading'
vi.mock('./mobile-tasks-dependencies', async () => {
const { useCallback, useEffect, useRef } = await import('react')
return { useCallback, useEffect, useRef }
})
vi.mock('./mobile-tasks-legacy-foundation', async () => import('./mobile-tasks-item-mapping'))
const issue: JiraIssue = {
id: '1',
key: 'ORCA-7',
title: 'Start from mobile Jira',
url: 'https://acme.atlassian.net/browse/ORCA-7',
siteId: 'site-a',
description: '',
labels: [],
project: { id: 'p', key: 'ORCA', name: 'Orca' },
issueType: { id: 'task', name: 'Task' },
status: { id: 'open', name: 'Open', categoryKey: 'new', categoryName: 'To Do' },
updatedAt: '2026-01-01T00:00:00Z',
createdAt: '2026-01-01T00:00:00Z'
}
const repo: RepoSummary = { id: 'repo-a', displayName: 'App', path: '/app', kind: 'folder' }
const mounted: ReactTestRenderer[] = []
afterEach(() => {
act(() => {
for (const renderer of mounted.splice(0)) {
renderer.unmount()
}
})
})
function mountJira(fetchRepos: () => Promise<RepoSummary[]>) {
const client = new FakeSession('connected')
client.sendRequest.mockResolvedValue({ id: 'jira-test', ok: true, result: [issue] })
const setItems = vi.fn()
const setError = vi.fn()
const serverRepoList: RepoSummary[] = []
const effectiveRepoIds = new Set<string>()
const holder: {
repos?: HostRepoListResource<RepoSummary>
load?: () => Promise<void>
} = {}
function Probe() {
const repos = useHostRepoList(client, fetchRepos)
holder.repos = repos
holder.load = useMobileTasksTaskListLoading(
mountFixture<Parameters<typeof useMobileTasksTaskListLoading>[0]>({
appliedQuery: '',
client,
clientRef: { current: client },
connState: 'connected',
githubMode: 'items',
gitlabFilter: 'opened',
gitlabView: 'project',
jiraConnection: { connected: true, selection: 'site-a' },
jiraFilter: 'assigned',
effectiveRepoIds,
serverRepoList,
linearConnected: false,
loadGenerationRef: { current: 0 },
provider: 'jira',
repoListEnsureLoaded: repos.ensureLoaded,
resetGitHubItemsState: vi.fn(),
setError,
setItems,
setLoading: vi.fn(),
setRefreshing: vi.fn(),
taskStateHydrated: true,
tasksSupported: true
})
).loadTasks
return null
}
act(() => {
mounted.push(create(createElement(Probe)))
})
return {
setItems,
setError,
get repos() {
if (!holder.repos) {
throw new Error('Repository resource did not render')
}
return holder.repos.state
},
async load() {
await act(async () => {
if (!holder.load) {
throw new Error('Jira load did not render')
}
await holder.load()
})
}
}
}
describe('direct mobile Jira entry', () => {
it('loads workspace repositories without delaying the Jira issue list', async () => {
let answer: (repos: RepoSummary[]) => void = () => {
throw new Error('Repository request is not ready')
}
const response = new Promise<RepoSummary[]>((resolve) => {
answer = resolve
})
const fetchRepos = vi.fn(() => response)
const fixture = mountJira(fetchRepos)
expect(fixture.repos.status).toBe('idle')
await fixture.load()
expect(fixture.setItems).toHaveBeenLastCalledWith([
expect.objectContaining({ provider: 'jira', source: issue })
])
expect(fetchRepos).toHaveBeenCalledTimes(1)
expect(fixture.repos.status).toBe('loading')
await act(async () => answer([repo]))
expect(fixture.repos).toMatchObject({ status: 'loaded', repos: [repo] })
})
it('keeps Jira readable when the background repository request fails', async () => {
const fetchRepos = vi.fn(async (): Promise<RepoSummary[]> => {
throw new Error('Repository host unavailable')
})
const fixture = mountJira(fetchRepos)
await fixture.load()
expect(fetchRepos).toHaveBeenCalledTimes(1)
expect(fixture.repos.status).toBe('error')
expect(fixture.setItems).toHaveBeenLastCalledWith([
expect.objectContaining({ provider: 'jira', source: issue })
])
expect(fixture.setError).toHaveBeenLastCalledWith('')
})
})
@@ -0,0 +1,118 @@
import { createElement } from 'react'
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { FakeSession } from '../transport/mobile-endpoint-supervisor-test-fakes'
import type { RpcClient } from '../transport/rpc-client'
import type { RpcResponse } from '../transport/types'
import {
type ProviderLoadActionsInput,
useMobileTasksProviderLoadActions
} from './use-mobile-tasks-provider-load-actions'
vi.mock('./mobile-tasks-dependencies', async () => {
const { useCallback } = await import('react')
return { useCallback }
})
vi.mock('./mobile-tasks-legacy-foundation', () => ({}))
const mounted: ReactTestRenderer[] = []
afterEach(() => {
act(() => {
for (const renderer of mounted.splice(0)) {
renderer.unmount()
}
})
})
function mountRefresh(client: RpcClient, clientRef: { current: RpcClient | null }) {
const setJiraConnection = vi.fn()
const jiraStatusRequestIdRef = { current: 0 }
const holder: { refresh?: () => Promise<void> } = {}
function Probe() {
const input: ProviderLoadActionsInput = {
appliedQuery: '',
client,
clientRef,
connState: 'connected',
defaultLinearTeamSelectionRef: { current: null },
githubKind: 'issues',
setJiraConnection,
jiraStatusRequestIdRef,
setLinearConnected: vi.fn(),
setLinearTeams: vi.fn(),
setLinearWorkspaces: vi.fn(),
setSelectedLinearTeamIds: vi.fn(),
setSelectedLinearWorkspaceId: vi.fn(),
taskUiReady: true,
tasksSupported: true
}
holder.refresh = useMobileTasksProviderLoadActions(input).refreshJiraConnection
return null
}
act(() => {
mounted.push(create(createElement(Probe)))
})
return {
setJiraConnection,
refresh: async () => {
if (!holder.refresh) {
throw new Error('Jira refresh did not render')
}
await holder.refresh()
}
}
}
function pendingStatus() {
let answer: (reply: RpcResponse) => void = () => {
throw new Error('Status promise is not ready')
}
const response = new Promise<RpcResponse>((resolve) => {
answer = resolve
})
return { response, answer }
}
describe('mobile Jira status host ownership', () => {
it('applies an explicit status refresh for the current host', async () => {
const client = new FakeSession('connected')
client.sendRequest.mockResolvedValue({
id: 'jira-test',
ok: true,
result: { connected: false }
})
const fixture = mountRefresh(client, { current: client })
await fixture.refresh()
expect(client.sendRequest).toHaveBeenCalledWith('jira.status')
expect(fixture.setJiraConnection).toHaveBeenCalledTimes(1)
})
it('ignores an earlier explicit refresh that settles after a newer one', async () => {
const client = new FakeSession('connected')
const earlier = pendingStatus()
const newer = pendingStatus()
client.sendRequest.mockReturnValueOnce(earlier.response).mockReturnValueOnce(newer.response)
const fixture = mountRefresh(client, { current: client })
const first = fixture.refresh()
const second = fixture.refresh()
newer.answer({ id: 'newer', ok: true, result: { connected: true } })
await second
earlier.answer({ id: 'earlier', ok: true, result: { connected: false } })
await first
expect(fixture.setJiraConnection).toHaveBeenCalledTimes(1)
})
it('ignores a previous host status that arrives after switching hosts', async () => {
const client = new FakeSession('connected')
const nextClient = new FakeSession('connected')
const status = pendingStatus()
client.sendRequest.mockReturnValue(status.response)
const clientRef = { current: client }
const fixture = mountRefresh(client, clientRef)
const refresh = fixture.refresh()
clientRef.current = nextClient
status.answer({ id: 'jira-test', ok: true, result: { connected: false } })
await refresh
expect(fixture.setJiraConnection).not.toHaveBeenCalled()
})
})
@@ -28,6 +28,8 @@ export function useMobileTasksListAndDetailEffects(model: ProjectLoadingActionsM
hostedRepos,
linearConnected,
linearFilter,
jiraFilter,
refreshJiraConnection,
linearMetadataItem,
loadGitHubProjectTable,
loadGitHubProjects,
@@ -119,6 +121,32 @@ export function useMobileTasksListAndDetailEffects(model: ProjectLoadingActionsM
})
}, [appliedQuery, linearFilter, persistTaskResumeState, provider, taskUiReady])
useEffect(() => {
if (!taskUiReady || provider !== 'jira') {
return
}
persistTaskResumeState({
jiraPreset: jiraFilter,
jiraQuery: appliedQuery.trim()
})
}, [appliedQuery, jiraFilter, persistTaskResumeState, provider, taskUiReady])
// Pick up desktop connection changes when entering the Jira tab.
useEffect(() => {
if (!taskStateHydrated || provider !== 'jira') {
return
}
let stale = false
void refreshJiraConnection().catch((err) => {
if (!stale) {
setError(err instanceof Error ? err.message : 'Failed to read Jira status')
}
})
return () => {
stale = true
}
}, [provider, refreshJiraConnection, taskStateHydrated])
useEffect(() => {
if (connState !== 'connected' || !taskStateHydrated) {
return
@@ -16,6 +16,8 @@ import {
taskRepositoryMeta
} from './mobile-tasks-legacy-foundation'
import { styles } from './mobile-tasks-legacy-styles'
import { PROVIDER_LABELS } from './mobile-task-view-options'
import { jiraSiteLabel } from './jira-mobile-connection'
export function useMobileTasksPickerProjection(model: DetailCommentRenderersModel) {
const {
@@ -30,6 +32,7 @@ export function useMobileTasksPickerProjection(model: DetailCommentRenderersMode
githubRepoSources,
hostedRepos,
items,
jiraConnection,
linearTeams,
provider,
reposById,
@@ -71,10 +74,20 @@ export function useMobileTasksPickerProjection(model: DetailCommentRenderersMode
provider === 'github' || provider === 'gitlab'
? ((selectedCreateTarget as RepoSummary | null)?.displayName ?? 'Select target')
: ((selectedCreateTarget as LinearTeam | null)?.name ?? 'Select target')
const providerLabel =
provider === 'github' ? 'GitHub' : provider === 'gitlab' ? 'GitLab' : 'Linear'
const providerLabel = PROVIDER_LABELS[provider]
const showHeaderCreateTask =
provider === 'linear' || (provider === 'github' && githubMode === 'items')
const jiraSiteOptions = useMemo<PickerOption<string>[]>(
() => [
{ value: 'all', label: 'All sites' },
...jiraConnection.sites.map((site) => ({
value: site.id,
label: jiraSiteLabel(site),
subtitle: site.siteUrl
}))
],
[jiraConnection.sites]
)
const providerOptions = useMemo(
() => PROVIDER_OPTIONS.filter((option) => visibleProviders.includes(option.value)),
[visibleProviders]
@@ -213,6 +226,7 @@ export function useMobileTasksPickerProjection(model: DetailCommentRenderersMode
createTargetOptions,
selectedCreateTarget,
selectedCreateTargetLabel,
jiraSiteOptions,
providerLabel,
showHeaderCreateTask,
providerOptions,
@@ -1,4 +1,5 @@
import type { RuntimeHydrationModel } from './use-mobile-tasks-runtime-hydration'
import { readJiraConnection, reconcileJiraConnection } from './jira-mobile-connection'
import type { RpcSendParams } from '../transport/rpc-params-contract'
import {
CROSS_REPO_DISPLAY_LIMIT,
@@ -32,13 +33,37 @@ import {
import { githubWorkItemSearchRead } from './mobile-task-source-search-operations'
import { taskSettingsWrite } from './mobile-task-runtime-operations'
export function useMobileTasksProviderLoadActions(model: RuntimeHydrationModel) {
export type ProviderLoadActionsInput = Pick<
RuntimeHydrationModel,
| 'appliedQuery'
| 'client'
| 'clientRef'
| 'connState'
| 'defaultLinearTeamSelectionRef'
| 'githubKind'
| 'setJiraConnection'
| 'jiraStatusRequestIdRef'
| 'setLinearConnected'
| 'setLinearTeams'
| 'setLinearWorkspaces'
| 'setSelectedLinearTeamIds'
| 'setSelectedLinearWorkspaceId'
| 'taskUiReady'
| 'tasksSupported'
>
export function useMobileTasksProviderLoadActions<Model extends ProviderLoadActionsInput>(
model: Model
) {
const {
appliedQuery,
client,
clientRef,
connState,
defaultLinearTeamSelectionRef,
githubKind,
setJiraConnection,
jiraStatusRequestIdRef,
setLinearConnected,
setLinearTeams,
setLinearWorkspaces,
@@ -47,6 +72,18 @@ export function useMobileTasksProviderLoadActions(model: RuntimeHydrationModel)
taskUiReady,
tasksSupported
} = model
// Desktop connection changes need an explicit status refresh on mobile.
const refreshJiraConnection = useCallback(async (): Promise<void> => {
if (!client || connState !== 'connected' || !tasksSupported) {
return
}
const requestId = ++jiraStatusRequestIdRef.current
const next = await readJiraConnection(client)
if (clientRef.current === client && jiraStatusRequestIdRef.current === requestId) {
setJiraConnection((current) => reconcileJiraConnection(current, next))
}
}, [client, clientRef, connState, tasksSupported, setJiraConnection, jiraStatusRequestIdRef])
const loadLinearContext = useCallback(async (): Promise<void> => {
if (!client || connState !== 'connected' || !tasksSupported) {
return
@@ -220,6 +257,7 @@ export function useMobileTasksProviderLoadActions(model: RuntimeHydrationModel)
[appliedQuery, githubKind]
)
return Object.assign(model, {
refreshJiraConnection,
loadLinearContext,
persistLinearTeamSelection,
fetchGitHubItemsPage,
@@ -227,4 +265,6 @@ export function useMobileTasksProviderLoadActions(model: RuntimeHydrationModel)
})
}
export type ProviderLoadActionsModel = ReturnType<typeof useMobileTasksProviderLoadActions>
export type ProviderLoadActionsModel = ReturnType<
typeof useMobileTasksProviderLoadActions<RuntimeHydrationModel>
>
@@ -115,6 +115,7 @@ const DEFAULT_INPUT: ProbeInput = {
/** The hook mutates the model it is handed, so each render gets a fresh envelope around
* the same input identities — exactly how the picker projection feeds it in the app. */
function createModel(input: ProbeInput): PickerProjectionModel {
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: This fixture supplies every field the projection reads; unrelated screen actions are never called.
return {
activeGitHubProject: null,
defaultGitHubPreset: 'open',
@@ -128,6 +129,14 @@ function createModel(input: ProbeInput): PickerProjectionModel {
githubProjects: [],
gitlabFilter: 'opened',
hostedRepos: [],
jiraConnection: {
connected: false,
sites: [],
selection: null,
credentialError: null,
unavailableReason: null
},
jiraFilter: 'assigned',
items: input.items,
linearDisplayProperties: new Set(['state', 'priority']),
linearFilter: 'all',
@@ -1,4 +1,6 @@
import type { PickerProjectionModel } from './use-mobile-tasks-picker-projection'
import { JIRA_FILTER_LABELS } from './mobile-jira-issue-filters'
import { jiraSiteLabel } from './jira-mobile-connection'
import {
type PickerOption,
githubProjectKey,
@@ -36,6 +38,8 @@ export function useMobileTasksProviderViewProjection(model: PickerProjectionMode
items,
linearDisplayProperties,
linearFilter,
jiraFilter,
jiraConnection,
linearGroupBy,
linearOrderBy,
linearTeamPropertyTouched,
@@ -63,6 +67,12 @@ export function useMobileTasksProviderViewProjection(model: PickerProjectionMode
githubPresetOptions.find((preset) => preset.value === githubPreset)?.label ?? 'Open'
const gitlabFilterLabel =
GITLAB_FILTER_OPTIONS.find((filter) => filter.value === gitlabFilter)?.label ?? 'Open'
const jiraFilterLabel = JIRA_FILTER_LABELS[jiraFilter]
const jiraSelectedSite =
jiraConnection.selection && jiraConnection.selection !== 'all'
? (jiraConnection.sites.find((site) => site.id === jiraConnection.selection) ?? null)
: null
const jiraSiteChipLabel = jiraSelectedSite ? jiraSiteLabel(jiraSelectedSite) : 'All sites'
const linearFilterLabel =
LINEAR_FILTER_OPTIONS.find((filter) => filter.value === linearFilter)?.label ?? 'All'
const linearViewLabel =
@@ -250,6 +260,8 @@ export function useMobileTasksProviderViewProjection(model: PickerProjectionMode
githubPresetPickerOptions,
githubPresetLabel,
gitlabFilterLabel,
jiraFilterLabel,
jiraSiteChipLabel,
linearFilterLabel,
linearViewLabel,
linearGroupLabel,
@@ -48,6 +48,8 @@ import {
getTaskPresetQuery
} from './mobile-tasks-legacy-foundation'
import { useMobileTasksItemState } from './use-mobile-tasks-item-state'
import { useMobileJiraConnectionState } from './use-mobile-jira-connection-state'
import type { JiraIssueFilter } from '../../../src/shared/jira-types'
export function useMobileTasksRouteAndItemState() {
const { hostId, taskSource } = useLocalSearchParams<{ hostId: string; taskSource?: string }>()
@@ -85,6 +87,11 @@ export function useMobileTasksRouteAndItemState() {
normalizeVisibleTaskProviders(undefined)
)
const [linearConnected, setLinearConnected] = useState(false)
const [jiraConnection, setJiraConnection] = useMobileJiraConnectionState(client, connState)
const jiraStatusRequestIdRef = useRef(0)
const [jiraFilter, setJiraFilter] = useState<JiraIssueFilter>('assigned')
const [showJiraFilterPicker, setShowJiraFilterPicker] = useState(false)
const [showJiraSitePicker, setShowJiraSitePicker] = useState(false)
const [githubMode, setGithubMode] = useState<'items' | 'project'>('items')
const [githubKind, setGithubKind] = useState<GitHubTaskKind>('issues')
const [githubPreset, setGithubPreset] = useState<GitHubPreset>('issues')
@@ -210,6 +217,15 @@ export function useMobileTasksRouteAndItemState() {
setVisibleProviders,
linearConnected,
setLinearConnected,
jiraConnection,
setJiraConnection,
jiraStatusRequestIdRef,
jiraFilter,
setJiraFilter,
showJiraFilterPicker,
setShowJiraFilterPicker,
showJiraSitePicker,
setShowJiraSitePicker,
githubMode,
setGithubMode,
githubKind,
@@ -1,5 +1,8 @@
import { settingsRead } from '../transport/settings-read-operations'
import type { ClientSettingsActionsModel } from './use-mobile-tasks-client-settings-actions'
import { jiraConnectionStatusProbe } from './mobile-jira-operations'
import { readJiraConnectionProbeReply } from './jira-mobile-connection'
import { resolveHydratedTaskViewState } from './mobile-tasks-hydrated-view-state'
import {
taskLinearStatusRead,
taskPreflightRead,
@@ -17,13 +20,7 @@ import {
import {
EMPTY_GITHUB_PROJECT_SETTINGS,
type RuntimeTaskSettings,
type TaskResumeState,
getTaskPresetQuery,
githubKindFromQuery,
isTaskProvider,
normalizeGitHubPreset,
normalizeLinearFilter,
scopeGitHubTaskSearch
type TaskResumeState
} from './mobile-tasks-legacy-foundation'
export function useMobileTasksRuntimeHydration(model: ClientSettingsActionsModel) {
@@ -48,6 +45,9 @@ export function useMobileTasksRuntimeHydration(model: ClientSettingsActionsModel
setGithubProjectTable,
setItems,
setLinearConnected,
setJiraConnection,
jiraStatusRequestIdRef,
setJiraFilter,
setLinearFilter,
setLinearStatusPickerItem,
setLinearTeams,
@@ -243,6 +243,16 @@ export function useMobileTasksRuntimeHydration(model: ClientSettingsActionsModel
}
setTasksSupportState({ kind: 'supported', client })
setError('')
// Jira is advisory and must not delay the other providers.
const jiraRequestId = ++jiraStatusRequestIdRef.current
void jiraConnectionStatusProbe
.request(client)
.catch(() => null)
.then((reply) => {
if (!stale && jiraStatusRequestIdRef.current === jiraRequestId) {
setJiraConnection(readJiraConnectionProbeReply(reply))
}
})
// Why raw requests in the group and not startRpcOperation: main's Promise.all rejects as soon
// as one leg rejects, and interpreting at an all-settled barrier would instead wait for the
// slowest peer and let a later policy surface a different error.
@@ -298,44 +308,27 @@ export function useMobileTasksRuntimeHydration(model: ClientSettingsActionsModel
setSelectedLinearTeamIds(new Set())
setSelectedLinearWorkspaceId(null)
}
const nextProvider =
requestedTaskSource && nextVisibleProviders.includes(requestedTaskSource)
? requestedTaskSource
: resolveVisibleTaskProvider(
isTaskProvider(settings.defaultTaskSource) ? settings.defaultTaskSource : undefined,
nextVisibleProviders
)
const preset =
resume.githubItemsPreset === null
? normalizeGitHubPreset(settings.defaultTaskViewPreset)
: normalizeGitHubPreset(resume.githubItemsPreset ?? settings.defaultTaskViewPreset)
const defaultPreset = normalizeGitHubPreset(settings.defaultTaskViewPreset)
const githubQuery =
resume.githubItemsPreset === null
? (resume.githubItemsQuery ?? '')
: getTaskPresetQuery(preset)
const nextLinearFilter = normalizeLinearFilter(resume.linearPreset)
const nextLinearQuery = resume.linearQuery ?? ''
// The user's saved choice, kept as is: readers derive what of it is listed now.
setSelectedRepoIds(new Set(settings.defaultRepoSelection ?? []))
defaultLinearTeamSelectionRef.current = settings.defaultLinearTeamSelection ?? null
const nextQuery =
nextProvider === 'github' ? githubQuery : nextProvider === 'linear' ? nextLinearQuery : ''
const nextAppliedQuery =
nextProvider === 'github'
? scopeGitHubTaskSearch(githubQuery, githubKindFromQuery(githubQuery, preset))
: nextQuery
const view = resolveHydratedTaskViewState({
settings,
resume,
requestedTaskSource,
visibleProviders: nextVisibleProviders
})
setVisibleProviders(nextVisibleProviders)
setProvider(nextProvider)
setProvider(view.provider)
setGithubMode(resume.githubMode === 'project' ? 'project' : 'items')
setDefaultGitHubPreset(defaultPreset)
setGithubPreset(preset)
setGithubKind(githubKindFromQuery(githubQuery, preset))
setLinearFilter(nextLinearFilter)
setDefaultGitHubPreset(view.defaultGithubPreset)
setGithubPreset(view.githubPreset)
setGithubKind(view.githubKind)
setLinearFilter(view.linearFilter)
setJiraFilter(view.jiraFilter)
setGithubProjectSettings(settings.githubProjects ?? EMPTY_GITHUB_PROJECT_SETTINGS)
setQuery(nextQuery)
setAppliedQuery(nextAppliedQuery)
setQuery(view.query)
setAppliedQuery(view.appliedQuery)
setTaskStateHydrated(true)
}

Some files were not shown because too many files have changed in this diff Show More