Add, remove, and set flags for project teams, labels, and initiatives - #282
Open
schpetbot wants to merge 1 commit into
Open
Add, remove, and set flags for project teams, labels, and initiatives#282schpetbot wants to merge 1 commit into
schpetbot wants to merge 1 commit into
Conversation
project update could only replace a project's teams and labels, and could not change initiatives at all: project create accepts --initiative, but after creation the only route was a hand-written mutation. Every collection field an update command can change should offer the same three operations, following the pattern issue update already has for labels: --team/--label/--initiative replace the whole set, --add-* appends, --remove-* detaches, all repeatable, and a replace flag cannot be combined with its add/remove flags. Linear's ProjectUpdateInput only takes a full teamIds/labelIds list, so add and remove read the project's current connection, following every page, compute the new set, and send it. Removing something the project does not have is an error that lists what it does have, rather than a silent no-op, and removing the last team errors up front because Linear rejects an empty teamIds. Initiatives have no input field: membership is an InitiativeToProject join row, so all three initiative flags diff the desired set against the current rows and run initiativeToProjectDelete then initiativeToProjectCreate. Deletes go first because a project may appear only once in an initiative hierarchy, so moving it to a parent or child initiative is rejected while the old link still exists; that is also why a UUID-shaped initiative is confirmed to exist before any link is touched, since the shared resolver passes UUIDs through unchecked. Those mutations are not transactional, so a failure part-way reports what was applied, including the already-committed project fields, and what is still pending, with the remaining flags given by initiative ID because names are not unique, instead of implying a rollback. The survey of issue update and initiative update found no other replace-only collection flag: IssueUpdateInput's releaseIds and subscriberIds and InitiativeUpdateInput's labelIds are not exposed by either command. Claude-Session: https://claude.ai/code/session_01A9qEGri4p2HZMQSuYsBmub
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.
First of a two-PR stack (this → #283), continuing the MCP-informed series (#279, #280, #281).
project updatenow offers the same three operations for every collection it can change, mirroring the label triadissue updatealready had:--team/--label/--initiativereplace the set,--add-*appends,--remove-*detaches, all repeatable, with replace rejected alongside add or remove and the same item rejected in both add and remove.project updatepreviously could not change initiatives at all.Implementation notes worth knowing:
ProjectUpdateInputonly has replace-styleteamIds/labelIds, so add and remove read the current connection with cursor pagination, compute the full set, and send it. Removing an absent item errors and lists the current set; removing the last team errors because Linear rejects an empty team set (verified live). Initiatives have no input field, so the flags diff againstproject.initiativeToProjectsand runinitiativeToProjectDeletebeforeinitiativeToProjectCreate(delete first because the schema forbids a project on both an initiative and its ancestor or descendant). UUID-shaped initiatives are confirmed to exist before any link is touched, and a partial failure reports applied, unknown, and not-applied changes with retry flags by UUID.Verified: 747 tests in both color modes, a 21-case QA table run live on scratch objects (all deleted), and four high-effort Codex review passes whose real findings (link ordering against the initiative hierarchy, hidden already-applied update, unvalidated UUIDs, non-unique retry flags, non-advancing cursor, unknown outcome on a thrown request) are all fixed with tests.
https://claude.ai/code/session_01A9qEGri4p2HZMQSuYsBmub