Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
142 commits
Select commit Hold shift + click to select a range
ae2b523
fix(mobile): bump react-native-shiki-engine to 0.3.12 (#3120)
skiniks Jun 18, 2026
b7b00bf
fix: Adapt destructive menu icon to dark mode (#3126)
StiensWout Jun 18, 2026
3bdaa6e
Polish marketing homepage: nav, hero, and endorsements (#3137)
t3dotgg Jun 18, 2026
e95b57d
[codex] Rewrite client connection architecture (#2978)
juliusmarminge Jun 18, 2026
1fcc57a
fix(web): Remove saved environments atomically (#2917)
mwolson Jun 19, 2026
30034ec
Add archived threads and mobile file viewer (#3155)
juliusmarminge Jun 19, 2026
52a24c8
Add origin-based worktree bootstrap option (#3157)
juliusmarminge Jun 19, 2026
0651af9
fix(server): use bound host for MCP endpoint (#3114)
D3OXY Jun 19, 2026
804d44c
fix(ssh): fix support for remotes that use fnm (#2641)
soorria Jun 19, 2026
20f37f3
Avoid repeated theme DOM sync during startup (#2779)
mjc Jun 19, 2026
3e01c4b
Migrate desktop auth to Clerk bridge (#3092)
juliusmarminge Jun 19, 2026
5d4e2fa
feat: allow disabling provider update checks (#3130)
repparw Jun 19, 2026
13917df
Use idiomatic Effect options for server secret reads (#3110)
cursor[bot] Jun 19, 2026
7dc1823
[codex] fix: show nightly badge from primary web server version (#3103)
StiensWout Jun 19, 2026
494350c
feat(composer): clickable PR pill next to branch selector (#3065)
TheIcarusWings Jun 19, 2026
a4446e2
Improve idiomatic Effect usage in config and Tailscale paths (#3073)
cursor[bot] Jun 19, 2026
d9f59be
feat(sidebar): worktree indicator on session rows (#3057)
TheIcarusWings Jun 19, 2026
c08b968
Use Effect schema decoders for JSON parsing (#3060)
cursor[bot] Jun 19, 2026
fbf6263
Only show enabled providers in picker sidebar (#3168)
juliusmarminge Jun 19, 2026
cdaba7f
Share thread state idle TTL across client atoms (#3163)
juliusmarminge Jun 19, 2026
e29ad76
Unify mobile typography tokens across the app (#3162)
juliusmarminge Jun 19, 2026
753bc46
Harden preview ownership and option-based secret handling (#3172)
juliusmarminge Jun 20, 2026
ab0baaa
[codex] Inline contracts request-context service shapes (#3204)
juliusmarminge Jun 20, 2026
60b16d1
[codex] Refactor desktop app Effect services (#3185)
juliusmarminge Jun 20, 2026
53d0107
[codex] align relay foundation Effect services (#3182)
juliusmarminge Jun 20, 2026
1cf3647
[codex] Normalize server core Effect service modules (#3187)
juliusmarminge Jun 20, 2026
d2c0a6a
Add diff scope switching and provider update settings (#3169)
juliusmarminge Jun 20, 2026
3b56dc1
[codex] Refactor primary HTTP Effect service (#3205)
juliusmarminge Jun 20, 2026
938b19a
[codex] Normalize Desktop IPC Effect service (#3203)
juliusmarminge Jun 20, 2026
9544e72
chore: run eas only when labelled (#3208)
Yash-Singh1 Jun 20, 2026
b630278
[codex] Refactor review and text generation services (#3196)
juliusmarminge Jun 20, 2026
8bfdacf
[codex] Use namespace imports for desktop core services (#3207)
juliusmarminge Jun 20, 2026
5c1ac92
[codex] normalize server process and preview Effect services (#3191)
juliusmarminge Jun 20, 2026
742dd7a
[codex] Standardize Effect protocol adapter services (#3201)
juliusmarminge Jun 20, 2026
d5a72d5
[codex] align relay agent activity Effect services (#3179)
juliusmarminge Jun 20, 2026
df1540f
[codex] align persistence Effect service modules (#3184)
juliusmarminge Jun 20, 2026
93fd972
[codex] Refactor desktop window and update Effect services (#3202)
juliusmarminge Jun 20, 2026
01492eb
[codex] Refactor agent awareness relay service (#3197)
juliusmarminge Jun 20, 2026
4ee719a
[codex] refactor server cloud Effect services (#3183)
juliusmarminge Jun 20, 2026
01cd564
[codex] Align desktop preview Effect services (#3199)
juliusmarminge Jun 20, 2026
ffae12f
[codex] refactor desktop backend Effect services (#3192)
juliusmarminge Jun 20, 2026
ccf8331
[codex] Align diagnostics and telemetry Effect services (#3189)
juliusmarminge Jun 20, 2026
b17b6d5
[codex] Complete relay agent activity Effect cleanup (#3210)
juliusmarminge Jun 20, 2026
4e8ee13
[codex] Align terminal Effect service modules (#3193)
juliusmarminge Jun 20, 2026
7118e43
[codex] Refactor checkpointing Effect services (#3181)
juliusmarminge Jun 20, 2026
d9e9539
[codex] Migrate server source control Effect services (#3186)
juliusmarminge Jun 20, 2026
6b74476
Add Effect service conventions check (#3212)
juliusmarminge Jun 20, 2026
2f8d3ba
[codex] finish server process and preview Effect cleanup (#3209)
juliusmarminge Jun 20, 2026
0ad1e9d
[codex] Refactor client-runtime Effect services (#3198)
juliusmarminge Jun 20, 2026
10a8d3f
Tighten structural Effect error checks (#3213)
juliusmarminge Jun 20, 2026
6d2dae0
Preserve full cause chains in Effect error checks (#3215)
juliusmarminge Jun 20, 2026
65385c0
[codex] Refactor desktop settings Effect services (#3188)
juliusmarminge Jun 20, 2026
2fbbe68
Clarify Effect error discriminator modeling (#3217)
juliusmarminge Jun 20, 2026
335e0b5
Fix PR creation from origin-based worktrees (#3218)
juliusmarminge Jun 20, 2026
dc82f79
[codex] Close right panel when its last tab closes (#3221)
juliusmarminge Jun 20, 2026
8d61172
Cosmetic fix: Sync web title with nightly server branding (#3219)
StiensWout Jun 20, 2026
3900c45
Preserve observable Effect error semantics (#3220)
juliusmarminge Jun 20, 2026
79c5717
[codex] Make settings environment-scoped by default (#3216)
juliusmarminge Jun 20, 2026
b0a3a50
[codex] Refactor project and workspace Effect services (#3190)
juliusmarminge Jun 20, 2026
c00e721
[codex] Refactor client runtime Effect services (#3200)
juliusmarminge Jun 20, 2026
7eda6ba
[codex] Migrate desktop shell and SSH Effect services (#3194)
juliusmarminge Jun 20, 2026
9a1c487
[codex] Remove redundant Effect type annotations (#3229)
juliusmarminge Jun 20, 2026
f58b6da
[codex] Preserve workspace RPC error messages (#3222)
juliusmarminge Jun 20, 2026
30acfa9
[fix/feat:ui] Fix clipped chatbar provider badge (#3224)
sandersonstabo Jun 20, 2026
6c5fb79
[fix/feat:ui] Use shared button for model favorites (#3223)
sandersonstabo Jun 20, 2026
db27502
[fix/feat:ui] Capitalize Work Log heading (#3228)
sandersonstabo Jun 20, 2026
2fa37ec
[codex] Enforce canonical Node namespace imports (#3238)
juliusmarminge Jun 20, 2026
b19fc1b
[codex] refactor desktop Electron Effect services (#3178)
juliusmarminge Jun 20, 2026
97e5cd3
[codex] align server auth Effect services (#3180)
juliusmarminge Jun 20, 2026
86db35c
[codex] Structure mobile native static-check failures (#3302)
juliusmarminge Jun 20, 2026
20734d4
[codex] Structure macOS passkey signing failures (#3303)
juliusmarminge Jun 20, 2026
515303e
[codex] Preserve desktop user-data probe failures (#3304)
juliusmarminge Jun 20, 2026
b4fe8fa
[codex] Structure relay activity-row persistence errors (#3305)
juliusmarminge Jun 20, 2026
8c3755a
[codex] Structure bootstrap errors (#3256)
juliusmarminge Jun 20, 2026
e55dd00
[codex] Structure preview session key errors (#3388)
juliusmarminge Jun 20, 2026
d87ec96
[codex] Structure empty mobile pairing payload errors (#3372)
juliusmarminge Jun 20, 2026
0675252
[codex] Structure unavailable Bun PTY operations (#3394)
juliusmarminge Jun 20, 2026
7a8bab5
[codex] Keep PTY spawn errors structural (#3325)
juliusmarminge Jun 20, 2026
f7867ad
[codex] Structure mobile notification setting failures (#3391)
juliusmarminge Jun 20, 2026
bf1a650
[codex] Preserve review path resolution failures (#3357)
juliusmarminge Jun 20, 2026
7160814
[codex] Split preferred editor precondition errors (#3324)
juliusmarminge Jun 20, 2026
cc69aef
[codex] Structure relay domain label errors (#3347)
juliusmarminge Jun 20, 2026
40d1464
[codex] Structure catalog dependency resolution failures (#3298)
juliusmarminge Jun 20, 2026
08650a7
[codex] Structure web diff worker failures (#3356)
juliusmarminge Jun 20, 2026
8331511
[codex] structure Electron theme source errors (#3294)
juliusmarminge Jun 20, 2026
f3b43a1
[codex] Structure missing client cloud config errors (#3346)
juliusmarminge Jun 20, 2026
b9e22de
[codex] Preserve desktop update state read failures (#3370)
juliusmarminge Jun 20, 2026
8112aff
[codex] Structure relay install confirmation conflicts (#3365)
juliusmarminge Jun 20, 2026
350e229
[codex] Structure OAuth scope encoding failures (#3368)
juliusmarminge Jun 20, 2026
779c237
[codex] Structure native view resolution failures (#3353)
juliusmarminge Jun 20, 2026
4fbc4f9
[codex] Structure mobile project thread validation errors (#3387)
juliusmarminge Jun 20, 2026
ac77fe4
[codex] Preserve relay trace error causes (#3377)
juliusmarminge Jun 20, 2026
9d5ca2c
[codex] Structure Electron protocol teardown failures (#3310)
juliusmarminge Jun 20, 2026
30a084c
[codex] Preserve mobile composer draft failures (#3348)
juliusmarminge Jun 20, 2026
fccecd8
[codex] Preserve detached desktop action causes (#3371)
juliusmarminge Jun 20, 2026
ce0c20b
[codex] Structure desktop network interface failures (#3313)
juliusmarminge Jun 20, 2026
04f82ae
[codex] Structure relay environment link errors (#3334)
juliusmarminge Jun 20, 2026
61aade9
[codex] Preserve desktop asset probe failures (#3373)
juliusmarminge Jun 20, 2026
5a2c92e
[codex] Structure Electron app boundary failures (#3301)
juliusmarminge Jun 20, 2026
d84ebe8
[codex] Structure process resource sampling failures (#3415)
juliusmarminge Jun 20, 2026
53a477c
[codex] Structure desktop backend settings read errors (#3379)
juliusmarminge Jun 20, 2026
300d4d5
[codex] Structure missing provider command failures (#3384)
juliusmarminge Jun 20, 2026
716ae73
[codex] Structure relay publish signature errors (#3335)
juliusmarminge Jun 20, 2026
3ecf368
[codex] structure Bitbucket API failures (#3332)
juliusmarminge Jun 20, 2026
4d790f0
[codex] Bound relay registration replay diagnostics (#3420)
juliusmarminge Jun 20, 2026
4d3fcac
[codex] Type malformed Clerk public config failures (#3422)
juliusmarminge Jun 20, 2026
bfe6174
[codex] Simplify desktop client settings errors (#3265)
juliusmarminge Jun 20, 2026
f98448e
[codex] Structure relay JWT failures (#3270)
juliusmarminge Jun 20, 2026
ed6ba74
[codex] sanitize provider runtime failure diagnostics (#3414)
juliusmarminge Jun 20, 2026
1e5f628
[codex] Structure MCP snapshot failures (#3423)
juliusmarminge Jun 20, 2026
1486a4a
[codex] Structure Electron updater errors (#3280)
juliusmarminge Jun 20, 2026
c3e3e26
[codex] Structure primary environment request failures (#3409)
juliusmarminge Jun 20, 2026
5ca7676
[codex] Structure Claude adapter failures (#3406)
juliusmarminge Jun 20, 2026
49c2322
[codex] Structure mobile secure storage failures (#3345)
juliusmarminge Jun 20, 2026
2b8e012
[codex] Structure desktop server exposure errors (#3269)
juliusmarminge Jun 20, 2026
2910d9f
[codex] Structure preview config failures (#3271)
juliusmarminge Jun 20, 2026
48f88d5
[codex] Structure desktop Clerk bridge failures (#3308)
juliusmarminge Jun 20, 2026
9c98cd6
Remove persistence error constructor wrappers (#3398)
juliusmarminge Jun 20, 2026
ee3e2da
[codex] Structure remote pairing input errors (#3393)
juliusmarminge Jun 20, 2026
5b1b35c
[codex] Preserve desktop shell environment probe failures (#3383)
juliusmarminge Jun 20, 2026
833c8ab
[codex] Remove project setup error constructor wrappers (#3329)
juliusmarminge Jun 20, 2026
1dc36ce
Structure relay auth parsing errors (#3290)
juliusmarminge Jun 20, 2026
e01b190
[codex] Structure process diagnostics failures (#3389)
juliusmarminge Jun 20, 2026
7eb7b4f
[codex] Structure release metadata failures (#3296)
juliusmarminge Jun 20, 2026
708bc70
Structure server environment ID failures (#3286)
juliusmarminge Jun 20, 2026
2c16edd
[codex] Structure desktop bridge state errors (#3381)
juliusmarminge Jun 20, 2026
32c7f90
[codex] Structure APNs delivery queue errors (#3326)
juliusmarminge Jun 20, 2026
d512dea
[codex] Structure preview URL failures (#3275)
juliusmarminge Jun 20, 2026
d8bf307
[codex] Enrich process runner errors (#3268)
juliusmarminge Jun 20, 2026
b6e384f
[codex] Preserve trace IDs across error causes (#3426)
juliusmarminge Jun 20, 2026
13a4789
[codex] Structure terminal adapter startup defects (#3425)
juliusmarminge Jun 20, 2026
eacceb9
[codex] Bound shared schema diagnostics (#3424)
juliusmarminge Jun 20, 2026
d60f5c6
[codex] Structure mobile external link failures (#3363)
juliusmarminge Jun 20, 2026
f4ef356
[codex] structure desktop IPC registration errors (#3291)
juliusmarminge Jun 20, 2026
cdfecb3
[codex] Structure workspace file system errors (#3274)
juliusmarminge Jun 20, 2026
731b1a6
[codex] structure VCS process errors (#3408)
juliusmarminge Jun 20, 2026
0e71885
[codex] Structure primary environment target failures (#3413)
juliusmarminge Jun 20, 2026
d53237c
[codex] sanitize ACP native event diagnostics (#3417)
juliusmarminge Jun 20, 2026
0cd0ed1
[codex] Sanitize client error log diagnostics (#3405)
juliusmarminge Jun 20, 2026
46fdc76
[codex] Structure telemetry identity errors (#3306)
juliusmarminge Jun 20, 2026
1cbe8a7
[codex] Structure VCS project config failures (#3315)
juliusmarminge Jun 20, 2026
ff0f702
[codex] Structure client VCS action errors (#3263)
juliusmarminge Jun 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
# T3CODE_CLERK_JWT_TEMPLATE=t3-relay
# T3CODE_CLERK_CLI_OAUTH_CLIENT_ID=oauthapp_...

# Optional: signed macOS passkey builds. The RP domain defaults to the Frontend API
# hostname encoded in T3CODE_CLERK_PUBLISHABLE_KEY. Set the override only when Clerk
# returns a different RP ID or when multiple domains must be entitled.
# T3CODE_APPLE_TEAM_ID=ABC1234567
# T3CODE_MACOS_PROVISIONING_PROFILE=/absolute/path/to/t3code.provisionprofile
# T3CODE_CLERK_PASSKEY_RP_DOMAINS=example.clerk.accounts.dev,clerk.example.com

# Get this from your relay deployment. `infra/relay` deploys update it automatically.
# T3CODE_RELAY_URL=https://relay.example.com

Expand Down
47 changes: 1 addition & 46 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
run: |
test -f apps/desktop/dist-electron/preload.cjs
grep -nE "desktopBridge|getLocalEnvironmentBootstrap|PICK_FOLDER_CHANNEL|wsUrl" apps/desktop/dist-electron/preload.cjs
grep -n "__clerk_internal_electron_passkeys" apps/desktop/dist-electron/preload.cjs

test:
name: Test
Expand All @@ -60,52 +61,6 @@ jobs:
- name: Test
run: vp run test

test_browser:
name: Test Browser
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Setup Vite+
uses: voidzero-dev/setup-vp@v1
with:
node-version-file: package.json
cache: true
run-install: true

- name: Cache Playwright browsers
uses: actions/cache@v5
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-playwright-

- name: Install browser test runtime
run: vp run --filter @t3tools/web test:browser:install

- name: Browser test / Chat view
working-directory: apps/web
run: vp test run --mode browser --browser=chromium src/components/ChatView.browser.tsx

- name: Browser test / Chat markdown
working-directory: apps/web
run: vp test run --mode browser --browser=chromium src/components/ChatMarkdown.browser.tsx

- name: Browser test / Components
working-directory: apps/web
run: |
vp test run --mode browser --browser=chromium \
src/components/GitActionsControl.browser.tsx \
src/components/KeybindingsToast.browser.tsx \
src/components/ThreadTerminalDrawer.browser.tsx \
src/components/chat/MessagesTimeline.browser.tsx \
src/components/chat/ProviderModelPicker.browser.tsx \
src/components/chat/CompactComposerControlsMenu.browser.tsx \
src/components/settings/SettingsPanels.browser.tsx

mobile_native_static_analysis:
name: Mobile Native Static Analysis
runs-on: blacksmith-12vcpu-macos-26
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/mobile-eas-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ name: Mobile EAS Preview

on:
pull_request:
types: [opened, reopened, synchronize, labeled, unlabeled]

jobs:
preview:
name: EAS Preview
if: contains(github.event.pull_request.labels.*.name, '🚀 Mobile Continuous Deployment')
runs-on: blacksmith-8vcpu-ubuntu-2404
permissions:
contents: read
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,9 @@ jobs:
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_TEAM_ID: ${{ vars.APPLE_TEAM_ID }}
MACOS_PROVISIONING_PROFILE: ${{ secrets.MACOS_PROVISIONING_PROFILE }}
T3CODE_CLERK_PASSKEY_RP_DOMAINS: ${{ vars.CLERK_PASSKEY_RP_DOMAINS }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
Expand Down Expand Up @@ -452,9 +455,21 @@ jobs:

if [[ "${{ matrix.platform }}" == "mac" ]]; then
if has_all "$CSC_LINK" "$CSC_KEY_PASSWORD" "$APPLE_API_KEY" "$APPLE_API_KEY_ID" "$APPLE_API_ISSUER"; then
if ! has_all "$APPLE_TEAM_ID" "$MACOS_PROVISIONING_PROFILE"; then
echo "macOS signing is configured, but APPLE_TEAM_ID or MACOS_PROVISIONING_PROFILE is missing." >&2
exit 1
fi

key_path="$RUNNER_TEMP/AuthKey_${APPLE_API_KEY_ID}.p8"
printf '%s' "$APPLE_API_KEY" > "$key_path"
export APPLE_API_KEY="$key_path"

profile_path="$RUNNER_TEMP/t3code.provisionprofile"
printf '%s' "$MACOS_PROVISIONING_PROFILE" | base64 -D > "$profile_path"
security cms -D -i "$profile_path" >/dev/null
export T3CODE_APPLE_TEAM_ID="$APPLE_TEAM_ID"
export T3CODE_MACOS_PROVISIONING_PROFILE="$profile_path"

echo "macOS signing enabled."
args+=(--signed)
else
Expand Down
76 changes: 76 additions & 0 deletions .macroscope/check-run-agents/effect-service-conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
title: Effect Service Conventions
model: claude-opus-4-8
effort: high
input: full_diff
tools:
- browse_code
- git_tools
- github_api_read_only
- modify_pr
include:
- "apps/**/*.ts"
- "apps/**/*.tsx"
- "packages/**/*.ts"
- "packages/**/*.tsx"
- "infra/**/*.ts"
- "infra/**/*.tsx"
conclusion: failure
showToolCalls: true
---

# Effect service review

Review changed TypeScript and directly affected call sites for the conventions below. Apply them when a pull request creates, moves, refactors, or consumes an Effect service. Do not demand unrelated repository-wide cleanup. Treat these instructions as authoritative when older code differs.

## Imports and module namespaces

- Import Effect library modules from their subpaths as namespaces, for example `import * as Effect from "effect/Effect"` and `import * as Layer from "effect/Layer"`. Flag consolidated named imports from `"effect"` in touched Effect service code.
- At a service boundary, import the local service module as a namespace and use its public module shape: `WorkspacePaths.WorkspacePaths`, `WorkspacePaths.make`, and `WorkspacePaths.layer`. Flag aliases such as `import { layer as workspacePathsLayer }` that erase the module namespace.
- Namespace imports are not a blanket rule. Keep named imports for whole packages such as `@t3tools/contracts`, and for modules used only for a pure helper, error, schema, config value, or standalone type. Do not request `import type * as Contracts`.
- A package subpath that is itself a service module may use a namespace import when callers access its service/tag, `make`, or `layer` members.
- When a barrel exposes an entire service module, prefer `export * as TokenStore from "./tokenStore.ts"` so consumers can use `TokenStore.TokenStore` and `TokenStore.layer`. Do not individually rename `make` and `layer` exports to simulate a namespace.

## Service definition

- Use the canonical single-file order: imports, error/schema declarations, the `Context.Service` tag with its inline interface, `make`, then `layer`.
- Keep a service's schemas/errors, `Context.Service` tag, construction, and layer in one canonical module when they form one implementation.
- Define the service interface inline in the `Context.Service` declaration. Do not retain a standalone `FooShape` or `FooServiceShape` interface/type.
- Refer to the inferred service interface as `Foo["Service"]`, including in mechanically updated orchestration, MCP, tests, and integration harnesses.
- Export a real `make` when the module owns construction. Do not create `make = Effect.succeed(...)` solely to force `Layer.effect`.
- Export the canonical layer as `export const layer = Layer...`. `Layer.effect` is not required: use `Layer.succeed`, `Layer.scoped`, or another appropriate constructor when that matches the implementation.
- In a concrete implementation module already named for the implementation, use plain `make` and `layer` (for example `BunPtyAdapter.ts` and `NodePtyAdapter.ts`).
- Keep implementation-specific names when an abstract port module contains one of several possible implementations, for example `makeCloudflaredRelayClient` and `layerCloudflared` in `RelayClient.ts`.
- `infra/relay/src/db.ts` is an intentional exception: an inline `Layer.succeed(RelayDb, db)` is acceptable without generic `make`/`layer` exports.

## Errors and predicates

- Define service failures with `Schema.TaggedErrorClass` and structured attributes. Derive `message` from those attributes rather than storing an unstructured message as the only data.
- `Schema.Defect()` is not a substitute for modeling a generic error: its tag, fields, or both must identify the failure structurally, and its `message` must not merely stringify an opaque cause. A semantically precise error tag may preserve a real `cause` without inventing a redundant singleton field when no additional variable context exists; still retain any real path, resource, request, or entity context available at the wrapping site.
- Capture stable, serializable domain context such as the operation or stage, resource/path or entity identifier, and normalized category/status. Map failures where that context is known instead of wrapping an entire multi-step pipeline in one generic error. Do not add a `detail` field that merely copies `cause.message` and then use it to construct the wrapper message.
- When translating or wrapping a real failure, preserve the immediate underlying error itself as `cause` alongside the structural fields so the complete error chain and stack remain available. If every construction wraps a failure, `cause` should be required; make it optional only when the same error can legitimately originate without an underlying failure.
- Derive the wrapper's `message` exclusively from its stable structural attributes, never from `cause`, `cause.message`, or a stringified defect. Do not replace the immediate error with only `error.cause`, erase a structured upstream error into a string, or manufacture an `Error` merely to populate `cause`. Pure validation/domain errors created without an underlying failure do not need a cause.
- Do not encode the same distinction twice with both a specific error tag and a single-value `operation`, `reason`, `kind`, or `phase` literal. Choose one coherent model: use distinct error classes and omit the redundant discriminator when callers or messages treat the failures as genuinely different, or use one service-level error with a multi-value operation discriminator and a generic message derived from that operation when the failures share the same semantics.
- Treat an error message exposed through an HTTP/RPC response, persisted state, UI, or another caller-visible boundary as behavior. Preserve those messages during a structural refactor. Existing distinct caller-visible messages are evidence that the failures should normally remain distinct error tags without redundant singleton discriminators, rather than being collapsed into a generic operation error.
- Split semantically distinct failures into separate error classes when a `reason`, `kind`, `phase`, or similar discriminator is used to choose the user-facing message or drive caller control flow. A discriminator used only for internal diagnostics may remain a field.
- Use `Schema.Union` of error classes when a shared schema, predicate, or helper type is useful.
- Export direct schema predicates such as `export const isFoo = Schema.is(Foo)`. Flag a private `Schema.is` constant wrapped by a redundant function with the same signature.
- Do not introduce a large `switch` or lookup table in an error's `message` getter to model failures that deserve separate error classes.

## File layout and migrations

- When combining `domain/Services/Foo.ts` and `domain/Layers/Foo.ts`, hoist the result to `domain/Foo.ts`.
- Delete the old service/layer files. Do not leave compatibility re-export shims. Mechanically update every consumer, including orchestration, MCP, tests, and integration harnesses, to the canonical path.
- Do not flag genuinely separate implementation/adapter modules merely because they remain in an implementation-oriented directory.
- Avoid substantive orchestration or MCP redesign in service-cleanup PRs. Mechanical import, layer, and `Service["Service"]` updates are expected when required to remove obsolete paths or shapes.

## Change discipline

- Preserve useful comments, invariants, and specification documentation while moving code.
- Do not add large tests solely to prove a mechanical refactor. Update existing tests and imports as needed.
- If backend behavior changes, require focused tests. Use test implementations/layers for external services only; do not mock out core business logic.
- Do not require `Layer.effect`, universal namespace imports, generic `make`/`layer` names for abstract-port implementations, separate error classes for diagnostic-only fields, or new tests for import-only changes.

## Reporting

Report only concrete violations introduced or retained in the pull request's changed scope. Prefer precise inline comments on the smallest relevant line range and state the expected fix. A clear convention violation may fail the check. Do not fail for optional style preferences or unrelated legacy code. If there are no findings, report exactly `All clear`.
3 changes: 3 additions & 0 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
"smoke-test": "node scripts/smoke-test.mjs"
},
"dependencies": {
"@clerk/electron": "catalog:",
"@clerk/electron-passkeys": "catalog:",
"@effect/platform-node": "catalog:",
"@t3tools/client-runtime": "workspace:*",
"@t3tools/contracts": "workspace:*",
Expand All @@ -20,6 +22,7 @@
"@t3tools/tailscale": "workspace:*",
"effect": "catalog:",
"electron": "41.5.0",
"electron-store": "^8.2.0",
"electron-updater": "^6.6.2",
"playwright-core": "1.60.0",
"react-grab": "^0.1.32"
Expand Down
32 changes: 16 additions & 16 deletions apps/desktop/scripts/build-preview-annotation-css.mjs
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import { readFile, writeFile } from "node:fs/promises";
import { createRequire } from "node:module";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import * as NodeFSP from "node:fs/promises";
import * as NodeModule from "node:module";
import * as NodePath from "node:path";
import * as NodeURL from "node:url";

import { compile } from "tailwindcss";

const directory = dirname(fileURLToPath(import.meta.url));
const appRoot = join(directory, "..");
const sourcePath = join(appRoot, "src", "preview", "Annotation.css");
const preloadPath = join(appRoot, "src", "preview", "PickPreload.ts");
const outputPath = join(appRoot, "src", "preview", "AnnotationStyles.generated.ts");
const require = createRequire(import.meta.url);
const tailwindRoot = dirname(require.resolve("tailwindcss/package.json"));
const directory = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url));
const appRoot = NodePath.join(directory, "..");
const sourcePath = NodePath.join(appRoot, "src", "preview", "Annotation.css");
const preloadPath = NodePath.join(appRoot, "src", "preview", "PickPreload.ts");
const outputPath = NodePath.join(appRoot, "src", "preview", "AnnotationStyles.generated.ts");
const require = NodeModule.createRequire(import.meta.url);
const tailwindRoot = NodePath.dirname(require.resolve("tailwindcss/package.json"));

const [annotationSource, preloadSource, themeSource, preflightSource] = await Promise.all([
readFile(sourcePath, "utf8"),
readFile(preloadPath, "utf8"),
readFile(join(tailwindRoot, "theme.css"), "utf8"),
readFile(join(tailwindRoot, "preflight.css"), "utf8"),
NodeFSP.readFile(sourcePath, "utf8"),
NodeFSP.readFile(preloadPath, "utf8"),
NodeFSP.readFile(NodePath.join(tailwindRoot, "theme.css"), "utf8"),
NodeFSP.readFile(NodePath.join(tailwindRoot, "preflight.css"), "utf8"),
]);

const candidates = new Set(
Expand All @@ -37,4 +37,4 @@ const encodedCss = `'${css
.replaceAll("\n", "\\n")}'`;
const moduleSource = `// Generated by scripts/build-preview-annotation-css.mjs. Do not edit.\nexport const previewAnnotationStyles =\n ${encodedCss};\n`;

await writeFile(outputPath, moduleSource);
await NodeFSP.writeFile(outputPath, moduleSource);
22 changes: 13 additions & 9 deletions apps/desktop/scripts/dev-electron.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { spawn, spawnSync } from "node:child_process";
import { watch } from "node:fs";
import * as NodeChildProcess from "node:child_process";
import * as NodeFS from "node:fs";
import * as NodeOS from "node:os";
import { join } from "node:path";
import * as NodePath from "node:path";

import {
desktopDir,
Expand Down Expand Up @@ -64,15 +64,17 @@ function killChildTreeByPid(pid, signal) {
return;
}

spawnSync("pkill", [`-${signal}`, "-P", String(pid)], { stdio: "ignore" });
NodeChildProcess.spawnSync("pkill", [`-${signal}`, "-P", String(pid)], { stdio: "ignore" });
}

function cleanupStaleDevApps() {
if (hostPlatform === "win32") {
return;
}

spawnSync("pkill", ["-f", "--", `--t3code-dev-root=${desktopDir}`], { stdio: "ignore" });
NodeChildProcess.spawnSync("pkill", ["-f", "--", `--t3code-dev-root=${desktopDir}`], {
stdio: "ignore",
});
}

function startApp() {
Expand All @@ -87,7 +89,7 @@ function startApp() {
? electronArgs
: [...electronArgs, `--t3code-dev-root=${desktopDir}`, "dist-electron/main.cjs"];
const electronCommand = resolveElectronLaunchCommand(launchArgs);
const app = spawn(electronCommand.electronPath, electronCommand.args, {
const app = NodeChildProcess.spawn(electronCommand.electronPath, electronCommand.args, {
cwd: desktopDir,
env: childEnv,
stdio: "inherit",
Expand Down Expand Up @@ -180,8 +182,8 @@ function scheduleRestart() {

function startWatchers() {
for (const { directory, files } of watchedDirectories) {
const watcher = watch(
join(desktopDir, directory),
const watcher = NodeFS.watch(
NodePath.join(desktopDir, directory),
{ persistent: true },
(_eventType, filename) => {
if (typeof filename !== "string" || !files.has(filename)) {
Expand All @@ -202,7 +204,9 @@ function killChildTree(signal) {
}

// Kill direct children as a final fallback in case normal shutdown leaves stragglers.
spawnSync("pkill", [`-${signal}`, "-P", String(process.pid)], { stdio: "ignore" });
NodeChildProcess.spawnSync("pkill", [`-${signal}`, "-P", String(process.pid)], {
stdio: "ignore",
});
}

async function shutdown(exitCode) {
Expand Down
Loading