From 616e50b6dd6281d56039f9ccc736962ce9171521 Mon Sep 17 00:00:00 2001 From: NekoPunch Date: Fri, 28 Aug 2026 01:43:40 -0700 Subject: [PATCH 1/4] fix(computer-use): require explicit lab root Generated-by: Codex --- scripts/computer-use/lab-root.mjs | 28 +++++++++++++ scripts/computer-use/lab-root.test.mjs | 39 +++++++++++++++++++ .../computer-use/process-restart-harness.mjs | 3 +- .../computer-use/process-restart-launcher.mjs | 4 +- scripts/computer-use/real-ax-harness.mjs | 5 +-- scripts/computer-use/real-ax-launcher.mjs | 9 ++--- 6 files changed, 77 insertions(+), 11 deletions(-) create mode 100644 scripts/computer-use/lab-root.mjs create mode 100644 scripts/computer-use/lab-root.test.mjs diff --git a/scripts/computer-use/lab-root.mjs b/scripts/computer-use/lab-root.mjs new file mode 100644 index 0000000000..387e440962 --- /dev/null +++ b/scripts/computer-use/lab-root.mjs @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export function requireComputerUseLabRoot(env = process.env) { + const labRoot = env.MAKA_CU_AX_MODEL_LAB_ROOT; + if (!labRoot) { + throw new Error( + 'MAKA_CU_AX_MODEL_LAB_ROOT is required: point it at a local checkout of the Codex CUA Lab fixture', + ); + } + return labRoot; +} diff --git a/scripts/computer-use/lab-root.test.mjs b/scripts/computer-use/lab-root.test.mjs new file mode 100644 index 0000000000..8fcdbb3f30 --- /dev/null +++ b/scripts/computer-use/lab-root.test.mjs @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import assert from 'node:assert/strict'; +import { test } from 'node:test'; + +import { requireComputerUseLabRoot } from './lab-root.mjs'; + +test('returns the configured Computer Use Lab root', () => { + assert.equal( + requireComputerUseLabRoot({ MAKA_CU_AX_MODEL_LAB_ROOT: '/tmp/codex-cua-lab' }), + '/tmp/codex-cua-lab', + ); +}); + +test('rejects a missing Computer Use Lab root', () => { + assert.throws( + () => requireComputerUseLabRoot({}), + new Error( + 'MAKA_CU_AX_MODEL_LAB_ROOT is required: point it at a local checkout of the Codex CUA Lab fixture', + ), + ); +}); diff --git a/scripts/computer-use/process-restart-harness.mjs b/scripts/computer-use/process-restart-harness.mjs index e911cf7714..4e8a397666 100644 --- a/scripts/computer-use/process-restart-harness.mjs +++ b/scripts/computer-use/process-restart-harness.mjs @@ -22,10 +22,11 @@ import { tmpdir } from 'node:os'; import { join, relative, resolve } from 'node:path'; import { createMakaCuBackend } from '../../packages/computer-use/dist/index.js'; import { buildComputerUseTools } from '../../packages/runtime/dist/computer-use-tools.js'; +import { requireComputerUseLabRoot } from './lab-root.mjs'; const repoRoot = new URL('../..', import.meta.url).pathname; const binaryPath = join(repoRoot, 'apps/desktop/resources/bin/maka-cu'); -const labRoot = '/Users/haoqing/Documents/Learning/codex-computer-use-lab'; +const labRoot = requireComputerUseLabRoot(); const expectedAppPath = join(labRoot, 'test-app/build/Codex CUA Lab.app'); const statePath = join(labRoot, 'test-app/runtime/state.json'); const temporaryDirectory = process.env.MAKA_CU_RESTART_TEMP_DIR; diff --git a/scripts/computer-use/process-restart-launcher.mjs b/scripts/computer-use/process-restart-launcher.mjs index 349e4e6953..0af6e81ae6 100644 --- a/scripts/computer-use/process-restart-launcher.mjs +++ b/scripts/computer-use/process-restart-launcher.mjs @@ -23,11 +23,13 @@ import { tmpdir } from 'node:os'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; +import { requireComputerUseLabRoot } from './lab-root.mjs'; + const here = dirname(fileURLToPath(import.meta.url)); const repoRoot = join(here, '../..'); const harnessPath = join(here, 'process-restart-harness.mjs'); const monitorPath = join(here, 'real-e2e-monitor.swift'); -const labRoot = '/Users/haoqing/Documents/Learning/codex-computer-use-lab'; +const labRoot = requireComputerUseLabRoot(); const statePath = join(labRoot, 'test-app', 'runtime', 'state.json'); const SOAK_ROUNDS = 5; const FIXTURE_BUNDLE_ID = 'com.openai.codex.cualab'; diff --git a/scripts/computer-use/real-ax-harness.mjs b/scripts/computer-use/real-ax-harness.mjs index 643604d177..b5801da64a 100644 --- a/scripts/computer-use/real-ax-harness.mjs +++ b/scripts/computer-use/real-ax-harness.mjs @@ -28,13 +28,12 @@ import { buildComputerUseTools } from '../../packages/runtime/dist/computer-use- import { getAIModel } from '../../packages/runtime/dist/model-factory.js'; import { createMakaCuBackend } from '../../packages/computer-use/dist/index.js'; import { createDirectRuntimeTurnLedger } from './direct-runtime-ledger.mjs'; +import { requireComputerUseLabRoot } from './lab-root.mjs'; import { sanitizeCuDirectReport } from './report-sanitize.mjs'; const repoRoot = new URL('../..', import.meta.url).pathname; const binaryPath = join(repoRoot, 'apps/desktop/resources/bin/maka-cu'); -const labRoot = - process.env.MAKA_CU_AX_MODEL_LAB_ROOT ?? - '/Users/haoqing/Documents/Learning/codex-computer-use-lab'; +const labRoot = requireComputerUseLabRoot(); const expectedAppPath = join(labRoot, 'test-app/build/Codex CUA Lab.app'); const statePath = join(labRoot, 'test-app/runtime/state.json'); const fixturePID = Number(process.env.MAKA_CU_AX_MODEL_FIXTURE_PID); diff --git a/scripts/computer-use/real-ax-launcher.mjs b/scripts/computer-use/real-ax-launcher.mjs index 2c0987bd6b..cadb65131f 100644 --- a/scripts/computer-use/real-ax-launcher.mjs +++ b/scripts/computer-use/real-ax-launcher.mjs @@ -23,17 +23,14 @@ import { tmpdir } from 'node:os'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; +import { requireComputerUseLabRoot } from './lab-root.mjs'; + const here = dirname(fileURLToPath(import.meta.url)); const repoRoot = join(here, '../..'); const harnessPath = join(here, 'real-ax-harness.mjs'); const monitorPath = join(here, 'real-e2e-monitor.swift'); const inputAgeSource = join(here, 'physical-input-age.swift'); -const labRoot = process.env.MAKA_CU_AX_MODEL_LAB_ROOT; -if (!labRoot) { - throw new Error( - 'MAKA_CU_AX_MODEL_LAB_ROOT is required: point it at a local checkout of the Codex CUA Lab fixture', - ); -} +const labRoot = requireComputerUseLabRoot(); const statePath = join(labRoot, 'test-app/runtime/state.json'); const fixtureBundleId = 'com.openai.codex.cualab'; const expectedAppPath = join(labRoot, 'test-app/build/Codex CUA Lab.app'); From 5ff51bf19c0b25c52ea26d2761dd16865f782464 Mon Sep 17 00:00:00 2001 From: NekoPunch Date: Fri, 28 Aug 2026 02:05:50 -0700 Subject: [PATCH 2/4] docs(computer-use): document lab setup Generated-by: Codex --- docs/computer-use-evidence-classes.md | 24 ++++++++++++++++++++++-- docs/computer-use-provider-evidence.md | 6 ++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/computer-use-evidence-classes.md b/docs/computer-use-evidence-classes.md index 8f2a49ed8a..4822c749a8 100644 --- a/docs/computer-use-evidence-classes.md +++ b/docs/computer-use-evidence-classes.md @@ -64,10 +64,30 @@ real-machine qualification runner was removed. The five-round process-restart runner remains as a non-qualifying soak after its qualification checks moved into the canonical Runtime-backed harness. -The canonical operator commands are: +### Lab fixture setup -```text +The `real-ax` and `restart-soak` commands require a local checkout of the +[Codex Computer Use Lab](https://github.com/hqhq1025/codex-computer-use-lab). +Clone it outside this repository and export its absolute repository root: + +```bash +git clone https://github.com/hqhq1025/codex-computer-use-lab.git ../codex-computer-use-lab +export MAKA_CU_AX_MODEL_LAB_ROOT="$(cd ../codex-computer-use-lab && pwd)" +``` + +The path must contain `test-app/launch.sh`. The launchers invoke that script, +which builds the fixture when its application bundle is absent. + +With the variable exported, run the canonical operator commands: + +```bash npm run computer-use -- real-ax npm run computer-use -- real-ax --scenario restart-recovery npm run computer-use -- real-model ``` + +The non-qualifying five-round restart soak uses the same fixture checkout: + +```bash +npm run computer-use -- restart-soak +``` diff --git a/docs/computer-use-provider-evidence.md b/docs/computer-use-provider-evidence.md index a579c677b4..e143cd44f1 100644 --- a/docs/computer-use-provider-evidence.md +++ b/docs/computer-use-provider-evidence.md @@ -82,8 +82,10 @@ Qualification also keeps three fail-closed invariants: The old direct real-machine qualification runner was removed. The five-round restart runner remains available as `npm run computer-use -- restart-soak`, -but is regression-only and cannot satisfy a provider matrix cell. There is one -qualification path rather than parallel evidence standards. +using the `MAKA_CU_AX_MODEL_LAB_ROOT` fixture checkout described in +[Lab fixture setup](./computer-use-evidence-classes.md#lab-fixture-setup). The +runner is regression-only and cannot satisfy a provider matrix cell. There is +one qualification path rather than parallel evidence standards. ## Next Layer From 004392eee929cb35d157388d0394b67fafd35fde Mon Sep 17 00:00:00 2001 From: NekoPunch Date: Fri, 28 Aug 2026 02:10:52 -0700 Subject: [PATCH 3/4] test(computer-use): enforce lab root contract Generated-by: Codex --- .github/workflows/ci.yml | 4 ++-- scripts/computer-use/lab-root.test.mjs | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b73068e25b..e443738e3c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,8 +84,8 @@ jobs: - name: Test the epoch guard run: node --test --test-concurrency=1 scripts/protocol-epoch-check.test.mjs - - name: Test AX tree audit contract - run: node --test scripts/ax-tree-audit.test.mjs + - name: Test Computer Use script contracts + run: node --test scripts/ax-tree-audit.test.mjs scripts/computer-use/lab-root.test.mjs - name: Verify ASF npm preflight policy run: npm run check:asf-npm diff --git a/scripts/computer-use/lab-root.test.mjs b/scripts/computer-use/lab-root.test.mjs index 8fcdbb3f30..a3526a2e18 100644 --- a/scripts/computer-use/lab-root.test.mjs +++ b/scripts/computer-use/lab-root.test.mjs @@ -18,6 +18,7 @@ */ import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; import { test } from 'node:test'; import { requireComputerUseLabRoot } from './lab-root.mjs'; @@ -37,3 +38,26 @@ test('rejects a missing Computer Use Lab root', () => { ), ); }); + +test('Lab-backed entry points require the configured root', async () => { + const entryPoints = [ + 'process-restart-harness.mjs', + 'process-restart-launcher.mjs', + 'real-ax-harness.mjs', + 'real-ax-launcher.mjs', + ]; + + for (const entryPoint of entryPoints) { + const source = await readFile(new URL(entryPoint, import.meta.url), 'utf8'); + assert.match( + source, + /const labRoot = requireComputerUseLabRoot\(\);/, + `${entryPoint} must require the configured Lab root`, + ); + assert.doesNotMatch( + source, + /codex-computer-use-lab/i, + `${entryPoint} must not embed a contributor-specific Lab checkout`, + ); + } +}); From 6ca9c435d4013d6dbb48365bd9a8c5258960f918 Mon Sep 17 00:00:00 2001 From: NekoPunch Date: Fri, 28 Aug 2026 10:27:42 -0700 Subject: [PATCH 4/4] fix(computer-use): validate lab root Generated-by: Codex --- scripts/computer-use/lab-root.mjs | 32 ++++++++++- scripts/computer-use/lab-root.test.mjs | 80 +++++++++++++++++++++----- 2 files changed, 97 insertions(+), 15 deletions(-) diff --git a/scripts/computer-use/lab-root.mjs b/scripts/computer-use/lab-root.mjs index 387e440962..e9d0341a11 100644 --- a/scripts/computer-use/lab-root.mjs +++ b/scripts/computer-use/lab-root.mjs @@ -17,12 +17,40 @@ * under the License. */ +import { accessSync, constants, realpathSync, statSync } from 'node:fs'; +import { isAbsolute, join } from 'node:path'; + export function requireComputerUseLabRoot(env = process.env) { - const labRoot = env.MAKA_CU_AX_MODEL_LAB_ROOT; - if (!labRoot) { + const configuredRoot = env.MAKA_CU_AX_MODEL_LAB_ROOT; + if (typeof configuredRoot !== 'string' || configuredRoot.trim() === '') { throw new Error( 'MAKA_CU_AX_MODEL_LAB_ROOT is required: point it at a local checkout of the Codex CUA Lab fixture', ); } + if (!isAbsolute(configuredRoot)) { + throw new Error('MAKA_CU_AX_MODEL_LAB_ROOT must be an absolute path'); + } + + let labRoot; + try { + labRoot = realpathSync(configuredRoot); + if (!statSync(labRoot).isDirectory()) throw new Error('Lab root is not a directory'); + } catch (cause) { + throw new Error( + `MAKA_CU_AX_MODEL_LAB_ROOT must point to an existing directory: ${configuredRoot}`, + { cause }, + ); + } + + const launcherPath = join(labRoot, 'test-app', 'launch.sh'); + try { + if (!statSync(launcherPath).isFile()) throw new Error('fixture launcher is not a file'); + accessSync(launcherPath, constants.X_OK); + } catch (cause) { + throw new Error( + `MAKA_CU_AX_MODEL_LAB_ROOT must contain an executable test-app/launch.sh: ${launcherPath}`, + { cause }, + ); + } return labRoot; } diff --git a/scripts/computer-use/lab-root.test.mjs b/scripts/computer-use/lab-root.test.mjs index a3526a2e18..d15304e26d 100644 --- a/scripts/computer-use/lab-root.test.mjs +++ b/scripts/computer-use/lab-root.test.mjs @@ -18,25 +18,79 @@ */ import assert from 'node:assert/strict'; -import { readFile } from 'node:fs/promises'; +import { + chmod, + mkdir, + mkdtemp, + readFile, + realpath, + rm, + symlink, + writeFile, +} from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; import { test } from 'node:test'; import { requireComputerUseLabRoot } from './lab-root.mjs'; -test('returns the configured Computer Use Lab root', () => { - assert.equal( - requireComputerUseLabRoot({ MAKA_CU_AX_MODEL_LAB_ROOT: '/tmp/codex-cua-lab' }), - '/tmp/codex-cua-lab', - ); +test('rejects missing and invalid Computer Use Lab roots', async () => { + const root = await mkdtemp(join(tmpdir(), 'maka-cu-lab-root-invalid-')); + try { + assert.throws( + () => requireComputerUseLabRoot({}), + new Error( + 'MAKA_CU_AX_MODEL_LAB_ROOT is required: point it at a local checkout of the Codex CUA Lab fixture', + ), + ); + assert.throws( + () => requireComputerUseLabRoot({ MAKA_CU_AX_MODEL_LAB_ROOT: ' ' }), + /MAKA_CU_AX_MODEL_LAB_ROOT is required/, + ); + assert.throws( + () => requireComputerUseLabRoot({ MAKA_CU_AX_MODEL_LAB_ROOT: 'relative/lab' }), + /MAKA_CU_AX_MODEL_LAB_ROOT must be an absolute path/, + ); + assert.throws( + () => requireComputerUseLabRoot({ MAKA_CU_AX_MODEL_LAB_ROOT: join(root, 'missing') }), + /MAKA_CU_AX_MODEL_LAB_ROOT must point to an existing directory/, + ); + assert.throws( + () => requireComputerUseLabRoot({ MAKA_CU_AX_MODEL_LAB_ROOT: root }), + /MAKA_CU_AX_MODEL_LAB_ROOT must contain an executable test-app\/launch\.sh/, + ); + const nonExecutableRoot = join(root, 'non-executable'); + const launcherPath = join(nonExecutableRoot, 'test-app', 'launch.sh'); + await mkdir(join(nonExecutableRoot, 'test-app'), { recursive: true }); + await writeFile(launcherPath, '#!/usr/bin/env bash\n'); + await chmod(launcherPath, 0o644); + assert.throws( + () => requireComputerUseLabRoot({ MAKA_CU_AX_MODEL_LAB_ROOT: nonExecutableRoot }), + /MAKA_CU_AX_MODEL_LAB_ROOT must contain an executable test-app\/launch\.sh/, + ); + } finally { + await rm(root, { recursive: true, force: true }); + } }); -test('rejects a missing Computer Use Lab root', () => { - assert.throws( - () => requireComputerUseLabRoot({}), - new Error( - 'MAKA_CU_AX_MODEL_LAB_ROOT is required: point it at a local checkout of the Codex CUA Lab fixture', - ), - ); +test('canonicalizes a valid Computer Use Lab root', async () => { + const root = await mkdtemp(join(tmpdir(), 'maka-cu-lab-root-valid-')); + try { + const fixtureRoot = join(root, 'fixture'); + const linkedRoot = join(root, 'fixture-link'); + const launcherPath = join(fixtureRoot, 'test-app', 'launch.sh'); + await mkdir(join(fixtureRoot, 'test-app'), { recursive: true }); + await writeFile(launcherPath, '#!/usr/bin/env bash\n'); + await chmod(launcherPath, 0o755); + await symlink(fixtureRoot, linkedRoot); + + assert.equal( + requireComputerUseLabRoot({ MAKA_CU_AX_MODEL_LAB_ROOT: linkedRoot }), + await realpath(fixtureRoot), + ); + } finally { + await rm(root, { recursive: true, force: true }); + } }); test('Lab-backed entry points require the configured root', async () => {