feat(night-shift): default Night Shift to always on - #2091
Conversation
Night Shift state lives in the per-user CoreBrightness session and is not exposed through any plist system.defaults can write, so express it as a home-manager launchd user agent that calls nightlight on login. Clear the schedule before turning it on, since sunset-to-sunrise would otherwise flip Night Shift back off at sunrise. RunAtLoad without a StartInterval keeps this a default rather than an enforcement: a manual toggle sticks until the next login.
|
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
Warning Review limit reached
Next review available in: 21 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces a new night-shift service for macOS (Darwin) systems, which configures a launchd agent to set a default screen temperature and enable Night Shift on login. It includes a shell script to apply the settings and a shell spec test suite to verify its behavior. Feedback on the pull request highlights two important issues in the Nix configuration: static log paths in /tmp that can cause permission conflicts on multi-user systems, and top-level evaluation of Darwin-only packages on non-Darwin platforms. A refactoring is suggested to wrap the module in a Darwin-conditional block and remove the static log paths.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| { pkgs }: | ||
| let | ||
| # 0 = least warm, 100 = warmest. | ||
| temperature = 100; | ||
|
|
||
| applyNightShiftScript = pkgs.replaceVars ./apply-night-shift.sh { | ||
| nightlightBin = "${pkgs.nightlight}/bin/nightlight"; | ||
| inherit temperature; | ||
| }; | ||
| in | ||
| { | ||
| # Night Shift state lives in the per-user CoreBrightness session and is not | ||
| # exposed through any plist `system.defaults` can write, so the only way to | ||
| # express "on by default" is to talk to that session on login. | ||
| # | ||
| # RunAtLoad without a StartInterval deliberately makes this a default rather | ||
| # than an enforcement: a manual toggle sticks until the next login. | ||
| launchd.agents.night-shift = pkgs.lib.mkIf pkgs.stdenv.isDarwin { | ||
| enable = true; | ||
| config = { | ||
| ProgramArguments = [ | ||
| "${pkgs.bash}/bin/bash" | ||
| "${applyNightShiftScript}" | ||
| ]; | ||
| RunAtLoad = true; | ||
| StandardOutPath = "/tmp/night-shift.log"; | ||
| StandardErrorPath = "/tmp/night-shift.error.log"; | ||
| }; | ||
| }; | ||
| } |
There was a problem hiding this comment.
This refactoring addresses two important issues:
- Multi-user Permission Conflicts: Writing logs to static paths in
/tmp(like/tmp/night-shift.log) causes permission conflicts on multi-user macOS systems. When the first user logs in, the log file is created under their ownership. When a subsequent user logs in, their launchd agent will fail to start because they lack write permissions to the existing log file. Since this is a simple run-once script, we can safely removeStandardOutPathandStandardErrorPathentirely. - Platform Evaluation Isolation: Defining
applyNightShiftScript(which referencespkgs.nightlight) in the top-levelletblock forces its evaluation even on non-Darwin systems (like Linux). Sincenightlightis a Darwin-only package, this can cause evaluation or platform-support errors on Linux. Wrapping the entire module inpkgs.lib.optionalAttrs pkgs.stdenv.isDarwinensures that Darwin-specific derivations are only evaluated on Darwin.
{ pkgs }:
pkgs.lib.optionalAttrs pkgs.stdenv.isDarwin (
let
# 0 = least warm, 100 = warmest.
temperature = 100;
applyNightShiftScript = pkgs.replaceVars ./apply-night-shift.sh {
nightlightBin = "${pkgs.nightlight}/bin/nightlight";
inherit temperature;
};
in
{
# Night Shift state lives in the per-user CoreBrightness session and is not
# exposed through any plist `system.defaults` can write, so the only way to
# express "on by default" is to talk to that session on login.
#
# RunAtLoad without a StartInterval deliberately makes this a default rather
# than an enforcement: a manual toggle sticks until the next login.
launchd.agents.night-shift = {
enable = true;
config = {
ProgramArguments = [
"${pkgs.bash}/bin/bash"
"${applyNightShiftScript}"
];
RunAtLoad = true;
};
};
}
)
There was a problem hiding this comment.
Performed full review of 9feacad...f165c67
Analysis
No significant architectural issues identified. The PR implements a well-structured macOS service module with appropriate boundary selection (home-manager vs system-level), proper platform gating, deterministic dependency injection, and a pragmatic imperative bridge pattern consistent with existing precedent. The design appropriately respects user preferences while providing sensible defaults, and includes test coverage for implementation verification.
Tip
Help
Slash Commands:
/review- Request a full code review/review latest- Review only changes since the last review/describe- Generate PR description. This will update the PR body or issue comment depending on your configuration/help- Get help with Mesa commands and configuration options
0 files reviewed | 0 comments | Edit Agent Settings • Read Docs
| # A sunset-to-sunrise schedule turns Night Shift back off at sunrise, so the | ||
| # schedule has to be cleared before `on` can mean "stays on". | ||
| "$nightlight_bin" schedule stop | ||
| "$nightlight_bin" temp "$temperature" |
There was a problem hiding this comment.
Login resets temperature too, not just on/off: The PR description frames this agent as a default that a manual toggle can override until next login, and that's true for on/off — but this line unconditionally re-applies temp 100 at every login, so a manual nightlight temp 60 (or any other value) gets silently stomped on the next login. If that's intentional (i.e. the 'default' is really on @ temp=100, not just on), consider calling it out in the module comment; if not, gating the temp call on the current preference (or dropping it and relying on macOS to remember the last temp) would keep temperature adjustments sticky the same way on/off is.
Mesa DescriptionTL;DRDefaults macOS Night Shift to "always on" (warmest temperature, no schedule) via a per-user What changed?
Description generated by Mesa. Update settings |
There was a problem hiding this comment.
1 issue found across 4 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="home-manager/services/night-shift/default.nix">
<violation number="1" location="home-manager/services/night-shift/default.nix:26">
P2: Using fixed paths in `/tmp` for `StandardOutPath` and `StandardErrorPath` will cause permission conflicts on multi-user macOS systems. The first user's login creates these files with their ownership; subsequent users' launchd agents will fail to write to them. Since this is a simple run-once script, consider removing these log paths entirely (launchd will route output to the system log), or use a per-user path like `~/Library/Logs/night-shift.log`.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| "${applyNightShiftScript}" | ||
| ]; | ||
| RunAtLoad = true; | ||
| StandardOutPath = "/tmp/night-shift.log"; |
There was a problem hiding this comment.
P2: Using fixed paths in /tmp for StandardOutPath and StandardErrorPath will cause permission conflicts on multi-user macOS systems. The first user's login creates these files with their ownership; subsequent users' launchd agents will fail to write to them. Since this is a simple run-once script, consider removing these log paths entirely (launchd will route output to the system log), or use a per-user path like ~/Library/Logs/night-shift.log.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At home-manager/services/night-shift/default.nix, line 26:
<comment>Using fixed paths in `/tmp` for `StandardOutPath` and `StandardErrorPath` will cause permission conflicts on multi-user macOS systems. The first user's login creates these files with their ownership; subsequent users' launchd agents will fail to write to them. Since this is a simple run-once script, consider removing these log paths entirely (launchd will route output to the system log), or use a per-user path like `~/Library/Logs/night-shift.log`.</comment>
<file context>
@@ -0,0 +1,30 @@
+ "${applyNightShiftScript}"
+ ];
+ RunAtLoad = true;
+ StandardOutPath = "/tmp/night-shift.log";
+ StandardErrorPath = "/tmp/night-shift.error.log";
+ };
</file context>
treefmt expands multi-element lists one entry per line. Unformatted since the gh-stack extension was added in 797139f.
Closes the shell-test failure introduced in #2091, which added apply-night-shift.sh and its spec without registering the script in coverage_spec.sh.
What
Makes Night Shift the default color mode on macOS: always on, rather than the previous sunset-to-sunrise schedule.
Adds a home-manager service at
home-manager/services/night-shift/, wired intohome-manager/services/default.nix, with a shellspec covering the apply script.Why this shape
Night Shift state lives in the per-user CoreBrightness session and is not exposed through any plist
system.defaultscan write, so there is no declarative nix-darwin option for it. The only way to express "on by default" is to talk to that session, which is why this shells out tonightlight(in nixpkgs, darwin-only) from a launchd agent. Same reasoning as the keyboard module shelling out tohidutil.It lives in home-manager rather than nix-darwin because the state is per-user, not system-wide.
Notes for reviewers
Two deliberate choices worth a look:
schedule stopbeforeon. A sunset-to-sunrise schedule turns Night Shift back off at sunrise, so the schedule has to be cleared beforeoncan mean "stays on".RunAtLoadwith noStartInterval. This makes the setting a default rather than an enforcement — a manual toggle off sticks until the next login. A periodic re-assert would fight the user every few minutes.Temperature is pinned at 100 (warmest), matching what the machine was already set to, so the mode is fully declarative rather than half-pinned.
Commits
feat(night-shift): default Night Shift to always on— the change itself.style(gh): format extensions list— drive-by treefmt fix onhome-manager/programs/gh/default.nix, unformatted on main since the gh-stack commit (797139f). Whitespace only, unrelated to Night Shift; separated out so it stays easy to skip.Verification
spec/night_shift_spec.shpasses (4 examples, 0 failures); picked up by normal shellspec discovery.nix build .#checks.aarch64-darwin.eval-darwin-defaultpasses.nix build .#checks.aarch64-darwin.treefmtpasses (failed on main before commit 2).on until sunrise->on, schedulesunset to sunrise->off, temp100.nightlightpackage does not break Linux evaluation — the Linux home config errors at the identical pre-existingdocker.serviceplatform mismatch (same drv hash) with and without this change.