Repository navigation
Conversation
…tifier Nothing put a job's PR on its Linear ticket. upsertStatusComment would have listed the URLs in a comment, but nothing ever called it. Each PR a writable step opens is now attached to the ticket once, through attachmentLinkURL, which makes a rich GitHub attachment where the workspace has that integration. `linearLinkedPrs` records what landed, and a failed write retries on the next tick. Recording a new PR URL ticks the job, because the PR watcher never does. Linear's state sync never ran for a polled job either: the intake script sends `issueIdentifier` and never `linearUuid`, and linearReady required the UUID. The writer now takes either, and resolves the UUID through issue(id:) before each mutation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
attachmentLinkURL is not idempotent. It refuses a URL the ticket already
holds ("This URL has already been linked with CLT-1923."), which is the
case for a PR linked by hand or by Linear's own GitHub integration. The
refusal comes back as an HTTP 200 with a GraphQL error, so withRetry
slept 1s, 5s, 30s, 5m, 30m and 1h on it. The startup tick walks the
queue one job at a time, so every job behind that one waited too.
linkPr now reads the ticket's attachments first and skips a URL already
there. LinearError carries Linear's `userError` flag and the
userPresentableMessage, and withRetry rethrows a user error at once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tick awaited each Linear write, and the writer retries a transient failure for up to an hour and a half. That held the job's own reaping for as long, and on the startup tick, which walks the queue one job at a time, every job behind it. syncLinear starts the write without awaiting it and records the result when it lands. An in-flight set keeps a tick that arrives mid-retry from starting the same write again. Nothing is marked until a write lands, so one that finally fails is re-emitted by a later tick. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Outpost never puts a job's PR on its Linear ticket, and it never moves a polled ticket's status.
LinearWriter.upsertStatusCommentwould list the PR URLs in a comment, but nothing calls it. The state sync (In Progress, In Review, Done) exists, butlinearReadyrequiresexternalRef.linearUuid. The hourly intake script fetches each issue'sidand then sends onlyurlandissueIdentifier, so no polled job has a UUID, and every Linear write skips every job.Some tickets still show their PR, and none of those links came from Outpost. Linear's GitHub integration links a PR only when the issue ID is in the branch name or the PR title, or follows a magic word such as "Fixes" in the body. Outpost's PRs match none of these. Their branches read like
fix/backup-codec-layer-dimensions, and PR #2029's body says "Linear: CLT-2995", which has no magic word. Where a ticket shows a PR, a person linked it by hand. Or the PR "Fixes" a GitHub issue that Linear's issue sync had already attached, which makes the PR look linked when it is not.This change attaches each PR a writable step opens to the job's ticket, once, through
attachmentLinkURL. Where the workspace has Linear's GitHub integration, that call makes a rich PR attachment with status, checks and reviews. Otherwise it makes a plain link.decideJobTransitionsemits alink-linear-prtransition for each unlinked URL. The engine writes it and records the URL inlinearLinkedPrs. Review steps, cancelled steps and abandoned jobs are skipped. Failed and done jobs still get their links.applyPrFactsticks the job when it records a new PR URL, because the PR watcher never ticks a job.The writer accepts the issue's UUID or its identifier (
CLT-2995). Before each mutation it resolves the UUID throughissue(id:), which reads either form. The intake script is left as it is. The daemon never overwrites the stored copy of a built-in schedule, so a script change would not reach existing installs.attachmentLinkURLis not idempotent. It refuses a URL the ticket already holds, with "This URL has already been linked with CLT-1923.", and it returns that refusal as an HTTP 200 with a GraphQL error.withRetrythen slept 1 s, 5 s, 30 s, 5 min, 30 min and 1 h on a call that can never pass. The startup tick handles jobs one at a time, so every job behind that one waited too.linkPrreads the ticket's attachments first and skips a URL already there.LinearErrorcarries Linear'suserErrorflag, andwithRetryrethrows a user error at once.Impact
On one install, none of the 27 Linear jobs had a
linearUuid. Of the 7 PRs those jobs opened, Outpost linked none, and only one was on its ticket: PR #1634 on CLT-1923, linked by hand in 2025. With this change, the first start attached 5 PRs in 12 seconds, for example livekit/client-sdk-js#2029 on CLT-2995, which then moved to In Review. The start also hit the refusal above on CLT-1923 and stalled the rest of the queue. The second commit fixes that.On the first start after an upgrade, existing jobs catch up: their PRs get attached, and their tickets move to the state the job is in, including Done for jobs that already finished. Unit tests cover the transition, the writer's identifier resolution, the already-linked skip, the no-retry rule and the engine's link-on-new-URL path, all against a fake Linear client.
🤖 Generated with Claude Code