Status: accepted. P6_010 adds a bounded, deterministic
plotter-base-a0 SVG serializer to the packaged Monkey native
process. It consumes the four unchanged, frozen Phase-5 Plotter-IR
contracts. It does not replace the established public Python SVG wrappers
or the Cruncher compositor; that no-fallback handoff belongs to P6_020.
The closed handshake a0 remains exactly
["design-facts"]. A separate handshake-a1 advertises
design-facts then render-svg. New strict generated
roots describe the a1 handshake and SVG request/result; failures reuse
NativeErrorA0. The request explicitly discriminates footprint,
symbol, board, or schematic and embeds that frozen document. No Phase-5
schema or union is widened.
A positive explicit viewport is mandatory. Footprint, symbol, and board documents do not own the live source-model bounds used by legacy wrappers. A schematic viewport must exactly equal its zero-origin document canvas. This prevents an A4 fallback or unreviewed text-dependent bbox inference.
plotter-base-a0 is presentation-neutral source SVG: a white
background, nanometre viewBox, explicit millimetre extent, deterministic
ordering, escaped record/block identifiers, and no raw XML injection.
Typed render-cache polygons become paths. Uncached text becomes semantic
SVG text without system-font discovery. Contract-validated image bytes are
retained as data URLs without pixel decoding. Oval pad flashes use KiCad's
rotated stadium geometry rather than an ellipse.
The profile renders every frozen drawable arm and source/block ownership hook. It deliberately performs no layer filtering, live-model bbox, legacy standalone-symbol framing, worksheet/drill presentation reordering, Cruncher enrichment/theming/virtual layers, or arbitrary metadata. Custom pads use their nominal frozen polygons; mask-view expansion and equivalent polygon boolean geometry remain P6_020 provider work.
The producer semantic validator runs before rendering. Preflight bounds records, operations, points, text, encoded images, and aggregate work. A bounded sink independently limits block depth, SVG elements, serialization work, and SVG bytes; transport bounds the larger request and complete result. Before generated decoding or nested document clones, the native transport performs an allocation-free streaming preflight capped at 8,388,608 JSON nodes. Negative dimensions that would create invalid SVG fail closed. Errors publish bounded stderr and no stdout.
No path, project, font, template, layer, or application discovery occurs. XML control characters are rejected, text and attributes are escaped, raw metadata is unavailable, and the result's byte count and SHA-256 are recomputed by Rust and Python before publication.
Focused readable goldens independently lock the renderer semantics that
a serializer-generated hash cannot prove, including embedded-footprint
placement, authored stroke widths, multiline text placement, cached text,
odd-nanometre geometry, ownership attributes, XML safety, and exact element
accounting. On top of those semantic checks,
native_svg_a0_vectors.json covers all 30 frozen documents:
six footprint, two symbol, thirteen board, and nine schematic. Twenty-nine
cases pin a viewport, SVG byte count/hash, and XML tag counts. The remaining
board case pins a fail-closed rejection because its intentionally negative
segment width is valid retained Plotter-IR evidence but cannot be serialized
as valid SVG geometry. Every case pins its source-document hash.
Rust repeats every serialization and proves exact/one-under ceilings for
records, operations, points, text, images, blocks, elements, work, SVG, and
result bytes. L0_063 owns envelopes, L1_038 the real Python-to-Rust process,
and L3_023 the composed gate. The isolated Windows wheel smoke executes a
real render-svg request.
The reusable Windows phase6-native-svg workflow runs those
three Rack owners and is required by both ordinary CI and Monkey release
publication. The Phase-5 freeze and generated bindings are rechecked in the
composed P6_010 gate before the renderer is built.
native_render_svg is a Monkey operation client, but no
existing to_svg, render_ir_to_svg, or Cruncher call
is redirected here. P6_020 must add same-change cross-package tests when
Cruncher consumes the provider and must fail instead of falling back after
that boundary is promoted.
P6_010 closed on 2026-08-17 after three independent reviews. The final warm Rack evidence was L0_063 (17 tests, 0.19 seconds), L1_038 (2 tests, 1.34 seconds), and L3_023 (1 test, 11.05 seconds; 11.92 seconds including Rack orchestration). The full Rust workspace format, check, Clippy, and test gate was also green.