Feature: Add allocation tools and a planning toolset - #481
Merged
Conversation
Adds eight tools for resource-scheduler allocations — the plane of planning that commits a person's time to a project, distinct from task estimates and from logged time: list_allocations, get_allocation, count via count_only, create_allocation, update_allocation, delete_allocation, restore_allocation, link_task_to_allocation, unlink_task_from_allocation They live in a new `twprojects-planning` sub-toolset alongside `users_workload`, which moves out of `twprojects-people`: the two are the allocated and the estimated plane of the same question, and answering "how loaded is this person" from one alone gets it wrong. `twprojects-planning` is added to the `project-manager` and `analyst` profiles so those keep the workload tool, but anyone pinning `-toolsets=twprojects-people` has to add the new toolset to keep it. Three behaviours the tool descriptions have to carry, because the response does not: - The list endpoint windows its results to today through 30 days from today when neither date is given, and says nothing about having narrowed the range. The tool sends no window of its own — defaulting one here would reorder every existing caller's results — so the description tells callers to always pass both, and a test pins the omission on the query string. - `linkedTaskEstimatedTime` counts each linked task whole, and a task can sit behind several allocations, so it must not be summed across them. - The per-day rate is what is held constant, so extending an allocation's end date adds committed time rather than spreading the existing total. `seconds_per_day` is the rate parameter rather than hours, since the hours form is a float and rounds. Delete is a soft delete and does not expose hardDelete, which is what leaves restore_allocation something to act on; restore is gated behind allowDelete for the same reason. Forecasted revenue and cost sit behind an explicit `include_financial_details` opt-in, as they are entitlement- and permission-gated and withheld silently. Tool-definition cost is +3,321 o200k tokens (+5.82%).
Three gaps found by comparing the tools against a real request and response. `inform_of_over_allocation` was missing, and it is the parameter that matters most here. Without it a change that overruns the assigned user's capacity is refused outright, leaving `ignore_collisions` as the only way through — and that one lets the change land while suppressing the over-allocation report with it, so nobody is told the person is now over-booked. It is defaulted on, and the create and update results say so when the API reports it; the report is the whole point of the flag, so returning a clean success would be worse than not offering it. `ignore_collisions` now documents that it takes precedence and what that costs. The sideloads a verbose read requests now arrive. `get_allocation` asked for `include=projects,assignee` and then dropped both, because the typed response modelled no sideloads — the query string was correct and the related objects never appeared. Covered by a test that also validates the result against the output schema the sideloads widened. Writes send `linkedTaskIDs` rather than `linkedTaskIds`, matching the response and the API's other clients. `distributeType` and `linkedTaskLoggedTime` stay unmodelled rather than being stripped from responses, so `list_allocations` still returns both in its streamed body. That is fine because neither can be set from here, which is now pinned by a test: an argument naming either is dropped before the payload is built, in both naming conventions.
Picks up the typed allocation support the allocation tools are built on, including the fix that sends a sideload list as one comma-separated `include` parameter. Before it, a verbose read asked for the project and assignee sideloads and received only the first. The branch could not build standalone until this landed: go.mod still resolved the SDK to a version with no Allocation type, and the tools were developed against a local replace directive.
Both sideload assertions read `lastURL.Query()["include"]` and compared the elements, which passes whether the sideloads travel as one comma-separated parameter or as one parameter per sideload. The endpoint only honours the former, so these tests accepted the encoding that silently returned the first sideload and nothing else. They now assert the raw query carries a single `include`, and check membership within its value.
The parameter returns only deleted allocations; it does not add them to the active ones. Against a live site the same window reports 307 allocations without it and 277 with it, every one of those carrying status "deleted". The description said "include deleted allocations in the results", which reads as additive — so a caller could treat such a list as the full picture and report nothing currently scheduled, or conclude an active allocation does not exist.
Restore was gated behind allowDelete on the reasoning that it only applies to a row a delete created. That was wrong: it acts on anything soft-deleted by any client, and the staging site carries 277 allocations deleted through the UI that it can recover. Confirmed against one of them, which the server had never touched. Gating it was also incoherent with the read surface. list_allocations is always registered, so an agent could already find deleted allocations with show_deleted and had no way to act on them. And restore is not destructive in the first place — it undoes a deletion. delete_allocation stays gated. cmd/mcp-stdio hardcodes allowDelete to false, so nothing that removes an allocation ships today.
ripexz
force-pushed
the
feature/allocation-tools
branch
from
August 20, 2026 10:03
a8c555c to
8340e37
Compare
rafaeljusto
approved these changes
Aug 20, 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.
Description
Adds eight tools for resource-scheduler allocations — the planning plane that commits a person's time to a project, distinct from task estimates and from logged time:
list_allocations,get_allocation,create_allocation,update_allocation,delete_allocation,restore_allocation,link_task_to_allocation,unlink_task_from_allocationBuilt on the typed allocation support added in twapi-go-sdk v1.23.1.
users_workloadmoves toolsetThe tools live in a new
twprojects-planningsub-toolset, andusers_workloadmoves into it fromtwprojects-people. The two are the allocated and the estimated plane of the same question, and answering "how loaded is this person" from one alone gets it wrong.twprojects-planningis added to theproject-managerandanalystprofiles, so profile users are unaffected. Anyone pinning-toolsets=twprojects-peoplelosesusers_workloadand needs to addtwprojects-planning.Behaviours the descriptions have to carry, because the response does not
Each was confirmed against a live site.
show_deletedreplaces the result set rather than adding to it: 307 allocations without it, 277 with, every one of those deleted.linkedTaskEstimatedTimecounts each linked task whole, and a task can sit behind several allocations, so it must not be summed across them.Notable choices
seconds_per_day, not hours. Same quantity, but the hours form is a float and rounds.inform_of_over_allocationdefaults on. Without it a change that overruns someone's capacity is refused outright, leavingignore_collisionsas the only way through — and that one lets the change land while suppressing the over-allocation report, so nobody is told the person is now over-booked. The create and update results say so when the API reports it. Verified live: "Allocation created successfully with ID … Note that it puts the assigned user over their capacity for this period."hardDelete, which is what leavesrestore_allocationsomething to act on. Restore is not gated behindallowDelete: it undoes a deletion rather than performing one, and it recovers anything soft-deleted by any client — the site tested carries 277 allocations deleted through the UI. Gating it would also leavelist_allocationsable to find deleted allocations, being a read tool, with no way to act on them.include_financial_detailsopt-in, being entitlement- and permission-gated and withheld silently.canViewFinancialDetailson each row reports what actually happened.distribute_typeand recurring allocations are not exposed, pending backend support.Tool-definition cost is +3,507 o200k tokens (+6.15%) (
go run ./cmd/mcp-tokens -base=main),list_allocationsbeing 1,338 of it. The rarely-useful project filters (projectHealths,onlyStarredProjects,hideObservedProjects,onlyProjectsWithExplicitMembership) are supported by the SDK but deliberately left off the tool.Type of Change
users_workloadmoves out oftwprojects-peopleTesting
go test -v ./...)gofmt,go vet,golangci-lintandcmd/docs-genare clean. Filters, ordering values, sideloads and write payloads are asserted on the wire rather than through the mocks' canned body, since a dropped parameter is otherwise indistinguishable from a working one. Results are validated against the published output schema, which nothing else does in-process.Also exercised end to end against a live site through the STDIO server: create → get → sparse-field get → update → list →
count_only→ ordered list → restore, plus the sideloads arriving and the over-allocation report firing. Test data created during the run was removed.Checklist
🤖 Generated with Claude Code