General source code refactoring

This commit is contained in:
2026-09-17 12:07:01 -03:00
parent 4421b28efe
commit ac817a69d4
92 changed files with 18211 additions and 19394 deletions

198
docs/architecture.md Normal file
View File

@@ -0,0 +1,198 @@
# Architecture and Compatibility Contracts
This document describes the integrated modular implementation. Verification and
preexisting risks are tracked in [refactoring.md](refactoring.md).
## Sources, Outputs, and Runtime Boundaries
- Editable sources are factory modules under `src/`; the former monoliths were removed.
- `scripts/sources.cjs` explicitly lists modules for each context. `compose.cjs`
validates syntax, serializes CSS, wraps the modules in an IIFE and calls the
corresponding application factory/start method. `build.mjs` writes two packages.
- `dist/chromium` serves Chrome and Edge; `dist/firefox` serves Firefox. Do not edit outputs.
- Both manifests use V3. Chromium has a classic background service worker; Firefox
uses `background.scripts`. Neither entry point is declared as a module.
- Content scripts run at `document_idle`, in all matching frames on the two
configured Fusion welcome pages. Their code also checks the hostname/path.
- There are no extension UI pages, injected page-context scripts, custom CSP,
declared execution world, or web-accessible resources.
- Permissions remain `cookies` and `storage`, with host access limited to the
configured Fusion host and `spa.oracle.com`.
Content owns the DOM and per-frame state. Background owns privileged cookies,
temporary tabs, extension storage and SPA requests. These contexts communicate
using serializable runtime messages, not shared variables. Page localStorage is
different from extension storage and is not a safe place for credentials.
## Current Ownership and Side Effects
| Context | Responsibilities | State and effects |
| --- | --- | --- |
| Content | Fusion tile and main/preferences/consumption modals | DOM nodes, injected CSS, keyboard/pointer/resize listeners, MutationObserver |
| Content | Opportunity query and presentation | Filter sets, sorting, search, request versions, column formatters, dashboard animation |
| Content | Products UI | Current request ID, product map, expanded rows, progress, last update |
| Content | Consumption UI | Plan state, per-plan/period trend cache, chart navigation and selected series |
| Content | AI summary | Per-opportunity in-flight state and summary; fresh generation on expansion |
| Content | Authentication/debug | General and scoped token promises, SPA authorizations, request logs, visibility flag |
| Background | Message dispatcher | Synchronously registered listener, asynchronous replies using `return true` |
| Background | Products | Four workers, per-opportunity in-flight map, lazy cache read and serialized writes |
| Background | Cookie/session recovery | Inactive tabs, polling/retry delays, cleanup in `finally` |
| Background | SPA operations | HTTP/JSON parsing, account/plan extraction and debug envelopes |
Closing the main modal hides it rather than destroying it. Many requests can
continue while it is hidden. Version and request-ID checks suppress stale UI
updates; they do not abort network traffic. No AbortController is currently used.
Some listeners have window guards; there is no general disposal lifecycle.
## Authentication and API Routing
Never substitute one token family for another or assume an opaque token is a JWT.
Production tokens are held in memory, not deliberately persisted by the extension.
| Credential | Acquisition | Consumers | Lifetime/recovery |
| --- | --- | --- | --- |
| XSRF | Background cookie lookup, then content `document.cookie` fallback | `x-xsrf-token` for both tokenrelays | Cookie recovery tab on missing/invalid response; existing retry rules |
| Fusion general | GET `/fscmRestApi/tokenrelay` | Opportunity POST and product GET Bearer | Content promise/cache; no expiry-based refresh equivalent to Sales Common |
| Sales Common | GET `/fscmRestApi/tokenrelay?scopesfq=urn%3Aopc%3Aresource%3Afusion%3Aeeho%3Asales-common%2F` | AI summary Bearer | In-memory `expires_in`, shared in-flight acquisition; at most one summary retry after 401/403 |
| ACCP-TS | SPA consumer service `uiaasHeader.value` | Account summary authorization | Retrieved with the consumer service response; expiry recorded, not automatically acted on |
| OCICONS-TS | SPA consumer service `uiaasHeader.value` | Account plans and consumption trend authorization | Same service response, distinct credential from ACCP-TS |
Fusion calls use `https://eeho.fa.us2.oraclecloud.com`; SPA calls use
`https://spa.oracle.com/oalcrm/web/api/`. Preserve `credentials: include` and
operation-specific headers rather than unifying all calls under a default token.
| Request | Method/path | Important contract |
| --- | --- | --- |
| requestOpportunities | POST `/crmRestApi/searchResources/11.13.18.05/custom-actions/queries` | RecordSet OR plus EffectiveDate AND, original fields, Preference transient, pages of 15 |
| requestOpptyProducts | GET `/crmRestApi/rest/rv:be91c002-2e5d-4ed2-a37e-e0c837bf141f/en/11.13.18.05:9/opportunities/{number}/child/ChildRevenue` | Encoded identifier, original query/fields/order, count/offset pagination, maximum 250 pages |
| requestGenerateAiSummary | POST `/api/sales-common/gen-ai/v1/summarizations/generate` | `{path,promptCode,object,puid}`; prompt `cx.sales.generate_opportunity_summary`; only summaryText displayed |
| requestConsumerTokenService | GET `g2m-consumer-application/consumerTokenService` | No-store; parse JSON; HTML 200 recovery via inactive SPA UI tab; maximum five HTTP attempts |
| requestAccountSummary | POST `provider-proxy/g2m-account/service/accountsummary` | ACCP-TS; `[{key:"regId",value:partyNumber}]` |
| requestAccountPlans | POST `provider-proxy/g2m-consumption/service/v1/account/accountPlans/{id}` | OCICONS-TS; original hierarchy/products/language/planStatus payload |
| requestConsumptionService{Period} | GET `provider-proxy/g2m-consumption/service/v1/plan/trend/{subPlanNum}?quarter=All&timePeriod={period}` | G2m-Authorization; quarterly/monthly/weekly/daily; weekly default |
SPA parsing removes BOM and the existing anti-JSON-hijacking prefixes before
JSON.parse. Content uses response.json directly. Do not silently unify these behaviors.
POST retry policies require operation-specific review; do not add generic retries.
## Message Contracts
All names below have the `opportunitiesExtension.` prefix. Unknown messages return
false without a response. Supported background messages return true immediately
and later call sendResponse. `platform/background-api.js` centralizes the existing
callback/promise differences. `platform/content-messages.js` retains the original
cookie callback path as well as general runtime messaging; no browser behavior
was silently changed during extraction.
| Message | Input | Result |
| --- | --- | --- |
| getXsrfToken | type | ok, cookieName, token, matchedCookieNames, lookupDetails; error on failure |
| refreshXsrfCookie | type; sender tab context | ok, refreshed, cookieName, durationMs, error |
| requestOpptyProducts | requestId, accessToken, optyNumbers, cacheMaxAgeMs | ok, requestId, completed, total, failed, cacheHits; error envelope on dispatcher failure |
| opptyProductsProgress | Background to sender tab/frame | requestId, state, counters; per-item optyNumber/items/fromCache/cachedAt or error |
| requestConsumerTokenService | type; sender tab context | ok, authorizations.accountSummary/accountPlans, debugRequests |
| requestAccountPlans | partyNumber, authorizations | ok, accountPlanId, plans, debugRequests |
| requestConsumptionTrend | subPlanNum, period, authorization | ok, subPlanNum, period, items, debugRequests |
SPA failures return `{ok:false,error,debugRequests}`. Progress states include
`running`, `complete` and `complete-with-errors`; content additionally tracks idle
and error states. A new content request ID rejects progress from an older job.
Do not expose a caller-provided arbitrary request URL through the background.
## Storage Contracts
| Location/key | Value |
| --- | --- |
| Page localStorage: `opportunities-extension-filter-preferences` | JSON object: period, stages, statuses, customerKeys, ownerKeys, customerSearch, ownerSearch, tableSearch; unset customer/owner selections may be null |
| Page localStorage: `opportunities-extension-theme` | `light` or `dark` |
| Page localStorage: `opportunities-extension-opportunity-type-preferences` | JSON object: opportunityTypeValues, opptyProductsCacheHours |
| Extension storage.local: `opportunitiesExtension.opptyProductsCache.v1` | Object indexed by OptyNumber, each entry `{fetchedAt,items}` |
Product TTL defaults to 24 hours and is capped at 30 days in background. Storage
failures are tolerated for the active view. Recreating background reloads stored
products but does not restore in-flight jobs. Cache TTL does not physically delete
expired entries. Account/container isolation limitations are tracked separately.
## UI Contracts
Preserve labels/IDs/classes, column order, URLs, clipboard behavior, sort/filter
semantics, formatting, theme colors, keyboard shortcuts and row expansion.
WON includes WON_PENDING without rewriting StatusCode. Owner uses first/last name
with the existing fallbacks. Changing reference resets customers/owners to all;
closing/reopening preserves search. Fiscal years begin in June; RENEWALS currently
has no date range and therefore does not load opportunities.
Preserve SVG chart data normalization, animation, tooltips and series selection.
AI text is inserted with textContent. Debug masking and DOM isolation have known
limitations; see the separate security backlog rather than assuming full redaction.
## Module Ownership and Dependencies
`content/main.js` and `background/main.js` only initialize. Each `application.js`
is a composition root: constructs feature-owned state and factories, wires explicit
function dependencies, then exposes `start`. The returned components are available
to isolated tests, not published on the page/global object in production.
Factories do not import or instantiate peers. Features receive service/auth ports;
services/auth receive transport/platform ports; pure models receive data. The
composition roots supply callback notifications for UI updates and debug events,
preserving existing interactions without module-import cycles. Shared messages
are the only source included in both contexts, with distinct instances/state.
Some stateful UI collaborators intentionally share a feature-owned state object;
these objects are created per application, never stored in a global registry.
| Original responsibility | Integrated module(s) | Reason for boundary |
| --- | --- | --- |
| content initialization/tile | `content/main.js`, `fusion-page.js` | Page lifecycle and route guard |
| modal/theme/CSS | `content/modal.js`, `theme.js`, `styles.js`, `styles/redwood.css` | DOM shell separate from operations and cascade |
| fiscal dates/query payload | `features/opportunities/fiscal-periods.js`, `services/fusion/opportunities.js` | Pure date rules vs HTTP operation |
| table/search/filters/formatting/dashboard | `features/opportunities/` | Related presentation responsibilities, explicit state |
| saved settings | `features/preferences/storage.js`, `view.js` | Serialization/fallbacks vs dialog |
| token families | `auth/content-session.js`, `sales-common.js`, `token-relay.js`, `spa-credentials.js` | Different credentials and lifetime policies |
| cookie lookup/recovery, SPA session | `auth/xsrf-*.js`, `consumer-session.js` | Privileged recovery separate from UI |
| product queue/cache | `features/products/background-jobs.js`, `cache.js`, `services/fusion/products.js` | Scheduling, persistence and pagination |
| product progress/details | `features/products/content-jobs.js`, `details.js` | Message consumer vs row rendering |
| AI summary | `features/ai-summary/content.js`, `state.js` | Per-opportunity lifecycle, safe text and auth port |
| consumption | `features/consumption/`, `services/spa/` | Plans, chart model/view/controller vs HTTP operations |
| debug | `features/debug/content-log.js`, `content-view.js` | Retention/formatting vs DOM presentation |
| background dispatcher | `background/messages.js`, `shared/messages.js` | Stable serializable message contracts |
| browser APIs | `platform/background-api.js`, `content-messages.js` | Only APIs actually used, not a universal wrapper |
The background composition deliberately has no DOM dependency. Listener registration
is synchronous, before any storage read or request. Worker restart reconstructs
volatile state and lazily reads the existing product cache; tokens are not persisted.
Content effects (DOM, fetch, storage, animation) enter through the environment at
the composition root. localStorage access stays lazy to retain blocked-storage
fallbacks. Tests may inject effects or individual callback ports without changing
source strings. Time-sensitive modules are tested with isolated clocks.
## Adding a Feature
1. Define its message/API/storage contract and synthetic expected behavior first.
2. Add a factory under the owning feature, or a service under the correct host.
Accept narrow function dependencies; do not call another factory inside it.
3. Add its source path to the appropriate array in `scripts/sources.cjs`. Source
inclusion and duplicate factory checks run automatically.
4. Construct it in that context's `application.js`, supplying auth, platform and
render callbacks explicitly. Extend `background/messages.js` only if crossing
contexts is necessary; do not expose arbitrary destinations.
5. Add a `tests/*.test.cjs` suite using `tests/support/application.cjs` for composed
integration or `vm.Script` for an individual pure factory. Use only synthetic
data and fail unexpected network requests.
6. Run tests, build and `--check`; inspect the local fixture and load both packages
in the target browsers. Record any browser not available for verification.
As a concrete example, adding a new consumption period should first define its
request/data/authorization contract and synthetic tests, then extend the period
allowlist and normalization/presentation through the existing trend message. It
must not add a URL-proxy message or reuse the general Fusion token. These edits
belong in the consumption feature and SPA service, not the bootstrap.
## Source vs Generated Code
Distribution `content.js` and `background.js` are generated bundles and may remain
large. No copy/export/execute JavaScript generator feature was found in the source.
AI summaries are text, not executable JavaScript. No empty generators subsystem
was added. Build serialization uses JSON.stringify for CSS, retains quotes,
backslashes, Unicode and line breaks, and is syntax-checked by vm.Script. Visual
fixtures now load composed scripts normally; they no longer eval rewritten sources.

63
docs/browser-smoke.md Normal file
View File

@@ -0,0 +1,63 @@
# Native Browser Validation
This checklist complements the synthetic tests; it is not evidence of a native
pass until executed and recorded separately for each browser.
## Preconditions
- Use an authorized test profile/account. Do not delete cookies, sign out, change
account security settings or inspect credential values to force failure cases.
- Connect Chrome/Edge through Settings > Computer use for assisted browser tests.
Firefox needs a separate supported/manual test session.
- Run `node scripts/build.mjs --check` and `node scripts/test.cjs` first.
- Load `dist/chromium` using Load unpacked in Chrome/Edge, or load
`dist/firefox/manifest.json` as a temporary Firefox add-on. Verify the source
directory and version 1.0.1; avoid testing a previously installed stale copy.
- Keep debug secrets hidden. Record only status codes, request names, durations
and sanitized errors. Do not export real tokens, response bodies or account data.
- AI generation and tests requiring a deliberately expired session must use an
explicitly authorized test environment. Do not trigger live writes solely to
complete this checklist.
## Checklist
| Check | Expected result |
| --- | --- |
| Package load | No manifest/background/content syntax errors; no additional permissions |
| Both Fusion welcome routes | Exactly one tile before the add-icon item; no tile on unrelated pages |
| Modal open/close/reopen | Same search value and matching results; Esc closes the correct dialog |
| Filters and sorting | Correct customer/owner/stage/status/text combinations; WON includes WON_PENDING |
| Reference change | Customer and owner reset to all; stale responses do not overwrite new results |
| Preferences | Save/reopen retains selected record sets and cache duration; restore original settings afterward |
| Products | Progress completes; badges/counts and expanded rows agree; warm cache avoids redundant product fetches |
| AI in authorized test environment | Summary appears without replacing products; concurrent expansions share scoped-token acquisition; reopen uses valid token |
| Consumption | Dialog opens promptly, weekly chart by default; plan/period changes and series filters work |
| Themes and layout | Light/dark controls work; check long names, WON_PENDING and full update timestamps |
| Debug | Expected operation names/statuses; sensitive values remain hidden |
| Background restart | Close its inspector, allow suspension, then trigger a message; listener responds and stored product cache is reloaded |
| Multiple tabs | Progress remains scoped to requesting frame/job; no duplicate UI/listeners from normal navigation |
| Firefox containers | Test only in authorized containers; record current cookie/cache isolation limitations without silently changing behavior |
| Natural session expiry | When encountered, record recovery outcome and temporary-tab cleanup without revealing cookies or forcing sign-out |
Background interruption does not currently resume in-flight jobs. Do not mistake
that known limitation for a new regression or claim a mocked restart proves native
worker suspension. Any differences must be compared with the pre-refactor behavior.
## Execution Record
2026-09-17 preflight:
| Target | Discovery | Native test result |
| --- | --- | --- |
| Chrome | Executable version 153.0.8010.37; automation not connected | Blocked pending connection |
| Edge | Executable version 153.0.4234.32; automation not connected | Blocked pending connection |
| Firefox | No executable found at the checked standard Program Files path; installation not confirmed | Not run |
The running-process diagnostic was denied by the environment, so browser running
state was not established. Connected targets were Brave and the in-app browser;
neither was substituted for this native matrix. The user chose to connect
Chrome/Edge. All five local suites and the output consistency check passed again.
No real Fusion/SPA calls, runtime changes, release or commit were made in preflight.
For each completed run, append date, browser/version, package path, checklist
outcomes, sanitized errors and remaining gaps. Never mark unexecuted checks passed.

184
docs/refactoring.md Normal file
View File

@@ -0,0 +1,184 @@
# Incremental Refactoring Record
## Stage 1: Characterization Baseline
Stage 1 initially added tests and documentation only, before any runtime extraction.
The following records that historical baseline. The completed integration and
remaining verification gaps are recorded under "Completed Extraction Stages" below.
### Coverage Added
`tests/characterization.test.cjs` contains 22 test cases using Node's built-in
test runner, assert and vm. `tests/support/characterization.cjs` provides isolated
fixtures: no real browser cookies, account identifiers, tokens or network access.
Unexpected fetches reject rather than falling through to global fetch.
- Exact current manifests and source/output equality, with classic-script syntax validation.
- Disallowed-origin initialization and reinjection when the tile already exists.
- Fiscal boundaries, leap years, rolling quarters and unsupported RENEWALS.
- Full opportunity query payload, pagination offsets/counts and stale request versions.
- Filter storage round-trip, WON_PENDING, search, numeric sort, reference reset and malformed preferences.
- Missing-cookie tokenrelay recovery and existing 400/401/403 retry limit.
- Chrome callback and Firefox promise mocks for background dispatch, storage, cookies and tabs.
- Product pagination, default/custom TTL, concurrency limit, in-flight sharing and partial errors.
- Persisted product reads after recreating the background context; storage failures and stalled pages.
- XSRF discovery, recovery timeout, successful recovery and temporary-tab cleanup.
- SPA credential selection, account payloads, encoded identifiers and debug envelopes.
- Invalid messages, network errors, HTTP 401 and HTTP 200 invalid JSON in consumption.
Existing suites remain intact: consumption normalization/requests, consumer-session
recovery, AI summary rendering/state, scoped token reuse/expiry and bounded retry.
Tests for the current security defects are deliberately not encoded as acceptable
behavior. They belong to separately approved fixes.
### Former Temporary Test Seam
The original content IIFE had no exports, so baseline tests initially exposed
functions before initialization with a guarded source marker. That temporary
mechanism is now removed. Tests use the actual application factories through
`tests/support/application.cjs`, without replacing source strings. Background
integration tests still execute the production listener and asynchronous replies.
### Running Checks
```sh
npm test
npm run check
```
Without npm, use the Node commands in package.json individually. The new suite is:
```sh
node tests/characterization.test.cjs
```
At baseline, `build.mjs --check` did not compare output contents. It now checks
both generated manifests and both scripts byte-for-byte against composition.
Characterization tests retain the exact manifest expectations.
Stage-1 verification on Node.js v24.19.0:
| Command | Result |
| --- | --- |
| `node --check src/content.js` and `node --check src/background.js` | Passed |
| `node --check tests/support/characterization.cjs` | Passed |
| `node tests/consumptionTrend.test.cjs` | Passed |
| `node tests/consumerTokenSessionRetry.test.cjs` | Passed |
| `node tests/aiSummary.test.cjs` | Passed |
| `node tests/characterization.test.cjs` | 22 passed, 0 failed |
| `node scripts/build.mjs --check` | Passed; distribution equivalence also checked by the suite |
| `git diff --check` | Passed |
The individual commands from the npm scripts were executed directly. No build
regeneration was needed because runtime sources and the build were unchanged;
`git diff -- src dist scripts/build.mjs` was empty.
### Verification Limits
- API mocks are not proof of Chrome/Edge/Firefox compatibility or real authentication.
- Recreating a VM tests cache rehydration, not actual service-worker suspension or job recovery.
- DOM mocks do not prove layout, focus, accessibility or SPA lifecycle correctness.
- The original visual fixture used eval. The completed extraction removed it and
serves the composed factory through a normal local script instead.
- No installed-browser smoke test or live Fusion request was performed in stage 1.
- No JavaScript export/generator feature was located. Do not create a generators
directory until its actual source and standalone-output contracts are identified.
## Separate Security and Reliability Backlog
These are preexisting observations, not authorized functional changes in this refactor.
| Issue | Evidence/impact | Separate change to evaluate |
| --- | --- | --- |
| Debug redaction after truncation | Invalid/truncated JSON is returned unmasked by formatDebugBody; reproduced with a synthetic token | Redact before truncation, fail closed on malformed bodies, test all response shapes |
| Debug secrets and shared DOM | Raw headers/bodies are retained for the reveal control; revealed text is in page DOM | Review retention, safe diagnostic schema and whether revealing credentials remains acceptable |
| Cookie store selection | Lookup scans all stores instead of binding to sender account/container | Scope selection to the requesting context and test multi-account behavior |
| Product cache isolation | Cache and in-flight keys use OptyNumber only | Design identity partitioning and explicit cache migration/retention policy |
| Privileged message validation | No explicit sender identity/origin check; supplied header names are accepted | Validate senders/messages and allowlist headers without introducing an arbitrary proxy |
| Session tab retry | Opening failures retry indefinitely; interruption can strand work | Decide cancellation/limits and worker lifecycle recovery explicitly |
| DOM lifecycle | Observer can remain active; guards do not cover every reinjection/resize path | Define ownership/disposal and characterize navigation before changing behavior |
| Token expiry | General/SPA tokens have policies different from Sales Common | Review independently; do not unify policies as part of extraction |
Never include real secrets in repro cases, snapshots, documentation or test output.
Do not add persistent token storage to compensate for worker termination.
## Completed Extraction Stages
All modules listed below are integrated through `scripts/sources.cjs` and the
two composition roots. Former `src/content.js` and `src/background.js` were removed;
their names remain only for the generated distribution scripts. No version change,
commit, publication, runtime dependency, permission expansion or CSP change occurred.
| Stage/responsibility | Files/boundaries | Preserved contracts and evidence | Remaining risk/next safe step |
| --- | --- | --- | --- |
| Build and pure logic | `scripts/compose.cjs`, `sources.cjs`; fiscal-periods, formatters, consumption model | Classic IIFEs, exact manifests, dates, payload/normalization tests; syntax checked per source and composed bundle | Native browser package loading still needs a smoke test |
| Platform and dispatch | `platform/`, `background/main.js`, `messages.js`, `shared/messages.js` | Sync listener registration, callback/promise mocks, async responses, sender-frame progress and unknown-message behavior | Sender validation is a separate security change |
| Auth and HTTP operations | `auth/`, `services/` | Credential selection, scoped-token reuse/expiry, 401/403 retry, missing-cookie and HTML-session recovery, request/debug envelopes | Real SSO and worker suspension not simulated completely |
| Products | `features/products/` | Four-worker queue, pagination, TTL, cache restore, in-flight sharing, partial errors and expansion behavior | Existing identity/cache-retention policy unchanged |
| Content features | opportunities, preferences, AI-summary, consumption and debug factories/state | Filter/search storage round-trip, sorting, stale requests, AI safe text, plan/period controls, preserved labels and DOM | Existing DOM lifecycle and narrow-screen limitations remain |
| Bootstrap, CSS and documentation | `content/main.js`, `background/main.js`, application roots, styles; README/docs | CSS byte-identical to original (hash test); factories connected, no orphan sources or peer-factory dependencies; old marker/eval fixture removed | Native browser matrix below remains pending |
The automated suites were run after the initial extraction and again after the
platform/auth refinements, cleanup and final composition. Intermediate verification
found and corrected test-harness expectations for source inventory and fixture
callback messaging; no production auth, cookie or session policy was changed.
### Final Automated Verification
Executed with Node.js 24.19.0, directly through the package-script equivalents:
| Command | Result |
| --- | --- |
| `node scripts/build.mjs` | Both packages regenerated; all source/composed JavaScript syntax valid |
| `node scripts/test.cjs` | All five suite files passed: AI, architecture (6 cases), characterization (22 cases), session recovery, consumption |
| `node scripts/build.mjs --check` | Generated scripts/manifests match source composition |
| `git diff --check` | No whitespace errors |
There is no configured linter. Syntax validation uses Node's JavaScript parser
through vm.Script, not eval or new Function in the extension. No generators feature
was located; standalone JavaScript generation therefore has no feature-specific
snapshot to assert. The architectural suite verifies deterministic composition,
source coverage, dependency injection, independent per-frame state and lazy
localStorage failure handling. Feature suites use synthetic credentials only.
### UI Smoke Verification
The loopback fixture was exercised in the Codex in-app browser, using the same
composed content factories with injected synthetic runtime/fetch/storage adapters.
It is not an installed extension and does not validate privileged browser APIs.
- Paginated synthetic opportunities, product progress/counts and dashboard rendered.
- Search text/filter remained after closing and reopening the modal.
- Light/dark toggle and product expansion retained the existing presentation.
- AI text, including literal HTML delimiters, appeared as text above the product table.
- Preferences retained checkbox/cache-duration selections after Save and reopen.
- Cloud Consumption opened with plan tabs; weekly default, daily switch, alternate
plan and series visibility toggles worked. Debug listed scoped relay and AI calls.
- Screenshots inspected at 1920x1080, 1280x720 and 390x844. Desktop columns rendered
with complete Last Update Date and WON_PENDING. Existing compact-table scrolling
and clipping at 1280 remain unchanged; mobile controls stack. This is not a full
mobile workflow/accessibility audit and no layout redesign was included.
- No console errors were reported during the exercised flows.
### Outstanding Native Browser Matrix
| Target | Package/runtime | Status |
| --- | --- | --- |
| Chrome | `dist/chromium`, MV3 service worker | Not run as an installed extension |
| Edge | `dist/chromium`, MV3 service worker | Not run as an installed extension |
| Firefox | `dist/firefox`, MV3 background scripts | Not run as an installed extension |
The available automation connection exposed the in-app browser and a Brave
profile, not Chrome, Edge or Firefox. No live Fusion requests, user-session access
or extension installation were used to substitute for the missing native tests.
Actual suspension/reactivation, container routing and SSO remain unverified.
The next safe validation is to load the generated packages in an authorized test
profile in each target browser, then exercise tile insertion, modal reopen,
filters/preferences, products, AI, consumption, debug and background restart.
Resolve the separately listed security issues only in explicitly approved changes.
See [the native browser checklist and execution record](browser-smoke.md) for the
2026-09-17 follow-up. Local tests and output checks passed again; Chrome/Edge were
installed but not connected to automation. Native verification remains pending
while the user connects those browsers.