Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

18KB

Sprint Backlog

Sprint: 5 Dates: 2026-10-05 - 2026-10-09 Sprint Goal: Let operators mix literal text with one or more CSV fields within a single text element (replacing today's static-text-or-single-column constraint across the whole designer, with existing templates unaffected), and settle the production configuration delivery question for CLI runtime settings.

Committed Items

Story Size Status Tasks
Mix static text and CSV fields within a single text element 13 points Done - [x] Design the run-sequence content model (ordered literal-text and field-token runs) replacing TextElementLayout's exclusive StaticText/ColumnName, and the equivalent TemplateElement change in the CLI, with the single-run case behaving exactly as today
- [x] Decide and implement the properties-panel editing affordance for composing mixed content (working assumption per the sizing note: a {Column Name} bracket-typing convention parsed into runs on commit) — the first UI to edit static text content at all, since none exists today
- [x] Design and implement backward-compatible XML persistence on both sides (TemplateLayoutXmlSerializer desktop, TemplateXmlParser CLI): legacy single-run templates round-trip byte-for-byte unchanged; new multi-run content persists in a new shape, documented in TEMPLATE_FORMAT.md
- [x] Update RenderEngine/DebenuPdfRenderer/TextDraw to resolve and concatenate a run sequence per record into the single string the existing draw call already expects, and extend the “unmapped column fails before any page renders” pre-flight check to run per token instead of per element
- [x] Update TemplateCanvasControl to visually distinguish field-token portions from literal text within the same element via per-run highlight segments
- [x] Update AddressBlockPreviewCalculator/canvas preview to resolve and concatenate runs against the loaded sample record, keeping preview and final render in agreement
- [x] Unit tests: run-sequence model, mixed-content editing/parsing, backward-compatible persistence round-trip against previously-saved template files, per-token unmapped-column failure, canvas highlight segmentation
- [x] Live manual verification against the built .exe and a real CLI render: compose a mixed line (e.g. “Attn: {Full Name}") against the real sample CSV, confirm canvas display, save/reopen, and confirm the rendered PDF text matches; also confirm a template saved before this story still opens and renders identically
Harden production configuration delivery for CLI runtime settings 2 points Done - [x] Explicitly decide with the product owner whether the key.txt-adjacent-to-executable mechanism (DebenuLicenseKeyResolver) is the accepted production approach, or replace it with an alternative
- [x] Document the decision and rationale in CLI_CONTRACT.md and/or project_config.md
- [x] If the mechanism changes, update DebenuLicenseKeyResolver and its tests to match; if kept as-is, no code change required beyond documentation
- [x] Confirm whether the same mechanism should generalize to future CLI configuration values or stay license-key-specific, and document that scope decision

Notes

  • Capacity signal: velocity range holds at 18-20 points, now off four data points (Sprint 1: 20, Sprint 2: 19, Sprint 3: 18, Sprint 4: 18) — the last two sprints both landed exactly on their committed total with zero scope change, a tightening trend toward the low end.
  • Committing 15 points, below even the low end of the velocity range. Deliberate, not timid: the 13-point story is this team's largest single story to date (previous ceiling was 8) with real structural risk called out in its own sizing note (a breaking model change, no reusable UI pattern to copy, a two-sided backward-compatible persistence design) — extra buffer beyond the usual ~10% grooming reserve is warranted given that uncertainty, per AGENTS.md's general guidance applied to a higher-risk-than-usual story rather than a rote percentage.
  • Deliberately not committed this sprint: “Group lines into a single, movable Address Control” (13 points, epic 8) — hard-depends on this sprint's “Mix static text and CSV fields” story landing first, and dev-team's own sizing note recommended against committing both epic-08 stories together (26 points, over one sprint's capacity on their own). Clean pull for Sprint 6 once this sprint's story is Done.
  • Planning rationale: “Mix static text and CSV fields within a single text element” is pulled first as this sprint's primary goal and highest-risk item (per this team's established pattern of pulling the highest-risk/most-open-ended item first — see Sprint 3's throughput story). “Harden production configuration delivery” is small, independent, and low-risk (a documented decision plus at most a small code change) — pulled second, not because it's less important, but because it has no dependency on the larger story and can be swarmed to completion quickly whenever capacity allows within the sprint.
  • Sizing: both committed stories were sized during backlog refinement — “Mix static text and CSV fields” on 2026-09-29 with real code inspection (see backlog/epics/08_composite_address_controls.md's sizing note: no existing UI edits static text today, and the persisted-format work is a two-sided structural schema design, not a scalar default); “Harden production configuration delivery” was already Ready from earlier refinement (2 points, epic 5). Both pass the Definition of Ready.
  • 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 4 completed all 3 committed stories).
  • Sprint 5 retrospective action items to apply during this sprint (from backlog/sprints/sprint-4-retrospective.md): continue empirical vendor/spec verification before implementing render- or format-affecting code (directly relevant here — the bracket-syntax parsing and backward-compatible persistence shape should be validated against real saved templates, not assumed); continue live actual-built-artifact verification for the new mixed-content editing UI.

Execution Order

Sequenced by priority and risk; the larger, higher-risk story is pulled first since it carries this sprint's goal and has no dependency on the smaller story.

Batch Story Why it's gated here
1 Mix static text and CSV fields within a single text element No dependency on any other Sprint 5 story. Highest priority and highest risk (this team's largest story to date) — pulled first.
2 Harden production configuration delivery for CLI runtime settings No dependency on Batch 1 or any other story. Small and independent — pulled second, swarmed whenever capacity allows within the sprint.

Daily Scrum Log

Day Date Completed Planned Blocked/At risk
1 2026-10-05 Batch 1 (“Mix static text and CSV fields within a single text element”, 13 points) done, all 6 ACs met — this team's largest story to date, delivered as a genuine breaking model change rather than an additive property, matching the sizing note's own framing. Model: added TextRun (Literal/ColumnName, mutually exclusive) on both sides — EnvelopeRenderer.Desktop.Core.Design.TextRun and EnvelopeRenderer.Cli.Render.TemplateTextRun (kept as two independently-implemented types, per this project's standing desktop/CLI-never-share-internals rule) — and replaced TextElementLayout's/TemplateElement's exclusive StaticText/ColumnName storage with an ordered Runs list. Backward compatibility was designed in from the start rather than bolted on: StaticText/ColumnName/IsDynamic are now computed back-compat projections over Runs (non-null/true exactly when they used to be for the one-run case), so all 205 pre-existing Desktop.Core tests and all 28 pre-existing CLI tests kept passing completely unmodified — a concrete, test-count-verified confirmation that “the single-run case behaves exactly as today” rather than an assumption. Editing affordance (AC3): confirmed the sizing note's working assumption (a {Column Name} bracket-typing convention parsed into runs on commit) as the fastest-to-deliver option and implemented it as-is — TextRunTextConverter.Parse/ToEditableText (round-trip tested) plus a new “Content” row leading the properties panel (TemplateDesignerForm, the first UI in this designer able to edit static text at all) wired through a new TextElementPropertiesEditor.SetContent. Persistence (AC5): chose nested <run text="..."/>/<run column="..."/> child elements over inline bracket-syntax in the persisted XML specifically to eliminate any risk to AC2 — a legacy single-run element (the overwhelming majority of existing content) is still written and read via the exact pre-Sprint-5 column="..." attribute or inline-text shape with zero <run> children, so the new parsing branch (triggered only by the presence of <run> children, a shape that can never appear in a template saved before this story) can never misinterpret pre-existing content — implemented identically but independently in TemplateLayoutXmlSerializer (desktop) and TemplateXmlParser (CLI). Render engine (AC1/AC6): RenderEngine.ResolveText concatenates a run sequence into the one string TextDraw already expected (no DebenuPdfRenderer changes needed, exactly as the sizing note predicted), and the “unknown column fails the whole run before any page renders” pre-flight check was flattened from per-element to per-run (SelectMany(e => e.Runs)), verified to catch a bad token even when it's one of several runs within a single element alongside other valid runs, not just across separate elements. Canvas (AC4): TemplateCanvasControl.DrawElement now measures and highlights each run's own display segment (MeasureRunSegments, mirroring MeasureElement's existing GDI+ approach) instead of one whole-element box, so a mixed line shows literal text unhighlighted and only the field-token portions filled blue (or orange/red if that specific run's column is unmapped — AddressBlockPreviewCalculator's ElementPreviewState.UnmappedRuns now tracks this per run, not just per element). Preview parity (AC1 companion): AddressBlockPreviewCalculator.ResolveSampleText concatenates every run against the loaded sample record the same way the CLI does, so a mixed element's blank/collapse decision reflects its full resolved text, not just one run in isolation. Tests: new coverage added across both projects for the run-sequence model, bracket-syntax parsing (including documented edge cases — unmatched/empty braces, whitespace-trimmed column names), SetContent/SetColumnName interplay (rebind stays scoped to the legacy single-field-run case, general editing goes through SetContent), per-run unmapped-column flagging, multi-run XML parsing/round-tripping, and the pre-Sprint-5 backward-compatibility regression (Load_PreSprint5TemplateFile_LoadsAndRoundTripsAsBeforeThisStory) — 330/330 tests passing across the full solution (91 in EnvelopeRenderer.Cli.Tests, 239 in EnvelopeRenderer.Desktop.Tests), up from 288 at Sprint 4 close. AC2 live regression check, exactly as briefed (a real pre-Sprint-5 binary, not just old-format XML fed to new code): built the CLI from the last commit before this sprint (git worktree at 08d6a84, no working-tree changes), rendered the real sample-envelope-template.xml against the real 392-record sample CSV with both the pre-Sprint-5 binary and the Sprint-5 binary, and byte-compared the two outputs. Raw byte diff showed ~12KB of differences; running the same unchanged pre-Sprint-5 binary twice against identical input showed an equivalent ~12.6KB of run-to-run difference on its own — proving the diff is pre-existing Debenu-internal nondeterminism (a per-run internal font-resource identifier, e.g. /QuickPDFF6036434f vs /QuickPDFF44e781a1, plus /CreationDate//ModDate/XMP timestamps one second apart), not a regression. After normalizing those two known-nondeterministic token classes, old-vs-new and old-vs-old showed the identical residual (only the timestamp difference) — the strongest available evidence that this story's changes produce byte-identical content for a genuine pre-existing template, not merely “looks the same.” AC6 live verification against the real CLI: a template with a single element mixing one valid run (column="Full Name") and one invalid run (column="NotARealColumn") in the same <text> element failed the whole run before any page rendered (exit=1, completed=0, no output file, error message naming the exact bad column) — the strictest form of the per-token check, not just per-element. AC1 live verification: a real mixed-content template (Attn: {Full Name}) rendered against the real sample CSV produced literal, grep-confirmed page text (Attn: WILLIAM EDWARD ZIMMERMAN JR)Tj, (Attn: MURIEL LYN ZIMMERMAN)Tj, etc. — genuine per-record concatenation, not a placeholder. Live GUI verification against the actual built .exe (real Win32 keyboard/UI-Automation input injection — mouse-coordinate clicks were tried first and proved unreliable in this environment, apparently a DPI-scaling mismatch between the automation script's coordinate space and the real screen; keyboard mnemonics plus an AttachThreadInput-based foreground-focus fix, the standard workaround for Windows’ foreground-lock-timeout protection blocking a background process's SetForegroundWindow call, proved reliable instead — an honestly-reported tooling wrinkle, not a product defect, in the same spirit as Sprint 4's similar SetForegroundWindow tooling note): opened the Template Designer, added a static element, typed Attn: {Full Name} into the new Content field, confirmed the selection label and canvas picked up the parsed mixed content, added a second element with a deliberately-unmapped column, saved, and confirmed the on-disk XML used the new <run> shape exactly as designed for both elements. This live pass caught one real, honestly-reported gap in the application (not the test tooling): TemplateDesignerForm.RefreshSelectionLabel still used the old IsDynamic ? "{ColumnName}" : StaticText formatting, which silently showed an empty "{}" for any genuinely mixed-content element (ColumnName is null once an element has more than one run) — fixed by using TextElementLayout.DisplayText (which already generalizes the exact same concatenation) instead of re-deriving the two-case logic, and reverified live that the label then read Selected: Attn: {Full Name} at (29.7, 547.2) pt. correctly. TEMPLATE_FORMAT.md updated with the <run> shape, the per-token pre-flight rule, and the bracket-syntax editing convention's own documented known limitation (a literal {...} in static text that isn't meant as a token will be parsed as a field-token attempt — an accepted trade-off of the faster-to-deliver bracket convention, not a silently-swallowed edge case). No CLI_CONTRACT.md changes needed — the CLI's argument/exit-code contract is unaffected; only the template format's internal content model changed. Batch 2 (“Harden production configuration delivery for CLI runtime settings”), pulled next per the Execution Order table (independent of Batch 1). None.
1 2026-10-05 Batch 2 (“Harden production configuration delivery for CLI runtime settings”, 2 points) done, all 4 ACs met, swarmed same day as Batch 1 per the Execution Order table (small, independent, no dependency on Batch 1). No live product owner to consult mid-sprint, so dev-team made the call itself, same as past sprints’ Development-Team design decisions (e.g. Sprint 3's explicit-bind-action choice) — documented for product-owner to confirm or override at Sprint Review, per this story's own AC framing. Decision: keep DebenuLicenseKeyResolver's existing mechanism (env var first, then key.txt walked up from the executable's directory) as the accepted production approach, unchanged. Reasoning recorded in CLI_CONTRACT.md's new “Production configuration delivery decision” section: (1) it already works and is already verified against the real built .exe (the 2026-09-04 fix); (2) it fits this product's actual deployment model — a single-workstation, Windows-login-only desktop app for non-technical operators, where a plaintext key file requires zero extra operator steps, unlike an environment variable or credential-manager entry; (3) the key's exposure risk is a vendor-licensing concern, not a security/privacy one, since no user/customer/CSV data passes through this mechanism; (4) no packaging/installer story exists yet to justify a stronger mechanism (e.g., an installer-provisioned %ProgramData% location) — building one now would be speculative. Recorded an explicit revisit trigger: the next release/packaging story must reference this decision rather than reopening it, but should revisit if the chosen distribution mechanism makes a stronger option cheap to add. Scope-generalization question answered: the pattern (env var, then a file-based fallback near the executable) is the team's convention for any future CLI setting, but a generic reusable “settings resolver” abstraction is deliberately not built now with only one configuration value in existence (YAGNI) — revisit if/when a second setting appears. No code change to DebenuLicenseKeyResolver or its 5 existing tests, per the story's own AC3 allowance for a “kept as-is” outcome. With this batch done, all of Sprint 5's committed backlog is complete: 330/330 tests passing. Sprint's committed backlog is now fully done (15/15 points across both committed stories). Next: sprint review and retrospective per process/03_sprint_execution.md's exit criteria. None.

Powered by TurnKey Linux.