Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
da60517
feat(linear): import Linear issues into threads
maslinedwin Jul 5, 2026
b3ad31a
feat(linear): deep-integration foundation — list/filter/mutation API …
maslinedwin Jul 5, 2026
342a1da
feat(linear): Phase 1 bulk browser — filters, pagination, multi-selec…
maslinedwin Jul 5, 2026
50e12eb
feat(linear): Phase 2 — persist thread↔issue link + sidebar badge
maslinedwin Jul 5, 2026
34931ef
feat(linear): Phase 3 — status write-back (LinearSyncReactor + settings)
maslinedwin Jul 5, 2026
1f04d05
fix(linear): address PR review (Macroscope + Bugbot)
maslinedwin Jul 5, 2026
e184aa7
fix(linear): treat partial import as success with a warning
maslinedwin Jul 5, 2026
13aba65
fix(linear): make stateMappingByTeam a whole-map replacement on patch
maslinedwin Jul 5, 2026
511410b
fix(linear): don't mask connectivity errors; retain failed rows on pa…
maslinedwin Jul 5, 2026
f948fce
fix(linear): distinguish outage from disconnected; stop mapping wipes…
maslinedwin Jul 5, 2026
a037ddb
docs(linear): document bulk browser + status write-back settings
maslinedwin Jul 5, 2026
766d42e
fix(linear): catchTags convention + non-auth probe errors + retry-sel…
maslinedwin Jul 5, 2026
4356002
fix(linear): preserve selection on blanket import failure
maslinedwin Jul 5, 2026
08fa28b
feat(linear): default imports to Claude Opus 4.8 + right-click 'mark …
maslinedwin Jul 5, 2026
2638ae9
feat(linear): bring the Linear integration to the mobile app
maslinedwin Jul 6, 2026
5ab69b5
chore(mobile): allow fork builds under a different Apple/Expo account
maslinedwin Jul 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions apps/mobile/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ function resolveAppVariant(value: string | undefined): AppVariant {

const variant = VARIANT_CONFIG[APP_VARIANT];

// Account-specific overrides so a fork can build/submit under its own Apple +
// Expo account (e.g. for a personal TestFlight) without editing this file.
// All default to the upstream T3 Tools values.
const iosBundleIdentifier = process.env.T3CODE_IOS_BUNDLE_ID ?? variant.iosBundleIdentifier;
const easProjectId = process.env.EAS_PROJECT_ID ?? "d763fcb8-d37c-41ea-a773-b54a0ab4a454";
const appleTeamId = process.env.APPLE_TEAM_ID ?? "ARK85ZXQ4Z";
const expoOwner = process.env.EXPO_OWNER ?? "pingdotgg";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Empty env bypasses defaults

Medium Severity

The new fork overrides use ?? on process.env, so an empty string (e.g. a placeholder in .env / .env.local merged via loadRepoEnv) is treated as set and replaces the documented upstream defaults. That can yield an empty ios.bundleIdentifier, a broken updates.url, or invalid EAS owner/project values instead of falling back like “unset”.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5ab69b5. Configure here.


const config: ExpoConfig = {
name: variant.appName,
slug: "t3-code",
Expand All @@ -73,18 +81,19 @@ const config: ExpoConfig = {
userInterfaceStyle: "automatic",
updates: {
enabled: true,
url: "https://u.expo.dev/d763fcb8-d37c-41ea-a773-b54a0ab4a454",
url: `https://u.expo.dev/${easProjectId}`,
checkAutomatically: "ON_LOAD",
fallbackToCacheTimeout: 0,
},
ios: {
icon: variant.iosIcon,
supportsTablet: true,
bundleIdentifier: variant.iosBundleIdentifier,
bundleIdentifier: iosBundleIdentifier,
// Pin code signing to the T3 Tools team so non-interactive `expo run:ios`
// does not fall back to a personal team (which cannot sign app groups,
// Sign in with Apple, or push notification entitlements).
appleTeamId: "ARK85ZXQ4Z",
// Sign in with Apple, or push notification entitlements). Override with
// APPLE_TEAM_ID for a fork build under a different Apple account.
appleTeamId,
associatedDomains: [
`applinks:${variant.relyingParty}`,
`webcredentials:${variant.relyingParty}`,
Expand Down Expand Up @@ -154,8 +163,8 @@ const config: ExpoConfig = {
[
"expo-widgets",
{
bundleIdentifier: `${variant.iosBundleIdentifier}.widgets`,
groupIdentifier: `group.${variant.iosBundleIdentifier}`,
bundleIdentifier: `${iosBundleIdentifier}.widgets`,
groupIdentifier: `group.${iosBundleIdentifier}`,
enablePushNotifications: true,
widgets: [
{
Expand Down Expand Up @@ -185,10 +194,10 @@ const config: ExpoConfig = {
tracesToken: repoEnv.EXPO_PUBLIC_OTLP_TRACES_TOKEN ?? null,
},
eas: {
projectId: "d763fcb8-d37c-41ea-a773-b54a0ab4a454",
projectId: easProjectId,
},
},
owner: "pingdotgg",
owner: expoOwner,
};

export default config;
19 changes: 19 additions & 0 deletions apps/mobile/src/Stack.tsx

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium

const WORKSPACE_OVERLAY_ROUTES = new Set([

The new LinearImport route is presented as a formSheet overlay but is missing from WORKSPACE_OVERLAY_ROUTES. While the import sheet is open, workspacePathFromState returns /linear-import instead of the underlying workspace path, so AdaptiveWorkspaceLayout drops the active thread selection and recomputes pane behavior as if the user navigated away. Add "LinearImport" to the WORKSPACE_OVERLAY_ROUTES set alongside the other sheet routes.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/mobile/src/Stack.tsx around line 231:

The new `LinearImport` route is presented as a `formSheet` overlay but is missing from `WORKSPACE_OVERLAY_ROUTES`. While the import sheet is open, `workspacePathFromState` returns `/linear-import` instead of the underlying workspace path, so `AdaptiveWorkspaceLayout` drops the active thread selection and recomputes pane behavior as if the user navigated away. Add `"LinearImport"` to the `WORKSPACE_OVERLAY_ROUTES` set alongside the other sheet routes.

Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ import { AddProjectSourceRoute } from "./features/projects/AddProjectSourceRoute
import { NewTaskDraftRouteScreen } from "./features/threads/NewTaskDraftRouteScreen";
import { NewTaskFlowProvider } from "./features/threads/new-task-flow-provider";
import { NewTaskRouteScreen } from "./features/threads/NewTaskRouteScreen";
import { LinearImportRouteScreen } from "./features/linear/LinearImportRouteScreen";
import { SettingsAppearanceRouteScreen } from "./features/settings/SettingsAppearanceRouteScreen";
import { SettingsLinearRouteScreen } from "./features/settings/SettingsLinearRouteScreen";
import { SettingsAuthRouteScreen } from "./features/settings/SettingsAuthRouteScreen";
import { SettingsEnvironmentsRouteScreen } from "./features/settings/SettingsEnvironmentsRouteScreen";
import { SettingsRouteScreen } from "./features/settings/SettingsRouteScreen";
Expand Down Expand Up @@ -145,6 +147,13 @@ const SettingsSheetStack = createNativeStackNavigator({
title: "Appearance",
},
}),
SettingsLinear: createNativeStackScreen({
screen: SettingsLinearRouteScreen,
linking: "linear",
options: {
title: "Linear",
},
}),
SettingsAuth: createNativeStackScreen({
screen: SettingsAuthRouteScreen,
linking: "auth",
Expand Down Expand Up @@ -431,6 +440,16 @@ export const RootStack = createNativeStackNavigator({
sheetGrabberVisible: true,
},
}),
LinearImport: createNativeStackScreen({
screen: LinearImportRouteScreen,
linking: "linear-import",
options: {
title: "Import from Linear",
presentation: "formSheet",
sheetAllowedDetents: [0.7, 0.92],
sheetGrabberVisible: true,
},
}),
NewTaskSheet: createNativeStackScreen({
screen: NewTaskSheetStack,
linking: "new",
Expand Down
14 changes: 14 additions & 0 deletions apps/mobile/src/components/LinearIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Svg, { Path } from "react-native-svg";

/** Linear brand mark. Defaults to the current-color tint when no color given. */
export function LinearIcon(props: { readonly size?: number; readonly color?: string }) {
const size = props.size ?? 18;
return (
<Svg width={size} height={size} viewBox="0 0 100 100" fill="none">
<Path
fill={props.color ?? "#5E6AD2"}
d="M1.225 61.523c-.222-.949.908-1.546 1.597-.857l36.512 36.512c.69.69.092 1.82-.857 1.597C20.15 94.612 5.388 79.85 1.225 61.523ZM.002 46.889a.99.99 0 0 0 .29.76l52.062 52.061a.99.99 0 0 0 .76.29c2.765-.169 5.47-.559 8.1-1.152.75-.169 1.014-1.084.47-1.63L2.784 38.32c-.545-.545-1.46-.28-1.63.47A50.229 50.229 0 0 0 .002 46.89ZM4.472 29.34a.99.99 0 0 0 .208 1.104l64.878 64.877a.99.99 0 0 0 1.104.208 50.293 50.293 0 0 0 5.807-3.132.99.99 0 0 0 .154-1.53L9.135 23.38a.99.99 0 0 0-1.53.154 50.29 50.29 0 0 0-3.132 5.807ZM12.293 18.229c-.309-.309-.334-.803-.045-1.13C21.376 6.626 34.637 0 49.45 0 76.869 0 99.1 22.231 99.1 49.65c0 14.813-6.626 28.074-17.099 37.202-.327.29-.821.264-1.13-.045L12.293 18.23Z"
/>
</Svg>
);
}
Loading
Loading