Skip to content

feat(core): one rect morph for touch presentations that grow out of a source rect - #9769

Open
cixzhang wants to merge 31 commits into
mainfrom
motion/rect-morph
Open

cixzhang wants to merge 31 commits into
mainfrom
motion/rect-morph

Conversation

@cixzhang

@cixzhang cixzhang commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Supersedes #8329 by @vjeux, who stays the author of this commit. Stacked on #9764 (the morph paths) and targets main. #8329's branch carries the open chain under #8909, #8340 and #8360, about 30,000 lines. This pull request is #8329's own change, rebuilt on main and ported to main's engine (createMotionAnimator; #8340 was absorbed into main).

Stack: #9764 → #9769 → #8485 and #8080 (#8078 and #8508 have merged).

Behavior module: packages/core/src/motion/rectMorph.ts, with packages/core/src/motion/progress.ts; every other file supports them.

Maintainer impact

Five touch presentations grow a surface out of what the finger touched and shrink it back: menus, Popover, the lifted preview, Lightbox's zoom, and a sub-menu's parent card. Without one module, each would write its own version and get interruptions and Reduced Motion right on its own.

Intended invariant

No behavior change: the module is internal, and no component imports it yet.

  • Timing follows the component's motion record (spec:AST-094 FR45), as ruled by Specs & Decisions. present(path, motion) and dismiss(path, motion, {target}) take a RectMorphMotion: either {duration, easing} or {spring}, whichever the adopting component's motion record names. The morph has no per-trigger rule of its own. Either starts at the velocity it is given, by default the presented velocity, so a new target keeps the current velocity (spec:AST-094 FR6). The doc example takes the timing as given.
  • Existing callers keep their values, bit for bit. easedProgress moves to progress.ts byte for byte, so Popover, SegmentedControl and the layout glide read the same positions and velocities as on main.
  • The pager changes in two places only. timedCurve is solveAnimatedPageScroll's curve as on main (the Hermite carry that keeps a retargeted velocity), with these two changes:
    • From rest, its velocity at t ≤ 0 is the curve's own starting slope, not 0. A retarget at the first instant now keeps that speed: a linear 300 px move over 0.3 s reads about 1,000 px/s at t = 0, where main reads 0.
    • A 0 ms duration is on its target at once, at rest. On main it reads NaN at t = 0. This matches the rect morph's 0 ms timing (spec:AST-094 FR5).
  • The rect morph plays timedCurve from 0 to 1, so its timed motion keeps a retargeted velocity and reads its velocity with the finer central difference.
  • architecture:react-component-runtime INV12 says the size-change glide plays through the shared rectangle morph once Core has it. Now that Core has it, INV12 records an implementation gap: the glide still plays its own compositor translate. Moving it onto the morph is a separate, later pull request.

Change

  • motion/rectMorph.ts, internal: createRectMorph presents along a path (lerpPath, edgeGrowPath, or a caller's own) and dismisses back onto a target the caller gives (a copy, or the element). Each interrupts the other from the rect on screen. Nothing stays inline once a present settles, and Reduced Motion is the animator's.
  • motion/progress.ts: easedProgress, moved unchanged from themeTiming.ts, and timedCurve, moved from paging.ts, a timed motion on values that keeps a retargeted velocity. themeTiming.ts, Popover and SegmentedControl import easedProgress from there. solveAnimatedPageScroll and the morph call timedCurve.
  • architecture:react-component-runtime INV12: the implementation-gap sentence above.
  • Tests: the unit suite (rectMorph.test.ts: 24 cases, including a timed present that keeps its velocity) and the browser interaction spec. A README row and a [chore] changeset.
  • Tests for the callers' values, based on @vjeux's test (vjeux/astryx coverage/rect-morph-first-instant):
    • motion/__tests__/callerMotionValues.test.ts keeps copies of main's easedProgress and solveAnimatedPageScroll (ebb9ddec). On a fixed grid it checks that the shipped functions give the same number, bit for bit (Object.is), for each position and velocity. The only value it leaves out is the pager's from-rest velocity at t ≤ 0. The check runs on whatever platform runs the test, so it holds wherever that platform's math library differs.
    • progress.test.ts holds easedProgress's 1 ms slope step: an ease-in reads 0.2053 progress/s, and [0.2, 0, 0, 1] reads 0.9761, at t = 0.1 ms on a 0.1 s curve.
    • paging.test.ts adds a retarget at the first instant (0 → about 1,000 px/s, with the position continuous) and a 0 ms move.

Evidence

  • Before: feat(core): one rect morph for touch presentations that grow out of a source rect #8329 can't merge as it stands; it carries an open chain and imports createAnimator, which main doesn't have.
  • After: tsc is clean, and the motion, Popover, SegmentedControl and pager suites pass (1,459 tests).
  • Red first: on main's code, the first-instant and 0 ms pager tests fail. On this pull request's earlier head (6b6310c6), the easedProgress slope test and both reference comparisons fail.
  • Values compared with main (ebb9ddec). Each hash is the first 16 hex digits of the SHA-256 of the raw float64 values. The grid samples each curve at 1/200 of its length, plus 10 µs, 0.1 ms, 0.5 ms and 1 ms from each end. It covers nine cubic-bézier easings, steps(4) and each caller's fallback timing. Popover's channels are sampled through its own surfaceCurves mapping, and SegmentedControl's through its lens mapping.
Caller Samples Positions, main = head Velocities, main = head Largest velocity difference
easedProgress 8,520 2939ddc076992be9 966708df1d2d58ae 0
Popover surface motion 149,526 7ddf1d33716be387 b952c22259b2f795 0
SegmentedControl selection motion 33,228 f41cbbd9f678b98b 6ac217e2a392ecc5 0
Pager 34,080 ab5cff1a2d9edd29 t > 0: e0758e6000b52217 0 for t > 0. Only the 96 from-rest readings at t ≤ 0 differ (the intended change).
  • The hashes in the table are from macOS arm64. A float64 hash is not the same on every platform: for the same main code, CI's Linux gives a different easedProgress hash. What counts is that main and head match on one platform, and the test checks exactly that on CI.
  • Product/runtime behavior verified unchanged: no component imports the module.

Scope

  • No intended public API, product behavior, visual direction, or policy change.
  • Product changes discovered during the work were removed or split.
  • Public text and artifacts contain no internal Meta context.

Testing

  • pnpm exec vitest run --project ui packages/core/src/motion packages/core/src/Popover packages/core/src/SegmentedControl packages/core/src/hooks/usePager --maxWorkers=1: 1,459 passed. scripts/snap-paging-bundle.test.mjs: 4 passed.
  • pnpm exec tsc -p packages/core --noEmit: clean. Strict ESLint on the changed files, pnpm check:knowledge and pnpm check:changesets: pass.
  • The browser interaction spec (rectMorph.interaction.browser.spec.ts) runs in CI's interaction job.

Writing rubric results

Record changed: architecture:react-component-runtime INV12, one implementation-gap sentence.

Item Result
R1 Pass: the sentence states the system now (the glide plays its own translate); no plan or history.
R2 Pass: no research in the record.
R3 Pass: no pull request, issue or commit as evidence.
R4 Pass: no rejected alternative.
R5 Pass: no # number, /pull/ or /issues/.
R6 Pass: no open question.

vjeux and others added 5 commits November 3, 2026 06:35
progressCurve, mapProgress, mapRange and presentedProgressVelocity in
motion/progress.ts, internal, one velocity unit (progress per second).
Rebuilt on main from the pull request's own change, without the open
dependency chain it no longer needs.

Co-authored-by: Cindy Zhang <cindyxz@meta.com>
…ressCurve

CodeBunny on #9755: springProgress solved the same spring as progressCurve.
It now reads progressCurve (remaining = 1 - progress), so one implementation
remains. Same curve, same settle epsilon (0.001) and duration.
…shows at each progress

lerpPath, edgeGrowPath, each path's restartFrom and morphCurves in
motion/morphPath.ts, internal. Pure geometry over a progress curve: the
caller chooses the timing. Rebuilt on the progress module from the pull
request's own change, without the open chain it no longer needs.

Co-authored-by: Cindy Zhang <cindyxz@meta.com>
# Conflicts:
#	packages/core/src/motion/README.md
… source rect

createRectMorph in motion/rectMorph.ts, internal: present along a path and
dismiss back onto a copy or the element, each continuing the other from the
rect on screen. Rebuilt on main from the pull request's own change and
ported to main's engine (createMotionAnimator).

Timing follows the owner's rule for touch motion: present and dismiss take
a motion, either a theme duration and easing (a tap, a key, the program) or
a spring (a gesture's release, from its velocity). timedProgressCurve joins
the progress helpers. architecture:react-component-runtime INV12 records the
implementation gap: the size-change glide moves onto the morph next.

Co-authored-by: Cindy Zhang <cindyxz@meta.com>
@vercel

vercel Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
astryx Ready Ready Preview Nov 11, 2026 5:51pm UTC

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Nov 3, 2026
@cixzhang

cixzhang commented Nov 3, 2026

Copy link
Copy Markdown
Contributor Author

/approve-spec 131ca14

@github-actions github-actions Bot added the needs:design-review Affects visuals — Design should review label Nov 3, 2026
@astracat-bot

astracat-bot Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

AI review status for this pull request.

Review status Updated
❓ Needs maintainer input (for maintainers only) Nov 11, 2026, 5:58 PM UTC

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approval waits on an engineering owner's decision about the rect morph's timing rule, and one fix is needed.

rectMorph.ts documents and implements a rule no current record states (a present or dismissal that a tap, a key or the program starts plays the theme's duration and easing from rest, and only a gesture's release plays a spring), which contradicts AST-094 FR31, where a touch menu grows on springs.menuOpen, and FR6, where a new target keeps the current velocity. An engineering owner either records the rule in AST-094 (amending FR31 and FR6) and approves that edit by commenting /approve-spec <full head SHA> on this pull request (the author may do so as an engineering owner), or the morph carries the presented velocity into a timed retarget and its example and changeset follow FR31.

Blocking

  • A dismissal onto a copy calls attach(null) on a handed-in animator (rectMorph.ts:448), and neither reset() nor dispose() attaches it again, so the owner's own set and play stop reaching the element; attach a handed-in animator again and add a test that the owner's write lands after a copy dismissal.

Everything else matches the current records, and the motion suites, the typecheck and the Chromium interaction spec pass.

[Automated review]

Comment thread packages/core/src/motion/rectMorph.ts Resolved
Comment thread packages/core/src/motion/rectMorph.ts Resolved
Comment thread packages/core/src/motion/rectMorph.ts Resolved
Comment thread packages/core/src/motion/rectMorph.ts Resolved
Comment thread packages/core/src/motion/rectMorph.ts Resolved
…opy dismissal

CodeBunny on #9769: a dismissal onto a copy detached the animator, and a
handed-in one was never attached again, so its owner's writes stopped
reaching the element. Only the morph's own animator lets go now.

Co-authored-by: Cindy Zhang <cindyxz@meta.com>

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approval waits on an engineering owner's decision about the rect morph's timing rule, and two fixes are needed whatever that decision is.

The morph documents and implements a rule no current spec states: a present or dismissal that a tap, a key or the program starts plays the theme's duration and easing from rest, and only a gesture's release plays a spring. That contradicts AST-094 FR31, where a touch menu grows on springs.menuOpen, and FR6, where a new target keeps the current velocity. To settle it, an engineering owner either records the rule in AST-094 (amending FR31 and FR6) and approves that edit by commenting /approve-spec <full head SHA> on this pull request (the author may do so as an engineering owner), or the morph carries the presented velocity into a timed retarget and its example and changeset follow FR31.

Blocking

  • timedProgressCurve (packages/core/src/motion/progress.ts:79-99) is a second copy of the timed curve solveAnimatedPageScroll already provides in paging.ts, without that curve's velocity carry, so move that curve with its optional velocity into progress.ts and call it from the pager and the morph.
  • The INV12 edit needs the ## Writing rubric results section in the description and should end at "its own compositor translate.)" instead of the plan "it moves onto the morph next", as the knowledge-contracts writing rules require.

The handed-in animator now stays on the element through a copy dismissal, reset() and dispose(), and the motion suites, the typecheck and the Chromium interaction spec pass.

[Automated review]

Comment thread packages/core/src/motion/progress.ts Resolved
…er and the morph

CodeBunny on #9769: solveAnimatedPageScroll already played this curve and
keeps the velocity of the move it retargets. timedCurve in progress.ts is
now that one curve; the pager and the morph's timed motion call it, so a
timed present that interrupts a dismissal keeps its velocity. The INV12
note states the system only. File header and comments trimmed.

Co-authored-by: Cindy Zhang <cindyxz@meta.com>
@cixzhang

cixzhang commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

/approve-spec e3a43d0

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approval waits on an engineering owner's decision about the rect morph's timing rule, and one fix is needed whatever that decision is.

The RectMorphMotion doc, its example and the changeset say that a present or dismissal a tap, a key or the program starts plays the theme's duration and easing, and only a gesture's release plays a spring; no current spec states that rule, and it contradicts AST-094 FR31, where a menu opened under a finger grows on springs.menuOpen, and FR4, where touch motion reads its named roles. To settle it, an engineering owner either records the rule in AST-094 (amending FR31 and FR4) and approves that edit by commenting /approve-spec <full head SHA> on this pull request (the author may do so as an engineering owner), or the doc, the example and the changeset name the presentation's motion roles instead.

Blocking

  • A timed present or dismissal that starts at rest is handed a velocity of 0 (packages/core/src/motion/rectMorph.ts:351 and :397-399), which timedCurve treats as a retarget and uses to cancel the easing's starting slope, so a 250 ms present on the entrance curve is 48 px wide at 50 ms where the easing gives 114 px; pass no velocity when nothing is in flight, and test a present from rest on an easing that does not start flat, such as [0, 0, 0.58, 1], against the easing itself.

A timed retarget now keeps its velocity, the duplicated timed curve is gone, and the motion and pager suites and the typecheck pass.

[Automated review]

Comment thread packages/core/src/motion/rectMorph.ts Resolved
Comment thread packages/core/src/motion/rectMorph.ts Resolved
Comment thread packages/core/src/motion/rectMorph.ts Resolved
Comment thread packages/core/src/motion/rectMorph.ts Resolved
Comment thread packages/core/src/motion/rectMorph.ts Resolved
Comment thread packages/core/src/motion/rectMorph.ts Resolved
CodeBunny on #9769: with nothing in flight the morph handed timedCurve a
velocity of 0, which it treats as a retarget and uses to cancel the
easing's starting slope. Only a motion in flight hands its velocity on now;
tests check a present from rest and a dismissal from a settled present
against the easing itself. Header and comments shortened.

Co-authored-by: Cindy Zhang <cindyxz@meta.com>

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approval waits on an engineering owner's decision about the rect morph's timing rule; nothing else blocks.

  • The RectMorphMotion doc (packages/core/src/motion/rectMorph.ts:109-115), its example (:214-215, a tapped menu on the theme's entrance duration and easing) and the changeset say that a present or dismissal a tap, a key or the program starts plays the theme's duration and easing, and only a gesture's release plays a spring; no current spec states that rule, and it contradicts AST-094 FR31, where a menu opened under a finger grows on springs.menuOpen and shrinks on springs.menuClose, and FR4, where touch motion reads the role named for its use.

To settle it, an engineering owner either records the rule in AST-094 (amending FR31 and FR4) and approves that edit by commenting /approve-spec <full head SHA> on this pull request (the author may do so as an engineering owner), or the doc, the example and the changeset name the presentation's motion roles instead. The earlier /approve-spec comments name earlier heads, so the INV12 note needs that same exact-head approval.

A timed motion from rest now plays the easing it is given, and the new tests fail without the fix. Non-blocking: the description's Testing line still says the unit suite has 35 cases including a timed present that ignores a given velocity; rectMorph.test.ts has 24 cases and the one at line 276 proves the opposite.

[Automated review]

The type's doc, the example and the changeset name no rule of their own
about which input plays which timing: each play takes the timing the
adopting component's motion record names (AST-094 FR45).

Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
@cixzhang

cixzhang commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

/approve-spec 3272828

@cixzhang

cixzhang commented Nov 9, 2026

Copy link
Copy Markdown
Contributor Author

/approve-spec 33a3ee8

vjeux added 2 commits November 9, 2026 19:55
A popover's grow path read its opacity on a straight line, so before
its start and past its end it went below 0: a turn-around toward the
source read -0.025 at 1.05. An opacity below 0 draws as 0, so
opacityAt now stops at 0. The popover proofs compare opacities as
drawn, since Popover's own model still reads below 0 there.
The anchored menu's case modeled a straight line from a 22 px square on
its control. The menu now grows out of its trigger on Popover's edge
path, which the popover cases already check, so the case no longer
describes any presentation's model. The file header follows.
@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec 6b6310c

@cixzhang cixzhang added the ci:run-a11y Run the accessibility suite on this PR; write access, next push (AST-030 FR12) label Nov 10, 2026

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved pending CI: CI's heavy suites have not run on this head yet; they run in the re-run before merge.

createRectMorph matches the current specs: it is the internal shared rectangle morph architecture:layer-runtime INV15 names, each play keeps the velocity on screen (spec:AST-094 FR6) and takes whichever timing the adopting component's motion record names (FR45), and the INV12 and AST-094 record edits carry an engineering owner's approval for this head. The motion unit suites and the morph's Chromium browser proof pass on this head.

Non-blocking:

  • Popover still plays its own copy of the edge growth (Popover/popoverSurfaceMotion.ts); morphPath.test.ts pins it to edgeGrowPath, and moving Popover onto the morph removes the copy.
  • Two comment suggestions (INV29) are inline.

#8485 does not contain this head yet, so the stack line does not hold right now and this pull request was reviewed on its own, the morph paths included.

[Automated review]

Comment on lines +6 to +12
* @output Exports `progressCurve`, a spring's progress from 0 to 1 as a
* MotionCurve. Exports `easedProgress`, a duration and easing that keeps a
* retargeted velocity, and `timedCurve`, that curve on values. Exports
* `mapProgress`, one curve as a function of another, and `mapRange`, the
* clamped linear map most companions use. Exports
* `presentedProgressVelocity`, the progress velocity that continues what
* an animator shows

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Non-blocking (INV29): this header now runs 39 lines, and this pull request rewrites its @output. Keep @input, @output, @position and SYNC:. Move the velocity-projection paragraph to presentedProgressVelocity's JSDoc and the companion paragraph to mapProgress's.

Comment on lines +318 to +321
// A spring goes through `resolveMotion`. A motion role resolves to its
// table value, and a bare spring comes back as given. `velocity` is
// undefined when nothing was moving: a timed motion then plays its easing
// as given.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Non-blocking (INV29): the first two sentences restate the call below. Suggested: "With no motion in flight, velocity is undefined, so a timed motion plays its easing as given."

…and the rect morph use timedCurve

easedProgress moves to the progress helpers byte for byte, so Popover and
SegmentedControl read the same positions and velocities as on main.
timedCurve is the pager's curve as on main, with one change: from rest,
its velocity at t <= 0 is the curve's own starting slope. The rect morph
plays its timed motion on timedCurve from 0 to 1.

callerMotionValues.test.ts pins easedProgress and the pager on a fixed
grid, bit for bit, to main's values.

Based on vjeux's test (vjeux/astryx coverage/rect-morph-first-instant)
… speed

A linear move of 300 px over 0.3 s, retargeted at t = 0, reads 0 px/s on
main, so the retarget starts from rest. Now it reads about 1000 px/s, the
curve's own starting slope, and the retarget keeps that speed. The
position stays continuous.

Based on vjeux's test (vjeux/astryx coverage/rect-morph-first-instant)
On main a 0 ms animated scroll reads NaN at t = 0 and its start position
before it. timedCurve's 0 ms guard puts it on its target with no speed,
as the rect morph's 0 ms timing does.
@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Nov 11, 2026
@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec 9d40852

@github-actions github-actions Bot removed the needs:spec-owner-review Current knowledge records await owner approval label Nov 11, 2026

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Changes requested: one blocking finding.

  • packages/core/src/motion/__tests__/callerMotionValues.test.ts:60 pins the easedProgress grid to 617cb8ab38270e58, but main's easedProgress and this branch's both produce e0dc2c4f1f0daee6 on that grid, so the new test fails; pin the value main's code actually produces, or compare against a frozen copy of main's function.

The rest matches the current specs (architecture:layer-runtime INV15, spec:AST-094 FR6 and FR45), the INV12 and AST-094 edits carry an engineering owner's approval for this head, and the morph's Chromium browser proof passes. Non-blocking: INV12 now states its condition twice (drop "once Core has that morph; until then…" and keep the gap sentence), and two INV29 comment suggestions from the earlier review still apply. The stack line does not hold (no listed pull request contains all the others), so this pull request was reviewed on its own, the morph paths included.

Automated review (maintainer link)

through the shared rectangle morph (`architecture:layer-runtime` INV15;
`spec:AST-094` FR5) once Core has that morph; until then, the module plays
its own compositor translate. It never animates height. Its walk visits only
its own compositor translate. (Implementation gap: Core has the shared

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Non-blocking (records carry no temporary status): now that Core has the morph, "once Core has that morph; until then, the module plays its own compositor translate" is sequencing that the gap sentence already covers. Suggested: "…played through the shared rectangle morph (architecture:layer-runtime INV15; spec:AST-094 FR5). (Implementation gap: the size-change glide still plays its own compositor translate.)"

…functions

A float64 hash of easedProgress's values is not the same on every
platform: macOS arm64 and CI's Linux give different hashes for main's
code. The test now keeps copies of easedProgress and
solveAnimatedPageScroll as they are on main (ebb9dde), and checks that
the shipped functions give the same number, bit for bit (Object.is), for
each position and velocity on the grid. From rest, the pager's velocity
at t <= 0 stays the one value left out.

Based on vjeux's test (vjeux/astryx coverage/rect-morph-first-instant)
@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec 6c17632

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved pending CI: CI's heavy suites have not run on this head yet; they run before merge.

createRectMorph and its paths are the shared rectangle morph the layer runtime calls for, follow the touch-motion timing and velocity rules, and the record edits are owner-approved for this head. The caller-value test now compares the shipped easedProgress and pager curve with copies of main's functions, bit for bit, so it holds on any platform.

Three optional cleanups from the earlier inline notes still stand: the long progress.ts header, the comment at rectMorph.ts:317-320 that restates its code, and INV12's "once Core has that morph; until then" clause, which the new gap sentence makes redundant.

Automated review (maintainer link)

…ect-morph

# Conflicts:
#	packages/core/src/SegmentedControl/useSegmentedSelectionMotion.ts
@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Nov 11, 2026

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approval waits on an engineering owner's decision about one record edit. No code change is needed.

  • This head adds the shared rectangle morph to Core. Current architecture:react-component-runtime INV12 says the size-change glide plays through that morph once Core has it. The pull request records the glide as an implementation gap instead, and that record edit, with the spec:AST-094 path link, has no engineering owner's approval at this head. An engineering owner records the decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner. If the gap is not accepted, the glide needs to play through createRectMorph in this pull request.

The morph's unit suite, the comparison of the existing callers' values and the Chromium browser proofs pass at this head. The open inline suggestions on header length, one comment and the INV12 wording are non-blocking.

Automated review (maintainer link)

This branch was successfully deployed

1 active deployment
Preview — 18fcf706 Deployed Nov 11, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) ci:run-a11y Run the accessibility suite on this PR; write access, next push (AST-030 FR12) CLA Signed This label is managed by the Meta Open Source bot. needs:design-review Affects visuals — Design should review needs:spec-owner-review Current knowledge records await owner approval

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants