Skip to content

Surface vectorization: drop legacy TFMs, row stride padding, vectorized alpha compositing - #3

Merged
Malcolmnixon merged 17 commits into
mainfrom
feature/surface-vectorization
Sep 20, 2026
Merged

Malcolmnixon merged 17 commits into
mainfrom
feature/surface-vectorization

Conversation

@Malcolmnixon

Copy link
Copy Markdown
Member

Summary

This PR drops legacy target-framework support and adds internal row-stride padding plus a first batch of vectorized bulk pixel operations to Surface, per the approved plans in .agent-logs/planning-vectorization-surface-stride-7f2a91.md and .agent-logs/planning-vectorization-surface-stride-part0-9c14e2.md.

Part 0 — Drop netstandard2.0 / net481 / Polyfill

  • Library TargetFrameworks: netstandard2.0;net8.0;net9.0;net10.0 -> net8.0;net9.0;net10.0.
  • Test project: collapsed OS-conditional TFM lists (with net481 on Windows) into a single unconditional net8.0;net9.0;net10.0.
  • Removed the Polyfill PackageReference, PolyArgumentExceptions/PolyUseEmbeddedAttribute properties, and Polyfills using-directives from both csproj files; cleaned up stale doc-comments in 3 codec fixture test files.
  • Removed the now-false CanvasNet-Platform-NetStd20 requirement and updated design docs/README to drop netstandard2.0/.NET Framework mentions.

Part 1 — Internal row stride/padding in Surface

  • Each row's internal buffer allocation is now padded to a multiple of 16 pixels (64 bytes), computed once at construction as _strideBytes.
  • All public accessors (GetRowSpanBytes, GetRowSpan, indexer) continue to expose exactly Width-length spans; padding bytes are never observable.
  • Re-verified all 4 codecs (Bmp/Png/Tiff/Jpeg) access Surface exclusively through the public per-row accessor — no changes needed there.
  • Added boundary-width regression tests (1, 15, 16, 17, 31, 32, 33, 100, 257) plus a PNG codec round-trip test for an odd width.

Part 2 — Vectorized bulk pixel operations

Added to Surface:

  • PremultiplyAlpha()
  • UnpremultiplyAlpha()
  • CompositeOver(Surface foreground) (ArgumentNullException for null, ArgumentException for size mismatch)
  • CompositeOver(Rgba32 color)

Implementation: per row, RGBA bytes are deinterleaved into planar R/G/B/A arrays via a simple scalar loop (clarity over cleverness for this layout-shuffle step), then the numerically heavy work (byte->float widening, multiply/divide, rounding, float->byte narrowing) uses System.Numerics.Tensors.TensorPrimitives, unconditionally across all three TFMs (no #if gating, since netstandard2.0 is gone). Porter-Duff "over" compositing in normalized [0,1] alpha space; round-half-away-from-zero rounding clamped to [0,255]; UnpremultiplyAlpha at alpha == 0 defines RGB = 0.

Companion artifacts updated: docs/reqstream/canvas-net/canvas/surface.yaml (+ parent subsystem/system requirements to keep the requirement hierarchy non-orphaned), docs/design/canvas-net/canvas/surface.md (+ system/subsystem Dependencies sections), docs/verification/canvas-net/canvas/surface.md, docs/sysml2/model/canvas-net/canvas/surface.sysml, README.md, docs/user_guide/introduction.md.

Quality retry

One quality-review cycle found two issues (stale "zero runtime dependencies" claims in system/subsystem design docs after adding System.Numerics.Tensors, and a system-level requirement linked to a test that didn't actually exercise premultiply/unpremultiply). Both were fixed in a follow-up commit and re-verified as resolved by a second quality pass.

Validation

  • dotnet build --configuration Release: 0 Warnings, 0 Errors
  • pwsh ./build.ps1 (full test suite, net8.0/net9.0/net10.0): 1275 total, 0 failed
  • dotnet reqstream --requirements requirements.yaml --tests "**/*.trx" --enforce: 202/230 satisfied, same pre-existing 28-item gap as main (unrelated Quality-/Platform-/OTS-* items), zero new orphans
  • dotnet reviewmark --lint --enforce: exit 0
  • pwsh ./lint.ps1: exit 0

Commits

  1. 26def64 - Drop netstandard2.0/net481/Polyfill support
  2. 6a619ba - Add internal row stride/padding to Surface
  3. c0839eb - Add vectorized alpha premultiply and Porter-Duff compositing to Surface
  4. a971546 - Fix quality review findings: dependency docs and premultiply requirement evidence

Malcolm Nixon added 4 commits September 19, 2026 12:21
…ce (Part 2)

Adds PremultiplyAlpha, UnpremultiplyAlpha, CompositeOver(Surface), and CompositeOver(Rgba32) to Surface, implemented via System.Numerics.Tensors.TensorPrimitives for the numeric-heavy work (byte<->float conversion, multiply, divide, round) on planar per-channel float spans, with a plain scalar loop used only for RGBA<->planar deinterleave/reinterleave and for the alpha==0 degenerate-case fix-up (TensorPrimitives has no conditional-select primitive). Single unconditional code path across net8.0/9.0/10.0 (netstandard2.0 already dropped in Part 0). Updates requirements (surface.yaml, canvas.yaml, canvas-net.yaml), design doc, verification doc, SysML2 model, README, and user guide; adds unit tests with independently hand-computed expected values (Python float32 simulation) for compositing/premultiply math, plus null/size-mismatch error-path tests.
Copilot AI lite review requested due to automatic review settings September 19, 2026 17:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved critical and moderate findings remain, including stride-overflow validation, dependency traceability, and requirements/test-scope gaps.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity · 4 Medium severity

Open (5)
What changed in this PR

This PR drops legacy target frameworks and adds padded row storage plus vectorized alpha/compositing operations to Surface.

Changes:

  • Removes legacy TFMs and Polyfill dependencies.
  • Adds 16-pixel row-stride padding.
  • Adds premultiplication, unpremultiplication, and Porter-Duff compositing APIs.
  • Updates tests, documentation, requirements, and design artifacts.
File Summary
test/​DemaConsulting.CanvasNet.Tests/​DemaConsulting.CanvasNet.Tests.csproj Removes legacy TFMs and Polyfill settings.
test/​DemaConsulting.CanvasNet.Tests/​Codecs/​TiffFixtureTests.cs Removes obsolete Polyfill documentation.
test/​DemaConsulting.CanvasNet.Tests/​Codecs/​PngSuiteTests.cs Removes obsolete Polyfill documentation.
test/​DemaConsulting.CanvasNet.Tests/​Codecs/​JpegFixtureTests.cs Removes obsolete Polyfill documentation.
test/​DemaConsulting.CanvasNet.Tests/​CanvasNetTests.cs Adds integration coverage.
test/​DemaConsulting.CanvasNet.Tests/​Canvas/​SurfaceTests.cs Adds stride, alpha, compositing, and codec tests.
src/​DemaConsulting.CanvasNet/​DemaConsulting.CanvasNet.csproj Updates TFMs and adds Tensor primitives.
src/​DemaConsulting.CanvasNet/​Canvas/​Surface.cs Implements padded storage and vectorized pixel operations.
README.md Updates supported platforms and features.
docs/​verification/​canvas-net/​canvas/​surface.md Adds Surface verification scenarios.
docs/​user_guide/​introduction.md Documents the new Surface APIs.
docs/​sysml2/​model/​canvas-net/​canvas/​surface.sysml Updates the Surface model.
docs/​reqstream/​canvas-net/​platform-requirements.yaml Removes legacy platform requirements.
docs/​reqstream/​canvas-net/​canvas/​surface.yaml Adds Surface requirements and traceability.
docs/​reqstream/​canvas-net/​canvas.yaml Updates subsystem requirements and dependencies.
docs/​reqstream/​canvas-net.yaml Adds system-level requirements and evidence.
docs/​design/​canvas-net/​canvas/​surface.md Documents storage, APIs, and dependencies.
docs/​design/​canvas-net/​canvas.md Updates Canvas dependency documentation.
docs/​design/​canvas-net.md Updates system design documentation.
.cspell.yaml Adds vectorization terminology.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/design/canvas-net.md Resolved
Comment thread docs/reqstream/canvas-net.yaml Resolved
…ign/verification docs

- docs/design/canvas-net.md: add PremultiplyAlpha, UnpremultiplyAlpha, and both
  CompositeOver overloads to the External Interfaces list and table (previously
  omitted despite existing system-level requirements covering them).
- docs/verification/canvas-net.md: add the 3 missing system-level test scenarios
  (CompositeColorOverSurface, PremultiplyAlpha, UnpremultiplyAlpha) already present
  in CanvasNetTests.cs, and update the acceptance criteria count from six to nine.
- .cspell.yaml: add 'unpremultiplication' alongside the existing premultiplication
  family of dictionary entries.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 19, 2026 17:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Unresolved moderate findings remain in overflow handling, test coverage, dependency traceability, and requirement evidence.

Review effort: Lite
Findings: 1 High severity · 4 Medium severity

Open (5)
Previously missed (1)

In code that hasn't changed since last review

Low severity Correct inaccurate acceptance criterion requirement count

docs/​verification/​canvas-net/​canvas/​surface.md:238

The acceptance criterion's count is inaccurate: this document contains 21 scenario headings, while surface.yaml defines 22 Surface requirements, not 24. Remove the hard-coded number or state the unit being counted so the acceptance criterion cannot misreport verification scope.

Malcolm Nixon added 3 commits September 19, 2026 14:09
…c (Finding 1)

Add MaxDimension (16384) validation to Surface(int, int) so width/height
are bounded before the padded-stride and total-buffer-size arithmetic
runs, making the plain int arithmetic provably overflow-safe (max buffer
size 16384*16384*4 = 1,073,741,824 bytes, comfortably under int.MaxValue).

- Surface.cs: add MaxDimension const, new ArgumentOutOfRangeException
  checks, and an explanatory comment above the stride/buffer arithmetic.
- SurfaceTests.cs: add boundary tests for width/height at 16384
  (succeeds) and 16385 (throws).
- docs/reqstream/canvas-net/canvas/surface.yaml: add
  WidthExceedsMaximum/HeightExceedsMaximum sibling requirements and link
  the new boundary-success tests to the Construction requirement.
- docs/design + docs/verification: document the new bound, throws, and
  overflow-safety rationale; reword the surface.md verification
  Acceptance Criteria to avoid a fragile hardcoded scenario count.
Confirmed docs/design/canvas-net/canvas/surface.md 'Dependencies',
docs/design/ots.md 'Overview', and docs/design/canvas-net.md
'Dependencies' are coherent (Findings 2/3) and staged the pre-existing
fix to docs/design/ots.md's Overview alongside this commit.

- docs/design/ots/system-numerics-tensors.md: new OTS design doc
  (Purpose/Features Used/Integration Pattern), documenting the exact
  TensorPrimitives methods Surface calls and that this is CanvasNet's
  only OTS item shipped as a transitive runtime dependency.
- docs/reqstream/ots/system-numerics-tensors.yaml: new OTS requirement
  linked to the existing Surface tests that exercise every listed
  TensorPrimitives method end-to-end.
- docs/verification/ots/system-numerics-tensors.md: new OTS
  verification doc describing the indirect, end-to-end verification
  approach and four test scenarios.
- docs/design/ots.md, docs/verification/ots.md: split the OTS Items
  table into build-time/quality-pipeline tools vs. the one runtime OTS
  dependency, and added a row for System.Numerics.Tensors to each.
- .reviewmark.yaml: add OTS-SystemNumericsTensors review-set covering
  the 3 new files, following the existing OTS-{Name} pattern.
- requirements.yaml: include the new OTS requirements yaml.
…nt (Findings 5, 6)

- CanvasNetTests.cs: add
  CanvasNet_SystemIntegration_CompositeSurfaceOverSurface_ReturnsExpectedPixel,
  exercising CompositeOver(Surface) end-to-end (the existing system
  test only covered CompositeOver(Rgba32)), asserting an independently
  hand-computed Porter-Duff 'over' result.
- docs/reqstream/canvas-net.yaml: link the new test as additional
  evidence for CanvasNet-Lib-CanvasComposite, alongside the existing
  CompositeOver(Rgba32) system test.
- docs/verification/canvas-net.md: add a corresponding scenario entry
  and update the acceptance-criteria count from nine to ten scenarios.
Copilot AI review requested due to automatic review settings September 19, 2026 18:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved findings remain in codec boundary validation, multi-row compositing coverage, SysML OTS modeling, and stale contract or comment text.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Low severity

Open (1)
Resolved since last review (5)

Comment thread docs/design/canvas-net.md Resolved
… table

Addresses PR review comment: the system-level interface table only
listed the positive-dimension constraint and omitted the new
MaxDimension upper bound, so consumers could miss the
ArgumentOutOfRangeException boundary.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 19, 2026 21:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved critical data-exposure risk and multiple moderate correctness, coverage, and traceability issues remain.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity

Open (1)
Resolved since last review (1)
Previously missed (3)

In code that hasn't changed since last review

Medium severity Add SysML2 OTS model and dependency traceability

docs/​reqstream/​ots/​system-numerics-tensors.yaml:14

Adding this runtime OTS item without a corresponding SysML2 OTS part and system usage leaves architecture traceability incomplete: docs/sysml2/model/canvas-net.sysml still exposes only Canvas and Codecs, and no System.Numerics.Tensors model element exists. Add the OTS definition and dependency usage, plus any needed view updates, in the same change.

Medium severity Normalize oversized image errors at codec boundaries

src/​DemaConsulting.CanvasNet/​Canvas/​Surface.cs:114

These new dimension guards are reached by all codec loaders after they parse image dimensions. A valid-but-oversized BMP/PNG/TIFF/JPEG will therefore leak Surface's ArgumentOutOfRangeException, while each loader's public contract documents InvalidDataException for unsupported or invalid image data. Normalize this at the codec boundary (or update all four contracts) and add an oversized-image regression test.

Medium severity Add multi-row stride-boundary coverage for bulk operations

src/​DemaConsulting.CanvasNet/​Canvas/​Surface.cs:380

These bulk methods operate on the padded row (_strideBytes / BytesPerPixel) and iterate every row, but the new result assertions use only 1×1 surfaces except the constant-color test's 3×2 case; no test exercises a width such as 17/31/33 or a multi-row CompositeOver(Surface) input. A stride-boundary or row-offset error in these paths could therefore pass the suite. Add parameterized multi-row cases using BoundaryWidths and assert public pixels on more than the first row for each operation.

RowChannelBuffers and CompositeWorkBuffers rent scratch byte/float
arrays from ArrayPool<T>.Shared containing caller-provided pixel
channel data and derived compositing values. ArrayPool.Return
defaults to clearArray: false, so a later renter of the shared pool
could observe leftover image data from a previous operation. Return
every buffer with clearArray: true to prevent this cross-request
data exposure.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 19, 2026 21:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Unresolved moderate findings remain around codec contracts, dependency modeling, requirement evidence, and test coverage.

Review effort: Lite
Findings: None

Resolved since last review (1)
Previously missed (4)

In code that hasn't changed since last review

Medium severity Codec decoders do not enforce the new dimension limit

src/​DemaConsulting.CanvasNet/​Canvas/​Surface.cs:114

The new upper bounds are enforced only by Surface, so a valid positive-dimension image above 16384 now escapes each codec's documented InvalidDataException contract as ArgumentOutOfRangeException when the codec reaches new Surface(...) (for example, BmpCodec validates only positivity before construction). PNG also computes width * channels before constructing the surface. Validate/translate the limit in every decoder before size arithmetic, or explicitly update all codec contracts to declare this new unsupported range.

Medium severity Missing zero-alpha compositing regression tests

src/​DemaConsulting.CanvasNet/​Canvas/​Surface.cs:494

The outA == 0 compositing fix-up is not exercised by the new compositing tests: the transparent-foreground cases use backgrounds with nonzero alpha, so output alpha never reaches zero. Add cases where both the background and foreground/color have alpha zero (with nonzero RGB garbage) for both overloads to verify the documented zero-RGB result and guard the NaN/division-by-zero path.

Medium severity Surface unit test improperly depends on PngCodec

test/​DemaConsulting.CanvasNet.Tests/​Canvas/​SurfaceTests.cs:444

This is a Surface unit test but it directly invokes PngCodec. The repository's test boundary permits unit tests to use only the unit and documented dependencies, while the design documents the dependency in the opposite direction (Codecs -> Surface); placing this round-trip here creates a test-only cycle and makes the Surface requirement evidence depend on another unit. Move the round-trip to PngCodecTests or a system test, and leave SurfaceTests focused on Surface behavior.

Low severity User guide omits the 16384 dimension limit

src/​DemaConsulting.CanvasNet/​Canvas/​Surface.cs:77

The new 16384 upper bound is not reflected in the user-facing constructor documentation: docs/user_guide/introduction.md still says width and height only need to be greater than zero and lists no exception for values above the limit. Update that guide so consumers see the same precondition and exception contract as this API documentation.

Malcolm Nixon added 3 commits September 19, 2026 17:56
…e size arithmetic

Codec Load methods (BmpCodec, PngCodec, TiffCodec, JpegCodec) previously
relied solely on Surface's constructor to reject width/height above
Surface.MaxDimension (16384), which let an ArgumentOutOfRangeException
escape from deep inside
ew Surface(...) instead of the documented
InvalidDataException contract for malformed/unsupported codec data. PNG
additionally computed width * channels before constructing the Surface,
so that arithmetic itself needed protection from the same class of
overflow the Surface fix addressed.

- Changed Surface.MaxDimension from private to internal so codecs in the
  same assembly can reference it directly instead of duplicating the
  constant.
- Added an explicit MaxDimension/positivity validation step in each
  codec immediately after the existing dimension parse and before any
  width/height arithmetic (stride/row-byte-width/MCU-grid sizing),
  throwing InvalidDataException with a clear message when either
  dimension exceeds 16384.
- Added regression tests per codec asserting InvalidDataException (not
  ArgumentOutOfRangeException) is thrown for width/height one greater
  than the limit.
- Updated each codec's XML doc comments, design docs, and
  reqstream/verification docs to document the new rejection scenario.
…rip test out of SurfaceTests

Addresses two related PR review comments:

1. (Missing zero-alpha compositing regression tests) Surface's
   CompositeOver implementations have a fix-up path for outA == 0 to
   avoid NaN/division-by-zero when un-normalizing premultiplied
   intermediate values. No existing test exercised this path, since the
   'transparent foreground' cases used backgrounds with nonzero alpha.
   Added Surface_CompositeOverSurface_BothFullyTransparent_ResultIsZero
   and Surface_CompositeOverColor_BothFullyTransparent_ResultIsZero,
   using nonzero/garbage RGB with zero alpha on both operands, asserting
   an exact zero RGBA result.

2. (Surface unit test improperly depends on PngCodec) Moved
   Surface_PngCodecRoundTrip_BoundaryWidths_ReturnsExpectedPixels out of
   SurfaceTests.cs into CanvasNetTests.cs as
   CanvasNet_SystemIntegration_PngCodecRoundTrip_BoundaryWidths_ReturnsExpectedPixels.
   The dependency direction in this codebase is Codecs -> Surface, so a
   Surface unit test depending on PngCodec created an inappropriate
   test-only cycle and made Surface's requirement evidence spuriously
   depend on PngCodec passing. The test verifies that Surface's internal
   row padding is invisible across a codec round-trip, which is a
   Codecs/Surface integration concern, so it now lives as a
   system-level scenario.

Updated docs/reqstream/canvas-net/canvas/surface.yaml and the
surface.md/canvas-net.md verification docs to reflect both test
additions/relocations.
docs/user_guide/introduction.md previously said the Surface constructor's
width/height only need to be greater than zero, omitting the new
Surface.MaxDimension (16384) upper bound and the ArgumentOutOfRangeException
thrown when it is exceeded. Updated the parameter and exception
documentation to match the XML doc comments on Surface's constructor and
docs/design/canvas-net.md's interface table.
Copilot AI review requested due to automatic review settings September 19, 2026 21:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved critical allocation-safety and moderate modeling, verification, and test-coverage findings remain.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity

Open (1)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Add multi-row boundary tests for alpha operations

src/​DemaConsulting.CanvasNet/​Canvas/​Surface.cs:381

The bulk implementation deliberately iterates the padded row count and all rows, but the new PremultiplyAlpha and UnpremultiplyAlpha tests only use 1x1 surfaces. They therefore do not exercise the newly introduced y > 0 path or a non-multiple-of-16 stride for these methods; a row-offset or padding-boundary regression could pass. Add multi-row cases at representative boundary widths and assert every visible pixel.

Malcolm Nixon added 2 commits September 19, 2026 18:34
Bounds worst-case Surface buffer allocation to 8192x8192x4 = 256 MiB
(down from ~1 GiB at 16384), closing an allocation-safety gap where
two attacker-controlled header fields (parsed by BmpCodec/PngCodec/
TiffCodec/JpegCodec from untrusted input) could force a large
allocation before the codec's InvalidDataException contract is
returned. 8192 remains sufficient for an 8K video frame.

Updates the overflow-safety XML doc/inline comments with recomputed
arithmetic (8192*4=32,768 stride bytes; 8192*32,768=268,435,456 total
bytes, comfortably under int.MaxValue), and updates every 16384/16,384
reference repository-wide (tests, design docs, verification docs, user
guide) to 8192/8,192. No additional total-pixel-budget check was
added, per the confirmed design decision.

Also fixes two unrelated broken cross-references
(docs/verification/surface-net.md -> canvas-net.md) in the jpeg-codec
and tiff-codec verification docs, and corrects the PngCodecTests.cs
test-method count in the png-codec verification doc (27, not 32),
found incidentally while touching these files.
…remultiplyAlpha

Surface.PremultiplyAlpha/UnpremultiplyAlpha iterate over the full
padded row count (16-pixel row-stride alignment), but existing tests
only used 1x1 surfaces, never exercising the y>0 code path or a
non-multiple-of-16 width, where padding bytes could otherwise leak
into or corrupt visible pixel results without detection.

Adds theory tests over the existing BoundaryWidths data set (including
non-16-aligned widths 1/15/17/31/33/100/257) at height=3, asserting
every visible pixel against an independent scalar-arithmetic oracle
(distinct from the production SIMD/TensorPrimitives implementation).

Extends the existing PremultiplyAlpha/UnpremultiplyAlpha requirement
entries in docs/reqstream/canvas-net/canvas/surface.yaml with the new
test names (no new requirement IDs, matching the CompositeOverColor
precedent) and updates the corresponding verification doc scenarios.
Copilot AI review requested due to automatic review settings September 19, 2026 22:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Unresolved moderate findings remain around system evidence, OTS verification, model coverage, and multi-row compositing tests.

Review effort: Lite
Findings: 1 High severity

Open (1)
Previously missed (5)

In code that hasn't changed since last review

Medium severity Add system-level validation for canvas compositing

docs/​reqstream/​canvas-net.yaml:132

CanvasNet-Lib-CanvasCompositeValidation is a root [system] requirement, but its evidence is three Surface_* unit tests. The other root requirements in this file use CanvasNet_SystemIntegration_* evidence, and unit tests do not demonstrate the system-level boundary. Add corresponding CanvasNet_SystemIntegration_* validation tests and link them here, or move this requirement/evidence into the Canvas/Surface hierarchy.

Low severity Replace Markdown anchor with Dependencies cross-reference

docs/​design/​canvas-net/​canvas/​surface.md:19

This new design document uses a Markdown anchor link, but internal anchor/file links do not survive the repository’s Pandoc PDF compilation. Use a verbal cross-reference to the Dependencies section instead.

This issue also appears on line 83 of the same file.

Low severity Align Porter-Duff verification claim with test evidence

docs/​verification/​canvas-net/​canvas/​surface.md:224

This says the expected Porter-Duff result comes from a separate float32 simulation, but the test asserts a hard-coded value and contains no such reference implementation. The surrounding test comments describe an independent hand computation instead; align this verification claim with the actual evidence or add the reference simulation.

Low severity Label TIFF totals as executed test cases

docs/​verification/​canvas-net/​codecs/​tiff-codec.md:287

The updated total is counting expanded xUnit executions, not methods: TiffCodecTests.cs has 37 methods but 49 cases after its theory rows, and TiffFixtureTests.cs has 5 methods but 19 cases. Calling these 68 “test methods” is inaccurate and conflicts with the parenthetical “including every [Theory] case”; label them as executed test cases (and similarly update the preceding sentence if needed).

Low severity Align float32 simulation claim with actual oracle

docs/​verification/​ots/​system-numerics-tensors.md:23

The verification text claims a separate float32 simulation, but the independent conversion helpers in SurfaceTests use scalar double arithmetic and the compositing expectations are hard-coded values; no float32 reference implementation is present. Please describe the actual oracle or add the claimed simulation.

Formal review found the prior fix's 'twenty-seven' count was itself
wrong: PngCodecTests.cs has 27 methods but PngSuiteTests.cs adds 3
more, for a true total of 30 (matching the phrasing style already
used in jpeg-codec.md).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 19, 2026 22:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Unresolved moderate findings remain in requirement traceability, SysML2 dependency modeling, and multi-row compositing coverage.

Review effort: Lite
Findings: 1 High severity

Open (1)
Previously missed (6)

In code that hasn't changed since last review

Medium severity Add system-level integration evidence for the requirement

docs/​reqstream/​canvas-net.yaml:132

This new requirement is tagged at the system level, but its evidence consists only of Surface_* unit tests. The other root requirements in this file use CanvasNet_SystemIntegration_* evidence, and system behavior is verified through system integration tests; add a system-level validation scenario or place this requirement at the Canvas subsystem level.

Low severity Replace Markdown anchors with PDF-safe verbal references

docs/​design/​canvas-net/​canvas/​surface.md:19

These new anchor links violate the repository's documentation rule that Markdown section/file links break during Pandoc PDF compilation; use a verbal reference such as "see the Dependencies section below" instead.

This issue also appears in the following locations of the same file:

  • line 82
  • line 117
Low severity Describe the hand-computed Porter-Duff verification accurately

docs/​verification/​canvas-net/​canvas/​surface.md:224

The referenced test does not use a separate float32 simulation: the expected compositing value is a hard-coded hand calculation. Describe this as an independently hand-computed Porter-Duff result so the verification document matches the actual evidence.

Low severity Refer to all system integration scenarios

docs/​verification/​canvas-net/​codecs/​jpeg-codec.md:221

The system verification document now contains eleven scenarios, not one, including the new alpha-compositing and boundary-width integrations. Change this acceptance statement to refer to the system-level integration scenarios in the linked document.

Low severity Refer to all system integration scenarios

docs/​verification/​canvas-net/​codecs/​tiff-codec.md:289

The system verification document now contains eleven scenarios, not one, including the new alpha-compositing and boundary-width integrations. Change this acceptance statement to refer to the system-level integration scenarios in the linked document.

Low severity Correct description of the scalar-double verification oracle

docs/​verification/​ots/​system-numerics-tensors.md:25

SurfaceTests computes the multi-row premultiply/unpremultiply oracle with scalar double arithmetic and the compositing expectations as hand calculations, not through a separate float32 simulation. This wording overstates the verification evidence; describe the actual scalar-double/hand-computed oracle instead.

- Correct verification/OTS docs that inaccurately claimed a 'separate
  float32 simulation' oracle for premultiply/unpremultiply/composite
  tests; the actual code uses scalar double arithmetic (premultiply/
  unpremultiply) and hand-computed Porter-Duff constants (composite),
  per the tests' own doc comments.
- Replace Markdown anchor links in surface.md (design doc) with
  verbal section references, per this repo's rule that such links
  break during Pandoc PDF compilation.
- Fix jpeg-codec.md/tiff-codec.md verification docs' stale 'the
  single system-level integration scenario' wording now that
  canvas-net.md documents eleven system scenarios.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 20, 2026 00:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Two moderate findings and one documentation nit remain unresolved.

Review effort: Lite
Findings: None

Resolved since last review (1)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Expand CompositeOver tests to cover multi-row pixel indexing

src/​DemaConsulting.CanvasNet/​Canvas/​Surface.cs:496

The new CompositeOver(Surface) path is only exercised with 1x1 unit fixtures; the 2x2 system test asserts only [0, 0]. A row/column or foreground-row indexing defect would therefore pass, unlike the constant-color overload which has a multi-row assertion. Add a multi-row/multi-column case with distinct foreground and background pixels (ideally at a non-16-aligned width) and assert every visible pixel.

@Malcolmnixon
Malcolmnixon merged commit 3cdc863 into main Sep 20, 2026
7 checks passed
@Malcolmnixon
Malcolmnixon deleted the feature/surface-vectorization branch September 20, 2026 01:48
Malcolmnixon added a commit that referenced this pull request Oct 8, 2026
* Add CanvasNetVsdx design, requirements, and SysML2 skeleton

Lays the documentation groundwork for a new DemaConsulting.CanvasNet.Vsdx
NuGet package providing Microsoft Visio (.vsdx) diagram-rendering support,
following the software-items.md standard:

- docs/design/canvas-net-vsdx.md + canvas-net-vsdx/vsdx-document.md: system
  and unit design docs, describing the complete final feature set (shipped
  as a single PR) with an ordered 9-milestone internal implementation plan.
- docs/sysml2/model/canvas-net-vsdx.sysml + subfolder: SysML2 architecture
  model, wired into docs/sysml2/views/design-views.sysml.
- docs/reqstream/canvas-net-vsdx.yaml + subfolder: requirement entries
  (CanvasNetVsdx-* / CanvasNetVsdx-VsdxDocument-* IDs) covering package
  parsing, geometry/transform, master/stencil inheritance, style
  resolution, text, connectors, color/theme, and the public Render API;
  wired into root requirements.yaml.
- docs/verification/canvas-net-vsdx.md + subfolder: TODO verification
  stubs, to be completed once the test suite exists.
- docs/design/introduction.md and .reviewmark.yaml updated to reference
  the new system.

A research pass (not committed - see session artifacts) grounded this
design in real sample .vsdx files from the permissively-licensed
dave-howard/vsdx (BSD-3-Clause) and jgreywolf/vsdx-js (MIT) repositories;
14 of those sample files are staged for the later test-suite milestone at
test/DemaConsulting.CanvasNet.Vsdx.Tests.Fixtures-STAGING/.

No C# source/project files yet - implementation follows in subsequent
local commits per the phase plan in docs/design/canvas-net-vsdx.md.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add CanvasNetVsdx project scaffolding (Milestone 1)

Creates the empty, compiling project skeletons for the new
DemaConsulting.CanvasNet.Vsdx system, per Milestone 1 ("Project
scaffolding") of docs/design/canvas-net-vsdx.md's Implementation
Phase Plan.

- src/DemaConsulting.CanvasNet.Vsdx/DemaConsulting.CanvasNet.Vsdx.csproj:
  new library project mirroring the sibling Pptx/Pdf/Charts csproj
  conventions (net8.0/net9.0/net10.0, analyzers, SBOM, ApiMark,
  SourceLink), referencing only the core DemaConsulting.CanvasNet
  project (no Charts/Svg/Pdf dependency, per the design doc). Uses the
  newest package versions currently in use anywhere in the repo
  (DemaConsulting.ApiMark.MSBuild 0.5.4, SonarAnalyzer.CSharp
  10.35.0.4138, Microsoft.CodeAnalysis.NetAnalyzers 10.0.401,
  Microsoft.Sbom.Targets 4.1.13, Microsoft.SourceLink.GitHub
  10.0.401).
- src/DemaConsulting.CanvasNet.Vsdx/NamespaceDoc.cs: forward-looking
  namespace XML doc describing the final VsdxDocument feature set
  (OPC package layer, page/master/style model, geometry+transform,
  master/stencil inheritance, text, connector/glue-point routing,
  color/theme resolution, public Render API), with a remarks note
  that implementation is in progress and detailed scope will be
  filled in on VsdxDocument as milestones land.
- src/DemaConsulting.CanvasNet.Vsdx/VsdxDocument.cs: an empty, public
  sealed partial class VsdxDocument with only a one-line summary -
  partial so later milestones can add VsdxDocument.*.cs files
  (mirroring PptxDocument's own partial-class-per-concern
  convention) without touching this file.
- test/DemaConsulting.CanvasNet.Vsdx.Tests/
  DemaConsulting.CanvasNet.Vsdx.Tests.csproj: new xunit v3 test
  project mirroring the sibling *.Tests.csproj conventions,
  referencing only the new Vsdx project. The shared TestSupport
  synthetic-font-helper link is intentionally omitted - this
  milestone has no text-rendering code yet; a later milestone can
  add it.
- test/DemaConsulting.CanvasNet.Vsdx.Tests/VsdxDocumentTests.cs: one
  trivial smoke test confirming VsdxDocument exists and is
  referenceable.
- CanvasNet.slnx: wires both new projects into the existing /src/
  and /test/ solution folders, positioned immediately after their
  Pptx counterparts (matching the existing, non-alphabetical but
  consistent ordering).

Deferred to later milestones per the phase plan: OPC package
parsing/Open(...), page enumeration, shape/geometry model,
master/stencil inheritance, style resolution, text rendering,
connector/glue-point routing, theme/color resolution, the public
Render API, and promoting
test/DemaConsulting.CanvasNet.Vsdx.Tests.Fixtures-STAGING/ into this
test project (left untouched here).

Verified: dotnet build CanvasNet.slnx (0 warnings, 0 errors across
all three target frameworks); dotnet test on the new
DemaConsulting.CanvasNet.Vsdx.Tests project (3/3 passed - net8.0,
net9.0, net10.0); fix.ps1 run (no changes needed beyond what was
already formatted); dotnet format --verify-no-changes clean. lint.ps1
still reports pre-existing, out-of-scope cspell findings in
docs/design/introduction.md, docs/design/canvas-net-vsdx.md, and
test/DemaConsulting.CanvasNet.Vsdx.Tests.Fixtures-STAGING/ from the
prior commit; none are introduced by this change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix cspell gaps in VSDX design/fixture docs

Adds missing technical terms (Visio, slnx, jgreywolfvsdxjs, Greywolf,
Lucidchart, basicshapes, diagramwithstyles, flowchartshapes) to existing
cspell:ignore comments, surfaced by lint.ps1 after the design-doc and
fixture-staging commits. Full lint.ps1 now passes clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 2: OPC package layer + page enumeration

Implements the OPC/ZIP package layer and page index resolution for
DemaConsulting.CanvasNet.Vsdx, mirroring the proven PptxDocument precedent:

- VsdxDocument.Open(Stream) / Open(string path): opens a .vsdx as a
  read-only ZIP archive with bounded buffering (MaxPackageBytes).
- VsdxDocument.Package.cs: generic OPC mechanics - [Content_Types].xml
  parsing, relationship parsing/caching (_rels/.rels and part-level
  .rels), "../"-relative target resolution, ResolveRelationshipByType /
  TryResolveRelationshipByType, XXE-hardened XML loading.
- VsdxDocument.Pages.cs: resolves visio/document.xml (required) and
  visio/pages/pages.xml (required) via relationship type; resolves
  visio/masters/masters.xml and visio/theme/theme1.xml if present but
  does not require them (confirmed optional via real fixture evidence
  and the design doc's own exception contract); parses each <Page>'s
  Name/NameU, PageWidth/PageHeight cells (inches -> EMU, ignoring any
  U= formatting-hint attribute).
- VsdxPageInfo: readonly record struct (Name, WidthEmu, HeightEmu)
  mirroring PptxSlideSize's shape/unit convention.
- PageCount property and GetPageSize(int) method exposed on
  VsdxDocument, with idempotent Dispose().
- Exception contract mirrors Pptx exactly: ArgumentNullException/
  ArgumentException for null/empty stream or path; InvalidDataException
  for corrupt ZIP or a missing/empty required part. No new custom
  exception type was needed.
- Full Milestone-2 test suite in VsdxDocumentTests.cs covering all
  forward-declared reqstream test names, the full negative/validation
  matrix, and real-fixture sanity checks (including a loop over all 14
  staged sample files from the Fixtures-STAGING folder, wired into the
  test project via a Link-based fixture include).

Deferred to later milestones (3+): shape/geometry parsing, masters/
styles/theme content, text, connectors, and rendering. Page-content
parts (visio/pages/pageN.xml) are intentionally not resolved yet.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 3: Shape/geometry model + master/stencil inheritance + style resolution

Implements the internal per-shape resolution model for .vsdx diagrams:

- Shape tree parsing: generic Cell/Section cell-bag model for <Shapes>/<Shape>
  XML (VsdxCell, VsdxCellBag, VsdxShapeNode), forward-compatible with future
  group/nested-shape composition (Milestone 6).
- Master/Stencil resolution: visio/masters/masters.xml + per-master
  masterN.xml parsing, with cell and geometry-row override-merge (including
  the geometry-row-delete pattern) when a page shape references Master="N".
- StyleSheet chain resolution from visio/document.xml's <StyleSheets>,
  including built-in style IDs, with a shape's direct cell overrides taking
  final precedence.
- Geometry row parsing (MoveTo/LineTo/RelMoveTo/RelLineTo); other row types
  (ArcTo, RelCubBezTo, Ellipse, etc.) are skipped gracefully per the design
  doc's Risk Control Measures section, not thrown.
- 2-D and 1-D shape-local-to-page-coordinate affine transform, verified
  against the format reference's worked rotation example with exact
  expected-coordinate assertions.
- FillPattern 0/1 solid-or-none color/fill resolution with literal-RGB
  fallback; Themed colors get a tolerant neutral-gray fallback pending full
  theme/built-in-palette support.
- Reuses existing core Path/PathBuilder/Vector2/Rgba32 primitives from
  DemaConsulting.CanvasNet; no parallel geometry/paint types invented.

New files: VsdxCell.cs, VsdxCellBag.cs, VsdxGeometry.cs, VsdxShapeTransform.cs,
VsdxResolvedPaint.cs, VsdxColorPalette.cs, VsdxUnsupportedFeatureException.cs,
VsdxShapeNode.cs, VsdxDocument.Shapes.cs, VsdxDocument.Masters.cs,
VsdxDocument.CellMerge.cs, VsdxDocument.Geometry.cs, VsdxDocument.Transform.cs,
VsdxDocument.Styles.cs, VsdxDocument.Paint.cs, plus VsdxTestPackages.cs and
five new test classes (Geometry/Transform/MasterInheritance/
StyleResolution/FixtureShapeResolution).

Deferred to later milestones: text (M4), connectors/glue-points (M5),
recursive group/nested-shape composition (M6), Themed/full built-in color
palette (M6), public Render API / pixel painting onto Surface (M7).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 4: text parsing, TextStyle resolution, and layout

Implements Milestone 4 ("Text rendering") of the Vsdx 9-milestone plan:

- Parse each shape's <Text> element (character data interleaved with
  <cp IX="..."> / <pp IX="..."> run and paragraph markers) into an
  ordered sequence of raw text runs (VsdxDocument.Text.cs,
  VsdxTextRun.cs).
- Resolve character properties (font family/size/color/bold/italic) via
  Section N="Character" cells and paragraph properties (alignment,
  spacing) via Section N="Paragraph" cells, through the same TextStyle
  StyleSheet chain-walking pattern already used for LineStyle/FillStyle
  in Milestone 3 (instance -> Master -> Stencil -> built-in defaults),
  plus direct per-shape text-section overrides
  (VsdxDocument.TextStyle.cs, VsdxDocument.CellMerge.cs,
  VsdxDocument.Styles.cs).
- Text-box positioning: implicit default equal to the shape's own
  geometry bounding box in shape-local coordinates, with support for
  explicit TxtPinX/TxtPinY/TxtWidth/TxtHeight/TxtAngle override cells
  (VsdxDocument.TextBox.cs, VsdxTextBoxTransform.cs).
- Word-wrap/line-breaking and horizontal/vertical alignment within the
  resolved text box, mirroring the Pptx text-layout tokenize/pack/
  position pipeline, and glyph-ink path resolution via the shared
  TrueTypeFont/SystemFontCatalog primitives
  (VsdxDocument.TextLayout.cs, VsdxTextLayout.cs). A standalone glyph
  painter (VsdxDocument.TextRender.cs) is included but intentionally
  left unwired from any public API.
- VsdxShapeNode now carries resolved text runs, text-box transform, and
  text layout alongside the existing geometry/paint data.

Adds VsdxTextParsingTests, VsdxTextStyleTests,
VsdxTextBoxPositioningTests, VsdxTextLayoutTests, VsdxTextRenderTests,
and VsdxFixtureTextResolutionTests (real-fixture coverage), growing the
Vsdx test suite from 201 to 297 passing tests.

Deferred to later milestones: connectors/glue-points and group/nested-
shape recursive composition (Milestone 5/6), themes (Milestone 6), and
the public Render API / pixel painting to a Surface (Milestone 7).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 5: connector/glue-point routing

Implements connector endpoint resolution and arrowhead rendering support
for DemaConsulting.CanvasNet.Vsdx, per the 9-milestone VSDX plan.

- Parse <Connects> sections per page into VsdxConnect records (connector
  shape ID, endpoint, target shape ID, target cell, connection-point
  index), associating connector shapes with their glued targets for
  future use (e.g. hit-testing). Dangling/unresolved targets are
  tolerated, not thrown.
- Endpoint-resolution strategy CONFIRMED against real fixture evidence:
  trust the pre-baked BeginX/BeginY/EndX/EndY V= cell values already
  resolved by Milestone 3's 1-D transform path, rather than dynamically
  re-solving glue-point geometry. Verified across three real connector
  fixtures (davehoward-test4-connectors.vsdx, jgreywolfvsdxjs-connectors.vsdx,
  jgreywolfvsdxjs-diagramwithstyles.vsdx): every connector's Begin/End
  cells carry a literal numeric V even when F= is a formula such as
  _WALKGLUE(...) or PAR(PNT(...)), matching the format reference's
  guidance that Visio bakes current glue coordinates at save time.
- Resolve BeginArrow/EndArrow/BeginArrowSize/EndArrowSize cells through
  the existing StyleSheet-chain-then-direct-override precedence
  (reusing the line-cell resolver), producing a VsdxArrowhead
  descriptor (style + size) per endpoint. Implemented a conservative
  style subset (None/OpenArrow/Arrow/Stealth/Circle/Diamond) with
  graceful degrade-to-None for any unrecognized or non-numeric index,
  never throwing. A real fixture (BeginArrow=13) exercises this
  degrade path directly.
- New resolved-connector model types: VsdxConnect, VsdxConnectEndpoint,
  VsdxConnectorEndpoints, VsdxArrowhead, VsdxArrowheadStyle. Wired into
  VsdxShapeNode (Connects, ConnectorEndpoints) and VsdxResolvedPaint
  (BeginArrowhead, EndArrowhead). Still no pixel painting to Surface.
- Added VsdxDocument.Connects.cs and VsdxDocument.Arrowheads.cs
  following the existing partial-class-per-concern convention.
- Tests: VsdxConnectsParsingTests, VsdxGluePointResolutionTests,
  VsdxArrowheadResolutionTests, VsdxConnectorFixtureTests (+20 tests;
  297 -> 357 aggregated across net8.0/net9.0/net10.0, all green).
- Updated docs/reqstream/canvas-net-vsdx/vsdx-document.yaml wording for
  ArrowheadRendering to reflect degrade-never-throw behavior.

Deferred to later milestones: group/nested-shape recursion and
theme/built-in-palette resolution (Milestone 6); public Render API and
pixel painting of resolved connectors/arrowheads to Surface
(Milestone 7).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 6: Theme color resolution and group/nested-shape transform composition

- Add VsdxTheme / VsdxDocument.Theme.cs: lazy, non-throwing parsing of visio/theme/themeN.xml
  color scheme (12 RGBA slots), with graceful fallback when no theme part is present.
- Wire THEMEVAL(slot)-style cell formulas into the existing color resolution pipeline
  (VsdxColorPalette.cs, VsdxDocument.Paint.cs, VsdxDocument.Styles.cs) alongside the
  existing explicit RGB and FillPattern 0/1 paths.
- Add VsdxDocument.Groups.cs: recursive <Shapes> group/nested-shape resolver that composes
  each child shape's local transform with its parent group's transform to arbitrary nesting
  depth, with a depth-budget and resolved-shape-count guard throwing InvalidDataException on
  pathological/cyclic structures.
- Extend VsdxShapeNode with a Parent reference to support the composed shape tree; rewire
  VsdxDocument.Shapes.cs to use the new recursive resolver.
- Add VsdxThemeResolutionTests.cs and VsdxGroupResolutionTests.cs, grounded in real fixtures
  (jgreywolfvsdxjs-* for theme parts, davehoward-test10-nested-shapes.vsdx and
  davehoward-test3-house.vsdx for group nesting / Master-driven group children), plus
  synthetic tests for the no-theme fallback, canonical theme-slot resolution, and the
  depth/count budget exception paths that are unreachable via any real fixture.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 7: Public Render API integration

Implement VsdxDocument.Render(...) public API: page-indexed rendering to
a Surface via width/height and DPI overloads, with VsdxRenderOptions for
background color control (default opaque white). Walk the resolved
shape tree (including nested groups from Milestone 6) in document
order, accumulating page-to-pixel coordinate transforms via the new
VsdxShapeTransform.ToPageMatrix(), and dispatch each shape to geometry
fill/stroke painting (Milestone 3), text layout painting via
PaintTextLayout (Milestone 4, now wired in for the first time), and
connector line + arrowhead painting (Milestone 5) using the new
VsdxArrowheadGeometry local-space arrowhead builder.

Add end-to-end rendering regression tests covering shape-only, text,
connector, and group fixtures, plus synthetic unit tests for argument
validation, background defaults, DPI consistency, document-order
z-ordering, lazy shape-tree caching, and arrowhead painting. Link test
coverage to the pre-registered reqstream requirement IDs for the public
render API.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 8: promote Vsdx fixtures into test project

Move the 14 .vsdx fixtures, README.md, and the two LICENSE files out of
the test/DemaConsulting.CanvasNet.Vsdx.Tests.Fixtures-STAGING staging
folder into an in-project test/DemaConsulting.CanvasNet.Vsdx.Tests/VsdxFixtures
folder, mirroring the sibling Pptx package's PptxFixtures convention
exactly. Update the Vsdx test project's .csproj to use the same bare
<None Include="VsdxFixtures\**"> item form as Pptx, remove the
Staging Note section from the moved README.md, and update
.reviewmark.yaml's stale STAGING path reference on the
CanvasNetVsdx-VsdxDocument review-set. The STAGING folder is deleted
entirely now that all fixture-dependent tests pass from the new
location (confirmed: AppContext.BaseDirectory-based fixture resolution
required no code changes).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 8: backfill reqstream traceability, verification docs, and coverage gap tests

Reqstream traceability backfill (docs/reqstream/canvas-net-vsdx.yaml and
docs/reqstream/canvas-net-vsdx/vsdx-document.yaml): fix the drifted
tests: entries so every requirement ID references a real,
namespace-qualified, currently-passing test method name. Correct
requirement text for FillPatternDeferral (CanvasNetVsdx-ColorFill /
CanvasNetVsdx-VsdxDocument-FillPatternDeferral): the originally-planned
behavior (throw VsdxUnsupportedFeatureException on unsupported fill
patterns) was never implemented; the actual, already-approved behavior
is graceful degradation to solid fill, and the requirement text/title
now describe that - no new exception type was implemented (that would
be scope creep).

Two additional drift corrections discovered while writing this
milestone's own test coverage (documentation-only, no production code
changed, analogous to and consistent with the plan-authorized
FillPatternDeferral fix):
- GeometryUnrecognizedRowSkip: EllipticalArcTo/NURBSTo were claimed to
  convert to approximating Bezier path segments; they are actually
  skipped like any other unrecognized geometry row.
- ColorTableResolution: a document-level Colors/FaceNames table was
  claimed to be parsed and resolved against; no such element is parsed
  anywhere in VsdxDocument's source.
Both docs/design/canvas-net-vsdx.md and
docs/design/canvas-net-vsdx/vsdx-document.md are corrected to match,
each with a 'Correction note (Milestone 8)' annotation.

Verification documentation: replace the TODO stubs in
docs/verification/canvas-net-vsdx.md (system-level, 25 scenario
subsections) and docs/verification/canvas-net-vsdx/vsdx-document.md
(unit-level, 35 requirement-keyed subsections) with real test-strategy,
coverage-summary, and traceability content, mirroring the Pptx
sibling's structure and level of detail.

New test/DemaConsulting.CanvasNet.Vsdx.Tests/VsdxSystemIntegrationTests.cs:
25 test methods (24 Fact + 1 Theory over all 14 real fixtures), one per
system-level requirement, mirroring PptxSystemIntegrationTests.cs's
pattern, including a true end-to-end render-every-page-of-a-real-fixture
scenario.

Close the 5 concrete coverage gaps identified in the plan:
- Master-resolution caching (VsdxMasterInheritanceTests.cs)
- EllipticalArcTo/NURBSTo geometry rows skip tolerantly
  (VsdxGeometryTests.cs)
- Geometry-row replace-by-IX merge semantics
  (VsdxMasterInheritanceTests.cs)
- StyleSheet ID-0 ('No Style') chain termination
  (VsdxStyleResolutionTests.cs)
- Color-table resolution: built-in palette index and direct hex literal
  (new VsdxColorPaletteTests.cs)

Narrow, documented source change: VsdxDocument.Masters.cs's
ResolveMasterShape changed from private to internal (zero behavior
change) solely to allow the new caching-coverage test to call it
directly and assert the parsed master content tree is reused by
reference across repeated resolutions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 9: final local review and documentation reconciliation

- Removed stale STAGING fixture-path reference in the vsdx-document.sysml
  comment (pre-dated Milestone 1; test fixtures were promoted out of staging
  in Milestone 8).
- Reconciled docs/sysml2/model/canvas-net-vsdx.sysml and its vsdx-document
  subfolder descriptive text with the Milestone 8 design-doc corrections:
  no document-level <Colors>/<FaceNames> table is parsed (only the built-in
  palette, literal hex, and the Themed sentinel), EllipticalArcTo/NURBSTo/
  InfiniteLine rows are tolerantly skipped rather than resolved into Path
  segments, and no Drawing.Gradient type is referenced (every non-solid
  FillPattern degrades to solid fill).
- Refreshed the vsdx-document.sysml sourceRef/testRef comments from a stale
  pre-implementation planning-skeleton file list to the actual, current
  source/test file layout.
- Refreshed the CanvasNetVsdx-VsdxDocument .reviewmark.yaml path comments
  from '(planned; not yet created)' to match the completed implementation.
- Added a DemaConsulting.CanvasNet.Vsdx Features bullet and Installation
  section to README.md, mirroring the existing Svg/Pdf/Charts/Pptx entries
  (previously absent despite the package being fully implemented).

Full-repo validation: pwsh ./build.ps1 (11,127/11,127 tests passing across
net8.0/net9.0/net10.0) and pwsh ./lint.ps1 (markdownlint-cli2, cspell,
yamllint, dotnet format, reqstream, versionmark, reviewmark, sysml2tools)
both exit 0. dotnet reviewmark --enforce passes with no required-review
violations; the Vsdx-document review set's remaining coverage-warning gap
(*.Tests.csproj project file) is a pre-existing, repo-wide pattern shared
by every sibling test project, not a Vsdx-specific gap, and is left
untouched per scope discipline. An independent code-review pass over the
full src/DemaConsulting.CanvasNet.Vsdx and
test/DemaConsulting.CanvasNet.Vsdx.Tests diff (range 1b97b3e..HEAD) found
no defects: exception contracts, geometry/transform math, Master/StyleSheet
resolution, group-recursion budgets, resource disposal, thread-safety, and
public-API XML-doc coverage were all confirmed consistent with the
documented design.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 10: Bug #1 - exclude deleted shape stubs and suppress NonPrinting self-paint

Fixes two distinct root causes of spurious/duplicate text confirmed against
the apache/poi test-data corpus's real-world fixtures, verified visually
against Visio COM reference PNGs via the external smoke-test harness:

- VsdxDocument.Shapes.cs: ParseShapeElements now excludes a group child
  <Shape Del="1"> stub (shape-level deletion, distinct from the already-
  handled row-level geometry Del marker) from the resolved shape tree.
  Confirmed, via a controlled git-stash A/B pixel diff, to eliminate
  60973.vsdx's duplicate "OOB: N/A"/"[B] Lo1: N/A"/"TS: N/A" placeholder
  text.
- VsdxDocument.Render.cs: RenderShapeRecursive now skips a shape's own
  self-paint (fill/stroke/text/arrowheads) when its effective NonPrinting
  cell resolves true, while still recursing into its children. Confirmed
  to eliminate 44501b.vsdx's spurious "Activity" heading.

Adds VsdxDocument_GroupChildDeletedStub_ExcludedFromResolvedShapeTree to
VsdxGroupResolutionTests.cs covering the Del-stub exclusion with a small
synthetic fixture (the NonPrinting-suppression test rides along with
Bug #2's commit in VsdxRenderFixtureTests.cs, since both new tests were
appended as one contiguous diff hunk).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 10: Bug #2 - visible hairline stroke floor plus transform-cell merge fix for missing connector lines

Fixes 60973.vsdx's missing connector-line defect, confirmed via the
external smoke-test harness against the Visio COM reference PNG. This
required two complementary changes beyond the plan report's original
stroke-width-only diagnosis, discovered via root-cause iteration after
the stroke-width floor alone did not resolve the visual discrepancy:

- VsdxDocument.Render.cs: PaintShapeGeometry's PaintStroke call now
  applies a MinStrokeWidthPixels = 1f floor to the resolved stroke width,
  so a sub-pixel LineWeight (confirmed ~0.5px at 150 DPI on this
  fixture's connectors) still rasterizes a visible hairline instead of
  vanishing.
- VsdxDocument.CellMerge.cs: discovered during deeper root-cause
  investigation that the generic Master-wins merge rule was also
  collapsing connector shape 802's resolved position to its Master's
  own small, unrelated template-local corner, because its own correct,
  baked PinX/PinY/LocPinX/LocPinY cells were marked F="Inh". Adds
  TransformCellNames/PreferInstanceTransformCells so an instance's own
  cell for the nine transform cells (PinX/PinY/Width/Height/LocPinX/
  LocPinY/Angle/FlipX/FlipY) always wins over the Master's same-named
  cell, even when marked Inh - narrowly scoped to exactly these cells,
  since every other (genuinely shared) style cell keeps the original
  Master-wins-over-Inh rule.

Adds MasterInheritance_InstanceInhMarkedTransformCell_PreferredOverMasterValue
and MasterInheritance_InstanceInhMarkedNonTransformCell_StillDefersToMasterValue
to VsdxMasterInheritanceTests.cs, and Render_SubPixelLineWeight_StillPaintsVisibleHairlineStroke
plus Render_NonPrintingShape_SkipsOwnFillButStillRendersChildren (Bug #1(b)'s
own test, appended in the same diff hunk) to VsdxRenderFixtureTests.cs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 10: Bug #3 - honor FillForegndTrans/LineColorTrans transparency cells

Fixes 60973.vsdx's "Virtual Devices" container rendering fully opaque and
obscuring its own children, confirmed via the external smoke-test harness
against the Visio COM reference PNG.

VsdxDocument.Paint.cs's ResolvePaint now resolves FillForegndTrans/
LineColorTrans through the same FillStyle/LineStyle StyleSheet chain
already used for FillForegnd/LineColor, and a new ApplyTransparency
helper modulates the resolved FillColor/StrokeColor's alpha channel by
the parsed 0..1 transparency fraction. Confirmed via direct pixel
sampling: the container's resolved fill (127,127,127,153) blends to
(178,178,178) over white, matching the rasterizer's own alpha
compositing and the reference PNG's lighter, mostly-see-through frame.

Adds StyleResolution_FillForegndTrans_ReducesResolvedFillAlpha and
StyleResolution_LineColorTrans_ReducesResolvedStrokeAlpha to
VsdxStyleResolutionTests.cs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 10: companion documentation for the 3 rendering-fidelity fixes

Updates the companion design/requirements/verification artifacts for this
milestone's 3 code fixes plus one doc-only note, per the originating plan
report's companion-artifact table:

- docs/design/canvas-net-vsdx/vsdx-document.md: documents the shape-level
  Del="1" exclusion, the NonPrinting self-paint suppression, the stroke
  hairline floor, the *Trans alpha resolution, and the CellMerge.cs
  transform-cell-preference refinement discovered during this milestone's
  own Bug #2 root-cause investigation (a deviation beyond the plan
  report's original stroke-width-only diagnosis).
- docs/design/canvas-net-vsdx.md: records test.vsdx's header-bar/star as
  a confirmed real-world sample of the already-documented Themed
  neutral-fallback limitation - a documentation-only addition, no code
  change, per the plan report's own non-bug determination.
- docs/reqstream/canvas-net-vsdx/vsdx-document.yaml: adds requirement
  entries for deleted-shape exclusion, NonPrinting suppression, the
  minimum visible stroke width, transparency honoring, and extends the
  existing CellMerge requirement for the transform-cell exception.
- docs/verification/canvas-net-vsdx/vsdx-document.md: adds verification
  entries for all 5 new/extended behaviors, tracing to their new unit
  tests and to the external smoke-test visual-comparison evidence.
- .cspell.yaml: adds 'unconsulted', a genuine technical term (following
  this file's own existing un-prefixed-word precedent) needed by the new
  design/reqstream/verification prose and VsdxDocument.Paint.cs's own
  ApplyTransparency remarks.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* vsdx milestone10 retry1: narrow CellMerge transform-cell exception to 1-D shapes (Finding #3)

Scope the PreferInstanceTransformCells exception in VsdxDocument.CellMerge.cs to
1-D (connector) shapes only, reusing the existing BeginX/EndX detection
convention from VsdxDocument.Groups.cs, rather than applying it to every
shape type including 2-D shapes where the Master's own value can be
legitimately inherited.

- Updated the existing 1-D test fixture
  (MasterInheritance_InstanceInhMarkedTransformCell_PreferredOverMasterValue)
  to include BeginX/BeginY/EndX/EndY cells so it still exercises the
  narrowed 1-D-only code path correctly.
- Added MasterInheritance_2DShapeInhMarkedTransformCell_DefersToMasterValue
  proving a legitimate 2-D Master-inheritance-of-position scenario still
  resolves correctly post-guard.
- Restored 2 assertions silently dropped from
  MasterInheritance_InstanceGeometryRowWithoutDel_ReplacesMasterRowAtMatchingIndex
  in a prior milestone 10 commit (Finding #4).
- Re-verified via smoke-test harness that 60973.vsdx's connector lines
  (Bug #2 fix) are unaffected by the narrowing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* vsdx milestone10 retry1: add full-transparency locking tests (Finding #1)

Independently re-verified quality's claimed github260.vsdx visual
regression (a LineColorTrans="1" shape allegedly losing a border shown by
the Visio reference) by pixel-scanning the actual Visio-reference PNG at
the shape's own bounding box: no border/stroke ring exists in the
reference at all - it is a pure, borderless fill. The current invisible
rendering of that shape is caused by an unrelated, already-documented
NURBS-skip geometry limitation (confirmed via raw XML inspection and via
'git show f9c46f9 --stat', which touched only Paint.cs), not by the Bug #3
transparency fix.

No code change was made to VsdxDocument.Paint.cs. Added
StyleResolution_LineColorTrans_FullyTransparent_HasNoVisibleStroke and
StyleResolution_FillForegndTrans_FullyTransparent_HasNoVisibleFill to lock
in the existing, re-verified-correct V="1" (fully transparent) behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* vsdx milestone10 retry1: correct documentation for Findings #1-#3 (docs)

- docs/design/canvas-net-vsdx/vsdx-document.md: document the 1-D-only
  scoping of the CellMerge transform-cell exception (Finding #3), add the
  Finding #1 re-verification note to the transparency resolution
  narrative, and clarify the Del="1" stub-exclusion fix addresses the
  stale-stub clutter text only, not the AIRttt/AIRuuu defect (Finding #2).
- docs/design/canvas-net-vsdx.md: cite github260.vsdx's circle as a second
  confirmed instance of the NURBS-skip limitation, and add a new, named
  deferred-limitation bullet documenting the AIRttt/AIRuuu defect as a
  distinct, pre-existing, out-of-scope issue (Finding #2).
- docs/reqstream/canvas-net-vsdx/vsdx-document.yaml: update the CellMerge
  entry's title/justification/tests for the 1-D-only scope, update the
  TransparencyResolution entry's justification/tests with the Finding #1
  re-verification evidence, and clarify the DeletedShapeExclusion entry's
  justification to distinguish the Del="1" fix from the AIRttt/AIRuuu
  defect.
- docs/verification/canvas-net-vsdx/vsdx-document.md: mirror the same
  scope corrections and new test-scenario names in the verification
  narrative for CellMerge, DeletedShapeExclusion, and
  TransparencyResolution.
- .cspell.yaml: add genuine proper nouns/fixture identifiers surfaced by
  the above documentation (AIRttt, AIRuuu, Lucidchart, lucidchart,
  borderless) so `lint.ps1` passes cleanly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 11: cell-by-cell geometry-row merge (Group A: Findings #2, #3-frame contribution)

Fixes MergeGeometryRows (VsdxDocument.CellMerge.cs) to merge a matched instance/Master geometry
row cell-by-cell instead of whole-row replacement, so an instance row overriding only a subset
of its own cells still inherits the Master row's other, un-overridden cell values rather than
collapsing them to CLR defaults.

Adds MasterInheritance_GeometryRowPartialOverride_MergesCellByCellNotWholesale regression test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 11: EllipticalArcTo/ArcTo geometry conversion (Group B: Finding #1)

Adds EllipticalArcTo (general ellipse/arc) and ArcTo (circular, bow-height-derived) geometry-row
conversion to VsdxDocument.Geometry.cs's ResolveGeometry, producing an ArcTo path command that
reaches the row's own destination point instead of tolerantly skipping the row. A zero-bow ArcTo
row (A=0) degrades to a plain LineTo rather than throwing or producing a degenerate zero-radius
arc.

Adds Geometry_EllipticalArcToRow_ConvertsToArcReachingDestination,
Geometry_ArcToRow_ConvertsToArcWithBowDerivedRadius, and
Geometry_ArcToRow_ZeroBow_DegradesToLineTo, replacing the now-outdated
Geometry_EllipticalArcToAndNurbsToRows_AreSkippedWithoutThrowing test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 11: HideText suppression (Group C: Finding #8)

Adds HideText cell resolution (VsdxDocument.Paint.cs) and consults it in VsdxDocument.Render.cs
to skip only a shape's own text-run paint when HideText resolves truthy, leaving its own
fill/stroke/arrowheads and its children's painting unaffected - distinct from the broader
NonPrinting suppression which also skips fill/stroke.

Adds Render_ShapeWithHideTextCell_SuppressesOwnTextButNotFillOrStroke regression test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 11: fld element text parsing (Group D: Finding #3 label sub-symptom)

Adds a <fld> (Field reference) element case to BuildRawRuns (VsdxDocument.Text.cs) so a field's
own nested, save-time-cached text content is parsed as a literal run instead of being silently
dropped; a <fld> interleaved between plain text segments merges into the surrounding run exactly
like a plain XText node would.

Adds TextParsing_FldElement_NestedTextContentParsedAsLiteralRun and
TextParsing_FldElementInterleavedWithText_MergesIntoSurroundingRun regression tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 11: arrowhead index 4/254 mapping (Group E: Finding #5)

Maps confirmed-in-use arrowhead index 254 to a new hollow/unfilled-triangle VsdxArrowheadStyle
(VsdxArrowhead.cs, VsdxArrowheadGeometry.cs, VsdxDocument.Arrowheads.cs), found via a full-
document scan of the poi-fixtures corpus, and adds coverage for index 4 (existing solid-arrow
style family). Index 254 previously silently degraded to the unrecognized-index plain-line-end
fallback.

Adds ArrowheadResolution_EndArrowIndex4_ResolvesToArrowStyle and
ArrowheadResolution_EndArrowIndex254_ResolvesToHollowTriangleStyle regression tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Vsdx Milestone 11: companion documentation updates for 5 real-world fixes

Updates design docs (canvas-net-vsdx.md, canvas-net-vsdx/vsdx-document.md), reqstream
requirements (vsdx-document.yaml), verification entries (vsdx-document.md), and the sysml2
model (vsdx-document.sysml) to document:
- EllipticalArcTo/ArcTo geometry conversion (new ArcGeometryResolution requirement)
- cell-by-cell geometry-row merge correction (GeometryRowMerge)
- fld element text parsing (TextRunParsing)
- arrowhead index 4/254 mapping (ArrowheadRendering)
- HideText suppression (new HideTextSuppression requirement)

Also documents, as confirmed-but-deferred limitations (no code change):
- the shared 2-D shape transform-cell-merge-exclusion root cause behind Findings #3's frame/
  label and #6's residual text-overlap symptoms
- Finding #4's partial-fix nuance (triangle/bar shape fixed; unrelated pre-existing text-overlap
  position defect persists)
- Finding #5's arrowhead-size-proportional-to-stroke-width caveat as a pre-existing, unchanged
  design limitation
- a second confirmed real-world instance of the documented neutral-theme-fallback limitation
  (60973.vsdx's Nwwww/Neeeee rack-slot bars)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(vsdx): unconditionally overlay instance geometry-row cells over Master merge

Milestone 11 quality-retry (retry 1): fixes a confirmed geometry-row
cell-merge regression that fused/mis-sized ellipses in 60489.vsdx (Shape
114/122) and produced a floating, mispositioned, wrong-colored triangle in
44501e.vsdx (Shape 64, Master 28). Root cause: MergeGeometryRow applied the
generic "instance literal wins, else Master's cell" rule to geometry-row
coordinate cells, but these are almost always cached as F="Inh"
(formula-derived from Width/Height), so the generic rule treated them as
absent and substituted the Master's own differently-scaled cached value.

Fix: generalize PreferInstanceTransformCells to PreferInstanceCells(merged,
instanceCells, names), add an ArrowCellNames array (BeginArrow/EndArrow/
BeginArrowSize/EndArrowSize) applied through the existing 1-D-only gate
alongside TransformCellNames, and have MergeGeometryRow unconditionally
overlay the instance row's own present cells (literal or Inh) over the
generic merge result, preserving Master fallback only for cells genuinely
absent from the instance row.

While adding regression coverage for the ArrowCellNames extension, a new
test caught that PreferInstanceCells's original design (swapping which
VsdxCell wins, preserving its Formula) silently failed to affect arrowhead
resolution at all when overlaying an Inh-marked cell: ResolveLineCellValue/
ResolveFillCellValue (VsdxDocument.Paint.cs) use TryGetLiteral, which treats
any Inh-marked cell as "defer to the StyleSheet chain" rather than an
override. Fixed by writing the overlaid cell back as a literal
(Formula: null) - confirmed safe for TransformCellNames too, since its
consumer never inspects Formula.

Investigated the related arrowhead-rendering quality finding (44501e.vsdx
page 1) against the real fixture: the floating triangle is the same
geometry-row regression (fixed above); the separate "dash stub" artifacts
are a pre-existing, deliberate architecture boundary (arrowhead-decoration
painting is gated on 1-D ConnectorEndpoints; the dash-producing shapes are
ordinary 2-D Shape-typed icons) - an accepted, out-of-scope limitation, not
force-fixed.

Added regression tests:
- MasterInheritance_GeometryRowAllCellsInhButInstanceSizeDiffers_InstanceCoordinatesWin
- MasterInheritance_GeometryRowCellGenuinelyAbsentOnInstance_StillFallsThroughToMaster
- ArrowheadResolution_InstanceInhMarkedArrowCell_PreferredOverMasterValue
- ArrowheadResolution_2DShapeInhMarkedArrowCell_DefersToMasterValue

Verified via the external smoke-test harness: 60489.vsdx page 0 now renders
two distinct, correctly-sized ellipses; 44501e.vsdx page 1 no longer shows
the floating triangle and continues to render the EndArrow=254 hollow
triangle correctly; no regressions in 44501d.vsdx, 44501a.vsdx, 60973.vsdx
(pages 0-3), test.vsdx, and github260.vsdx.

dotnet build CanvasNet.slnx: 0 warnings, 0 errors.
Vsdx test project: 657/657 passed.
Full solution: 11193/11193 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs(vsdx): update companion docs for Milestone 11 quality-retry (retry 1)

Updates the reqstream YAML, design docs, and verification doc to match the
actual, evidence-based outcome of this quality-retry cycle rather than the
plan's hypothesis:

- GeometryRowMerge: corrected wording to the verified unconditional-overlay
  rule; cites both the 60489.vsdx ellipse and 44501e.vsdx Shape-64 triangle
  evidence; lists the 2 new geometry regression tests.
- CellMerge: documents the ArrowCellNames generalization and the
  literalization fix discovered via testing; lists the 2 new arrow
  regression tests.
- ArrowheadRendering: documents the investigation's conclusion - the
  floating triangle is fixed via the geometry-row fix; the dash-stub
  artifacts are characterized as an accepted, pre-existing, out-of-scope
  1-D-only rendering-architecture limitation, not a regression and not
  force-fixed. No requirement scope change.

docs/design/canvas-net-vsdx.md gains a new "Milestone 11's quality-retry
cycle (retry 1)" list entry; docs/design/canvas-net-vsdx/vsdx-document.md
corrects the geometry-row-merge cell-by-cell description and documents the
fix's rationale; docs/verification/canvas-net-vsdx/vsdx-document.md adds
matching verification notes with before/after visual-confirmation evidence
for GeometryRowMerge, CellMerge, and ArrowheadRendering.

pwsh ./fix.ps1 and pwsh ./lint.ps1 both ran clean after these changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix 2-D group-child transform-cell merge gap (Milestone 12 Finding #3)

MergeCells now applies the TransformCellNames (PinX/PinY/Width/Height/
LocPinX/LocPinY/Angle/FlipX/FlipY) instance-wins overlay when the merged
shape is a Group child (isGroupChild: parent is not null), independent of
the existing 1-D-only test. This fixes a stale Master template-default
transform cell incorrectly winning over a resized Group child instance's
own correct, F="Inh"-marked value.

Confirmed via the external smoke-test harness against the real-world
corpus: Test_Visio-Some_Random_Text.vsdx's "View" table shape no longer
over-wraps its text (TxtWidth no longer falls back to a stale Master
Width), test.vsdx's header bar now sits flush at the top of its group,
and 44501e.vsdx's "-has" connector label now resolves to its own correct
external position. The top-level (non-group-child) 2-D shape exclusion
is untouched and remains locked by
MasterInheritance_2DShapeInhMarkedTransformCell_DefersToMasterValue.

Adds synthetic-fixture tests:
- MasterInheritance_2DGroupChildInhMarkedTransformCell_PrefersInstanceValue
- TextBox_GroupChildInhMarkedWidthDiffersFromMaster_TxtWidthReflectsInstanceWidth
- TextLayout_CorrectedGroupChildWidth_FitsOneLineWhereStaleMasterWidthOverWrapped

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Document Milestone 12 group-child cell-merge fix and corpus findings

docs/design/canvas-net-vsdx.md: revise the Milestone 11 "2-D shape
transform-cell-merge exclusion" deferred-limitation bullet to record it
is now fixed for the group-child case, while 60973.vsdx's rack-frame
remains invisible for a separate, already-documented Themed-color/
gradient-fill reason, not a transform-cell problem. Adds a new bullet
extending the Milestone 4 neutral-theme-fallback deferral with this
milestone's exhaustive cross-fixture Themed-color investigation
(test_text_extraction.vsdx/60489.vsdx needing unfilled vs. 60973.vsdx
needing filled with real theme colors; no safe discriminator found).
Documents Finding #5 (UML class-name text crowding the compartment's own
top border in 44501d.vsdx/44501e.vsdx) as deferred/unresolved with
precise follow-up scope.

docs/verification/canvas-net-vsdx/vsdx-document.md and
docs/reqstream/canvas-net-vsdx/vsdx-document.yaml: update the
CanvasNetVsdx-VsdxDocument-CellMerge requirement/verification entry to
describe the group-child transform-cell carve-out, list the new tests,
and record the real-world-corpus confirmation evidence.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Strengthen HideText suppression test to scan full rect for glyph ink

Code review flagged that Render_ShapeWithHideTextCell_SuppressesOwnTextButNotFillOrStroke
only sampled a single interior pixel, which would still pass even if the
HideText suppression were removed entirely. Now scans every pixel in the
shape's full rendered bounds and asserts each matches the pure fill color,
proving no glyph ink was painted anywhere.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs: fix formal-review traceability/documentation gaps for Vsdx

Addresses all Critical/High/Medium findings (and one trivial Low) from the
7 formal-review reports run against the Vsdx work on this branch. No C#
production source behavior changes; one new unit test added to close a
coverage gap, plus a cspell-ignore fix in an existing test file.

Purpose review (F9, High):
- Add a full `VsdxDocument` User Guide API Reference section and a new
  `Example 16` to docs/user_guide/introduction.md, mirroring the existing
  PdfDocument section structure/tone.

Decomposition review (DES-01/DES-02):
- Reword the self-contradictory ChartModel unit-count sentence in
  docs/design/introduction.md (two units, not "a single unit... and a
  ChartRenderer unit").
- Add the missing CanvasNetVsdxView.svg diagram embed to
  docs/design/canvas-net-vsdx/vsdx-document.md.

CanvasNetVsdx-Architecture review (DES-02, High):
- Add CanvasNetVsdx to docs/design/introduction.md's Software Structure and
  Folder Layout sections.
- Document 6 existing unit tests (geometry, group-resolution, style,
  render) as system-level Test Scenario coverage for the Milestone 10/11
  capabilities in docs/verification/canvas-net-vsdx.md, since they already
  exercise the full public API end-to-end.

CanvasNetVsdx-Design review:
- Link the 6 orphaned Milestone 10/11 unit requirements under their system
  parent requirements in docs/reqstream/canvas-net-vsdx.yaml (arc-geometry
  resolution, deleted-shape exclusion, transparency resolution,
  non-printing suppression, minimum stroke width, hide-text suppression).
- Fix the broken MasterShapeInheritance -> CellMerge cross-references and
  the stale "unimplemented"/planning-skeleton header comment in
  docs/reqstream/canvas-net-vsdx/vsdx-document.yaml.

CanvasNetVsdx-Verification review:
- Add the missing Acceptance Criteria section to
  docs/verification/canvas-net-vsdx/vsdx-document.md.
- Add CanvasNetVsdx to docs/verification/introduction.md's Scope section.
- Correct the stale "EllipticalArcTo is never converted" claim in
  docs/verification/canvas-net-vsdx.md (Milestone 11 implemented real
  elliptical-arc geometry resolution).

CanvasNetVsdx-AllRequirements review:
- Covered by the same reqstream/vsdx-document.yaml fixes above.

CanvasNetVsdx-VsdxDocument review (F-1 Medium, F-2/F-3 Low):
- Update docs/design/canvas-net-vsdx/vsdx-document.md's Key Methods entry
  for ResolveEffectiveShape/CellMerge to describe the Milestone 12
  isGroupChild carve-out, and the Data Model section to describe the
  Milestone 11 arrowhead-index 4/254 recognition.
- Add a regression test
  (ArrowheadResolution_2DGroupChildInhMarkedArrowCell_DefersToMasterValue)
  proving the isGroupChild carve-out is deliberately scoped away from
  ArrowCellNames, closing the F-3 coverage gap.

Verified clean with `pwsh ./lint.ps1` (markdownlint, cspell, yamllint,
reqstream, versionmark, reviewmark, sysml2tools) and a full
`pwsh ./build.ps1` run (11,205 tests passed, 0 failed, 0 skipped).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix PR #42 review comments: multi-section fill/line flags, rotation-invariant scale, TxtAngle text rotation, exception accessibility, stale docs, and ApiMark bump

Addresses all 8 GitHub Copilot automated review findings on PR #42:

1. VsdxDocument.Paint.cs: ResolvePaint no longer derives shape-wide HasFill/HasLine
   from only geometry section 0's NoFill/NoLine; shape-wide flags now come purely
   from FillPattern/LinePattern, with per-section suppression handled exclusively
   by PaintShapeGeometry's existing per-section check. Regression test added.

2. VsdxDocument.Render.cs: AverageScale now computes the lengths of the transformed
   basis vectors instead of the raw matrix diagonal, making it rotation-invariant.
   Regression test compares stroke thickness of a rotated vs unrotated line.

3. VsdxDocument.Render.cs/TextLayout.cs/VsdxTextBoxTransform.cs: TxtAngle is now
   composed into a dedicated text-to-pixel transform (ResolveTextToPixelTransform)
   applied at paint time, rotating the text box about its own pin - a contained,
   low-risk fix (approach a). Added a direct unit test of the composed transform.

4. VsdxUnsupportedFeatureException.cs: made public (was internal) to match its own
   documented "callers can distinguish unsupported features" contract and the
   sibling PptxUnsupportedFeatureException's public convention. Confirmed via
   repo-wide search it has zero throw-sites today; kept (not removed) since
   existing approved docs already describe it as intentional future scaffolding.

5. docs/sysml2/model/canvas-net-vsdx.sysml: corrected stale row-support list -
   EllipticalArcTo/ArcTo are implemented (Milestone 11, resolved into real ArcTo
   path commands); only NURBSTo/InfiniteLine/RelCubBezTo/SplineStart/SplineKnot/
   PolylineTo/Ellipse remain genuinely skipped/deferred.

6. docs/sysml2/model/canvas-net-vsdx/vsdx-document.sysml: corrected stale claim
   that Foreign shapes and non-solid FillPattern values throw
   VsdxUnsupportedFeatureException - non-solid fills degrade to solid, and
   Foreign shapes have no distinct Type-based handling at all.

7. VsdxDocument.cs: rewrote the class's stale Milestone-2-era XML remarks (which
   claimed no shape/geometry/style/text/connector parsing existed) to accurately
   describe current capabilities, mirroring PptxDocument's remarks style/tone.
   Also corrected a stale "deferred to Milestone 7" remark on VsdxTextLayout.

8. VsdxTextStyleTests.cs: strengthened the VerticalAlign/margin test, which
   previously only asserted TextLayout was non-null, to assert the resolved
   glyph's actual X/Y origin differs measurably between the StyleSheet-inherited
   default and a shape's own overriding VerticalAlign/LeftMargin cells.

Also bumps DemaConsulting.ApiMark.MSBuild to 0.5.7 across every package in the
repo (previously an inconsistent mix of 0.5.1/0.5.4/0.5.4 on Vsdx) - 0.5.7 picks
up fixes for cref link resolution and type-only cref rendering between 0.5.4 and
0.5.7 that are directly relevant to this repo's heavy XML-doc <see cref> usage.

Full solution build and all 11214 tests pass; lint.ps1 exits 0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix PR #42 review round 2 findings (7 Copilot review comments + 2 formal-review recheck doc gaps)

Addresses all 7 findings from GitHub Copilot's automated review round 2 on
PR #42 (after commit f6e3cf0 fixed the first 8), plus 2 additional
doc-propagation gaps found by a formal-review recheck of that same commit.

1. (High) VsdxDocument.Shapes.cs: ParseShapeElement/ParseShapeElements now
   enforce the same MaxGroupNestingDepth/MaxResolvedShapeCount budget during
   the parse pass itself (not only during ResolveShapeRecursive), so a
   deeply nested or very wide untrusted page/master can no longer exhaust
   the call stack or memory before either safety budget is reached. Added
   direct ParseShapeElement unit tests (bypassing the resolver entirely) for
   both the depth and count budgets.
2. (Medium) VsdxDocument.Groups.cs / VsdxTextRun.cs / VsdxDocument.Text.cs:
   added VsdxRawText.HasElement so an explicit, empty <Text/> element is
   distinguished from a genuinely absent one; the Master-text fallback now
   checks HasElement instead of Runs.Count, so an instance's intentional
   empty <Text/> no longer reintroduces the Master's text. Added regression
   tests for both the empty-and-present and absent cases.
3. (Medium) VsdxDocument.Pages.cs: ParseSizeCellEmu now catches the EMU
   conversion's OverflowException for a pathologically large page
   dimension and re-throws the documented InvalidDataException, preserving
   the public error contract. Added a regression test with a 1e300-inch
   PageWidth asserting InvalidDataException (not OverflowException).
4. (Low) NamespaceDoc.cs: replaced the stale "implementation is in
   progress" remarks with an accurate description of the shipped
   VsdxDocument/Render functionality and its known limitations (FillPattern
   edge cases, one unsupported gradient pattern, minor UML text padding).
5. (Low) VsdxDocument.TextRender.cs: corrected the class summary - the text
   painter is wired into RenderShapeRecursive via PaintTextLayout, not
   "not yet wired into any public API".
6. (Low) VsdxShapeNode.cs: removed the incorrect "only for page-level
   shapes" qualifier from the EffectiveCells doc - ResolveShapeRecursive
   assigns it for every descendant shape too.
7. (Low) VsdxFixtures/README.md: confirmed the existing 14-file
   davehoward-*/jgreywolfvsdxjs-* provenance list/count is accurate, and
   added a clarifying note that the separate Apache-POI-derived
   12-file/16-page corpus and smoke-test scratch tooling mentioned in the
   PR description were never checked into this repository.
8. Removed the stale "VsdxUnsupportedFeatureException is thrown for
   Foreign shapes / non-solid FillPattern" claim from VsdxDocument.cs's own
   class remarks, docs/design/canvas-net-vsdx.md, and
   docs/design/introduction.md, matching the wording already corrected in
   docs/sysml2/model/canvas-net-vsdx/vsdx-document.sysml by commit f6e3cf0:
   neither case is distinctly branched on or throws today.
9. Documented the TxtAngle/text-box-rotation fix
   (ResolveTextToPixelTransform) from f6e3cf0: added a Key Methods entry in
   docs/design/canvas-net-vsdx/vsdx-document.md, a new requirement
   (CanvasNetVsdx-VsdxDocument-TextBoxRotationRendering) in
   docs/reqstream/canvas-net-vsdx/vsdx-document.yaml, and a matching
   verification section in docs/verification/canvas-net-vsdx/vsdx-document.md.

pwsh ./build.ps1: Build succeeded, 0 warnings, 0 errors; 11229/11229 tests
passed across all target frameworks.
pwsh ./lint.ps1: exit code 0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Link orphaned TextBoxRotationRendering requirement under CanvasNetVsdx-Render

The CanvasNetVsdx-VsdxDocument-TextBoxRotationRendering unit requirement
added for the TxtAngle text-box rotation fix (PR #42 round 1) was never
added as a child of its system-level parent, causing reqstream --enforce
to report it as orphaned (unreachable from any system/quality-tagged
requirement).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Malcolm Nixon <Malcolm.Nixon@hiarc.inc>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants