Your Codex session costs ~5,000 tokens before you type a character. codex-eco measures that with a free offline audit, cuts 35–59% of it with settings verified against your own Codex build, and installs behavioural rules through the one channel that costs nothing to deliver — 16% cheaper threads, 37% fewer output tokens, no preamble turn, both planted bugs still found in every run.
The interesting part is the measurement that told us the obvious channel was the wrong one. Two of this project's own studies are retracted in public, and the design changed because of it.
Works in Codex CLI and in Codex inside the ChatGPT desktop app. One install covers both.
git clone https://github.com/sup3x/codex-eco && cd codex-eco && ./install.shWindows: .\install.ps1.
That is the whole setup. There is nothing to invoke — the rules go into $CODEX_HOME/AGENTS.md, which Codex loads into the prompt on every turn, at every reasoning effort, on every model, in the CLI and in the desktop app alike. Open a new session and it is on.
$ ./install.sh
codex-eco
rules: /home/you/.codex/AGENTS.md (global, short block)
skills dir: /home/you/.agents/skills (default)
rules: block appended to /home/you/.codex/AGENTS.md (short; previous file at .../.eco-backups/AGENTS.md-20260817-135234)
eco: installed and verified (3 files)
eco-max: installed and verified (2 files)
The block is delimited by <!-- codex-eco:start --> / <!-- codex-eco:end -->, so re-running replaces it in place instead of appending a second copy, --uninstall removes exactly it and leaves the rest of your file untouched, and anything about to change is copied to .eco-backups/ first. Both installers produce byte-identical files.
| What | When | |
|---|---|---|
./install.sh |
rules globally + both skills | the default |
./install.sh --project |
rules into this repo's AGENTS.md |
you want it per-project, or in a repo you share |
./install.sh --full |
the complete 3.6 kB rule block instead of the 1.1 kB one | you would rather have every rule than the cheapest block |
./install.sh --rules-only / --skills-only |
one half | you only want one |
./install.sh --uninstall |
removes the block and the skills |
The skills stay available for two jobs the rules file cannot do:
| Codex CLI | Codex in the ChatGPT desktop app | |
|---|---|---|
| Configure your setup | $eco setup |
@eco setup |
| Turn the mode on for one thread | $eco <task> |
@eco <task> |
Invoking a skill costs one extra shell round trip, for a reason worth understanding before you rely on it — see why the rules do not live in the skill.
This is the project's main finding, and it cost a retraction to learn.
Codex publishes a skill to the model as one catalogue line — name, description, and a path:
- eco: Token-frugal mode for Codex - fewer tokens per turn ... (file: ~/.agents/skills/eco/SKILL.md)
The body is not in the prompt. You can see this for yourself without spending a token, because
codex debug prompt-input renders the exact model-visible input list offline:
codex debug prompt-input '$eco review src/app.ts' > with.json
codex debug prompt-input 'review src/app.ts' > without.json
diff with.json without.json # they differ by five bytes: the literal "$eco "Three consequences follow, and all three are measurable:
- The rules only apply after the agent reads the file. That read is a shell command — a full extra
round trip, and a round trip re-sends the entire prefix. Measured on
gpt-5.6-terra, invoking$ecoraised cached input from 28.2k to 43.7k tokens on a single-turn task. - The one rule with the largest measured effect cannot work through a skill at all. "Your first output is a tool call, not an announcement" is violated before the body is read: at the moment the model decides to invoke a skill, it has seen only the description. In 5/5 runs it announced the mode, then read the rule telling it not to. No wording fixes this; it is the invocation order.
- Whether the body gets read is a model decision, not a guarantee. Re-scanning every run this
project ever recorded for a command touching
SKILL.mdfound 10/10 reads in some batches and 1/20 in others. Two whole studies had therefore compared a baseline against itself, and their results are retracted inbench/preregistration/001-first-study.mdrather than quietly deleted.
AGENTS.md has none of these properties. Codex injects it verbatim, inside <INSTRUCTIONS>, with no
round trip and no decision to make — confirmed the same free offline way, by planting a marker string in
the file and finding it in the rendered prompt. That is why the installer's primary act is to write the
block, and why the skill is documented as the secondary path.
What the skill is still for. $eco setup — reading your config, proposing the levers, applying
nothing without confirmation — is a one-shot job where an extra round trip is irrelevant. And invoking
$eco mid-thread is the only way to turn the discipline on in a repository whose AGENTS.md you do not
control.
Keeping these apart is the difference between an honest claim and a marketing number:
| How it is measured | How stable it is | |
|---|---|---|
| The fixed prefix — the skills catalogue, the plugin advert, the instruction prose sent before you type | codex debug prompt-input, offline, no model call |
Deterministic. Run it twice, get the same bytes. Reported as exact character counts. |
| The rules' effect on behaviour | A live model, n=5 per arm, arms interleaved in one batch, deterministic grading | Noisy. On this task a single batch cannot settle a direction, so the bar is the direction repeating across independent batches and the published effect is a range. |
The audit is the part you can verify on your own machine in one second. The behavioural numbers are the part this repository argues about at length, in the open, including where they came out against us.
The most useful thing in this repository costs nothing to run and makes no model call:
node scripts/prefix-audit.mjssection chars ~tokens
-------------------------------------------------
skills catalog 15,211 3,803
recommended-plugins advert 2,371 593
core instruction prose 2,269 567
multi-agent mode note 271 68
-------------------------------------------------
TOTAL before you type 20,122 5,031
configuration chars ~tokens change
-------------------------------------------------------------
as configured now 20,122 5,031 -
eco profile (safe) 13,166 3,292 −34.6%
eco profile (aggressive) 8,250 2,063 −59.0%
Those are real numbers from one machine, produced by codex debug prompt-input — the exact item list Codex will send. Run it in your own project and you get your own numbers, including what your AGENTS.md costs. Every key the audit suggests is validated against your Codex build with codex mcp-server --strict-config before it is offered, so a typo can never masquerade as a saving.
| Component | What it does |
|---|---|
AGENTS.eco.lean.md |
The rules block the installer writes by default: 1.1 kB, hand-curated down to the lines that carried the measured effect. AGENTS.md is re-sent on every request, so this file's size is a per-turn cost — CI fails if it grows past 1,600 bytes. |
AGENTS.eco.md |
The complete rule block, 3.6 kB, generated from the eco skill body so the two cannot drift. ./install.sh --full installs this instead. |
eco skill |
$eco setup reads your config, proposes the levers and applies nothing without confirmation. Invoking $eco <task> turns the discipline on inside one thread, at the cost of one round trip. |
eco-max skill |
The same rules at the tightest reply budget, for routine chores. Generated from eco. |
profiles/eco.config.toml |
The safe prefix tier: four verified settings plus two caps. codex --profile eco. |
profiles/eco-max.config.toml |
Adds a reasoning-effort floor and the aggressive prefix tier. |
scripts/prefix-audit.mjs |
The free, offline audit below. Validates every key it suggests against your own Codex build before offering it. |
scripts/cost-report.mjs |
Re-scores any recorded batch on what a turn actually bills, not just output tokens. |
bench/ |
The harness, the deterministic grader, the provenance manifest, and the pre-registrations — including the retractions. |
On codex-cli 0.147.0, with 21 skills in the catalogue, the instruction prefix sent before you type is 20,122 characters (~5,031 tokens). The safe profile takes it to 13,166 (-34.6%) and the aggressive one to 8,250 (-59.0%). Reproduce it on your own machine in one command: node scripts/prefix-audit.mjs.
gpt-5.6-terra, the model's default effort, n=5 per arm, arms interleaved in one batch. Each run is one three-turn thread — review, patch, open question — with usage summed over the thread.
| Arm | cost | vs baseline | output | cmds | preamble | both bugs |
|---|---|---|---|---|---|---|
no rules |
49,818 | — | 1,993 | 1.4 | 1.00 | 5/5 |
$eco skill |
63,471 | +27.4% (95% CI 19.1% .. 37.7%, p = 0.008) | 2,462 | 2.0 | 1.00 | 5/5 |
AGENTS.md full |
45,601 | −8.5% (95% CI −18.8% .. 3.7%, p = 0.222) | 1,328 | 2.0 | 0.00 | 5/5 |
AGENTS.md short |
41,856 | −16.0% (95% CI −26.1% .. −5.6%, p = 0.032) | 1,248 | 1.4 | 0.00 | 5/5 |
Every arm found both planted bugs in every run, so cheapness decides. The short block wins; the $eco skill loses significantly — why is above.
The shipped block was run against no rules in 7 independent batches on gpt-5.6-terra (n=3 per arm): 6/7 batches moved the same way, two-sided sign test p = 0.125. Across the 5 levels whose batches agree, the effect ran from −7.0% to −25.1%; none is unresolved and is described below. Both planted bugs were found at every level in every run. The published number is that range, not any one batch.
The trend is clear and its mechanism is plausible: the higher the effort, the longer the baseline's output, so the more fat there is to cut.
| effort | cost | output | 95% CI | both bugs |
|---|---|---|---|---|
none #1 |
+34.1% | −27.4% | −16.1% … 112.9% | yes |
none #2 |
−9.1% | −23.1% | −45.5% … 47.2% | yes |
low |
−7.0% | −24.7% | −19.2% … 1.5% | yes |
medium |
−14.8% | −20.4% | −23.5% … −5.0% | yes |
high |
−25.1% | −35.3% | −37.9% … −17.4% | yes |
xhigh |
−18.6% | −18.0% | −47.3% … 29.5% | yes |
max |
−24.1% | −17.6% | −29.2% … −18.2% | yes |
At none, total cost is unresolved. The 2 independent batches disagree (+34.1%, −9.1%), and the difference sits entirely in the cached/uncached split: with the same block, one batch billed 53,155 uncached tokens and the other 24,865. So the positive figure in the first batch was cache warmth, not a treatment effect — and a single batch would have "shown" the block to be harmful there. Output tokens fell consistently in both (−27.4% and −23.1%), and both planted bugs were found in every run. The total-cost claim therefore covers low and above; for none the honest word is unresolved, not worse.
| Model | n | cost | output | cmds | preamble | both bugs |
|---|---|---|---|---|---|---|
gpt-5.6-terra |
5 | −16.0% | −37.4% | 1.4 → 1.4 | 1.00 → 0.00 | yes |
gpt-5.6-sol |
3 | −14.5% | −34.4% | 3.0 → 2.0 | 1.00 → 0.00 | yes |
gpt-5.6-luna |
3 | −40.4% | −27.9% | 2.0 → 1.0 | 1.00 → 0.00 | yes |
gpt-5.5 |
3 | −23.3% | −45.0% | 3.7 → 1.3 | 1.00 → 0.00 | yes |
gpt-5.4-mini |
3 | −18.0% | −34.4% | 4.0 → 1.3 | 1.00 → 0.00 | yes |
gpt-5.4 |
3 | −20.2% | −26.6% | 4.0 → 1.0 | 1.00 → 0.00 | yes |
6/6 models moved the same way, two-sided sign test p = 0.03125, effect between −14.5% and −40.4%. The preamble turn went to zero on every model, and both planted bugs were found in every run of every model. Absolute counts are not comparable across models — different tokenizers — so what is compared is the percentage within a row.
Every rule exists because the unarmed agent was observed doing the thing it forbids, in a real transcript:
- The preamble turn. Codex opens with a message announcing what it is about to do — "I'll inspect the test file and its nearby project context, then summarize" — and only then runs a command. That is billed output that moves no work forward, and it is the single most reliable effect in this repository: 1.00 preambles per run without the block, 0.00 with it, in every batch measured. An earlier attempt to pick the best wording for this rule by A/B-testing four phrasings is retracted — 19 of its 20 runs never loaded the rules at all — so the wording shipped is the one that was measured working, not one that won a comparison.
- The unasked survey. Asked to review one file, the unarmed agent also ran
Get-ChildItem -Forceand a tree-widerg -n "orders" .— a directory listing and a full-tree grep nobody requested. On the three-turn thread the block keeps the command count at the baseline's 1.4 while cutting output 37%, so it is removing waste rather than trading one cost for another. - Whole-file dumps. Codex has no editor tools; everything is a shell command. So the rules are about command hygiene — ask for the region (
sed -n,Get-Content -TotalCount),rg -lbeforerg -n, batch independent commands into one call,apply_patchinstead of rewriting a file through the shell. - Thread growth. Codex has a
new_contexttool the model itself can call, and its own guidance says compactions can cost accuracy. The rules say: start a fresh context when the history stops mattering, and never switch model or effort mid-thread — measured, that drops the cached-prefix ratio from 0.95 to 0.07.
Published Codex guides recommend all of these. None of them saves anything on Codex 0.147:
| Recommendation | Reality |
|---|---|
model_reasoning_summary = "none" |
Every current model already defaults to none. Zero change. |
model_verbosity = "low" |
Already the default on every current model except gpt-5.4-mini. Zero change for almost everyone. |
hide_agent_reasoning / show_raw_agent_reasoning |
Display-only. Measured identical prompt, byte for byte. |
features.token_budget |
Under development, and enabling it adds ~1,858 characters of guidance to your prompt. |
model_supports_reasoning_summaries |
In the official sample config; rejected as an unknown field by the installed binary. |
minimal reasoning effort |
The CLI accepts any string silently, and the request then fails. Verified on gpt-5.6-terra, which answers with HTTP 400 and enumerates what it does take: "Unsupported value: 'minimal' is not supported ... Supported values are: 'none', 'low', 'medium', 'high', 'xhigh', and 'max'." So the real floor is none, not low — it runs, and it produces zero reasoning tokens. |
Lowering model_context_window to shrink the skills block |
It works (−1,883 chars at 100k) but also lowers the auto-compact trigger, and compaction is a total cache kill. Net negative. |
That table is the reason this project exists in the form it does: on Codex it is easy to publish a configuration that feels frugal and measurably is not.
- Codex's own validators gate the repository. Codex 0.147 ships
skill-creatorandplugin-creatoras system skills on disk, with executable validators.plugins/eco/skills/*passesquick_validate.pyandplugins/ecopassesvalidate_plugin.py. That is how we learnedargument-hint— copied from the Claude Code port — is not a Codex field at all. - Prompt sizes come from
codex debug prompt-input, not from an estimate. Token figures are marked as~because they are chars/4; character counts are exact. - Config keys are validated with
codex mcp-server --strict-configbefore being recommended. Codex silently ignores unknown keys, so this is the only way to know a setting is real. - Quality is graded deterministically.
bench/lib/grade.mjsscores each answer for the planted bugs with no model in the loop. It also has a documented false-negative it caused and how that was caught — see Amendment 2 in the pre-registration. - Rule changes are pre-registered. Endpoints and thresholds are written down before the runs, and the failures are published with the successes.
- The experiment is checked before it is run.
bench/lib/preflight.mjsrenders the batch's own prompt withcodex debug prompt-inputand refuses to start if a staged skill name does not resolve to exactly one file inside the staged workspace. It was written after that check would have failed every batch this project had run: a stale copy ofecoin$HOME/.agents/skillshad been appearing in the catalogue beside the copy under test. - A grading change is applied to every stored run at once.
scripts/regrade.mjsre-grades all recorded event streams and prints a per-arm before/after, so widening a criterion cannot quietly help one arm. CI fails if any committed summary disagrees with what the current rubric produces. - Charts and tables are generated, never written.
scripts/build-charts.mjsrenders both languages from the recorded data, and--checkfails CI if a committed SVG or results table drifts from it. The chart renderer throws when a label would be clipped rather than shipping a truncated number. - Nothing reaches a README without passing the headline gate.
bench/headline.jsonnames the studies allowed to produce a claim. Two studies in this project were retracted after the fact; the gate is what kept them out of the numbers while they were still believed. - No dollar figures, ever. The
codex execevent stream contains no cost field. On a ChatGPT plan the currency is your rate limit, so this project reports tokens.
./install.sh --rules-only # $CODEX_HOME/AGENTS.md, or ~/.codex/AGENTS.md
./install.sh --rules-only --project # ./AGENTS.md in the repo you are standing in
./install.sh --rules-only --full # the 3.6 kB block instead of the 1.1 kB one
./install.sh --rules-only --uninstallCodex loads AGENTS.md from the global $CODEX_HOME and from the project you are working in, both
into the same <INSTRUCTIONS> section of the prompt. The global one is the default here because the
saving should not depend on remembering to set up each repository.
Two things worth knowing before you install it:
- The block is re-sent on every request. That is why the default is 1.1 kB and why CI refuses to let
it grow past 1,600 bytes.
--fullis there if you would rather have every rule than the cheapest block. project_doc_max_bytestruncates silently, at 32,768 bytes by default. If yourAGENTS.mdis already near that, adding to it can push your own instructions off the end.node scripts/prefix-audit.mjsrun inside the project shows you what yours currently costs.
./install.sh --skills-only # $HOME/.agents/skills
CODEX_SKILLS_DIR=... ./install.sh --skills-only
./install.sh --skills-only --uninstallCodex reads standalone skills from three roots, most specific first:
$CWD/.agents/skills # this project only
$HOME/.agents/skills # you, everywhere
/etc/codex/skills # the whole machine or container
It publishes a skill from every root it finds one in, so two copies of eco mean two catalogue
entries with one name: both descriptions billed every turn, and $eco no longer pointing at one body.
Keep one copy. node scripts/prefix-audit.mjs reports duplicates it finds.
codex plugin marketplace add sup3x/codex-eco
codex plugin add eco@codex-ecoRetracted claim. An earlier version of this README said a plugin "supplies the body directly" and was therefore cheaper to invoke. That is wrong. Rendering the prompt with the plugin installed shows the same single catalogue line and no body — a plugin-installed skill is read from disk exactly like a standalone one. The 35-versus-131-token observation behind the claim was one unreplicated pair of runs, and its stated explanation did not survive checking.
Installing both is worse than installing one. Codex publishes every root it finds a skill in, so a standalone copy and a plugin copy appear as two catalogue entries with the same name: both descriptions are billed on every turn, and $eco no longer names one body. node scripts/prefix-audit.mjs reports duplicates it finds, and the benchmark harness now refuses to run a batch whose skill name is ambiguous — a defect it found in every batch this project had run until then.
Note that the aggressive profile turns the plugin subsystem off, so it pairs with the standalone install, not the plugin one.
cp profiles/eco.config.toml "$CODEX_HOME/eco.config.toml" # ~/.codex by default
codex --profile ecoA profile is layered at launch, so it never invalidates a cached prefix the way changing model or effort mid-thread does, and uninstalling is deleting one file.
| Project | Layer | Honest comparison |
|---|---|---|
| RTK | Shell-output compression proxy | The giant in this space and complementary: it shrinks command output before it reaches context. Its Codex integration is its weakest — on Codex it degrades to instructions — which is the gap the rules here fill. |
| token-diet | Terse ruleset | Advertises Codex support with numbers ported from another agent. This project measures on Codex itself, which is the whole difference. |
| agent-token-saver | Controlled A/B on Codex | The prior art for measuring this on Codex at all. We cite it and try to beat it on protocol: pre-registration, n per arm, bootstrap CI, exact Mann-Whitney, deterministic grading, and published negative results. |
| ccusage-style dashboards | Monitoring | Measure spend after the fact; reduce nothing. |
A star is the whole price, and the only one.
If it doesn't save you tokens, an issue with your numbers is worth more than a star. Run
node bench/bench.mjs study review-thread --n 5 --model <yours> on your own workload and post what
you get — results where eco loses are as publishable as results where it wins, and there are already
several in here.
Benchmark results from other models, plans and platforms are the most valuable contribution — especially ones where eco loses. node bench/bench.mjs ab --task "..." --n 5 --rubric orders-review writes every run's event stream for you. See CONTRIBUTING.md.
MIT © 2026 Kerim



