Skip to content

websem: retain complete blend-source domains - #138

Merged
softmarshmallow merged 2 commits into
mainfrom
rung/blend-source-extents
Sep 8, 2026
Merged

websem: retain complete blend-source domains#138
softmarshmallow merged 2 commits into
mainfrom
rung/blend-source-extents

Conversation

@softmarshmallow

@softmarshmallow softmarshmallow commented Sep 8, 2026

Copy link
Copy Markdown
Member

What this unlocks

A rectangular linear-gradient blend source can now keep the source contribution
of a transparent or zero-alpha solid local stroke without inventing visible
stroke paint. This follows B2a (#137), which correctly refused
these cases after discovering their source-origin sensitivity.

The decisive test was information loss, not another blend-equation adjustment.
With only the prior guard bypassed, all facts of the old resolved Frame were
equal across none/transparent/zero-alpha/zero-width/wider-stroke sources:
owners, bounds, geometry, transforms, scopes and paint programs. Yet Chromium
distinguishes positive-width invisible strokes: 30/102 pixels for
Multiply/Screen, maximum channel delta one. Width eight gives 14/25; zero width
is identical to none. Actual strict and best-effort command runs confirmed the
guarded baseline and the diagnostic's silent mismatch. These necessity pairs
are measured, not additional committed cells. The diagnostic was restored
before implementation.

Smallest contract and bounded consumers

An independent rframe maintainer counter-proposed and implemented one checked,
optional BlendSourceDomain on the existing ScopeBlend. It is the complete,
already-enclosed source-local domain and its exact map into the containing
stream, before final blend/opacity. It is not an allocation hint, damage box,
clip, fake paint or second scene tree. Thirteen producer-only tests and the
full rframe suite passed before the two consumers were changed.

Web lowering initially declares only the proven unmapped rectangle profile.
The glyphless consumer independently validates that profile, retains the
declaration in raster identity, and maps it through the current host view at
execution. Bad device bounds refuse by owner before canvas mutation. An
independent illustration verifies changed-view reuse equals fresh, balanced
saves and no geometry/coverage changes. Untimed trace confirms the larger
source extent still uses one layer; no timing improvement or new cache is
claimed.

Transformed/nested source spaces, nonpainted siblings, unresolved/context
strokes, non-scaling invisible strokes, incomplete pattern-sibling domains,
resource source programs, wider geometry and other blend modes remain guarded.
The final numeric probe found an inward-float-enclosure case that failed the
consumer preflight late without emitting pixels. Its named refusal now occurs
at the SVG group path, letting best-effort roll back that group normally.

Evidence and refusal promotion

44 new exact Chromium-gated cells cover the omitted-stroke spellings, positions
and widths, combined/outer opacity, source union/order, leaf and neutral-group
ownership, inherited/currentColor and px/percentage stroke routes, multiple
ramps and live strokes, ancestor clips and local instances. Every candidate
also ran through cargo run -p n0_cli --bin n0 in both admissions.

All captures and bakes use the unchanged hash-pinned Chromium module. Existing
oracles are untouched, manifest changes are additions-only, and no tolerance
was added. Corpus: 1,467 primitives, 329 named refusals; unchanged 16 sampled
frames, 16 text-pixel cells and eight text-geometry witnesses. No checklist
ticks, score, FLIP record or baseline change.

Captured-essence promotion: the old stroke-transparent refusal is now the
exact Multiply fractional cell, and stroke-opacity-zero is the exact Screen
zeroalpha cell. Their obsolete unsupported assertions are deliberately dropped,
not their visual witnesses. Six new refusal witnesses guard the
remaining source-domain boundaries.

Independent TICK/LAW found a real root escape: the omission exemption could
reach the separately lowered root, where no domain was attached. Both actual
admissions silently differed at 85 pixels / delta 6 on transparent-stroke and bare
linear sources under root Multiply/Screen; a full-canvas backdrop hid it.
These root sources now refuse by name in both admissions. The regression test
fails before the guard; two registered witnesses preserve the newly exposed
and pre-existing cases. Attaching a domain alone is not claimed as the fix.

Sensitivity is demonstrated: disabling only consumption of the declaration
makes just gate fail on 37 new cells, including the original 30/102-pixel pair.
Six sibling-origin/live-stroke controls retain the same derived origin; the
elided Normal control has no physical source. These seven remain exact.
Restoring the painter byte-for-byte re-gates green.

Verification

No Workflow runner is exposed in this environment, so
.agents/workflows/verify-rung.js could not be invoked through a Workflow tool.
Its independent TICK/LAW and REPRO roles were reproduced manually; final
verdicts are both pass after applying the root finding. No must_fix or
should_fix remains. REPRO independently renewed the original matrices,
24 root refusal checks and 12 exact neighboring renders, plus gate/status and
all manifest/bake hashes. No claim that the saved Workflow script ran.

Local verification is green:

  • Full cg, csscascade, websem, rframe, n0-model, n0, animation-sampling,
    textlayout and n0_cli tests; full Web compiler/CLI suites rerun after the
    final root guard.
  • Full n0 trace suite and final trace-enabled Chromium pixel gate.
  • cargo fmt --all -- --check, cargo clippy --no-deps -- -D warnings.
  • just bake, restored just gate, just status; 95 new local-link checks,
    additions-only manifest/oracle audit and pre-PR OSS/security cleaning pass.

This rung does not fold in the separate native-opacity work in
#136, generic damage/coverage in #87/#88, text,
external I/O or the other deliberately separate follow-ups. It establishes the
bounded source-domain contract before any wider source-space admission.
Position: #43.

Final hosted verification at 8f356cc1: every check is green, including the
complete test-crates workflow and its dependent Clippy job, consolidation
gates, both builds and deployment. CodeRabbit completed with no actionable
findings and there are no unresolved review threads. Its generic docstring
warning was evaluated: the added public contract is documented, and no concrete
missing documentation was identified. Both independent manual roles also pass.

Preserve complete source domains without fake paint; admit the bounded solid-stroke omission profile with 44 exact Chromium cells. Deliberately promote two obsolete refusal witnesses into positive evidence and retain six new source-domain/root refusals. No checklist ticks or tolerance changes.
@vercel

vercel Bot commented Sep 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
nothing Ready Ready Preview Sep 8, 2026 8:27am UTC

Request Review

@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 0664afc3-720a-4997-9943-30486b4890c3

📥 Commits

Reviewing files that changed from the base of the PR and between 64803a8 and 8f356cc.

⛔ Files ignored due to path filters (96)
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-bevel.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-currentcolor.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-dashed.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-edge.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-fractional.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-integer.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-leaf.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-nested-neutral.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-normal-elided.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-opacity-half.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-opacity-near.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-outer-clip.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-outer-opacity.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-painted-stroke.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-pixel-boundary.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-sibling-reversed.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-sibling.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-two-ramps.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-use.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-width-fractional.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-width-one.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-width-px.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-multiply-zeroalpha.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-edge.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-fractional.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-inherited.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-integer.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-leaf.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-nested-neutral.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-opacity-half.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-opacity-near.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-outer-clip.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-outer-opacity.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-painted-stroke.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-pixel-boundary.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-round.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-sibling-reversed.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-sibling.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-two-ramps.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-use.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-width-fractional.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-width-one.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-width-percent.png is excluded by !**/*.png
  • fixtures/web-first/chromium/svg-group-blend-source-screen-zeroalpha.png is excluded by !**/*.png
  • fixtures/web-first/svg-group-blend-source-multiply-bevel.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-currentcolor.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-dashed.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-edge.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-fractional.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-integer.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-leaf.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-nested-neutral.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-normal-elided.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-opacity-half.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-opacity-near.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-outer-clip.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-outer-opacity.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-painted-stroke.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-pixel-boundary.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-sibling-reversed.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-sibling.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-two-ramps.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-use.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-width-fractional.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-width-one.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-width-px.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-multiply-zeroalpha.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-edge.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-fractional.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-inherited.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-integer.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-leaf.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-nested-neutral.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-opacity-half.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-opacity-near.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-outer-clip.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-outer-opacity.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-painted-stroke.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-pixel-boundary.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-round.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-sibling-reversed.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-sibling.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-two-ramps.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-use.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-width-fractional.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-width-one.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-width-percent.svg is excluded by !**/*.svg
  • fixtures/web-first/svg-group-blend-source-screen-zeroalpha.svg is excluded by !**/*.svg
  • fixtures/web-first/unsupported/svg-group-blend-linear-extent-stroke-opacity-zero.svg is excluded by !**/*.svg
  • fixtures/web-first/unsupported/svg-group-blend-linear-extent-stroke-transparent.svg is excluded by !**/*.svg
  • fixtures/web-first/unsupported/svg-group-blend-source-domain-enclosure.svg is excluded by !**/*.svg
  • fixtures/web-first/unsupported/svg-group-blend-source-domain-non-scaling.svg is excluded by !**/*.svg
  • fixtures/web-first/unsupported/svg-group-blend-source-domain-pattern-sibling.svg is excluded by !**/*.svg
  • fixtures/web-first/unsupported/svg-group-blend-source-domain-range.svg is excluded by !**/*.svg
  • fixtures/web-first/unsupported/svg-group-blend-source-domain-root-bare.svg is excluded by !**/*.svg
  • fixtures/web-first/unsupported/svg-group-blend-source-domain-root-transparent.svg is excluded by !**/*.svg
📒 Files selected for processing (20)
  • crates/n0/README.md
  • crates/n0/src/frame.rs
  • crates/n0/src/glyphless.rs
  • crates/n0/src/paint.rs
  • crates/n0/tests/group_blending.rs
  • crates/n0_cli/README.md
  • crates/rframe/README.md
  • crates/rframe/src/lib.rs
  • crates/rframe/src/scope.rs
  • crates/rframe/tests/blend_source_domain.rs
  • crates/websem/src/svg.rs
  • crates/websem/tests/svg_blending.rs
  • crates/websem/tests/unsupported_corpus.rs
  • docs/wg/consolidation/svg-engine-of-record.md
  • docs/wg/consolidation/web-checklist.md
  • fixtures/web-first/README.md
  • fixtures/web-first/STATUS.md
  • fixtures/web-first/oracle-bake.json
  • fixtures/web-first/primitives.json
  • fixtures/web-first/unsupported/README.md

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


Walkthrough

This change adds checked BlendSourceDomain declarations to blend scopes. SVG lowering supplies domains for eligible linear sources. Glyphless compilation validates supported profiles and execution preflights domains against the current view. Tests and fixtures cover admitted and refused cases.

Changes

Blend source-domain contract

Layer / File(s) Summary
Source-domain type and scope integration
crates/rframe/src/scope.rs, crates/rframe/src/lib.rs
Adds BlendSourceDomain, validation errors, accessors, and ScopeBlend integration. The public types are re-exported.
Contract documentation and producer tests
crates/rframe/README.md, crates/rframe/tests/blend_source_domain.rs
Documents construction and consumer rules. Tests cover numerical validation, equality, nesting, patterns, and structural invariants.

SVG source-domain lowering

Layer / File(s) Summary
Domain derivation and stroke propagation
crates/websem/src/svg.rs
Derives bounded rectangular domains for eligible linear sources and propagates omitted transparent stroke widths. Root and incomplete domains remain refused.
SVG behavior and corpus coverage
crates/websem/tests/svg_blending.rs, crates/websem/tests/unsupported_corpus.rs, crates/n0_cli/README.md
Tests accepted domains, ordering, enclosure, transactional refusal, and root behavior. Updates refusal classifications and accounting.

Glyphless compilation and execution

Layer / File(s) Summary
Declaration validation and execution preflight
crates/n0/src/frame.rs, crates/n0/src/glyphless.rs
Adds an owner-bearing FrameExecutionError::SourceDomain. Compilation validates supported source profiles, and execution checks mapped device bounds before canvas work.
Authoritative blend extents
crates/n0/src/paint.rs
Uses declared domains as authoritative extents mapped through the current view. Undeclared blends retain draw-derived behavior.
Consumer tests and documentation
crates/n0/tests/group_blending.rs, crates/n0/README.md
Tests accepted profiles, rejected nested effects, raster extents, changed-view replay, and refusal before canvas mutation.

Fixtures and capability records

Layer / File(s) Summary
Admitted fixture registry and oracle data
fixtures/web-first/primitives.json, fixtures/web-first/oracle-bake.json, fixtures/web-first/STATUS.md
Adds multiply and screen blend-source fixtures, updates oracle metadata, and records admitted cells and remaining refusals.
Capability and working-group records
fixtures/web-first/README.md, fixtures/web-first/unsupported/README.md, docs/wg/consolidation/*
Updates corpus totals, exact-cell descriptions, refusal rows, and the B2b source-domain capability description.

Estimated code review effort: 4 (Complex) | ~60 minutes

Merge Risk: ⚪ Minimal · up to 8f356

The implementation and cross-layer contracts are covered by passing checks, with no actionable merge-blocking risk identified.

Sequence Diagram(s)

sequenceDiagram
  participant SVG
  participant Frame
  participant Glyphless
  participant Canvas
  SVG->>Frame: attach validated BlendSourceDomain
  Frame->>Glyphless: compile source-domain profile
  Glyphless->>Glyphless: map and preflight domain for current view
  Glyphless->>Canvas: execute after successful preflight
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 74.58% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 59 functions across 9 files. (11 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description clearly explains the BlendSourceDomain changes, supported and refused cases, test coverage, and verification results.
Title check ✅ Passed The title is concise and accurately identifies the main change: retaining complete blend-source domains in websem.
Full details: Docstring Coverage

Explanation

Docstring coverage is 74.58% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 59 functions across 9 files. (11 skipped: 10 unsupported, 1 too large.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rung/blend-source-extents

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@softmarshmallow

Copy link
Copy Markdown
Member Author

Hosted review completed with no actionable code findings. The generic docstring warning was checked against the new public surface: BlendSourceDomain, its validation/refusal semantics, ScopeBlend integration, and the consumer profile are documented and independently reviewed. No concrete documentation gap was identified; named test/helper functions are not receiving repetitive metric-only comments. Independent TICK/LAW and REPRO both pass after the root correction. All completed CI checks are green; the final dependent Clippy job is still running.

@softmarshmallow
softmarshmallow merged commit 0291a26 into main Sep 8, 2026
15 checks passed
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.

1 participant