Skip to content

Commit 7035bca

Browse files
authored
refactor: modularize shell feature lifecycles (#85)
Decompose the largest Aurora Shell modules into cohesive resource owners for Screenshot UI hooks, capture toolbar behavior and OCR sessions; tray viewport calculations and SNI entries; dock configuration, per-monitor bindings, contextual drag reveal and external-storage operations; Dash visibility, application activation, fixed items and spring loading; Aurora Menu recent-item parsing; Meeting Clock presentation and alerts; clipboard card construction; and IconWeave patching, inspection and window registration. Preserve the existing facades, public module APIs and DevTool contracts while making each extracted component responsible for its own actors, signals, cancellables, GLib sources and teardown. Introduce factory-based ManagedSource replacement and apply symmetric lifecycle orchestration across the affected modules, including explicit asynchronous identity checks, safe actor transition cleanup, conditional monkey-patch restoration and deterministic enable, disable and re-enable behavior. Tighten the implementation against the EGO review rules by removing redundant guards, lifecycle flags, defensive cleanup wrappers, unnecessary aliases and stale compatibility helpers, while documenting accepted analyzer findings and keeping settings, translations and review guidance synchronized. Expand pure unit coverage for tray layout, SNI icon handling and identity matching, dock configuration transitions, Dash visibility and window selection, Aurora Menu recent parsing, clipboard card classification, IconWeave scoring and registration, Meeting Clock alert timing and ManagedSource ordering. Extend Shell integration coverage for the refactored Dock, IconWeave and Volume Mixer paths so resource destruction and lifecycle restoration remain exercised in a real GNOME Shell environment.
1 parent 6349192 commit 7035bca

115 files changed

Lines changed: 6694 additions & 5129 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 63 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,29 @@ Per the GNOME review guidelines, clipboard-related keyboard shortcuts must not s
171171
- Constants: `UPPER_CASE`
172172
- Keep `enable()` and `disable()` symmetric.
173173
- Read settings through `this.context.settings`. Importing `Main`/`Shell`/`St` directly is fine — keep heavy algorithms in shell-free pure files so they stay unit-testable.
174+
- Optimize refactors for human readability, not line count. Do not compress control flow, callback bodies,
175+
object literals, or several operations onto one line merely to shorten a file.
176+
- Visually separate guard clauses, state preparation, actor mutation, animation, scheduling, and cleanup
177+
with blank lines. Keep local constants next to the logical block that consumes them; avoid unexplained
178+
aliases in the middle of a stateful method.
179+
- Do not add pass-through methods that only forward the same arguments to a stored function or object.
180+
Expose a meaningful domain operation, return the required callable directly, or keep the call at its
181+
natural owner.
182+
- Do not hide lifecycle invariants behind optional chaining with fallback values, such as
183+
`owner?.value ?? default` or `owner?.operation() ?? false`. At public boundaries, guard the inactive
184+
state explicitly and access stable fields directly during synchronous work. Reserve optional
185+
chaining and nullish fallbacks for genuinely optional external data and idempotent cleanup.
186+
- Do not create a local alias for an instance field merely to shorten `this._field`, repeat the same
187+
name, or satisfy nullable type narrowing during synchronous work. Guard the field explicitly and
188+
use it directly when it cannot change inside the block. A snapshot of an instance field is justified
189+
only when it transfers ownership before the field is cleared or captures the exact resource across
190+
an `await` or asynchronous callback. A local result is also appropriate for a genuinely dynamic
191+
lookup or computation that must remain stable; directly reading `this._field` is not such a lookup.
192+
Name identity captures explicitly, such as `scheduledRetry` or `activeRequest`, so the reason is
193+
visible.
194+
- Before finishing a refactor, review every newly created or substantially edited file as prose: expand
195+
dense one-line branches and loops, remove redundant wrappers, and make lifecycle ownership obvious
196+
without requiring the reader to infer it from implementation details.
174197

175198
## Human Review Quality Bar
176199

@@ -185,29 +208,50 @@ Changes intended for the production extension must follow both:
185208

186209
Apply these rules during implementation and review:
187210

188-
- Target the Shell versions declared in `metadata.json`; do not add speculative compatibility checks
189-
or optional calls for APIs guaranteed by those versions.
190-
- Keep `extension.ts` small and keep `enable()`/`disable()` close, symmetric, and limited to lifecycle
191-
orchestration.
192-
- Every signal, GLib source, cancellable, child actor, menu, and other resource created by a component
193-
must be cleaned up by that same component. Remove sources and signals before destroying owned actors,
194-
and call `super.destroy()` last in widget overrides.
195-
- Override a widget's `destroy()` method for its cleanup. Do not connect the widget's own `destroy`
196-
signal as a substitute.
211+
- Target only the Shell versions declared in `metadata.json`. Do not add speculative compatibility
212+
branches, `typeof method === 'function'` checks, or optional calls for methods guaranteed by those
213+
versions. For real multi-version support, follow the
214+
[official port guide](https://gjs.guide/extensions/upgrading/gnome-shell.html).
215+
- Do not wrap deterministic lifecycle methods such as `destroy()`, `connect()`, `disconnect()`,
216+
`disconnectObject()`, `abort()`, `GLib.Source.remove()`, or `Gio.DBusConnection.unregister_object()`
217+
in defensive `try`/`catch`. Catch failures only at operations whose contract can genuinely fail,
218+
such as I/O, parsing, D-Bus calls, subprocesses, and asynchronous result propagation.
219+
- Do not add optional calls such as `object?.method(...)` or `object?.method?.(...)` when the object and
220+
method are guaranteed by the active lifecycle or the targeted API. Use an explicit boundary guard
221+
when the owning object itself is legitimately inactive or absent.
197222
- Do not add `_enabled`, `_destroyed`, or similar lifecycle flags when owned references, cancellables,
198-
or the underlying GObject lifecycle already express the state. Any unavoidable exception needs a
199-
concise invariant comment and regression coverage.
200-
- Avoid defensive `try`/`catch` around deterministic cleanup and avoid trivial comments that merely
201-
restate the next line.
202-
- Do not use emoji or ASCII art as UI icons, do not ship placeholders, keep generated JavaScript lines
203-
at 200 characters or fewer, and keep production packages free of developer-only files.
204-
- Avoid subprocesses in the Shell process. If a subprocess is unavoidable, document why a D-Bus
205-
service is not practical and keep invocation local, explicit, cancellable, and free of shell
206-
interpretation.
223+
or the underlying GObject lifecycle already express the state. After destruction, the owner must
224+
clear its reference and must not call the instance again.
225+
- In widget `destroy()` overrides, remove GLib sources and timeouts first, disconnect signals next,
226+
release owned children and references after that, and call `super.destroy()` last. A widget must
227+
override its own `destroy()` method instead of connecting its own `destroy` signal for cleanup;
228+
observing the destruction of an external actor is valid when the observer owns that connection.
229+
- Every signal, GLib source, cancellable, child actor, menu, Soup session, and other resource created
230+
by a component must be cleaned up by that same component. Never spread initialization and cleanup
231+
ownership across unrelated classes.
232+
- When a repeatable operation creates a timeout, remove or replace its prior source immediately next
233+
to the new source creation. Do not separate replacement and creation into distant methods or blocks.
234+
- Keep `extension.ts` minimal. Keep `enable()` and `disable()` adjacent, symmetric, and limited to
235+
lifecycle orchestration; avoid aliases that merely forward lifecycle calls. Never ship empty,
236+
placeholder, or partially implemented lifecycle methods.
237+
- Split large features into cohesive, single-responsibility modules. Extract repeated logic into
238+
helpers instead of copying blocks. Modules imported by both Shell and preferences must remain free
239+
of `St`, `Clutter`, `Gtk`, `Gdk`, and `Adw`; keep process-specific UI under clearly named runtime or
240+
`preferences/` directories.
241+
- Keep the extension's schema ID in `metadata.json` as `settings-schema` and call `this.getSettings()`
242+
without repeating the schema ID in source code.
243+
- Use `St.Icon` or `icon_name` for Shell UI and `Gtk.Image` for preferences. Do not use Unicode emoji
244+
as icons or ASCII strings as progress indicators; use Shell widgets such as `BarLevel` or `St.Bin`.
245+
- Keep generated JavaScript lines at 200 characters or fewer. Prefer self-explanatory names and remove
246+
comments that restate syntax or translate the following statement into prose.
247+
- Avoid subprocesses in the Shell process. Prefer D-Bus for system services and move heavy work to a
248+
separate application. If a subprocess is unavoidable, document why D-Bus is not practical and keep
249+
invocation local, explicit, cancellable, and free of shell interpretation.
207250
- Review every Shexli finding. Fix real ownership/lifecycle defects and record accepted manual-review
208251
findings or analyzer false positives in `EGO_REVIEW.md`.
209252

210-
- Do not add optional calls such as `object?.method?.(...)` unless that method is a real, documented API or the local type intentionally models it. Never use patterns like `this.disconnectObject?.(this)` on objects that do not own that signal connection contract.
253+
- Never use patterns like `this.disconnectObject?.(this)` on objects that do not own that signal
254+
connection contract.
211255
- Do not ship fake behavior. If a UI label, schema description, README entry, or module subtitle says a feature is wired to NetworkManager, ModemManager, UPower, sensors, widgets, or GNOME internals, the code must actually call the relevant API or clearly describe itself as a fallback.
212256
- Keep runtime capability checks honest. Hardware-specific modules must detect missing services/devices at runtime and stay inactive or degrade explicitly.
213257
- Do not scatter `as unknown as ...` casts through feature modules. If GObject construction or Shell internals require a cast, isolate it in a small shared helper/factory with a clear name.

EGO_REVIEW.md

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,38 +17,54 @@ default, and neither clipboard nor OCR data is shared with third parties.
1717
### Capture Tools OCR subprocess
1818

1919
Capture Tools invokes the local `tesseract` executable only after an explicit OCR action. The command
20-
uses `Gio.SubprocessLauncher` arguments directly rather than a shell, supports cancellation and forced
20+
uses `Gio.Subprocess.new()` with an argument vector rather than a shell, supports cancellation and forced
2121
termination, and does not transmit captured content. A companion D-Bus service would add installation
2222
and lifecycle complexity disproportionate to this optional, user-triggered operation, so this remains
2323
a documented subprocess exception.
2424

25+
### Other subprocesses
26+
27+
Aurora Shell does not package executable binaries or invoke a command through a shell. The remaining
28+
process launches use explicit argument vectors and follow direct user actions:
29+
30+
- Aurora Menu launches only commands configured by the user and selected from its menu.
31+
- Volume Mixer opens `gnome-control-center sound` from its Sound Settings item.
32+
- Background Apps first requests the application's `quit` action and uses `flatpak kill <app-id>` only
33+
as a fallback for a user-selected Quit action.
34+
2535
## Analyzer interpretation
2636

2737
Aurora Shell uses `LifecycleScope`, `connectObject()`, and actor ownership for cleanup. Static analyzers
2838
can miss those indirect ownership paths. Treat a warning as a false positive only after tracing the
2939
corresponding enable/disable or create/destroy path; do not suppress or ignore findings by category.
3040

31-
`AuroraDash._isDestroyed` is a narrow compatibility exception. The GNOME Shell base Dash creates raw
32-
connections that may call overridden methods after the subclass begins teardown, so the guard protects
33-
those callbacks until the base actor finishes destruction. Keep the invariant comment and Shell
34-
integration coverage if this exception changes.
41+
### Shexli baseline (2026-07-31)
3542

36-
### Shexli baseline (2026-07-28)
37-
38-
The production ZIP reports five findings, zero errors, and four warnings:
43+
The production ZIP reports four findings, zero errors, and three warnings:
3944

4045
- `EGO-A-005` is the declared clipboard manual review described above.
41-
- `EGO-L-002` is a structural false positive. Capture Tools destroys session actors through its
42-
session `LifecycleScope`; Trash and External Storage are custom actors whose menus, monitors,
43-
cancellables, and signals are released by their `destroy()` overrides before `super.destroy()`
44-
destroys the owned child tree.
45-
- `EGO-L-005` is a custom-actor false positive for the non-null `toggleButton` child expected by
46-
`DashItemContainer`. It remains actor-owned and is released by `super.destroy()`.
46+
- `EGO-L-002` is an ownership-indirection false positive. Clipboard Item releases its menu before
47+
`super.destroy()` destroys the card actor tree; Trash and External Storage release their menus,
48+
monitors, cancellables, operations, and signals before their final `super.destroy()`; Meeting Clock
49+
Pill unregisters and destroys its widget in its own `destroy()` method.
50+
- `EGO-L-005` reports child references retained by the short-lived owner object. Clipboard Item's
51+
actions and the Trash/External Storage `toggleButton` are actor-owned and released by
52+
`super.destroy()`. Meeting Clock Pill destroys its widget, after which the module drops the pill
53+
owner itself.
4754
- `EGO-L-003` is an indirection false positive. The listed signals are owned by `LifecycleScope`,
4855
`connectObject()`, widget destruction, or the corresponding backend/manager `destroy()` method.
49-
- `EGO-L-004` is an indirection false positive. Clipboard History removes its startup idle through
50-
`LifecycleScope`; Aurora Dash removes all six stored source IDs in `destroy()`; Auto Theme Switcher
51-
registers `_cancelScheduledTick()` in its `LifecycleScope`.
56+
This includes the signals in Capture Tools, Clipboard History/Panel, Tray Icons, Aurora Menu,
57+
Bluetooth, Meeting/Weather Clock, Lock Keys, Low Battery, Volume Mixer, App Search Tooltip, Privacy,
58+
Theme Changer, and Auto Theme Switcher. A scope deterministically disconnects its registrations in
59+
reverse order when the owning module or widget is disabled or destroyed.
60+
61+
Capture Tools destroys its session actors directly in `disable()`. Replaceable main-loop sources are
62+
owned by `LifecycleScope` through `ManagedSource`; replacing a source removes the previous one and
63+
disposing the scope removes the active source. Aurora Dash, Dock bindings, Clipboard History, Auto
64+
Theme Switcher, clocks, tray widgets, Bluetooth, and the remaining single-source owners use this
65+
path. Dynamic source collections in Icon Weave and Dock Intellihide remain explicitly removed because
66+
their per-operation ownership is clearer as a set. Shexli does not currently report `EGO-L-004` for
67+
either cleanup form.
5268

5369
Recheck this classification against every new Shexli run. A stable rule ID does not imply that new
5470
locations are automatically accepted.

0 commit comments

Comments
 (0)