# Sprint Retrospective **Sprint:** 10 **Date:** 2026-10-27 **Facilitated by:** `scrum-master`, per `process/05_sprint_retrospective.md` **Inputs used:** `backlog/sprints/sprint-10.md`, `backlog/backlog.md` (Sprint 10 refinement, planning, and Review outcome notes), `backlog/epics/02_template_designer_gui_foundation.md`, `backlog/epics/04_live_preview_and_record_navigation.md`, `backlog/epics/06_layout_efficiency_and_operator_tooling.md`, `logs/technical_debt_log.md`, `logs/impediment_log.md`, `logs/process_improvement_log.md`, `backlog/sprints/sprint-9-retrospective.md` (for follow-through check). This session had direct shell/build access throughout and ran `dotnet test` itself at every checkpoint. ## Signals **Objective:** - 3/3 committed stories Done (13/13 points), sprint goal assessed "met in full" at Sprint Review (`backlog/backlog.md`, Sprint 10 Review outcome). - Test suite grew from 364/364 (Desktop.Tests, Sprint 9 close) to 388/388 across the sprint's three batches (+12 wrap/clip indicator tests, +12 multi-select tests, +12 alignment/distribution tests); CLI.Tests unaffected throughout at 124/124. - Two ad-hoc, non-committed items handled mid-sprint on live human product-owner feedback: (1) mid-Batch-1, adding Delete/Backspace support and correcting the resize handle to width-only; (2) between Batch 1 and Batch 2, a canvas click-smoothness regression, root-caused and fixed with disclosed before/after timing evidence (~50ms to 0.3ms per click). - Zero new impediments; the two pre-existing open ones (asset path strategy, UNC timeout/retry) remain non-blocking and untouched. - Two new technical debt items logged this sprint (both Low/Open/non-blocking): the pre-existing Address-Control-interaction-code testability gap grew further (now three features built on the untested WinForms-only side: rotation, multi-select, align/distribute); a pre-existing, unrelated GDI+ selection-highlight measurement quirk was found and disclosed rather than fixed in-scope. - Live verification spanned reflection-driven built-form harnesses with real screenshots for every GUI-touching item this sprint, including a **fresh** (not reused) full-form screenshot for Batch 3 per its own explicit AC. **Subjective:** - The human product owner actively used the live built app between batches and surfaced two real issues immediately, both handled the same session rather than being logged for a later sprint — a fast, effective feedback loop, though it means the sprint's actual path diverged twice from its planned batch sequence. - The additive-layer design for multi-select (decided at story-writing time: extend `CanvasElementEditor`/`TemplateCanvasControl`'s selection state rather than replace it) paid off exactly as intended — every pre-existing single-select code path needed zero changes, confirmed by the full test suite and live smoke passing unmodified. - Architecture reuse compounded well across the sprint: Batch 2's rotation-aware world-space AABB helper (built for rubber-band selection) was exposed and reused directly for Batch 3's alignment/distribution bounding boxes, rather than a second implementation. - When the click-smoothness regression was reported, the team went straight to direct temporary instrumentation (`Stopwatch` timers inserted into the suspect method, removed after) rather than reasoning from assumption — this disproved an initially plausible but wrong hypothesis (the new wrap/clip indicator's paint cost) with hard numbers before landing on the real cause. ## What Went Well - **Multi-select shipped as a genuinely additive layer, verified, not just designed that way on paper.** The story's own conversation notes called for this explicitly; the full test suite (388/388) and live smoke both confirm every existing single-select interaction — properties panel, resize/rotate handles, address-line drill-in — works completely unchanged whenever 0 or 1 items are selected. - **Root-cause-first debugging for the ad-hoc smoothness regression.** Rather than guessing or re-applying the Sprint 8 fix's exact shape reflexively, the team measured first, found the initial hypothesis wrong, and kept measuring until the actual cause (an unconditional `SelectionChanged` raise) was confirmed with before/after numbers. - **Consistent, compounding architecture reuse.** The same rotation-aware bounding-box math served rubber-band selection (Batch 2) and alignment/distribution (Batch 3) without a second implementation — a direct payoff of exposing `CanvasElementEditor.GetWorldBounds` publicly rather than keeping it a private implementation detail of one feature. - **Honest technical-debt logging continued under pressure to just move on.** Two new gaps found during this sprint's own live verification — one architectural (Address-Control testability), one cosmetic (GDI+ measurement) — were logged with enough detail to act on later, rather than silently ignored or scope-crept into an unplanned fix. ## What Didn't Go Well - **The click-smoothness regression was, in shape, a repeat of a bug this team had already found and fixed once.** Sprint 8 found and fixed "an expensive full-refresh runs on every tick of a hot mouse event" for `ElementsChanged`/mouse-move during a drag. This sprint's own `SelectionChanged`/mouse-down handler had the exact same shape of defect — an unconditional expensive refresh on a hot UI event — sitting in the very same file, and it wasn't caught by this team's own verification practices; it took the human product owner noticing a real hitch during actual use, two sprints after the sibling bug was fixed, before it was found. The fix itself was fast and well-verified once reported, but the team's own review/DoD process had no mechanism that would have caught this proactively by generalizing from the earlier, structurally identical fix. ## Patterns / Insights (Prioritized) 1. **(Team-level, real and actionable)** When a performance bug is fixed because a UI event handler runs expensive work unconditionally on a hot event, check every *sibling* event handler on the same control for the identical shape of defect, not just re-verify the one specific gesture that was reported. Sprint 8 fixed this for `OnMouseMove`/`ElementsChanged`; the identical defect in `OnMouseDown`/`SelectionChanged` on the same control went unnoticed for two sprints until a human caught it live. 2. **(Team-level, real and actionable)** Direct temporary instrumentation (inline `Stopwatch` timers in the suspect method, removed once root-caused) is a fast, effective technique for a reported perf/smoothness regression — proved out this sprint after an initial plausible-sounding hypothesis was measured and disproved. Worth reaching for this *before* forming a hypothesis on the next such report, not only as a fallback once a first guess fails. 3. **(Team-level, minor, tooling)** A reflection-driven mouse-event harness that clicks exactly on an element's boundary corner (rather than a few points inside its box) can produce a false negative: pixel-truncation in the round-trip through integer mouse coordinates can land a fraction of a point outside a `>=`-style hit-test boundary. Worth a standing habit for future harness-writing: click safely inside an element's box, not on its exact edge. ## Action Items (Add These To Sprint 11's Plan) - [ ] When fixing an expensive-refresh-on-a-hot-UI-event bug on any canvas control, audit every sibling event handler on that same control for the identical anti-pattern before considering the fix complete — owner: dev-team — due: ongoing, and explicitly re-checked the next time any `TemplateCanvasControl` mouse/key handler is touched. - [ ] When investigating a reported perf/smoothness regression, reach for direct temporary instrumentation (inline timers in the suspect code path) as a first step, not a fallback after a hypothesis fails — owner: dev-team — due: ongoing. - [ ] In reflection-driven mouse-event harnesses, click a few points inside an element's bounding box rather than exactly on its edge/corner, to avoid false negatives from pixel-truncation round-tripping — owner: dev-team — due: ongoing. - [ ] Escalate rather than defer again: actually extract `TemplateCanvasControl`'s Address-Control-specific hit-test/drag/resize/multi-select/align state and math into a new `AddressControlEditor` (Desktop.Core), mirroring `CanvasElementEditor`'s role for standalone elements. This has now been logged and consciously deferred across three features (rotation, multi-select, align/distribute) — recommend product-owner actually size and schedule this as its own story next time capacity allows, rather than continuing to note it and move on — owner: product-owner (to schedule) / dev-team (to size and build) — due: Sprint 11 backlog refinement, as a real candidate, not just a re-logged note. ## Deferred / Lower-Priority Ideas - "Add an adjustable width and height with text wrapping to Address Control lines" (epic 4, 8 pts) remains the natural next epic-4 pull, deferred from Sprint 10 for exactly this reason. - "Warn on text overflow before render" (epic 4, provisional 5 pts) remains blocked on the Address Control wrap/clip story above landing first. - "Undo and redo layout changes" (epic 6, 13 pts) remains this backlog's highest-uncertainty estimate and the last story in epic 6 — a reasonable Sprint 11 candidate on its own, but not to be paired with another large/novel story per this team's established caution. - "Complete the first text-only operator workflow" (epic 1, 5 pts) remains flagged as a stale label needing re-verification before ever being pulled blind — carried forward unchanged for several sprints now. ## Follow-Up On Previous Retro's Actions All four of Sprint 9's retrospective action items, checked against `backlog/sprints/sprint-10.md` and `backlog/backlog.md`'s Sprint 10 refinement/planning/review outcomes: 1. **Treat a story's GUI-layer verification as equally load-bearing as any other check, not optional once a different check "feels sufficient"** — Applied. Every GUI-touching item this sprint (the indicator, the delete/resize-handle correction, multi-select, align/distribute) got a real reflection-driven built-form smoke with a genuine screenshot; Batch 3 specifically captured a **fresh** screenshot rather than reusing Batch 2's, exactly matching this discipline's intent. 2. **Re-confirm/re-size "Support rotated wrapped text and a live wrap/clip indicator for text elements" before it is committed** — Applied, exactly as recommended, during Sprint 10 backlog refinement: renamed, re-sized from 8 to 3 points, confirmed via real code inspection and a licensed end-to-end test that rotation composition was already delivered as a Sprint 9 byproduct. 3. **Apply the built-form-vs-canvas-only smoke rule per story when epic 6's multi-select/align pair is finally pulled** — Applied, its trigger having finally occurred after being carried forward unchanged for two prior sprints. Batch 2 (multi-select, touches the properties panel) and Batch 3 (align/distribute, adds a new toolbar) each got their own full built-form smoke, with Batch 3's screenshot freshly captured rather than reused — no drop, and the multi-sprint carry finally resolved. 4. **Evaluate extracting the pre-existing Sprint 6 Address Control interaction logic to a `CanvasElementEditor`-equivalent before/alongside the next story that deepens Address Control interaction** — Triggered twice this sprint (both multi-select and align/distribute deepened Address Control interaction) but the actual extraction was, again, consciously deferred rather than done — correctly logged and disclosed both times rather than silently dropped, but this is now the second sprint in a row this exact trigger has fired without the refactor happening. Escalated above into its own concrete Sprint 11 action item rather than carried forward as a passive note a third time. Three items show clean follow-through; one (item 4) is honestly flagged as acknowledged-but-deferred for a second consecutive occurrence, escalated rather than quietly re-carried — no silent drops. ## Kit-Level Decision No kit-level edit proposed this sprint. Both new insights (auditing sibling event handlers for a known anti-pattern; instrumentation-first perf debugging) are team coding/debugging practices, not gaps in the Scrum-kit process, templates, or agent files themselves — logged above as team action items rather than routed to `logs/process_improvement_log.md`. The Sprint 9 retrospective's own **Proposed** license-key-search-depth entry there remains awaiting a human product-owner decision on wording/placement; not revisited this sprint since nothing new changed its status. ## Anti-Patterns Checked - **No follow-through on prior retro actions:** Ruled out; see "Follow-Up" above — three items show clean follow-through, one is honestly escalated rather than silently re-carried a third time. - **Blame-focused discussion:** Ruled out; the click-hitch regression and the recurring Address-Control-testability gap are both described as process/practice gaps to generalize a fix from, not attributed to a person or session. - **Hidden mini-waterfall within the sprint:** Ruled out; each of the three batches was designed, implemented, tested, documented, and live-verified within its own pass, consistent with every prior sprint's practice. - **Avoiding an obvious known problem:** Ruled out; the click-smoothness regression was investigated to a confirmed root cause and fixed with disclosed before/after evidence, not patched around or left as a caveat. - **Status-theater:** Ruled out; every claim this sprint is backed by a specific, checkable number or artifact — exact test counts at each batch boundary, a specific before/after timing measurement for the smoothness fix, and real screenshots for every GUI-touching change. - **Review rubber-stamping (Sprint 3-named, resolved Sprint 4, checked at every sprint since):** Ruled out. Sprint 10's review independently re-ran the full test suite and read the actual `CanvasElementEditor`/`TemplateCanvasControl`/`AlignmentCalculator` code directly, and explicitly assessed both ad-hoc items' scoping rather than accepting dev-team's framing at face value.