v3 Designer: Variables editor (Phase 5) + validation/Reset/delete (Phase 6) — v0.11#77
Merged
Conversation
…ler ticks, convert _unknownKeys (v0.9) Six Codex-flagged bugs in landed Phase 4 work: - A1: #sequenceList drag/drop listeners moved to one-time startup wiring. Previously re-attached on every renderSequence(); a single drop after N renders fired N handlers, inserting N duplicate refs and pushing N undo entries. Surfaced within a normal editing session. - A2: onMoveSequenceEntry and onInsertSequenceRefFromLib now walk selection.index across the full displacement, not just the moved entry. Library drag-drop no longer steals selection focus — it shifts the existing selection through the insert (matches the +Add Ref button's intent of selecting the new entry only for explicit button clicks). - A3: Timeline ruler tick positions are computed piecewise by walking the layout array (rulerXForRealTime), so ticks stay aligned with clamped step widths. Flat pxPerSec drifted as soon as any short step clamped to the 60px / 40px minimum. - A4: isConvertibleBlock rejects entries with non-empty _unknownKeys. Block→ref convert was silently dropping forward-compat fields like retry_on_fail, undermining the Tier 1 unknown-passthrough guarantee. The user-facing error message now enumerates every blocker including forward-compat keys. - A5: _buildSequenceEntry mirrors the parser's positive-integer repetitions validation. Closed a doc/JS-mirror divergence path that D4/paste-import would hit (entry.repetitions = 0 → YAML '0' but mirror = 1 via `|| 1` default). - A6: onMoveCommand defensively early-returns on no-op / out-of-bounds, matching the onMoveSequenceEntry pattern. Avoids pushUndo pollution. Tests: 375/375 (was 369). Added 6 new builder-side validation cases to Suite 10b for A5. Verified in browser: - A1: 30 forced re-renders + 1 simulated library drop adds exactly 1 entry. - A4: Right-click on future_keys block shows full error listing all blockers including `forward-compat keys: retry_on_fail, abort_if`. - A3: Multi_block fixture (mixed durations exercising 60px clamping) shows "1m" tick at x=2447 of a 3516-px ruler — piecewise positioning. Footer bumped to v0.9 | 2026-05-27 19:54 ET. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…e cascade (v0.10)
Closes the largest user-facing feature gap in the v3 designer. Three
pieces, all additive — only one existing function (renderEditableField)
gains a 🔗 button.
## Inline-editable Variables table
Replaces the read-only Variables section in the Settings drawer with:
- Editable anchor-name input on every row (renames cascade via the
modal).
- Editable value input (numeric or text by inferred type).
- Read-only "complex anchor" badge for map/seq anchors.
- ✕ delete button — blocked when references exist, with confirmation
modal offering cascade-unbind.
- "+ Add variable" footer row with name + value + Add button.
## Anchor binding popover
Every editable scalar (controller/plugin command fields) gains a 🔗
button. Single global popover positioned beneath the trigger; outside-
click and Escape close it. Content varies by alias state:
- Literal scalar: "Bind to existing anchor" select (with type-mismatched
options visibly disabled with a "(type: X)" suffix) + "Create new
anchor from this value" with name input and "Create & bind".
- Aliased scalar: summary card "→ &name = value" + "Edit in Variables…"
jump button + "Rebind to a different anchor" select + "Unbind (convert
to literal)" danger button.
Create-and-bind runs as a single pushUndo → so one Ctrl+Z reverts both
the variable creation and the binding atomically.
## Rename cascade
Confirmation modal lists every reference path that will update; on
confirm, a single pushUndo wraps docRenameVariable, which atomically
walks every *alias source in one synchronous pass via YAML.visit. One
undo step for the whole rename.
Empty-refs case skips the modal (no friction for unused anchors).
## New helpers in js/protocol-yaml-v3.js (10 added → 27 total)
- docCreateVariable / docDeleteVariable / docRenameVariable
- docSetVariableValue (mutates Scalar.value in place — preserves anchor)
- docBindToAnchor / docUnbindAnchor
- findAliasesTo (recursive walk of doc.contents — simpler/safer than
the YAML.visit ancestors-chain reconstruction)
- variableIsComplex / isValidAnchorName / anchorExists
extractVariables now prefers pair.value.anchor as identity (falls back
to map key). This means renaming via the table cascades the anchor
without churning the map key — per the plan-mode user decision.
## New UI primitive: confirmModal({title, body, list, confirmLabel})
Promise-based, backdrop-dismiss, Escape/Enter keys. ~70 lines. Phase 6's
full pre-export validation modal will reuse it.
## Tests
54 new tests in Suite 29 (Variable lifecycle + anchor binding):
- Create / delete (incl. ANCHOR_HAS_REFS + cascade-unbind)
- Rename (anchor + every alias, count match, collision rejection,
comment preservation, merge-key cascade `<<: *foo`)
- Bind literal → alias / unbind alias → literal round-trip
- setVariableValue preserves anchor declaration
- findAliasesTo count matches grep on canonical fixture
- variableIsComplex / isValidAnchorName / anchorExists basics
Total: 429/429 (was 375 after cleanup PR).
## Verified in browser
- Variables table renders 4 editable rows + Add row for canonical_a
- Rename `&dur_long → &duration_long` shows modal listing all 9
references, applies in one undo step
- Click 🔗 on aliased duration: popover shows current binding, rebind
options (with color_command disabled as type-mismatched string),
unbind action
- Click 🔗 on literal: bind-existing dropdown + create-new section
- Create-and-bind: single Ctrl+Z reverts both ops; Variables table
loses the new anchor on undo
- Unbind: alias chip replaced by editable input with resolved value
## Updated copy
The BETA banner now says "Editing. Scalar fields, sequence layout, and
Variables are all editable. Click the 🔗 icon next to any value..."
(was "anchor-bound fields are read-only until the Variables editor
lands"). Footer bumped to v0.10 | 2026-05-27 20:07 ET.
## Decisions logged
- Variables table identity: anchor name only (per plan-mode Q&A).
Rename cascades the anchor; the map key is preserved untouched
through round-trip but not surfaced. Rare existing-YAML cases where
they differ appear "renamed" in the UI.
- Codex plan-review pass on Phase 5: skipped (per plan-mode Q&A).
## Open items deferred (not in this PR)
- Phase 6 (full validation modal + Reset)
- Phase 7 (comment-preservation tests at strategic positions)
- Phase 8 (MATLAB-validation flow docs)
- Phase 9 (quickstart HTML)
- D4 (cross-library import) — now unblocked since Phase 5 closes the
prefix-clutter UX gap
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…e (v0.11) Builds on the Phase 5 Variables editor (de31235). Adds the deferred Phase 6 variables-adjacent work, all browser-verified this session. - collectBlockingErrors(experiment) in protocol-yaml-v3.js: a blocking sibling to collectExportWarnings. Composes validateReferences and adds two CST checks via YAML.visit — duplicate anchor names (yaml@2 accepts them silently, so they're counted) and dangling aliases (safety net for the in-memory mutation model). Exported from all three surfaces. - Pre-export validation modal: Export runs collectBlockingErrors first; on errors it shows a confirmModal listing each one with an "Export anyway" escape hatch. Soft warnings stay in the non-blocking banner. - Reset button (header): confirms, then loads the blank skeleton. Reversible via a new loadYamlText({ keepUndo: true }) option + pushUndo, so one Undo restores the prior doc. - Library-row delete (✕): blocked while usage > 0 ("remove from sequence first"); when unused, confirm then docDelete(['conditions', idx]). Clears selection if the deleted condition was selected. - Suite 30 (17 checks) covering the validator + library delete. Full suite 446/446 (arena 10, v2 137, v3 446). Footer bumped v0.10 -> v0.11. Browser-verified: the 6 Phase 5 checks (rename cascade, bind, unbind, create-and-bind, cascade-unbind delete, complex-anchor badge) plus the 3 Phase 6 checks (validation modal block/cancel, Reset + Undo, library delete blocked-when-used / works-when-unused). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced May 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Completes the Variables feature set for the v3 Experiment Designer and ships the deferred Phase 6 affordances. Three unmerged commits over
main:259254e_unknownKeys)de3123575a4b87Phase 5 — Variables editor (v0.10)
Inline-editable Variables table in Settings (rename cascades to every
*alias, cascade-unbind delete, complex map/seq anchors as read-only badges); a 🔗 anchor-binding popover on every editable scalar (bind to existing / create-and-bind / rebind / unbind); rename cascade as a single undo step.Phase 6 — validation + Reset + delete (v0.11, this session)
collectBlockingErrors(experiment)— a blocking sibling tocollectExportWarnings. ComposesvalidateReferencesand adds two CST checks viaYAML.visit: duplicate anchor names (yaml@2 accepts them silently, so they're counted) and dangling aliases (a safety net for the in-memory mutation model — a fully-dangling alias throws at import). Exported from all three surfaces.⟲ Resetconfirms, then loads the blank skeleton. Reversible:pushUndo()+ a newloadYamlText({ keepUndo: true })option, so a single Undo restores the prior doc.docDelete(['conditions', idx]). Clears selection if the deleted condition was selected.Testing
npm test→ 446/446 (arena 10, v2 137, v3 446). Suite 30 (17 checks) covers the Phase 6 validator + library delete; Suite 29 covers the Phase 5 variable lifecycle.Notes
.jsfiles are hand-formatted and predate strict Prettier conformance (mainitself failsprettier --checkon them, and no CI enforces it). New code matches the surrounding hand-style; no global reformat, to keep the diff focused.v3-editor-handoff-2.mdupdated for Phase 6; the originalv3-editor-handoff.mdmarked superseded.🤖 Generated with Claude Code