fix: describe what transfer_workitems actually does - #209
Conversation
The note said "moves everything to new_cycle_id". Both halves were wrong.
The endpoint transfers only the *unfinished* work items — its own API
description says so — and it refuses outright unless the source cycle has
already ended:
if old_cycle.end_date is not None and old_cycle.end_date > timezone.now():
return Response({"error": "The old cycle is not completed yet"}, 400)
So the natural order for "close this cycle and move its leftover work" is
backwards: complete first, then transfer. Nothing on the tool surface said
so, and the failure is a bare 400 discovered only by making the call.
Found while measuring the tool surface with an eval harness. Three models
across two vendors hit it on the same task, deterministically: identical
arguments, identical 400, then a recovery. It was the single most errored
action in every run — one wasted call each time, paid by every agent that
tries the obvious order.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review. 📝 WalkthroughWalkthroughThe ChangesCycle work item transfer
Estimated code review effort: 1 (Trivial) | ~5 minutes Merge Risk: ⚪ Minimal · up to This localized documentation update clarifies the existing transfer behavior without changing runtime behavior, and no actionable merge-blocking risk remains after normal checks and review. Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Description
cycle.transfer_workitemsadvertised itself as "moves everything to new_cycle_id". Both halves of that are wrong.So for the ordinary request "this sprint is wrapping up — close it and move the leftover work to the next one", the intuitive order is rejected. You must complete the cycle first, then transfer. An agent discovers this only by making the call and reading a bare 400.
How it was found
While measuring this tool surface with an eval harness. Three models across two vendor CLIs hit it on the same task, deterministically — identical arguments, identical 400, then a recovery attempt. It was the most-errored action in every run, and the only error attributable to the tool description rather than to the model.
Every one of those is a wasted round trip paid by every agent that tries the obvious order.
Verified by re-measuring
Same task, same models, same harness — only the note changed:
transfer_workitemserroredEvery failed call is gone on both tiers, and the transfer count settles at exactly one per
repetition with no retries. Total calls fell 28% for sonnet and 43% for haiku, with success
unchanged. Haiku's incidental
cycle.listandproject.retrieveerrors disappeared too —it no longer has to re-orient after being refused.
Type of Change
Test Scenarios
transfer_workitems (project_id, cycle_id, new_cycle_id) -- moves only the unfinished work items to new_cycle_id; the source cycle must have ended first, so complete it before transferring or the call is rejectedNote:
pytest tests/toolsreports 17 pre-existing failures onorigin/main(all intest_workitem_property.py,TypeError: 'function' object is not subscriptable). Identical count with and without this change — unrelated, but worth someone's attention.References
Text-only change to one
Actionnote; no behaviour change.🤖 Generated with Claude Code
Summary by CodeRabbit