Skip to content

fix(core): per-property-group keyframe foundations#1354

Merged
miguel-heygen merged 1 commit into
mainfrom
fix/kf-parser-foundations
Jun 12, 2026
Merged

fix(core): per-property-group keyframe foundations#1354
miguel-heygen merged 1 commit into
mainfrom
fix/kf-parser-foundations

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add PropertyGroupName type system (position/scale/size/rotation/visual/other) and PROPERTY_GROUPS constant
  • Implement classifyPropertyGroup and classifyTweenPropertyGroup classification functions
  • Parser generates group-aware animation IDs (e.g. #box-to-0-position)
  • Add position resolver for +=, -=, <, > timeline position strings
  • Numeric keyframe matching with 2% tolerance
  • ID rescue on all parser mutators

Test plan

  • All 1466 core tests pass
  • Golden baselines updated for new propertyGroup field
  • Parser stress tests updated for group suffix IDs

Stack: 1/7

Add PropertyGroupName type system (position/scale/size/rotation/visual/other),
PROPERTY_GROUPS constant, classifyPropertyGroup/classifyTweenPropertyGroup
functions. Parser generates group-aware animation IDs, resolves position strings
(+=, -=, <, >), uses numeric matching with 2% tolerance, and preserves IDs
across all mutations.
@github-actions

Copy link
Copy Markdown

Fallow audit report

Found 64 findings.

Duplication (44)
Severity Rule Location Description
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:306 Code clone group 1 (8 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:338 Code clone group 2 (12 lines, 4 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:357 Code clone group 1 (8 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:383 Code clone group 2 (12 lines, 4 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:397 Code clone group 2 (12 lines, 4 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:438 Code clone group 2 (12 lines, 4 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:1585 Code clone group 3 (5 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:1594 Code clone group 3 (5 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:1605 Code clone group 4 (5 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:1643 Code clone group 4 (5 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:1793 Code clone group 5 (22 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:1844 Code clone group 5 (22 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:1972 Code clone group 6 (10 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:1994 Code clone group 7 (6 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2008 Code clone group 8 (8 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2024 Code clone group 6 (10 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2043 Code clone group 7 (6 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2095 Code clone group 9 (7 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2106 Code clone group 10 (11 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2121 Code clone group 8 (8 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2123 Code clone group 9 (7 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2135 Code clone group 10 (11 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2166 Code clone group 11 (10 lines, 4 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2196 Code clone group 13 (9 lines, 3 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2196 Code clone group 11 (10 lines, 4 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2196 Code clone group 12 (15 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2224 Code clone group 11 (10 lines, 4 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2224 Code clone group 13 (9 lines, 3 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2240 Code clone group 13 (9 lines, 3 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2240 Code clone group 12 (15 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.test.ts:2240 Code clone group 11 (10 lines, 4 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.ts:602 Code clone group 14 (7 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.ts:714 Code clone group 14 (7 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.ts:1330 Code clone group 15 (12 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.ts:1384 Code clone group 15 (12 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.ts:1755 Code clone group 16 (8 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.ts:2012 Code clone group 17 (10 lines, 3 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.ts:2012 Code clone group 16 (8 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.ts:2048 Code clone group 18 (10 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.ts:2048 Code clone group 17 (10 lines, 3 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.ts:2087 Code clone group 17 (10 lines, 3 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.ts:2087 Code clone group 18 (10 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.ts:2224 Code clone group 19 (18 lines, 2 instances)
minor fallow/code-duplication packages/core/src/parsers/gsapParser.ts:2307 Code clone group 19 (18 lines, 2 instances)
Health (20)
Severity Rule Location Description
critical fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:85 'resolveNode' has CRAP score 315.9 (threshold: 30.0, cyclomatic 36)
minor fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:159 'selectorFromQueryCall' has CRAP score 49.5 (threshold: 30.0, cyclomatic 13)
minor fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:237 'visitCallExpression' has CRAP score 43.1 (threshold: 30.0, cyclomatic 12)
minor fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:296 'resolveTargetSelector' has CRAP score 43.1 (threshold: 30.0, cyclomatic 12)
minor fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:325 'objectExpressionToRecord' has CRAP score 43.1 (threshold: 30.0, cyclomatic 12)
minor fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:365 'extractTimelineDefaults' has CRAP score 31.6 (threshold: 30.0, cyclomatic 10)
minor fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:445 'visitCallExpression' has CRAP score 31.6 (threshold: 30.0, cyclomatic 10)
minor fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:620 'computeKeyframesTotalDuration' has CRAP score 37.1 (threshold: 30.0, cyclomatic 11)
critical fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:750 'parseMotionPathNode' has CRAP score 210.7 (threshold: 30.0, cyclomatic 29)
minor fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:956 'resolvePositionString' has CRAP score 49.5 (threshold: 30.0, cyclomatic 13)
minor fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:1250 'buildTweenStatementCode' has CRAP score 31.6 (threshold: 30.0, cyclomatic 10)
critical fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:1749 'addKeyframeToScript' has CRAP score 37.9 (threshold: 30.0, cyclomatic 34)
major fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:1942 'resolveConversionProps' has CRAP score 49.5 (threshold: 30.0, cyclomatic 13)
major fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:2131 'buildMotionPathObjectCode' has CRAP score 56.3 (threshold: 30.0, cyclomatic 14)
critical fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:2181 'setArcPathInScript' has CRAP score 315.9 (threshold: 30.0, cyclomatic 36)
major fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:2289 'updateArcSegmentInScript' has CRAP score 63.6 (threshold: 30.0, cyclomatic 15)
critical fallow/high-crap-score packages/core/src/parsers/gsapParser.ts:2497 'unrollDynamicAnimations' has CRAP score 482.4 (threshold: 30.0, cyclomatic 45)
minor fallow/high-crap-score packages/core/src/parsers/gsapSerialize.ts:94 'lines' has CRAP score 43.1 (threshold: 30.0, cyclomatic 12)
minor fallow/high-crap-score packages/core/src/parsers/gsapSerialize.ts:248 '<arrow>' has CRAP score 37.1 (threshold: 30.0, cyclomatic 11)
minor fallow/high-crap-score packages/core/src/parsers/gsapSerialize.ts:300 '<arrow>' has CRAP score 43.1 (threshold: 30.0, cyclomatic 12)

Generated by fallow.

@miguel-heygen miguel-heygen marked this pull request as ready for review June 12, 2026 00:18

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: approve-with-nits. Load-bearing base of the 7-PR restructure; all 3 R2 carry-overs from monolithic #1344 resolved.

R2 carry-overs

  • Goldens refreshed ✅ — moderate.parsed.json now contains 4 propertyGroup instances + new resolvedStart/implicitPosition fields; complex.parsed.json / fromto.parsed.json / minimal.parsed.json updated. CI Test + Tests on windows-latest green. (Vai R1 Blocker 1 closed.)
  • fromTo "inversion" intent now explicit ✅ — gsapParser.ts:1932-1972 resolveConversionProps carries a docstring stating resolvedFromValues represents the new destination of a drag and is intentionally merged into toProps for fromTo() (L1964-1968: "This is intentional and not inverted."). The R2 concern was a misread of intent; author made it unmisreadable. (Vai R1 Blocker 3 closed.)
  • ID-suffix scope bounded by inline invariant ✅ — gsapParser.ts:1031-1039: "No database, localStorage, or file stores animation IDs, so changing the ID format (e.g. adding a -scale/-position suffix) is safe." Group suffix emitted at L1047-1048. Did not independently verify the no-persistence claim in pacific or studio localStorage — if false, it'd be a separate-PR break, not this PR's regression. Worth a one-line cross-repo grep before flipping any cohort that depends on stable IDs.

Taxonomy + types

  • PROPERTY_GROUPS taxonomy at gsapConstants.ts:52-86 (defines PropertyGroupName + PROPERTY_GROUPS + classifyPropertyGroup + classifyTweenPropertyGroup). Subpath export ./gsap-constants exists in packages/core/package.json:77-80 — downstream studio PRs can consume via @hyperframes/core/gsap-constants. ✅
  • New GsapAnimation fields all optionalgsapSerialize.ts:34,36,39 adds resolvedStart?, implicitPosition?, propertyGroup?. Backwards-compat for existing serialized state.
  • playerStore.activeKeyframePct NOT in this PR — expected in #1356 (confirmed there).

Concerns

  • Fallow audit 64 findings (only failing CI check) — 44 minor test-scaffolding clones in gsapParser.test.ts + 20 health/CRAP-score warnings. resolveConversionProps lands at CRAP 49.5 (major). Pre-existing criticals (unrollDynamicAnimations 482, resolveNode 315, setArcPathInScript 315, parseMotionPathNode 210) are getting worse in the foundations slot, not better. Worth a follow-up extraction PR before the rest of the stack lands more parser growth on top.
  • Date.now() ID at gsapParser.ts:1328 in addAnimationToScript — author-time studio mutator path; ID rebuilds canonically on next parseGsapScript. Not a determinism violation, but consider a monotonic counter for in-flight sessions where two adds within the same ms produce duplicate transient IDs.

Test coverage

gsapParser.test.ts +341/-10, ~145 it() blocks total, 51 references to new propertyGroup/classify/resolvedStart/implicitPosition machinery. New describe blocks: resolvedStart — timeline position resolution (L292), splitIntoPropertyGroups (L2163).

Net

Solid foundation. The Fallow trend is the only real concern — extraction follow-up before more parser growth lands.

Review by Rames D Jusso

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

R3 verification — approve-with-nit.

The new docstring at gsapParser.ts:1093-1096 ("This is intentional and not inverted") closes my R2 axis (the fromTo direction concern). Via's R2 ask — rename resolveConversionProps to resolvedLiveValues OR split into per-method helpers — still open. Not blocking; the inline docstring shields the next reader, but the rename would have been cheaper than the comment.

Review by Rames D Jusso

@vanceingalls vanceingalls left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM — Via reviewed, stamp applied.

— Vai

@miguel-heygen miguel-heygen merged commit 889e9f0 into main Jun 12, 2026
46 of 47 checks passed
@miguel-heygen miguel-heygen deleted the fix/kf-parser-foundations branch June 12, 2026 04:02
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.

3 participants