Environment
- nightshift v0.3.2
- Linux (Arch), systemd user timer
- Providers: Claude + Codex (preference: claude → codex)
Bug 1: schedule.max_projects config value ignored by run command
Expected
Setting schedule.max_projects: 5 in config.yaml should be respected by nightshift run.
Actual
The run command reads max-projects exclusively from the CLI flag (hardcoded default 1) and never consults the config file. The config set schedule.max_projects 5 command accepts and stores the value, but run ignores it.
Source trace (v0.3.2)
cmd/nightshift/commands/run.go:
- Line 107:
runCmd.Flags().Int("max-projects", 1, ...) — hardcoded default
- Line 120:
maxProjects, _ := cmd.Flags().GetInt("max-projects") — reads CLI flag only
- Lines 198-199: truncates project list to
maxProjects BEFORE processing
No code path reads schedule.max_projects from config.
Impact
With the default of 1, only the first configured project is ever considered. If that project is skipped ("already processed today") or fails, no other projects are tried for the entire day. The systemd service must be manually edited to pass --max-projects N as a workaround.
Reproduction
nightshift config set schedule.max_projects 5 --global
nightshift config get schedule.max_projects # Returns: 5
nightshift run --dry-run # Shows: "Projects (0 of 1)" — config ignored
nightshift run --dry-run --max-projects 5 # Shows: "Projects (4 of 5)" — CLI flag works
Suggested fix
In runRun(), after loading config, check if the flag was explicitly set; if not, fall back to cfg.Schedule.MaxProjects:
if !cmd.Flags().Changed("max-projects") && cfg.Schedule.MaxProjects > 0 {
maxProjects = cfg.Schedule.MaxProjects
}
Bug 2: Budget calibration reports "exhausted" at day boundary despite 0% usage
Expected
At midnight (start of new day), with 0% daily usage and tokens remaining in the weekly pool, Claude provider should have budget available.
Actual
At midnight, nightshift logs:
provider claude: budget exhausted (0.0% used)
This is contradictory — 0% used means nothing was consumed, yet the budget is reported as exhausted. This causes nightshift to fall through to the Codex provider (which then fails separately — see Bug 3).
Conditions
calibrate_enabled: true with "low confidence, 2 samples"
- No
stats-cache.json exists (JSONL fallback is always used)
- Happens consistently at 00:00 local time on day boundary
- Does NOT happen later in the day (budget shows correctly at 10:00+)
Reproduction
Observed on two consecutive nights:
2026-02-16T00:00:02 — "provider claude: budget exhausted (0.0% used)" → task failed
2026-02-17T00:00:48 — "provider claude: budget exhausted (0.0% used)" → task failed
Successful runs on prior days (Feb 13–15) all occurred at non-midnight times (07:58, 22:00, 11:30).
Workaround
nightshift config set budget.calibrate_enabled false --global
This uses the static weekly_tokens config value (2.5M) instead of the calibrated estimate, which produces stable budget calculations at all times of day.
Bug 3: Codex provider crashes with exit status 2 in automatic fallback path
Expected
When Claude budget is exhausted and Codex is the fallback provider, Codex should execute the task.
Actual
Codex fails instantly (~55ms) with exit status 2 (CLI argument error) when invoked through the automatic fallback path. However, Codex works correctly when invoked directly:
# Fails (automatic fallback after Claude exhausted):
nightshift run # → "agent execution: exit status 2" in 55ms
# Works (explicit provider):
nightshift task run lint-fix --provider codex --project ~/Sync/code/qtdashboard
Hypothesis
The automatic fallback path may be passing incorrect CLI flags to the Codex binary — possibly Claude-specific flags like --dangerously-skip-permissions that Codex doesn't recognize (Codex only has --dangerously-bypass-approvals-and-sandbox). The instant failure (55ms) and exit code 2 are consistent with a CLI argument parse error.
Environment details
- Claude config:
dangerouslyskippermissions: true, dangerouslybypassapprovalsandsandbox: false
- Codex config:
dangerouslyskippermissions: false, dangerouslybypassapprovalsandsandbox: true
Environment
Bug 1:
schedule.max_projectsconfig value ignored byruncommandExpected
Setting
schedule.max_projects: 5in config.yaml should be respected bynightshift run.Actual
The
runcommand readsmax-projectsexclusively from the CLI flag (hardcoded default1) and never consults the config file. Theconfig set schedule.max_projects 5command accepts and stores the value, butrunignores it.Source trace (v0.3.2)
cmd/nightshift/commands/run.go:runCmd.Flags().Int("max-projects", 1, ...)— hardcoded defaultmaxProjects, _ := cmd.Flags().GetInt("max-projects")— reads CLI flag onlymaxProjectsBEFORE processingNo code path reads
schedule.max_projectsfrom config.Impact
With the default of 1, only the first configured project is ever considered. If that project is skipped ("already processed today") or fails, no other projects are tried for the entire day. The systemd service must be manually edited to pass
--max-projects Nas a workaround.Reproduction
Suggested fix
In
runRun(), after loading config, check if the flag was explicitly set; if not, fall back tocfg.Schedule.MaxProjects:Bug 2: Budget calibration reports "exhausted" at day boundary despite 0% usage
Expected
At midnight (start of new day), with 0% daily usage and tokens remaining in the weekly pool, Claude provider should have budget available.
Actual
At midnight, nightshift logs:
This is contradictory — 0% used means nothing was consumed, yet the budget is reported as exhausted. This causes nightshift to fall through to the Codex provider (which then fails separately — see Bug 3).
Conditions
calibrate_enabled: truewith "low confidence, 2 samples"stats-cache.jsonexists (JSONL fallback is always used)Reproduction
Observed on two consecutive nights:
Successful runs on prior days (Feb 13–15) all occurred at non-midnight times (07:58, 22:00, 11:30).
Workaround
This uses the static
weekly_tokensconfig value (2.5M) instead of the calibrated estimate, which produces stable budget calculations at all times of day.Bug 3: Codex provider crashes with exit status 2 in automatic fallback path
Expected
When Claude budget is exhausted and Codex is the fallback provider, Codex should execute the task.
Actual
Codex fails instantly (~55ms) with
exit status 2(CLI argument error) when invoked through the automatic fallback path. However, Codex works correctly when invoked directly:Hypothesis
The automatic fallback path may be passing incorrect CLI flags to the Codex binary — possibly Claude-specific flags like
--dangerously-skip-permissionsthat Codex doesn't recognize (Codex only has--dangerously-bypass-approvals-and-sandbox). The instant failure (55ms) and exit code 2 are consistent with a CLI argument parse error.Environment details
dangerouslyskippermissions: true,dangerouslybypassapprovalsandsandbox: falsedangerouslyskippermissions: false,dangerouslybypassapprovalsandsandbox: true