Skip to content

feat: factor weeklyProgressWorkDays into pace calculation#1357

Closed
pstanton237 wants to merge 6 commits into
steipete:mainfrom
pstanton237:feat/workday-aware-pace
Closed

feat: factor weeklyProgressWorkDays into pace calculation#1357
pstanton237 wants to merge 6 commits into
steipete:mainfrom
pstanton237:feat/workday-aware-pace

Conversation

@pstanton237

@pstanton237 pstanton237 commented Jun 8, 2026

Copy link
Copy Markdown

Live app proof (Mon 22:00 KST, Settings > Display > Work Days = 5)

Before (main, 7-day linear) After (feat, 5-day workday)
Before: 2% in deficit After: 9% in deficit
27% used, 2% in deficit 27% used, 9% in deficit

Same usage, same time — the 5-day model distributes expected usage across configured work days only.

Summary

Threads the existing weeklyProgressWorkDays setting into the pace calculation so the expected usage curve distributes 100% across only the configured work days. Non-work days contribute zero expected usage, making the pace indicator accurate for users who only work on weekdays.

  • Adds workDays parameter to UsagePace.weekly()
  • Introduces workdayAwareExpected() which walks calendar day boundaries and classifies each slice by its actual calendar weekday (Mon=1..Sun=7, workday if isoWeekday <= workDays)
  • Passes weeklyProgressWorkDays from settings through all call sites
  • Falls back to linear model when workDays is nil, 7, or window is not 10080 minutes

Before / After

Scenario (5-day user, Friday 18:00) Before After
Expected usage 82% (linear 7-day) 95% (workday-aware)
100% actual → pace verdict 18% deficit 5% deficit (on pace)

After-fix proof (test run output)

✔ Test "weekly pace hides when usage exists but no elapsed" passed after 0.001 seconds.
✔ Test "session pace computes delta and eta for five hour window" passed after 0.001 seconds.
✔ Test "weekly pace computes delta and eta" passed after 0.001 seconds.
✔ Test "weekly pace hides when reset missing or outside window" passed after 0.001 seconds.
✔ Test "workday aware pace falls back to linear when workDays is nil or 7" passed after 0.001 seconds.
✔ Test "weekly pace marks lasts to reset when usage is low" passed after 0.001 seconds.
✔ Test "workday aware pace ignores non weekly windows" passed after 0.001 seconds.
✔ Test "workday aware pace shows on track for five day user on friday" passed after 0.001 seconds.
✔ Test "workday aware pace shows on track midweek" passed after 0.001 seconds.
✔ Suite UsagePaceTests passed after 0.001 seconds.

Verified on macOS 15.4, Swift 6.1, swift test --filter UsagePaceTests (2026-06-08).

Compatibility note

This PR intentionally reuses the existing weeklyProgressWorkDays setting (Off/4/5/7 in Display preferences). Users who previously set this value for visual markers will now also get workday-aware pace. This is the natural intent of the setting — a user who configured "5 days" wants their quota framed around a 5-day work week, both visually and in pace calculation. If maintainers prefer a separate opt-in, this can be split, but we believe the unified setting is the correct UX.

When Codex historical pace is active (historicalTrackingEnabled), it takes precedence over the workday model because it already incorporates actual usage patterns. The workday model serves as the baseline for users without historical data or for non-Codex providers.

Test plan

  • workday aware pace shows on track for five day user on friday — verifies 7-day model reports deficit, 5-day model reports on-pace
  • workday aware pace shows on track midweek — Wed 18:00 with 60% used ≈ on pace for 5-day model
  • workday aware pace falls back to linear when workDays is nil or 7
  • workday aware pace ignores non weekly windows — session windows unaffected
  • All existing UsagePaceTests and HistoricalUsagePaceTests pass unchanged

Fixes #1356
Follow-up to #1096 (visual segmentation)

@clawsweeper

clawsweeper Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codex review: needs real behavior proof before merge. Reviewed June 11, 2026, 5:29 PM ET / 21:29 UTC.

Summary
Review failed before ClawSweeper could summarize the requested change.

Reproducibility: unclear. The review failed before ClawSweeper could establish a reproduction path.

Review metrics: none identified.

Merge readiness
Overall: 🌊 off-meta tidepool
Proof: 🌊 off-meta tidepool
Patch quality: 🌊 off-meta tidepool
Result: rating does not apply to this item.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Risk before merge

  • [P1] No close action taken because the review did not complete.

Maintainer options:

  1. Decide the mitigation before merge
    Retry the Codex review after fixing the execution failure.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P1] Review did not complete, so no work-lane recommendation was made.
Review details

Best possible solution:

Retry the Codex review after fixing the execution failure.

Do we have a high-confidence way to reproduce the issue?

Unclear. The review failed before ClawSweeper could establish a reproduction path.

Is this the best way to solve the issue?

Unclear. Retry the review first so ClawSweeper can evaluate the actual issue and fix direction.

AGENTS.md: unclear because the file could not be read completely.

Codex review notes: model internal, reasoning high; reviewed against 7c279bca0477.

Label changes

Label changes:

  • remove merge-risk: 🚨 compatibility: Current PR review selected no merge-risk labels.
  • remove P3: Current review triage priority is none.

Label justifications:

  • rating: 🌊 off-meta tidepool: Overall readiness is 🌊 off-meta tidepool; proof is 🌊 off-meta tidepool and patch quality is 🌊 off-meta tidepool.
Evidence reviewed

What I checked:

  • failure reason: retryable codex transport failure.
  • codex failure detail: Codex review failed for this PR with exit 1.
  • codex stderr: y Friday is using their quota exactly as planned across 5 work days, yet the current model reports a large deficit all week.\n\n## Proposed behavior\n\nWhen weeklyProgressWorkDays is set (e.g. 5), the pace expected curve should distribute 100% across only the configured work days rather than uniformly across 7 calendar days:\n\n| Day (5-day model) | Expected | Actual | Verdict |\n|---|---|---|---|\n| Wednesday | 60% | 60% | on pace |\n| Friday | 100% | 100% | on pace |\n| Saturday | 100% | 100% | on pace |\n\nOn non-work days, the expected curve should remain flat (no additional expected usage).\n\n## Implementation notes\n\nUsagePace.weekly() currently computes:\nswift\nlet expected = ((elapsed / duration) * 100).clamped(to: 0...100)\n\n\nThis could be replaced with a workday-aware calculation when workDays is provided, distributing expected usage only across work-day hours within the window.\n\nThe weeklyProgressWorkDays value is already available in settings — it just needs to be threaded into the pace calculation path.\n\n## Context\n\nFollow-up to Feature (small?): Weekly Progress Bar configurable to segment in number of work days #1096 which added visual segmentation. This completes the feature by making the pace indicator workday-aware.".
  • codex stdout: No stdout captured.

Likely related people:

  • unknown: Codex failed before it could trace repository history. (role: review did not complete; confidence: low)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ebdb2d2217

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread Sources/CodexBarCore/UsagePace.swift Outdated
// (workDays=5 → Mon-Fri are work days)
var dayCursor = windowStart
while dayCursor < resetsAt {
let dayEnd = min(dayCursor.addingTimeInterval(daySeconds), resetsAt)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Split workday pace at local day boundaries

When the weekly reset is not at local midnight, this loop walks 24-hour slices anchored to windowStart and labels the entire slice by the slice's starting weekday. For example, with a Sunday 20:00 local reset and 5 workdays, Monday 00:00–20:00 is counted as Sunday/non-work, so expected usage stays at 0 for most of Monday and the whole workday curve is shifted by the reset time. Since resetsAt can be any Date, the workday-aware calculation should split intervals on calendar day boundaries before classifying them.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P3 Low-risk cleanup, docs, polish, ergonomics, or speculative feature. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. proof: sufficient Contributor real behavior proof is sufficient. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 8, 2026
@TheAngryPit

Copy link
Copy Markdown

Thanks for pointing me here. I do not want to duplicate your PR, so I took a quick pass over this branch instead.

I ran swift test --filter UsagePaceTests in a clean worktree, and it passed here. Swift selected UsagePaceTests plus HistoricalUsagePaceTests, 53 tests total.

One small consistency edge case I noticed: UsagePace.weekly(... workDays:) accepts workDays == 1, but the existing marker path treats that as invalid (workDayMarkerPercents only accepts 2...7) and the UI picker exposes Off/4/5/7. So if weeklyProgressWorkDays ever ends up as a stale/manual value of 1, pace would switch to a one-workday model while the marker UI still treats it as disabled/invalid.

Tiny fix would be to align the valid range in UsagePace with the marker/UI semantics, plus one focused test that workDays: 1 falls back to the normal linear weekly pace.

Happy to send a small patch or paste the exact diff here if useful.

The broader maintainer decision still seems to be whether the existing Display setting should now also affect pace semantics. If yes, the setting copy / PR description probably just needs to say that clearly.

@pstanton237

Copy link
Copy Markdown
Author

Thanks for catching that! Just pushed a fix — workDays >= 2 with a focused test for the linear fallback.

@clawsweeper clawsweeper Bot added rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. and removed proof: sufficient Contributor real behavior proof is sufficient. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 11, 2026
@steipete steipete force-pushed the feat/workday-aware-pace branch from de889d2 to 1810fb9 Compare June 11, 2026 21:26
pstanton237 and others added 6 commits June 11, 2026 23:30
The existing weeklyProgressWorkDays setting only affected visual markers
on the progress bar. This change threads the work-days value into
UsagePace.weekly() so that the expected usage curve distributes 100%
across configured work days only. Non-work days contribute zero expected
usage, producing a flat curve on weekends.

Users who work Mon-Fri and consume 100% by Friday now see "on pace"
instead of a misleading ~29% deficit from the 7-day linear model.

Fixes steipete#1356
Address Codex bot review feedback: when the weekly reset time is not at
midnight, the previous 24-hour-slice approach could misclassify hours
near day boundaries. Now uses calendar.startOfDay to split intervals at
local midnight, ensuring each slice is classified by its actual calendar
weekday regardless of reset time offset.
Align the valid workDays range (>= 2) with the UI picker (Off/4/5/7)
and marker path (2...7) to prevent inconsistent behavior from stale
or manual values.
@steipete steipete force-pushed the feat/workday-aware-pace branch from 1810fb9 to 6e1b7c5 Compare June 11, 2026 22:36
@steipete steipete closed this Jun 11, 2026
@steipete steipete reopened this Jun 11, 2026
@steipete

Copy link
Copy Markdown
Owner

Rebased and reviewed this contribution, fixed the pre-workday zero-usage edge case, and preserved the contributor commits in #1451. Repository CI would not start for this fork head even after a synchronize event and close/reopen, so the maintainer-hosted draft is now the reviewable continuation. Thanks @pstanton237!

@steipete steipete closed this Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P3 Low-risk cleanup, docs, polish, ergonomics, or speculative feature. rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: factor weeklyProgressWorkDays into pace calculation

3 participants