Skip to content

Commit d15aeaa

Browse files
committed
ship: prepare launch cleanup lane
1 parent d5e8456 commit d15aeaa

103 files changed

Lines changed: 5379 additions & 5670 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ __pycache__/
2323

2424
# Build outputs
2525
/apps/ade-cli/dist/
26+
/apps/ade-cli/dist-static/
2627
/apps/ade-code/dist/
2728
/apps/desktop/release/
2829
/apps/desktop/dist/

AGENTS.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@
2323

2424
- Desktop checks:
2525
- `npm --prefix apps/desktop run typecheck`
26-
- `npm --prefix apps/desktop run test`
26+
- `npm run test:desktop:sharded`
2727
- `npm --prefix apps/desktop run build`
2828
- `npm --prefix apps/desktop run lint`
2929
- ADE CLI checks:
3030
- `npm --prefix apps/ade-cli run typecheck`
3131
- `npm --prefix apps/ade-cli run test`
3232
- `npm --prefix apps/ade-cli run build`
3333
- Run the smallest relevant subset first when iterating, then finish with the broader checks that cover the touched surfaces.
34-
- If even running the full desktop test suit, u ahve to shard like ci
34+
- Run full desktop tests with the root `npm run test:desktop:sharded` command; use single-file or single-shard Vitest commands for iteration.
3535

3636
## Terminology
3737

@@ -81,7 +81,7 @@ Desktop release:
8181
- **Node.js 22.x** is required (`node:sqlite` is used as the primary database engine).
8282
- Each app under `apps/` has its own independent `node_modules` and `package-lock.json` (no npm workspaces).
8383
- Validation commands are documented in the "Validation" section above.
84-
- The desktop test suite (265 test files) is large; CI shards it. For local iteration, run targeted tests (e.g. `npm --prefix apps/desktop run test:unit`) or a single file rather than the full suite.
84+
- The desktop test suite is large; CI shards it. For local iteration, run a single file or one CI-style shard rather than the full suite.
8585

8686
### Inspecting the local Electron desktop app with Codex Computer Use on macOS
8787

@@ -111,7 +111,7 @@ Desktop release:
111111
- The `ADE_PROJECT_ROOT=/workspace` env var tells the main process to auto-open a project at startup. However, there is a timing race: the renderer's initial `getProject()` call may return null before the async project switch completes, causing the welcome screen to appear even though the backend loaded the project. A workaround is to open the project manually via the "Open a project" button in the top bar.
112112
- Computer-use features (screenshot, video capture, GUI automation) are macOS-only (`screencapture`, `osascript`). On Linux these gracefully degrade — the app returns `blocked_by_capability`.
113113
- `electron-builder` config only defines a `mac` target. Distributable Linux builds (deb/AppImage) are not configured, but dev mode works fine.
114-
- The `test:unit` script (`npm --prefix apps/desktop run test:unit`) uses `--project unit` which the pinned vitest 0.34.6 doesn't support. Use `npx vitest run <specific-test-file>` in `apps/desktop` for targeted tests, or `npm --prefix apps/desktop run test` for the full suite.
114+
- The pinned Vitest 0.34.6 does not support `--project`. Use `npx vitest run <specific-test-file>` in `apps/desktop` for targeted tests, `npx vitest run --shard=<n>/8` for a CI-style shard, or `npm run test:desktop:sharded` from the repo root for the full desktop unit workspace.
115115
- In the Cursor Cloud VM the active X display is `:1`, not `:99`. When launching Electron set `DISPLAY=:1`.
116116
- To launch the desktop dev app quickly when the CLI is already built: `npm run dev:desktop -- --skip-runtime-build`.
117117
- To launch the TUI against an already-running dev runtime: `npm run dev:code -- --skip-runtime-build --attach --project-root <path> --workspace-root <path>`.

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ Daily desktop dev:
154154
npm run dev
155155
```
156156

157-
That aliases to `npm run dev:desktop`: it rebuilds `apps/ade-cli`, launches the Electron desktop app, and points it at the dev runtime socket `/tmp/ade-runtime-dev.sock`. If no dev runtime is listening, desktop is allowed to create it. This is the normal desktop-dev flow.
157+
That aliases to `npm run dev:desktop`: it rebuilds `apps/ade-cli`, refreshes the shared dev runtime at `/tmp/ade-runtime-dev.sock` when needed, launches the Electron desktop app, and points desktop at that runtime. This is the normal desktop-dev flow.
158158

159159
When these commands are run from an ADE lane worktree under `.ade/worktrees/`,
160160
they still run code from that lane checkout, but they open the primary checkout's
@@ -165,7 +165,7 @@ and uses the lane path as the workspace root for `dev:code`.
165165
Dev command matrix:
166166

167167
```bash
168-
npm run dev:desktop # desktop only; dev socket; desktop may auto-create runtime
168+
npm run dev:desktop # refresh shared dev runtime, then launch desktop
169169
npm run dev:desktop:attach # desktop only; fail if dev runtime is not already running
170170
npm run dev:desktop:clean # desktop only; clear Vite cache before launch
171171
npm run dev:code:web # `ade code` in the browser (PTY + inspector WebSocket)
@@ -192,11 +192,11 @@ ADE_DEV_RUNTIME_SOCKET_PATH=/tmp/my-ade-dev.sock npm run dev:runtime
192192
ADE_DESKTOP_BRIDGE_SOCKET_PATH=/tmp/my-bridge.sock npm run dev:desktop
193193
```
194194

195-
To test auto-runtime creation, use the `:auto`/default commands after stopping the dev runtime:
195+
To test auto-runtime creation, use the default dev commands after stopping the dev runtime:
196196

197197
```bash
198198
npm run dev:stop
199-
npm run dev:desktop # tests desktop creating the dev runtime
199+
npm run dev:desktop # tests the desktop wrapper creating the dev runtime
200200
npm run dev:stop
201201
npm run dev:code # tests TUI wrapper creating the dev runtime
202202
```
@@ -211,7 +211,7 @@ npm run package:beta # origin/main -> ADE Beta.app, ade-beta, ~/.ade-bet
211211
These are unsigned local macOS app builds under `apps/desktop/release-alpha` and `apps/desktop/release-beta`. Beta fetches `origin/main`, fast-forwards the local `main` checkout when possible, and builds that checkout as `ADE Beta`. It does not create a packaging worktree. These builds do not replace the production `ADE.app`, production `ade`, or `~/.ade` runtime/state. Alpha and Beta also use separate Electron profile directories (`ade-desktop-alpha` / `ade-desktop-beta`) so their browser storage and window state do not collide with dev or stable.
212212
Local channel packages include the host runtime binary for this Mac. Release builds still require the full cross-platform runtime artifact set used by remote runtime bootstrap.
213213

214-
Validate with `npm --prefix apps/desktop run typecheck` and `run test`. The desktop test suite is large run the smallest relevant subset first.
214+
Validate with `npm --prefix apps/desktop run typecheck` and `npm run test:desktop:sharded` for the full desktop suite. The desktop test suite is large, so run the smallest relevant subset first.
215215

216216
## Links
217217

apps/ade-cli/src/adeRpcServer.ts

Lines changed: 16 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
} from "../../desktop/src/main/services/computerUse/localComputerUse";
1616
import { loadAgentBrowserArtifactPayloadFromFile, parseAgentBrowserArtifactPayload } from "../../desktop/src/main/services/proof/agentBrowserArtifactAdapter";
1717
import {
18-
ADE_ACTION_ALLOWLIST,
1918
ADE_ACTION_DOMAIN_NAMES,
2019
type AdeActionDomain,
2120
callerHasRoleAtLeast,
@@ -44,8 +43,6 @@ import {
4443
import {
4544
type LinearWorkflowConfig,
4645
type ComputerUseArtifactOwner,
47-
type DockLayout,
48-
type GraphPersistedState,
4946
type LaneLinearIssue,
5047
type MergeMethod,
5148
type AppNavigationRequest,
@@ -69,6 +66,7 @@ import {
6966
import type { AgentChatPermissionMode, TerminalSessionSummary } from "../../desktop/src/shared/types";
7067
import type { AdeRuntime } from "./bootstrap";
7168
import { JsonRpcError, JsonRpcErrorCode, type JsonRpcHandler, type JsonRpcRequest } from "./jsonrpc";
69+
import { normalizeAdeRuntimeRole } from "./runtimeRoles";
7270
import { getSharedModelPickerStore } from "./services/modelPickerStore";
7371

7472
// Cross-surface (desktop + TUI + iOS) model picker favorites & recents.
@@ -2101,12 +2099,6 @@ const MACOS_VM_TOOL_NAMES = new Set([
21012099
"macos_vm_type",
21022100
]);
21032101

2104-
const ALL_TOOL_SPECS: ToolSpec[] = [
2105-
...TOOL_SPECS,
2106-
...CTO_OPERATOR_TOOL_SPECS,
2107-
...CTO_LINEAR_SYNC_TOOL_SPECS,
2108-
...COORDINATOR_TOOL_SPECS,
2109-
];
21102102
const COORDINATOR_TOOL_NAMES = new Set(COORDINATOR_TOOL_SPECS.map((tool) => tool.name));
21112103
const READ_ONLY_TOOLS = new Set([
21122104
"check_conflicts",
@@ -2323,13 +2315,6 @@ function asOptionalTrimmedString(value: unknown): string | null {
23232315
return text.length ? text : null;
23242316
}
23252317

2326-
function parseEnvBoolean(value: string | undefined): boolean | null {
2327-
const normalized = value?.trim().toLowerCase() ?? "";
2328-
if (normalized === "1" || normalized === "true" || normalized === "yes") return true;
2329-
if (normalized === "0" || normalized === "false" || normalized === "no") return false;
2330-
return null;
2331-
}
2332-
23332318
function asBoolean(value: unknown, fallback = false): boolean {
23342319
return typeof value === "boolean" ? value : fallback;
23352320
}
@@ -2924,52 +2909,6 @@ function resolveRunContextLaneId(runtime: AdeRuntime, callerCtx: CallerContext):
29242909
return asOptionalTrimmedString(mission?.laneId) ?? asOptionalTrimmedString(mission?.lane_id);
29252910
}
29262911

2927-
function resolveAuthorizedWorkspaceRoot(
2928-
runtime: AdeRuntime,
2929-
session: SessionState,
2930-
toolArgs?: Record<string, unknown>,
2931-
): string {
2932-
const requestedLaneId = toolArgs ? extractLaneId(toolArgs) : null;
2933-
if (requestedLaneId) {
2934-
const laneWorktreePath = resolveLaneWorktreePath(runtime, requestedLaneId);
2935-
if (!laneWorktreePath) {
2936-
throw new JsonRpcError(
2937-
JsonRpcErrorCode.invalidParams,
2938-
`Requested lane '${requestedLaneId}' does not have an available worktree.`,
2939-
);
2940-
}
2941-
return laneWorktreePath;
2942-
}
2943-
2944-
const sessionLaneId = resolveChatSessionLaneId(runtime, session);
2945-
if (sessionLaneId) {
2946-
const laneWorktreePath = resolveLaneWorktreePath(runtime, sessionLaneId);
2947-
if (!laneWorktreePath) {
2948-
throw new JsonRpcError(
2949-
JsonRpcErrorCode.invalidParams,
2950-
`Chat session lane '${sessionLaneId}' does not have an available worktree.`,
2951-
);
2952-
}
2953-
return laneWorktreePath;
2954-
}
2955-
2956-
const runContextLaneId = resolveRunContextLaneId(runtime, resolveCallerContext(session));
2957-
if (runContextLaneId) {
2958-
const laneWorktreePath = resolveLaneWorktreePath(runtime, runContextLaneId);
2959-
if (!laneWorktreePath) {
2960-
throw new JsonRpcError(
2961-
JsonRpcErrorCode.invalidParams,
2962-
`Run context lane '${runContextLaneId}' does not have an available worktree.`,
2963-
);
2964-
}
2965-
return laneWorktreePath;
2966-
}
2967-
2968-
const fallbackWorkspaceRoot = typeof runtime.workspaceRoot === "string" ? runtime.workspaceRoot.trim() : "";
2969-
if (fallbackWorkspaceRoot.length > 0) return fallbackWorkspaceRoot;
2970-
return runtime.projectRoot;
2971-
}
2972-
29732912
function resolveRequestedOrSessionLaneId(
29742913
runtime: AdeRuntime,
29752914
session: SessionState,
@@ -3364,15 +3303,7 @@ type CallerContext = {
33643303
};
33653304

33663305
function resolveEnvCallerContext(): CallerContext {
3367-
const envRoleRaw = process.env.ADE_DEFAULT_ROLE?.trim() ?? "";
3368-
const envRole: SessionIdentity["role"] | null =
3369-
envRoleRaw === "cto"
3370-
|| envRoleRaw === "orchestrator"
3371-
|| envRoleRaw === "agent"
3372-
|| envRoleRaw === "external"
3373-
|| envRoleRaw === "evaluator"
3374-
? envRoleRaw
3375-
: null;
3306+
const envRole = normalizeAdeRuntimeRole(process.env.ADE_DEFAULT_ROLE);
33763307
const envChatSessionId = process.env.ADE_CHAT_SESSION_ID?.trim() || null;
33773308
const envMissionId = process.env.ADE_MISSION_ID?.trim() || null;
33783309
const envRunId = process.env.ADE_RUN_ID?.trim() || null;
@@ -3498,11 +3429,6 @@ function parseInitializeIdentity(runtime: AdeRuntime, params: unknown): SessionI
34983429
const data = safeObject(params);
34993430
const identity = safeObject(data.identity);
35003431
const envContext = resolveEnvCallerContext();
3501-
const identityRole = asOptionalTrimmedString(identity.role);
3502-
const parsedIdentityRole: SessionIdentity["role"] | null =
3503-
identityRole === "cto" || identityRole === "orchestrator" || identityRole === "agent" || identityRole === "external" || identityRole === "evaluator"
3504-
? identityRole
3505-
: null;
35063432
const validRole: SessionIdentity["role"] = envContext.role ?? "external";
35073433
const requestedChatSessionId = asOptionalTrimmedString(identity.chatSessionId);
35083434
const resolvedChatSessionId = envContext.chatSessionId ?? requestedChatSessionId;
@@ -5968,7 +5894,7 @@ async function runTool(args: {
59685894
}
59695895

59705896
if (name === "ingest_computer_use_artifacts") {
5971-
const backendStyle = assertNonEmptyString(toolArgs.backendStyle, "backendStyle") as "external_cli" | "manual" | "local_fallback";
5897+
assertNonEmptyString(toolArgs.backendStyle, "backendStyle");
59725898
const backendName = assertNonEmptyString(toolArgs.backendName, "backendName");
59735899
const manifestPath = asOptionalTrimmedString(toolArgs.manifestPath);
59745900
let inputs = Array.isArray(toolArgs.inputs) ? toolArgs.inputs.map((entry) => safeObject(entry)) : [];
@@ -7402,9 +7328,8 @@ const APP_NAVIGATE_SUPPORTED_KINDS = new Set([
74027328
export function createAdeRpcRequestHandler(args: {
74037329
runtime: AdeRuntime;
74047330
serverVersion: string;
7405-
onActionsListChanged?: (() => void) | null;
74067331
}): JsonRpcHandler & { dispose: () => void } {
7407-
const { runtime, serverVersion, onActionsListChanged } = args;
7332+
const { runtime, serverVersion } = args;
74087333

74097334
const session: SessionState = {
74107335
initialized: false,
@@ -7518,11 +7443,21 @@ export function createAdeRpcRequestHandler(args: {
75187443
protocolVersion: session.protocolVersion,
75197444
runtimeInfo: {
75207445
name: "ade-rpc",
7521-
version: serverVersion
7446+
version: serverVersion,
7447+
buildHash:
7448+
typeof process.env.ADE_RUNTIME_BUILD_HASH === "string" &&
7449+
process.env.ADE_RUNTIME_BUILD_HASH.trim()
7450+
? process.env.ADE_RUNTIME_BUILD_HASH.trim()
7451+
: null,
7452+
defaultRole:
7453+
normalizeAdeRuntimeRole(process.env.ADE_DEFAULT_ROLE),
7454+
projectRoot: runtime.projectRoot,
7455+
workspaceRoot: runtime.workspaceRoot ?? null,
7456+
pid: process.pid
75227457
},
75237458
capabilities: {
75247459
actions: {
7525-
listChanged: true
7460+
listChanged: false
75267461
},
75277462
...(resourcesEnabled
75287463
? {

apps/ade-cli/src/cli.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,18 @@ describe("ADE CLI", () => {
5959
]);
6060
});
6161

62+
it("defaults ordinary CLI calls to the CTO runtime role", () => {
63+
const previousRole = process.env.ADE_DEFAULT_ROLE;
64+
delete process.env.ADE_DEFAULT_ROLE;
65+
try {
66+
const parsed = parseCliArgs(["lanes", "list"]);
67+
expect(parsed.options.role).toBe("cto");
68+
} finally {
69+
if (previousRole === undefined) delete process.env.ADE_DEFAULT_ROLE;
70+
else process.env.ADE_DEFAULT_ROLE = previousRole;
71+
}
72+
});
73+
6274
it("maps ade code to the terminal Work chat launcher", () => {
6375
const parsed = parseCliArgs([
6476
"--project-root",

0 commit comments

Comments
 (0)