feat: Ignite v3 Plan 2 — scheduling - #6
Merged
Conversation
Six tasks implementing D8 and spec section 6: a hasTime model field, an untimed-sorts-last pure seam, row labels that respect it, and the recurrence dialog becoming a schedule dialog with an optional repeat. Stress-tested before committing; five findings folded in: - sortByDueThenUntimed compares the calendar day FIRST. Section 6.2 scopes the untimed-last rule to a single day, but the sort also runs over the overdue bucket, which spans weeks — without it, an untimed task from three weeks ago sorts below this morning's 09:00. - parseDate/parseTime close a RangeError path. buildDueAt runs as an argument to onSave, so a throw fires before closeRecurrenceEditor clears inert, wedging the app behind an open dialog. - Open focus moves to the date field. The old selector took the first cadence radio whatever was checked, parking a weekly task on "Does not repeat" and skipping the two fields above it. - A local [hidden] rule so the dialog does not depend on a design-system rule for correctness. Both containers set display, which beats the UA [hidden]; the reset only started covering this in 2.0.1 today. - The createdAt tie-break is documented as safe only because the value is always a UTC ISO string. Security and privacy passes were clean.
Day ascending first, then timed-before-untimed within each day, then clock time, then createdAt. The day comparison matters because this sorts the overdue bucket as well as today's, and overdue spans weeks — without it an untimed task from three weeks ago lands below this morning's 09:00. Also updates the existing "sorts today by dueAt ascending" fixture to set hasTime on both dated tasks. Sorting by clock time is only meaningful for tasks that carry one; without the flag those fixtures are untimed and correctly sort by createdAt instead. The test's intent was always two timed tasks — the fixture just could not say so before now.
This was referenced Aug 13, 2026
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.
Implements decision D8 and all of §6 of the v3 spec: any task can now carry a real due date and, optionally, a time of day.
Plan:
docs/superpowers/plans/2026-08-13-ignite-v3-scheduling.md(stress-tested before execution; five findings folded in).What changed
hasTimeon the modelBOOL_FIELDS0/1 conversion. No DB version bump, no migration — absent fields read asfalse.sortByDueThenUntimedinutils/time.js: day ascending, then timed-before-untimed within each day, then clock time, thencreatedAt.task.jspicksformatTimeLabelwhen timed,formatOccurrenceLabelwhen not.A one-off task could not previously be given a date at all.
buildRule()always returned a rule, so every save created a repeat. That is the part of §6 that is easy to miss — it appears there as a finding rather than a requirement.Verification
221 tests / 15 files, Biome clean (57 files), build clean.
Browser-verified against
ignite-dev, all eleven dialog invariants re-checked after the restructure rather than assumed:0), interval clamping on blur not mid-type, the date remaining the single source for monthlyday, chips remaining the source for weekly, backdrop-click closing only from the backdrop, Escape returning focus to.task__menu-btn— no focus reaching<body>on any path.<img src=x onerror=…>renders as literal text in the heading, with no injected element and the payload never firing.RangeErrorpath is closed —99:99and free text both disable Save rather than throwing out of the handler and leaving the backgroundinert.09:00,10:00, untimed last.dueAtexactly 7 days to 14:45 local, withhasTimeintact andcompletedstill0..repeat-inputso no new contrast surface.Not done, deliberately
.repeat-panelis still owed.pickNextTasktreats an untimed task as already overdue (its stored midnight is behindnow), so it is never the "Next" highlight. Pre-existing and invisible while every dated task was midnight. Plan 3 replaces the function, so fixing it now means deriving its semantics twice.repeat-*naming is unchanged — only user-visible copy says "Schedule".New invariants are recorded in project memory.