Skip to content

feat(setbuilder): set_curve_point/remove_curve_point agent tools (#468)#470

Merged
thewrz merged 3 commits into
mainfrom
feat/issue-468
Jun 18, 2026
Merged

feat(setbuilder): set_curve_point/remove_curve_point agent tools (#468)#470
thewrz merged 3 commits into
mainfrom
feat/issue-468

Conversation

@thewrz

@thewrz thewrz commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Why

Part of #442 (Family 2 — safe mutations). Beyond add_slow_window/set_peak_at, the WrzDJSet pass-2 agent needs precise curve editing: place or remove an individual energy point at a time offset. These two tools manage standalone SetCurvePoint rows.

What

Two new mutation tools on the pass-2 agent:

  • set_curve_point — upsert a non-window energy point at position_sec (energy validated 0–10, optional label).
  • remove_curve_point — remove a non-window point at position_sec.

New service helpers in curve.py (upsert_curve_point, remove_curve_point, get_standalone_point) keep pass2_agent.py thin and match how the curve domain already lives there. Both helpers filter to non-window points (both window flags False), so add_slow_window's paired vibe-window rows are never touched.

Wiring mirrors the existing mutation tools: both registered in MUTATION_TOOLS, the apply_tool_call allowlist, the _agent_tools() schema list (via _tool(...), which auto-adds the required rationale), and the display-summary chain. The frontend ToolCard renders the new tools through its existing generic display_summary path — no frontend change needed.

Design decisions

  • Remove key — position_sec (not point_id). The agent reasons about the curve in time offsets and never sees DB row ids, so keying both tools by position_sec is symmetric and matches the upsert match rule.
  • Upsert match rule. A non-window point (is_slow_window_start == False AND is_slow_window_end == False) at the exact same position_sec: same position_sec → update energy + label; otherwise insert. A standalone point may share a position_sec with a window boundary without colliding — the standalone filter makes window rows invisible to the upsert/remove queries.
  • Not-found behavior — AgentToolError. remove_curve_point on a missing (or window-only) position_sec raises rather than silently no-opping, giving the agent a clear signal. This is also what protects window rows: removing at a window boundary errors instead of deleting the paired row.

Security invariants (#442)

  • Owner-scoped: every SetCurvePoint query filters set_id == set_obj.id.
  • Never corrupts slow-window pairs (only touches points with both window flags False).
  • Requires a rationale (enforced by MUTATION_TOOLS).
  • Dispatched only through apply_tool_call's closed allowlist.
  • Writes only to set_curve_points; a regression test asserts the requests table is unchanged.
  • No new REST endpoints, no DB migrations (model already exists), no openapi/type regen.

Testing

  • Unit tests added (server/tests/test_setbuilder_pass2.py): insert, upsert-update, remove, slow-window-rows-untouched, remove-at-window-boundary-errors, energy validation, missing-rationale, missing-point, requests-untouched regression, display-summary readability
  • ruff check / ruff format --check clean
  • bandit -r app -c pyproject.toml clean
  • pytest green: 2943 passed, coverage 87.89% (gate 85%)
  • CI green

🤖 Co-authored by Claude Opus 4.8. Closes #468.

Summary by CodeRabbit

  • New Features

    • Added standalone (non-window) curve point management to create, upsert, and remove curve markers by position.
    • Exposed new AI agent tools to edit curve points automatically, including optional labeling.
    • Enforced validation and safety rules so edits won’t affect slow-window curve pairs.
  • Tests

    • Added end-to-end coverage for inserting, updating, removing, validation errors, missing-point handling, non-modification of slow-window rows, and tool display/schema behavior.

Add two Family-2 mutation tools to the WrzDJSet pass-2 agent for precise
energy-curve editing: place or remove an individual energy point at a time
offset. Both manage standalone (non-window) SetCurvePoint rows and never
disturb add_slow_window's paired vibe-window rows.

- curve.py: upsert_curve_point / remove_curve_point / get_standalone_point
  helpers, all filtered to non-window points (both window flags False).
- pass2_agent.py: _tool_set_curve_point (upsert by position_sec, energy 0-10)
  and _tool_remove_curve_point (remove by position_sec; AgentToolError if
  absent). Wired into MUTATION_TOOLS, the apply_tool_call allowlist, the
  _agent_tools() schema list, and the display-summary chain.

Owner-scoped, rationale-required, allowlist-dispatched. Writes only to
set_curve_points; a regression test asserts the requests table is unchanged.

Closes #468

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@thewrz, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 1 hour, 34 minutes, and 58 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f24e7922-3d69-432f-9b7c-45f817b84953

📥 Commits

Reviewing files that changed from the base of the PR and between b6d2bdb and a1074cd.

📒 Files selected for processing (2)
  • server/app/services/setbuilder/pass2_agent.py
  • server/tests/test_setbuilder_pass2.py
📝 Walkthrough

Walkthrough

Adds set_curve_point and remove_curve_point as two new mutation tools to the setbuilder pass-2 agent. Three new service functions in curve.py handle standalone SetCurvePoint CRUD by filtering only rows where both window flags are False. The agent tools validate inputs, delegate to those helpers with deferred commits, and are wired into dispatch, MUTATION_TOOLS, display summary, and the LLM tool schema. Comprehensive tests cover insert, upsert, remove, window-row isolation, validation, and invariant assertions.

Changes

Standalone curve point tools

Layer / File(s) Summary
Standalone curve point service helpers
server/app/services/setbuilder/curve.py
Adds _standalone_points_filter, get_standalone_point, upsert_curve_point, and remove_curve_point. The filter restricts all queries to rows with both is_slow_window_start and is_slow_window_end as False, preventing any contact with paired window rows. Upsert inserts a new row or updates energy/label on an existing one; remove returns bool indicating whether a row was deleted. Both support conditional commit/flush for transaction control.
Agent tool implementations and wiring
server/app/services/setbuilder/pass2_agent.py
Adds set_curve_point and remove_curve_point to MUTATION_TOOLS and the apply_tool_call dispatch handler map. _tool_set_curve_point validates energy in 0–10, truncates optional label to 50 chars, and calls upsert_curve_point with commit=False. _tool_remove_curve_point calls remove_curve_point and raises AgentToolError if no row was found. Updated _tool helper supports optional_fields for LLM schema generation. Display summary and LLM tool schemas are wired for both tools; both return empty affected-positions set().
Tests
server/tests/test_setbuilder_pass2.py
Adds _standalone_points query helper and twelve test functions covering: insert non-window point, upsert-at-same-position update, remove standalone point, window-row isolation (including slow-window boundary edge cases where operations must not affect paired rows), energy out-of-range rejection, rationale requirement enforcement, missing-point error on removal, requests table unchanged invariant, human-readable _tool_display_summary output for both tools, and schema validation that label is optional while position_sec, energy, and rationale remain required.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.15% 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 clearly and specifically describes the main change: adding two new agent tools (set_curve_point and remove_curve_point) for setbuilder curve editing.
Linked Issues check ✅ Passed The PR implements all backend requirements from #468: service helpers in curve.py, energy validation (0-10), optional label, tool wiring in mutation infrastructure, window-row protection, and comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes are scoped to issue #468: curve.py service helpers, pass2_agent.py tool integration, test coverage, and the _tool helper extension for optional fields—no unrelated modifications.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-468

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 `@server/app/services/setbuilder/pass2_agent.py`:
- Around line 1044-1047: The `label` field in the `set_curve_point` tool schema
definition is currently marked as required by the `_tool()` function, but the
actual implementation in `_tool_set_curve_point` uses `payload.get("label")`
indicating it should be optional. Modify the schema definition for the
`set_curve_point` tool to mark the `label` field as optional so it matches the
actual implementation behavior and PR contract, rather than requiring it like
the `position_sec` and `energy` fields.
🪄 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: 92a6bce0-6a2c-4e43-8f7b-c01d3533bfc8

📥 Commits

Reviewing files that changed from the base of the PR and between d9fa5d0 and 7918430.

📒 Files selected for processing (3)
  • server/app/services/setbuilder/curve.py
  • server/app/services/setbuilder/pass2_agent.py
  • server/tests/test_setbuilder_pass2.py

Comment thread server/app/services/setbuilder/pass2_agent.py
thewrz and others added 2 commits June 18, 2026 09:27
CodeRabbit (PR #470): the _tool() helper marked every listed field required,
so label was wrongly required in the LLM-facing set_curve_point schema even
though _tool_set_curve_point reads it via payload.get("label") and the contract
says it is optional.

Extend _tool() with an optional_fields kwarg (in schema properties but not in
required); set_curve_point now passes label there. rationale stays required for
all mutation tools. Pinned with a schema test asserting label is optional while
position_sec/energy/rationale remain required.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts:
#	server/app/services/setbuilder/pass2_agent.py
#	server/tests/test_setbuilder_pass2.py
@thewrz thewrz merged commit 025e5b7 into main Jun 18, 2026
1 check was pending
@thewrz thewrz deleted the feat/issue-468 branch June 18, 2026 17:04
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): set_curve_point/remove_curve_point agent tools — finer curve editing

1 participant