doc: revise options spec after empirical verification

Two verification passes, driving real Firefox 153.0.1 under Xvfb rather than
reading docs, changed several load-bearing decisions.

Added back: the keyboard shortcut now acts on selected text. activeTab adds
no install-prompt line (confirmed by running Firefox's own
formatPermissionStrings: ["activeTab","storage","menus"] yields msgs: []),
and ext-commands.js grants activeTab before firing onCommand. Records the
three failure shapes of executeScript, one of which resolves silently to
[null] on parent-process about: pages, and that commands.getAll() reports a
suggested_key as registered even when Firefox has silently overridden it.

Rejected with evidence, in Appendix B: auto-submitting the prompt. The
scheme authenticated navigation provenance when auto-submit needs text
provenance, and a hostile page can smuggle ~16 KB of invisible instructions
into info.selectionText. webNavigation.transitionType is not a security
boundary either -- the marker is consumed on commit, so an uncommitted
urlbar navigation leaves it armed for the next navigation to inherit
(demonstrated at 10.47s), and its freshness guard is dead code. Prefill-only
keeps the human checkpoint that makes the context menu safe.

Also rejected: a bundled extension page as search_url (Firefox refuses to
install), and browser.omnibox -- which works and needs no permissions, but
moves the transmitter of search terms from Firefox to extension code, the
change that puts data_collection_permissions ["none"] at risk. Declined to
minimise AMO exposure.

New Risk 6 records the residual AMO question and that it should be asked
before submission, since a forced searchTerms declaration would add the
install line this design exists to avoid.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
golem 2026-07-30 07:51:43 -06:00
parent f057c66155
commit 3d5ce6a9f4

@ -10,14 +10,15 @@ Add an options panel to the extension, reachable from `about:addons` → the
extension's detail card → the **Options** tab (Windows) / **Preferences** tab
(macOS, Linux). The panel configures two new entry points that this change also
introduces: a context-menu item on selected text, and a keyboard shortcut that
opens a fresh ChatGPT chat.
does the same for the current selection, falling back to a fresh empty chat when
nothing is selected.
The existing `gpt <query>` address-bar search provider is **not** modified and
does not read the settings. See [Why the search provider is untouched](#why-the-search-provider-is-untouched).
New permissions: `storage` and `menus`. Both are in Firefox's no-prompt class,
so the install dialog gains no new lines and `data_collection_permissions:
["none"]` remains truthful.
New permissions: `storage`, `menus`, and `activeTab`. All three are in Firefox's
no-prompt class, so the install dialog gains no new lines and
`data_collection_permissions: ["none"]` remains truthful.
## Goals
@ -30,14 +31,20 @@ so the install dialog gains no new lines and `data_collection_permissions:
## Non-goals
- Making the `gpt <query>` search provider configurable. Out of scope by
decision; it requires either host permissions on `chatgpt.com` or a
navigation-interception mechanism, both of which conflict with goals 3 and 4.
- Making the `gpt <query>` search provider configurable. The declarative
`search_url` is fixed at install time, and the three routes around that were
each rejected: host permissions plus navigation interception (conflicts with
goals 3 and 4), a bundled extension page as `search_url` (Firefox refuses to
install), and `browser.omnibox` (works, but moves the transmitter of search
terms from Firefox to extension code — see Risk 6). `gpt <query>` keeps
behaving exactly as it does today.
- Auto-submitting the prompt on the user's behalf. Rejected on security grounds
after dedicated adversarial testing; see
[Appendix B](#appendix-b-why-auto-submit-was-rejected).
- Moving the `gpt` keyword to `browser.omnibox`. Deliberately declined to
minimise AMO data-collection exposure, not for lack of a working mechanism.
- A toolbar button.
- Named/multiple prompt templates with add-remove-reorder UI. One template only.
- Reading the page selection from the keyboard shortcut. That requires
`activeTab` + `tabs.executeScript`; deliberately excluded (see
[Rejected alternatives](#rejected-alternatives)).
- Migrating to Manifest V3.
- Chrome or other-browser packaging (`AGENTS.md` browser scope).
- User-facing error notifications. The `notifications` permission *does*
@ -77,11 +84,23 @@ The one parameter with quasi-official standing is `q`, because OpenAI's own
"ChatGPT search" browser extension uses `https://chatgpt.com/?q=`. Build on it;
treat everything else as best-effort.
**`q` prefills but does not submit.** Auto-submit was real historically but has
been prefill-only since approximately November 2025, after OpenAI gated it on
the `Sec-Fetch-Site` header as a prompt-injection mitigation. No copy in the
options panel, README, or AMO listing may say the query is *searched*, *sent*,
or *asked* — only that ChatGPT opens **with the query filled in**.
**`q` prefills but does not submit — confirmed for this extension.** Auto-submit
was real historically but has been prefill-only since approximately November
2025, after OpenAI gated it on the `Sec-Fetch-Site` header as a prompt-injection
mitigation.
Verified on 2026-07-29 in the maintainer's own logged-in Firefox profile, on
this extension's actual navigation path: the prompt lands in the composer and
waits. The user presses Enter twice — once to leave the address bar, once to
send. The hypothesis that address-bar navigation might still be favoured
(`Sec-Fetch-Site: none` being the most-trusted value) **does not hold in
practice**.
No copy in the options panel, README, or AMO listing may say the query is
*searched*, *sent*, or *asked* — only that ChatGPT opens **with the query filled
in**. The two-keypress behaviour is an upstream ChatGPT change, not a defect in
this extension, and is documented as such rather than worked around; see
[Rejected alternatives](#rejected-alternatives).
## Architecture
@ -134,7 +153,7 @@ only `mergeSettings()` is unit-tested.
### Manifest additions
```json
"permissions": ["storage", "menus"],
"permissions": ["storage", "menus", "activeTab"],
"background": {
"scripts": ["background/background.js"],
"type": "module",
@ -146,9 +165,9 @@ only `mergeSettings()` is unit-tested.
"browser_style": false
},
"commands": {
"new-chat": {
"ask-chatgpt": {
"suggested_key": { "default": "Alt+Shift+G" },
"description": "Open a new ChatGPT chat"
"description": "Ask ChatGPT about the selected text"
}
}
```
@ -302,8 +321,25 @@ both.
## Data flow
1. **Trigger.** `browser.menus.onClicked` supplies `info.selectionText`, or
`browser.commands.onCommand` supplies no query.
1. **Trigger.** Two entry points, both producing a query string:
- `browser.menus.onClicked` supplies `info.selectionText` directly. No
injection, no `activeTab` involved.
- `browser.commands.onCommand` reads the selection itself: resolve the active
tab with `tabs.query({active: true, currentWindow: true})`, then
`tabs.executeScript` returning `window.getSelection().toString()`. Read
**only** `tab.id` from the query result — reading `tab.url` or `tab.title`
would require the `tabs` permission, which *does* carry an install warning.
Pass **both** `allFrames: true` (a selection inside an iframe is invisible
to the top frame) and `matchAboutBlank: true` (needed for `about:blank`
frames). Have the injected snippet report which frame it came from, and
take the first non-empty result.
**Null-check every element of the result array.** `executeScript` does not
only throw — on parent-process `about:` pages such as `about:addons` and
`about:preferences` it resolves *silently* to `[null]`, so
`results[0].text` is a TypeError waiting to happen. If nothing usable comes
back the query is empty and a blank chat opens.
2. `settings = await load()``load()` already pipes the raw
`storage.sync.get()` result through `mergeSettings()`, so callers always
receive a complete, validated settings object and never need to merge again.
@ -327,6 +363,7 @@ Every path resolves to something useful. No path shows an error to the user.
| Case | Behaviour |
|---|---|
| Selection empty or whitespace | Omit `q` entirely; open a blank chat with the other settings applied. |
| `executeScript` fails on a restricted page (`about:`, `addons.mozilla.org`, `accounts.firefox.com`, `view-source:`, the PDF viewer) | Failure has **three** measured shapes, all of which must be handled: a thrown `Missing host permission for the tab`, a thrown variant naming frames, and a **silent resolution to `[null]`** on parent-process `about:` pages. Catch the throws, null-check the array, and treat all three as an empty selection → open a blank chat. The shortcut must never appear broken just because the user pressed it on a Firefox page. |
| Prompt exceeds the URL budget | Truncate per step 3 above. Never sever a percent-escape or split a surrogate pair. |
| `storage.sync` rejects (quota, or a sanitized backend error) | `try`/`catch` → fall back to `DEFAULT_SETTINGS`. Never block opening ChatGPT. Do not branch on error type: Firefox sanitizes all non-quota backend errors to `"An unexpected error occurred"`. |
| Fresh or signed-out profile returns `{}` | `mergeSettings` layers stored values over `DEFAULT_SETTINGS`, so `{}` yields defaults. |
@ -378,8 +415,18 @@ Add:
- The page is also usable standalone via `browser.runtime.openOptionsPage()`
- Settings persist across a browser restart
- The context-menu item appears only when text is selected, and is absent otherwise
- `Alt+Shift+G` opens a new chat, appears under `about:addons` → gear → Manage
Extension Shortcuts, and can be rebound
- `Alt+Shift+G` with text selected opens ChatGPT with that text, matching the
context menu; with nothing selected it opens a blank chat; on a restricted
page (`about:config`, an AMO page) it opens a blank chat rather than failing
visibly. It appears under `about:addons` → gear → Manage Extension Shortcuts
and can be rebound
- **The shortcut must be verified by actually pressing it.**
`commands.getAll()` reports a `suggested_key` as registered even when Firefox
has silently overridden it — measured: `Ctrl+Shift+Y` (Downloads on Linux)
showed as registered and never fired. A green `getAll()` is not evidence the
shortcut works.
- A selection inside an iframe is picked up by the shortcut, not just top-level
page text
- Each of the three `openIn` values behaves correctly
- **A packaged build's install prompt gains no new lines.** This cannot be
checked with `npm run dev` — install prompts are suppressed for temporary
@ -469,6 +516,21 @@ does not imply CI green.
Sync with Add-ons enabled, synced end-to-end encrypted through their own
Mozilla account. Do **not** write "never leaves your device" unconditionally.
Nothing reaches Zavage Software, so the zero-data-collection claim stands.
Revisit the bare sentence "This extension collects zero user data." It is true
in the plain-English sense and `data_collection_permissions: ["none"]` is
retained, but AMO's definition of transmission is broader (Policy 6, 6.2.2.1),
and the context menu does put selected text into a chatgpt.com URL. Prefer
wording that is specific about what happens — the extension sends the text you
chose to the ChatGPT URL you asked it to open, stores your settings in your own
profile, and reports nothing to Zavage Software — over a blanket claim a
reviewer could read differently. See Risk 6.
- **README** — document the two-keypress behaviour plainly as an upstream
ChatGPT limitation: ChatGPT stopped auto-submitting prompts passed by URL, so
the query arrives typed into the composer and the user presses Enter to send.
State that this is outside the extension's control and that no API key,
server, or intermediary is involved. Do not describe it as a bug or promise a
fix.
- **AGENTS.md** — update Project Structure for the new directories, Build/Test
commands for `test:unit`, and the Testing Guidelines manual checklist.
- **CHANGELOG.md** — entry for the release.
@ -480,12 +542,16 @@ does not imply CI green.
|---|---|
| Rewrite the `gpt` keyword's navigation to honour settings | Needs host access to `chatgpt.com` plus `webRequest`/`tabs`; adds install-prompt lines. |
| Point `search_url` at a bundled extension page that redirects | Firefox requires an http(s) search URL; also a visible redirect flash. Not pursued. |
| Keyboard shortcut reads the page selection | Requires `activeTab` + `tabs.executeScript`. No lighter route exists — a declared `<all_urls>` content script is strictly worse. Excluded by decision; revisitable as its own change. |
| ~~Keyboard shortcut reads the page selection~~ | **No longer rejected — now in scope.** Initially dropped believing `activeTab` carried a permission cost. Verification showed `activeTab` is in Firefox's `OptionalPermissionNoPrompt` class and adds no install warning, and reading a selection on an explicit gesture to build the requested URL transmits nothing, so `data_collection_permissions: ["none"]` holds. The lighter-looking alternatives are all worse: a declared `<all_urls>` content script is a real host permission with a warning. |
| `background.page` HTML shim for ES modules | Works, but unnecessary once `background.type: "module"` was confirmed for MV2. |
| `background.scripts` without `type` | Classic scripts; an `import` statement fails with a compile-time `SyntaxError` and silently kills the script. |
| Curated model dropdown | Slug lists rot within a quarter and every stale entry fails silently to the account default. |
| Migrate to MV3 now | Orthogonal, larger diff. Mozilla has announced no MV2 sunset and promises ≥12 months' notice. |
| Toast/notification on failure | The `notifications` permission adds an install warning. All failure paths degrade silently instead. |
| Content script on `chatgpt.com` that presses Enter for the user | **Rejected on security grounds after dedicated adversarial testing — see [Appendix B](#appendix-b-why-auto-submit-was-rejected). Not a future path; the minimum bar is recorded there so it cannot be revived cheaply.** Three independent attack lenses returned "not viable". The decisive result is that the design authenticates the wrong thing: proving the extension started the navigation is easy, but auto-submit needs to prove the *user authored the prompt*, and a hostile page can smuggle ~16 KB of invisible text into `info.selectionText`. **Decision: document the two-keypress behaviour as an upstream ChatGPT limitation instead.** |
| Opt-in auto-submit via `optional_permissions` | The permission machinery works — verified, `permissions.request()` succeeds from the inline `about:addons` panel and the Firefox install dialog is provably unchanged. It was the *safety* that failed, not the plumbing. Also: an optional host permission is visible on the AMO listing page before install, so it was never free. |
| `browser.omnibox` to make `gpt <query>` honour the settings | **Technically viable and verified working** — needs no permissions, reuses the same `gpt` keyword, lints clean. Rejected for policy exposure: it moves the transmitter of search terms from Firefox to extension code, which is the specific change that puts `data_collection_permissions: ["none"]` at risk (AMO 6.2.2.1 classes search terms as personal data). Declining it keeps Firefox as the transmitter. Revisit only after AMO answers the declaration question. |
| `search_url` pointing at a bundled extension page | Impossible, not merely discouraged. The Firefox schema and addons-linter both constrain `search_url` to `https://` or `http://localhost`, and Firefox 153.0.1 **refuses to install the extension entirely** ("Extension is invalid") for `moz-extension://`, a relative path, or a root-relative path. `search_url_post_params` only changes the method; `search_form` is scheme-restricted and unsupported on Firefox. |
## Risks
@ -504,6 +570,26 @@ does not imply CI green.
4. **MV2's long-term future is not guaranteed.** The strongest Mozilla
commitment is dated March 2024 and nothing newer restates it. Mitigated by
keeping the code MV3-portable.
5. ~~The shortcut's selection-reading depends on a `commands` invocation
conferring `activeTab`.~~ **Resolved, positive.** Firefox's
`ext-commands.js` calls `tabManager.addActiveTabPermission(tabTracker.activeTab)`
before firing the event, and a real synthesized keypress in Firefox 153.0.1
read page text with `activeTab` as the only host-ish permission. No longer a
risk.
6. **`data_collection_permissions: ["none"]` carries residual policy risk for
the selection paths.** AMO Policy 6 defines transmission as data handled
outside the add-on or local browser, and 6.2.2.1 names search terms as
personal data. Once extension code reads page text and puts it in a
chatgpt.com URL, a reviewer could argue `websiteContent` applies. The
mitigating reading is 6.2.2.2's implicit consent — a user-initiated action
through a clearly labelled control — but 6.2.2.2(5) is explicitly
discretionary and no published Mozilla guidance resolves it. This design
deliberately minimises exposure by **not** moving the `gpt` keyword to
`browser.omnibox`, leaving Firefox as the transmitter for the address-bar
path. If AMO ever requires declaring `searchTerms`, the install prompt gains
"The developer says this extension collects: search terms" — which would
conflict with this design's no-new-install-lines goal. Worth asking AMO
before submission, not at submission.
## Appendix A: Verification findings
@ -580,7 +666,7 @@ by running commands against copies under `/tmp`; the repo itself was unmodified.
| Parameter | Rating | Basis |
|---|---|---|
| `q` prefills | **Solid** | Dated 2026 sources; maintained tooling; OpenAI's own extension uses it; a live probe returned HTTP 200 with all parameters preserved. |
| `q` auto-submits | **Refuted** | Prefill-only in every dated report since 2025-11-11. Tenable TRA-2025-22 documents OpenAI gating auto-submit on `Sec-Fetch-Site`. |
| `q` auto-submits | **Refuted, and confirmed by first-hand test** | Prefill-only in every dated report since 2025-11-11. Tenable TRA-2025-22 documents OpenAI gating auto-submit on `Sec-Fetch-Site`. Reproduced 2026-07-29 in the maintainer's logged-in Firefox via this extension's own search-provider path. |
| `model` honoured alongside `q` | **Uncertain** | An unresolved 2024-12-28 report says `q` forces GPT-4o regardless; counter-evidence exists from 20242025; no 2026 source positively confirms. Maintained tooling grades it experimental. |
| Valid `model` slugs | **Uncertain** | No authoritative list; sightings rot (`gpt-4`, `o1-preview`, `gpt-4o`, `gpt-5-thinking`, `auto`, `instant`, `thinking`). |
| `temporary-chat=true` | **Uncertain** | Community-confirmed originally; multiple reports of it not applying or reverting after the first message; OpenAI reworking the feature. |
@ -594,11 +680,129 @@ by running commands against copies under `/tmp`; the repo itself was unmodified.
### Unresolved
- Whether Firefox search-provider navigations land in the favoured
`Sec-Fetch-Site: none` bucket for auto-submit. Inference from the Fetch
Metadata spec, contradicted by a dated report of a default-search string
losing auto-submit. Settled only by the manual parameter reality check.
- ~~Whether Firefox search-provider navigations land in the favoured
`Sec-Fetch-Site: none` bucket for auto-submit.~~ **Resolved 2026-07-29,
negative.** Tested in the maintainer's logged-in Firefox on this extension's
own path: the prompt only prefills. Being in the most-trusted navigation
bucket does not restore auto-submit.
- Whether Firefox enforces Chrome-style `storage.sync` write-rate quotas. The
constants exist in the schema but no rate-limiting logic appears in the Rust
backend. Made moot by debouncing writes.
- Whether `Alt+Shift+G` collides with OS-level or other-add-on shortcuts.
- Whether AMO accepts `required: ["none"]` once extension code reads page text
(Risk 6). Ask AMO; 6.2.2.2(5) is discretionary and unresolved in published
guidance.
### Round two (2026-07-30)
A second verification pass, again with adversarial challenge. Browser claims
tested by driving real Firefox 153.0.1 under Xvfb via Marionette — real
keypresses, real address-bar input, real permission doorhangers — not headless
approximations.
- **A `commands` keyboard shortcut confers `activeTab`.** Firefox's
`ext-commands.js` calls `tabManager.addActiveTabPermission(tabTracker.activeTab)`
before firing `onCommand`. A synthesized real keypress read page text with
`permissions: ["activeTab", "menus"]` and nothing else; the identical call
from a no-gesture background path threw `Missing host permission for the tab`.
- **`executeScript` fails in three shapes**, one silent: two distinct throws plus
a resolution to `[null]` on parent-process `about:` pages.
- **`suggested_key` loses silently to Firefox's built-ins** while
`commands.getAll()` still reports it registered. `Ctrl+Shift+Y` never fired.
- **`activeTab` adds no install-prompt line** — re-confirmed by running
Firefox's own `ExtensionData.formatPermissionStrings` inside 153.0.1:
`["activeTab", "storage", "menus"]` yields `msgs: []`, byte-identical to
requesting nothing.
- **`search_url` cannot be an extension page.** Schema and addons-linter
constrain it to `https://` or `http://localhost`; Firefox 153.0.1 refuses to
install ("Extension is invalid") for `moz-extension://`, relative, and
root-relative paths.
- **`browser.omnibox` works with zero permissions** and can reuse the `gpt`
keyword alongside the registered engine, linting clean. Declined for policy
exposure, not capability — see [Rejected alternatives](#rejected-alternatives).
- **`permissions.request()` works from the inline `about:addons` options page.**
Bug 1382953 was fixed in Firefox 61. The grant does not reload the inline
page. It must be the **first statement** in the click handler: awaiting a real
API call first loses the gesture and rejects with `permissions.request may
only be called from a user input handler`, and the gesture does not survive
`runtime.sendMessage` delegation to the background.
- **An optional permission cannot reach the Firefox install dialog** — the
install prompt's info object has no top-level `optionalPermissions`, the only
key the string formatter reads for that section. Verified with real XPI
installs plus an order-reversed control.
- **But the AMO listing page does change.** With an optional host permission the
listing gains "Optional permissions: Access your data for chatgpt.com",
*before* install. Confirmed against a live MV2 control (SponsorBlock). Note
publishing the already-committed `data_collection_permissions` will surface
that card by itself, independent of anything here.
## Appendix B: Why auto-submit was rejected
Recorded in full so the feature cannot be revived without re-clearing the same
bar. Three adversarial lenses — prompt injection, permission integrity, AMO
policy — independently returned "not viable".
**The design authenticated the wrong invariant.** "Did the user initiate this
navigation?" is provable. "Did the user *author this text*?" is what auto-submit
needs. For the two entry points this spec builds, the text comes from the page
DOM — so extension-initiated provenance is not merely insufficient, it is
inverted: the paths the extension can prove it started are exactly the paths
where a hostile page supplies the payload.
**Selection smuggling (confirmed empirically).** A hostile page can place ~16 KB
of invisible instructions into `menus.onClicked`'s `info.selectionText` while the
user sees only innocuous bait text. The user's click is genuine; the prompt is
the attacker's. The only thing standing between an attacker-chosen prompt and
the user's logged-in ChatGPT account is that the text sits visibly in the
composer until a human presses Enter. **Auto-submit deletes that checkpoint.**
This is also why the context menu and shortcut remain safe *as long as they only
prefill*.
**`webNavigation.transitionType` is not a security boundary.** The signal looks
right — a `gpt` search emits `transitionType: "generated"` with
`transitionQualifiers: ["from_address_bar"]`, and content cannot forge those
values directly. But the marker is stashed per-tab and consumed only when a
document *commits*, so a urlbar navigation that never commits (HTTP 204, a
download, an external-protocol handler, a cancelled load) leaves it armed and
**the next navigation to commit inherits it, whoever started it.** Demonstrated:
a page navigating itself via its own inline timer was reported as `typed` +
`["from_address_bar"]` **10.47 seconds** after the urlbar action, against a
control of `link` + `[]`. The intended 5-second freshness guard is dead code —
`data.time - Date.now() > RECENT_DATA_THRESHOLD` is always false (inverted
subtraction, and a microsecond-scaled constant compared against a millisecond
delta) — so the stale marker never expires and an attacker need win no race: it
arms a timer on blur and waits. Separately, `webNavigation` needs no host
permission to see full URLs, so granting it would expose the user's entire
real-time browsing history for a gate that does not hold.
**Same-tab substitution defeats the pending-record scheme (confirmed).** Even
restricted to omnibox-typed text, `omnibox`'s default disposition is the current
tab. The outgoing hostile document receives `beforeunload` — a deterministic
signal that the extension's navigation has begun, not a race — arms
`setTimeout(…, 0)`, and sets `location.href` to `chatgpt.com/?q=EVIL`. Measured
beacon sequence: `ext-navigate-start[q=HONEST]``beforeunload fired`
`timer0: set location OK``chat-loaded[q=EVIL-TIMER]`. The extension believes
it navigated the tab to the user's prompt; the document actually there is the
attacker's, and the pending record is still armed. (Opener-based substitution is
dead in Firefox 153 — chatgpt.com sends `COOP: same-origin`, and cross-origin
location writes are denied regardless.)
**What was *not* the problem.** The plumbing all works: `permissions.request()`
from the inline panel, the unchanged Firefox install dialog, optional
data-collection permissions in Firefox 153. The rejection is about safety, and
secondarily about AMO policy — not about feasibility.
**Minimum bar if ever revisited.** Auto-submit may only ever apply to text the
extension can attest the user typed into browser chrome (an `omnibox`
`onInputEntered` value — a page can neither write the omnibox, fire the event,
nor observe it). Never to `info.selectionText`. It must open a **new** tab, never
navigate the current one. The pending map must stay in memory only — never
persisted, because tab ids restart low at browser start, creating a replay
hazard. Authorization must be re-validated synchronously at the instant of
submit against the live `location.search`, never latched, since chatgpt.com is a
client-routed SPA and the content script survives same-document navigation.
Single attempt, ~1.52 s timeout, `allFrames: false` with an explicit top-frame
assertion, abandon permanently on any navigation event, never retry, never a
fallback chain. And the AMO declaration question (Risk 6) must be answered
first, because it decides whether the feature is compatible with a clean install
prompt at all.