Contributing
September 13, 2026 ยท View on GitHub
Thanks for helping improve codex-profiles.
Local setup
git clone https://github.com/Ducksss/codex-profiles.git
cd codex-profiles
make check
make check is the canonical local gate: it runs syntax validation, all Bash
and Node behavior suites, and ShellCheck from one deterministic file inventory.
If ShellCheck is unavailable, run make test and state the missing lint result
explicitly in the pull request.
Product contract
Changes must preserve the distinction between two scopes:
cli,login,env, anduseselect Codex-local state throughCODEX_HOME. They do not switch ChatGPT Desktop.app defaultpreserves the stock ChatGPT session and uses~/.codex.- A named
app <profile>launch uses matchingCODEX_HOMEand separate Electron state for that named ChatGPT window across Chat, Work, and Codex. - The tool does not inspect or claim equality between CLI and Desktop accounts.
- Local-state separation is not an account, OS, or server-side boundary.
The Desktop launcher must use the original signed application. Do not add app cloning, bundle patching, ad-hoc signing, global app quitting, broad process killing, token copying, or cookie migration.
Development guidelines
- Keep the distributed CLI dependency-free: Bash plus standard macOS/POSIX tools only.
- Keep macOS-only behavior inside the Desktop launcher.
- Preserve
default -> ~/.codexand<name> -> ~/.codex-<name>. - Treat
--instance,--rebuild, andapp-instanceas compatibility spellings, not distinct launch modes. - Do not read, copy, print, parse, upload, compare, or migrate authentication tokens or ChatGPT cookies. Do not inspect or compare account identifiers.
- Keep human and JSON output explicit about whether a fact concerns Codex-local state or a ChatGPT Desktop window.
- Add regression tests for every observable behavior change.
- Update CLI help, README, completions,
docs/llms.txt, structured data, andCHANGELOG.mdtogether when the public surface changes. - Keep
bin/codex-profile, npm/package-lock, docs, PKGBUILD, and.SRCINFOversions in sync. A release also requires a dated changelog section.
Repository automation and test placement
- Keep installed behavior in the single
bin/codex-profilefile. Repository automation belongs underscripts/and must not enter the npm package. scripts/checkowns source and test discovery. Make, npm, CI, and release verification delegate to it rather than copying file lists.- Put tests beside their responsibility:
test/cli,test/install,test/packaging,test/release,test/site, ortest/outreach. - Put only reusable test infrastructure in
test/lib; product expectations stay in the owning suite. Tests must be independently runnable and use disposable state. - Keep GitHub workflow files focused on permissions, secrets, runner setup, and
sequencing. Release behavior belongs in directly tested
scripts/releasecommands. scripts/aur/prepare.shandscripts/aur/verify.shnever publish. The AUR credential, reviewed commit, and push remain maintainer-owned steps in the AUR runbook.
Useful focused commands include:
bash test/cli/workspace-test.sh
bash test/release/npm-test.sh
node test/packaging/aur-test.mjs
node test/site/geo-test.mjs
scripts/check list
Testing Desktop changes
Automated tests should use disposable fake app bundles and temporary homes. They must assert exact arguments and environment without opening or modifying a real installed app.
The following six-case matrix is required before a live release unless the maintainer explicitly waives it using the release input documented below. Test the current signed ChatGPT app with non-sensitive accounts:
- Confirm
app defaultkeeps the existing stock session. - Confirm a named profile persists across relaunches.
- Confirm two different names can run concurrently without state crossover.
- Confirm Chat, Work, and Codex remain in the same named window context.
- Confirm CLI actions do not switch any open Desktop window.
- Confirm the installed app bundle remains byte-for-byte untouched by the launcher.
Never publish screenshots, logs, account names, histories, or tokens from this test without explicit sanitization.
Release rehearsal and attestation
Dispatch the Release workflow from the current main commit with the exact
tracked version. Its default dry_run: true path requires no Desktop
attestation and makes no tag, registry, release, tap, or Pages changes. The
always-run rehearsal invokes the same canonical make check gate used locally,
then requires a clean checkout with no unstaged, staged, or untracked artifacts.
This verification job has read-only repository permissions, and its checkout
does not persist a GitHub credential.
After the six cases above pass, dispatch with dry_run: false and set
desktop_smoke_attestation to exactly two public app facts, in this form:
ChatGPT version 1.2026.168; bundle ID com.openai.codex
Substitute the installed public values. The version must have two or three
dot-separated numeric components. The bundle ID must begin com.openai. and
use only alphanumeric segments separated by dots or hyphens. Leading or trailing
whitespace, newlines, and extra text are rejected. Do not add an account name or
identifier, email address, screenshot, token, cookie, history, log, private
path, or other account data. The workflow converts the validated value to a
single escaped summary line and rejects a missing or malformed attestation
before any live release step.
If the maintainer explicitly chooses to publish without completing the Desktop
checks, use the exact value waived-by-maintainer instead. This records the
checks as unverified in the workflow summary and public release notes; it
never claims the checks passed. The waiver does not skip automated verification
or publication checks. Separately, skip_homebrew: true skips the tap update
and its formula validation, leaving the tap unchanged. Leave the Desktop input
empty to keep failing closed.
The separate live-only job receives the write permissions. Before its first
external mutation, it checks out the verified commit again, preflights the npm
token's owner identity and the tap token's reported GitHub account access, then
re-fetches the current origin/main tip and exact remote tag state immediately
before tagging. Stale verification outputs cannot authorize a release after
either has moved. These read-only identity checks cannot prove a granular
token's write scope: configure NPM_TOKEN for codex-profile publication and
use a dedicated fine-grained TAP_TOKEN limited to Ducksss/homebrew-tap with
Contents read/write. Do not reuse a broad personal or organization token; the
actual npm publish and tap push remain the authoritative write checks.
npm publish can finish before the package becomes publicly available: npm's
publish-time scanning
typically takes about five minutes and can take 15 minutes or more. Registry
metadata can appear before the package tarball is downloadable. The workflow
therefore verifies metadata and public installation in separate stages, each
with its own 20-minute wait budget: 41 attempts with 40 sleeps of 30 seconds.
Metadata verification requires the registry's exact SHA-512 integrity to match
the release artifact. Installation verification uses a fresh prefix and checks
help and version through both command aliases. On either timeout, investigate
npm review notifications and registry status, then rerun the same immutable
release once the exact artifact is publicly installable. Never bypass checks or
move tags to recover.
The workflow then verifies the exact GitHub Release tag, downloads install.sh
from that immutable tag, and exercises its public releases/latest path in
fresh prefixes until both aliases report the exact version and the plural alias
is the expected relative symlink. The workflow also requires a newly created
Pages run from the tag to succeed and polls the public site for the exact visible
version. Homebrew formula validation completes before the tap push. Tracked AUR
metadata and tagged files are validated fail-closed here, but publication to the
external AUR repository remains a maintainer action.
Maintainers performing that handoff must follow the
AUR publication and update runbook, including its
dedicated-key, immutable-tag, clean-build, and public-verification requirements.
Pull requests
Before opening a pull request:
make check
If ShellCheck is unavailable, state that clearly. The pull request must include what changed, why it changed, tests run, supported platforms, and any migration or compatibility impact. Use the repository template and identify whether the change affects Codex-local state, Desktop state, or both.
Security
Do not paste real auth files, access tokens, OAuth codes, cookies, connector credentials, private logs, or account identifiers into issues, discussions, pull requests, or test fixtures. Follow SECURITY.md for private vulnerability reporting.