booking: spec revision 9 — two more engine bugs, and the spec rounds close
Fortnightly BYDAY skips the first selected weekday of an eligible week, and ParseRule accepts a monthly BYDAY the expander then reads as the master's day of month. Both are calendar bugs every view already had. Eight rounds, 58 findings, 58 accepted; the next review is of the code.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 file changed,
+46
−7
docs/superpowers/specs/2026-09-07-booking-design.md+46 −7
diff --git a/docs/superpowers/specs/2026-09-07-booking-design.md b/docs/superpowers/specs/2026-09-07-booking-design.md| index 034670a..a83ec9e 100644 |
| --- a/docs/superpowers/specs/2026-09-07-booking-design.md |
| +++ b/docs/superpowers/specs/2026-09-07-booking-design.md |
| @@ -4,11 +4,12 @@ |
| Fable 5.1. Approved in outline by Paul in the session that wrote it |
| (three questions, answered: confirm by email link; everything the host |
| can see counts as busy; pages are configurable per person). **Revision |
| -8.** Reviewed seven times by GPT-6 (`gpt-6-astra` on the one run that |
| -exposed its identifier) through `codex-cli 0.153.0`; all seven returned |
| -REWRITE, each round narrower than the last — rounds 6 and 7 found three |
| -each, and round 7's were a fourth pre-existing calendar bug and two |
| -transaction boundaries. §12 records every finding |
| +9.** Reviewed eight times by GPT-6 (`gpt-6-astra` on the one run that |
| +exposed its identifier) through `codex-cli 0.153.0`; all eight returned |
| +REWRITE, each round narrower than the last. Rounds 7 and 8 found |
| +nothing in the booking design itself: every finding was a pre-existing |
| +bug in the recurrence engine the design depends on, and the spec owns |
| +them (§4.1). The spec rounds end here; the next review is of the code. §12 records every finding |
| and disposition, including the two scoped rather than solved. |
| Asked for: a Calendly / Cal.com style "book time with me" page to share |
| @@ -324,6 +325,27 @@ time must be what the calendar shows. |
| resolved time. Regression: the daily 01:30 series shows 01:30 on 30 |
| March and on a September day, and a September booking page does not |
| offer 01:30–02:00 over it. |
| +- **Fortnightly `BYDAY` skips a weekday (r8 f1).** `advance` for a |
| + weekly rule with an interval searches only the next seven days and |
| + then jumps a whole interval, so `FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE` |
| + from Wednesday 2 September generates 16 September and never 14 |
| + September. Fix: weekly advancement enumerates every selected weekday |
| + within each ELIGIBLE civil week, anchored to the master's week |
| + (weeks counted from the master's week start, eligible when the |
| + difference is a multiple of the interval), in order. Regression: 14 |
| + and 16 September both show and neither is bookable. |
| +- **A parsed rule the expander cannot execute (r8 f2).** `ParseRule` |
| + accepts `FREQ=MONTHLY;BYDAY=MO` (every Monday of the month) and the |
| + expander ignores `ByDay` for monthly rules, falling back to the |
| + master's day of month. Fix: `ParseRule` validates COMBINATIONS — |
| + `ByDay` (non-ordinal) only with `FREQ=WEEKLY`; `Ordinal`/`MonthDay` |
| + only with `FREQ=MONTHLY`; nothing but `INTERVAL`/`COUNT`/`UNTIL` with |
| + `DAILY` and `YEARLY` — and returns `ErrForeignRule` otherwise. The |
| + rule is still stored verbatim (ruling 4), the calendar shows its |
| + first occurrence as it does for any foreign rule, and the strict |
| + reader fails closed on it. Regression: the import above → the day |
| + page shows one occurrence, `EventsInRangeStrict` errs, and the public |
| + page 503s. |
| - **Expansion that did not finish, or could not start (r4 B5, r6 f1; |
| B23).** `Expand` stops at 100,000 steps and returns an ordinary |
| slice; and `EventsInRange` renders a master whose rule `ParseRule` |
| @@ -726,7 +748,10 @@ NULL`. |
| inverted window return errors, not lists. |
| - **Range query**: the moved-override-of-finished-series case; the |
| long-final-occurrence case; a daily 01:30 Dublin series created 28 |
| - March shows 01:30 on 30 March and on a September day (gap drift); the final occurrence across the fold |
| + March shows 01:30 on 30 March and on a September day (gap drift); a |
| + fortnightly MO,WE series from Wednesday 2 September shows 14 AND 16 |
| + September; `FREQ=MONTHLY;BYDAY=MO` is foreign, shows once, and fails |
| + the strict reader; the final occurrence across the fold |
| with `UNTIL` the night before (the 25-hour slack); the 47-hour |
| two-day weekly event keeps its second day on later occurrences; a |
| 1700 daily rule → `ErrIncompleteExpansion` from the strict reader |
| @@ -962,4 +987,18 @@ Rejected: none. |
| Rejected: none. |
| -**Round 8** — to be recorded below. |
| +**Round 8: REWRITE.** 2 blockers, both recurrence-engine bugs; |
| +round 7's three HOLD. |
| + |
| +| Ref | Finding | Disposition | |
| +|---|---|---| |
| +| f1 | Fortnightly `BYDAY` skips the first selected weekday of an eligible week | **Accepted, a fifth calendar bug.** Enumerate selected weekdays per eligible week (§4.1). | |
| +| f2 | `ParseRule` accepts combinations the expander misreads | **Accepted, a sixth.** Combination validation → `ErrForeignRule`; strict fails closed (§4.1). | |
| + |
| +Rejected: none. |
| + |
| +**Spec review closed at revision 9.** Eight rounds, 58 findings, 58 |
| +accepted (two scoped). The design has been stable since revision 5; |
| +rounds 6–8 moved transaction boundaries and found six bugs in the |
| +recurrence engine that every calendar view already had. Further review |
| +is of the branch's code, recorded in the calendar spec's review log. |