Perform a manual code review of a GNOME Shell extension simulating what an EGO reviewer checks. Analyzes lifecycle correctness, signal disconnection, resource cleanup, async safety, security patterns, and code quality. Use before EGO submission, when reviewing GNOME extension code, or for pre-submission review.
Perform a manual code review of a GNOME Shell extension simulating what an EGO reviewer checks. Analyzes lifecycle correctness, signal disconnection, resource cleanup, async safety, security patterns, and code quality. Use before EGO submission, when reviewing GNOME extension code, or for pre-submission review.
ego-review
Simulated EGO reviewer code review for GNOME Shell extensions.
This skill guides a thorough manual code review that covers everything an
extensions.gnome.org reviewer checks, plus common rejection patterns learned
from real submissions.
Reviewer Context
Understanding the real EGO review process helps calibrate review severity:
Primary reviewer processes 15,000+ lines of code per day across many extensions
Lifecycle cleanup is the #1 rejection cause — not security, not metadata
AI slop triggers deeper scrutiny: When a reviewer spots one AI-generated pattern, they examine the entire extension more carefully. A clean extension with one pkexec usage gets more leeway than one with typeof super.destroy === 'function' guards everywhere
The domino effect: Bad patterns in published extensions get copied. Reviewers are stricter on patterns they've seen spread (excessive try-catch, empty catches, TypeScript JSDoc)
Developer understanding matters: When asked to explain code, developers who respond with more AI-generated text are immediately flagged. Extensions where the developer clearly understands the code get more benefit of the doubt
Phase 0: Automated Baseline
Run ego-lint on the extension directory (invoke the ego-lint skill)
Capture all FAIL/WARN/PASS results
For each FAIL/WARN, note the check name — Phases 2-5 should NOT re-report
issues already caught by ego-lint (avoid duplication)
Check constructor constraints (no resource allocation in constructor)
Reference the resource-tracking findings from Phase 0 lint. ego-lint
already ran build-resource-graph.py and check-resources.py. Use the
resource-tracking/* findings from the lint results as the starting point.
Do NOT re-run build-resource-graph.py.
For each resource-tracking FAIL/WARN from lint: read the cited file:line
to verify it's a true leak. Classify as: TRUE LEAK (blocking) | JUSTIFIED
(note why) | FALSE POSITIVE (skip). For true leaks, include the fix in the
report.
For ownership chains: if lint reports orphans, verify parent calls
child's destroy() in its own disable()/destroy() and that destroy
order is reverse of creation. If lint reports 0 orphans, do a brief
spot-check of 1-2 ownership chains to verify graph accuracy, but do not
perform a full ownership walk.
Resource tracking table: if the report needs a resource tracking table,
build it from the lint JSON's resource-tracking/* findings rather than
re-running build-resource-graph.py --format=table.
If the graph reports 0 orphans and complete ownership chains: abbreviate
this phase — focus on async guards and cleanup ordering below
Async guard verification: For every await in enable-path code, verify
a _destroyed check follows the resume point
Verify cleanup ordering (reverse order of creation)
Check for _destroyed flag pattern in async operations
Verify session mode handling if applicable
Phase 3: Signal & Resource Audit
Abbreviate this phase if Phase 2 found 0 orphans AND Phase 0 lint has
no resource-tracking/* or lifecycle/* FAILs/WARNs. In this case, do a
single spot-check: pick 1 resource entry from the graph and verify by
reading the cited file:line that create/destroy are correctly paired.
Otherwise, for each resource-tracking or lifecycle FAIL/WARN from lint,
verify by reading the cited code — focus on issues ego-lint cannot judge
semantically (e.g., whether a cleanup pattern is architecturally correct).
Check for resource types lint still misses: custom cleanup methods
(_cleanup(), _teardown(), _clear()) not recognized by the resource
graph. GSettings signal leaks and D-Bus connectSignal leaks are now
automated — do not re-check manually.
Only do a full manual grep if lint reported orphans AND you suspect the
graph missed resources after the spot-checks above.
Look for AI code artifacts (imaginary APIs, hallucinated imports)
Check for excessive logging
Verify private API usage is documented
Check error handling patterns
Hallucinated API cross-reference: Verify that every API method called actually exists in the declared shell-version range. Common hallucinations: Meta.Screen, St.Button.set_label(), GLib.source_remove(), Clutter.Actor.show_all()
GObject pattern verification: Check that registerClass calls have GTypeName, that destroy() chains to super.destroy(), that GObject properties emit notify
Prefs.js specific checks: Verify fillPreferencesWindow() exists, GTK4/Adwaita patterns used correctly, no deprecated GTK3 patterns, no Shell imports
Check for var declarations (should use const/let)
Check import ordering (GI → resource → extension)
Check for console.log() (banned — only debug/warn/error allowed)
Reviewer perspective notes:
When a reviewer sees console.log(), they think "developer forgot to clean up debug logging"
When a reviewer sees module-level let, they think "will this persist across enable/disable cycles?"
When a reviewer sees try { super.destroy() } catch, they think "AI-generated code"
1a. Automated items (24 of 46): Items 1-2, 4-5, 8, 11-12, 14-15,
18-26, 28, 34, 36, 41-42, 44. Pull the trigger count directly from
Phase 0 lint — do NOT re-read descriptions or re-search the code.
1b. Manual-only items (15 of 46): Items 3, 7, 9, 13, 27, 31-32,
35, 37-40, 43, 45-46. Search extension source for each pattern —
these require semantic judgment ego-lint cannot provide.
1c. Partial items (7 of 46): Items 6, 10, 16-17, 29-30, 33. Use
ego-lint's result as a starting point, then apply manual judgment for
aspects ego-lint cannot cover.
Score: N/46 triggered — [ADVISORY | BLOCKING]
Triggered items: list with file:line
Assessment: interpretation
Section 6: Submission Readiness
Ready to submit? [YES | NO] — N blocking issues remain
Action items (priority order):
First thing to fix
Second thing to fix
## Rejection-Risk Scoring Model
Calculate based on findings:
| Finding | Risk Points |
|---------|-------------|
| Each BLOCKING lifecycle issue | +3 |
| Each BLOCKING security issue | +4 |
| Each BLOCKING API hallucination | +5 (indicates AI) |
| AI slop score >= 3 | +5 |
| AI slop score >= 6 | +10 |
| Each ADVISORY issue | +1 |
| Justified advisory (with docs) | +0 |
**Verdict thresholds:**
- 0-2 points: **LIKELY APPROVED** — minor or no issues
- 3-6 points: **NEEDS REVISION** — fixable, resubmit after changes
- 7-12 points: **LIKELY REJECTED** — significant issues
- 13+ points: **LIKELY REJECTED** — fundamental problems or AI-generated
**Rough correspondence with ego-simulate scores:**
| ego-review risk | ego-simulate score | Interpretation |
|-----------------|-------------------|----------------|
| 0-2 | 0-4 | Likely to pass |
| 3-6 | 5-9 | May need revision |
| 7+ | 10+ | Likely rejected |
The scales use different inputs (ego-review: finding category points;
ego-simulate: taxonomy weights), so this mapping is approximate. When both
tools are run, prefer ego-review's assessment as the authoritative verdict.
## When to Use
- Before submitting to extensions.gnome.org
- After making significant changes to an extension
- When reviewing someone else's GNOME extension code
- As a learning tool for new extension developers