Skip to content

[SSH] Pass Codex context via developer_instructions - #7197

Merged
rclarey merged 2 commits into
mainfrom
ssh-codex-developer-instructions
Oct 28, 2026
Merged

rclarey merged 2 commits into
mainfrom
ssh-codex-developer-instructions

Conversation

@rclarey

@rclarey rclarey commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Changes

Use -c developer_instructions="..." instead of AGENTS.md to pass system context to Codex

Why

This avoids issues with stale/user defined AGENT.md files

Tests

tests updated

@rclarey
rclarey requested review from a team as code owners October 27, 2026 15:17
if err != nil {
return nil, fmt.Errorf("failed to encode agent context: %w", err)
}
return []string{"-c", strings.TrimSpace(string(kv))}, nil

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.

Blocking: this silently drops the user's own developer_instructions.

The old ContextHomeFile path deliberately didn't clobber (// already present — don't clobber), and a test covered that. A -c override is Codex's highest-priority config layer and it replaces the key, so any developer_instructions the user set in ~/.codex/config.toml (or a project .codex/config.toml) vanishes once they launch through the shim. No error or warning. This PR also removes the "does not clobber an existing file" test without a replacement.

Repro with a real codex (0.164.0), using the exact arg this function produces (arg below):

D=$(mktemp -d); W=$(mktemp -d)
printf 'developer_instructions = "USER-OWN-INSTRUCTIONS"\n' > $D/config.toml
cd $W && git init -q
CODEX_HOME=$D codex debug prompt-input | grep -c USER-OWN-INSTRUCTIONS                 # 1
CODEX_HOME=$D codex -c "$(cat arg)" debug prompt-input | grep -c USER-OWN-INSTRUCTIONS  # 0

The second run shows only the Databricks context. Could we combine this with the user's existing value, or use a mechanism that adds rather than replaces, and bring back a test that the user's own instructions survive?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@anton-107 This was intentional (for now at least) since I don't want to reimplement Codex's config resolution in order to read the existing value (if it exists).

I thought this was a reasonable trade off since these are agents running in a managed setting (ug configured in the SSH tunnel) and users can use AGENTS.md (global or project scope) to add additional context for their projects.

If this ends up being an issue in practice we can always add the config resolution to merge our instructions with the user's

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.

Fair, I'm withdrawing "blocking". Merging would mean reimplementing Codex's layered config resolution, and AGENTS.md still gets loaded alongside the override (I checked), so users have a way to add context. The old path also dropped our context completely whenever the user had their own AGENTS.md, so this is a net improvement. One optional nit: a line in the codexContextArg comment saying the override replaces any user-set developer_instructions, and why that's acceptable, so nobody "fixes" it later.

Comment thread experimental/ssh/internal/client/agentshim.go
@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 9f28a77

Run: 40676801599

Env ✅​pass 🙈​skip Time
✅​ aws linux-2core-8gb 281 15 6:19
✅​ aws-windows-latest-4core-16gb 283 13 5:03
✅​ azure linux-2core-8gb 280 15 6:12
✅​ azure-windows-latest-4core-16gb 282 13 3:33
✅​ gcp linux-2core-8gb 281 15 6:45
✅​ gcp-windows-latest-4core-16gb 283 13 3:50
Top 6 slowest tests (at least 2 minutes):
duration env testname
3:51 gcp linux-2core-8gb TestAccept
3:46 aws linux-2core-8gb TestAccept
3:45 azure linux-2core-8gb TestAccept
3:27 aws-windows-latest-4core-16gb TestAccept
3:27 azure-windows-latest-4core-16gb TestAccept
3:15 gcp-windows-latest-4core-16gb TestAccept

@rugpanov rugpanov left a comment

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.

[nice to have] This PR changes how the released Codex shim gets its context, so users can see the change. Codex now gets the context even when the user has their own ~/.codex/AGENTS.md. The rule in .agents/rules/changelog.md asks for a fragment for each user-visible change. Add a file under .nextchanges/cli/, like ssh-agents-dedicated.md.

return []string{"--append-system-prompt-file", f}, nil
}

func codexContextArg(home, systemContext string) ([]string, error) {

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.

[blocking] ug codex -- -c developer_instructions=... <args> always gives ug two or more passthrough args. At the pinned ugCommit, _smart_routing_launch_shape in src/ucode/cli.py treats a Codex launch as interactive only when it has no args or one explicit prompt. So _disable_smart_routing_for_subcommand turns off smart routing for every Codex session that starts through the shim, also for workspaces that turned it on. The tool_args[:1] == ["update"] checks in src/ucode/agents/codex.py also no longer match codex update. Agree with the Unity Gateway team on a supported way to add developer instructions, or make ug skip leading -c overrides when it picks the launch shape. Then add a Codex case to TestPrepareAgentLaunch.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

At the pinned ugCommit, _smart_routing_launch_shape in src/ucode/cli.py treats a Codex launch as interactive only when it has no args or one explicit prompt. So _disable_smart_routing_for_subcommand turns off smart routing for every Codex session that starts through the shim, also for workspaces that turned it on

Yeah this is a bad heuristic. There are plenty of flags a normal user would pass to an interactive session, and with this they are opted out of smart routing.

The tool_args[:1] == ["update"] checks in src/ucode/agents/codex.py also no longer match codex update

Will fix 👍

@rclarey
rclarey enabled auto-merge October 28, 2026 11:01
@rclarey
rclarey added this pull request to the merge queue Oct 28, 2026
Merged via the queue into main with commit 1d80643 Oct 28, 2026
24 checks passed
@rclarey
rclarey deleted the ssh-codex-developer-instructions branch October 28, 2026 11:45
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 1d80643

Run: 40682591677

Env 🔄​flaky ✅​pass 🙈​skip Time
🔄​ aws linux-2core-8gb 3 1495 1220 165:19
✅​ aws-windows-latest-4core-16gb 1423 1245 175:20
✅​ azure linux-2core-8gb 1345 1272 131:40
✅​ azure-windows-latest-4core-16gb 1270 1297 141:20
✅​ gcp linux-2core-8gb 1332 1277 145:36
✅​ gcp-windows-latest-4core-16gb 1257 1302 151:00
Test Name aws linux-2core-8gb
🔄​ TestAccept 🔄​f
🔄​ TestAccept/bundle/resources/postgres_synced_tables/recreate 🔄​f
🔄​ TestAccept/bundle/resources/postgres_synced_tables/recreate/DMS=true 🔄​f
Top 50 slowest tests (at least 2 minutes):
duration env testname
13:10 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
10:36 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=
9:39 aws linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
9:19 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
8:29 aws linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=true
8:13 aws linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
8:06 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
7:37 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DMS=true
7:34 aws linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
7:33 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DMS=
7:27 aws-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
7:26 aws-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
7:25 azure-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
7:24 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DMS=true
7:19 aws linux-2core-8gb TestAccept/bundle/invariant/no_drift/DMS=true/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
7:18 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
7:15 azure-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
7:13 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=true
7:13 azure linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
7:11 aws-windows-latest-4core-16gb TestAccept/bundle/invariant/destroy_idempotent/DMS=/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=1
7:09 aws linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
7:06 azure linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
6:54 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DMS=
6:52 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
6:51 aws linux-2core-8gb TestAccept/bundle/invariant/no_drift/DMS=true/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=
6:45 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
6:40 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
6:30 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/deploy/update-after-create/DMS=
6:30 aws linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=
6:26 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
6:16 azure linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
6:07 gcp linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=
6:06 azure linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
6:00 azure linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=
5:57 gcp linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=true
5:44 aws linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=
5:42 gcp linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
5:37 azure-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
5:35 aws linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=true
5:32 azure linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=true
5:29 gcp linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
5:22 azure-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
5:20 aws-windows-latest-4core-16gb TestAccept/bundle/deploy/spark-jar-task/DMS=true
5:15 aws-windows-latest-4core-16gb TestAccept/bundle/deploy/spark-jar-task/DMS=
5:13 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=true
5:10 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=
5:07 aws linux-2core-8gb TestAccept/bundle/deploy/spark-jar-task/DMS=true
5:02 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=true
4:51 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=
4:39 azure linux-2core-8gb TestAccept/bundle/deploy/spark-jar-task/DMS=

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.

4 participants