fix(atomic): stop registering the statusline extension with atomic - #2732
Merged
Conversation
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.
|
Tick the box to add this pull request to the merge queue (same as
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
atomic 0.9.13 runs every interactive session's extensions inside an isolated RPC engine child, and that child's
ctx.ui.setFooteris a warn-once no-op that neither binds its argument nor has a protocol frame to forward it.The
statuslineextension callssetFooterat 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 honouredsetFootercan 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
gateappears 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.nixgenerates one settings payload for both pi-lineage agents, andpackagesbecomes 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 tostatusline/index.ts, with the reason recorded beside it.packagesForAtomic—packageswith 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.nixconsumespackagesForAtomic.It still declares
packagesrather than dropping the key:merge-settings.shcan 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.atomicat all, so this would have landed unguarded.modules/checks/atomic-agent-environment.nixmirrorsmodules/checks/pi-agent-environment.nix, reusingmkStructuralCheckso 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.
atomicDeclaresPackagesis 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
atomicExtensionExclusionsfails it onatomicNegativeExtensions.Verification
Evaluated settings,
aarch64-darwin,crs58:statusline/index.ts-statusline/index.tsmodules/home/ai/pi/has a zero-line diff and pi's generatedpackagesis byte-identical tomain.Runtime, against the settings this change generates, merged onto a scratch agent dir exactly as
merge-settings.shmerges them —atomic --no-session --approveunder a pty, rendered at 80x40:Warning count
0; one footer rather than two; no blank gap;gateandADHD Modestill present.The runtime keys the merge must preserve —
onboardedVersion,defaultModel,defaultProvider— survived.Checks built locally on
aarch64-darwin:Buildbot covers the remaining checks and the non-darwin systems.