Skip to content

feat(sdk): stage 7 step 2 — setSelection API#1442

Merged
vanceingalls merged 4 commits into
mainfrom
sdk-stage7-setselection
Jun 15, 2026
Merged

feat(sdk): stage 7 step 2 — setSelection API#1442
vanceingalls merged 4 commits into
mainfrom
sdk-stage7-setselection

Conversation

@vanceingalls

@vanceingalls vanceingalls commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

What

Adds session.setSelection(ids: string[]) to the SDK Composition API. Fires a selectionchange event on the session; does not create an undo entry or emit a patch.

Why

Studio needs to mirror canvas selection state into the SDK session so downstream consumers (agent context, inspector panels, future collaborative cursors) can read who is selected without polling the DOM. Selection is metadata, not content — it must not appear in the undo stack or trigger a persist write.

How

  • setSelection stores the id array in session state and emits selectionchange
  • No patch event, no history entry, no persist write
  • session.getSelection() returns the current ids
  • 11 unit tests covering: set/get round-trip, selectionchange fires, multiple calls replace not append, empty array clears, no patch event emitted

Test plan

vanceingalls commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator Author

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

Verified at HEAD 88f067ef.

Stage 7 step 2 — setSelection API (+96/-0). Thin state-setter plus selectionchange dispatch, well-tested. Verdict: approve-with-concerns.

What's good

  • Defensive copy on both input ([...ids]) and output (h([...this.currentSelection])) — listeners can't mutate internal state. Good.
  • Explicit tests that setSelection does NOT push to undo stack (line 269) and does NOT emit a patch event (line 281). Exactly the determinism / replay-safety properties the stack needs for #1450's shadow-parity to work.
  • selection() proxy snapshots ids at call time (line 261) — no stale-ref hazard.

Concerns

  • No equality check before firing selectionchange (packages/sdk/src/session.ts:224-227). setSelection(["hf-title"]) followed by setSelection(["hf-title"]) fires selectionchange twice. Wired into Studio via #1443's useSdkSelectionSync, this becomes a real perf risk: any React render that recreates the domEditGroupSelections array (even with identical contents) re-fires the effect → re-fires setSelection → re-fires selectionchange → potentially re-renders subscribers. Cheap fix: shallow-compare before assigning and skip the dispatch if unchanged. Worth doing now before #1450 starts measuring shadow-parity dispatch counts.

  • No de-dup of input ids. setSelection(["hf-title", "hf-title"]) stores duplicates verbatim. Probably fine, but #1426's CanResult dispatch-boundary checks iterate selection — duplicate ids = duplicate work. Worth a Array.from(new Set(ids)) on the way in, or an explicit "callers are responsible for de-dup" note in the jsdoc.

  • No validation that ids exist in the composition. setSelection(["nonexistent-id"]) succeeds silently. Tolerant API choice (matches getSelection returning whatever was set), but means a typo from an agent caller silently selects nothing. Not blocking — tolerant-by-default is consistent with the rest of the SDK surface.

Nits

  • jsdoc on setSelection (types.ts:256) says "Pass [] to clear" — true, but setSelection(["only-hf-id"]) also implicitly clears any prior selection. The "replace" semantic is in the first half of the sentence; consider tightening to "Replaces the current selection (use [] to clear)."

What I didn't verify

  • Whether #1450's shadow-parity comparison includes selectionchange event counts — if it does, the no-equality-check concern is a likely false-positive source for shadow-parity drift.

Review by Rames D Jusso

miguel-heygen
miguel-heygen previously approved these changes Jun 15, 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.

CI blocked by the root oxfmt failure in #1423. setSelection implementation is clean.

Strengths:

  • packages/sdk/src/session.ts setSelection — correctly fires a fresh copy of the array to each listener ([...this.currentSelection]), so a listener that mutates its argument doesn't corrupt the stored selection. The snapshot test verifies this.
  • Tests cover: empty-array clear, multi-id set, event payload copy, unsubscribe stops firing, undo-stack isolation, patch-event isolation. Comprehensive for a 5-line implementation.

Important:

  • packages/sdk/src/session.ts setSelection does not validate that the ids exist in the document. Passing ids for elements that don't exist silently sets the selection. For a debug/dev experience, a console.warn or CanResult-style validation when an id resolves to null would help authors catch typos. Not a blocker — callers may intentionally set selection before the document is populated.

Nit:

  • packages/sdk/src/types.tssetSelection is documented "Replace the current selection; fires selectionchange." Missing: "Pass [] to clear." (already on the impl comment but not the type doc — copy it there for IDE hover).

Verdict: APPROVE (pending #1423 oxfmt fix)
Reasoning: Correct, minimal, and well-tested. Selection does not affect undo stack or patch events — correct isolation.

— magi

@vanceingalls vanceingalls force-pushed the sdk-stage7-setselection branch from 88f067e to 5f335b3 Compare June 15, 2026 05:06
@vanceingalls vanceingalls force-pushed the sdk-stage7-http-adapter branch from db5032f to 757ec4c Compare June 15, 2026 05:06
@vanceingalls vanceingalls force-pushed the sdk-stage7-setselection branch from a32b07b to 02bd36c Compare June 15, 2026 05:41
@vanceingalls

Copy link
Copy Markdown
Collaborator Author

Thanks @james-russo-rames-d-jusso and @miguel-heygen.

Fixed:

  • Equality checksetSelection now shallow-compares before firing selectionchange. Same ids (same order) = no-op. Tests added.
  • De-dupArray.from(new Set(ids)) applied on input. setSelection(["hf-title", "hf-title"]) stores and compares as ["hf-title"]. Test added for de-dup + de-dup-equals-stored no-fire.
  • setSelection JSDoc — already says "Replaces the current selection (pass [] to clear)" in both types.ts and the method doc. Confirmed on current HEAD.

Not changed:

  • No validation that ids exist — intentional tolerant API. Agents may set selection before the doc is populated. A console.warn on non-existent id would be noise for that common pattern.

@vanceingalls vanceingalls force-pushed the sdk-stage7-setselection branch from 02bd36c to c13f332 Compare June 15, 2026 07:27
@vanceingalls vanceingalls force-pushed the sdk-stage7-http-adapter branch from 0f6f62b to ae58fa2 Compare June 15, 2026 07:27
@vanceingalls

Copy link
Copy Markdown
Collaborator Author

@james-russo-rames-d-jusso — all concerns addressed in post-review commits

  • No equality check: setSelection now shallow-compares (length + element-wise) before firing selectionchange. Same ids in same order → no-op.
  • No de-dup: Array.from(new Set(ids)) applied on input before comparison and assignment.
  • No validation that ids exist: left intentionally tolerant (consistent with the rest of the SDK surface). Added a note in the jsdoc.
  • jsdoc "replace" semantic: tightened to "Replaces the current selection ([] to clear)".

@vanceingalls vanceingalls force-pushed the sdk-stage7-http-adapter branch from ae58fa2 to 9e58fd1 Compare June 15, 2026 09:25
@vanceingalls vanceingalls changed the base branch from sdk-stage7-http-adapter to graphite-base/1442 June 15, 2026 20:52
vanceingalls and others added 4 commits June 15, 2026 13:54
Adds setSelection(ids: string[]) to Composition interface and CompositionImpl.
Fires selectionchange; does not touch undo stack or patch stream.
11 contract tests: get/set/clear, event firing, copy semantics, no undo/patch side-effects.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Skip event dispatch when ids are identical (same length, same order)
to prevent double-firing selectionchange from callers that call
setSelection with the same list. Two new tests (RED→GREEN verified).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
Reviewer noted it is dead surface in this stack — no caller uses it.
Add comment explaining it is wired up in stage 8 when the preview host
pushes selection events up to the SDK session.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
@vanceingalls vanceingalls force-pushed the sdk-stage7-setselection branch from c13f332 to 377007a Compare June 15, 2026 20:54
@vanceingalls vanceingalls changed the base branch from graphite-base/1442 to main June 15, 2026 20:55
@vanceingalls vanceingalls dismissed miguel-heygen’s stale review June 15, 2026 20:55

The base branch was changed.

@vanceingalls vanceingalls merged commit 30ce1f3 into main Jun 15, 2026
17 checks passed
@vanceingalls vanceingalls deleted the sdk-stage7-setselection branch June 15, 2026 20:55
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