Skip to content

feat(linear): resolve label names for issue writes (--label-name)#1411

Open
ericlitman wants to merge 2 commits into
mvanhorn:mainfrom
ericlitman:codex/mob-340-479-upstream
Open

feat(linear): resolve label names for issue writes (--label-name)#1411
ericlitman wants to merge 2 commits into
mvanhorn:mainfrom
ericlitman:codex/mob-340-479-upstream

Conversation

@ericlitman

Copy link
Copy Markdown
Contributor

feat(linear): resolve label names for issue writes (--label-name)

Follow-up to #1317 (--project-name portfolio resolver). Adds a repeatable --label-name flag to issues create and issues edit that resolves a label name to its UUID at write time — including team-safe global (workspace) labels — so agents can apply labels by name instead of needing UUIDs.

Change (entirely within library/project-management/linear/)

  • New internal/cli/label_resolve.go, mirroring portfolio_resolve.go: searchIssueLabelsLive (paginated issueLabels GraphQL, name-contains filter via the shared portfolioNameContainsFilter, stable sort), resolveLabelNameForWriteLive (exact-match within {team} ∪ {global, team==null}; typed ambiguous / not found via the generic portfolioResolveErr[T]), resolveLabelNamesForWriteLive (per-name resolve + dedup), mergeLabelIDs, matchingIssueLabelTeam (nil/empty team ⇒ global, included for any team).
  • Wires --label-name into issues create (resolves with --team) and issues edit (resolves with the issue's team); resolved UUIDs merge into the existing --label set before validateIssueLabelTeams runs on the merged set. --label (UUID) and --label-name are combinable and de-duplicated.
  • Tests in linear_agent_test.go style: resolve-by-name, ambiguity, not-found, team-safe global resolution, --label + --label-name merge, and a --project-name regression.

Validation

go build ./... and go test ./... green (against this branch's main base). Live-verified: issues create --team … --label-name "kind:bug" --label-name "area:review-tooling" applies both real labels; issues edit … --label-name … applies the resolved label.

Notes

  • Reuses existing validateIssueLabelTeams / filterIssueLabelsForTeam and the feat(linear): resolve portfolio names for issue writes #1317 portfolio resolver helpers (the generic typed-error helpers, normalizePortfolioName, pagination filter).
  • No behavior change to the existing --label (UUID) path or --project-name resolution.

Opened from a maintained fork (ericlitman/printing-press-library).

Add a repeatable --label-name flag to `issues create` and `issues edit`
that resolves a team-scoped label name (including team-safe global labels)
to its UUID at write time, mirroring the mvanhorn#1317 --project-name resolver.
Reuses validateIssueLabelTeams for post-resolution team validation.

MOB-340, MOB-479
@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds a --label-name flag to issues create and issues edit that resolves Linear label names to UUIDs at write time, including team-safe global (workspace) labels, mirroring the existing --project-name resolver from PR #1317.

  • label_resolve.go (new): searchIssueLabelsLive (paginated GraphQL with name-contains filter), resolveLabelNameForWriteLive/resolveLabelNamesForWriteLive (exact-match with ambiguity/not-found typed errors), mergeLabelIDs (case-insensitive UUID dedup), and matchingIssueLabelTeam (team-specific + global labels for any team query).
  • issues_edit.go: Wires --label-name using the issue's fetched team; refactors the pre-mutation metadata fetch into parseIssueEditMetadata and gates the second fetch correctly on !issueMetaLoaded, resolving the redundant-fetch concern from a prior review.
  • issues_create.go: Wires --label-name using --team (no issue fetch needed); resolved UUIDs merge into --label before validateIssueLabelTeams runs.

Confidence Score: 5/5

Safe to merge — all label resolution paths are correct and tested; the refactored metadata-fetch logic in issues_edit.go preserves existing behavior.

The new resolver faithfully mirrors the established portfolio-resolver pattern, the needsIssueMetadata guard correctly eliminates the previously-flagged redundant fetch, and the eight new tests cover the critical combinations including global labels, dedup, ambiguity, not-found, and media+label-name interaction. No mutation path is affected without going through validateIssueLabelTeams.

No files require special attention; the two minor observations are confined to an efficiency note in issues_edit.go and a UX note in label_resolve.go.

Important Files Changed

Filename Overview
library/project-management/linear/internal/cli/label_resolve.go New resolver: correctly implements paginated label search, exact-match disambiguation, global-label inclusion for any team, name-level dedup, and UUID-level dedup. Mirrors portfolio_resolve.go patterns faithfully.
library/project-management/linear/internal/cli/issues_edit.go Wires --label-name with a pre-resolution issue fetch and the new parseIssueEditMetadata helper; the needsIssueMetadata guard correctly prevents a redundant second fetch. When combined with --project-name, a second issue fetch still occurs (fetchIssueTeamKeyLive does not set issueMetaLoaded), but correctness is preserved.
library/project-management/linear/internal/cli/issues_create.go Wires --label-name using the --team flag for resolution; resolved IDs merge with --label before validation. Logic is clean and follows the existing pattern.
library/project-management/linear/internal/cli/linear_agent_test.go Adds 8 new test cases covering: dry-run UUID resolution (create/edit), media+label-name description preservation, duplicate label-name dedup with single lookup, ambiguity and not-found envelopes, global label resolution, and --label+--label-name merge. No test for --project-name+--label-name combined in issues edit.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant CLI as issues edit/create
    participant LR as label_resolve.go
    participant Linear as Linear API

    CLI->>CLI: parse --label-name flags
    alt --label-name provided
        CLI->>Linear: fetchIssueLive (edit only, if !issueMetaLoaded)
        Linear-->>CLI: "issue {id, team}"
        CLI->>LR: resolveLabelNamesForWriteLive(names, team)
        loop per unique normalized name
            LR->>Linear: issueLabels(filter: nameContains)
            Linear-->>LR: label nodes (all teams + global)
            LR->>LR: "matchingIssueLabelTeam (team-specific OR nil=global)"
            LR->>LR: exactLabelMatches 1 match resolve else error
        end
        LR-->>CLI: resolved UUIDs
        CLI->>CLI: mergeLabelIDs(--label UUIDs, resolved UUIDs)
    end
    CLI->>CLI: "input labelIds = merged labels"
    CLI->>Linear: issueCreate / issueUpdate mutation
    Linear-->>CLI: result
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant CLI as issues edit/create
    participant LR as label_resolve.go
    participant Linear as Linear API

    CLI->>CLI: parse --label-name flags
    alt --label-name provided
        CLI->>Linear: fetchIssueLive (edit only, if !issueMetaLoaded)
        Linear-->>CLI: "issue {id, team}"
        CLI->>LR: resolveLabelNamesForWriteLive(names, team)
        loop per unique normalized name
            LR->>Linear: issueLabels(filter: nameContains)
            Linear-->>LR: label nodes (all teams + global)
            LR->>LR: "matchingIssueLabelTeam (team-specific OR nil=global)"
            LR->>LR: exactLabelMatches 1 match resolve else error
        end
        LR-->>CLI: resolved UUIDs
        CLI->>CLI: mergeLabelIDs(--label UUIDs, resolved UUIDs)
    end
    CLI->>CLI: "input labelIds = merged labels"
    CLI->>Linear: issueCreate / issueUpdate mutation
    Linear-->>CLI: result
Loading

Reviews (2): Last reviewed commit: "fix(linear): harden label-name edit reso..." | Re-trigger Greptile

@ericlitman

Copy link
Copy Markdown
Contributor Author

QA complete on our side: crabbox-council reviewed (3 reviewer lanes; sole P2 adjudicated false by code trace), go build ./... && go test ./... green, and live-verified that issues create --label-name / issues edit --label-name apply real labels (incl. team-safe global labels). All required checks and Greptile are green here. Opened from a maintained fork — we have read access only, so this is ready for a maintainer to merge. The one Greptile nit (a redundant issue fetch in live issues edit --label-name) is a non-blocking perf follow-up tracked on our side.

@ericlitman

Copy link
Copy Markdown
Contributor Author

MOB-630 follow-up landed on this PR branch.

Proof:

  • PR head: d4a5535b09e8b9123c4becaa1be59dd709dd5135
  • Local validation on PR branch:
    • python3 .github/scripts/verify-skill/verify_skill.py --dir library/project-management/linear/
    • cd library/project-management/linear && go test ./...
    • cd library/project-management/linear && go build ./...
    • cd library/project-management/linear && go vet ./...
    • cd library/project-management/linear && govulncheck ./...
    • git diff --check
  • GitHub checks at d4a5535b0: Verify, Validate SKILL.md, Govulncheck, Greptile Review, and Greptile policy gate all passed.
  • Greptile helper: current head reviewed, no unresolved threads, no actionable markers.
  • Upstream merge attempt is permission-blocked for this token: GraphQL: ericlitman does not have the correct permissions to execute MergePullRequest.
  • Local fork integration completed: fork/main is 9df04331cca1fcc3954ba89eb66c29a12c6d9e30, a cherry-pick of the reviewed MOB-630 patch. Stable patch-id matches d4a5535b0: a573a95f23065949f27b8b93629bef0403cacbc9.

Linear follow-up filed from review: MOB-641 covers the separate --label-name SKILL.md documentation gap.

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.

1 participant