Skip to content

Heat: keep Low Power Mode on until nominal, and show it in the menu - #80

Open
aldikosh23 wants to merge 4 commits into
krishhgg:mainfrom
aldikosh23:feat/heat-hold
Open

aldikosh23 wants to merge 4 commits into
krishhgg:mainfrom
aldikosh23:feat/heat-hold

Conversation

@aldikosh23

@aldikosh23 aldikosh23 commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Problem

  • Low Power Mode turned on for heat goes off at fair, the first step down. That brings back the load that made the Mac serious, so the mode goes on and off again as the Mac heats up, with a notification each time.
  • The notification after that cooldown says "Back above the floor.", which is about the battery.
  • A battery floor or the thermal rules changed in Settings during a session apply only at the next battery or thermal event, which can be minutes away. Only the lid option ran the floors again.
  • The menu shows neither the thermal state nor whether Insomnia has Low Power Mode on, so it cannot tell you why the Mac is slower.

Fix

  • Heat hold. The thermal cause holds from a serious reading until a nominal one, or until the session ends if the Mac stays at fair (by design, see the spec next to the hold: a release on a timer would bring the cycling back, only slower). FloorRuleDriver keeps that history for the session, in memory (hot in FloorRules.evaluate); after a relaunch a Mac at fair counts as cooled down. The notifications say what the mode waits for, "Thermal state is serious. Low Power Mode stays on until it is back to nominal.", and why it went off, "Thermal state is back to nominal." (or "Thermal rules are off." when Settings turned them off). Settings has one line under the thermal toggle saying the same.
  • Settings apply at once. SessionManager.config calls a new hook, floorInputsChanged, when a battery floor, the thermal rules or the lid option changes (Config.floorInputs), and AppServices points it at reevaluateFloors(). Settings writes every change through manager.config, so the view's onChange for the lid option is gone too and every floor input takes the same path.
  • Menu. The machine line names a thermal state above nominal ("Thermal: serious"), read fresh when the menu opens rather than through PowerMonitor, whose copy moves only with the notification. The actions line says "Low Power Mode set by Insomnia" while the journal has lowPowerSetByUs.

Nothing here touches the journal, the backstop or the scripts. One side effect: while Insomnia's Low Power Mode is on, the display sample is held (the "display sampling hold" in .greptile/rules.md), so with the mode on longer after heat, a brightness change made in that time is not journaled at the next lid close. The spec says so next to the hold.

Related

Tests

  • FloorRulesTests: the hold at fair after serious, its release at nominal and with the thermal rules off, and the cause it names.
  • FloorRuleDriverTests: the mode goes on once and stays on through fair and a second serious (one lowpowermode 1, one notification), the cooldown and rules-off notifications, and the takeover case with its new text.
  • FloorRuleDriverTests, Settings: a floor input change asks for one floor run and any other change for none, and during a session each change (the thermal rules on and off, a Low Power Mode floor and an end floor raised above the charge) applies through that hook with no battery, thermal or lid event, in the power calls, the journal and the session end.
  • UIStatusTests and IntegrationWiringTests: the two menu lines, and the thermal state through LiveStatusSource.
  • Full swift test at fecfc61, rebased on main db9085e, on macOS 27.2 (MacBook Pro M5, Xcode 27.0), with tmux installed: 1,240 tests, 0 skipped. The only failures are 4 assertions in 3 tests that failed the same way on unmodified main on this Mac before the rebase, because they compare macOS error text and the system language here is Russian: PackagingTests.testPinOfTheRunningAppRefusesABundleReSignedSinceLaunch, PackagingTests.testPinRefusesABundleWhoseSealedScriptWasEditedAfterSigning and UIStatusTests.testHotspotPasswordProblemCopy.
  • swift build -c release -Xswiftc -warnings-as-errors passes, and each commit builds with its tests on its own.

Not verified

  • Nothing here ran on hardware: I did not heat a Mac to reach these states. There are two new items in "Real-Mac checks before a release" in docs/releasing.md.
  • The Settings line and the menu lines were not checked in an installed app.

RetriggerView in GreptileConfidence Score: 5/5 Tier: plus

The PR appears safe to merge; no new actionable issues were found.

Summary

Keeps heat-triggered Low Power Mode on through fair until nominal, applies floor settings without waiting for a power event, and adds thermal and Low Power Mode status to the menu.

  • Heat-triggered Low Power Mode stays on until the Mac returns to nominal.
  • Changes to floor settings now trigger a check right away.
  • The menu shows the current heat state and Insomnia's Low Power Mode.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  Settings["Settings changes manager.config"] --> Hook["floorInputsChanged"]
  Hook --> Queue["reevaluateFloors queues a floor run"]
  Events["Battery, thermal, or lid event"] --> Queue
  Queue --> History["Update this session's heat history"]
  History --> Rules["Evaluate battery, heat, and lid causes"]
  Rules --> Mode["Keep or change Low Power Mode"]
  Rules --> End["End session when an end rule matches"]
Loading

Reviews (4) · Last reviewed commit: "Docs: the heat hold ends at nominal or a..." · Reviewed by Greptile

Comment thread Sources/Insomnia/UI/SettingsView.swift Outdated

@paoValle paoValle left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Source review only — I could not run it: this machine has Command Line Tools and no Xcode, so the package does not compile here (the SwiftUI macro plugin is missing) and I did not run swift test. Read from the diff:

  • the rule change is the one the spec now describes (serious, or fair with no nominal since a serious) and FloorRules stays a pure function with hot as an input;
  • HeatLog lives on FloorRuleDriver, and AppServices.start(for:) builds a new driver per session, so the history is session-scoped as the comment claims — it does not leak into the next session or survive a relaunch;
  • Config.FloorInputs covers exactly the four settings evaluate/lowPowerCause read, so the Settings hook cannot miss one;
  • the release wording now says which state it waited for, and lastCause distinguishes the thermal release from the battery one;
  • the two new "Not run" rows in docs/release-validation.md are the honest place for the hardware part of this.

One question inline about the release condition. Also: the branch is CONFLICTING against main right now; the section 6 table rows it shares with #61 are the likely spot, and you already flagged that whichever lands second takes the small conflict.

config: Config
) -> LowPowerCause? {
if config.thermalRules, thermal == .serious { return .thermal }
if config.thermalRules, thermal == .serious || (hot && thermal == .fair) { return .thermal }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hold releases only on nominal, and nothing else ends it: if the Mac stays at fair for the rest of the session (a sustained build is enough), Low Power Mode and the display sample hold stay on until the session ends. The cycling you are fixing is real and I would not go back to releasing at fair — the question is whether the hold should also have a bound, for example a release after the state has stayed below serious for N minutes, or after a second fair reading. If "session end is the bound" is the intended answer, one sentence in the spec next to the hold would say so for the next reader.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reading it this closely. Session end is the intended bound. A Mac at fair after serious is still carrying the heat, and a release on a timer, or on a second fair reading, brings the cycling back at a slower pace. While the hold lasts, the menu shows "Low Power Mode set by Insomnia" and the thermal state, and turning the thermal rules off in Settings releases it at once. I wrote that next to the hold in docs/spec.md and in docs/lid-close.md (fecfc61).

The branch is rebased on main now, so the conflict is gone, and the two hardware checks moved to "Real-Mac checks before a release" in docs/releasing.md, after #66.

Low Power Mode turned on for a serious thermal state went off at fair, the
first step down. That brought back the load that made the Mac serious, so
the mode went on and off again as the Mac heated up, with a notification
each time. The thermal cause now holds from serious until nominal;
FloorRuleDriver keeps that history for the session, in memory.

The notification after the cooldown said "Back above the floor.", which is
about the battery. It now says "Thermal state is back to nominal.", or
"Thermal rules are off." when Settings turned them off, and the one at the
start says the mode stays on until then.

A battery floor or the thermal rules changed in Settings during a session
applied only at the next battery or thermal event. They now run the floors
at once, as the lid option already did.
The machine line adds "Thermal: serious" (or fair, critical), read again
when the menu opens rather than through the monitor, whose copy moves only
with the notification and is not kept current outside a session. The
actions line says "Low Power Mode set by Insomnia" while the journal has
lowPowerSetByUs, so the reason the Mac is slower is in the menu.
The Settings view asked for a floor run with an onChange per setting, a
path no test could reach. SessionManager.config now calls
floorInputsChanged when a battery floor, the thermal rules or the lid
option changes (Config.floorInputs), and AppServices points it at
reevaluateFloors(). The view's onChange calls, the lid option's included,
are gone, so there is one path for every floor input.

Tests: a floor input change asks for one run and any other change none,
and during a session each change applies through that hook with no
battery, thermal or lid event, in the power calls, the journal and the
session end.
A Mac that stays at fair after serious, under a long build say, keeps
Low Power Mode and the held display sample until the session ends. Say so
next to the hold, with why a release on a timer is not used: it would
bring the cycling back at a slower pace. The menu shows the mode and the
state meanwhile, and turning the thermal rules off releases it.
@aldikosh23

Copy link
Copy Markdown
Contributor Author

@greptileai please review this PR.

@krishhgg krishhgg closed this Oct 18, 2026
@krishhgg krishhgg reopened this Oct 18, 2026
@krishhgg
krishhgg marked this pull request as draft October 18, 2026 04:50
@krishhgg
krishhgg marked this pull request as ready for review October 18, 2026 04:50
@krishhgg

krishhgg commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Thanks for this. The Codex review of fecfc61 found nothing to fix. Two things before it can merge.

1. The open review thread

paoValle's thread on FloorRules.swift:80 is still open, and every thread has to be resolved before a merge. paoValle asked for either a bound on the hold or one sentence in the spec saying session end is the bound. Your reply and the sentence in docs/spec.md at fecfc61 give the second. Please resolve the thread. @paoValle, if you would still like a bound, say so here instead.

2. A test report from your Mac

The PR says nothing ran on hardware, and the Settings and menu lines weren't checked in an installed app. CI covers the logic. This report covers the app as people run it.

  1. A floor change applies at once. On battery, start a session. In Settings, raise "Low Power Mode below" above the current charge. Expected: Low Power Mode comes on within a few seconds, with no battery or thermal event in between, and the menu shows "Low Power Mode set by Insomnia". Then lower it below the charge. Expected: the mode goes off within a few seconds. Post pmset -g | grep -iE 'lowpowermode|powermode' after each change.
  2. The new lines show in the app. Post a screenshot of the Settings line under the thermal toggle and of the menu from check 1.
  3. The heat hold. Never heat the Mac to test it. If the thermal state reaches serious on its own during a session you're watching, post what the menu and notifications showed through fair and back to nominal. Otherwise, list it under Not run.

Build and what to post

  • Build. This branch at fecfc61, with no local changes, installed with ./scripts/install.sh. Post git rev-parse HEAD and the output of git status --short, which should be empty.
  • Mac. Post sw_vers, sysctl -n hw.model and sysctl -n machdep.cpu.brand_string.
  • For each check, post the steps you ran with times, pass or fail, the lines of ~/Library/Logs/Insomnia/insomnia.log for that run, and the readings named in the check.
  • Not run. List anything you couldn't run, and why.

Post the results as a comment on this PR. Put long logs in a gist and link it. If you push another commit, the report needs to be redone at the new head.

@paoValle

paoValle commented Oct 19, 2026 •

Copy link
Copy Markdown

Thanks both. The sentence now in docs/spec.md at fecfc61 is the bound I was asking for — session end is what releases the hold, and it is stated rather than left to be inferred, so I have nothing to add and no timer needed.

I cannot resolve the thread myself: the API answers paoValle does not have the correct permissions to execute ResolveReviewThread, so someone with write access needs to click it. From my side the finding is settled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants