Skip to content

macOS 27: allow the DisplayServices calls and read Low Power Mode from powermode - #58

Merged
krishhgg merged 8 commits into
krishhgg:mainfrom
D4TBot:macos-27
Oct 12, 2026
Merged

krishhgg merged 8 commits into
krishhgg:mainfrom
D4TBot:macos-27

Conversation

@D4TBot

@D4TBot D4TBot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Two things stop Insomnia's lid-close actions on macOS 27:

  1. Display darkening is refused. measuredDisplayServicesMajors is [26], so on 27 the display stays lit under the closed lid. I measured the calls on 27.0.1 (26A434) and opened the gate to [26, 27]. Tests now use 28 as the unmeasured major.
  2. Low Power Mode is never enabled. On macOS 27, pmset -g custom no longer prints lowpowermode; it prints powermode (0 automatic, 1 Low Power, 2 High Power). Every read failed with "no lowpowermode line under Battery Power". pmset -b lowpowermode 0|1 still sets the mode on 27 (it flips powermode under Battery Power), so the sudoers commands are unchanged. Only the parser changes: it falls back to powermode when lowpowermode is absent. lowpowermode wins when both appear, and powermode 2 reads as unknown so High Power is never taken over.

Evidence (MacBook Pro, Apple M5 Pro, macOS 27.0.1 26A434)

  • Read-only probe: all three DisplayServices symbols resolve; GetBrightness returns 0 (brightness 1.0) for the built-in display; CanChangeBrightness is true; all six KeyboardBrightnessClient type encodings match the macOS 26 measurements.
  • Supervised lid test on AC power on a desk, 900 s session, lid closed about 11 minutes: no sleep or wake in pmset -g log; "display darkened (was brightness 1.0)", built-in brightness read 0.000 for the whole closed period and was restored to 1.0; keyboard backlight, mute and the freeze list were applied and undone; battery 34.0 → 35.4 °C while charging; thermal state nominal. The Low Power read failed on every power event in that run (before the parser change).
  • sudo -n pmset -b lowpowermode 1 set powermode 1 under Battery Power; lowpowermode 0 set it back to 0.
  • The new parser read this Mac's live pmset -g custom output as off, and a standalone harness passed 8 cases.
  • The same is recorded in docs/release-validation.md.

Not verified

  • The Swift test suite was not run locally: this Mac has only the Command Line Tools (Swift 6.4), which ship no XCTest, so hosted CI is the gate for the new and changed tests. The release build with -Xswiftc -warnings-as-errors passes against the macOS 26.5 SDK.
  • Side note: a Command Line Tools build against the macOS 27 SDK fails, because SwiftUI's @State is now a macro whose plugin (SwiftUIMacros) ships with Xcode only.
  • No lid-closed session yet with the new Low Power read, no session on battery, nothing longer than 15 minutes.

🤖 Generated with Claude Code

RetriggerConfidence Score: 5/5

The PR appears safe to merge with no outstanding findings.

Summary

The PR allows DisplayServices calls on macOS 27 and reads Low Power Mode from powermode when lowpowermode is absent. The latest changes clarify the High Power error message and update its tests.

  • Insomnia can dim the display on macOS 27.
  • Insomnia reads Low Power Mode from macOS’s newer pmset output.

Reviews (4) · Last reviewed commit: "Low Power Mode: the High Power reason de..." · Reviewed by Greptile

Comment thread Tests/InsomniaTests/PrivateDisplayGuardTests.swift Outdated
Comment thread docs/release-validation.md

@CodingCossack CodingCossack 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.

Same issue here, on a second Mac: MacBook Pro M1 Max (MacBookPro18,2), macOS 27.0 (26A428). pmset -g custom shows only powermode 1 under Battery Power, with no lowpowermode line. During an overnight lid-closed session on v0.1.0, every Low Power Mode read failed with "no lowpowermode line under Battery Power". sudo -n pmset -b lowpowermode 1 still works on this build.

The approach looks right to me. Keeping powermode 2 (High Power) as unknown is the important part. My local fix treated it as "off", which would have let a session end reset High Power to Automatic.

One thing needs fixing before merge. I ran swift test on this branch: 1222 tests, 3 failures.

  • RefusedDarkeningTests.testARefusalDoesNotHideAFailedWriteOnTheOtherDevice and testARefusalDoesNotHideAFailedAudioRestore still expect measured on macOS 26 only at PrivateDisplayGuardTests.swift:1124 and :1148. This is the failure Greptile reported. Changing both to measured on macOS 26, 27 only makes all 70 RefusedDarkeningTests pass locally.
  • UIStatusTests.testReopeningDuringAOneWriteCollapseDropsTheFoldAndBringsThePillsBack failed once during the full run and passed 3 times out of 3 on its own. It looks like a timing flake under load and is unrelated to this PR.

A small follow-up, not a blocker: the refusal message measured on macOS 26, 27 only is spelled out in about seven test assertions. Every new macOS major will break all of them again. Asserting on this is macOS N, or building the expected text from measuredDisplayServicesMajors, would make the next version bump a one-line change.

LGTM once the two strings are updated.

D4TBot and others added 4 commits October 11, 2026 16:17
Measured on macOS 27.0.1 (26A434, MacBook Pro M5 Pro): all three
DisplayServices symbols resolve, GetBrightness returns 0 for the built-in
display and CanChangeBrightness is true; in a supervised lid test the
display was darkened to 0 and restored. Tests now use 28 as the
unmeasured major.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016LRkYqs4KG6k4Dxe5aoCjw
macOS 27 pmset -g custom prints powermode (0 automatic, 1 Low Power,
2 High Power) and no lowpowermode line, so every read failed and Low
Power Mode was never enabled on lid close. pmset -b lowpowermode 0|1
still sets the mode there, so the sudoers commands are unchanged; only
the parser falls back to powermode. lowpowermode wins when both appear,
and High Power reads as unknown so it is never taken over.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016LRkYqs4KG6k4Dxe5aoCjw
Probe, supervised lid test, the Low Power Mode read failure and what
was not exercised.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016LRkYqs4KG6k4Dxe5aoCjw
… refusal text from the measured list

Review on krishhgg#58: two RefusedDarkeningTests still expected "measured on macOS 26
only" (PrivateDisplayGuardTests.swift:1124, :1148). Every expected refusal
message is now built from DisplayPower.measuredDisplayServicesMajors and an
unmeasured major past the list, so measuring the next macOS is a one-line
change. docs/spec.md and docs/lid-close.md say 26 and 27; the unmeasured-version
row of the hardware table says 26 or 27, and a Not run row covers a macOS 27
session that reads powermode, enables Low Power Mode and leaves High Power alone.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01R72BAk5W5GvNnS2GR7qAFN
@D4TBot

D4TBot commented Oct 11, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @CodingCossack for running the suite and confirming on a second Mac — good to have the High Power case backed by your near-miss.

Pushed d37441a, rebased on main (2c116dd):

  • The two RefusedDarkeningTests strings (PrivateDisplayGuardTests.swift:1124, :1148) — fixed. Following your suggestion, every expected refusal message in that file is now built from DisplayPower.measuredDisplayServicesMajors, with an unmeasured major one past the list, so measuring the next macOS is a one-line change in DisplayPower.swift.
  • Greptile's checklist note — added a Not run row to "Hardware validation still required": a macOS 27 session on battery that reads powermode, enables Low Power Mode, restores it, and leaves a pre-set High Power (powermode 2) alone. The unmeasured-version row now says "other than 26 or 27".
  • Docs — docs/spec.md and docs/lid-close.md now say the display calls were measured on 26 and 27.

CI on this exact head, run in my fork while the workflow here awaits approval: 1,226 tests, 0 failures (10 skipped), release build, ShellCheck and workflow lint all green — https://github.com/D4TBot/Insomnia/actions/runs/38142732115

If #66 lands first, I'll move the new row to releasing.md with it.

@nsd97

nsd97 commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

Tested on a second macOS 27 Mac, including the High Power case listed as not run.

Machine: MacBook Pro (Mac14,6), Apple M2 Max, 64 GB, macOS 27.0 (26A428), Xcode 27.1 (27A9269), Swift 6.4.
Build: main bb5cf2b with this PR's head d37441a cherry-picked, plus #76, #74 and a local change that adds Comet to the browser lists. The only conflict was docs/release-validation.md, kept as main's. On that tree swift test ran 1,256 tests with 0 failures and 0 skipped (tmux installed), and swift build -c release -Xswiftc -warnings-as-errors, ShellCheck, bash -n and scripts/check-lid-simulation-gate.sh pass. The installed build adds one more fix, /usr/bin/stat in backstop.sh, which this Mac needed to install. It was installed with scripts/install.sh.

Supervised, on a desk:

  • High Power (powermode 2 under Battery Power and AC Power), session on AC, lid closed 2.5 minutes: "display darkened (was brightness 0.8934256)", "keyboard backlight off (was brightness 0.6853962)" and the mute were applied and undone on open. Low Power Mode was not enabled. After the session powermode 2 was still set under Battery Power and SleepDisabled was 0.
  • Automatic (powermode 0), session on battery, lid closed 2 minutes: "low power mode on (lid closed)". powerd logged User preference for custom powermode on battery: 1 and com.apple.system.lowpowermode current state: 0, new state: 1, pluggedin: 0. The open logged "low power mode off (lid opened)" and "display restored again after low power mode", and powerd custom powermode on battery: 0. pmset -g log shows no sleep or wake while the lid was closed. Afterwards powermode 0, SleepDisabled 0, journal clean.
  • The keyboard backlight check passed on 27.0: no refusal line, and the backlight was saved and restored both times.

One wording issue: with High Power the log line is

could not read low power mode; not enabling it: `pmset -g custom` failed with status 0: no lowpowermode or powermode line under Battery Power

but the powermode 2 line is there. parseLowPowerMode returns nil both when the line is missing and for 2, and SleepGuard.swift:87 has one message for both. Something like "powermode 2 (High Power) under Battery Power, left alone" would match what happened.

Not exercised: sessions longer than a few minutes, the battery floors (the battery was at 100%), the heat rules, and a restart with the mode claimed.

Sources

  • Fetching the PR heads without a fork, git fetch origin pull/ID/head:BRANCH: GitHub, Checking out pull requests locally
  • Applying each PR's own commits, git cherry-pick ..BRANCH: git-cherry-pick
  • Keeping main's text in a docs-only conflict, git restore --source=HEAD --staged --worktree: git-restore
  • What a real-Mac report should include: CONTRIBUTING.md and Real-Mac checks before a release
  • man pmset on macOS 27.0 documents neither powermode nor lowpowermode, so the powermode values above come from live pmset -g custom output and powerd's log, not from Apple documentation.

krishhgg and others added 3 commits October 11, 2026 19:16
krishhgg#66 moved the real-Mac checklist from docs/release-validation.md to docs/releasing.md. This PR's two checklist changes move there: the darkening-refused item now says a macOS major other than 26 or 27, and the macOS 27 Low Power Mode item goes under Sessions and recovery. The October 10 macOS 27.0.1 results stay in release-validation.md. Brings in krishhgg#85's macOS 27 CI job and krishhgg#89's test fix.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Brings in krishhgg#86, which runs ShellCheck over the scripts the tests write.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
A Battery Power `lowpowermode` line with no value was skipped, so a
`powermode 0` below it read as "off" and Insomnia took over the mode.
The lowpowermode line now wins even with no value, and reads as unknown.

The read error also said "no lowpowermode or powermode line" for High
Power (`powermode 2`) and for values like 3. It now names the line it
read: missing, High Power left alone, or a value that is not 0 or 1.

Tests add krishhgg#69's macOS 26.7.1 excerpt (Battery `powermode 0`, AC
`powermode 2`), the reasons, and the error isLowPowerModeOn throws.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@krishhgg

krishhgg commented Oct 12, 2026 •

Copy link
Copy Markdown
Owner

I pushed c835d51 and 7c7eae9 to this branch with two small fixes from review:

  • A lowpowermode line with no value under Battery Power used to fall back to powermode, so powermode 0 below it read as "off" and Insomnia would take over the mode. That line now wins and reads as unknown, so Insomnia leaves the mode alone.
  • When the mode can't be read, the log said "no lowpowermode or powermode line" even for High Power (powermode 2). It now says what pmset printed: no line, High Power, or a value other than 0 or 1.

I also added tests for both, plus the macOS 26.7.1 excerpt from #69 (Battery powermode 0, AC powermode 2, which reads as off).

🤖 Generated with Claude Code

"a setting Insomnia leaves alone" was wrong in recovery, which logs the
same error and then switches the mode off when the journal says it was
ours. The reason now says only what pmset printed.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@krishhgg
krishhgg merged commit 57a68f5 into krishhgg:main Oct 12, 2026
5 checks passed
krishhgg added a commit to nsd97/Insomnia that referenced this pull request Oct 12, 2026
docs/release-validation.md conflicted: main's October 10 macOS 27.0.1
section (krishhgg#58) and this branch's October 11 Comet section were added at
the same place. Both are kept, oldest first.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@krishhgg

Copy link
Copy Markdown
Owner

Thanks again for this fix. Before it ships in a release, I need test evidence from your Mac for the code as it is now on main. The evidence in this PR's description came from an earlier build. Since then the branch got review fixes and merges of main, so it doesn't cover the code that merged. Any change that can affect how Insomnia behaves needs this, so I'm asking every contributor from this round.

Checks for this change (on your macOS 27.0.1 MacBook Pro)

  1. Low Power Mode on battery. Unplug, turn on "Low Power Mode while the lid is closed", start a session, and close the lid for at least 5 minutes. Expected: no "could not read low power mode" line in the log, the samples show powermode 1 under Battery Power while the lid is closed, and after the session ends it's back to 0.
  2. High Power is left alone. Set High Power on battery beforehand, so pmset -g custom shows powermode 2 under Battery Power. Repeat check 1. Expected: the log says the read was refused with "powermode 2 under Battery Power is High Power, not Low Power Mode on or off", Insomnia never turns Low Power Mode on, and powermode 2 is still set after the session.
  3. Your own Low Power Mode is kept. Turn Low Power Mode on yourself (powermode 1) before starting. Run a session with a lid close. Expected: it's still on after the session ends.
  4. Recovery after a crash. With Insomnia's Low Power Mode on (check 1's setup, lid open), quit the app with kill -9 <pid> and let the session's end time pass. Expected: the recovery agent restores sleep and switches the mode back to 0. Post the backstop lines from insomnia.log and pmset -g custom afterwards.
  5. Display and keyboard darkening on 27. Close the lid during a session. Expected: the log shows "display darkened" and "keyboard backlight off" and no "refused" line, and Settings shows no note under the darken toggle. Opening the lid restores both.
  6. A plain session on AC. Close the lid for at least 10 minutes. Expected: pmset -g log shows no sleep in that window, and after the session ends SleepDisabled reads 0.

Build and what to post

  • Build. A clean checkout of current main (db9085e or later), with no other PRs applied, 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.
  • Samples. Before each check, start a sampler in a Terminal window and leave it running. It keeps logging while the lid is closed, because the Mac stays awake:
    while :; do date '+%T'; pmset -g | grep SleepDisabled; pmset -g custom | sed -n '/Battery Power/,/AC Power/p' | grep -E 'powermode|lowpowermode'; sleep 5; done | tee -a ~/insomnia-samples.txt
  • For each check, post the steps you ran with times, pass or fail, and the raw output that shows it:
    • the lines of ~/Library/Logs/Insomnia/insomnia.log for that run;
    • the samples for that run;
    • pmset -g log | grep -E ' (Sleep|Wake|DarkWake) ' for that time window, which shows whether the Mac slept;
    • the extra 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. Run everything while you're with the Mac, on a stable, ventilated surface, never in a bag, and not while you have unsaved work open.

🤖 Generated with Claude Code

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.

4 participants