Frontend spec parity: HSV mode, conflicts, regenerate confirm, audit-log page, carousel, static editor, guide, import panel #48

Closed
opened 2026-07-15 15:33:11 +00:00 by claude-bot · 7 comments
Contributor

Split from #40 — the UI gaps against spec §§4.3, 10, 3.6, 5.4. Ordered roughly by user impact.

Checkboxes re-audited against the code on 2026-09-01. They had drifted badly: most of this had shipped without being ticked. Each box below was verified by reading the implementation, not by memory.

  • Regenerate confirmation modal warning that the approved state will be replaced (§4.3) — SidePanel.tsx:319. Was already present; my earlier claim that it was missing matched on hook wiring rather than the button's onClick.
  • HSV color mode alongside RGB in ColorSlots.tsx (§10.1/10.2) — entryMode: "rgb" | "hsv", with rgbToHsv/hsvToRgb covered by colorConversion.test.ts
  • Conflict handling: conflict status, ⚠ indicator, "conflicts" filter chip, day-cell icon semantics reconciled with spec (#95)
  • Audit log page (admin) — AuditLogPage.tsx, routed in App.tsx; actor + action filtered server-side, date range client-side (the API takes no date params)
  • Preview carousel⚠️ half done. The stop control exists and is global (Header.tsx, SidePanel.tsx), which was the harder half. But the carousel still cycles allSchemeIds = scheme ? [scheme.id] : [] (SidePanel.tsx:94) — the selected day's single scheme, which is exactly what this item says is wrong. A one-element carousel is not a carousel. Still needs: cycle upcoming schemes (next week/month).
  • Static segment editorSettingsPage.tsx reads/writes static_config, seeding a default when a segment first becomes static
  • No-event default scheme control in Settings — default_scheme bound in SettingsPage.tsx
  • Dismissible review-workflow guide (§10.3) — ReviewGuideBanner.tsx
  • Import panel: file upload + browsable multi-preset list with per-preset event association and structured segment mapping (§3.6) — #98
  • Day swatches as gradient strips; month backdrop as a full-width bar (§10.1) — #96
  • Quick push: arbitrary color path, not just scheme ID (§6.1) — #97
  • EffectPicker: full WLED effect list — 113-entry static fallback, live fetch from /json/eff via useWledEffects, plus a Custom ID input for anything absent
  • Un-gate presets.json export from admin-only not started. export_presets_json still carries Depends(require_admin) (backend/app/routers/import_export.py:37), though the spec allows viewer GETs. Backend change too.

Remaining: 2 of 13

  1. Carousel over upcoming schemes — the larger of the two; needs a decision on the window (next week? next month? approved only?) and where it's triggered from, since "upcoming" isn't a property of the selected day.
  2. Un-gate the export endpoint — small: drop require_admin to require_viewer on GET /export/presets.json, and check whether /export/events/{id}.json should follow.
Split from #40 — the UI gaps against spec §§4.3, 10, 3.6, 5.4. Ordered roughly by user impact. > **Checkboxes re-audited against the code on 2026-09-01.** They had drifted badly: most of this had shipped without being ticked. Each box below was verified by reading the implementation, not by memory. - [x] **Regenerate confirmation modal** warning that the approved state will be replaced (§4.3) — `SidePanel.tsx:319`. Was already present; my earlier claim that it was missing matched on hook wiring rather than the button's `onClick`. - [x] **HSV color mode** alongside RGB in `ColorSlots.tsx` (§10.1/10.2) — `entryMode: "rgb" | "hsv"`, with `rgbToHsv`/`hsvToRgb` covered by `colorConversion.test.ts` - [x] **Conflict handling**: conflict status, ⚠ indicator, "conflicts" filter chip, day-cell icon semantics reconciled with spec (#95) - [x] **Audit log page** (admin) — `AuditLogPage.tsx`, routed in `App.tsx`; actor + action filtered server-side, date range client-side (the API takes no date params) - [ ] **Preview carousel** — ⚠️ **half done.** The stop control exists and is global (`Header.tsx`, `SidePanel.tsx`), which was the harder half. But the carousel still cycles **`allSchemeIds = scheme ? [scheme.id] : []`** (`SidePanel.tsx:94`) — the selected day's single scheme, which is exactly what this item says is wrong. A one-element carousel is not a carousel. Still needs: cycle *upcoming* schemes (next week/month). - [x] **Static segment editor** — `SettingsPage.tsx` reads/writes `static_config`, seeding a default when a segment first becomes `static` - [x] **No-event default scheme control** in Settings — `default_scheme` bound in `SettingsPage.tsx` - [x] **Dismissible review-workflow guide** (§10.3) — `ReviewGuideBanner.tsx` - [x] **Import panel**: file upload + browsable multi-preset list with per-preset event association and structured segment mapping (§3.6) — #98 - [x] **Day swatches as gradient strips**; month backdrop as a full-width bar (§10.1) — #96 - [x] **Quick push**: arbitrary color path, not just scheme ID (§6.1) — #97 - [x] **EffectPicker**: full WLED effect list — 113-entry static fallback, live fetch from `/json/eff` via `useWledEffects`, plus a Custom ID input for anything absent - [ ] **Un-gate presets.json export from admin-only** — ❌ **not started.** `export_presets_json` still carries `Depends(require_admin)` (`backend/app/routers/import_export.py:37`), though the spec allows viewer GETs. Backend change too. ## Remaining: 2 of 13 1. **Carousel over upcoming schemes** — the larger of the two; needs a decision on the window (next week? next month? approved only?) and where it's triggered from, since "upcoming" isn't a property of the selected day. 2. **Un-gate the export endpoint** — small: drop `require_admin` to `require_viewer` on `GET /export/presets.json`, and check whether `/export/events/{id}.json` should follow.
claude-bot added this to the v1.0.0 milestone 2026-07-15 15:33:11 +00:00
Author
Contributor

Picking this up in batches (it's large). Batch A on fix/48-frontend-parity-a: regenerate-confirm modal, no-event default_scheme Settings control (pairs with merged #44), dismissible review guide, and the audit-log page. Remaining items (HSV color mode, conflict handling, import panel, EffectPicker, gradient swatches, carousel, static-segment editor, quick-push color path, un-gate presets export) will follow in a Batch B — leaving this issue open until they're all done. Note: #44 changed the no-event push response action field to be mode-aware (no_event/default_dim_white/default_scheme).

Picking this up in batches (it's large). **Batch A** on `fix/48-frontend-parity-a`: regenerate-confirm modal, no-event `default_scheme` Settings control (pairs with merged #44), dismissible review guide, and the audit-log page. Remaining items (HSV color mode, conflict handling, import panel, EffectPicker, gradient swatches, carousel, static-segment editor, quick-push color path, un-gate presets export) will follow in a Batch B — leaving this issue open until they're all done. Note: #44 changed the no-event push response `action` field to be mode-aware (`no_event`/`default_dim_white`/`default_scheme`).
Author
Contributor

Batch B on fix/48-frontend-parity-b: HSV color mode in ColorSlots (§10.1/10.2), static-segment editor (§3.2), EffectPicker full WLED effect list (fetch /json/eff when connected, else complete static list), and exposing the new daily_backup_retain_count Settings field (from #57). Remaining for a Batch C: conflict handling, import-panel overhaul, gradient swatches/month backdrop, preview carousel, quick-push arbitrary-color path, un-gate presets export (needs a backend viewer-GET change too).

**Batch B** on `fix/48-frontend-parity-b`: HSV color mode in ColorSlots (§10.1/10.2), static-segment editor (§3.2), EffectPicker full WLED effect list (fetch `/json/eff` when connected, else complete static list), and exposing the new `daily_backup_retain_count` Settings field (from #57). Remaining for a Batch C: conflict handling, import-panel overhaul, gradient swatches/month backdrop, preview carousel, quick-push arbitrary-color path, un-gate presets export (needs a backend viewer-GET change too).
Author
Contributor

Batch B merged (PR #77, Docker-verified): HSV color mode, static-segment editor, corrected + live-fetchable EffectPicker, daily_backup_retain_count control.

Two backend follow-ups this surfaced (needed to fully light up Batch B's frontend, but not blocking it):

  • GET /api/v1/wled/effects proxy — proxy the controller's /json/eff so EffectPicker's live list works (frontend already calls it and degrades to the static list on 404).
  • static_config consumer in scheme_translator — the static-segment editor persists a sensible Segment.static_config shape, but nothing reads it yet.

Batch C (still open on this issue): conflict handling (§4.3/§10) — note this needs a conflict-status definition (likely a small backend change too); import-panel overhaul (§3.6); day-swatch gradient strips + month backdrop (§10.1); preview carousel; quick-push arbitrary-color path (§6.1); un-gate presets.json export for viewers (backend auth change too). These are lower-impact polish/larger items — good for a focused follow-up pass.

**Batch B merged** (PR #77, Docker-verified): HSV color mode, static-segment editor, corrected + live-fetchable EffectPicker, `daily_backup_retain_count` control. Two backend follow-ups this surfaced (needed to fully light up Batch B's frontend, but not blocking it): - [ ] **`GET /api/v1/wled/effects` proxy** — proxy the controller's `/json/eff` so EffectPicker's live list works (frontend already calls it and degrades to the static list on 404). - [ ] **`static_config` consumer** in `scheme_translator` — the static-segment editor persists a sensible `Segment.static_config` shape, but nothing reads it yet. **Batch C (still open on this issue):** conflict handling (§4.3/§10) — note this needs a conflict-status definition (likely a small backend change too); import-panel overhaul (§3.6); day-swatch gradient strips + month backdrop (§10.1); preview carousel; quick-push arbitrary-color path (§6.1); un-gate `presets.json` export for viewers (backend auth change too). These are lower-impact polish/larger items — good for a focused follow-up pass.
Author
Contributor

Batch C scope — audited against the code, not the original list

"Batch C" was never defined: this issue's body lists the whole parity gap from the 2026-07-15 review, and batches A and B were carved out ad hoc without recording what they closed. I read the current frontend against spec §10 to establish what actually remains.

Already closed by batches A/B — do not redo

Gap Evidence
HSV colour mode components/panel/color.tshsvToRgb/rgbToHsv, used by ColorSlots.tsx
Audit-log page pages/AuditLogPage.tsx exists and is routed
Dismissible review guide (§10.3) components/layout/ReviewGuideBanner.tsx
Save-as-named-scheme SchemeEditor.tsx — "Save as…" dialog
Promote Secondary SidePanel.tsx
Full WLED effect list components/panel/effects.ts
Static-segment editor in Settings pages/SettingsPage.tsx
No-event default scheme control SettingsPage.tsxDefaultSchemeMode = "off" | "dim_white" | "scheme"

That last one is worth noting: #40 listed it as missing, and it is now present. The original gap list is stale.

Batch C — what genuinely remains

1. Day-cell status icons mean the wrong things (DayCell.tsx:13-16)

approved: "✓", stale: "⚠", rejected: "✕", candidate: "⏳"

Spec §10.1: ✓ approved, ⏳ candidate, ✕ no scheme, ⚠ conflict. So and are both bound to the wrong concept. Cannot be fixed independently of item 2 — there is no conflict concept to bind to yet.

2. No conflict detection, and no "conflicts only" filter (Toolbar.tsx:23)

type Filter = "all" | "unapproved" | "stale" | "ai"

Spec wants unapproved | conflicts | AI-sourced | stale. conflicts is absent. The backend already resolves conflicts (priority_resolver.get_events_for_date returns primary + secondaries), so this is surfacing existing data, not new logic.

3. Day swatches are solid blocks, not gradients (DayCell.tsx:71-77)

const [r, g, b] = seg.colors[0] ?? [128,128,128]
style={{ backgroundColor: `rgb(${r},${g},${b})` }}

Spec: "one horizontal gradient strip per themed segment, blending that segment's colour slots (matches WLED's own preset thumbnails)". Only slot 0 is used; slots 1 and 2 are discarded. A linear-gradient across the segment's slots is the fix.

4. Month backdrop is a header tint, not a bar behind the row (YearCalendar.tsx:65-78)

The backdrop renders as backgroundColor: hsla(...,0.15) plus a left border on the month header div. Spec §10.1: "full-width coloured bar behind each month's row". The day grid itself has no backdrop.

5. Carousel cannot be stopped from the UI

stopCarousel exists in api/index.ts:141 but has no hook and no callerSidePanel imports useStartCarousel only. A user can start a carousel and has no way to end it short of waiting it out. Arguably the worst of these for real use.

6. Regenerate has no confirmation (SidePanel.tsx:211)

Fires regenerate.mutateAsync directly. Spec §4.3 requires a warning before replacing approved presets — this can silently destroy approved work.

7. Import is raw JSON paste (Toolbar.tsx:95-97, 223)

JSON.parse(importText) from a <textarea>, plus a second textarea for segment mapping. Spec §10.1 wants a browsable multi-preset panel with a mapping wizard.

8. Quick push takes a scheme ID only (Toolbar.tsx:135-137)

{ id: quickPushId.trim(), mins } — no arbitrary-colour path.

Suggested sequencing

Items 1 + 2 are one unit (icons are meaningless until conflicts exist). 3 + 4 are one visual unit in the calendar. 5 is small and high-value. 6 is small and prevents data loss. 7 is the largest and most self-contained. 8 is smallest.

If batch C needs trimming, 5 and 6 are the two that protect users from losing work or losing control of the controller, and I would keep those regardless.

## Batch C scope — audited against the code, not the original list "Batch C" was never defined: this issue's body lists the *whole* parity gap from the 2026-07-15 review, and batches A and B were carved out ad hoc without recording what they closed. I read the current frontend against spec §10 to establish what actually remains. ### Already closed by batches A/B — do not redo | Gap | Evidence | |---|---| | HSV colour mode | `components/panel/color.ts` — `hsvToRgb`/`rgbToHsv`, used by `ColorSlots.tsx` | | Audit-log page | `pages/AuditLogPage.tsx` exists and is routed | | Dismissible review guide (§10.3) | `components/layout/ReviewGuideBanner.tsx` | | Save-as-named-scheme | `SchemeEditor.tsx` — "Save as…" dialog | | Promote Secondary | `SidePanel.tsx` | | Full WLED effect list | `components/panel/effects.ts` | | Static-segment editor in Settings | `pages/SettingsPage.tsx` | | **No-event default scheme control** | `SettingsPage.tsx` — `DefaultSchemeMode = "off" \| "dim_white" \| "scheme"` | That last one is worth noting: #40 listed it as missing, and it is now present. The original gap list is stale. ### Batch C — what genuinely remains **1. Day-cell status icons mean the wrong things** (`DayCell.tsx:13-16`) ```ts approved: "✓", stale: "⚠", rejected: "✕", candidate: "⏳" ``` Spec §10.1: `✓ approved, ⏳ candidate, ✕ no scheme, ⚠ conflict`. So `✕` and `⚠` are both bound to the wrong concept. Cannot be fixed independently of item 2 — there is no conflict concept to bind `⚠` to yet. **2. No conflict detection, and no "conflicts only" filter** (`Toolbar.tsx:23`) ```ts type Filter = "all" | "unapproved" | "stale" | "ai" ``` Spec wants `unapproved | conflicts | AI-sourced | stale`. `conflicts` is absent. The backend already resolves conflicts (`priority_resolver.get_events_for_date` returns primary + secondaries), so this is surfacing existing data, not new logic. **3. Day swatches are solid blocks, not gradients** (`DayCell.tsx:71-77`) ```ts const [r, g, b] = seg.colors[0] ?? [128,128,128] style={{ backgroundColor: `rgb(${r},${g},${b})` }} ``` Spec: *"one horizontal gradient strip per themed segment, blending that segment's colour slots (matches WLED's own preset thumbnails)"*. Only slot 0 is used; slots 1 and 2 are discarded. A `linear-gradient` across the segment's slots is the fix. **4. Month backdrop is a header tint, not a bar behind the row** (`YearCalendar.tsx:65-78`) The backdrop renders as `backgroundColor: hsla(...,0.15)` plus a left border **on the month header div**. Spec §10.1: *"full-width coloured bar behind each month's row"*. The day grid itself has no backdrop. **5. Carousel cannot be stopped from the UI** `stopCarousel` exists in `api/index.ts:141` but has **no hook and no caller** — `SidePanel` imports `useStartCarousel` only. A user can start a carousel and has no way to end it short of waiting it out. Arguably the worst of these for real use. **6. Regenerate has no confirmation** (`SidePanel.tsx:211`) Fires `regenerate.mutateAsync` directly. Spec §4.3 requires a warning before replacing approved presets — this can silently destroy approved work. **7. Import is raw JSON paste** (`Toolbar.tsx:95-97, 223`) `JSON.parse(importText)` from a `<textarea>`, plus a second textarea for segment mapping. Spec §10.1 wants a browsable multi-preset panel with a mapping wizard. **8. Quick push takes a scheme ID only** (`Toolbar.tsx:135-137`) `{ id: quickPushId.trim(), mins }` — no arbitrary-colour path. ### Suggested sequencing Items **1 + 2** are one unit (icons are meaningless until conflicts exist). **3 + 4** are one visual unit in the calendar. **5** is small and high-value. **6** is small and prevents data loss. **7** is the largest and most self-contained. **8** is smallest. If batch C needs trimming, **5 and 6 are the two that protect users from losing work or losing control of the controller**, and I would keep those regardless.
Author
Contributor

Correction: item 6 is already done

Starting implementation, I checked SidePanel.tsx properly and the regenerate confirmation already exists. My audit was wrong.

SidePanel.tsx:307 renders:

<Dialog open={confirmRegen} onOpenChange={setConfirmRegen}>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Regenerate this scheme?</DialogTitle>
      <DialogDescription>
        This replaces the current scheme with a freshly generated one. If it is
        already approved, that approval is discarded and you'll need to review
        and approve the new version. This cannot be undone.
      </DialogDescription>

and the button is onClick={() => setConfirmRegen(true)}, not a direct mutation.

How I got it wrong: my grep matched regenerate.mutateAsync in the hook-wiring block near the top of the file and I inferred the button fired straight through, without reading the button's actual onClick. Spec §4.3 is satisfied, including the "approval is discarded" warning.

Batch C is therefore seven items, not eight. Item 6 is struck; the other seven stand as audited — I re-checked item 5 the same way and stopCarousel genuinely has no hook and no caller, so that one is real.

Renumbered:

  1. Day-cell status icons bound to wrong concepts
  2. No conflict detection / "conflicts only" filter
  3. Day swatches solid instead of gradient
  4. Month backdrop tints the header instead of sitting behind the row
  5. Carousel cannot be stopped from the UI ← starting here
  6. Regenerate confirmation — already implemented
  7. Import is raw JSON paste
  8. Quick push takes a scheme ID only
## Correction: item 6 is already done Starting implementation, I checked `SidePanel.tsx` properly and **the regenerate confirmation already exists**. My audit was wrong. `SidePanel.tsx:307` renders: ```tsx <Dialog open={confirmRegen} onOpenChange={setConfirmRegen}> <DialogContent> <DialogHeader> <DialogTitle>Regenerate this scheme?</DialogTitle> <DialogDescription> This replaces the current scheme with a freshly generated one. If it is already approved, that approval is discarded and you'll need to review and approve the new version. This cannot be undone. </DialogDescription> ``` and the button is `onClick={() => setConfirmRegen(true)}`, not a direct mutation. **How I got it wrong:** my grep matched `regenerate.mutateAsync` in the hook-wiring block near the top of the file and I inferred the button fired straight through, without reading the button's actual `onClick`. Spec §4.3 is satisfied, including the "approval is discarded" warning. **Batch C is therefore seven items, not eight.** Item 6 is struck; the other seven stand as audited — I re-checked item 5 the same way and `stopCarousel` genuinely has no hook and no caller, so that one is real. Renumbered: 1. Day-cell status icons bound to wrong concepts 2. No conflict detection / "conflicts only" filter 3. Day swatches solid instead of gradient 4. Month backdrop tints the header instead of sitting behind the row 5. **Carousel cannot be stopped from the UI** ← starting here 6. ~~Regenerate confirmation~~ — already implemented 7. Import is raw JSON paste 8. Quick push takes a scheme ID only
Author
Contributor

Batch C item 8 — quick-push arbitrary colour: done in #97.

The gap was real and slightly wider than the item described: the spec says quick push takes "an arbitrary color or scheme", but the API only had the scheme half — POST /wled/quick-push required a scheme_id and looked up a Scheme row. So this needed a backend path, not just a UI control.

What landed:

  • QuickPushRequest takes exactly one of scheme_id or color, with a validator rejecting both-or-neither instead of letting one silently win.
  • New scheme_translator.solid_color_state; dim_white_state now delegates to it, since warm-white ambient was already the same operation with a fixed colour.
  • Both push kinds funnel into one _start in quick_push_manager, so the revert paths (duration timer, scheduler revert, manual cancel, superseding) are identical either way.
  • Toolbar dialog gains a Scheme / Colour tab pair. Push stays disabled until the hex field is valid — hexToRgb would otherwise turn a typo into black.

Nothing is written to the scheme library on the colour path, so an ad-hoc push leaves no residue.


Batch C status: 7 of 8 items complete. (Recall item 6 was withdrawn — regenerate confirmation already existed at SidePanel.tsx:307; my original audit matched hook wiring rather than the button's onClick.)

Remaining: item 7, the browsable multi-preset import panel — currently a raw JSON textarea. That is the largest of the batch and I want to scope it against the spec before building rather than inventing a UI.

**Batch C item 8 — quick-push arbitrary colour: done in #97.** The gap was real and slightly wider than the item described: the spec says quick push takes *"an arbitrary color or scheme"*, but the API only had the scheme half — `POST /wled/quick-push` required a `scheme_id` and looked up a `Scheme` row. So this needed a backend path, not just a UI control. What landed: - `QuickPushRequest` takes exactly one of `scheme_id` or `color`, with a validator rejecting both-or-neither instead of letting one silently win. - New `scheme_translator.solid_color_state`; `dim_white_state` now delegates to it, since warm-white ambient was already the same operation with a fixed colour. - Both push kinds funnel into one `_start` in `quick_push_manager`, so the revert paths (duration timer, scheduler revert, manual cancel, superseding) are identical either way. - Toolbar dialog gains a Scheme / Colour tab pair. Push stays disabled until the hex field is valid — `hexToRgb` would otherwise turn a typo into black. Nothing is written to the scheme library on the colour path, so an ad-hoc push leaves no residue. --- **Batch C status: 7 of 8 items complete.** (Recall item 6 was withdrawn — regenerate confirmation already existed at `SidePanel.tsx:307`; my original audit matched hook wiring rather than the button's `onClick`.) Remaining: **item 7**, the browsable multi-preset import panel — currently a raw JSON textarea. That is the largest of the batch and I want to scope it against the spec before building rather than inventing a UI.
Author
Contributor

All 13 items are done — closing.

The last two landed today:

  • Un-gate presets.json export (#100) — both export GETs moved to require_viewer, matching the spec's "GET endpoints allow viewer unless noted otherwise". The API change alone would have been useless: the Export button sat inside {isAdmin && …}, so viewers still couldn't reach it. Moved out, and pinned with two tests in the auth matrix.
  • Preview carousel over upcoming schemes (#103) — it was cycling [scheme.id], the selected day's single scheme, which duplicated the Preview button beside it. Starting one moved to the toolbar with a 7/30/90-day window; it walks the resolved day map so it previews what will actually run (no-event days contribute nothing, a multi-day event appears once). Viewer-accessible, since both carousel endpoints are require_viewer.

For the record, the checkbox audit on 2026-09-01 was the useful part of this issue's tail: 11 of 13 items had already shipped without being ticked, and two that read as open (regenerate confirm, HSV mode) had been done for weeks. Only the carousel and the export gate were genuinely outstanding. Worth remembering that a stale checklist misrepresents remaining work in both directions — it inflated the apparent scope here while hiding that the carousel was half-finished rather than done.

**All 13 items are done — closing.** The last two landed today: - **Un-gate presets.json export** (#100) — both export GETs moved to `require_viewer`, matching the spec's "GET endpoints allow `viewer` unless noted otherwise". The API change alone would have been useless: the Export button sat inside `{isAdmin && …}`, so viewers still couldn't reach it. Moved out, and pinned with two tests in the auth matrix. - **Preview carousel over upcoming schemes** (#103) — it was cycling `[scheme.id]`, the selected day's single scheme, which duplicated the Preview button beside it. Starting one moved to the toolbar with a 7/30/90-day window; it walks the resolved day map so it previews what will actually run (no-event days contribute nothing, a multi-day event appears once). Viewer-accessible, since both carousel endpoints are `require_viewer`. For the record, the checkbox audit on 2026-09-01 was the useful part of this issue's tail: **11 of 13 items had already shipped without being ticked**, and two that read as open (regenerate confirm, HSV mode) had been done for weeks. Only the carousel and the export gate were genuinely outstanding. Worth remembering that a stale checklist misrepresents remaining work in both directions — it inflated the apparent scope here while hiding that the carousel was half-finished rather than done.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Iris-WLED#48
No description provided.