Skip to content

fix(atomic): stop registering the statusline extension with atomic - #2732

Merged
cameronraysmith merged 2 commits into
mainfrom
fm/vx-atomic-footer-duplication
Aug 17, 2026
Merged

fix(atomic): stop registering the statusline extension with atomic#2732
cameronraysmith merged 2 commits into
mainfrom
fm/vx-atomic-footer-duplication

Conversation

@cameronraysmith

Copy link
Copy Markdown
Owner

Root cause

atomic 0.9.13 runs every interactive session's extensions inside an isolated RPC engine child, and that child's ctx.ui.setFooter is a warn-once no-op that neither binds its argument nor has a protocol frame to forward it.
The statusline extension calls setFooter at session start to evict atomic's native footer and put everything on one line, which is what it does under pi.
Under atomic the call is refused, so it prints ctx.ui.setFooter is unavailable in isolated interactive mode … on every startup, and — because atomic's footer dock is single-occupancy and only an honoured setFooter can vacate it — the native two-line footer stays docked beneath the extension's own status bar.
The extension's branch subscription and status pump live inside the same refused factory, so they never run either: that is why gate appears in atomic's native footer but never in the bar, and why the bar never repaints on a branch change.
The defect is upstream and atomic exposes no local remedy — no setting suppresses the native footer, and engine isolation cannot be disabled — so this change stops registering the extension with atomic while keeping it for pi.

Accepted trade-off

atomic loses the bar's subscription-usage and cost segments.
Everything else the bar showed is already in atomic's native footer: model, provider, cwd, branch, extension statuses, and context usage.
The consequence is that the two agents no longer render the same status line.
A shared single-line statusline across both is only reachable by fixing the refusal upstream, which this change does not attempt.

What changed

modules/home/ai/agent-settings.nix generates one settings payload for both pi-lineage agents, and packages becomes the single key they no longer share verbatim.
The divergence is declared there as its own named option rather than patched into a consumer, so one file still answers what each agent is given and why:

  • atomicExtensionExclusions — the extensions atomic must not load, defaulting to statusline/index.ts, with the reason recorded beside it.
  • packagesForAtomicpackages with those exclusions appended as --prefixed force-excludes to every entry that selects extensions.

atomic applies force-excludes last and unconditionally, so the - entry wins over the include of the same name and the shared list stays intact for pi.
The force-exclude form is preferred over deleting the include because it survives a later change that widens the include set, and because a pattern matching nothing is inert.

modules/home/ai/atomic/default.nix consumes packagesForAtomic.
It still declares packages rather than dropping the key: merge-settings.sh can update a nix-owned key but not retract one, so dropping the declaration would freeze whatever value last reached ~/.atomic/agent/settings.json.

New check

Nothing in the flake read programs.atomic at all, so this would have landed unguarded.
modules/checks/atomic-agent-environment.nix mirrors modules/checks/pi-agent-environment.nix, reusing mkStructuralCheck so a failure emits a unified diff naming the violated claim.

It asserts both halves of the divergence together, because either alone admits a wrong state: atomic must force-exclude statusline, and pi must still select it.
Asserting only atomic's half would pass a change that disarmed the extension for both agents.
atomicDeclaresPackages is asserted separately because the retraction failure above is invisible in the generated payload and shows only as the key's absence.

The check was verified severe rather than vacuous: emptying atomicExtensionExclusions fails it on atomicNegativeExtensions.

Verification

Evaluated settings, aarch64-darwin, crs58:

statusline/index.ts -statusline/index.ts
atomic selected force-excluded
pi selected absent

modules/home/ai/pi/ has a zero-line diff and pi's generated packages is byte-identical to main.

Runtime, against the settings this change generates, merged onto a scratch agent dir exactly as merge-settings.sh merges them — atomic --no-session --approve under a pty, rendered at 80x40:

                                                                0.0%/1.0M (auto)
────────────────────────────────────────────────────────────────────────────────
❯
────────────────────────────────────────────────────────────────────────────────
(zai) glm-5.3 high • ~/.treehouse/… (fm/vx-atomic-…)
 gate • ● ADHD Mode

Warning count 0; one footer rather than two; no blank gap; gate and ADHD Mode still present.
The runtime keys the merge must preserve — onboardedVersion, defaultModel, defaultProvider — survived.

Checks built locally on aarch64-darwin:

atomic-agent-environment-structural   (new)
pi-agent-environment-structural
pi-agent-environment-policy
pi-agent-environment-smoke
home-manager-crs58
home-configurations-exposed
home-module-exports
home-module-exports-neg
naming-conventions
eval-md-format

Buildbot covers the remaining checks and the non-darwin systems.

atomic 0.9.13 runs every interactive session's extensions in an isolated
RPC engine child whose ctx.ui.setFooter is a warn-once no-op
(packages/coding-agent/src/modes/rpc/rpc-extension-ui.ts:232). The
statusline extension calls it at session start to evict atomic's native
footer, so under atomic the call warns on every startup and the native
two-line footer stays docked beneath the extension's own bar.

atomic's footer dock is single-occupancy and only an honoured setFooter
can vacate it (modes/interactive/interactive-extension-widgets.ts:75),
so nothing short of honouring the call removes the duplicate. atomic
exposes no setting that suppresses the native footer and no way to
disable engine isolation, which leaves not loading the extension as the
only local remedy. The defect is upstream; this is the local mitigation.

agent-settings gains the divergence as its own named option rather than
letting a consumer patch the shared value, so one file still answers
what each agent is given. atomic applies force-excludes last and
unconditionally (core/package-manager-resource-patterns.ts:143), so the
-prefixed entry wins over the include of the same name and the shared
list stays intact for pi, which honours setFooter in-process.

Accepted cost: atomic loses the bar's subscription-usage and cost
segments. Its native footer already carries model, provider, cwd,
branch, extension statuses, and context usage.
modules/home/ai/agent-settings.nix generates one settings payload for two
agents, and nothing asserted that the one key they deliberately do not
share stays diverged in the direction intended. Nothing in the flake read
programs.atomic at all, so the preceding change would have landed
unguarded and a regression in either direction would have been silent.

The check mirrors modules/checks/pi-agent-environment.nix, reusing
mkStructuralCheck so a failure emits a unified diff naming the violated
claim rather than one aggregate boolean.

Both halves are asserted together because either alone admits a wrong
state: atomic must force-exclude statusline, and pi must still select it.
Asserting only atomic's half would pass a change that disarmed the
extension for both agents. atomicDeclaresPackages is separate because
merge-settings.sh cannot retract a nix-owned key, so a change that
stopped declaring packages would freeze the last value written to
~/.atomic/agent/settings.json — a failure the generated payload cannot
show.

Verified severe rather than vacuous: emptying atomicExtensionExclusions
fails the check on atomicNegativeExtensions.
@mergify

mergify Bot commented Aug 17, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@cameronraysmith
cameronraysmith merged commit 6b3b93b into main Aug 17, 2026
6 checks passed
@cameronraysmith
cameronraysmith deleted the fm/vx-atomic-footer-duplication branch August 17, 2026 04:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant