Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

19KB

Sprint Backlog

Sprint: 8 Dates: 2026-10-26 - 2026-10-30 Sprint Goal: Ship whole-Address-Control rotation end-to-end — settable via the properties panel and adjustable by dragging a canvas handle — staying record-stable and visually consistent across the editing canvas, the Sprint 7 live preview, and the real CLI-rendered PDF, directly delivering the human product owner's reversal of the Sprint 6 Review's “whole-control rotation remains out of scope” call.

Committed Items

Story Size Status Tasks
Rotate the whole Address Control as a single unit 8 points Done - [x] Enable and wire the properties panel's existing -360.0/360.0, 0.1-precision angle input for a selected Address Control (currently disabled for that selection)
- [x] Add a point-rotation-around-a-pivot helper to EnvelopeRenderer.Cli's RenderEngine (no equivalent exists there today) and reuse the same formula on the desktop side (extracted to a new shared PointRotation helper, since CanvasElementEditor.RotatePointAroundPivot was private) so each visible line's anchor is rotated around the control's authored, unrotated box center before being handed to the existing per-line draw call — no DebenuPdfRenderer changes needed
- [x] Apply the same rigid-group rotation to TemplateCanvasControl.DrawAddressControl (box, every line's text, and any per-line highlight/selection overlay together, via a GDI+ transform around the box center) and to TemplatePreviewBuilder's address-control counterpart
- [x] Port a rotated-rectangle test to TemplateCanvasControl.HitTestAddressControl (and HitTestAddressResizeHandle, plus the resize-drag math itself) so a rotated control stays correctly click-selectable/resizable at its rotated position
- [x] Confirm the pivot is always computed from the control's authored X/Y/Width/Height, never from any record's AddressLineCollapser-shifted line positions — regression tests with two records (one collapsing a blank line, one not) assert an identical pivot, both CLI and desktop
- [x] Persist a new angle attribute on <addressControl> (default 0) in TemplateLayoutXmlSerializer/TemplateXmlParser; confirmed pre-existing templates (no attribute) render unchanged
- [x] Unit tests: rigid rotation math (CLI and desktop), collapse/pivot-stability regression, persistence round-trip (hit-test itself lives in the WinForms Views project and is covered by the live smoke below, not a unit test — see new technical-debt log entry)
- [x] Live actual built-form smoke showing a rotated Address Control matching across the canvas, the live preview, and a real CLI render of the same template
Rotate the whole Address Control by dragging a handle on the canvas 5 points Done - [x] Add a control-specific handle-position/hit-test/rotate-drag path for the Address Control, paralleling (not reusing as-is) CanvasElementEditor's existing TextElementLayout-only handle methods — implemented as a new framework-free AddressControlRotateHandle class (Desktop.Core) so it is unit tested, computed around the control's box center established by the prior story
- [x] Paint the rotate-handle glyph for a selected Address Control in TemplateCanvasControl (visual style consistent with the standalone-element handle — same SeaGreen dot/dashed line/white outline, same HandleOffsetPoints/HandleHitRadiusPoints constants reused for consistency)
- [x] Keep the properties panel's numeric angle field and the canvas handle synchronized in both directions (drag updates the number live; typing a number moves the handle) — free from Batch 1's existing wiring, confirmed live
- [x] Confirm dragging the handle persists the resulting angle the same way a typed value does, and does not disturb line-drill-in editing or the control's existing whole-unit move/resize behavior
- [x] Unit tests for handle position/hit-test/drag-angle math (AddressControlRotateHandleTests)
- [x] Live actual built-form smoke of a real handle drag rotating the control, synced with the properties panel, in the built .exe — fresh screenshots (not reused from Batch 1), per the Sprint 7 retrospective carry-in

Notes

  • Capacity signal: completed totals across Sprints 1-7 are 20, 19, 18, 18, 15, 18, 18 — seven data points, a stable 18-20 point range. Sprint 5's 15 was a deliberate, reasoned under-commit around one large/risky story, not a capacity miss.
  • The real capacity decision, reasoned through explicitly, not defaulted: the only Ready work beyond this sprint's 13-point commitment is epic 6's multi-select (“Select multiple elements at once on the canvas,” 5 pts) + align/distribute (“Align and distribute multiple elements,” 5 pts) pair, which product-owner's own recommendation (and this team's established practice — see Sprint 4's rotation chain, Sprint 7's preview pair) says must be pulled whole or not at all, never split across sprints. That leaves exactly two options: 13 points alone (a new low, below even Sprint 5's deliberate 15) or 23 points (13 + 10, a new high this team has never attempted — its best sprint to date is 20, itself flagged at the time as “a ceiling-level coincidence to watch, not a target to hit”). A middle option (splitting either pair) was ruled out per the reasoning above, not overlooked.
    • Why 23 was not chosen, even though it would use proven-idle capacity: the rotation pair and the multi-select/align pair are not formally dependent on each other, but they are not independent either — both land real changes in the same small set of GUI files (CanvasElementEditor, TemplateCanvasControl, TemplateDesignerForm). Concretely: this sprint's drag-handle story adds a new, Address-Control-specific handle/hit-test/rotate-drag path in CanvasElementEditor, while the multi-select story reworks CanvasElementEditor.Selected from a single reference into a genuinely new multi-item selection model (confirmed by code inspection at 2026-10-16 refinement: no multi-select concept exists anywhere today) — a foundational, first-of-its-kind interaction rework in the exact same class, with the exact same character of risk this team has consistently avoided stacking alongside another large/novel story in one sprint (the same reasoning “Undo and redo layout changes”‘s own sizing note gives for staying solo). Sequencing both in the same sprint, even in separate batches, risks one story's in-progress selection/handle-model change complicating the other's, beyond what the point totals alone would suggest. No external date or contractual pressure was cited anywhere in this backlog to justify reaching for a new, unproven high purely to keep capacity from going idle.
    • Checked for a smaller, independent item to round out capacity instead of jumping to 23 — found none that cleanly fits: “Complete the first text-only operator workflow” (epics/01_end_to_end_text_rendering_slice.md, 5 pts, labeled Ready) is a placeholder from the very first, 2026-09-04 onboarding refinement, never re-verified since — the same kind of staleness that made epic 4's “Ready” label wrong at the 2026-10-16 refinement (it predates rotation, mixed content, the Address Control, and the live preview, all of which likely already satisfy this story's acceptance criteria as a byproduct). Pulling it without a real re-verification pass would repeat that exact mistake rather than learn from it, so it is left out and flagged below for product-owner attention rather than blindly committed. “Undo and redo layout changes” (13 pts) is both too large to be a “rounding out” item and its own sizing note already recommends against pairing it with another large/novel story. Nothing else in the backlog is currently Ready.
    • Decision: commit 13 points — the rotation pair alone — as a deliberate, reasoned under-commit, the same posture Sprint 5 took around its own largest/riskiest story. This is not a capacity miss; it is protecting sprint quality against a concrete, named interference risk rather than chasing a number.
  • Epic reorder confirmed (my own decision, not just inherited from product-owner): epic 8 (Composite Address Controls and Mixed-Content Text) stays ahead of epic 6 (Layout Efficiency and Operator Tooling) in backlog/backlog.md's order. Reasoning: it is a direct, recent, explicit human-product-owner request reversing a scope call the same human product owner made only one sprint ago (Sprint 6 Review), and it is exactly what this sprint commits to — keeping the backlog's stated order and the sprint's actual commitment consistent, rather than committing one thing while the backlog implies another is more urgent.
  • Recommended for Sprint 9: the multi-select/align pair (10 pts, epic 6) as its own clean, undisturbed sprint slice — deliberately not sharing a sprint with another novel GUI-selection/canvas rework, per the reasoning above. “Undo and redo layout changes” (13 pts, epic 6) remains available whenever it can be paired appropriately; its own sizing note still recommends against pairing it with another large/novel story, so it is not a natural fit alongside the multi-select/align pair either — a future planning session should weigh that explicitly rather than default to bundling it in.
  • Backlog-hygiene flag for the next backlog refinement (not decided here): “Complete the first text-only operator workflow” (epic 1, 5 pts) has carried a stale “Ready” label since 2026-09-04 onboarding, untouched through seven sprints of real feature work that plausibly already satisfies its acceptance criteria. Recommend product-owner re-verify (or close outright) at the next backlog refinement rather than let it sit indefinitely.
  • Impediments: template asset path strategy (absolute vs. relative) and UNC timeout/retry behavior remain open in logs/impediment_log.md. Neither blocks this sprint's committed items.
  • Carried over from previous sprint: none (Sprint 7 completed all 3 committed stories, 18/18 points).
  • Not committed: “Select multiple elements at once on the canvas” + “Align and distribute multiple elements” (10 pts combined, epic 6 — clean whole-pair pull for Sprint 9, per the reasoning above); “Undo and redo layout changes” (13 pts, epic 6 — feasibility-confirmed 2026-10-19, still not recommended for pairing with another large/novel story); “Complete the first text-only operator workflow” (5 pts, epic 1 — stale label, needs product-owner re-verification before being pulled, see flag above); “Warn on text overflow before render” (epic 4) remains Not Ready, blocked on an open product question for the human product owner.

Execution Order

Sequenced by feature dependency (only two committed stories this sprint, hard-dependent on each other).

Batch Story Why it's gated here
1 Rotate the whole Address Control as a single unit No dependency on anything else. Delivers the underlying RotationAngle property, persistence, and rigid-group rotation/pivot math that the drag-handle story below needs.
2 Rotate the whole Address Control by dragging a handle on the canvas Strictly depends on Batch 1's angle property, persistence, and render/pivot logic — adds only the interactive handle and its live drag behavior on top.

Daily Scrum Log

Day Date Completed Planned Blocked/At risk
1 2026-10-26 Batch 1 (“Rotate the whole Address Control as a single unit”, 8 points) done, all 6 ACs met. Added AddressControlLayout.RotationAngle/BoxCenter (desktop) and TemplateAddressControl.RotationAngle/Height/BoxCenter (CLI) — independently-implemented parity, per this project's established CLI/desktop split — confirming by direct inspection that both Height (line font sizes + LineSpacingMultiplier) and Width (author-set) never depend on any record's resolved text, so BoxCenter is a pure function of authored geometry alone. Added RenderEngine.BuildAddressControlDraws’ own rotate-point-around-pivot helper (no equivalent existed in EnvelopeRenderer.Cli before this story) and a new shared EnvelopeRenderer.Desktop.Core.Design.PointRotation helper on the desktop side (extracted since CanvasElementEditor.RotatePointAroundPivot was private to that class) reused by TemplatePreviewBuilder and TemplateCanvasControl. Each visible line's per-record (collapse-adjusted) anchor is rotated as a rigid group around the fixed BoxCenter pivot, then handed to the existing fixed-pivot TextDraw/PreviewTextDraw path exactly as a rotated dynamic/mixed standalone element already does — confirmed zero DebenuPdfRenderer changes were needed. TemplateCanvasControl.DrawAddressControl now wraps its whole unrotated paint call (box, lines, highlight fills, selection border, and the pre-existing resize handle) in one GDI+ rotation transform around BoxCenter, so everything rotates together automatically; HitTestAddressControl and HitTestAddressResizeHandle (plus the resize-drag math itself, a correctness fix beyond the story's literal AC list, found while implementing the rotated hit test) now rotate the click point backward into local space before testing, so a rotated control stays correctly click-selectable and resizable at its real on-screen position. Persistence: new optional angle attribute on <addressControl> (default 0) in both TemplateLayoutXmlSerializer and TemplateXmlParser, following the exact convention <text>'s own angle already uses; TEMPLATE_FORMAT.md updated. Tests: 20 new (6 CLI — RenderEngineTests/TemplateXmlParserTests — and 14 desktop — AddressControlLayoutTests, new PointRotationTests, TemplateLayoutXmlSerializerTests, TemplatePreviewBuilderTests), including explicit pivot-stability regression tests (two records, one collapsing a blank line, one not, asserting an identical rotated position for the unaffected first line) on both CLI and desktop. Full suite 402/402 -> 422/422 (301->315 desktop, 101->107 CLI). Live verification: real CLI render of a 20-degree-rotated copy of sample-envelope-template3.xml against the real 392-record sample CSV (exit 0, all 392 pages) — confirmed via direct PDF content-stream inspection that the rotated render emits real cos(20)/sin(20) rotation matrices with distinct per-line translations (proving each line rotated individually as part of one rigid group), while the same template with no angle attribute produces zero rotation matrices at all (proving the zero-angle path is untouched). Built the desktop .exe and drove the real TemplateDesignerForm/TemplateCanvasControl/TemplatePreviewControl via a reflection-driven harness (same technique as Sprint 7): loaded the rotated template, screenshotted canvas + preview + full form showing the identical 20-degree rotation on both surfaces; simulated real mouse clicks through the actual OnMouseDown/OnMouseUp handlers proving the rotated control is selectable at its real (forward-rotated) position and correctly NOT selectable at its old un-rotated position; typed 65 into the real properties-panel _angleInput control and confirmed the canvas/preview both live-updated to the new angle; saved and reopened the template, confirming the panel-driven angle round-tripped through XML. New technical debt logged (Low impact): the pre-existing (Sprint 6) Address Control hit-test/move/resize logic still lives directly in the WinForms TemplateCanvasControl rather than a CanvasElementEditor-equivalent, so it remains smoke-tested only, not unit tested — this sprint's new rotation math was deliberately extracted to unit-testable Desktop.Core classes, but retroactively refactoring the older code was out of scope. Begin Batch 2 (“Rotate the whole Address Control by dragging a handle on the canvas”), which depends on this batch's angle property, persistence, and render/pivot logic. None.
1 2026-10-26 Batch 2 (“Rotate the whole Address Control by dragging a handle on the canvas”, 5 points) done, all 5 ACs met, bringing Sprint 8 to 13/13 points Done. Added EnvelopeRenderer.Desktop.Core.Design.AddressControlRotateHandle — a new, framework-free, unit-tested class (mirroring CanvasElementEditor's per-element handle math but implemented fresh against the control's own box geometry, since no equivalent existed for a selected Address Control) providing LocalPosition/WorldPosition/HitTest/RotateDragTo, reusing CanvasElementEditor.HandleOffsetPoints/HandleHitRadiusPoints directly for exact visual/hit-test consistency with the standalone-element handle (the story's own recommended default). TemplateCanvasControl paints the handle (same SeaGreen-dot/dashed-line/white-outline style as DrawRotateHandle) inside the same rotation transform DrawAddressControlUnrotated already runs under, so it automatically orbits with the box; a new _isRotatingAddressControl drag-gesture flag parallels the existing _isResizingAddressControl one in OnMouseDown/OnMouseMove/OnMouseUp. The properties panel's angle field and the canvas handle were already bidirectionally wired by Batch 1's RefreshPropertiesPanel/SetSelectedRotationAngle plumbing, so no new sync code was needed — confirmed live rather than assumed. Tests: 11 new (AddressControlRotateHandleTests — local/world position, hit-test including a rotated-vs-unrotated-position discrimination case, rotate-drag-to-angle math, pivot-exactly-under-pointer no-op, and a bidirectional angle-drives-position check). Full suite 422/422 -> 433/433 (315->326 desktop, 107 CLI unchanged). Live verification: built the desktop .exe and, via the same reflection-driven harness, added a fresh Address Control interactively (AddAddressControl(), not loaded from XML), screenshotted it unrotated with both handles visible; grabbed the real rotate handle via a simulated OnMouseDown at its actual computed world position, dragged mid-gesture to a point 90 degrees around the pivot (screenshotted before mouse-up, confirming ~89.4 degrees live in both the model and the real _angleInput control — small deviation from the harness's pixel-rounding, not a model precision issue) and confirmed the drilled-in address line index (1) was unchanged mid-drag; released the drag and confirmed the angle persisted; typed -45 into the real Angle field afterward and confirmed the handle/box moved to match; confirmed the control's existing whole-unit move still worked correctly afterward (X+20/Y-20 within the same pixel-rounding tolerance). Took a fresh full-form screenshot for this batch (not reused from Batch 1), per the Sprint 7 retrospective carry-in explicitly reinforced in this sprint's own plan. One test-design note, not a product defect: a later step in the same harness run clicked the control's body to test whole-unit move, which (correctly, by pre-existing Sprint 6 design) re-picked the drilled-in line under that click point — unrelated to the rotate-drag itself, which the mid-drag check already separately confirmed does not disturb line selection. Sprint 8 complete — move to product-owner Sprint Review. None.

Powered by TurnKey Linux.