Skip to content

Commit 8263aee

Browse files
authored
feat: advance Codex to 0.144.5 and fix co-owned config.toml drift (#14)
Pin the tested Codex CLI to the official rust-v0.144.5 release (published 2026-07-16): version, tag, package checksums/sizes, and the builder config-schema link. install.sh/install.ps1 are byte-identical to 0.144.4, so those installer pins are unchanged. Module version 0.3.3 -> 0.3.4. Fix the project-trust drift lifecycle bug. config.toml is co-owned: the Codex runtime persists [projects."<workspace>"] trust into it at launch, which previously read as drift and could fail-close the next launch. Drift detection and the launch pre-check now verify the managed base keys are intact (a tomllib subset check) while tolerating the runtime's additions; a changed managed base key, or any change to AGENTS.md, is still drift. Claude-Session: https://claude.ai/code/session_01Y29c9sJRhChvxDcUjab5Xo
1 parent 3c77780 commit 8263aee

11 files changed

Lines changed: 120 additions & 47 deletions

File tree

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,26 @@
22

33
All notable changes to this project are documented here.
44

5+
## [0.3.4] - 2026-07-18
6+
7+
### Changed
8+
9+
- Tested Codex CLI advanced to the official `rust-v0.144.5` release
10+
(published 2026-07-16). All version pins, installer references, package
11+
checksums/sizes, and the builder's config-schema source link now point at
12+
0.144.5. The `install.sh`/`install.ps1` installers are byte-identical to
13+
0.144.4, so those pins are unchanged.
14+
15+
### Fixed
16+
17+
- `config.toml` is now treated as co-owned managed state. The Codex runtime
18+
persists project-trust decisions into it at launch (new
19+
`[projects."<workspace>"]` tables), which previously read as drift and could
20+
fail-close the next `launch`. Drift detection and the launch pre-check now
21+
verify the managed base keys are intact while tolerating the runtime's
22+
additions; a change to a managed base key, or any change to `AGENTS.md`, is
23+
still drift.
24+
525
## [0.3.3] - 2026-07-14
626

727
### Changed

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# NDDev Codex Setup Manager
22

33
`nddev-codex-app` is a dependency-free manager for a caller-selected Codex
4-
home. Version `0.3.3` installs the exact tested official Codex CLI standalone
4+
home. Version `0.3.4` installs the exact tested official Codex CLI standalone
55
release into that target and switches one of two complete NDDev configuration
66
sets without deleting unrelated target state. The repository also publishes
77
the independently installable `nddev-builder` Codex marketplace.
88

99
The current OpenAI desktop product is the ChatGPT app for macOS and Windows. It
1010
contains separate Chat plus selectable ChatGPT Work and Codex modes. Codex CLI
11-
`0.144.4` still implements `codex app` through legacy `Codex.app`/`Codex.dmg`
11+
`0.144.5` still implements `codex app` through legacy `Codex.app`/`Codex.dmg`
1212
packaging. This module does not manage either desktop bundle directly; on
1313
macOS, `desktop` is only a narrow delegation to that upstream command.
1414

@@ -42,7 +42,7 @@ or defaults to `~/.codex`.
4242
- Python directory-FD and no-follow filesystem operations for mutations
4343
- an absolute target whose parent already exists
4444

45-
This build installs and requires Codex CLI `0.144.4`. The configuration format
45+
This build installs and requires Codex CLI `0.144.5`. The configuration format
4646
remains compatible with Codex CLI `0.138.0` or newer.
4747

4848
## Install the official Codex CLI
@@ -56,7 +56,7 @@ python3 cli-tools/nddev_codex.py update-cli \
5656
--target /absolute/path/to/codex-home
5757
```
5858

59-
Install and update first download the pinned OpenAI `rust-v0.144.4`
59+
Install and update first download the pinned OpenAI `rust-v0.144.5`
6060
`install.sh` asset into a temporary directory, enforce its exact size and
6161
SHA-256, and invoke it without a shell pipeline. That verified official
6262
installer then downloads the pinned checksum manifest and host package and
@@ -72,7 +72,7 @@ symlinks, compatibility entrypoint, code-mode host, bundled ripgrep, Linux
7272
sandbox helper, executable ownership/modes, bounded `codex --version` output,
7373
the official temporary-directory PATH-alias diagnostic when present, and exact
7474
canonical stdout version. `install-cli` and `update-cli` are idempotent when
75-
`0.144.4` is already current. A different installed version must be advanced
75+
`0.144.5` is already current. A different installed version must be advanced
7676
with `update-cli`.
7777

7878
`software-status` is non-mutating and reports `installed`, `current`, `version`,
@@ -174,7 +174,7 @@ Both commands require the exact current target-owned CLI and never resolve
174174
setup and forwards Codex arguments unchanged. `desktop` invokes exactly
175175
`codex app` with no argument, or with one validated absolute workspace;
176176
arbitrary upstream installer/source flags are not exposed. It does not require
177-
a managed setup because Codex CLI `0.144.4` passes only a workspace URL to the
177+
a managed setup because Codex CLI `0.144.5` passes only a workspace URL to the
178178
GUI: setting `CODEX_HOME` for the bridge process is not a guarantee that the
179179
desktop application will inherit the selected target configuration. Both keep
180180
standard I/O attached and return the child exit code.

SECURITY.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ days. These targets are best-effort.
4646
they are not an administrator enforcement mechanism.
4747
- Permission profiles are a beta Codex surface whose configuration syntax is
4848
compatible from Codex CLI 0.138.0. This build installs, launches, and tests
49-
exactly 0.144.4.
49+
exactly 0.144.5.
5050
- `install-cli` and `update-cli` verify the exact pinned official
51-
`rust-v0.144.4` installer asset before execution. The official installer then
51+
`rust-v0.144.5` installer asset before execution. The official installer then
5252
downloads the pinned checksum manifest and host package and verifies their
5353
release digests and package checksum from isolated temporary state with a
5454
fixed release and install root. Abnormal installer exits terminate its whole

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.3.3
1+
0.3.4

build/manifest.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"location": "sibling .<target-name>.nddev-codex-backups",
66
"slots": 10
77
},
8-
"build_version": "0.3.3",
8+
"build_version": "0.3.4",
99
"command_policy": {
1010
"json_supported": [
1111
"list",
@@ -101,7 +101,7 @@
101101
"json_manager_errors_supported": true,
102102
"manager_command": "python3 cli-tools/nddev_codex.py launch --target <absolute-target> -- [args...]",
103103
"requires_clean_managed_target": true,
104-
"required_version": "0.144.4",
104+
"required_version": "0.144.5",
105105
"streams_child_stdio": true,
106106
"successful_child_output_wrapped": false,
107107
"target_environment_scope": "child-process-only",
@@ -134,10 +134,10 @@
134134
"installer": {
135135
"abnormal_exit_cleanup": "terminate-process-group-before-unlock",
136136
"asset": "install.sh",
137-
"release_tag": "rust-v0.144.4",
137+
"release_tag": "rust-v0.144.5",
138138
"sha256": "1154e9daf713aacd1534efca8042bfd6665ad24bc1d1dfd86b8f439fe60a7a5d",
139139
"size_bytes": 25133,
140-
"url": "https://github.com/openai/codex/releases/download/rust-v0.144.4/install.sh"
140+
"url": "https://github.com/openai/codex/releases/download/rust-v0.144.5/install.sh"
141141
},
142142
"layout": {
143143
"code_mode_host": "packages/standalone/current/bin/codex-code-mode-host",
@@ -154,7 +154,7 @@
154154
"macos",
155155
"linux"
156156
],
157-
"tested_version": "0.144.4"
157+
"tested_version": "0.144.5"
158158
},
159159
"transaction_policy": {
160160
"agents_override_blocked_commands": [

build/release-evidence.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@
22
"schema_version": 2,
33
"module": {
44
"repository": "NDDev-it-com/nddev-codex-app",
5-
"setup_digest": "sha256:7525d072d630848df5987a5c50d3dd2dc8bed5feac3d9c35b02a83384c7681ed"
5+
"setup_digest": "sha256:1343bef82ecf7eb773ec729d5b58956fcd0bf2617da184d41f91c453a3f7f129"
66
},
77
"harness": {
88
"repository": "NDDev-it-com/nddev-harnesses",
9-
"commit": "a598102aff438239c8efcd2c790cd31356278267"
9+
"commit": "6d3b932cf669b7acc24de11528ffb5271ca23556"
1010
},
1111
"adapter": {
1212
"id": "codex",
13-
"version": "0.3.3"
13+
"version": "0.3.4"
1414
},
1515
"vendor": {
16-
"cli_version": "0.144.4",
16+
"cli_version": "0.144.5",
1717
"permission_profiles_since": "0.138.0"
1818
},
1919
"platforms": [
@@ -29,8 +29,8 @@
2929
}
3030
],
3131
"lanes": [],
32-
"generated_at_utc": "2026-07-17T17:14:02Z",
33-
"expires_at_utc": "2027-01-13T17:14:02Z",
32+
"generated_at_utc": "2026-07-18T14:11:00Z",
33+
"expires_at_utc": "2027-01-14T14:11:00Z",
3434
"promotion": {
3535
"decision": "pending",
3636
"waivers": []

build/version.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"build_version": "0.3.3",
2+
"build_version": "0.3.4",
33
"codex_permission_profiles_since": "0.138.0",
4-
"codex_cli_tested": "0.144.4",
4+
"codex_cli_tested": "0.144.5",
55
"nddev_builder_plugin_version": "0.3.1",
66
"python_requires": ">=3.10",
77
"runtime_baseline_ref": "references/codex-baseline.json",

cli-tools/nddev_codex.py

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import sys
1919
import tempfile
2020
import time
21+
import tomllib
2122
import urllib.error
2223
import urllib.request
2324
from collections.abc import Iterator
@@ -42,7 +43,7 @@
4243
OWNER_DIRECTORY_MODE = 0o700
4344
METADATA_MAX_BYTES = 256 * 1024
4445
MANAGED_PAYLOAD_MAX_BYTES = 8 * 1024 * 1024
45-
TESTED_CODEX_VERSION = "0.144.4"
46+
TESTED_CODEX_VERSION = "0.144.5"
4647
INSTALLER_RELEASE_TAG = f"rust-v{TESTED_CODEX_VERSION}"
4748
INSTALLER_NAME = "install.sh"
4849
INSTALLER_URL = (
@@ -54,23 +55,23 @@
5455
f"https://api.github.com/repos/openai/codex/releases/tags/{INSTALLER_RELEASE_TAG}"
5556
)
5657
PACKAGE_CHECKSUM_ASSET = "codex-package_SHA256SUMS"
57-
PACKAGE_CHECKSUM_SHA256 = "09e949a00cbcbd95d5a3d3bd6c7647e38965e944a3f7d2eb148781d3a488414a"
58+
PACKAGE_CHECKSUM_SHA256 = "406f99fddeb6cdbce180f35dfdcf17014a0bde06f7c99d0e34be88660f22ab92"
5859
PACKAGE_ASSETS = {
5960
"aarch64-apple-darwin": (
6061
"codex-package-aarch64-apple-darwin.tar.gz",
61-
"312e6fa2826596fb23cc1193c30d51902b6522c36ccc43b36417590e0ebd533d",
62+
"8d1cd2d53b2070919d12c054b57485b6e08347e2666cb20932e9e95eb2aa2901",
6263
),
6364
"x86_64-apple-darwin": (
6465
"codex-package-x86_64-apple-darwin.tar.gz",
65-
"666671fbe761fe1fd57d74e725c5a306729f81f4fbefc16a01a0595e69864d41",
66+
"8322b8cf3747014b435318d2f34ee6d8f566b61360e5a770e30311cc0d90205b",
6667
),
6768
"aarch64-unknown-linux-musl": (
6869
"codex-package-aarch64-unknown-linux-musl.tar.gz",
69-
"b1bc561a5bf74f9c5767c698275ae8043cee2ce45341098048b0b0b8b4e2cfc5",
70+
"7703bbb6cbd4ba3df60c32d200bca2987691047353d3a6c825af2b8bc99f1808",
7071
),
7172
"x86_64-unknown-linux-musl": (
7273
"codex-package-x86_64-unknown-linux-musl.tar.gz",
73-
"d0a7bdb2ca821c9bb5f4cc2fb11a4ed96025db63b20d1bddf1e632361a108220",
74+
"23a7022a493c5404c50c62a4ad5655836adbee019d93c73114954d8daff20053",
7475
),
7576
}
7677
INSTALLER_MAX_BYTES = 64 * 1024
@@ -892,6 +893,46 @@ def load_stamp(target: Path) -> dict[str, Any] | None:
892893
return stamp
893894

894895

896+
def config_base_intact(current: bytes, base: bytes) -> bool:
897+
"""Return True when every top-level key the base config.toml declares is
898+
present in the current config.toml with an equal value.
899+
900+
config.toml is co-owned. The manager writes the setup base, but the Codex
901+
runtime persists project-trust decisions into it at launch as new
902+
``[projects."<workspace>"]`` tables (and comparable runtime state). Those
903+
additions must not read as drift, so the managed guarantee is scoped to the
904+
base keys the manager owns rather than to an exact byte image. Any change to
905+
an owned key -- or a config.toml that no longer parses -- is still drift.
906+
AGENTS.md stays byte-exact because the runtime never writes it.
907+
"""
908+
try:
909+
current_doc = tomllib.loads(current.decode("utf-8"))
910+
base_doc = tomllib.loads(base.decode("utf-8"))
911+
except (tomllib.TOMLDecodeError, UnicodeDecodeError, ValueError):
912+
return False
913+
return all(key in current_doc and current_doc[key] == value for key, value in base_doc.items())
914+
915+
916+
def _config_base_intact_on_disk(target: Path, setup_id: object) -> bool:
917+
"""Read the target's config.toml and confirm the managed base is intact,
918+
tolerating Codex's runtime additions. Any read/render failure is treated as
919+
drift (conservative)."""
920+
if not isinstance(setup_id, str):
921+
return False
922+
try:
923+
current, _ = read_target_file(
924+
target,
925+
"config.toml",
926+
f"managed path {target / 'config.toml'}",
927+
owner_only=False,
928+
max_bytes=MANAGED_PAYLOAD_MAX_BYTES,
929+
)
930+
_, rendered = render_setup(setup_id)
931+
except (SystemExit, OSError, ValueError):
932+
return False
933+
return config_base_intact(current, rendered["config.toml"])
934+
935+
895936
def inspect_target(target: Path) -> dict[str, Any]:
896937
if not ensure_target_directory(target, create=False):
897938
return {
@@ -928,12 +969,17 @@ def inspect_target(target: Path) -> dict[str, Any]:
928969
drift.append(name)
929970
continue
930971
owner_matches = not hasattr(os, "geteuid") or snapshot.owner == os.geteuid()
931-
if (
932-
snapshot.digest != expected[name]
933-
or snapshot.mode != OWNER_FILE_MODE
934-
or not owner_matches
935-
):
972+
if snapshot.mode != OWNER_FILE_MODE or not owner_matches:
936973
drift.append(name)
974+
continue
975+
if snapshot.digest == expected[name]:
976+
continue
977+
# config.toml is co-owned: the Codex runtime persists [projects.*] trust
978+
# into it at launch. Tolerate additions that leave the managed base
979+
# intact; a damaged base, or any change to AGENTS.md, is still drift.
980+
if name == "config.toml" and _config_base_intact_on_disk(target, stamp["setup_id"]):
981+
continue
982+
drift.append(name)
937983
stamp_snapshot = snapshot_target_file(target, STAMP_NAME, owner_only=False)
938984
if stamp_snapshot is None:
939985
drift.append(STAMP_NAME)
@@ -979,7 +1025,14 @@ def require_effective_clean_managed(target: Path) -> dict[str, Any]:
9791025
owner_only=True,
9801026
max_bytes=(METADATA_MAX_BYTES if name == STAMP_NAME else MANAGED_PAYLOAD_MAX_BYTES),
9811027
)
982-
if actual_content != expected_content:
1028+
# config.toml is co-owned: tolerate the Codex runtime's [projects.*]
1029+
# trust additions as long as the managed base is intact. AGENTS.md and
1030+
# the stamp stay byte-exact.
1031+
if name == "config.toml":
1032+
intact = config_base_intact(actual_content, expected_content)
1033+
else:
1034+
intact = actual_content == expected_content
1035+
if not intact:
9831036
fail(
9841037
"managed target is not the current canonical catalog setup; "
9851038
f"run apply --setup {setup_id} before launch"

config/nddev-contract.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
"json_manager_errors_supported": true,
8686
"manager_command": "python3 cli-tools/nddev_codex.py launch --target <absolute-target> -- [args...]",
8787
"requires_clean_managed_target": true,
88-
"required_version": "0.144.4",
88+
"required_version": "0.144.5",
8989
"streams_child_stdio": true,
9090
"successful_child_output_wrapped": false,
9191
"target_environment_scope": "child-process-only",
@@ -153,10 +153,10 @@
153153
"installer": {
154154
"abnormal_exit_cleanup": "terminate-process-group-before-unlock",
155155
"asset": "install.sh",
156-
"release_tag": "rust-v0.144.4",
156+
"release_tag": "rust-v0.144.5",
157157
"sha256": "1154e9daf713aacd1534efca8042bfd6665ad24bc1d1dfd86b8f439fe60a7a5d",
158158
"size_bytes": 25133,
159-
"url": "https://github.com/openai/codex/releases/download/rust-v0.144.4/install.sh"
159+
"url": "https://github.com/openai/codex/releases/download/rust-v0.144.5/install.sh"
160160
},
161161
"layout": {
162162
"code_mode_host": "packages/standalone/current/bin/codex-code-mode-host",
@@ -173,7 +173,7 @@
173173
"macos",
174174
"linux"
175175
],
176-
"tested_version": "0.144.4"
176+
"tested_version": "0.144.5"
177177
},
178178
"source_root": "setups",
179179
"version_ref": "build/version.json"

plugins/nddev-builder/scripts/check_codex_artifact.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -288,8 +288,8 @@
288288
"xxxxxxxx",
289289
}
290290

291-
# Top-level properties from the official Codex 0.144.4 ConfigToml schema:
292-
# https://github.com/openai/codex/blob/rust-v0.144.4/codex-rs/core/config.schema.json
291+
# Top-level properties from the official Codex 0.144.5 ConfigToml schema:
292+
# https://github.com/openai/codex/blob/rust-v0.144.5/codex-rs/core/config.schema.json
293293
# Validation is intentionally top-level only. Named tables such as
294294
# mcp_servers.<name>, permissions.<name>, plugins, marketplaces, projects, and
295295
# agents have dynamic keys and are validated by Codex or focused checkers.
@@ -2819,7 +2819,7 @@ def _validate_config_file(path: Path, *, budget: ScanBudget | None = None) -> Ar
28192819
path,
28202820
payload,
28212821
CONFIG_TOP_LEVEL_KEYS,
2822-
"Codex 0.144.4 config top-level",
2822+
"Codex 0.144.5 config top-level",
28232823
)
28242824
return report
28252825

0 commit comments

Comments
 (0)