feat(experimental): derive client toggles from ClientCapabilities#1252
Conversation
) Closes the Phase 5 audit partial: ExperimentalFeaturesPanel now accepts clientExperimental: ClientCapabilities['experimental'] (the SDK record shape) instead of a parallel clientToggles array of UI metadata. Adopts Option B from the design question: toggle metadata (label, description) lives in a static CLIENT_EXPERIMENTAL_TOGGLE_METADATA map inside the component file. Iteration is over Object.keys of the live record, so presence-in-record means enabled. Unknown keys (in record but not in the metadata map) render with the raw key as label and no description, per AC. Renders one Checkbox per active toggle plus an indented HintText description when metadata is known. Callback signature unchanged. Stories cover empty, all-known, and mixed-known-and-unknown cases. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address in-review feedback: with the strict-AC iteration over Object.keys(clientExperimental), unchecking a toggle made its row disappear (no schema slot for "disabled but available"), so the user couldn't re-enable a toggle they just turned off. Refines Option B: render the union of metadata-map keys and record keys. Each row's checked state is derived from name-in-record. Result: known toggles always render, regardless of record state; toggling is fully reversible from the UI; unknown record keys still surface with a raw label. Story rename: NoClientToggles → NoneEnabled, AllKnownClientToggles → AllKnownEnabled, MixedKnownAndUnknownToggles → MixedKnownAndUnknown — the new names describe state, not record shape. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Updated ac5ee8de to render the full toggle universe rather than only the keys present in the record. What changedThe strict-AC version iterated
The refined version computes the rendered set as the union of metadata-map keys and record keys, and derives each row's
Story changesStories renamed to describe state instead of record shape:
Coverage matches the AC's three cases. 8/8 stories pass; validate clean. AC deviation noteThe original AC said "Component iterates |
|
@claude review |
|
Claude finished @cliffhall's task in 2m 2s —— View job Code Review
Overall this is a clean, well-reasoned change. Removing the parallel
|
| Story | Scenario |
|---|---|
NoneEnabled |
clientExperimental: undefined — both known toggles render unchecked |
AllKnownEnabled |
Both metadata-map keys present — both checked |
MixedKnownAndUnknown |
One known + one unknown key — unknown renders with raw key as label |
Default meta.args |
Seeds clientExperimental with batchRequests so other stories show section populated |
The story renames (NoClientToggles → NoneEnabled, etc.) are an improvement — describing toggle state rather than record shape is more semantically accurate after the "full toggle universe" shift.
One minor observation: MixedKnownAndUnknown uses experimental/futureFeatureXyz as the unknown key. That's a perfectly readable test value. The story effectively demonstrates the AC's "unknown keys render with the raw capability key as the label" requirement.
Summary
Strengths:
- Clean elimination of
ClientExperimentalTogglein favor of the SDK type directly - Toggle reversibility is genuinely fixed by the "full universe" approach
getClientToggleNamesunion logic is correct and dedup-safe- Story coverage hits all three AC cases
Suggestions:
- Add a client-section empty-state (defensive, matches server-section parity)
- Consider a theme spacing token instead of
gap={4}(cosmetic)
Neither suggestion is a blocker. The logic is sound and the implementation is well-structured. LGTM.
|
|
Thanks for the second pass. Walking through the two suggestions: Client-section empty-state — declining. The empty branch is unreachable in practice: the section is only empty when
Also noted but no action: the No code changes; ready to merge if you're happy. |
* docs(agents): require version label on new issues and PRs (#1256) Recent v2 PRs (#1247, #1249, #1250, #1252, #1255) all landed without a v2 label, making version-filtered queries unreliable. Codify the expectation directly in the issue-driven workflow: each branch / board gets a fixed label, and the label is set at create time via the gh create flag rather than backfilled. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Also fix stale "PR against main" line The PR-target line still said "Open a PR against main", which is correct only for v1 work. Mirror the version-label mapping above so the PR base matches the board. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes #1240.
Summary
Adopts Option B from the design question. The component now accepts the SDK record shape directly:
instead of a parallel
clientToggles: ClientExperimentalToggle[]array of UI metadata.Toggle metadata (label, description) lives in a static
CLIENT_EXPERIMENTAL_TOGGLE_METADATAmap inside the component file. Iteration is overObject.keys(clientExperimental ?? {}), so presence in the record == enabled. Unknown keys (in the record but not in the metadata map) render with the raw capability key as the label and no description, per AC.The
onToggleClientCapability(name: string, enabled: boolean)callback signature is unchanged.Semantic note
Because only keys present in the record render, this panel can only signal disabling a currently-enabled client toggle (the checkbox starts checked; unchecking fires
(name, false)). Adding new toggles isn't a UI affordance here — that's a client-init concern. This matches the SDK schema, whereexperimentalisRecord<string, object>with no per-key boolean field.Story coverage
NoClientToggles—clientExperimentalundefined.AllKnownClientToggles— both metadata-map keys present.MixedKnownAndUnknownToggles— one known key +experimental/futureFeatureXyzrendering with the raw key.WithServerCaps,NoServerCaps,WithResponse,WithErrorResponse,WithHistory) continue to pass; the meta default now seedsclientExperimentalwith one batched-requests toggle so each story shows the section populated.Test plan
npm run validatepasses.npx vitest run --project=storybook src/components/groups/ExperimentalFeaturesPanel— 8/8 stories pass.git grep ClientExperimentalToggle clients/web/src— zero matches.Out of scope
ServerCapabilities['experimental']prop correctly).🤖 Generated with Claude Code