Back arrowAll guides

Why Claude Code Action Only Links the Issue Number, Not the Customer

Pixel art of a speech bubble routed through a gold robot into a code card on a dark green background
Talton Figgins••9 min read

Because "Fixes #182" is GitHub's own vocabulary, not Claude's. GitHub has recognized a fixed set of closing keywords in a pull request description for years: close, fixes, resolves, and their variants, each followed by an issue number, on the repository's default branch. When Claude Code's GitHub Action turns an issue into a pull request, it writes that same line because it's the only phrasing that actually closes the issue on merge, and closing the issue is the point.

The customer's name drops out for two different reasons, and they compound. Most of the time, nothing handed Claude Code the name to begin with: the action's GitHub App is scoped to contents, issues, and pull requests, so it reads what's in the repo and writes back to the repo, and a person's company, their plan tier, whether this is their third report this month, none of that lives in a GitHub issue by default. On the days someone did write the name into the issue, the second reason takes over: summarizing a diff and remembering which account is watching it are two different jobs, and the PR description is only doing the first one.

Where "Fixes #182" actually comes from

GitHub's linking documentation is specific about this: close, closes, closed, fix, fixes, fixed, resolve, resolves, resolved, each followed by # and a number, are the only strings GitHub parses to auto-close an issue when a PR merges to the default branch. Anything else in the description is just prose, as far as GitHub's parser is concerned.

Claude Code's GitHub Action doesn't add a proprietary version of this feature on top. Per the action's own FAQ, Claude "doesn't create PRs by default. Instead, it pushes commits to a branch and provides a link to a pre-filled PR submission page." Claude writes the description text going into that pre-filled page, and it writes a closing keyword into it for the same reason a human contributor would: it's fixing the issue it was pointed at, and this is how you say that in a way GitHub acts on.

The part of the story the repo can't see

GitHub issue #182
bug report, no customer field on GitHub issues
↓ reads issue + diff
Claude Code
summarizes the diff, writes the PR text
↓ writes
PR #182
Fixes #182
no customer named
company · plan · renewal
not a GitHub resource, no path in
The issue and the PR share a number. Nothing shares the company, because nothing wrote it down anywhere the action reads from.

The GitHub App permissions list eleven scopes in total, covering every Claude feature that touches GitHub. The Claude Code GitHub Action itself relies on three of them: Contents, Issues, and Pull requests, all read and write. Read and write access to your CRM, your support tool, or your billing system isn't in that list, and can't be, since it's a GitHub App and those aren't GitHub resources. Whatever Claude Code knows when it opens a PR, it knows because it's in the repo: the issue body, the linked comments, the code itself.

Most issue bodies don't carry a structured customer field either. GitHub issues have a title, a body, labels, and assignees. A reporter's employer sometimes shows up as a line of prose ("we're on the annual plan, this is blocking our rollout"), and just as often doesn't, because the person filing the bug is a support agent relaying it secondhand, or the original wording never got copied over when the ticket became a GitHub issue. Either way, there's no field for Claude to read reliably, so there's nothing reliable to put in the PR.

The workarounds teams try before changing anything structural

None of these require new tooling, which is why they're usually first:

  • A PR template field. Add "Customer:" as a line in the pull request template, and hope whoever opens or edits the PR fills it in. Claude Code will follow a template if the prompt tells it to look for one, but it's copying text out of the issue body into a new slot, not looking anything up, so a vague issue still produces a blank field.
  • A label convention, like customer:<account-slug>, applied by whoever triages the issue. This makes the account visible in the tracker's UI, but it's manual at the exact moment someone is least likely to remember, right after filing a bug, and it says nothing about how urgent that account makes the fix.
  • Editing the PR by hand after the fact. Someone who knows the backstory adds a line before merge. It works once. It's also the same person doing the same lookup every time, which is the definition of not scaling past a few PRs a week.

All three depend on a human noticing the connection and typing it in, every time, before anyone downstream can act on it. Below a handful of customer-linked bugs a month, that's a fine tax to pay. Past that, PRs start shipping clean and the follow-up email to the account that reported it either happens because someone happened to remember, or it doesn't happen at all.

Where this stops being a Claude Code problem

The fix isn't asking Claude Code to guess harder. It's giving it somewhere to look. Modem watches connected GitHub repos alongside your support tools and keeps a company profile attached to every bug report the moment it comes in, before an issue or a PR exists for it. A customer's report gets tied to their account the same way any other conversation from them would, independent of whether the person filing the GitHub issue thought to write the company name down.

From there it's a config line, not custom scripting. Claude Code's --mcp-config flag already accepts arbitrary MCP servers, and Modem's MCP server answers exactly the kind of question a PR description needs: which account is attached to this topic. Point the action at it and the prompt can ask for the customer before it writes the summary, so "Fixes #182" gets a second line naming the account attached to the topic and where that account's renewal stands. The Claude Code integration runs the same idea the other direction too, composing task descriptions for Claude Code from the underlying bug reports and topic data so the context arrives already attached rather than something the prompt has to go fetch. We make Modem, so this is not a neutral ranking. The GitHub Action's own triage behavior, covered in our guide to triaging GitHub issues at scale, is still doing real work underneath this; it's just not the layer this particular gap lives in. The wider comparison of ways to close it is in tools that give Claude Code customer context.

Change the prompt, not the issue template

Skip the human template field and put the instruction in the action's own prompt instead. Add a line to your CLAUDE.md or the workflow's claude_args: when the issue body names a company, add a "customer:" line to the PR description before writing the closing keyword. That's a prompt change, not a new integration, and it catches any issue whose body already names a company, since the name is already sitting in the text Claude Code read. It still only surfaces what's already written down. An issue that never mentions a customer gives the prompt nothing to extract, and ships exactly as blank as it always did.