TL-572: linear CLI: 'issues create' (and 'update') never return applied labels, only 'issues get' does - #7
Closed
FelixLisczyk wants to merge 3 commits into
Closed
TL-572: linear CLI: 'issues create' (and 'update') never return applied labels, only 'issues get' does#7FelixLisczyk wants to merge 3 commits into
FelixLisczyk wants to merge 3 commits into
Conversation
The issueCreate mutation sent labelIds correctly, so Linear applied the labels, but the mutation's selection set never asked for `labels` back. The response therefore always carried `"labels": null`, even on a fully successful create. A scripted or agentic caller reading the create response to confirm label application got a false negative and had to pay an extra `issues get` round-trip to find out the labels were there all along. Two defects were in play, and both are fixed here: 1. The selection set omitted `labels`. It also omitted `priority`, `estimate`, `dueDate`, and `cycle` — every one of them a value the caller passes *into* create, so every one of them carried the identical "I set it, the response says null, did it work?" failure. All five are now requested, copying the shapes from GetIssue verbatim so the two selection sets read as siblings. `delegate` and `attachments` are deliberately left out: issueCreate cannot set a delegate, and a new issue has no attachments, so requesting them could never surface caller-supplied data. 2. The DTO layer rendered an empty collection as `null`, which is ambiguous between "this issue has none" and "this renderer does not report them" — exactly the confusion above. populateIssueBase now always allocates, so `labels`, `children`, and `attachments` render as `[]`. Delegate deliberately keeps `omitempty` and still vanishes when nil; an absent object is unambiguous in a way an absent collection is not, and the comment on populateIssueBase records that so the two are not later "harmonised" back together. This changes read-path JSON as well: `issues get|list` and `search` at detailed|full report `[]` where they reported `null`. Confining it to create would mean duplicating the DTO layer and making create's output structurally diverge from get's, which undercuts the parity this fix is for. It is a real if small break for consumers using has(), == null, or // defaults, rather than a cosmetic one. Tests are layered because no single layer covers this. The service-level tests build their own core.Issue via the mock, so they prove the DTO/formatter path but would stay green if the selection set regressed. The new httptest-backed client test captures the outgoing mutation body and is the only guard that fails on that regression — verified by removing the fields and watching it go red. Its assertions are anchored to whole lines, since a bare `labels` substring also matches `labelIds` in the mutation input and would pass against the unfixed code.
The previous commit made labels, children, and attachments render as an empty array rather than null. Comments were left rendering as null, which would emit `"labels": []` next to `"comments": null` in the same JSON object — reproducing, one field over, the exact ambiguity that commit set out to remove. Kept deliberately separate so it can be reverted on its own without disturbing the labels fix, since it touches a field the create defect never involved. One acknowledged asymmetry: create does not request comments at all, so a newly created issue now reports `"comments": []` for something the server was never asked about. That is the accepted cost of a uniform contract — the DTO describes the shape it renders, not the wire response it was built from — and the test records it.
The end-to-end create test claimed to cover the GraphQL selection set, but its fake server ignored the request and answered with a canned body that carried labels regardless — so deleting the selection set left it green. Its handler now asserts the captured mutation actually asks for labels. Also cover parent in the field-survival test, and anchor the LabelDTO and delegate leak assertions to their own fields instead of substring-scanning the whole document, so they cannot start failing on unrelated content. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GBkx8wtrq2Mtb8N7jNerwZ
Owner
Author
|
Superseded by the upstream PR: joa23#74 |
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.
No description provided.