Skip to content

PNT1-effects 1/2: opacity as a native group alpha and z-index with Appendix E stacking (supersedes the opacity and stacking parts of #84-#85) - #196

Open
thejackshelton wants to merge 36 commits into
masterfrom
pnt1-opacity-z
Open

thejackshelton wants to merge 36 commits into
masterfrom
pnt1-opacity-z

Conversation

@thejackshelton

@thejackshelton thejackshelton commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

PNT1-effects, part 1 of 2: opacity and z-index with CSS2 Appendix E stacking on iOS and Android. Built from current origin/master (6385d46) as one branch with no stack. It does not depend on radius or shadow.

Supersedes the opacity part of #84 and the z-index/stacking part of #85. Both were stacked on PNT1-radius (#81) and PNT1-shadow (#82). The color-scheme and outline parts of #83, and #86/#87 (outline, foreground phase), are not covered here; outline will be a follow-up PR off master.

What changed

Source files are ported from #84/#85 and adapted to master's seams (EMS registries, ExpectedEngine, computed checks).

  • Parsing (css/properties/effects.ts, one stylesheet.ts line, math.ts grammars, grammar subset).
    • opacity takes a number or a percentage, kept as written and clamped at computed-value time.
    • z-index takes auto or an integer, clamped to 32 bits at parse time as Chrome does.
    • A calculation must fold to a number. For z-index it must also be a whole number.
    • These are refused by name, never dropped: a percentage inside a calculation in opacity; round(), abs() and the other math functions Dragon does not evaluate; and env().
  • opacity on native (lower/paint/effects.ts, emit/paint/effects.ts).
    • The writer sets UIView.alpha / View.setAlpha to Chrome's paint alpha byte / 255.
    • The byte comes from the new paint.ts opacityAlpha8, Skia's getAlpha done in float. The device runs it translated, and the host shares it.
  • Stacking on native (lower/paint/stacking.ts, emit/paint/stacking.ts, the state runtime's paintOrder write).
    • Stacking contexts are created by: the root; a positioned box or flex item with an integer z-index; opacity below 1; and, new against PNT1 6/8: z-index and CSS2 Appendix E paint order on the native tree #85, a transform or will-change: transform | opacity, since PNT2 is now on master.
    • Each layer item is re-hosted under its layer's view and sorted there by bucket and rank.
    • Only child order changes. No zPosition, translationZ or elevation is used.
  • Refusals (analysis/paint-values/stacking.ts, called from project.ts beside checkTranslucent since the OVFL A: scroll containers (overflow auto, scroll, clip), viewport propagation and Chrome scroll metrics, rebuilt on master (supersedes #96/#97) #194 catch-up), on the native targets only. The root's clip is the viewport's and counts in every chain.
    • Order: a layer item of any z-index, including auto, kept under an overflow clip in its containing-block chain, whose native order then differs from Appendix E's against some node.
    • Clip: a box that a native ancestor's clip view would clip although that clip is not in its containing-block chain. Example: an absolute box under an opacity or will-change context inside an overflow clip.
  • Not taken from the stack:
    • The shadow-in-opacity-group and shadow-above-skipped-ancestor refusals. box-shadow is not a longhand on master.
    • The shadow-companion handling in the sort. A view that is not a box view keeps its flow place.
    • color-scheme.
    • No radius code is needed or included. The host paint model has no radii, and its boxes() lives in the test file, so it cannot collide with the radius lane's paint-model.ts.

Proof

  • Fixtures (effects group). Each of these runs in both ltr and rtl: opacity-basic, opacity-cascade, stacking-basic, stacking-context, stacking-escape, and the new stacking-transform (transformed and will-change: opacity boxes against positioned ones). The rejects are opacity-reject-calc and stacking-reject-calc.
    • The native-only stacking refusals cannot be parity reject fixtures, because a reject needs blocked web output. paint-stacking.test compiles each one and asserts the refusal on ios only.
  • pnt1-effects.test: a model of Chrome's CPU raster (cc alpha folding, Skia Color32 and s32a blends, Appendix E order from the facts, whole-device-px translations) equals the committed Chrome PNGs at every non-edge sample point.
    • It covers every effects fixture at DPR 2, 3 and 2.625.
    • It covers 102 more corpus cases that the placements reach.
    • The alpha-ignored and tree-order model faults are caught on every fixture.
    • The corpus cases the model cannot draw are pinned in OUT with their reasons: images, rotations and scales, and a dashed border.
  • pnt1-facts.test: emulates the device placement step by step on every layout case of the corpus. Every layer item lands in Appendix E order, and every write's [host, index] matches.
  • paint-opacity.test and paint-stacking.test: parsing against Chrome's computed values, the refusals, Appendix E trees (including transforms), lowering, emission, and a guard against a stale stacking tree.
  • Plants. alpha-ignored on opacity-basic and order-swap on stacking-basic, both on device-pixels with interior: rules. They run on the landing driver's device turn.

Review fixes (precomputed review of 476e9bc, two Mediums)

  • Finding 1: the order refusal now covers every layer item kept under a clip, not only those with an integer z-index. The repro is C (overflow: hidden) > P (opacity 0.5 / relative / transformed) followed by S. It is a model test in paint-stacking.test and a compile test, which is refused on ios only.
  • Finding 2: stackingOf compares every node's native clip set (its native ancestors' clip views) with its containing-block chain, and reports where they first differ. The repro is C (overflow: hidden) > O (opacity 0.5 / will-change / flex item with a z-index) > I (absolute). It is a model test and a compile test, also refused on ios only.
  • The root's clip is now treated as the viewport's: it is in every chain, fixed boxes included. Without that, the check falsely refused the music player's fixed and absolute boxes under html's clip.
  • stacking-reject-clip is removed: a native-only refusal can never pass the parity reject kind, which needs blocked web output, so it was failing parity.test. Its case is a compile test now.
  • These fixes make no corpus layout case refused, as pnt1-facts.test shows by compiling them all. The music player is unchanged at 207 of 289.

Device judgement (landing driver, queue 53): native opacity limited to 0 and 1

The driver's device run found 46 iOS and 69 Android device-pixels failures, all in the opacity fixtures and stacking-context's opacity group. Each was one off Chrome in a channel. The alpha byte is right; the compositing arithmetic is not Skia's. Both UIKit and Android blend a translucent view with exact /255 rounding:

  • op-fold (alpha 77, rgb(230, 80, 40) over rgb(40, 60, 80)): exact /255 gives 97, 66, 68, the native capture on both platforms. Skia's folded blit gives 96, 65, 67, which is Chrome.

Skia's result depends on the backdrop pixel, so no native alpha reproduces it. Ruling (PM, option 1):

  • Native refuses opacity strictly between 0 and 1 by name (analysis/paint-values/effects.ts checkOpacity, at the declaration; it names package PNT1-opacity-b).
  • Opacity 0 and 1, the values that clamp to them, z-index and stacking land natively. Web keeps fractional opacity, proven against Chrome there.
  • Fixtures: opacity-basic and opacity-cascade now use 0, 1, 0%, 100%, 1.5, -2, 500% and calc() to 1. stacking-context's group is a will-change: opacity context instead of opacity 0.5. Every layout fixture must compile natively, so no fractional case stays in a fixture. The host model still pins Chrome's fractional composite bytes (pnt1-effects.test).
  • Proof: pnpm run parity:devices through /tmp/device-lease.sh at ce0b8ee's source (the regen commit; 1d0a6c9 only fixes a test), on iPad (A16), iPhone 17, dragon-320, dragon-smoke and dragon-480. Every opacity and stacking case passes. The only failures are master's known selectors-universal-root set (28 lines, the same normalized set as master's in the driver's log), so there are zero new failures. The device outputs are not committed; the driver records its own run.
  • Coverage change: the music player goes 199 → 203 of 289 on both native targets, where it was 207 before this ruling. Its fractional opacities are refused. The opacity coverage-rank probes compile on native for 0 and 1, not 0.5 (2 of 3).

Review Low (precomputed review): fractional opacity proven on web again

After the fixture change, no cited Chrome proof covered a fraction on web, so web rows overclaimed exact.

  • opacity-web (ltr and rtl): 0.3, 45%, an inherited 0.5, and translucent groups with a child, text and a border. The web rows opacity:<number>, <percentage> and inherit now cite it. No native row does: a test pins that the iOS and Android profiles never cite it.
  • Lane-only on native: outside the parity lanes, native refuses a fractional opacity (checkTranslucent). The lanes compile it and mark the case lane-only (project.ts, the same mechanism SELD-R2 uses), so it proves web rows only (pipeline.ts).
  • Device pixel lanes: they skip the points a translucent group paints (pixel-reference.ts devicePoints, used by every run file and reference). Only a lane-only case brings such a group to a device. A test pins that no other case loses a point, and that the stage and spacer points of opacity-web stay.
  • Host model: its alpha-ignored fault runs on real groups again, since opacity-web is in pnt1-effects.test. The device alpha-ignored plant still runs on opacity-basic's opacity-0 boxes.
  • Device run (/tmp/device-lease.sh pnpm run parity:devices, on all five devices, with opacity-web included): zero failures beyond master's known selectors-universal-root set.

Design note: PNT1-opacity-b, pre-compositing over a provably uniform backdrop (follow-up PR off master)

  • Idea: where the compiler can prove what is under a translucent group, it computes Chrome's exact result at compile time and writes opaque colours. No platform alpha is used, so platform rounding never applies.
  • Admissible: a group (box, borders, text and descendants, in Appendix E order) whose every painted pixel lies over one uniform opaque backdrop, in every reachable viewport. The backdrop is the nearest ancestor's opaque background, with nothing else painted between it and the group: no sibling, positioned box or overflow, and no outline, shadow or image under the group. Layout can vary with the device viewport, so "over the backdrop" is proven from the box geometry rules (in-flow descendants of a box with an opaque background and no overflow out of it), not from one reference layout.
  • Arithmetic: for a group whose one draw folds (cc PlaybackFoldingIterator), Skia's Color32 blit, premul(colour, a8) + (dst × (256 − a8) >> 8). Otherwise each drawing composites into a premultiplied layer, which is blended with blit_row_s32a_blend. These are the model pnt1-effects.test already verifies byte for byte against Chrome. The result per painted region (background, each border side, text colour) becomes an ordinary opaque write. Translucent text needs the glyph coverage composite, so text inside a group stays refused at first.
  • Everything else stays refused by name: groups over varied backdrops, overlapping translucent siblings and nested groups over non-uniform content.
  • Proof:
    • Restore the fractional fixtures in admissible shapes, plus reject variants.
    • The host model against Chrome's PNGs.
    • Device-pixels at zero channel delta, plus a plant that uses the platform alpha instead of the pre-composite.

Rows promoted (iOS and Android, status caveat; web exact)

12 rows on each target:

  • opacity:<number>, opacity:<percentage>, opacity:inherit, opacity:initial, each @ paint/ltr and paint/rtl.
  • z-index:<integer> and z-index:auto, each @ paint/ltr and paint/rtl.

The coverage-rank probes (opacity 0 / 0.5 / 1; z-index 1 / 10 / -1 / 9999, absolutely positioned) fall in these rows. I could not rerun coverage-rank.ts from #146, because it rejects the current Tailwind snapshot's na-native outcome.

Music player (north star)

  • Declarations supported on both native targets: 199 → 203 of 289 (68.9% → 70.2%). Fractional opacities are refused natively; this was 207 before the device judgement.
  • web: 230 → 239.
  • All 9 opacity / z-index UNSUPPORTED_PROPERTY errors are gone. The z-index ones compile, and the fractional opacities are now refused by name instead.
  • One new refusal on ios and android: library-button's z-index: 40 paints above the z-index 20/30 overlays outside page/app's overflow clip. Hosted under that clip, it would paint below them natively, so it is refused. Lifting it needs a clip-chain view (follow-up).

Changed tests and checks (reasons)

  • Catch-up, 2026-10-08:
    • paint-stacking.test: the REPL-a hand-built LayoutBox style gains overflowY: 'visible'. The stack tree now asks clipsChildren, which reads both axes, so the fixture needed the field the real type always has. The test's intent is unchanged.
    • pnt1-effects.test OUT pin gains overflow-replaced and overflow-replaced-rtl. OVFL's fixture has a relatively positioned image, so it now reaches the placements. It draws replaced-image, which puts it outside the model for the same reason as replaced-*.
    • The stacking clip refusal is now lane-only in the parity lanes (above). A user's compile refuses exactly what it refused before, and now also refuses inside auto, scroll and clip boxes.
  • Device judgement: paint-opacity.test compiles its computed-value cases for web; native now has its own refusal tests. Its lowering and emission tests use opacity 0. s4b's opacity pair is 0 and 100%. paint-stacking.test's review repros use position: relative and will-change: opacity in place of opacity 0.5. pnt1-effects.test checks the alpha-ignored plant on the opacity fixtures only, since stacking-context has no opacity group now.
  • paint-seams.test: program nodes now carry the stacking facts on every box (rt-hit reads them), so "empty facts" becomes "only stacking, on boxes".
  • paint-transform.test: body's facts are ['stacking'], not {}. The intent kept is that body has no transform facts.
  • pointer-events.test: the paint families after pointer-events now start with effects (opacity, z-index), and transform follows them.
  • s4b.test: opacity and z-index get value pairs, so they are checked like every other paint longhand.
  • layout paint-seams.test: paint.ts exports opacityAlpha8, and the index line names it.
  • expected-dump.test, native-host.ts: ExpectedEngine gains opacityAlpha8.
  • p6a-dash-oracle.test: a border inside an opacity group is composited with the group alpha, which that oracle does not model. pnt1-effects.test compares those pixels against Chrome.
  • hit-capture.ts hitRefusal and hit-report.test: the hit lane now also refuses, by name, a case with a stacking context below the root. rt-hit.ts orders positioned boxes as z-index auto. This follows the transform precedent (SELD-R2b). Only the new opacity/stacking fixtures are affected; no case the lane covered before is dropped.
  • device-run.ts: PLANT_CASES/PLANT_RULES gain the two plants, in registry order.
  • Floors: seams-floor.json and paint-seams-floor.json are appended with DRAGON_FLOOR_WRITE=1. Nothing was lowered.

Outside the spec

  • packages/dragon/src/css/paint-parsers.ts (effects registered in PNT1-radius: border-radius, its corner longhands and -webkit-border-radius drawn natively on iOS and Android (supersedes #80, #81) #198's paint value hook), packages/dragon/test/paint-radius.test.ts (facts pin), packages/parity/src/hit-capture.ts identity keys (catch-up, 2026-10-09)
  • packages/dragon/src/lower/native-program.ts (one line: an inline box or <br> publishes its stacking facts), packages/dragon/src/project.ts (the stacking clip refusal beside checkTranslucent), computed-checks.ts propagatedFrom exported (catch-up, 2026-10-08)
  • packages/parity/src/hit-capture.ts, packages/parity/test/hit-report.test.ts (the hit lane refusal above)
  • packages/parity/test/p6a-dash-oracle.test.ts
  • docs/ports.json (cc paint_op_buffer_iterator.cc, SkPaint.h, SkBlitRow_D32.cpp, SkColorData.h, SkBlitRow_opts.h), with the notices regenerated

Known limits (not claimed)

Catch-up after #91 (INL1a), #194 (OVFL) and #200 (CASC), 2026-10-08

The #91 catch-up (0ba1fe4, 845db75, regen 83be2ed) stopped with pnt1-facts.test failing on the merged tree: TypeError: Cannot read properties of undefined (reading 'layer'). origin/master was merged twice more (#194 OVFL, then #200 CASC) and regenerated on CI.

  • pnt1-facts root cause (1ea4660). native-program.ts placed inline box and <br> nodes with facts: {}, because the paint lowering runs only on block boxes. 135 inline nodes had no stacking facts (inline-mixed-sizes and every other inline fixture).
    • layoutStackTree now lays inline content out flat, the way the native tree places it (T044 R3). An inline box is a childless element node followed by its content, and it never clips.
    • inlineStackingFacts gives each inline box and <br> its facts. It throws if an inline box would be a layer item.
    • checkInline (computed-checks.ts) refuses on native an inline box with opacity below 1 or will-change: opacity. Its group would hold content the native runtime places beside its view, not inside it. Before this, the opacity was dropped silently.
  • OVFL interaction (f3e784e). OVFL makes overflow auto, scroll and clip host their children in a clip view (clip.ts clipsChildren). Both stacking trees still clipped only for hidden, so a layer item inside such a box was re-hosted out of its clip view: overflow-replaced's i4 would have painted unclipped on device.
    • layoutStackTree now uses clipsChildren.
    • resolvedStackTree clips for any overflow except visible. Two exceptions use visible: an inline box, and the element whose overflow the viewport takes (propagatedFrom, now exported).
    • The stacking clip refusal moved from checkComputed into project.ts, beside checkTranslucent and checkNativeScroll. A user's native compile still refuses it. The lanes compile the item kept under its clip as a lane-only native case, which proves no native row, and the device pixels judge it. That is overflow-end-padding's a6a and overflow-replaced's i4, both already lane-only for overflow auto. Without this, those two fixtures would block native compilation, and every layout fixture must compile natively.
  • Merge conflicts: the INL1a 5/5 (C2): lowering inline boxes and <br>s, native runtime emit, 7 inline fixtures and lanes #91 merge conflicted in computed-checks.ts, hit-capture.ts and hit-report.test.ts (resolved in 0ba1fe4). The OVFL A: scroll containers (overflow auto, scroll, clip), viewport propagation and Chrome scroll metrics, rebuilt on master (supersedes #96/#97) #194 merge conflicted in project.ts, and both sides' lane-only native refusals were kept. The CASC 1: @supports decided at build time as Chrome 145 decides it; revert and revert-layer; CSS-wide keyword rows #200 merge was conflict-free.

Catch-up after #208 (charset), #198 (pnt1-radius-native), #210 and #101, 2026-10-09

The driver's train merge onto af341be conflicted. Once #208 and #198 had landed (#218 failed landing and is not on master), origin/master 508db67 was merged in 949f033, then 981c068 (#210, #101) in a42c0bc. The second merge was conflict-free. Each was regenerated on CI.
Conflicts in the 949f033 merge, each resolved keeping both sides:

Follow-ups after the merge:

What passed

Latest, at head 9f85d98987727a5fcff05be544ac138113f75341:

Head 8a1250d7ad1412ad3a8946a779812175bbfc8d4b (2026-10-08, before the #198 merge):

Earlier rounds:

  • pnpm regen: fixed point every time (last: 2 passes, at 42db25f). The outputs are in the regen commits, which hold generated files only.
  • Catch-up: GitHub reported CONFLICTING, so origin/master (88f7da8) was merged; the merge drivers resolved it cleanly. A second pnpm regen followed, again a fixed point after 3 passes.
  • pnpm typecheck: clean.
  • dragon, layout and the targeted parity suites (including the full parity.test): 117 files, 3632 tests at the review-Low regen. All passed except the new opacity-web test, which compiled under the wrong project id; it is fixed in 139a827 and passes. Earlier: 117 files, 3621 tests at ce0b8ee. All pass except s4b's fractional opacity pair, which is fixed to 0 and 100% in 1d0a6c9 and passes. Earlier, at 42db25f: dragon and layout in full, 100 files, 2316 tests; targeted, 26 files, 1441 tests. The targeted files were:
    • dragon: paint-opacity, paint-stacking, paint-seams, seams, s4b, state-program, expected-dump, animation-kinds, registry-claims, grid, math-validity.
    • layout: paint-seams.
    • parity: parity (the full file), display-parse, pnt1-effects, pnt1-facts, hit-report, p6a-dash-oracle, device-run, pnt2-plants, registry-claims, chrome-ports, css-escapes, state-cases, native-compare, samples.
  • pnpm native:build -- --target ios: status pass. swiftc builds the opacity and paint-order writers, and the iOS 15 availability check passes.
  • pnpm native:build -- --target android: status pass.
  • Not run:
    • The full pnpm test (the landing driver runs it).
    • Device runs (device step pending). The lanes/device-record tests need the driver's device run.

The reviewed diff is about 160 KB, slightly over the ~150 KB guide. It is one theme (opacity groups and the stacking order they create), and splitting opacity from stacking would separate the stacking contexts that opacity creates from the code that orders them.

🤖 Generated with Claude Code


Generated by Claude Code

… byte, and z-index with CSS2 Appendix E paint order on the native tree

Ported from #84 and #85 onto current master, without radius or shadow: opacity and z-index parse as Chrome 145 does
(css/properties/effects.ts), opacity computes clamped, the effects module writes UIView.alpha / View.setAlpha at
paint.ts opacityAlpha8, and the stacking module re-hosts and sorts layer items (lower/paint/stacking.ts). Transforms and
will-change: transform or opacity (PNT2, now on master) join the stacking contexts. A z-index box that would leave the
overflow clip of its containing block is refused natively. Fixtures (effects group), the paint model test against Chrome,
the placement emulation test, plants alpha-ignored and order-swap, and the hit lane's refusal of stacking contexts.
…tive order differs from Appendix E's; stale-tree guard; corpus model scope pinned; dash oracle skips opacity groups
…ts on every box) and pointer-events' family order pin (effects before transform)
…lip whose native order differs, and every box a native clip view would clip outside its containing-block chain

Finding 1: the order refusal covered integer z-index items only; z-index auto items (relative, absolute, opacity < 1, transformed)
kept under a clip reorder the same way. Finding 2: an absolute box re-hosted under a stacking context (opacity, will-change, a
flex item with z-index) inside an overflow clip outside its containing-block chain was clipped natively; stackingOf now compares
every node's native clip set with its containing-block chain. Both repros are model and compile tests in paint-stacking.test.
stacking-reject-clip is removed: a parity reject fixture needs blocked web output, which a native-only refusal never gives.
@thejackshelton

Copy link
Copy Markdown
Contributor Author

Landing stopped at step judge-devices (pnpm land)

the device run differs from the previous position's device evidence:
  ios device-pixels: 46 failure(s) master does not have, e.g. device-pixels opacity-basic 3 interior:op-fold pixel | device-pixels opacity-basic 3 interior:op-group pixel | device-pixels opacity-basic 3 border:op-border:top pixel
  android device-pixels: 69 failure(s) master does not have, e.g. device-pixels opacity-basic 2 interior:op-fold pixel | device-pixels opacity-basic 2 interior:op-group pixel | device-pixels opacity-basic 2 border:op-border:top pixel

Fix the cause, then hand #196 back to the landing queue with the new clean head.

…tly between 0 and 1 by name (platform compositing rounds unlike Skia; package PNT1-opacity-b); opacity fixtures take 0, 1 and clamped values, stacking-context's group is will-change: opacity
…web opacity rows against Chrome as a lane-only native case

project.ts: outside the parity lanes native refuses a fractional opacity (checkTranslucent); the lanes compile it and mark the case
lane-only, so it proves web rows only (pipeline.ts). pixel-reference.ts devicePoints: the device pixel lanes skip the points a
translucent group paints, which only a lane-only case brings to a device. opacity-web (ltr, rtl): 0.3, 45%, an inherited 0.5, and
groups with a child, text and a border, so the host model's alpha-ignored fault runs on real groups again.
@thejackshelton thejackshelton added the regen Run pnpm regen on CI (regen-on-ci.yml) and push the outputs back label Oct 6, 2026
Commands: pnpm regen --skip lanes-host --skip tw-sweep (macos-26) and pnpm regen --only lanes-host (xcode-27), alternated
to a fixed point, then pnpm regen --only tw-sweep (macos-26).
Base: bf86a9d
Run: https://github.com/compiled-run/dragoncss/actions/runs/37499098126
@github-actions github-actions Bot removed the regen Run pnpm regen on CI (regen-on-ci.yml) and push the outputs back label Oct 6, 2026
# Conflicts:
#	packages/dragon/src/analysis/computed-checks.ts
#	packages/parity/src/hit-capture.ts
#	packages/parity/test/hit-report.test.ts
@thejackshelton thejackshelton added the regen Run pnpm regen on CI (regen-on-ci.yml) and push the outputs back label Oct 6, 2026
thejackshelton and others added 2 commits October 6, 2026 13:37
…e boxes and <br>, and the device-hit test names stacking-transform

stacking.ts layoutStackTree: an inline box is an element node (z-index when positioned, opacity always); a <br> is a leaf like a
text run (it paints nothing). Without it the merged tree crashed on a <br> (hit-capture in regen-on-ci: reading 'display' of
undefined). lanes.test.ts: the hit lane's non-inline, non-stacking refusals are the PNT2 transform- cases and PNT1's
stacking-transform, both with the transform reason. paint-stacking.test.ts: the hand-built LayoutBox gains INL1a's strut.
Commands: pnpm regen --skip lanes-host --skip tw-sweep (macos-26) and pnpm regen --only lanes-host (xcode-27), alternated
to a fixed point, then pnpm regen --only tw-sweep (macos-26).
Base: 845db75
Run: https://github.com/compiled-run/dragoncss/actions/runs/37512986344
claude and others added 6 commits October 8, 2026 19:31
… facts, hosted as the native tree places them

pnt1-facts failed on the merged tree: native-program.ts placed inline box and <br> nodes with facts: {} (the paint lowering runs
only on block boxes), so the device-placement check read undefined facts on inline-mixed-sizes and every other inline fixture.
stacking.ts layoutStackTree now lays inline content out flat, as the native tree does (an inline box is a childless element node
followed by its content; it never clips), and inlineStackingFacts publishes the facts of each inline box and <br>, refusing one
that would be a layer item. computed-checks.ts checkInline refuses on native an inline box with opacity below 1 or will-change:
opacity: its group would hold content the native runtime places beside its view, so it was dropped silently.
paint-stacking.test.ts: the three cases fail without the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
# Conflicts:
#	packages/dragon/src/project.ts (both sides' lane-only native refusals kept: PNT1's checkTranslucent and OVFL's checkNativeScroll)
… the native tree does, and the clip refusal is lane-only

OVFL made overflow auto, scroll and clip host their children in a clip view (clip.ts clipsChildren), but both stacking trees still
clipped only for hidden, so a layer item inside such a box was re-hosted out of its clip view: overflow-replaced's i4 would paint
unclipped on device. layoutStackTree now uses clipsChildren; resolvedStackTree clips for any overflow but visible, except an inline
box and the element whose overflow the viewport takes (propagatedFrom, now exported). The stacking clip refusal moves from
checkComputed to project.ts beside checkTranslucent and checkNativeScroll: a user's native compile still refuses it, and the lanes
compile the item kept under its clip as a lane-only native case (overflow-end-padding's a6a and overflow-replaced's i4, both
already lane-only for overflow auto), which the device pixels judge.
paint-stacking.test.ts: four cases that fail without the fix. pnt1-effects.test.ts OUT pin gains overflow-replaced(-rtl): its
relative image now reaches the placements and draws replaced-image, outside the model like replaced-*.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
Commands: pnpm regen --skip lanes-host --skip tw-sweep (macos-26) and pnpm regen --only lanes-host (xcode-27), alternated
to a fixed point, then pnpm regen --only tw-sweep (macos-26).
Base: f3e784e
Run: https://github.com/compiled-run/dragoncss/actions/runs/37840860625
Commands: pnpm regen --skip lanes-host --skip tw-sweep (macos-26) and pnpm regen --only lanes-host (xcode-27), alternated
to a fixed point, then pnpm regen --only tw-sweep (macos-26).
Base: 6f440e0
Run: https://github.com/compiled-run/dragoncss/actions/runs/37848881800

Copy link
Copy Markdown
Contributor Author

LAND-READY
READY pnt1-opacity-z:196:8a1250d7ad1412ad3a8946a779812175bbfc8d4b

Lane receipt (PM session A): pnpm typecheck pass; 13 platform-free files (dragon expected-dump, paint-opacity, paint-seams, paint-stacking, paint-transform, pointer-events, s4b, registry-claims, profile-index, inline-lowering, overflow, interaction; layout paint-seams) pass (local, at 8a1250d); ci:regen https://github.com/compiled-run/dragoncss/actions/runs/37848881800 (regen commit 8a1250d); ci:test-files https://github.com/compiled-run/dragoncss/actions/runs/37855643718 (tested 8a1250d at 8a1250d: android-profile 5, chrome-ports 13, hit-report 9, p6a-dash-oracle 314, pnt1-effects 138, pnt1-facts 11, parity registry-claims 8 pass; lanes.test "every device lane ran" is device step pending); pr:review 196 --once exit 0 (UNREVIEWED: Macroscope spending limit). Fixes in this round: the pnt1-facts landing failure (inline stacking facts, 1ea4660) and stacking clips for OVFL auto/scroll/clip (f3e784e), each with tests that fail without the fix.

Independent Claude review of the change since 83be2ed, at 8a1250d (Medium or higher only), including the OUT-pin additions and the lane-only stacking refusal:

{"pr":196,"head":"8a1250d7ad1412ad3a8946a779812175bbfc8d4b","findings":[]}

Generated by Claude Code

@thejackshelton thejackshelton added the land-ready Reviewed clean; the Mac landing driver takes it label Oct 8, 2026 — with Claude
@thejackshelton thejackshelton added landing-failed The landing driver stopped this PR; see its comment and removed landing-failed The landing driver stopped this PR; see its comment labels Oct 8, 2026
@thejackshelton

Copy link
Copy Markdown
Contributor Author

Landing stopped at step merge (pnpm land)

merge-train: merging pnt1-opacity-z onto af341be8309e00a94930e0fecfbbe623978694a7 failed; conflicts in packages/dragon/src/css/stylesheet.ts, packages/dragon/src/project.ts, packages/dragon/test/expected-dump.test.ts, packages/dragon/test/pointer-events.test.ts, packages/parity/src/device-run.ts, packages/parity/src/hit-capture.ts, packages/parity/src/native-host.ts, packages/parity/test/hit-report.test.ts, packages/parity/test/lanes.test.ts, packages/parity/test/p6a-dash-oracle.test.ts, scripts/gen-css-grammar.ts

Fix the cause, then hand #196 back to the landing queue with the new clean head.

@thejackshelton thejackshelton removed the land-ready Reviewed clean; the Mac landing driver takes it label Oct 9, 2026
claude and others added 8 commits October 9, 2026 01:42
…-opacity-z

# Conflicts, each resolved keeping both sides:
#	packages/dragon/src/css/stylesheet.ts: opacity and z-index register in #198's PAINT_VALUE_PARSERS hook (paint-parsers.ts)
#	packages/dragon/test/expected-dump.test.ts, packages/parity/src/native-host.ts: ExpectedEngine has opacityAlpha8 and paint.roundedShape
#	packages/dragon/test/pointer-events.test.ts: radius, then effects, then transform after pointer-events
#	packages/parity/src/device-run.ts: radius-square, alpha-ignored and order-swap plants, in registry order
#	packages/parity/src/hit-capture.ts: the stacking-context and border-radius hit refusals both
#	packages/parity/test/hit-report.test.ts, packages/parity/test/lanes.test.ts: the union of transform, stacking, radius and inline refusals
#	packages/parity/test/p6a-dash-oracle.test.ts: skips both an opacity group's borders and a rounded box's
#	scripts/gen-css-grammar.ts: both EFFECTS_LONGHANDS and RADIUS_LONGHANDS
# After the merge: EFFECTS_VALUES and STACKING_VALUES gain #198's check field (null: their refusals are lane-gated in project.ts),
# the NodeGeometry literals in paint-opacity and paint-stacking tests gain #198's size field, and hit-report.test pins that the
# stacking and radius refusals each fire with the other present or absent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
…s check field and NodeGeometry size, and the radius facts pin

EFFECTS_VALUES and STACKING_VALUES gain #198's check field, null: the fractional-opacity and stacking clip refusals are lane-gated in
project.ts. The paint-opacity and paint-stacking applied-value calls pass #198's NodeGeometry size. paint-radius.test: a rounded box
also carries PNT1's stacking facts (on every box, rt-hit reads them), so the pin checks a's radius facts and that b has only stacking,
as paint-transform.test does. hit-report.test: the stacking and radius hit refusals each fire with the other present or absent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
Commands: pnpm regen --skip lanes-host --skip tw-sweep (macos-26) and pnpm regen --only lanes-host (xcode-27), alternated
to a fixed point, then pnpm regen --only tw-sweep (macos-26).
Base: 45e05ec
Run: https://github.com/compiled-run/dragoncss/actions/runs/37871732935
Commands: pnpm regen --skip lanes-host --skip tw-sweep (macos-26) and pnpm regen --only lanes-host (xcode-27), alternated
to a fixed point, then pnpm regen --only tw-sweep (macos-26).
Base: a42c0bc
Run: https://github.com/compiled-run/dragoncss/actions/runs/37877358100
…and counts the effects fixtures as new

hit-capture-identity.test failed on the merged tree: every emitted rule gained opacity: 1 and z-index: auto with PNT1, so 2471
base files no longer hashed to the identity base. As #198 did for its corner radii, KEYS_SINCE_BASE gains opacity and z-index (the
base files must still differ only by those keys and pointer-events), and IDENTITY_NEW gains the effects group's opacity-* and
stacking-* fixtures (none is in the base manifest). The test fails without this and passes with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
… the identity strips opacity and z-index only when neutral

# Conflicts, each resolved keeping both sides:
#	packages/dragon/src/project.ts: checkComputed takes #218's options.faults; PNT1's lane-gated checkTranslucent (and the
#	  checkNativeScroll and checkStackingClips calls after it) stay
#	scripts/gen-css-grammar.ts: EFFECTS_LONGHANDS (PNT1) then GEN-b's content and list-style longhands
# Review of 9f85d98 (Medium, hit-capture.ts:217): KEYS_SINCE_BASE stripped opacity and z-index at any value, so a base fixture that
# gained z-index: 3 or opacity: 0.5 would still hash to the base. They now leave KEYS_SINCE_BASE and are stripped only at their neutral
# values (opacity 1, z-index auto), like #218's GEN-b longhands. hit-capture-identity.test: a new case strips the neutral pair and keeps
# opacity 0.5 and 0 and z-index 3, 0 and -1; it fails with the old any-value strip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkSiXMPXTAsNpS9BD9LDR
Commands: pnpm regen --skip lanes-host --skip tw-sweep (macos-26) and pnpm regen --only lanes-host (xcode-27), alternated
to a fixed point, then pnpm regen --only tw-sweep (macos-26).
Base: eb81776
Run: https://github.com/compiled-run/dragoncss/actions/runs/37889722851

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

landing-failed The landing driver stopped this PR; see its comment regen Run pnpm regen on CI (regen-on-ci.yml) and push the outputs back

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants