Skip to content

🤖 tests: run the real app AI in the bug-bash sandbox, through each job's proxy - #6079

Merged
ThomasK33 merged 3 commits into
mainfrom
tests/5714-b3-real-app-ai
Oct 10, 2026
Merged

ThomasK33 merged 3 commits into
mainfrom
tests/5714-b3-real-app-ai

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

Summary

Lets make bug-bash run the real app AI inside the bug-bash sandbox. The app reaches BUGBASH_APP_MODEL (default Haiku 4.5) only through its job's provider proxy, the app-AI probe goes through the run's budget, and no provider key enters any container. This is PR B3 of the #5714 plan, stacked on B2 (#6076).

Refs #5714

Background

B2 ran make bug-bash in the sandbox with the mock app AI only. The real app AI needs the app's own provider calls to go through the same proxy and budget as the explorer's. B3 also takes three follow-ups from the B1 readiness review: a stronger sandbox check, a startup guard for the kill switches, and a test for a call that outlives the proxy's close.

Implementation

  1. run.ts resolves BUGBASH_AI (auto, real, mock) once on the host. The probe is one max_tokens: 1 call through a proxy of its own and the run's Ledger (launch.ts probeApp), so it never goes straight to the provider and counts against the budget. The aiMode.ts rules stay: auto falls back to the mock only when the upstream is unavailable, a rejected key refuses, and real never falls back. Charters that name a [mock:...] prompt stay on the mock. A mode resolved elsewhere (BUGBASH_AI_RESOLVED) refuses.

  2. launch.ts: an explore job with a resolved real mode puts the app model on its proxy's allowlist next to the explorer model, and passes only the mode and the model name. The app model must be priced and Anthropic. Repro jobs and the MCP Apps suite keep the mock.

  3. aiMode.ts: inside a model-driven sandbox job, the real mode is the job's forwarder URL with a placeholder key. entry.ts runs a real mode only in a model-driven job.

  4. inContainer.ts: modelDrivenSandbox() also checks the launcher's boot ID and its per-launch nonce, as entry.ts does, so another container with --init, --network none and a socket at the mount path does not pass.

  5. startApp.ts (modelDrivenGuard): the app refuses to start when a sandbox job has a proxy socket but no BUGBASH_MODEL_DRIVEN marker, or when a model-driven job lacks any of the three XUM_DISABLE_* switches. It is a guard behind the command.env pass-through, which once dropped the marker (B1).

  6. startApp.ts (real mode) sets the naming agent's model (agentAiDefaults.name_workspace.modelString) to BUGBASH_APP_MODEL through the existing config API. Workspace naming and the sidebar status read that preference first, and otherwise start with Xum's built-in Haiku 5.5, which the job's proxy refuses (6 refused calls in the first paid run). No production default changes. If the Haiku 4.5 naming call fails, the built-in fallback still asks for Haiku 5.5, and the proxy refuses it before any upstream request. That costs nothing, and the allowlist stays as it is.

Claims

Claim Where it is wired or tested
The app AI reaches its model only through the job's proxy The proxy allowlist is the explorer model plus the app model. Tests: an app-model call passes in real mode and refuses in mock mode, and an unlisted model refuses
The probe goes through the run's ledger The probeApp() ledger is the run's. A test checks it is the same instance the jobs get. A probe proxy fault stops the run before any job (exit 5)
No key in the container The container env holds only the mode and the model name. A test checks every docker run arg against the host key
Startup guard modelDrivenGuard tests: a mounted socket without the marker refuses, a missing switch refuses, a repro job and the host start as before

All three new guards are test-proven: the test fails with the code reverted (nonce check, startup guard, app-model allowlist).

Validation

  1. Unit tests: 240 pass in tests/bugbash/.
  2. Transport check with the fake upstream, real Docker, real mode, at this code: 4 charters with --parallel 4.
    • The probe settled as one Haiku call (probe.proxy.jsonl), and the run printed "App AI: real (anthropic:claude-haiku-4-5 answered the probe)".
    • All 4 containers had XUM_DISABLE_AGENT_TOOLS=1, XUM_DISABLE_PROJECT_AUTOMATION=1 and XUM_DISABLE_TERMINALS=1. 3 had no XUM_MOCK_AI, and mock-errors (a [mock:...] charter) had it.
    • One chat message sent to each app server through docker exec produced one Haiku call per real-mode job, settled through that job's proxy. The proxies refused nothing.
    • The app's request shape: keys max_tokens, messages, model, stream, system, streaming, no anthropic-beta, 0 tools. A1's allowlists hold.
    • Every job exited 0, and 0 containers were left.
  3. Functional acceptance (one paid bug bash with the real app AI) follows as a PR comment.

Risks

Test-only code. A real-mode bug bash now makes paid app-model calls, but only through the job's proxy, within the run's budget, and with the app's agent tools, terminals and project automation off.


Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high • Cost: $112.03

@ThomasK33
ThomasK33 added this pull request to stack #6070 October 10, 2026 18:49
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-10T19:18:06.739867Z 59b8eca New commits
🔒 Security Review ✅ Completed 2026-10-10T19:20:07.598281Z 59b8eca New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@ThomasK33

ThomasK33 commented Oct 10, 2026 •

Copy link
Copy Markdown
Member Author

Paid bug bash with the real app AI (functional acceptance of B3)

One make bug-bash at 3dc21a9f5c: BUGBASH_AI=auto, the 6 charters × anthropic:claude-sonnet-5-5, --max-steps 6, BUGBASH_BUDGET_USD=10. make exited 0 after 348 s, all 6 containers ran at once, and 0 were left afterwards.

This run also covers the final B2 code. B3 sits on B2's final head c9764125d0, which includes the two B2 review fixes (2366c986c9: a charter's own exit 130 and the --config preflight; c9764125d0: proxy faults stop the run at once, and the docs). B2's own paid run was on d07865edf3, before those fixes.

The run printed App AI: real (anthropic:claude-haiku-4-5 answered the probe). The probe settled as one Haiku call through the run's ledger (probe.proxy.jsonl).

Charter App AI Job exit Steps Explorer calls App calls Refused Cleanup
artifacts-keyboard real 0 (1 warning) 4 of 6 40 0 0 removed
composer real 0 3 of 6 19 8 4 removed
workspace-lifecycle real 0 6 of 6 66 2 1 removed
settings real 0 6 of 6 68 0 0 removed
mock-errors mock ([mock:...]) 0 4 of 6 25 0 0 removed
phone-layout real 0 4 of 6 45 2 1 removed
  1. Cost: findings.md reports 276 proxied calls, 0 refused by the budget, $3.1493 of $10.0000 at list price. Haiku 4.5: 19545 tokens in, 1022 out. In the job records, 275 calls settled (all HTTP 200), 0 were kept in full, and 6 were refused, all with model: not allowed. boundExceeded = 0, and the highest reservation was $0.6561.
  2. The app AI went through the proxy: 12 Haiku 4.5 app calls settled through the proxies of 3 real-mode jobs. The app logs show app AI: real in 5 jobs and mock in mock-errors.
  3. The 6 refused calls are the app, not the explorer. A background call of the app asked for claude-haiku-5-5, Xum's built-in Haiku, which is not on the job's allowlist (the explorer model and BUGBASH_APP_MODEL only). The proxy refused it before any upstream request, as P3 requires. That app feature failed in those jobs. I propose a fix in this PR.
  4. S3: this run's sampler failed. It matched its own sh command line and read no app environ, so this run gives no S3 evidence. S3 for this head comes from two other sources:
    • The startup guard (modelDrivenGuard) refuses to start an app in a proxied sandbox job without the marker or without all three switches, and all 6 apps started.
    • A $0 rerun of the same 6 charters at this exact head, with the fake upstream and the fixed sampler: all 6 containers had XUM_DISABLE_AGENT_TOOLS=1, XUM_DISABLE_PROJECT_AUTOMATION=1 and XUM_DISABLE_TERMINALS=1. 5 real-mode containers had no XUM_MOCK_AI, and mock-errors had it.
  5. Findings: one, "Focus lost after closing Artifacts tab with keyboard" (warning 2/5, an explorer claim, not yet triaged).

Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high

Base automatically changed from tests/5714-b2-bug-bash to main October 10, 2026 19:03
…b's proxy

- run.ts resolves BUGBASH_AI (auto, real, mock) once on the host. The probe is
  one max_tokens 1 call through its own proxy and the run's ledger
  (launch.ts probeApp), never straight to the provider. aiMode.ts rules: auto
  falls back to the mock only when the upstream is unavailable; a rejected key
  refuses. `[mock:...]` charters stay on the mock.
- launch.ts: an explore job with a resolved real mode puts BUGBASH_APP_MODEL
  (priced, Anthropic only) on its proxy's allowlist and passes the mode, never a
  key. entry.ts runs a real mode only in a model-driven job.
- aiMode.ts: inside a model-driven sandbox job, real mode is the forwarder URL
  with a placeholder key.
- B1 review follow-ups: modelDrivenSandbox() also checks the launcher's boot ID
  and nonce; startApp.ts refuses to start a proxied sandbox job without the
  marker or without all three XUM_DISABLE_* switches; a call that outlives
  close() fails the job (exit 5) and leaves nothing.
- docs/AGENTS.md: the bug-bash app AI line (builtInSkillContent regenerated).

---
_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
@mintlify

mintlify Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
Mux 🟢 Ready View Preview Oct 10, 2026, 7:06 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

…lls; launcher wording

- startApp.ts (real mode): the naming agent gets BUGBASH_APP_MODEL. Naming and
  the sidebar status tried Xum's built-in Haiku 5.5 first, which the job's proxy
  refuses (6 refused calls in the paid B3 run). A $0 check against the fake
  upstream: before, the first naming call asked for claude-haiku-5-5; after, for
  claude-haiku-4-5.
- runner.test.ts: the stop that run.ts sends after another job's fault aborts
  this job's in-flight call, keeps its reservation and ends the job.
- docs/AGENTS.md: on the host the make targets are the launchers; only a
  model-driven e2e process refuses there. builtInSkillContent regenerated.

---
_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 082cd2f164

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tests/bugbash/sandbox/launch.ts
The probe's proxy used the 10-minute call deadline and never saw the run's
stop, so a silent upstream could hold `make bug-bash` for ten minutes before
any job, and a signal could not end it (Codex). probeApp() now takes the run's
stop (it closes the probe's proxy, which aborts the call) and a 10 s deadline,
aiMode.ts's probe timeout. run.ts exits with the signal after a stopped probe.

---
_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
@ThomasK33

Copy link
Copy Markdown
Member Author

Paid recheck after the naming fix (B3)

One make bug-bash at 082cd2f164 (the naming pin): composer, workspace-lifecycle and phone-layout × anthropic:claude-sonnet-5-5, --max-steps 6, BUGBASH_AI=auto, cap $6.85 (the rest of the approved $10). make exited 0 after 195 s. The run printed App AI: real (anthropic:claude-haiku-4-5 answered the probe).

Charter Job exit Steps Explorer calls App calls (Haiku 4.5) Refused Cleanup
composer 0 3 of 6 21 9 0 removed
workspace-lifecycle 0 (1 warning) 3 of 6 30 2 0 removed
phone-layout 0 4 of 6 32 2 0 removed
  1. 0 refusals: the job records have 96 settled calls, plus 1 probe call in probe.proxy.jsonl, 0 kept in full and 0 refused. The app logs have 0 model: not allowed lines. The first run had 6, all to Xum's built-in Haiku 5.5. boundExceeded = 0.
  2. Cost: findings.md reports 97 proxied calls, $1.0054 of $6.8500 at list price.
  3. S3, read from the actual app processes: a sampler, fed to sh on stdin so that no command line holds its patterns, read the node /repo/dist/cli/index.js server process in all 3 containers. Each had XUM_DISABLE_AGENT_TOOLS=1, XUM_DISABLE_PROJECT_AUTOMATION=1 and XUM_DISABLE_TERMINALS=1, and none had XUM_MOCK_AI. I checked the sampler at $0 first.
  4. Leftovers: 0 containers afterwards.
  5. Naming in the sandbox run: not proved there. The explorer typed every workspace name itself (alpha-test, then Bad Name, then beta-test), so the run generated no name.
  6. Naming proof (separate, host real mode): at 59b8eca536, I started startApp.ts in real mode on the host. That is the existing host real-AI path: agent tools, terminals and project automation off, no explorer, and the same name_workspace pin. A shape-only tap (model and usage, no bodies, no keys) sat between the app and the gateway. One nameGeneration/generate call returned {"name":"settings-theme-m5s5","title":"Fix login button dark mode","modelUsed":"anthropic:claude-haiku-4-5"}. The tap saw exactly one upstream call, to claude-haiku-4-5 (1117 tokens in, 67 out, $0.00145), and none to Haiku 5.5. Naming writes no headless-usage.jsonl row (only the sidebar status does), so modelUsed and the tap are the record. I stopped the server and the tap by the PIDs I started, and the server's temp root is gone.

Total paid spend on B3: $3.1493 + $1.0054 + $0.00145.


Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high

@ThomasK33
ThomasK33 added this pull request to the merge queue Oct 10, 2026
Merged via the queue into main with commit 5c1b971 Oct 10, 2026
32 checks passed
@ThomasK33
ThomasK33 deleted the tests/5714-b3-real-app-ai branch October 10, 2026 19:38
yermakoffivan pushed a commit to yermakoffivan/mux that referenced this pull request Oct 11, 2026
…1) (coder#6082)

## Summary

Runtime fixes from the readiness reviews of the bug-bash sandbox PRs,
with regression tests and their docs. Exit 3 now means only an unknown
container state, a SIGKILLed launcher leaves one job folder, `recover`
never treats an owner it cannot see as dead, and one unreadable leftover
no longer stops `recover`. This is PR D1 of the coder#5714 plan, stacked on
B3 (coder#6079). D2 (the self-check, the CI job and the runner docs) follows.

Refs coder#5714 coder#5930 coder#5932

## Implementation

1. **Exit 3 → 6** (`launch.ts` `exitFor()`, `run.ts` `charterCode()`): a
job whose own e2e exits 3 (its infrastructure error) now gives launcher
exit 6. Exit 3 means only that a container's state is unknown after
cleanup. The precedence is unchanged: unknown cleanup (3), then a stop
(130 or 143), then a proxy fault (5). The job's own code stays in
`JobOutcome.code` and the job log. No Makefile, CI or script caller
tests for exit 3.
2. **The proxy's private socket folder** (`proxy.ts` `privateParent`):
the inner HTTP socket's 0700 folder goes into the job folder. The job
folder itself is not mounted; only `stage/` and `proxy/` inside it are.
The container still reaches the HTTP server only through the front
socket, and a SIGKILLed launcher leaves one job folder instead of a
separate `xum-bugbash-proxy-*` folder in `$TMPDIR`. The app-AI probe
keeps its own folder.
3. **hidepid** (`ownerState()`): when `/proc` is mounted with `hidepid`,
another user's live launcher has no `/proc` entry either. A gone PID is
now "dead" only when the mount shows every process. Under `hidepid`, or
when the mount policy cannot be read, it is "cannot tell". A present
entry still decides either way.
4. **recover** (`runner.ts` `listJobs()`, `launch.ts` `recover()`): a
leftover that cannot be inspected comes back as an error entry instead
of stopping the whole command. recover reports it, leaves it, handles
the other leftovers, and exits 3. Uncertain ownership never authorizes a
removal.
5. **Docs** (`docs/AGENTS.md`, regenerated
`builtInSkillContent.generated.ts`, the `hostPause.ts` header):
- The budget scope: `BUGBASH_BUDGET_USD` caps one launch for `make
mcp-apps-e2e`, and the whole run (all jobs plus the probe) for `make
bug-bash`, at list price.
   - The sandbox exit codes and the proxy record path.
- The manual cleanup of leftovers that recover leaves, and of a
SIGKILLed launcher's folders.

## Validation

245 tests pass in `tests/bugbash/`. Each change has a test that fails
with the change reverted:
1. The remap: `exitFor` and `run.ts` with a job exit of 3, an unknown
cleanup, a stop and a proxy fault.
2. The socket folder: the fake job lists its job folder (`group passwd
proxy stage xum-bugbash-proxy-X`).
3. hidepid: `hidepid=2`, `invisible`, `1`, an unreadable or missing
mount line, and `hidepid=0`.
4. recover: two dead leftovers, the first not inspectable. The second is
removed, the first stays, and recover exits 3.

## Risks

Test-only code. The exit-code change is visible to anyone who scripted
against exit 3 of the sandbox launcher. Nothing in the repo does.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking:
`high` • Cost: `$140.84`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high
costs=140.84 -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant