* test(applet): replace covered main.qml presentation greps with executed tests
First third of the feature-parity assertions on contents/ui/main.qml
(main-parity-a): 27 removed after experiment, 3 kept with a stated reason.
No production file changes; git diff contents/ is empty.
New executed tests, each shown red against the same mutation first:
- tests/test_provider_cost_presentation.py:
test_main_cost_wrappers_delegate_to_presentation extracts the seven cost
wrappers plus spendTotalLine, spendProviderCosts and
spendHistoryStillBuilding and runs them against CostPresentation.js.
- tests/test_provider_metadata.py: test_normalizeStampsTheLiveReceiptClock
and test_dashboardSectionsMapThroughDisplayRows.
- tests/test_usage_recovery.py: test_popup_menu_offers_accounts_and_docs
(PopupMenuRows fixture extracting actionRows and the identity-table URL
wrappers).
- tests/test_panel_text_recovery.py:
test_production_menu_bar_text_follows_display_mode (MenuBarText fixture
extracting menuBarDisplayText and its row pipeline).
Evidence, per removed assertion (mutation -> red test):
- i18n("Tokens unavailable"): "Tokens gone" in spendTotalLine ->
test_spendTotalLinePrintsSubtotalOrUnavailable.
- function costBreakdownRows(tokenCost): total-tokens lane zeroed (row drops
out) -> test_breakdownRowsKeepEveryLane.
- function costModelRows(tokenCost): null payload ->
test_modelAndHistoryRowsFollowPresentation.
- function costHistoryRows(tokenCost): metric flipped ->
test_modelAndHistoryRowsFollowPresentation.
- function costPeakLine(points): forced "" ->
test_summaryLinesWordTheSelectedMetric.
- function costAverageDailyLine(points): forced "" -> same test.
- function costPerMillionLine(tokenCost): forced "" -> same test.
- CostPresentation.spendSnapshots(tokenCosts, costHistoryDays: {} input ->
total/sort/still-building wrapper tests.
- CostPresentation.breakdownRows(: result replaced by raw lane dicts ->
test_breakdownRowsKeepEveryLane.
- CostPresentation.modelRows(: null payload ->
test_modelAndHistoryRowsFollowPresentation.
- CostPresentation.historyRows(: metric flipped -> same test.
- CostPresentation.averageDailyValue(: metric flipped ->
test_summaryLinesWordTheSelectedMetric.
- CostPresentation.perMillionAmount(: null -> same test.
- CostPresentation.spendTotals(: [] input ->
test_spendTotalLinePrintsSubtotalOrUnavailable.
- CostPresentation.historyStillBuilding(: [] input ->
test_stillBuildingMirrorsScanCoverage.
- function costChartPoints(points): metric flipped ->
test_chartPointsFollowTheSelectedMetric.
- ProviderSnapshot.normalize(item, Date.now()): timestamp 0 ->
test_normalizeStampsTheLiveReceiptClock.
- kpis: dashboard.kpis.map(dashboardDisplayRow): kpis/rows emptied ->
test_dashboardSectionsMapThroughDisplayRows.
- rows: dashboard.rows.map(dashboardDisplayRow): same test.
- function selectAccount(providerID, accountIdentity): early return ->
test_account_refresh (AccountRefresh cases; pre-existing, no new test).
- function accountOptionsForProvider(providerID): return [] ->
test_account_refresh cached cases (pre-existing).
- function providerDocsUrl(providerID): return "" ->
test_menuOffersAccountsAndDocs.
- function providerLoginUrl(providerID): return "" -> same test.
- action: "docs": renamed to "help" -> same test.
- action: "accounts": renamed to "users" -> same test.
- function menuBarDisplayText(item): return "MUTATED" ->
test_menuBarTextFollowsDisplayMode.
- function safeMenuBarDisplayMode(value): return "bogus" ->
test_safeMenuBarDisplayModeFallsBackToPercent.
Kept with reason (breaking mutation stays green):
- CostPresentation.numberFormat(: separators swapped keeps all executed
modules green. The costNumberFormat property initializer cannot be
extracted, so fixtures declare the format themselves; unobservable in the
harness.
- property string menuBarDisplayMode / property bool
resetTimesShowAbsolute: hardcoding either value keeps every panel test
green. Both initializers read Plasmoid.configuration, which is absent
under test; mode routing through these values is covered by MenuBarText.
Before writing tests, a 14-mutation cost batch stayed green across all 14
executed modules naming main.qml, and a 12-mutation batch reddened only
test_account_refresh and test_forecast_clock (isolated to selectAccount and
accountOptionsForProvider).
Verification: make check-feature-parity, make check-shellcheck and the four
touched Python modules are green. check-python-lint is skipped (pyflakes
absent; CI installs it). Full make check left to the orchestrator.
* test(applet): replace covered main.qml provider greps with executed tests
Second third of the feature-parity assertions on contents/ui/main.qml
(main-parity-b): 23 removed after experiment, 13 kept with a stated reason.
No production file changes; git diff contents/ is empty.
New executed tests, each shown red against the same mutation first:
- tests/test_notification_wiring.py:
NotificationPipelineTests extracts processNotifications,
dispatchNotificationIntents, sendPlasmaNotification, resetLabel,
usageResetText, paceEtaText and resetText, and pins the disabled/empty
guards, prime-then-escalate quota dispatch (critical urgency, committed
memo stays silent) and status dispatch with mapped urgency.
notificationUrgency joined the extracted FUNCTIONS with a severity-mapping
test; the update fixture now compares the exact "Open release page"
action label.
- tests/test_panel_selection.py: ProviderSelectionTests extracts
providerIndexForID, overviewProviders, panelProviderItems,
autoSelectedProviderIndex, updateSelectedProvider,
selectedCompactProvider, globalViewAvailability and boundedConfigRevision
against the real OverviewProviders/PanelProviders/ProviderAutoSelect/
PopupSelection modules.
- tests/test_quota_reset_metadata.py: QuotaMarkerTests extracts
quotaWarningMarkers, quotaSeverity, quotaMeterColor and statusBadgeColor
against the real QuotaThresholds module and the real Kirigami Theme
singleton (compared, not hardcoded).
Evidence, per removed assertion (mutation -> red test):
- function primaryIncidentProvider(): return null ->
test_notification_adapters_preserve_real_transitions (pre-existing).
- function notificationObservations(): return [] -> same test.
- function quotaNotificationLevel(row): return "MUTATED" -> same test
(quota-escalation and recovery cases fail; genuine compare failures).
- function notifyAvailableUpdate(version, url, releaseUrl): early return ->
test_update_notification_adapters_keep_activations_source_scoped.
- function sendPlasmaNotification(...): return "" -> same test.
- function safeReleaseUrl(url): return "" -> same test.
- notificationDispatcher.send(cleanTitle, ...): return "MUTED-NEVER-SENT" ->
same test.
- Qt.openUrlExternally(releasePageUrl) dropped -> same test.
- function processNotifications(): early return -> new pipeline test.
- NotificationPlanner.transition( call: observations replaced by [] ->
new pipeline test.
- function dispatchNotificationIntents(intents, observations): early return
-> new pipeline test.
- function notificationUrgency(severity): return "normal" -> new urgency
test (plus the pipeline status case).
- i18n("Open release page"): relabelled inside notifyAvailableUpdate ->
new exact-label compare (was green before the compare existed).
- function providerIndexForID(providerID): return -1 -> new selection test.
- OverviewProviders.visibleItems(providers, overviewProviderIDsRaw):
argument replaced by "" -> new selection test.
- function panelProviderItems(): return [] -> new selection test.
- function autoSelectedProviderIndex(items): return -1 -> new selection test.
- function updateSelectedProvider(): early return -> new selection test
(after adding change-requiring cases: uninitialized lands on the global
view, an unknown stored ID falls back to the first entry).
- function selectedCompactProvider(): return null -> new selection test.
- function boundedConfigRevision(value): return 0 -> new selection test.
- function statusBadgeColor(severity): return "transparent" -> new markers
test.
- function quotaWarningMarkers(row): return [] -> new markers test.
- showQuotaWarningMarkers (bare): flag check dropped from
quotaWarningMarkers -> new markers test (hidden case fails).
Kept with reason (breaking mutation stays green across all 16 naming
modules; fixtures declare their own values, so readonly Plasmoid-backed
declarations and declarative handlers are structurally unobservable
without instantiating the applet):
- import QuotaThresholds.js (rename stays green), readonly
quotaWarningPercent/quotaCriticalPercent (zeroed stays green),
onQuotaWarningPercentChanged/onQuotaCriticalPercentChanged (no-op stays
green), enableNotifications/autoSelectProvider/selectedProviderID/
overviewProviderIDsRaw/notificationMemo defaults (flipped/seeded stays
green), readonly selectedProviderIndex binding (forced -1 stays green;
the lookup logic is pinned by the new selection test),
onOverviewProviderIDsRawChanged (no-op stays green), refreshInterval
Math.max bound (zeroed stays green).
Verification: make check-feature-parity and make check-shellcheck green;
the three touched Python modules green (7 tests); the full 16-module
naming set green (69 tests). check-python-lint is skipped (pyflakes
absent; CI installs it). Full make check left to the orchestrator.
* test(applet): replace covered main.qml remaining parity greps with executed tests
Last third of the feature-parity assertions on contents/ui/main.qml
(main-parity-c): 5 removed after experiment, 3 kept with a stated reason.
No production file changes; git diff contents/ is empty.
New executed test, shown red against the same mutation first:
- tests/test_panel_selection.py: CompactProvidersTests extracts
compactProviders, panelProviderItems, panelMeterRows,
switcherCandidateRows, usageRowForLane, appendUniqueUsageRow,
providerKey and clamp against the real PanelProviders/PanelRules/
PanelDisplay/ProviderIdentity/ProviderNormalizer modules, and pins
that allowed meters keep the provider, a usageAtLeast-99 meters rule
drops a 43% provider, and a disabled multi-provider flag hides all.
Evidence, per removed assertion (mutation -> red test):
- CostPresentation.peakPoint(points, costHistoryShowsTokens): metric flag
negated in costPeakLine ->
test_main_cost_wrappers_delegate_to_presentation (pre-existing).
- function spendHistoryStillBuilding(): forced false ->
test_stillBuildingMirrorsScanCoverage (pre-existing).
- PanelDisplay.rowForMode(switcherCandidateRows(item), mode,
panelQuotaLane): lane argument replaced by "secondary" in
panelDisplayRow -> test_production_menu_bar_text_follows_display_mode
(pre-existing; hardcoding the mode stays green because the single-row
fixture carries both metrics, so the lane mutation pins the call).
- PanelRules.matches(panelVisibilityRules.text, row, panelClockMs):
gated to false in compactTextSegments ->
test_production_panel_text_keeps_surrendered_content_recoverable and
test_production_text_distinguishes_automatic_and_explicit_empty_rosters
(both pre-existing).
- PanelRules.matchesAny(panelVisibilityRules.meters, rows,
panelClockMs): gated to true in compactProviders -> new
test_production_compact_providers_honor_visibility_rules.
Kept with reason (breaking mutation stays green across all 16 naming
modules; fixtures declare their own values, so readonly Plasmoid-backed
declarations are structurally unobservable without instantiating the
applet):
- Math.max(1, Math.min(365, ...costHistoryDays...): 365 -> 30 stays
green; fixtures declare their own day count.
- PanelDisplay.safeLane(...panelQuotaLane) and
PanelRules.normalizedRules(...panelVisibilityRules): bogus
replacements stay green; mode routing through these values is covered
by MenuBarText and the visibility gates by the selection tests.
Verification: make check-feature-parity and make check-shellcheck green;
the three touched Python modules green (7 tests). check-python-lint is
skipped (pyflakes absent; CI installs it). Full make check left to the
orchestrator.
* test(applet): replace covered main.qml security and theme greps with executed tests
The main-security step (24 assertions on contents/ui/main.qml): 22 removed
after experiment, 2 kept with a stated reason. No production file changes;
git diff contents/ is empty.
New executed tests, each shown red against the same mutation first:
- tests/test_notification_wiring.py: test_providerMapKeyNormalizesAliases
extracts providerMapKey, selectedAccountForProvider,
markNotificationProvidersFresh and notificationScopeKey from the applet
surface and pins that the 11labs alias shares the elevenlabs slot
everywhere; test_availableUpdatePersistsMemoVersion pins the
lastNotifiedUpdateVersion config write and the no-reannounce memo;
test_installedUpdateNotifiesOnceWithVersion pins the installed-update
notification and its disabled guard.
- tests/test_provider_metadata.py: test_dashboardRowBoundsUntrustedText
(600-char part renders exactly 500), test_hostileStatusUrlFallsBackTo-
IdentityTable (evil-host status URL falls back, same-host candidate
passes, unknown provider yields ""), test_providerIconSourceFallsBack-
ForUnusableKeys (path traversal/markup fall back to view-statistics).
safeStatusUrl, providerStatusUrl and providerIconSource joined the
extracted FUNCTIONS and the safeStatusUrl stub was removed.
- tests/test_usage_recovery.py: test_statusActionOpensGuardedUrl extracts
performAction with the external open routed to a recorder and pins the
status action opens the guarded URL.
- tests/test_quota_reset_metadata.py: ProviderColorTests extracts
providerColor, readableAccentColor and providerReadableColor and pins
brand survival, the highlight fallback and the >= 3 contrast bound
against the live Kirigami Theme singleton.
- tests/test_sessions_controller.py: AppletRefreshDelegationTests extracts
refreshSessions with a stub controller and pins the forward-and-return.
Evidence, per removed assertion (mutation -> red test):
- function providerMapKey(providerID): body returns raw id ->
test_providerMapKeyNormalizesAliases (new; green on pristine, red on
mutation; pre-existing suite stayed green).
- var providerID = providerMapKey(item.provider): raw id in
markNotificationProvidersFresh and in notificationScopeKey (each site
mutated separately) -> same new test.
- var key = providerMapKey(providerID): raw key in
selectedAccountForProvider -> same new test.
- boundedDisplayText(parts.join, 500): 500 -> 5000 ->
test_dashboardRowBoundsUntrustedText (new).
- var fileName = providerIconFileName(value): fallback dropped ->
test_providerIconSourceFallsBackForUnusableKeys (new).
- function safeStatusUrl(providerID, url): guard replaced by
passthrough-or-fallback -> test_hostileStatusUrlFallsBackToIdentityTable
(new).
- statusUrl: safeStatusUrl(...): replaced by snapshot.statusUrl ->
same new test.
- Qt.openUrlExternally(safeStatusUrl(...)): replaced by item.statusUrl ->
test_statusActionOpensGuardedUrl (new).
- function safeReleaseUrl(url): guard replaced by trim passthrough ->
test_updatesWithoutATrustedReleaseUrlStayNonActionable (pre-existing).
- Qt.openUrlExternally(releasePageUrl): line dropped ->
test_queuedActionableUpdatesOpenTheirOwnReleasePage (pre-existing).
- httpsUrlHost === github.com: host swapped to example.org ->
both update-wiring tests (pre-existing).
- nextPending[sourceName] = releasePageUrl: registration dropped ->
both update-wiring tests (pre-existing).
- delete nextPending[sourceName]: retirement dropped -> both
update-wiring tests (pre-existing).
- function providerColor(value): body returns highlightColor ->
test_brandColorsSurviveAndUnknownFallsBackToHighlight (new).
- function providerReadableColor(value, background): body returns the
background -> test_readableColorKeepsContrastOnThemeBackground (new).
- Kirigami.Theme.textColor: badge unknown branch -> transparent ->
test_badgeColoursFollowSeverity (pre-existing).
- Kirigami.Theme.highlightColor: providerColor fallback -> red ->
both new ProviderColors tests.
- Kirigami.Theme.negativeTextColor: badge critical branch ->
transparent -> test_badgeColoursFollowSeverity and
test_meterFillUsesTheBadgeColourPastTheWarningStep (pre-existing).
- Kirigami.Theme.neutralTextColor: badge minor branch -> transparent ->
test_badgeColoursFollowSeverity (pre-existing).
- notifyInstalledUpdate(version): early return ->
test_installedUpdateNotifiesOnceWithVersion (new).
- Plasmoid.configuration.lastNotifiedUpdateVersion = memoKey: write
dropped -> test_availableUpdatePersistsMemoVersion (new).
- function refreshSessions(): body returns false ->
test_refreshSessionsDelegatesToController (new).
Kept with reason (breaking mutation stays green; fixtures declare their
own state, so applet-level declarations are structurally unobservable
without instantiating the applet):
- property var pendingUpdateReleaseUrls: ({}): deleting it keeps the
whole updater suite green.
- import ThemeContrast.js as ThemeContrast: removing it keeps all six
naming modules green.
Verification: make check-process-lifecycle, check-security-regressions,
check-theme-boundaries, check-update-widget and check-shellcheck green;
the five touched Python modules green (incl. the 80s sessions-controller
lifecycle run). check-python-lint is skipped (pyflakes absent; CI installs
it). Full make check left to the orchestrator.
* test(applet): replace covered main.qml provider greps with executed tests
Second third of the feature-parity assertions on contents/ui/main.qml
(main-parity-b): 4 removed after experiment, 19 kept with a stated
reason. No production file changes; git diff contents/ is empty.
New executed test, shown red against each mutation first:
- tests/test_provider_cost_presentation.py:
test_cost_metric_helpers_sanitize_before_persisting extracts
safeCostHistoryMetric and setCostHistoryMetric from the applet surface
(Plasmoid.configuration rewritten to a stub, the established pattern)
and pins that only "tokens"/"cost" survive, so the chart and the
summary lines can never disagree.
Evidence, per removed assertion (mutation -> red test):
- property bool showQuotaWarningMarkers: flag check dropped from
quotaWarningMarkers -> test_markersFollowTheVisibilityFlag
(test_quota_reset_metadata.py; pre-existing).
- function safeCostHistoryMetric(value): body forced to return "cost" ->
new sanitize test (tokens case fails).
- function setCostHistoryMetric(metric): raw assign without the safe call
-> new sanitize test (bogus case persists unfiltered).
- CostPresentation.chartPoints(costNumberFormat, points,
costHistoryShowsTokens): flag negated in costChartPoints ->
test_chartPointsFollowTheSelectedMetric (pre-existing).
Kept with reason (breaking mutation stays green across all 17 Python
naming modules; fixtures declare their own values, so readonly
Plasmoid-backed declarations and declarative handlers are structurally
unobservable without instantiating the applet):
- import QuotaThresholds.js (alias renamed stays green), readonly
quotaWarningPercent/quotaCriticalPercent (zeroed stays green),
onQuotaWarningPercentChanged/onQuotaCriticalPercentChanged (no-op stays
green).
- enableNotifications/autoSelectProvider (flipped stays green),
selectedProviderID/overviewProviderIDsRaw/notificationMemo (seeded
stays green), readonly selectedProviderIndex binding (forced -1 stays
green; the lookup logic is pinned by the selection tests),
onOverviewProviderIDsRawChanged (no-op stays green), refreshInterval
Math.max bound (zeroed stays green).
- costUsageEnabled (flipped stays green; the controller's own copy stays
pinned by test_startupRunsWhileHiddenAndReentryKeepsFreshData),
costHistoryDays fallback (30 -> 7 stays green), costHistoryMetric
initializer (forced "cost" stays green; sanitization behind it is pinned
by the new test), 365-day clamp (365 -> 30 stays green).
- PanelDisplay.safeLane(...panelQuotaLane) ("bogus" stays green) and
PanelRules.normalizedRules(...panelVisibilityRules) (({}) stays
green); routing through these values is covered by the menu-bar and
compact provider selection tests.
Verification: make check-feature-parity and make check-shellcheck green;
the touched Python module green (3 tests); all 17 naming modules green
under the combined breaking mutations. check-python-lint is skipped
(pyflakes absent; CI installs it). Full make check left to the
orchestrator.
* test(applet): replace covered main.qml presentation greps with executed tests
The feature-parity assertions on contents/ui/main.qml (main-parity-a):
8 removed after experiment, 25 kept with a stated reason.
No production file changes; git diff contents/ is empty.
New executed tests, each shown red against the same mutation first:
- tests/test_panel_text_recovery.py: MenuBarText gains runOutItem (a
pace-clocked row: paceOnTop false, paceEtaSeconds 7200 observed at the
live panel clock) asserting runOut mode renders "2 hours".
- tests/test_notification_wiring.py: NotificationPipeline gains
test_quotaNotificationBodyCarriesTheResetLine, priming at 85% then
escalating to 96% with a +2h resetsAt row and asserting the quota body
carries both "96" and the "Resets" prefix.
Evidence, per removed assertion (mutation -> red test):
- PanelDisplay.rowForMode(: lane replaced by "secondary" in
panelDisplayRow -> test_production_menu_bar_text_follows_display_mode
(pre-existing).
- mode === "runOut": mutated to "runOutX" -> new runOutItem case
(was green before: the old single-row fixture yields "" on both the
run-out branch and the percent fallback, so nothing pinned the branch).
- if (!paceWarningActive(row)) {: guard negated in runOutTextForRow ->
panel-text, notification-wiring and forecast-clock suites
(2 failures, pre-existing).
- PanelDisplay.remainingSeconds(: call replaced by paceEtaText(0) ->
test_cached_account_forecast_uses_its_own_receipt_time
(test_forecast_clock.py; pre-existing).
- function resetText(window, absolute): forced "MUTATED" ->
test_translated_resets_follow_local_time_and_live_clock
(test_reset_presentation.py; pre-existing).
- function usageResetText(row): forced "MUTATED" -> same test.
- ResetPresentation.labelParts(value): argument replaced by "" -> same
test (qmltestrunner compare failures in all six locales).
- var resetLine = resetLabel(usageResetText(row)): resetLabel wrapper
dropped -> new test_quotaNotificationBodyCarriesTheResetLine
(was green before: pipeline quota fixtures carried no reset metadata).
Kept with reason (23 breaking mutations applied at once stay green
across all 18 Python modules naming main.qml, 75 tests OK; fixtures
declare their own values, so Plasmoid-backed declarations and the
declarative Timer wiring are structurally unobservable without
instantiating the applet):
- CostPresentation.numberFormat(: separators swapped stays green;
fixtures declare their own format.
- property string menuBarDisplayMode / Plasmoid.configuration.
menuBarDisplayMode: forced "pace" stays green; mode routing is
covered by MenuBarText.
- property bool resetTimesShowAbsolute: flipped stays green; covered
indirectly through the reset adapters.
- id: panelClockTimer (dropped) and root.panelClockMs = Date.now()
(frozen to 0): both stay green; the minute-timer wiring cannot run
without the applet instance.
- import QuotaThresholds.js (alias renamed), readonly
quotaWarningPercent/quotaCriticalPercent (zeroed),
onQuotaWarningPercentChanged/onQuotaCriticalPercentChanged (no-op).
- enableNotifications/autoSelectProvider (flipped),
selectedProviderID/overviewProviderIDsRaw/notificationMemo (seeded),
readonly selectedProviderIndex (forced -1; the lookup itself is
pinned by the panel-selection tests),
onOverviewProviderIDsRawChanged (no-op), refreshInterval Math.max
bound (zeroed).
- costUsageEnabled (flipped), costHistoryDays fallback (30 -> 7),
costHistoryMetric initializer (forced "cost"; sanitization behind it
is pinned by test_cost_metric_helpers_sanitize_before_persisting),
365-day clamp (365 -> 30).
- PanelDisplay.safeLane(...panelQuotaLane) ("bogus") and
PanelRules.normalizedRules(...panelVisibilityRules) (({})); routing
through these values is covered by MenuBarText and the compact
provider selection tests.
Verification: make check-feature-parity and make check-shellcheck
green; the two touched Python modules green (5 tests); all 18 naming
modules green under the combined keep-mutations. check-python-lint
not run (pyflakes absent on this machine; CI installs it). Full make
check left to the orchestrator.
* test(applet): pin installed-update forwarding and all menu-bar modes
1. tests/test_notification_wiring.py: new UpdateInstalledForwardingTests
extracts the onUpdateInstalled handler body from main.qml and executes it
against a stub recording root.notifyInstalledUpdate calls (no source-text
assertion). Evidence:
- pristine tree: new test green.
- line 2733 'root.notifyInstalledUpdate(version)' deleted: new test red
(qmltestrunner: 1 failed), while test_notification_wiring
UpdateNotificationWiringTests and test_widget_update_controller stay
green (6 tests OK), confirming the dropped protection is real.
- argument dropped ('root.notifyInstalledUpdate()'): new test red.
2. tests/test_panel_text_recovery.py: the safeMenuBarDisplayMode QML
checks are generated in Python from contents/ui/PanelDisplay.js
('var <name>Mode = "<value>"', yielding percent/pace/both/resetTime/
runOut) instead of the hardcoded 'pace' survival line. Evidence:
- pristine tree: suite green (2 tests OK).
- safeMode allowlist with runOut rejected: suite red (1 failure),
where the old pace-only checks stayed green.
Verification: make check-update-widget green; the two touched Python
modules green (6 tests OK). check-python-lint skipped (pyflakes absent).
No production changes; git diff contents/ is empty.13 files changedLine totals unavailable