fix(linear): seed a ticket prompt only when the worktree is created - #259
Conversation
handleSessionCreate ran ticketPromptFor on every session creation, which
seeded the agent's first message from a Linear ticket in two ways:
- reuse: linear.ExistingPrompt read back the prompt.txt already sitting
in .fleet/ticket/<ID>/. It never looked at the branch, so a checkout
that once held a ticket re-asked the original task on every session
added afterwards -- forever, including long after the checkout had
moved on to master.
- inference: a branch or worktree dir name matching <TEAM>-<N> fetched
and materialized the ticket, then seeded from it.
A seeded first message is the gesture of starting a worktree from a
ticket, not a property of the directory that worktree happens to be. So
both branches go, and handleSessionCreate now passes msg.prompt through
untouched. The worktree-creation path already set it explicitly (and
ticketPromptFor early-returned on it), so `w` + a ticket and
`fleet worktree --ticket` are unaffected; `a`/`n`/`A` always start empty.
The negative pin (NegativelyPinned/pinNoTicket) existed only to stop the
inference path re-asking Linear on every session start, so it goes with
it rather than staying as a write-only file. pathTailAfterRepo stays --
palette_tickets.go still uses it.
TestManualSessionCreationSeedsNoPrompt fails if handleSessionCreate
reaches into internal/linear again, or if anything in app.go assigns to a
.prompt field on the way to the launch.
Cost: a worktree created outside fleet on a ticket-named branch no longer
materializes its ticket.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBQUYTfR7kzUjgozd58mr6
|
🚨 gitStream Monthly Automation Limit Reached 🚨 Your organization has exceeded the number of pull requests allowed for automation with gitStream. To continue automating your PR workflows and unlock additional features, please contact LinearB. |
|
Note
|
| Layer / File(s) | Summary |
|---|---|
Remove prompt reuse and negative pinning internal/linear/materialize.go, internal/linear/linear_test.go |
Materialization no longer reuses existing prompts or creates negative-ticket pins. Related tests were removed. |
Keep manual session prompts unchanged internal/ui/app.go, internal/ui/ticket.go, CLAUDE.md |
Session creation preserves the caller-provided prompt and removes branch-based ticket inference. Worktree creation reports ErrNotFound with an explicit message. |
Validate explicit prompt seeding internal/ui/worktree_ticket_routing_test.go, changelog/unreleased/manual-session-no-ticket-prompt.md |
Tests verify that manual session creation does not access Linear or assign prompts. The changelog records the behavior change. |
Estimated code review effort: 2 (Simple) | ~10 minutes
Merge Risk: ⚪ Minimal · up to 21ca1
The change limits ticket prompt seeding to explicit worktree creation, with no actionable merge-blocking risk remaining after normal checks and review.
Possibly related PRs
- brizzai/fleet#248: Introduced the ticket inference and materialization flow removed by this PR.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title clearly and concisely describes the main change: ticket prompts are seeded only when a worktree is created. |
| Docstring Coverage | ✅ Passed | Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. |
| Linked Issues check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
✨ Finishing Touches
📝 Generate docstrings
- Create stacked PR
- Commit on current branch
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Commit unit tests in branch
fix/manual-session-no-ticket-prompt
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands.
Two review findings on the inference removal.
ticketStatusLine swallowed ErrNotFound alongside ErrNotConnected, which
was right while inference guessed identifiers out of branch names -- "no
such issue" was the ordinary answer there. Removing inference left one
caller, worktree creation, where the user picked the ticket in the `w`
dialog and Materialize re-fetched it: "not found" now means the issue was
deleted, or their access to it changed, in the seconds since. Swallowing
it hands back a worktree and a session with no prompt and no message at
all. It gets its own line, worded rather than %v-formatted because the
sentinel reads "linear: issue not found" and would render "Linear:
linear: issue not found". ErrNotConnected stays swallowed -- it can
barely reach this caller, since the dialog's own fetch had to succeed for
there to be a ticket to materialize. The CLI path was never affected; it
prints the error to stderr itself.
TestManualSessionCreationSeedsNoPrompt walked all of app.go for any
AssignStmt assigning to a selector named `prompt`. sessionCreateMsg
already declares that field, so the name is live in the package and any
future `x.prompt = ...` -- a dialog, a form -- would fail with an error
message about Linear tickets inheriting into manual sessions. Scoped to
the handleSessionCreate FuncDecl, which is what the doc comment already
described. Nothing is lost: the other way to set a prompt is a composite
literal (sessionCreateMsg{prompt: ...}), which is how the worktree path
legitimately does it and is not an AssignStmt in any file. Verified both
directions -- an assignment inside the function fails, one elsewhere in
app.go passes.
CLAUDE.md carried the inference-era rationale for the swallow, in a line
this branch wrote.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBQUYTfR7kzUjgozd58mr6
|
/ship |
|
🚀 Release PR opened: #260 (v2.30.1) |
The bug
A session added by hand (
a/n/A) to a checkout that once held a Linear ticket opened already working on that ticket — even when the checkout had since moved on tomaster.handleSessionCreateranticketPromptForon every session creation, seedingSession.InitialPrompttwo ways:linear.ExistingPrompt(path)read back theprompt.txtalready sitting in.fleet/ticket/<ID>/. It never looked at the branch, so the prompt was re-asked on every session after the first, forever. This is the reported case.<TEAM>-<N>fetched and materialized the ticket, then seeded from it.The fix
A seeded first message is the gesture of starting a worktree from a ticket, not a property of the directory that worktree happens to be. Both branches are gone;
handleSessionCreatenow passesmsg.promptthrough untouched.The worktree-creation path (
app.go) already setpromptexplicitly, andticketPromptForearly-returned when it was non-empty — so nothing there changes.a/n/Ain a ticket worktreea/n/Aon a ticket-named branchw+ a ticketfleet worktree --ticket/-pAlso removed
The negative pin (
NegativelyPinned/pinNoTicket/.no-ticket) existed only to stop the inference path re-asking Linear on every session start. With that path gone it would be a write-only file, so it goes too.pathTailAfterRepostays —palette_tickets.gostill uses it.Guard
TestManualSessionCreationSeedsNoPromptfails ifhandleSessionCreatereaches intointernal/linearagain, or if anything inapp.goassigns to a.promptfield on the way to the launch. Verified it fires by temporarily reintroducing an assignment.Known cost
A worktree created outside fleet on a ticket-named branch no longer materializes its ticket — that was the inference branch. Deliberate, per the scoping decision: manual session creation seeds nothing, ever.
Testing
make buildpasses.go test ./...is green exceptTestCredentialResolutionOrder, which fails identically on unmodifiedmasteron this machine (it asserts "nothing stored" while the local keychain holds a real Linear credential).🤖 Generated with Claude Code
https://claude.ai/code/session_01WBQUYTfR7kzUjgozd58mr6
Summary by CodeRabbit
Bug Fixes
Documentation