Skip to content

feat(sdk,core): ws-3 — unrollDynamicAnimations acorn port + SDK op#1501

Merged
vanceingalls merged 3 commits into
mainfrom
sdk-ws3-unroll-dynamic
Jun 17, 2026
Merged

feat(sdk,core): ws-3 — unrollDynamicAnimations acorn port + SDK op#1501
vanceingalls merged 3 commits into
mainfrom
sdk-ws3-unroll-dynamic

Conversation

@vanceingalls

@vanceingalls vanceingalls commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Ports unrollDynamicAnimations from the recast-based AST writer to the acorn-based writer, and exposes it as an SDK op so Studio can convert loop/helper-computed tweens into directly-editable static calls.

Background: When GSAP source uses a JS loop or helper function to generate tweens, the Studio panel shows those tweens as editability: "unroll" (read-only). The unroll operation replaces the loop body with a flat sequence of literal tl.to()/tl.from() calls, making each element's tween independently editable.

  • packages/core/src/parsers/gsapWriterAcorn.ts (+85 lines): implements unrollDynamicAnimations(elements, gsapSource) using the acorn AST writer — locates the originating loop/helper call by sourceRange, builds replacement literal tween calls via buildUnrollReplacement(), and splices the new nodes in place
  • packages/sdk/src/types.ts (+10 lines): adds UnrollElement export type and the unrollDynamicAnimations op variant to the MutateOp union
  • packages/sdk/src/engine/mutate.ts (+8 lines): dispatches the new op to gsapWriterAcorn.unrollDynamicAnimations()
  • packages/core/src/parsers/gsapWriter.parity.test.ts (+69 lines): parity tests ensuring the acorn writer produces identical output to the recast writer for the unroll operation across five representative loop patterns

Test plan

  • bun test packages/core — parity tests pass
  • bun test packages/sdk — mutate dispatch test passes
  • Manual: open a composition with a forEach-based GSAP animation → Studio shows unroll button → click unroll → loop replaced with literal tweens, all editable

🤖 Generated with Claude Code

This was referenced Jun 16, 2026

vanceingalls commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@miga-heygen miga-heygen 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.

Review: feat(sdk,core): ws-3 — unrollDynamicAnimations acorn port + SDK op

Good work porting the unroll operation and wiring it through the SDK. The parity test suite is excellent — the modelOf() differential approach is exactly right for catching serialization drift between the two writers. The shift/scale and arc-path parity expansions included here are a nice bonus that strengthens the safety net for the whole stack tip.

A few things worth discussing:

1. Unroll dispatched through applyArcPathOp — semantic mismatch (medium)

unrollDynamicAnimations is routed through applyArcPathOp in mutate.ts, which puts it in the arc-path switch. The underlying handleArcPathScript is generic (just setGsapScript + gsapScriptChange), so this works, but "unroll dynamic animations" has zero conceptual relationship to arc paths. Six months from now someone reading the dispatch table will be confused by an unroll case nested inside applyArcPathOp.

Suggestion: either (a) extract the generic handleArcPathScript as handleScriptRewrite and route unroll through a new applyUnrollOp function that calls it, or (b) at minimum rename handleArcPathScript to something neutral like handleScriptMutation and move the unroll case into applyGsapOp directly. The validateOp grouping should follow suit — unroll is currently lumped with the arc-path trio in the validation switch.

2. Duplicate UnrollElement type definition (low-medium)

UnrollElement is defined in gsapWriterAcorn.ts (lines ~1724-1729) and the identical shape is inlined in the EditOp union in types.ts (the elements field of unrollDynamicAnimations). Two sources of truth for the same shape — if someone adds easeEach handling to one and forgets the other, they'll diverge silently.

Consider: have types.ts import and reference the core type, or extract a shared type that both consume. The SDK types package is the canonical contract, so one option is defining UnrollElement there and having the core writer import it.

3. unrollId wrapper is unnecessary (nit)

In the test file, unrollId(script) is defined as:

function unrollId(script: string): string {
  const p = acornId(script);
  return p;
}

This is an identity function over acornId. It adds a layer of indirection with no semantic value — just call acornId directly in the unroll tests. If the intent was to leave a hook for future divergence, a comment would serve better than a wrapper.

4. buildUnrollReplacement indentation assumption (nit)

The joiner calls.join("\n ") hard-codes 2-space indentation for the replacement block. This works today because all test fixtures and real scripts use 2-space indentation at the loop level, but if a script had different indentation (tabs, 4-space), the unrolled block would be visually misaligned. Not a bug — the acorn writer is string-splice-based and doesn't preserve indentation generally — but worth a comment noting the assumption, since the recast writer does auto-indent.

5. Position defaults in buildUnrollReplacement

const pos = animation.position ?? 0;

When animation.position is undefined (implicit position / append-to-end-of-timeline), defaulting to 0 changes the semantic from "append after previous tween" to "start at absolute 0". The recast writer may handle this differently. The parity tests cover the explicit-position cases well (and the "non-loop fallback — label position" case covers string positions), but it would be worth adding a parity case for a tween with NO explicit position argument to make sure both writers agree on the default.

6. Test coverage is strong

The parity suite is thorough: for-loop, forEach, forEach-with-ease, non-loop fallback, plus the arc trio multi-segment expansion. The modelOf() approach of stripping per-parse metadata and comparing authored shapes is the right level of abstraction — it catches real semantic drift without being brittle to formatting differences. The shift/scale parity tests covering clamp-to-zero, string-position skip, adjacent-position, and implicit-position cases are well-chosen edge cases.


Overall: The implementation is correct, well-tested, and the core acorn writer logic is clean. The routing concern (#1) is the only thing I'd want addressed before merge — it's a maintainability issue for this stack tip where integration clarity matters. The type duplication (#2) is worth fixing but not blocking. Everything else is nit-level.

LGTM with the routing nit addressed — pinging @magi for the stamp. <@U0B1J4SL8H3>

— Miga

@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.

Review: stack tip — unrollDynamicAnimations + §4 parity + shift/scale port

Reviewed at HEAD db16b977 (same SHA Miga reviewed at). Layering on top of Miga's comment rather than re-litigating the routing concern.

Stack-tip cumulative-diff audit

Per the stack-tip review rubric: scanned git diff origin/main...db16b977 to check for fixes hoisted from earlier-stack PRs.

Verdict: clean — no hoists. All three commits touch only packages/core/src/parsers/{gsapWriter.parity.test.ts,gsapWriterAcorn.ts} and packages/sdk/src/{engine/mutate.ts,types.ts}. The 4-file scope advertised on the PR is honest. No edits to studio side, no edits to files claimed by #1500 / #1497 / #1498 / earlier ws-3 PRs. The updateAnimationInScript refactor (extracting overwritePosition) is a clean refactor inside this PR's net-new code, not a fix to a latent bug from an earlier PR.

PR body / diff mismatch (concern)

PR body describes commit 1 (unroll port) only. Commits 2 + 3 — the §4 parity expansion (+386 LOC of recast-vs-acorn parity for arc trio / unroll / addKeyframe / removeKeyframe / addAnimationWithKeyframes) and the shift/scale port (+76/-7 to writer, +75 to parity tests) — are NOT mentioned. Three commits' worth of work, one paragraph of body. For the stack-tip PR that closes the WS-3.F op-coverage gate, that's worth a body refresh so future archaeologists know what landed where.

unrollDynamicAnimations (port correctness)

Compared gsapWriterAcorn.ts:1722-1782 against the recast original at gsapParser.ts:2501-2587. Two semantic divergences worth flagging — neither is exposed by the new parity tests:

  1. Duration / ease source differs. Recast reads duration/ease from the original tween's varsArg via findPropertyNode + extractLiteralValue — strictly the tween's authored value. Acorn reads animation.duration/animation.ease from the parsed model, which has applyTimelineDefaults baked in. For a tween that relies on a gsap.timeline({ defaults: { duration: 1.5 } })-level default and has no explicit duration on the tween, recast falls through to the 8 fallback; acorn inherits 1.5. Real divergence, but the four parity cases all set explicit duration on the tween (or no default on the timeline), so the test suite doesn't catch it. Add a timeline({ defaults: { duration, ease } }) case to UNROLL_PARITY_CASES and the gap is closed.

  2. Position source differs. Recast: posArg ? extractLiteralValue(posArg) : 0. Acorn: animation.position ?? 0. For a forEach tween with no explicit position arg, recast → 0; acorn → whatever resolveTimelinePositions derived from prior-tween source order (often 0, but not always). Same shape as #1 — add a no-position-arg + nonzero-prior-tween case to surface it.

These map to Miga's #5 concern (pos = animation.position ?? 0 default) — verifying against recast confirms the implementations actually diverge, not just the API shapes.

§4 parity coverage — assessment

The §4 parity expansion (commit 2ca772a) adds real recast-vs-acorn differential coverage (using modelOf() to strip per-parse metadata) for:

  • Arc trio (set/update/remove) — 2 fixtures × 3 ops = 6 cases ✅
  • unrollDynamicAnimations — 4 cases (for-loop, forEach, forEach-with-ease + nonzero position, non-loop fallback) ✅
  • addKeyframeToScript — 10 cases including the non-object whole-value overwrite branch the commit message flags as previously-broken ✅
  • removeKeyframeFromScript — 7 cases including collapse-to-flat with _auto carry + per-keyframe-ease drop ✅
  • addAnimationWithKeyframesToScript — 2 cases (minimal + moderate) ✅

This significantly strengthens the WS-3.F gate. Two coverage notes:

  • Commit message overstates. The headline says "acorn fixes for arc/unroll/keyframe-add/%-removeKeyframe/add-with-keyframes" but commit 2 modifies only the parity test file — no gsapWriterAcorn.ts edits. If fixes were applied, they happened earlier in the stack or are pinned-by-test (parity tests would have failed before). Reword the commit headline to "parity tests" only, since the writer isn't touched.
  • updateAnimationInScript parity gap. §4 parity does NOT add an updateAnimationInScript case covering updates.extras (the asymmetry I flagged on HF#1533 earlier today). HF#1533's updates.extras open item remains open after this PR. Worth a follow-up parity case in a future WS-3.G PR — not blocking #1501.

Shift/scale port (commit 3) — verdict ✅

Direct comparison against recast shiftPositionsInScript/scalePositionsInScript at gsapParser.ts:1326-1385:

  • Arithmetic is 1:1 (shift: max(0, round((pos+delta)*1000)/1000); scale: round((newStart + (pos-oldStart)*ratio) * 1000)/1000, dur: max(0.001, round(dur*ratio*1000)/1000).
  • oldDuration <= 0 || newDuration <= 0 early-return matches.
  • overwritePosition extraction + adoption by updateAnimationInScript is a clean refactor — the old posIdx = call.method === "fromTo" ? 3 : 2; call.node.arguments?.[posIdx] and the new call.positionArg resolve to the same node (parser already exposes positionArg = args[3] for fromTo, else args[2]). No behavior change for updateAnimationInScript.
  • 10 recast-vs-acorn parity cases cover clamp-to-zero, string-position skip, adjacent-position, implicit-position. Good coverage.

One very minor diagnostic asymmetry: recast's try { … } catch (e) { console.warn(…); return script; } emits a warn on parse failure; acorn's parseGsapScriptAcornForWrite swallows the catch and returns null silently → caller returns the original script with no signal. At cutover time when an oncall is investigating "why didn't this timeline-move land", the recast path logged; the acorn path won't. Not blocking — but worth a console.warn("[gsap-writer-acorn] parse failed in shiftPositionsInScript", e) to match the recast behavior. Same goes for the unroll path and any other writer that hangs off parseGsapScriptAcornForWrite.

Concerns (non-blocking)

  1. PR body claims "parity tests" for the unroll commit (1aa31f5) — they aren't. The 4 tests added in commit 1 (describe("unrollDynamicAnimations: acorn output correctness", …)) are acorn-only correctness assertions; no recast comparison. The actual recast-vs-acorn parity arrived in commit 2. Body bullet should read "correctness tests" for commit 1 and "parity tests" gets credited to the §4 expansion. ([[feedback_author_claim_diff_mismatch]].)

  2. elements: [] corrupts the script. buildUnrollReplacement with empty elements returns "", which then ms.overwrite(loop.start, loop.end, "") — silently deletes the loop. The recast version has the same bug, so this is a faithful port of latent behavior. But the new validateOp case for unrollDynamicAnimations only checks for a GSAP script — it should also reject op.elements.length === 0 before dispatch. Cheap fix, surfaces during port.

  3. Re Miga's #1 (routing through applyArcPathOp). +1. The cleanest factoring is to rename handleArcPathScripthandleScriptRewrite (it's already generic — just setGsapScript + gsapScriptChange), pull unroll out into applyGsapOp directly, and split the validateOp case grouping. Otherwise the dispatch map's "arc-path" mental model gets sticky.

  4. Re Miga's #2 (duplicate UnrollElement). +1. Define in types.ts (SDK is the canonical contract surface) and import into gsapWriterAcorn.ts. The core writer already imports a few SDK-shape types elsewhere, so the import direction is established.

Nits

  • unrollId(script) is acornId(script) with no transformation — Miga's #3. Drop the wrapper.
  • buildUnrollReplacement hard-codes "\n " joiner — fine, but [[feedback_iterative_merge_behind_env_rubric]] says sharp on contracts: noting in a comment that "acorn writer is splice-based and does not auto-indent; 2-space joiner matches the convention in the rest of this file" would help the next archaeologist.

Overall: The cumulative-diff audit comes back clean — no fix-hoists from earlier-stack PRs, scope is honest at the file level (less so at the commit-body level — see concern #1). Unroll port is correct modulo two parity-test gaps that should be plugged in the same commit that lands the parity expansion. Shift/scale port is 1:1 with recast and well-covered. The §4 parity expansion is genuine recast-vs-acorn differential coverage and meaningfully closes the WS-3.F op-coverage gate.

Routing concern (Miga's #1) + elements: [] validator gap are the two things I'd want addressed before stamping. Everything else is solid.

— Rames D Jusso

@vanceingalls vanceingalls left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

First pass at HEAD db16b977. Co-reviewers (Miga, Rames) have landed independent passes at this same SHA; layering rather than restating. As stack tip, the cumulative final-state reading per [[graphite-stack-review-final-state]] applies — what lands on main when this 18-PR stack squash-merges sequentially.

Verdict. Clear with minor nits — ready for stamp.

The headline anti-band-aid finding (the long-term-solution moment). This PR is where the parity-discipline-via-mechanical-equality pattern is generalized across the writer family. The §4 parity expansion (commit 2ca772a) adds genuine recast-vs-acorn expect(modelOf(acornOut)).toEqual(modelOf(recastOut)) coverage for:

  • Arc path trio (set/update/remove) — gsapWriter.parity.test.ts:592. Two fixtures × 3 ops = 6 cases. This is the exact restoration of the discipline that #1500's per-PR view appeared to break. Equality assertions at lines 605-606, 615-619, 628-629.
  • unrollDynamicAnimations — line 695, equality at 710. 4 cases (for-loop, forEach, forEach-with-ease, non-loop label fallback).
  • addKeyframeToScript — line 752, equality at 764. 10 cases including non-object whole-value overwrite.
  • removeKeyframeFromScript — line 846, equality at 850-851. 7 cases including collapse-to-flat with _auto carry + per-keyframe-ease drop.
  • addAnimationWithKeyframesToScript — line 899, equality at 907 / 918.
  • shiftPositionsInScript / scalePositionsInScript — lines 929 / 968, equality at 933 / 972 (plus targeted cases at 940/947/954/960 and 979).

I cross-checked the per-PR view from 06-16 and the stack-tip reality matches the rule: when the regime-coherence pattern lives across multiple PRs in a stack, the final state is the artifact that lands on main, not any single PR's diff. The discipline that #1499 established and #1500 appeared to break is restored at this PR. This is exactly the anti-band-aid resolution Miguel's bar asks for — the long-term solution, not a wedge. Celebrate it; this is the model HF#1448's third refinement also pointed at.

Stack-tip cumulative-diff audit (per [[verify-r2-nits-across-stack]] Flavour 1+2). Per Rames's pass, the cumulative diff origin/main...db16b977 is honest — the 4 files this PR touches don't smuggle fixes from earlier-stack PRs. The updateAnimationInScript refactor (extracting overwritePosition) is a clean refactor inside this PR's net-new code, not a fix to a latent bug from an earlier PR. No stale-base squash hazard.

Title-vs-scope drift. Title says ws-3 — unrollDynamicAnimations acorn port + SDK op. Three commits:

  1. 1aa31f5 ws-3 — unrollDynamicAnimations acorn port + SDK op
  2. 2ca772a §4 parity expansion (arc trio + unroll + addKeyframe + %-removeKeyframe + addAnimationWithKeyframes parity tests)
  3. 0a6bc4f shift/scale acorn port + 10 parity cases

Commits 2 and 3 are the load-bearing work of this PR (they're what closes the parity-gap-on-#1500). Body refresh is non-optional given the size and the WS-3.F-gate-closing nature of commit 2. One paragraph explaining "commit 1 lands the unroll port; commit 2 is the §4 parity expansion that retroactively pins all five prior ws-3 ops + arc trio to recast as source of truth; commit 3 ports shift/scale and adds its own parity cases" would save the next archaeologist a real audit.

Findings worth banking (concur with Miga + Rames; all comment-tier).

  • Concur with Miga's routing concern. unrollDynamicAnimations is dispatched through applyArcPathOp in mutate.ts. The underlying handleArcPathScript is generic (setGsapScript + gsapScriptChange), so it works, but "unroll dynamic animations" has zero conceptual relationship to arc paths. Six months from now someone reading the dispatch table will be confused by an unroll case nested inside applyArcPathOp. Cleanest factoring: rename handleArcPathScript to handleScriptRewrite (it's already generic), pull unroll into a new applyUnrollOp, split the validateOp case grouping. Otherwise the dispatch map's "arc-path" mental model gets sticky.
  • Concur with Miga + Rames on UnrollElement duplication. UnrollElement is defined in gsapWriterAcorn.ts:~1724-1729 and the identical shape is inlined in the EditOp union in types.ts (the elements field of unrollDynamicAnimations). Two sources of truth for the same shape — if someone adds an extra field (e.g. easeEach handling) to one and forgets the other, they'll diverge silently. Same band-aid pattern as the #1500 ArcPathSegment curviness contradiction. Define in types.ts (SDK is the canonical contract surface) and import into gsapWriterAcorn.ts.
  • Concur with Rames's two semantic divergences vs recast. I retraced both:
    1. Duration / ease source differs. Recast reads duration/ease from the original tween's varsArg; acorn reads animation.duration/animation.ease from the parsed model which has applyTimelineDefaults baked in. For a tween relying on a timeline({ defaults: { duration: 1.5 } }) and no explicit duration, recast → 8 (the fallback); acorn → 1.5 (the default). Real divergence; the four parity cases set explicit duration so the test doesn't catch it. Add a timeline({ defaults: { duration, ease } }) case to UNROLL_PARITY_CASES and the gap is surfaced (and likely fixable in the writer). Same approach #1499's extras boolean drop should follow.
    2. Position source differs. Recast: posArg ? extractLiteralValue(posArg) : 0. Acorn: animation.position ?? 0. For a forEach tween with no explicit position arg, recast → 0; acorn → whatever resolveTimelinePositions derived from prior-tween source order (often 0, but not always). Add a no-position-arg + nonzero-prior-tween case to the parity matrix to surface it.
  • Concur with Rames's elements: [] validator gap. buildUnrollReplacement with empty elements returns "", which then ms.overwrite(loop.start, loop.end, "") — silently deletes the loop. Faithful port of the recast latent behavior, but the new validateOp case for unrollDynamicAnimations should reject op.elements.length === 0 before dispatch. Cheap fix.
  • Concur with Rames's diagnostic-asymmetry note on parser-failure logging. Recast's shiftPositionsInScript/scalePositionsInScript emit console.warn on parse failure; acorn's parseGsapScriptAcornForWrite swallows the catch silently. When an oncall investigates "why didn't this timeline-move land," recast logged, acorn won't. A console.warn("[gsap-writer-acorn] parse failed in shiftPositionsInScript", e) matches the recast contract — also applies to the unroll path and any other writer that hangs off parseGsapScriptAcornForWrite.
  • Concur with Miga on unrollId wrapper redundancy. In the test file, unrollId(script) = acornId(script). Identity wrapper, no semantic value. Drop the wrapper or leave a comment explaining the hook.
  • Concur with Miga on buildUnrollReplacement hard-coded "\n " joiner. Acorn writer is splice-based and doesn't auto-indent; the 2-space joiner matches the convention in the rest of the file but is a silent assumption. A comment closes the door.

On the per-claim correctness of commit 2's message ("acorn fixes for arc/unroll/keyframe-add/%-removeKeyframe/add-with-keyframes"). Rames flagged this: the commit modifies only the parity test file. No gsapWriterAcorn.ts edits in commit 2. If fixes were applied, they happened earlier in the stack or the parity tests would have failed. Reword the commit headline to "parity tests" only — the writer isn't touched. Co-signing this.

Dispatch chain (independent re-trace at db16b977). unrollDynamicAnimations flows into applyArcPathOp (cosmetically wrong, see Miga #1), then through handleArcPathScriptunrollDynamicAnimationsInScript (the new acorn writer) → setGsapScript. validateOp arm at mutate.ts:~1107-1110 is grouped with arc-path. setArcPath/updateArcSegment/removeArcPath still route through the same applyArcPathOp cleanly. shift/scale don't enter the apply-op path (they're called directly from updateAnimationInScript and the studio drag-commit path). ✓ — no dispatch unreachability.

CI. Green on db16b977 across regression, player-perf, preview-regression, all 8 shards. Parity tests run in the vitest job — the safety net is exercised on every push.

Cleared at db16b977. Re-verify if HEAD moves before stamp.

Review by Via

@vanceingalls

Copy link
Copy Markdown
Collaborator Author

Review finding addressed in #1539 (commit 7d7732c8f): unrollDynamicAnimations overwrote the entire loop body with only the unrolled tl.to() calls, destroying other loop-body statements (e.g. a tl.set initial-state). Now preserves non-target statements per iteration; test asserts the tl.set lines survive after unrolling the tl.to.

miguel-heygen
miguel-heygen previously approved these changes Jun 17, 2026

@miguel-heygen miguel-heygen 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.

Approved as part of SDK cutover stack. Reviewed by Miga, Rames D Jusso, and Via across R1-R4. LGTM.

jrusso1020
jrusso1020 previously approved these changes Jun 17, 2026

@jrusso1020 jrusso1020 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.

Stack-wide stamp — audited bottom-up at the #1539 stack-tip (Rames D Jusso R4 + Miga + Via verified all 16 R3 + 2 CF2 findings at 6c2d66892). SDK-cutover chain cleared end-to-end.

Base automatically changed from sdk-ws3-arc-path to main June 17, 2026 23:53
@vanceingalls vanceingalls dismissed stale reviews from jrusso1020 and miguel-heygen June 17, 2026 23:53

The base branch was changed.

vanceingalls and others added 3 commits June 17, 2026 16:53
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
…ame-add/%-removeKeyframe/add-with-keyframes (WS-3.F gate)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
shiftPositionsInScript + scalePositionsInScript were recast-only GSAP-script
writers reachable from executeGsapMutation (shift-positions/scale-positions),
called by Studio timeline clip move/resize — the last write ops blocking recast
retirement. Ported to gsapWriterAcorn.ts mirroring recast's arithmetic
(shift: max(0,pos+delta); scale: remap pos by duration ratio + scale duration),
reusing a shared overwritePosition helper (also adopted by updateAnimationInScript).
Adds 10 recast-vs-acorn parity tests. Closes the WS-3.F op-coverage gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vanceingalls vanceingalls force-pushed the sdk-ws3-unroll-dynamic branch from db16b97 to 8d17b08 Compare June 17, 2026 23:53
@vanceingalls vanceingalls merged commit 09cefc1 into main Jun 17, 2026
12 checks passed
@vanceingalls vanceingalls deleted the sdk-ws3-unroll-dynamic branch June 17, 2026 23:54
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.

5 participants