Never place outstanding work before the present moment - #104
Open
SociableSteve wants to merge 1 commit into
Open
SociableSteve wants to merge 1 commit into
SociableSteve wants to merge 1 commit into
Conversation
scheduleDay walked the plan's entries through the whole working window from the top regardless of what time it was, so a plan read mid-afternoon still drew an unfinished entry as if the day had not started, entirely behind the day bar's now marker. It has moved into src/domain/plan.ts, pure and generic over the entry shape, and now takes the present moment as a third argument. Completed and outstanding entries are placed in two separate passes rather than through one cursor floored at now: a shared cursor would still drag a finished entry into the future whenever it ranked ahead of one still outstanding. Completed entries are placed first, in their own rank order, from the top of the free time, exactly as before. Outstanding entries are then placed into whatever free time is left once the completed entries' own minutes and everything before now have both been removed, so a done entry's block straddling now cannot be handed to an outstanding entry either. Gaps are only the free time still ahead of now: elapsed free time is not drawn on the day bar's track at all, and is not offered as slack in the agenda. Spec 05 states the placement rule and gains criteria 21 to 24. Spec 08's criteria 41, 43 and 47 are extended in place to describe the same walk against the clock rather than the whole window; no new criteria there, since the day bar and the agenda are two renderings of one placement decided in spec 05. docs/using.md gains one sentence saying elapsed time reads as gone on the day bar. Closes #82. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #82.
What changed
scheduleDayinweb/surfaces/Dashboard.tsxwalked the plan's entries through the wholeworking window from the top, regardless of what time it was, so a plan read mid-afternoon
still drew an unfinished entry as though the day had not started, entirely behind the day
bar's now marker. It has moved into
src/domain/plan.ts, pure and generic over the entryshape (
SchedulableEntry, an estimate and a done flag), and its signature is nowscheduleDay(entries, freeIntervals, now).Completed and outstanding entries are placed in two separate passes rather than through one
cursor floored at
now:entry.done) are placed first, in their own rank order, from the topof the free intervals, exactly as the whole plan used to be walked. Completed work is a
fact about where it happened, and this pass never moves it.
is floored at
now.A single shared cursor floored at
nowwas not enough on its own: ranked ahead of anoutstanding entry, a completed one would be dragged into the future along with it. Doing
the subtraction before the floor is also what keeps a completed entry's block that
straddles
nowfrom being handed to an outstanding entry: the minutes it consumed areunavailable whichever side of
nowthey land on.Gaps are only the free time still ahead of
now: elapsed free time is not drawn on the daybar's track at all (no new track kind, per the plan this followed) and is not offered as
slack in the agenda. An outstanding entry with nowhere left to go past
nowstill comesback with
startsAt: nulland renders untimed, exactly as the existing unschedulable casealready did.
Specs and docs
Spec, then docs, then code, in this commit:
docs/specs/05-daily-plan.md: a new "Placing the plan against the clock" section statesthe rule (it belongs here, not in spec 08, since the day bar and the agenda are two
renderings of one placement), and criteria 21 to 24 are appended.
docs/specs/08-api-and-ui.md: criteria 41, 43 and 47 are extended in place to describethe same walk now that it also depends on the present moment. No new criteria there,
per the agreed scope: the decision moved to spec 05 rather than growing spec 08.
docs/using.md: one sentence added to the day bar bullet, saying elapsed time is notdrawn as free either.
README.md,docs/content-policy.mdanddocs/plan.mdchecked and left alone: no tool,content level or configuration changed, and this is a bug fix within an already-landed
milestone rather than a new one for
docs/plan.mdto record.Testing
Test-first, under
test/domain/plan.test.tsalongside the existing plan tests, drivingscheduleDaydirectly with a fixednowrather than through a rendered fixture. Nine newtests, one or more per new criterion:
now, never before it(21)
nowhas moved past it, and is notdragged forward (22)
(22)
nowdoes not let an outstanding entry start inside it(23)
nowyields no gap at all, and one straddlingnowistrimmed to its future part (23)
nowcomes back withstartsAt: nullrather than being dropped, and the genuinely-too-small remainder is still reported as a
gap (24)
nowbefore the window opens reproduces the old single-pass walk exactlynowafter the window closes places no outstanding work, while a done entry still keepsits own placement
All 79 existing tests in
web/Dashboard.test.tsxpass unchanged: every fixture in thatfile that sets
nowexplicitly does so against a calendar with no free intervals, andevery other test uses the default fixture where
nowequals the window's start, sonothing in the existing suite had elapsed time to expose the old defect. No real-clock
usage was found in that file either, so nothing needed pinning there.
Checks run, in this order, over the whole project:
npm run lint: clean.npm run typecheck: clean.npm test: 2933 tests pass across 136 files.npm run build: clean.npm run build:site: clean (needednpm installfirst in this fresh worktree, for afont package declared in
package.jsonbut missing fromnode_modules; unrelated tothis change, and neither
package.jsonnor the lockfile changed).Notes
While extending spec 08's criterion 47, I found its prose was already slightly stale in a
way this change makes worse: it explained the legend's four figures not summing exactly to
windowMinutesas a rounding artifact only. With elapsed free time now undrawn entirely,that gap can be much larger than a rounding error on a day with time behind it, so I
extended the criterion (and the matching code comments in
Dashboard.tsx) to name bothcauses rather than leave the older, now-incomplete explanation standing.
I did not touch the pre-existing (and, as far as I can tell, still present) edge case
where a no-estimate entry can be offered a placement sitting exactly on an interval's
boundary rather than genuinely inside it: it predates this change, is unrelated to the
present-moment rule, and fixing it was not part of what was asked.