Skip to content

feat(setbuilder): make agent chat mutations undoable#494

Merged
thewrz merged 7 commits into
mainfrom
feat/setbuilder-agent-undo
Jun 19, 2026
Merged

feat(setbuilder): make agent chat mutations undoable#494
thewrz merged 7 commits into
mainfrom
feat/setbuilder-agent-undo

Conversation

@thewrz

@thewrz thewrz commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Why

WrzDJSet agent chat mutations bypassed the dashboard's existing undo system, so a DJ could Ctrl+Z a manual drag but not an agent edit. This blocks the destructive Family 3 tools (autobuild/fill_to_duration, #491) and Family 4 imports, which need a recovery path before they can ship.

What

Bridges agent chat turns into the existing 50-deep document-history stack (useSetDocumentHistory) — unified global undo, so a mutating turn becomes an ordinary undo entry reverted by the same Ctrl+Z / ↶ as a manual edit. Frontend-only, no backend change — it rides the build_snapshot/restore_snapshot + GET/PUT /document surface (#395) that already exists.

Three small changes:

  1. commit gains an optional shouldRecord(result) predicate (backward-compatible) — record an undo entry only when the turn mutated.
  2. useAgentChat.send wraps the chat call in commit with a didMutate predicate; falls back to the old refresh when history is unavailable.
  3. history.commit is threaded through ChatSidebar/MobileAgentOverlay (gated on historyEnabled).

Because the captured snapshot is the whole document (slots + curve + pool), every current and future agent mutation — including the unbuilt Family 3/4 destructive tools — is undoable for free. This satisfies the #491 undo gate.

Design + plan: docs/superpowers/specs/2026-06-18-agent-undo-design.md, docs/superpowers/plans/2026-06-18-agent-undo.md.

Testing

  • Unit tests pass (5 new across the 3 changes; full suite 1345)
  • Coverage gate met (Branch 69.94% ≥ 68, Funcs 72.84% ≥ 65, Lines 81.48% ≥ 78, Stmts 79.1% ≥ 77)
  • tsc --noEmit + eslint clean
  • Manual: ask the agent to edit a set, press Ctrl+Z, confirm it reverts; ask a non-mutating question, confirm no undo entry
  • CI green

🤖 Co-authored by Claude Opus 4.8. Closes #493. Unblocks #491 + #442 Family 4.

Summary by CodeRabbit

  • New Features
    • Enabled undo/redo for agent-driven set mutations by routing mutating chat turns through the existing document history stack when history is available.
    • Mutations now create a single labeled undo entry per mutating agent turn (and skip history-free mutation handling when history is disabled).
  • Tests
    • Added/updated unit tests to verify commit-based undo routing and non-recording mutation behavior (including redo preservation).
  • Documentation
    • Added an end-to-end implementation plan and a design specification for the agent undo integration.

thewrz and others added 5 commits June 18, 2026 23:19
Brainstormed design for bridging agent chat mutations into the existing
useSetDocumentHistory undo stack — unified global undo, frontend-only via a
shouldRecord predicate on commit(). Gates #491 (Family 3 destructive tools) and
#442 Family 4 (imports); both become undoable for free since the snapshot is the
whole document.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TDD plan: (1) shouldRecord predicate on history commit, (2) bridge
useAgentChat.send through commit, (3) thread history.commit through the chat
surfaces (gated on historyEnabled). Frontend-only, three commits, build green
between each. Derived from the approved design spec.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cdd35f6d-0311-4bb5-9c63-b3133cf49545

📥 Commits

Reviewing files that changed from the base of the PR and between a3c4181 and a00bf82.

📒 Files selected for processing (1)
  • dashboard/app/(dj)/setbuilder/components/__tests__/MobileAgentOverlay.test.tsx

📝 Walkthrough

Walkthrough

The PR makes agent chat mutations undoable by extending BuilderCommit with an optional shouldRecord predicate, routing useAgentChat.send() through commit for mutating turns (falling back to onMutationApplied when absent), and threading an optional commit prop—gated on historyEnabled—from the set page through ChatSidebar and MobileAgentOverlay. Accompanying tests and design docs are also added.

Changes

Agent Undo Stack Integration

Layer / File(s) Summary
BuilderCommit shouldRecord extension and tests
dashboard/app/(dj)/setbuilder/components/useSetDocumentHistory.ts, dashboard/app/(dj)/setbuilder/components/__tests__/useSetDocumentHistory.test.tsx
BuilderCommit type gains an optional shouldRecord predicate parameter. commit() conditionally pushes to undoStack and clears redoStack only when shouldRecord(result) is true; otherwise it still executes the action and publishes the snapshot. Two new tests verify non-recording commits publish without undo depth growth and without redo stack clearing.
useAgentChat commit routing and tests
dashboard/app/(dj)/setbuilder/components/useAgentChat.ts, dashboard/app/(dj)/setbuilder/components/__tests__/useAgentChat.test.tsx
useAgentChat options type adds optional commit?: BuilderCommit. send() computes a didMutate predicate and wraps the chat API call in commit(label, action, didMutate) when present; without commit, it directly awaits the API and calls onMutationApplied when didMutate is true. Two new tests cover both dispatch paths.
commit prop threading and integration tests
dashboard/app/(dj)/setbuilder/components/ChatSidebar.tsx, dashboard/app/(dj)/setbuilder/components/MobileAgentOverlay.tsx, dashboard/app/(dj)/setbuilder/[setId]/page.tsx, dashboard/app/(dj)/setbuilder/components/__tests__/ChatSidebar.test.tsx, dashboard/app/(dj)/setbuilder/components/__tests__/MobileAgentOverlay.test.tsx
ChatSidebar and MobileAgentOverlay each add commit?: BuilderCommit to their props and forward it into useAgentChat. The set page passes history.commit to both components when historyEnabled is true, otherwise undefined. New integration tests for both components assert commit is called once and onMutationApplied is not called.
Design spec and implementation plan
docs/superpowers/specs/2026-06-18-agent-undo-design.md, docs/superpowers/plans/2026-06-18-agent-undo.md
Design spec defines the undo gap, decision rationale (single unified global undo, one entry per mutating turn), two implementation changes (add shouldRecord to commit and wrap useAgentChat.send), edge cases, and acceptance criteria. Implementation plan provides task-by-task test specifications, constraints, manual verification steps, and what the change unblocks.

Sequence Diagram(s)

sequenceDiagram
  participant Page as SetBuilder Page
  participant ChatSidebar as ChatSidebar / MobileAgentOverlay
  participant useAgentChat as useAgentChat.send()
  participant commit as useSetDocumentHistory.commit()
  participant API as api.chatWithSetAgent

  Page->>ChatSidebar: commit={history.commit} (when historyEnabled)
  ChatSidebar->>useAgentChat: useAgentChat({ commit, ... })
  useAgentChat->>commit: commit("Agent · rebuild the set", action, didMutate)
  commit->>API: action() → AgentChatOut
  API-->>commit: result
  commit->>commit: shouldRecord = didMutate(result)
  alt shouldRecord true
    commit->>commit: push undoStack, clear redoStack
  else shouldRecord false
    commit->>commit: skip undo entry, publish snapshot only
  end
  commit-->>useAgentChat: result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • wrzonance/WrzDJ#427: Introduced useSetDocumentHistory/history.commit(...) integration for builder mutations; this PR extends the same BuilderCommit type with shouldRecord and wires agent chat through that boundary.
  • wrzonance/WrzDJ#431: Introduced historyEnabled gating in useSetDocumentHistory and the set page; this PR reuses the same gating to conditionally pass history.commit to the chat surfaces.
  • wrzonance/WrzDJ#452: Wired useAgentChat into ChatSidebar and MobileAgentOverlay; this PR extends that same wiring by threading the commit prop into useAgentChat for undoable mutating turns.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main change: making agent chat mutations undoable in the setbuilder.
Linked Issues check ✅ Passed The PR fully implements the requirements from #493: extends commit() with shouldRecord predicate, routes mutations through history.commit, and threads the handler through ChatSidebar/MobileAgentOverlay when historyEnabled.
Out of Scope Changes check ✅ Passed All changes are scoped to the agent undo feature: extending history.commit, wiring chat mutations through commit, adding comprehensive tests, and documenting the design/plan.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/setbuilder-agent-undo

Comment @coderabbitai help to get the list of available commands and usage tips.

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@dashboard/app/`(dj)/setbuilder/components/__tests__/ChatSidebar.test.tsx:
- Around line 88-110: Add a new integration test for the MobileAgentOverlay
component that mirrors the existing test for ChatSidebar to validate the
commit-routing behavior. The new test should render MobileAgentOverlay with a
commit function and onMutationApplied callback, simulate the mobile send flow
(similar to how the ChatSidebar test simulates typing and clicking Send), and
assert that commit is called with the correct message while onMutationApplied is
not invoked. This ensures the commit path wiring works consistently across both
the desktop (ChatSidebar) and mobile (MobileAgentOverlay) agent surfaces as per
the agent functionality testing guidelines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fdcff954-3575-41b3-bc98-dc85cce71cba

📥 Commits

Reviewing files that changed from the base of the PR and between 903fcc0 and a3c4181.

📒 Files selected for processing (10)
  • dashboard/app/(dj)/setbuilder/[setId]/page.tsx
  • dashboard/app/(dj)/setbuilder/components/ChatSidebar.tsx
  • dashboard/app/(dj)/setbuilder/components/MobileAgentOverlay.tsx
  • dashboard/app/(dj)/setbuilder/components/__tests__/ChatSidebar.test.tsx
  • dashboard/app/(dj)/setbuilder/components/__tests__/useAgentChat.test.tsx
  • dashboard/app/(dj)/setbuilder/components/__tests__/useSetDocumentHistory.test.tsx
  • dashboard/app/(dj)/setbuilder/components/useAgentChat.ts
  • dashboard/app/(dj)/setbuilder/components/useSetDocumentHistory.ts
  • docs/superpowers/plans/2026-06-18-agent-undo.md
  • docs/superpowers/specs/2026-06-18-agent-undo-design.md

thewrz and others added 2 commits June 19, 2026 00:31
Mirror the desktop ChatSidebar commit-routing test for the mobile agent
surface: assert overlay sends route through the provided commit (carrying
the "Agent · <message>" label) and never fall back to onMutationApplied.
The undo-stack wiring was threaded through both surfaces; only the desktop
path was tested, leaving the mobile path free to silently regress.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thewrz thewrz merged commit 1599aa8 into main Jun 19, 2026
8 of 9 checks passed
@thewrz thewrz deleted the feat/setbuilder-agent-undo branch June 19, 2026 07:40
thewrz added a commit that referenced this pull request Jun 22, 2026
* docs(setbuilder): design spec for autobuild + fill_to_duration agent tools (#491)

Family 3 destructive structural tools. Records the key finding that #493/#494's
global undo already closes #491's undo gate, so the original Task 0 (per-tool
'Undo this rebuild' button) is replaced by: rely on global undo + a
discoverability hint + a snapshot round-trip identity test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(setbuilder): implementation plan for autobuild + fill_to_duration (#491)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(setbuilder): add commit flag to build_set for agent-turn atomicity (#491)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(setbuilder): autobuild agent tool — wholesale pass-1 rebuild (#491)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(setbuilder): fill_to_duration agent tool — bounded pool fill to target (#491)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(setbuilder): cover _duration_for fallback + fill cap display branch (#491)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* feat(setbuilder): undo-discoverability hint on destructive agent tool cards (#491)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(setbuilder): cover fill_to_duration in the destructive undo-hint test (#491)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(setbuilder): clarify the commit=False flush branch in _persist_slots (#491)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(setbuilder): treat target_duration_sec=0 as valid in fill_to_duration (#491)

CodeRabbit: 'if not target' rejected a legitimate 0 target as missing. Use an
explicit None check so a 0 target is a no-op (loop sees it as already met) rather
than an error. Pinned with a regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(setbuilder): log autobuild outcome metrics for observability (#491)

CodeRabbit nitpick: mirror fill_to_duration by logging autobuild's result
(slot_count + iterations) on this destructive turn.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thewrz added a commit that referenced this pull request Jun 23, 2026
…RLs (#442 Family 4b) (#529)

Exposes the existing public-playlist-URL pool import as a WrzDJSet agent tool,
completing epic #442 Family 4. The LLM can now pull a pool from a pasted public
Spotify/Tidal playlist link, mirroring the REST import_pool_url handler.

Thin wrapper over existing infrastructure: parse_public_playlist_url (SSRF-safe,
parse-only host allowlist) + pool.candidates_from_public_url + get_or_create_source
+ import_candidates(commit=False) for turn atomicity. Invalid/unsupported URLs and
PoolImportError surface as AgentToolError; Tidal URLs require a connected account.
kind="public_url" matches the REST source-dedupe key so REST and agent paths
coexist. Additive pool import rides the existing global undo (#493/#494); no
frontend change, requests table untouched, affected positions set().

Registered in the usual 4 spots (MUTATION_TOOLS, ToolSpec, pass2_agent handlers,
agent_display). Tests exercise the real parser with only the network fetch mocked.

Closes #528.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

feat(setbuilder): bridge agent mutations into the document undo stack (gates #491 + Family 4)

1 participant