Measured: CLAUDE_CODE_AUTO_COMPACT_WINDOW beats --autocompact, so fleetd.yaml's comment is backwards #618

Open
opened 2026-09-22 05:41:20 +02:00 by ltms · 2 comments
Owner

The question

fleetd/fleetd.yaml sets, on four Claude Code profiles (local, local-direct, opus, sonnet), autoCompactWindow: 250000 — which fleetd passes as --autocompact 250000 — while the same profiles export CLAUDE_CODE_AUTO_COMPACT_WINDOW: "300000".

The repo asserted both directions as fact and neither was ever measured:

  • fleetd/fleetd.yaml:25 and :74: # CB-636: --autocompact FLAG (outranks the env var below)
  • ClaudeCodeArguments.withAutoCompactWindow's javadoc used to claim the env var always wins, then was rewritten to pick no side.

Since #601 the daemon only warns on the conflict, so nothing was broken and nobody owned the question.

The answer: the environment variable wins

CLAUDE_CODE_AUTO_COMPACT_WINDOW outranks --autocompact. The fleetd.yaml comment is backwards.

Full precedence, highest first:

CLAUDE_CODE_AUTO_COMPACT_WINDOW (if it parses)  >  --autocompact  >  settings file  >
  clientdata  >  experiment  >  model default

--autocompact does outrank the settings file. It does not outrank the env var.

How I measured it

I read the installed CLI itself. It is a bun-compiled Mach-O binary with the JS bundle embedded:

/Users/dai.ha/.local/share/claude/versions/2.1.278   (217695408 bytes, Mach-O 64-bit arm64)
claude --version -> 2.1.278 (Claude Code)

strings is unusable on this Mac (Xcode licence not accepted) and returns a silent empty result, so every search below used grep -a on the binary with a positive control (grep -ac 'Claude Code' -> 4802).

Step 1 — the flag is parsed into u.autocompact (offset 187414783):

A.addOption(new z("--autocompact <auto|tokens>","Auto-compact window size (auto, or 100k-1M tokens)")...)

Step 2 — flag and settings file are merged, flag first (offset 187070705, function at 185100898):

let Lo = oHr(u.autocompact, Ke().autoCompactWindow), ...

function oHr(e, r) { if (e === void 0) return r; return e === "auto" ? void 0 : e; }

So Lo = the flag when given, otherwise the settings-file value. --autocompact auto yields undefined.

Step 3 — Lo becomes options.autoCompactWindow (offset 187103513): autoCompactWindow:Lo.

Step 4 — the resolver reads the env var first and returns immediately (function Dv, env read at offset 179283490):

function Dv(e, n, r = Jp()) {
  let g = uf(e, r);                     // the model's own max context window
  if (process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW) {
    let U = pke("CLAUDE_CODE_AUTO_COMPACT_WINDOW", process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW, Xje, ret);
    if (U.status !== "invalid") {
      let he = Math.max(Xje, U.effective);
      return { window: Math.min(g, he), configured: he, source: "env" };
    }
  }
  if (n !== void 0) return { window: Math.min(g, n), configured: n, source: "settings" };
  ...clientdata... ...experiment... ...model-default... ...unknown-model...
}

Step 5 — options.autoCompactWindow is the second argument n (call site at 194183175):

function Ct({messages: e, model: n, autoCompactWindow: o}) {
  ...
  let { window: u } = Dv(n, Vf() ? o : void 0);

The same shape appears at 179744016 (We = Vf() ? U : void 0; Dv(He, We)). Note Vf() gates only the flag/settings argument, never the env-var branch — which can only strengthen the conclusion.

Independent corroboration from the CLI's own UI strings (offsets 74706362 and 80865001), which are written by the same feature but are a different artefact from the resolver code:

... tokens (from CLAUDE_CODE_AUTO_COMPACT_WINDOW)
... tokens (from settings)
CLAUDE_CODE_AUTO_COMPACT_WINDOW is set and takes precedence. Unset it to change this setting.

What this means for this fleet, today

Every Claude Code lead and worker on the Mac host compacts at 300000 tokens (capped by the model's own maximum context window), not the 250000 the --autocompact flag asks for. The flag has had no effect on these four profiles for as long as both values have been set.

The hazard the previous lead warned about is real, and this is its direction: deleting CLAUDE_CODE_AUTO_COMPACT_WINDOW to "resolve the conflict" would silently drop every lead and every worker from 300000 to 250000. Do not do that as tidying. If 250000 is wanted, that is a decision to make on purpose.

What I did not do

I did not run a live experiment — no session was driven to its compaction threshold to watch which number fired. I read the code that runs. That is the stronger instrument for a precedence question, but it is one instrument, and it is specific to 2.1.278. A future CLI version can reorder this; the check is Dv's first branch.

Follow-ups

  1. Fix fleetd/fleetd.yaml comments on lines 25, 74, 110 and 124 — they currently tell the next session the opposite of the truth. (Local file, gitignored, lead-side.)
  2. Fix ClaudeCodeArguments.withAutoCompactWindow's javadoc — it says the precedence is unverified. It is verified now.
  3. Decide whether 300000 or 250000 is actually wanted on these four profiles. Not urgent; nothing is broken.
  4. Tell the fleet01 lead, who was warned this was unmeasured.
## The question `fleetd/fleetd.yaml` sets, on four Claude Code profiles (`local`, `local-direct`, `opus`, `sonnet`), `autoCompactWindow: 250000` — which fleetd passes as `--autocompact 250000` — while the same profiles export `CLAUDE_CODE_AUTO_COMPACT_WINDOW: "300000"`. The repo asserted **both** directions as fact and neither was ever measured: - `fleetd/fleetd.yaml:25` and `:74`: `# CB-636: --autocompact FLAG (outranks the env var below)` - `ClaudeCodeArguments.withAutoCompactWindow`'s javadoc used to claim the env var always wins, then was rewritten to pick no side. Since #601 the daemon only **warns** on the conflict, so nothing was broken and nobody owned the question. ## The answer: the environment variable wins **`CLAUDE_CODE_AUTO_COMPACT_WINDOW` outranks `--autocompact`. The `fleetd.yaml` comment is backwards.** Full precedence, highest first: ``` CLAUDE_CODE_AUTO_COMPACT_WINDOW (if it parses) > --autocompact > settings file > clientdata > experiment > model default ``` `--autocompact` does outrank the settings file. It does **not** outrank the env var. ## How I measured it I read the installed CLI itself. It is a bun-compiled Mach-O binary with the JS bundle embedded: ``` /Users/dai.ha/.local/share/claude/versions/2.1.278 (217695408 bytes, Mach-O 64-bit arm64) claude --version -> 2.1.278 (Claude Code) ``` `strings` is unusable on this Mac (Xcode licence not accepted) and returns a silent empty result, so every search below used `grep -a` on the binary with a positive control (`grep -ac 'Claude Code'` -> 4802). **Step 1 — the flag is parsed into `u.autocompact`** (offset 187414783): ```js A.addOption(new z("--autocompact <auto|tokens>","Auto-compact window size (auto, or 100k-1M tokens)")...) ``` **Step 2 — flag and settings file are merged, flag first** (offset 187070705, function at 185100898): ```js let Lo = oHr(u.autocompact, Ke().autoCompactWindow), ... function oHr(e, r) { if (e === void 0) return r; return e === "auto" ? void 0 : e; } ``` So `Lo` = the flag when given, otherwise the settings-file value. `--autocompact auto` yields `undefined`. **Step 3 — `Lo` becomes `options.autoCompactWindow`** (offset 187103513): `autoCompactWindow:Lo`. **Step 4 — the resolver reads the env var first and returns immediately** (function `Dv`, env read at offset 179283490): ```js function Dv(e, n, r = Jp()) { let g = uf(e, r); // the model's own max context window if (process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW) { let U = pke("CLAUDE_CODE_AUTO_COMPACT_WINDOW", process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW, Xje, ret); if (U.status !== "invalid") { let he = Math.max(Xje, U.effective); return { window: Math.min(g, he), configured: he, source: "env" }; } } if (n !== void 0) return { window: Math.min(g, n), configured: n, source: "settings" }; ...clientdata... ...experiment... ...model-default... ...unknown-model... } ``` **Step 5 — `options.autoCompactWindow` is the second argument `n`** (call site at 194183175): ```js function Ct({messages: e, model: n, autoCompactWindow: o}) { ... let { window: u } = Dv(n, Vf() ? o : void 0); ``` The same shape appears at 179744016 (`We = Vf() ? U : void 0; Dv(He, We)`). Note `Vf()` gates only the flag/settings argument, never the env-var branch — which can only strengthen the conclusion. **Independent corroboration from the CLI's own UI strings** (offsets 74706362 and 80865001), which are written by the same feature but are a different artefact from the resolver code: ``` ... tokens (from CLAUDE_CODE_AUTO_COMPACT_WINDOW) ... tokens (from settings) CLAUDE_CODE_AUTO_COMPACT_WINDOW is set and takes precedence. Unset it to change this setting. ``` ## What this means for this fleet, today Every Claude Code lead and worker on the Mac host compacts at **300000** tokens (capped by the model's own maximum context window), **not** the 250000 the `--autocompact` flag asks for. The flag has had no effect on these four profiles for as long as both values have been set. The hazard the previous lead warned about is real, and this is its direction: **deleting `CLAUDE_CODE_AUTO_COMPACT_WINDOW` to "resolve the conflict" would silently drop every lead and every worker from 300000 to 250000.** Do not do that as tidying. If 250000 is wanted, that is a decision to make on purpose. ## What I did not do I did not run a live experiment — no session was driven to its compaction threshold to watch which number fired. I read the code that runs. That is the stronger instrument for a precedence question, but it is one instrument, and it is specific to **2.1.278**. A future CLI version can reorder this; the check is `Dv`'s first branch. ## Follow-ups 1. Fix `fleetd/fleetd.yaml` comments on lines 25, 74, 110 and 124 — they currently tell the next session the opposite of the truth. (Local file, gitignored, lead-side.) 2. Fix `ClaudeCodeArguments.withAutoCompactWindow`'s javadoc — it says the precedence is unverified. It is verified now. 3. Decide whether 300000 or 250000 is actually wanted on these four profiles. Not urgent; nothing is broken. 4. Tell the fleet01 lead, who was warned this was unmeasured.
Author
Owner

Correction to the PR #619 brief: there is a THIRD stale spot, and my brief missed it

This is a correction to the brief, not a new unit. The brief named two places that still claim the precedence is unknown. There are three. The worker found the third and reported it without fixing it, exactly as asked.

The third spot: the method-level javadoc on FleetConfig.warnConflictingAutoCompactWindows, at roughly lines 2243–2247, directly above the log.warn call that PR #619 already rewrote:

 * <p>Which of the two inputs Claude Code actually follows when they disagree is intentionally
 * <em>not</em> asserted here. {@code ClaudeCodeArguments}'s javadoc used to state the
 * environment variable always wins; nobody had measured that, and this host's own
 * {@code fleetd.yaml} asserts the opposite in a comment. This method only detects and reports
 * the disagreement — see {@link dev.ltms.fleet.launch.ClaudeCodeArguments}.

I read this on the PR branch myself (git show refs/remotes/pr/619:...FleetConfig.java), so it is the state after #619's changes, not before.

Why it must be fixed in this PR rather than left: #619 makes the problem worse than it found it, through no fault of the worker.

  1. The paragraph sits immediately above a log.warn that now says the opposite. One method, two contradictory claims, a few lines apart.
  2. It tells the reader to go see ClaudeCodeArguments for the open question — and that javadoc now states the measured answer. The pointer sends you somewhere that disagrees with the pointer.
  3. It cites fleetd/fleetd.yaml's comment as evidence that the question is open. I corrected those four comments on this host earlier today. The cited evidence no longer exists.

Fix: replace that one paragraph with the measured answer — the env var wins, so autoCompactWindow is inert on a profile that sets both. Keep the ClaudeCodeArguments cross-reference; it is now a pointer to an agreeing claim rather than a contradicting one. Keep everything else in that javadoc, in particular the whole WARN-not-throw rationale in the paragraph above it, which is still correct and is #601's reasoning.

This one is mine, not the worker's. My brief enumerated two call sites by name when the right instruction was "fix every place in the repo that still claims this precedence is unknown". Naming the instances instead of the property is the same mistake recorded in this repo before: an acceptance criterion that names a construct is satisfied by that construct, and stops at its edge. The worker did the right thing — it did the named scope, spotted the shape outside it, and reported it in one line without going hunting.

## Correction to the PR #619 brief: there is a THIRD stale spot, and my brief missed it This is a correction to the brief, not a new unit. The brief named two places that still claim the precedence is unknown. There are three. The worker found the third and reported it without fixing it, exactly as asked. **The third spot:** the method-level javadoc on `FleetConfig.warnConflictingAutoCompactWindows`, at roughly lines 2243–2247, directly above the `log.warn` call that PR #619 already rewrote: ```java * <p>Which of the two inputs Claude Code actually follows when they disagree is intentionally * <em>not</em> asserted here. {@code ClaudeCodeArguments}'s javadoc used to state the * environment variable always wins; nobody had measured that, and this host's own * {@code fleetd.yaml} asserts the opposite in a comment. This method only detects and reports * the disagreement — see {@link dev.ltms.fleet.launch.ClaudeCodeArguments}. ``` I read this on the PR branch myself (`git show refs/remotes/pr/619:...FleetConfig.java`), so it is the state after #619's changes, not before. **Why it must be fixed in this PR rather than left:** #619 makes the problem worse than it found it, through no fault of the worker. 1. The paragraph sits immediately above a `log.warn` that now says the opposite. One method, two contradictory claims, a few lines apart. 2. It tells the reader to go see `ClaudeCodeArguments` for the open question — and that javadoc now states the measured answer. The pointer sends you somewhere that disagrees with the pointer. 3. It cites `fleetd/fleetd.yaml`'s comment as evidence that the question is open. I corrected those four comments on this host earlier today. The cited evidence no longer exists. **Fix:** replace that one paragraph with the measured answer — the env var wins, so `autoCompactWindow` is inert on a profile that sets both. Keep the `ClaudeCodeArguments` cross-reference; it is now a pointer to an agreeing claim rather than a contradicting one. Keep everything else in that javadoc, in particular the whole WARN-not-throw rationale in the paragraph above it, which is still correct and is #601's reasoning. **This one is mine, not the worker's.** My brief enumerated two call sites by name when the right instruction was "fix every place in the repo that still claims this precedence is unknown". Naming the instances instead of the property is the same mistake recorded in this repo before: an acceptance criterion that names a construct is satisfied by that construct, and stops at its edge. The worker did the right thing — it did the named scope, spotted the shape outside it, and reported it in one line without going hunting.
Author
Owner

Follow-ups 1, 2 and 4 are done. Item 3 stays open, and it is a real decision.

1. fleetd/fleetd.yaml comments — done (lead, this host). The four comments on lines 25, 74, 110 and 124 said --autocompact FLAG (outranks the env var below). They now say the flag is inert, name this ticket, and warn that deleting the env var lowers the live window. The daemon re-read the file at 10:41:35 and logged config reloaded, so the real parser validated the edit. The file stays gitignored and never entered git.

2. The code — done, merged as 8915e40 (PR #619). Three spots, not the two my brief named:

  • ClaudeCodeArguments.withAutoCompactWindow javadoc
  • FleetConfig.warnConflictingAutoCompactWindows WARN text
  • that method's own javadoc — the third spot, found by the worker, corrected on this ticket

4. fleet01 — told. I had already told them the question was unmeasured, so I sent a correction with the answer, the method, and both caveats (no live experiment; specific to 2.1.278). I also told them what it means for their host: if they set both, deleting the env var lowers their live window rather than fixing anything.

What I verified myself, rather than taking from the worker

  • Read the complete diff on the PR branch, both commits.
  • Ran an independent repo sweep with my own patterns and two positive controls (14 files mention auto-compact; #618 appears twice in each changed file, so the search can see the new text). Four hits remain and all four are the false positives the worker named — two are about --model outranking the environment, a different flag and a different feature.
  • Ran mvn clean install myself in a scratch worktree at 6cb31a1, not in the main clone: Tests run: 1877, Failures: 0, Errors: 0, Skipped: 0, BUILD SUCCESS, exit 0. That equals main's existing 1877, which is correct for a text-only change — no test was added, dropped or silently skipped.

Item 3 is still open: is 300000 or 250000 actually wanted?

Now that the precedence is known, this is a plain decision with no unknowns left in it. The facts:

  • Four Claude Code profiles (local, local-direct, opus, sonnet) set autoCompactWindow: 250000 and CLAUDE_CODE_AUTO_COMPACT_WINDOW: "300000".
  • The live value is 300000 on all four, capped by each model's own maximum context window.
  • 250000 has never taken effect on these profiles.

So nobody has to act. The risk is only that a future session "tidies" the conflict by deleting one key without knowing which way that moves the window. The config comments now say so on the spot where that edit would be made, which is the cheapest guard available.

Not urgent, nothing is broken. Leaving this open deliberately rather than closing the ticket, since the decision has an owner-shaped hole in it and the measurement work it was filed for is complete.

One thing worth a separate look, not fixed here

LeadLauncher.java:380 and ClaudeCodeLauncher.java:894 both assert that the --model flag outranks the environment. That is the same shape as the claim this ticket just disproved: a precedence between a flag and an environment variable, asserted in a comment, with no measurement cited. Different flag, different feature, and out of scope here. I have not measured it and I am not claiming it is wrong — only that it is the same kind of claim, and this ticket is evidence that kind can be backwards.

## Follow-ups 1, 2 and 4 are done. Item 3 stays open, and it is a real decision. **1. `fleetd/fleetd.yaml` comments — done (lead, this host).** The four comments on lines 25, 74, 110 and 124 said `--autocompact FLAG (outranks the env var below)`. They now say the flag is inert, name this ticket, and warn that deleting the env var lowers the live window. The daemon re-read the file at 10:41:35 and logged `config reloaded`, so the real parser validated the edit. The file stays gitignored and never entered git. **2. The code — done, merged as `8915e40` (PR #619).** Three spots, not the two my brief named: - `ClaudeCodeArguments.withAutoCompactWindow` javadoc - `FleetConfig.warnConflictingAutoCompactWindows` WARN text - that method's own javadoc — the third spot, found by the worker, corrected on this ticket **4. fleet01 — told.** I had already told them the question was unmeasured, so I sent a correction with the answer, the method, and both caveats (no live experiment; specific to 2.1.278). I also told them what it means for their host: if they set both, deleting the env var lowers their live window rather than fixing anything. ## What I verified myself, rather than taking from the worker - Read the complete diff on the PR branch, both commits. - Ran an **independent** repo sweep with my own patterns and two positive controls (14 files mention auto-compact; `#618` appears twice in each changed file, so the search can see the new text). Four hits remain and all four are the false positives the worker named — two are about `--model` outranking the environment, a different flag and a different feature. - Ran `mvn clean install` myself in a scratch worktree at `6cb31a1`, not in the main clone: `Tests run: 1877, Failures: 0, Errors: 0, Skipped: 0`, `BUILD SUCCESS`, exit 0. That equals `main`'s existing 1877, which is correct for a text-only change — no test was added, dropped or silently skipped. ## Item 3 is still open: is 300000 or 250000 actually wanted? Now that the precedence is known, this is a plain decision with no unknowns left in it. The facts: - Four Claude Code profiles (`local`, `local-direct`, `opus`, `sonnet`) set `autoCompactWindow: 250000` and `CLAUDE_CODE_AUTO_COMPACT_WINDOW: "300000"`. - The live value is **300000** on all four, capped by each model's own maximum context window. - `250000` has never taken effect on these profiles. So nobody has to act. The risk is only that a future session "tidies" the conflict by deleting one key without knowing which way that moves the window. The config comments now say so on the spot where that edit would be made, which is the cheapest guard available. **Not urgent, nothing is broken.** Leaving this open deliberately rather than closing the ticket, since the decision has an owner-shaped hole in it and the measurement work it was filed for is complete. ## One thing worth a separate look, not fixed here `LeadLauncher.java:380` and `ClaudeCodeLauncher.java:894` both assert that the `--model` flag outranks the environment. That is the same **shape** as the claim this ticket just disproved: a precedence between a flag and an environment variable, asserted in a comment, with no measurement cited. Different flag, different feature, and out of scope here. I have not measured it and I am not claiming it is wrong — only that it is the same kind of claim, and this ticket is evidence that kind can be backwards.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: fleet/fleetd#618