Mechanical reformat — no logic changes. 398 files changed, 19125 insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc (singleQuote:true, semi:true, tabWidth:2, trailingComma:all, printWidth:100). Isolated per D-13-08 for reviewability.
5.5 KiB
phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
| phase | plan | subsystem | tags | dependency_graph | tech_stack | key_files | decisions | metrics | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 06-ux-polish | 03 | api/broker |
|
|
|
|
|
|
Phase 06 Plan 03: hasRrule Server-Side Exposure Summary
Added hasRrule: boolean to the CalendarOccurrence interface in expand.ts and populated it via event.isRecurring() — the server-side signal that gates the whole-series-edit confirmation prompt (D-08).
Tasks Completed
| # | Task | Type | Commit | Outcome |
|---|---|---|---|---|
| 1 | RED — failing tests for hasRrule + bounded expansion | TDD test | 593302e |
3 hasRrule failures + duration test confirmed red |
| 2 | GREEN — add hasRrule to interface and populate it | TDD impl | 44d336c |
10/10 expand tests pass |
What Was Built
apps/api/src/broker/expand.ts
Added hasRrule: boolean field to CalendarOccurrence interface with JSDoc. Captured const isRecurring = event.isRecurring() once before the non-recurring/recurring branch. Both occurrences.push({...}) sites now include hasRrule: isRecurring — false in the non-recurring branch, true in the recurring branch.
apps/api/tests/broker/expand.test.ts
Added two new describe blocks:
hasRrule field — D-08 (2 tests):
weekly-dst.ics(recurring): all occurrences havehasRrule === truesingle-duration.ics(non-recurring): the single occurrence hashasRrule === false
Bounded RRULE (COUNT=3) — D-06 invariant (3 tests):
COUNT=3within a 6-month window returns exactly 3 occurrences- Each bounded occurrence duration = 1 hour from DTSTART→DTEND (not recurrence span)
- Bounded occurrences have
hasRrule === true
apps/api/tests/fixtures/weekly-count3.ics
New fixture: FREQ=WEEKLY;COUNT=3, DTSTART:20260601T090000Z, DTEND:20260601T100000Z (1-hour UTC events). Used for the bounded expansion invariant test.
TDD Gate Compliance
- RED commit (
test(06-03): ...) at593302e— tests written first, confirmed failing due to absenthasRrulefield - GREEN commit (
feat(06-03): ...) at44d336c— implementation added, all 10 tests pass
Deviations from Plan
1. [Rule 1 - Bug] Duration test using epochMilliseconds instead of epochSeconds
- Found during: Task 1 test writing
- Issue:
Temporal.ZonedDateTime.epochSecondsreturnsNaNin thetemporal-polyfillpackage used in the test suite;epochMillisecondsreturns a regularnumber - Fix: Duration assertion uses
endZdt.epochMilliseconds - startZdt.epochMillisecondsand compares to3_600_000(1 hour in ms) - Files modified:
apps/api/tests/broker/expand.test.ts - Commit:
593302e(incorporated into RED commit before final form)
No other deviations. Plan executed as written.
Verification
npx vitest run tests/broker/expand.test.ts
Test Files 1 passed (1)
Tests 10 passed (10)
grep -n "hasRrule" apps/api/src/broker/expand.ts
68: hasRrule: boolean
224: // Capture once — used in both branches to populate hasRrule.
261: hasRrule: isRecurring, // always false in the non-recurring branch
308: hasRrule: isRecurring, // always true in the recurring branch
client.ts untouched — mirror field is Plan 05's responsibility.
Known Stubs
None. hasRrule is fully populated from event.isRecurring() — no placeholder values.
Threat Flags
No new threat surface. hasRrule is a boolean derived from data already returned to the authenticated caller. T-06-03 accepted in plan threat model.