[codex] Refactor patch registry descriptors#167
Conversation
ilysenko
left a comment
There was a problem hiding this comment.
Review Mate: merge not recommended yet
🔴 Recommendation: merge not recommended yet
Risk level: high
Do not merge until the flagged risks are addressed.
This PR restructures the Linux ASAR patch registry into auto-discovered descriptor files and also carries a Nix fixed-output payload/hash cleanup in the same change set.
What changed
The patcher source of truth moves from a central registry model toward scripts/patches/core/**/patch.js descriptors driven by a new scripts/patches/engine.js, with scripts/patches/registry.js becoming orchestration. The PR also changes the Nix installer path in flake.nix and references changes to native-module payload normalization.
Why it matters
This is in the repo’s most fragile area: patch ordering, CI policy, target filtering, and patch-report metadata. Even if the descriptor engine is functionally equivalent, this is a broad patcher refactor rather than a narrow bug fix, and the PR also mixes in an unrelated Nix payload fix. That combination puts it above the auto-merge bar for this project.
Notes for the reviewer
I could not run local git diff inspection because the read-only command sandbox fails before git starts with the host’s disabled unprivileged user namespace setup. The risk call is therefore based on the supplied PR metadata and visible diff excerpt, not a full local line-by-line audit.
Risks
- High-risk patcher refactor: the PR changes the patch registry architecture and introduces auto-discovered core descriptors under
scripts/patches/core/**/patch.js, which directly affects patch order, CI policy, and patch-report behavior in the hot patcher area. - Scope-creep risk: the PR combines the patch registry descriptor refactor with a separate Nix fixed-output payload/path fix, which matches the maintainer’s recurring rejection pattern for unrelated refactor plus fix in one PR.
- Needs human validation that every moved descriptor preserves previous ordering, required-upstream policy, idempotency behavior, and smoke-fixture expectations across main-process, webview, Computer Use, keybinds, launch-actions, and package-json patches.
- Local full-diff verification was blocked by the execution sandbox, so caller checks, generated/source-of-truth checks, and exact smoke-test alignment could not be independently confirmed.
References
AGENTS.md:56AGENTS.md:57AGENTS.md:58AGENTS.md:144AGENTS.md:194flake.nix:86scripts/patches/registry.js:1scripts/patches/engine.js:1scripts/patches/core/all-linux/patch.js:1
Second opinion (Claude)
Risk level: high
This PR converts the shipped Linux ASAR patch registry from explicit lists in scripts/patches/registry.js to filesystem-discovered descriptors under scripts/patches/core/**/patch.js, grouped by target namespace (all-linux, distro, package, desktop). It introduces scripts/patches/engine.js (descriptor discovery, normalization, duplicate-id check, target-filter dispatch, structured patch-report metadata) and a new scripts/lib/linux-target-context.js for distro/package/desktop matching. The same PR also rewrites the Nix codexDesktopPayload install path: it prunes native-module build artifacts, sets deterministic build/remap flags (SOURCE_DATE_EPOCH, -ffile-prefix-map=..., --remap-path-prefix=..., -Wl,--build-id=none), and bumps the pinned recursive outputHash.
What changed
scripts/patches/registry.js— orchestrates discovered core descriptors viaengine.js; legacy export surface (MAIN_BUNDLE_PATCHES,WEBVIEW_ASSET_PATCHES,COMPUTER_USE_UI_ASSET_PATCHES,corePatchDescriptors,requiredPatchNamesForProfile) preserved.scripts/patches/engine.js— new descriptor engine (discover / normalize / sort / dispatch by phase).scripts/patches/core/all-linux/{main-process,webview,extracted-app}/*/patch.js— twelve new descriptor files, each delegating to the existingapply*functions inmain-process.js,webview-assets.js,computer-use.js, etc.scripts/lib/linux-target-context.js— new target-detection helpers (matchesId,packageFormatIs,desktopMatches,versionAtLeast).scripts/patch-linux-window-ui.test.js,tests/scripts_smoke.sh— tests extended for descriptor discovery, duplicate-id rejection, target-filter skip, and the new core/* + lib/linux-target-context.js packaged files.scripts/lib/package-common.sh:179— copieslinux-target-context.jsinto the update-builder payload (descriptors come along via the existingcp -r scripts/patches/.line).scripts/ci/validate-patch-report.js— unchanged in behavior; consumesrequiredPatchNamesForProfile(now backed by descriptors).flake.nix—codexDesktopPayloadinstall phase adds prune + deterministic flags;outputHashrefreshed (sha256-efRH4r4KIIsvAxMyzy2sd+Cezv1N5zKKEWyz+6vMc5Y=); two# shellcheck disable=SC2016comments added; newpatchNixGeneratedScriptshelper used inside the payload.AGENTS.md— updated to point atscripts/patches/core/**/patch.jsas the new source of truth for shipped Linux patches.
Why it matters
This touches the project's most refactor-hostile area. The maintainer's standing position from AGENTS.md / past PR history is that hot patchers (scripts/patches/*.js) should receive bug-fix-only changes — refactors that wrap fixes in generator patterns have been rejected. This PR is a pure refactor, but it (a) restructures the source-of-truth boundary for shipped patches (now scripts/patches/core/**/patch.js rather than registry.js), (b) touches all currently shipped patcher entrypoints simultaneously by re-homing them into descriptor files, and (c) bundles an unrelated Nix payload concern (artifact pruning + deterministic flags + recursive hash refresh) into the same diff. Any of these warrants its own review pass; together they substantially raise the diff's blast radius across all five packaging paths.
The descriptor model itself looks well-formed — engine.js enforces unique ids, sorts by (order, sourcePath, id), threads appliesTo(context) for distro/package/desktop self-filtering, and records skipped-target for filtered patches with targetSummary metadata. The main-process-ui aggregate report entry is preserved so existing CI consumers don't break. Re-exports through patch-linux-window-ui.js keep the old test/CLI surface working. Smoke and node tests are updated for the new layout. The behavioral risk is mostly schema- and ordering-stability around patch-report.json (new linuxTarget, new per-patch phase / targetSummary fields) and around the new fail-fast assertUniquePatchIds (a future patch with a duplicate id now throws instead of warning).
Notes for the reviewer
- The Nix payload changes (
flake.nix:182-269) are conceptually independent of the registry refactor. Consider asking the author to split into two PRs — registry refactor vs. payload determinism — to make the patcher half easier to validate and the Nix half easier to revert if it breaksnix buildin CI. - The recursive payload
outputHashwas refreshed (flake.nix:205); the upstreamcodexDmghash onflake.nix:25was not touched in this PR but should still validate because the DMG URL is unchanged. There are no separate pinned Cargo / Node / Electron hashes in this flake (it uses__structuredAttrs+outputHashMode = recursive), so the single hash refresh appears sufficient — worth confirming the maintainer's hash-refresh checklist matches. discoverCorePatchDescriptorswalks the filesystem at every call site (e.g.mainBundlePatchDescriptors,patchExtractedAppcalls it twice, plusEXPORTED_CORE_PATCHESat module load). Not a correctness issue, but it means a missing/broken descriptor will throw at registry require-time rather than at apply-time — verify upstream regenerate flows can tolerate that.legacyCorePatchDescriptorsis exported but only delegates tocorePatchDescriptors— looks like a transitional shim with no current external caller; safe to drop in a follow-up.tests/scripts_smoke.sh:154-156asserts existence ofcore/distro/nixos/README.md,core/desktop/i3/README.md,core/package/deb/README.md— confirm these placeholder READMEs are present in the diff (the empty namespaces are the contract surface for future distro-specific patches).
Risks
- Hot patcher refactor risk: this restructures
scripts/patches/registry.jsand re-homes every shipped patch intoscripts/patches/core/**/patch.js. Past project memory (andAGENTS.md) flags refactors of this area as the most common rejection reason — anchored expressions inmain-process.js/webview-assets.jsare kept unchanged but the surrounding orchestration is rewritten, increasing rebase friction with upstream syncs. - Scope creep: the PR bundles a registry refactor with an unrelated Nix payload restructure (artifact pruning, deterministic compile flags,
outputHashrefresh) inflake.nix:211-269. Per project convention, mixed-scope PRs are routinely split or bounced — these two changes have independent failure modes and should be reviewable independently. - Source-of-truth boundary shift:
AGENTS.mdis updated to say shipped Linux patches now live underscripts/patches/core/, whileregistry.jskeeps re-exporting the legacy constants. Future contributors may be confused about where to add new core patches if both surfaces remain exported. - New strict invariant:
engine.js::assertUniquePatchIdsnow throws on duplicate ids (scripts/patches/engine.js:90). This is stricter than the pre-refactor behavior; if a feature/* or future descriptor inadvertently reuses an id, the entire patch run now hard-fails instead of warning. patch-report.jsonschema additions (linuxTargetobject on the report, per-patchphase+targetSummary) may surprise consumers that pin fields. Smoke andvalidate-patch-report.jsare unaffected because they read by name/status, but downstream rebuild-report consumers should be checked.- Filesystem discovery walks
scripts/patches/core/synchronously on require (registry.js:224 EXPORTED_CORE_PATCHES = corePatchDescriptors()) and at every patch invocation. Errors in any descriptor file now manifest at module load time, which can mask the actual cause behind a genericrequire()throw. - Nix
outputHashrefresh (flake.nix:205) was generated against the new pruned/deterministic install path; if the prune step or any of the new compile flags differ between CI sandbox modes, the hash will drift andnix buildwill fail reproducibly elsewhere.
🤖 Reviewed by codex + claude.
Summary
Refactors the shipped Linux ASAR patch registry into auto-discovered patch descriptors under
scripts/patches/core/**/patch.js, with descriptors grouped by target namespace (all-linux,distro,package,desktop). Adds a shared descriptor engine, Linux target detection helpers, and structured patch-report target metadata so future distro/package/desktop-specific patches can self-filter without touching the rest of the registry.Also fixes the Nix fixed-output payload path after the update-builder contents changed. The payload now prunes nondeterministic native-module build artifacts, sets deterministic build/remap flags, refreshes the pinned recursive
outputHash, and keeps the Nix installer shellcheck-clean.Validation
node --test scripts/patch-linux-window-ui.test.jsnode --test linux-features/*/test.jsbash -n scripts/lib/native-modules.shbash tests/scripts_smoke.shgit diff --checknix build .#codex-desktop .#installer --no-link --print-build-logs --option sandbox falseNotes
The two-hour
update-codex-hash.ymlworkflow runsscripts/ci/update-nix-hashes.shagainst.#codex-desktop; that path now accepts the refreshed fixed-output payload hash in a clean Nix container.