docs/users/features/channels/gitlab.md
This guide covers setting up a Qwen Code channel that monitors GitLab todos and responds to mentions on issues and merge requests.
read_api and api scopesAdd the channel to ~/.qwen/settings.json:
{
"channels": {
"my-gitlab": {
"type": "gitlab",
"token": "$GITLAB_TOKEN",
"pollInterval": 60000,
"senderPolicy": "open",
"sessionScope": "chat_thread",
"cwd": "/path/to/your/project",
"groupPolicy": "open",
"action_prompt_template": {
"mentioned": "Project: %project% | URL: %project_url% | Author: %author% | Type: %target_type% | IID: %iid% | Title: %title% | Description: %description% | TodoID: %todo_id%"
}
}
}
}
Set the token as an environment variable:
export GITLAB_TOKEN="glpat-your_token_here"
For self-hosted instances, set baseUrl:
{
"baseUrl": "https://gitlab.example.com"
}
| Option | Default | Description |
|---|---|---|
token | (required) | PAT with read_api + api scopes |
pollInterval | 60000 | Poll interval in ms |
baseUrl | https://gitlab.com | GitLab instance URL |
action_prompt_template | (required for processing) | Maps GitLab action names to metadata templates |
groupPolicy | "disabled" | Must be "open", or "allowlist" with the project listed |
senderPolicy | "allowlist" | Who can trigger the bot |
This field controls which todo actions are processed and how metadata is rendered. Only actions with a configured template are dispatched; all others are skipped and marked done.
{
"action_prompt_template": {
"mentioned": "Project: %project% | Author: %author% | Title: %title%"
}
}
The directly_addressed action (comment starting with @bot) automatically falls back to the mentioned template if not explicitly configured.
| Key | Trigger |
|---|---|
mentioned | Someone @mentions the bot in a comment or description (not at the start) |
directly_addressed | A comment starts with @bot (falls back to mentioned template) |
assigned | Someone assigns the bot to an issue/MR |
review_requested | Someone requests the bot as a reviewer on an MR |
approval_required | An MR requires the bot's approval (approval rules) |
marked | Someone marks the bot's comment/issue/MR (star) |
build_failed | A CI/CD pipeline fails on the bot's branch/MR |
unmergeable | An MR the bot is involved with becomes unmergeable (conflicts) |
merge_train_removed | An MR is removed from the merge train |
Only keys present in action_prompt_template are processed. Unconfigured actions are skipped and marked done silently.
| Variable | Value |
|---|---|
%project% | Project path (e.g., owner/repo) |
%project_url% | Full project URL |
%author% | Todo author username |
%target_type% | Issue or MergeRequest |
%iid% | Issue/MR internal ID |
%title% | Issue/MR title |
%description% | Issue/MR description body |
%todo_id% | GitLab todo ID |
%% | Literal % (escape) |
Unknown variables are preserved as-is in the output.
The template renders into envelope.metadata (structured context). The triggering text (todo.body or description) goes into envelope.text (primary prompt). The base class assembles the final prompt sent to the agent:
[alice] please fix this bug
Project: owner/repo | URL: https://gitlab.com/owner/repo | Author: alice | Type: Issue | IID: 42 | Title: Test Issue | Description: ... | TodoID: 100
[sender] prefix + envelope.text (with @bot stripped)envelope.metadata (rendered template, sanitized)You do not need a %body% variable — the comment/description text is always the primary prompt content, and the template provides supplementary context below it.
On a public project, setting senderPolicy: "open" allows any GitLab user who @mentions the bot to submit prompts that drive the agent in your cwd.
Always use senderPolicy: "allowlist" with explicit allowedUsers on public projects.
The adapter always sets isMentioned = true on dispatched envelopes, because GitLab has already determined the mention when creating the todo. The action_prompt_template config is the real event filter — only actions with a configured template are processed. The @bot mention is stripped from the message text before dispatch via stripBotMention.
groupPolicy must be set to "open", or "allowlist" with the project explicitly listed, for todos to be processed. The default value "disabled" drops all mentions: todos are marked done and the cursor advances, but no dispatch occurs. A rejection is logged (preflight rejected reason=group_disabled) but the todo is still consumed. If your bot is not responding to mentions, check that groupPolicy is not "disabled".
The adapter uses GitLab's Todos API as the message source:
GET /todos?state=pending for new todosinitialized: false), all pending todos are marked done without dispatch and the cursor advances to the max todo ID. This prevents a backlog flood on first start.id <= cursor are marked done (best-effort) to prevent them from being re-fetched on every pollid > cursor and configured action_prompt_templatetarget_url anchor:
#note_123 present → comment mention → text is todo.body (the comment)handleInbound (requires groupPolicy: "open" or "allowlist" with the project listed)The cursor (lastProcessedId) advances regardless of dispatch success or failure. Failed dispatches post a ⚠️ error comment on the issue/MR and are not retried — the user can re-mention the bot to trigger a new todo.
{ lastProcessedId: 0, initialized: false } on first launch. On the first poll cycle, all pre-existing pending todos are marked done without dispatch (the initialized flag gates this one-time drain), preventing a backlog flood.read_api + api PAT scopes. Group-level or project-level tokens work if they have these scopes.qwen channel start my-gitlab