Co-authored-by: ayattara <alassane.yattara@savoirfairelinux.com> Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
31 KiB
status, created, updated, owner
| status | created | updated | owner |
|---|---|---|---|
| building | 2026-05-04 | 2026-08-05 | tbd |
GitLab Integration
Why
Teams whose code lives on GitLab cannot complete the same task, review, and automation workflows available for GitHub without leaving Kandev. Existing GitLab support can browse merge requests and issues and contains partial watch and review plumbing, but its connection is installation-wide and the main workflows are not usable end to end.
What
- GitLab and GitHub can be connected at the same time. Each integration only reads or mutates its own provider.
- Each Kandev workspace owns exactly one GitLab connection: one normalized host URL, one authentication method, one credential, and one health record.
- The default host is
https://gitlab.com; self-managedhttp://andhttps://origins are supported for API calls, web links, clone URLs, and merge request creation. Kandev preserves the configured scheme. - A workspace can authenticate with a personal access token or a
glablogin for its configured host.GITLAB_TOKENremains an explicit deployment fallback, but it is never persisted and only applies to workspaces configured to use that fallback. - GitLab browse, task-link, review, watch, and write endpoints require an
authoritative
workspace_idand resolve that workspace's connection. Data or credentials from another workspace are never used as fallback. - Task creation is the narrow unauthenticated exception: branch discovery for
an explicitly entered public
gitlab.comrepository URL works without a saved workspace connection. It does not expose private projects, browse results, merge requests, issues, or write actions. - GitLab repository matching uses provider, normalized provider host, and full subgroup project path. Repositories with unknown or mismatched provider hosts are not eligible for GitLab linking or merge-request actions. Decision: ADR-2026-07-20-repository-provider-origin-identity.
- Users can browse and search merge requests and issues, then launch a task from either row with the same configurable action presets used by GitHub.
- Launching from a merge request records the task-to-MR association after task creation. Launching from an issue includes the issue as task context but does not create a durable issue-sync relationship.
- Users can link an existing task to a merge request by pasting a full MR URL, including URLs from the workspace's configured self-managed host. They can unlink one association without deleting the task or upstream MR.
- A merge request opened outside Kandev's own MR-creation action (e.g.
glab mr createfrom an agent session, or opening the MR directly in the GitLab web UI) on a task's session branch is auto-linked: on the next push the open MR matching that branch is found and linked, scoped to the pushing repository, with an on-demand check available as an immediate alternative to waiting for a push or the background poller. Linking also creates the refresh watch that keeps review/pipeline status current, so no manual link step is required for this common case. Multi-repository and multi-branch tasks each get their own association and watch, scoped per(repository_id, branch); re-linking an already-linked MR is a no-op rather than a duplicate. - For a workspace with GitLab configured, task context menus expose
GitLab Merge Requestinside the sharedLinksubmenu. Desktop users can reach it by right-clicking a task, and touch users can reach the same action through the task row's visible actions menu. - An unlinked task does not show a persistent
Link MRaction in its task top bar. After at least one MR is linked, the top bar shows the linked-MR status control and continues to allow opening, unlinking, or linking another MR. - Linked MRs are visible from both the GitLab list and task detail. Multiple tasks can link to one MR, and a multi-repository task can link one MR per repository.
- A linked MR has an in-app review surface with title and description, source and target branches, mergeability/conflicts, files, commits, approvals, reviewers, pipeline rollup, and threaded discussions.
- From that surface, users can reply to and resolve discussions, approve or unapprove, merge, update labels and assignees, and set individual reviewers. Project-member search powers reviewer selection and uses GitLab numeric user IDs, not display names.
- Review feedback can be added to the active task session's prompt context. Adding context does not mutate the MR.
- Users can subscribe or unsubscribe the authenticated GitLab user from an individual issue or MR. This controls GitLab notifications upstream and does not create a Kandev automation watch.
- Users can configure workspace-scoped Kandev review watches for MRs requesting their review and issue watches for matching issues. A watch selects a workflow step, agent profile, executor profile, prompt, optional project and query filters, cleanup policy, enabled state, and poll interval.
- Watch settings provide create, edit, enable/pause, run-now, reset, and delete controls. Delete removes the watch and its dedup rows and best-effort deletes every task the watch created; it does not rerun the watch. Reset first previews the number of owned tasks, then after confirmation best-effort attempts all owned task deletions (including archived tasks), clears dedup rows, retains the watch definition, and makes current matches eligible again. Review-watch reset schedules an immediate rerun; issue-watch reset is reconsidered on the next poll/run-now.
- A newly observed watch match creates at most one task for the watch and external item. The task is linked to its MR when the match is a merge request, and configured auto-start behavior uses the selected profiles.
- The task changes panel can create a GitLab merge request for a GitLab remote. It pushes the current branch, respects an explicitly selected target branch or otherwise uses the project default, supports draft MRs, returns the MR URL, and records the association against the originating task/repository.
- Stored workspace tokens are injected only into executions for that workspace
as
GITLAB_TOKEN. Host-aware clone and MR creation never silently fall back from a self-managed host togitlab.com. - Settings can copy a connection to another workspace. Copying overwrites the target connection after confirmation but never copies automation watches or task-to-MR associations.
- Each linked MR's topbar control shows an "Automation" group with two
switches —
Auto-fix CI and address commentsandAuto-merge when ready— above a collapsibleReview follow-upgroup holding the three lifecycle notification switches (Your review is requested,MR merged,MR closed without merging) introduced for MR lifecycle notifications. These switches currently use task-level settings, so changing one linked MR's control also affects the task's other linked MRs. Per-MR switch scoping, including per-MR round/attempt state and identity-awarePATCH/MCP updates, is tracked in the "Scope GitLab MR automation switches per MR" follow-up task. The auto-fix prompt override remains task-level. See "Automation (lifecycle, auto-fix, auto-merge)" below. Auto-fix CI and address commentssends or queues an agent prompt when a linked MR's pipeline has a new or changed failing job, or a new or changed unresolved discussion note, capped at 10 accepted rounds per task.Auto-merge when readymerges a linked MR only when it is open, not a draft, its pipeline succeeded, it has zero unresolved discussions, and GitLab's own merge-readiness verdict agrees.- For a single linked MR on desktop (fine-pointer), hovering the topbar button opens a preview popover with everything: header actions (open the MR detail panel, open in GitLab, unlink), a pass-rate bar and pipeline stage groups, an approval row, an unresolved-discussions row, the Automation controls, a compact merge action when the MR is fully ready, and "Link another merge request" — mirroring GitHub's PR hover popover. Clicking the button opens the MR detail panel directly (no intermediate dropdown), also mirroring GitHub's single-PR topbar button. A task with 2+ linked MRs, and touch/coarse-pointer surfaces regardless of MR count, keep the click-only dropdown (per-MR review/open/unlink rows, the Automation group, and "Link another merge request") with no hover popover.
- The Kanban card shows a merge-request badge (
IconGitMerge, coloured by state/pipeline/approval) next to the existing pull-request badge when the task has at least one linked MR. Multiple linked MRs collapse into one badge showing a count and the most attention-worthy open MR's colour.
Data model
gitlab_configs
One row per workspace:
| Field | Type | Constraint |
|---|---|---|
workspace_id |
string | PK, FK to workspaces.id, cascade delete |
host |
string | required normalized HTTP(S) origin, no trailing slash |
auth_method |
enum | pat, glab_cli, or environment |
username |
string | last authenticated username, empty before a successful probe |
last_ok |
bool | last completed health result |
last_error |
string | sanitized provider/transport error |
last_checked_at |
timestamp | nullable |
created_at |
timestamp | required |
updated_at |
timestamp | required |
The PAT is stored in the secret store under
gitlab:<workspace_id>:token; secret values never appear in config or status
responses. glab_cli and environment rows do not copy host-machine auth data
into the secret store.
Task and watch records
gitlab_task_mrsremains the durable task-to-MR association. Its unique key is(task_id, repository_id, project_path, mr_iid);task_idand a non-emptyrepository_idmust belong to the same workspace as the resolved connection. Associations survive restart and archive/unarchive, but hard task deletion removes them and their refresh watches. Decision: ADR-2026-08-13-hard-delete-task-contribution-links.gitlab_review_watchesandgitlab_issue_watchesare workspace-owned durable automation definitions. Their workflow, workflow step, repository (when present), agent profile, and executor profile must belong to that workspace.gitlab_review_mr_tasksandgitlab_issue_watch_tasksare reservation/dedup records. Reservation occurs before task creation; a failed dispatch releases the reservation, while a successful dispatch attaches the created task ID.gitlab_mr_watchesremains the linked-MR refresh record for an active task session. It is not a replacement for a review watch. Its unique key is(session_id, repository_id, branch)— branch-scoped so a session with multiple worktrees on the same repository (multi-branch tasks) can hold one watch per branch instead of colliding on the second branch's insert. It is populated by both explicit URL linking and by push-detection/on-demand auto-link, and its periodic poll refreshes the matchinggitlab_task_mrsrow (state, pipeline, approval, merge status) in addition to notifying on notable transitions.- GitLab notification subscription state is owned by GitLab. Kandev reads it live and does not duplicate it in SQLite.
gitlab_task_mr_optionsis a per-task row:task_id(PK), the three lifecycle booleans (prompt_on_review_requested,prompt_on_merged,prompt_on_closed),review_reviewer_username,auto_fix_enabled,auto_merge_enabled,auto_fix_prompt_override(nullable; empty/NULLmeans use the built-inmr-auto-fixprompt), and timestamps. One row covers every MR linked to the task.gitlab_task_mr_stateis a per-(task_id, repository_id, project_path, mr_iid)row carrying lifecycle dedupe fields (from MR lifecycle notifications) pluslast_fix_signature,last_fix_checkpoint_json,last_fix_enqueued_at,last_fix_session_id,auto_fix_round_count,auto_fix_exhausted_at,last_merge_signature, andlast_merge_attempt_at.gitlab_task_mrsadditionally persistsdetailed_merge_status,reviewer_count, andunapproved_reviewersfrom every lifecycle sync, andunresolved_discussionsfrom the automation evaluation pass only (kept out of the lifecycle sync'sUPDATEso a plain poll never resets it to zero for an MR that is not automation-subscribed).custom_promptsincludes a built-inmr-auto-fixrow (id = "builtin-mr-auto-fix"), seeded the same way as GitHub'sci-auto-fix.
API surface
All routes below are under /api/v1/gitlab. workspace_id is required unless
the workspace is unambiguously derived from the referenced task and is still
validated against any supplied value.
Connection
GET /config?workspace_id=<id>returns host, auth method, username, health, andhas_secret; returns204when unconfigured.PUT /config?workspace_id=<id>accepts{host, auth_method, token?}and returns the saved config after validation.DELETE /config?workspace_id=<id>deletes the config and workspace secret; watch definitions remain persisted with their enabled flags unchanged but cannot poll or dispatch until the workspace is configured again.POST /config/test?workspace_id=<id>tests{host, auth_method, token?}without persisting it.POST /config/copy?workspace_id=<source>accepts{targetWorkspaceId}and copies connection settings and a stored PAT only.- Existing status, project, search, feedback, watch, preset, and write routes gain the same required workspace scope.
Task-to-MR association
POST /task-mrs?workspace_id=<id>accepts{task_id, repository_id?, mr_url}. It parses and validates the URL host, fetches the MR through the workspace client, and idempotently returns aTaskMR.DELETE /task-mrs/:association_id?workspace_id=<id>removes only that association and its refresh watch.GET /workspaces/:workspace_id/task-mrsandGET /tasks/:task_id/mrsreturn associations visible to the requested workspace/task.
Browse and review
GET /projects/branches?workspace_id=<id>&project=<path>uses the workspace connection when configured. When the workspace is unconfigured and the requested host isgitlab.com, it may list branches anonymously for the explicitly named public project so Remote task creation can continue.GET /user/mrs?workspace_id=<id>&filter=<filter>&page=<n>&per_page=<n>andGET /user/issues?workspace_id=<id>&filter=<filter>&page=<n>&per_page=<n>return the active workspace's paginated search results.GET /mrs/feedback?workspace_id=<id>&project=<path>&iid=<n>returns MR, approvals, discussions, and pipeline rollup.GET /mrs/files?workspace_id=<id>&project=<path>&iid=<n>andGET /mrs/commits?workspace_id=<id>&project=<path>&iid=<n>return changed files and commits for the same workspace/project/IID identity.POST /mrs/discussions/notes?workspace_id=<id>accepts{project, iid, discussion_id, body};POST /mrs/discussions/resolveaccepts{project, iid, discussion_id}.POST /mrs/approve?workspace_id=<id>andPOST /mrs/unapprove?workspace_id=<id>accept{project, iid}.PUT /mrs/merge?workspace_id=<id>accepts{project, iid, squash, squash_commit_message?}.PUT /mrs/labels?workspace_id=<id>accepts{project, iid, labels}andPUT /mrs/assignees?workspace_id=<id>accepts{project, iid, assignee_ids}.
MR automation (lifecycle, auto-fix, auto-merge)
GET /tasks/:taskID/mr-automationreturns the task'sTaskMRAutomationOptions: the three lifecycle booleans,review_reviewer_username,auto_fix_enabled,auto_merge_enabled,auto_fix_prompt_override(nullwhen unset),auto_fix_max_rounds(10),effective_auto_fix_prompt,using_default_prompt,updated_at, andmr_states(oneTaskMRLifecycleStateper linked MR, carrying both the lifecycle dedupe fields and the auto-fix/auto-merge checkpoint fields).PATCH /tasks/:taskID/mr-automationaccepts a partial body with any of the same fields (excludingauto_fix_max_rounds,effective_auto_fix_prompt,using_default_prompt,updated_at, andmr_states, which are server-computed). An unknown field returns400 unknown MR automation field;auto_fix_enabled/auto_merge_enabled/the three lifecycle booleans reject an explicitnull(they are switches, not clearable values);auto_fix_prompt_override: nullor""restores the built-inmr-auto-fixprompt.- Current-task MCP exposes
get_task_mr_automation_kandevandupdate_task_mr_automation_kandevwith the same shape, scoped to the connected task. - Auto-fix readiness requires the MR to be open, not draft, and its latest
pipeline settled (not
running/pending); it fires on a new or changed failing job, or a new/changed unresolved discussion note versus the stored checkpoint, and stops entirely once the MR ismerged,closed, orlocked. - Auto-merge readiness is GitLab's
detailed_merge_status == "mergeable"(15.6+), falling back tomerge_status == "can_be_merged"on older hosts, plus Kandev's own gates: open, not draft, pipelinesuccess, and zero unresolved discussions. An auto-fix dispatch in the same evaluation pass blocks that pass's auto-merge attempt. - Merge attempts route through the workspace client resolved with the linked
MR row's own stored
Hostas the expected host; a host mismatch fails closed (ErrWorkspaceHostMismatch) rather than merging through a differently-configured connection.
Automation watches
Review and issue watches use the same route shape under /watches/review and
/watches/issue:
GET /watches/<kind>?workspace_id=<id>lists the workspace's watches.POST /watches/<kind>?workspace_id=<id>creates a watch; the authoritative workspace comes from the query, not a body-supplied workspace ID.PATCH /watches/<kind>/:id?workspace_id=<id>partially updates a watch;DELETEapplies the delete semantics above.POST /watches/<kind>/:id/trigger?workspace_id=<id>runs an enabled watch.GET /watches/<kind>/:id/reset/preview?workspace_id=<id>returns{taskCount};POSTto the same path without/previewexecutes reset and returns{tasksDeleted}.
Reviewers and notifications
GET /projects/members?workspace_id=<id>&project=<path>&query=<text>returns matching active project members as{id, username, name, avatar_url}.PUT /mrs/reviewers?workspace_id=<id>accepts{project, iid, reviewer_ids}and replaces the MR reviewer list.GET /mrs/subscription?workspace_id=<id>&project=<path>&iid=<n>andGET /issues/subscription?workspace_id=<id>&project=<path>&iid=<n>return{subscribed: boolean}.PUT /mrs/subscription?workspace_id=<id>andPUT /issues/subscription?workspace_id=<id>accept{project, iid, subscribed}and subscribe or unsubscribe upstream.
Merge request creation
The existing worktree.create_pr WebSocket operation remains provider-neutral.
For a GitLab remote its successful response is
{success: true, pr_url: <merge-request-url>, provider: "gitlab"}; the product
labels the operation and result as "merge request" while preserving the
protocol action name for compatibility.
Errors
400indicates malformed URLs, hosts, filters, IDs, or request bodies.404indicates an absent resource or a resource outside the requested workspace; cross-workspace lookups do not reveal that a resource exists.409indicates a workspace/resource invariant conflict that cannot be applied idempotently.422indicates GitLab rejected a valid write, such as an ineligible reviewer.503indicates the workspace connection is absent or currently unavailable, except for anonymous branch discovery of an explicitly named publicgitlab.comproject.- Provider error bodies and logs are sanitized and never include tokens or authenticated remote URLs.
State machine
Connection health
unconfigured -> checking: a config is saved or tested.checking -> connected: GitLab authenticates and returns the current user.checking -> auth_required: GitLab returns an authentication/authorization failure.checking -> unavailable: transport, timeout, or GitLab 5xx failure.connected|auth_required|unavailable -> checking: the health poller runs or the user explicitly tests/reconnects.- Any state
-> unconfigured: the workspace config is deleted.
Automation watch
enabled: scheduled polls and run-now can dispatch matches.paused: no scheduled or manual dispatch; configuration and dedup rows stay.resetting: the confirmed reset attempts every owned task deletion, clears owned dedup rows, and retains the watch. Review watches rerun immediately; issue watches rerun on their next poll or run-now.error: an invalid/deleted bound profile or repository disables the watch and records a sanitized error; editing and re-enabling returns it toenabled.
Permissions
- GitLab configuration, watch, link, reviewer, subscription, and MR actions use the same workspace/task authorization boundary as their containing Kandev routes. A caller must have write access to the referenced workspace/task.
- Browse, feedback, member, and subscription reads require read access to the workspace and use only that workspace's GitLab connection.
- The configured GitLab identity must itself have upstream permission. Kandev does not elevate GitLab privileges or bypass protected-branch, approval, or reviewer eligibility rules.
- PAT mode requires GitLab
apiscope for the complete feature. Insufficient scope surfaces asauth_requiredor an action-specific error without deleting the saved config.
Failure modes
- Saving an invalid host or credential fails without replacing the last known working connection or secret.
- A transient health or API failure keeps the saved config and linked/watch records. The UI shows an unavailable state and allows retry; pollers back off and do not create tasks from incomplete results.
- Revoked credentials mark only the affected workspace
auth_requiredand pause API work for it until a successful probe. Other workspaces continue. - Watch dispatch reserves the external item before creating a task. Task-create or auto-start failure releases or completes the reservation according to the shared watcher dispatcher, preventing both lost matches and duplicate tasks.
- A deleted/soft-deleted watch dependency self-disables the watch with a visible error instead of creating an orphan task.
- Link creation is atomic from the user's perspective: if URL validation or MR fetch fails, no association is written. Repeating a successful request returns the existing association.
- Unlink failure leaves the association and refresh watch intact. Unlink never closes or unsubscribes from the upstream MR.
- Watch delete is best-effort for owned-task cleanup: individual task-delete failures are logged, remaining owned tasks are attempted, and the watch/dedup rows are removed. It never schedules another poll.
- Watch reset follows the shared GitHub
watchresetcontract: after preview and confirmation it best-effort attempts every owned task deletion (including archived), logs and continues past individual task-delete failures, then transactionally clears dedup andlast_polled_at. A clear failure surfaces together with the count already deleted; otherwise the response reports the successful delete count. Review watches rerun immediately and issue watches are eligible on their next poll/run-now. - Reviewer, discussion, merge, and subscription failures leave the last fetched UI state visible and show an action error; the UI refreshes after success.
- MR creation never retries against another host. A successful push followed by a failed MR request is reported as partial failure with the pushed branch intact so the user can retry without another commit.
Persistence guarantees
- Workspace config rows, PAT secrets, watch definitions, dedup reservations, task-to-MR associations, and last known MR status survive backend restarts.
- Archived tasks retain task-to-MR associations for later unarchive; hard task deletion removes task-owned associations and refresh watches.
- The startup migration moves the legacy global host/token to the active workspace, or the earliest-created workspace when no active workspace is available. It is idempotent and never duplicates automation watches.
glablogin andGITLAB_TOKENremain host-process state and are not copied or backed up by Kandev.- In-flight HTTP requests and poll iterations do not resume after restart. The next health/poll cycle re-runs safely against durable dedup state.
- GitLab notification subscriptions survive because GitLab owns them; Kandev re-reads their state after reload.
Scenarios
- GIVEN two workspaces connected to different GitLab hosts, WHEN each opens its GitLab page, THEN each sees only data fetched with its own host and credential.
- GIVEN a workspace without a GitLab connection, WHEN the user enters a
public
gitlab.comrepository URL in Remote task creation, THEN Kandev lists its branches anonymously without making any other GitLab browse or write capability available. - GIVEN a legacy global GitLab host and token, WHEN Kandev starts after upgrade, THEN one deterministic workspace receives the config and secret and other workspaces remain unconfigured.
- GIVEN a self-managed workspace connection, WHEN a user links a valid MR URL from that host, THEN the task shows the linked MR and its live review details after reload.
- GIVEN a task with no linked MR in a GitLab-configured workspace, WHEN
the task detail opens, THEN the top bar has no
Link MRaction and the task's contextualLinksubmenu offersGitLab Merge Request. - GIVEN a touch viewport and a task with no linked MR in a GitLab-configured
workspace, WHEN the user opens the task row's visible actions menu and
chooses
LinkthenGitLab Merge Request, THEN the GitLab MR link dialog opens without relying on right-click or long press. - GIVEN a task with a linked GitLab MR, WHEN the task detail opens, THEN the top bar shows the linked MR status control rather than a generic link action.
- GIVEN an MR URL from a different host, WHEN it is linked in the current workspace, THEN the request is rejected and no association is written.
- GIVEN a linked MR, WHEN the user unlinks it, THEN it disappears from the task and GitLab list indicator while the upstream MR is unchanged.
- GIVEN a GitLab MR or issue search row, WHEN the user launches a preset, THEN the task-create dialog is prefilled with the matching project and context and successful creation navigates to the task.
- GIVEN a linked MR with discussions, approvals, conflicts, and a pipeline, WHEN the user opens its task review panel, THEN all states and thread actions are available without leaving Kandev.
- GIVEN an eligible project member, WHEN the user selects them as a reviewer, THEN GitLab and the refreshed MR panel both show that reviewer.
- GIVEN a linked MR with auto-fix enabled, an open state, a settled
failing pipeline, and a promptable session, WHEN automation evaluates
the MR, THEN it sends or queues exactly one
@mr-auto-fixprompt andauto_fix_round_countincrements by one; an unchanged snapshot on the next evaluation dispatches nothing. - GIVEN a linked MR with auto-merge enabled and every readiness gate satisfied, WHEN automation evaluates the MR, THEN Kandev merges it exactly once, routed through the workspace client resolved with the linked row's own host; a host mismatch never merges.
- GIVEN a linked MR reaches
merged,closed, orlocked, WHEN auto-fix is still enabled, THEN no further auto-fix prompt is dispatched for that MR. - GIVEN a task with exactly one linked MR on desktop, WHEN the user hovers the topbar button past the open delay, THEN a preview popover shows the pass-rate bar, approval row, unresolved-discussions row, Automation controls, a merge action when ready, and link/open/unlink actions, all without a click.
- GIVEN a task with exactly one linked MR on desktop, WHEN the user clicks the topbar button, THEN the MR detail panel opens directly (no dropdown) and any open hover popover closes.
- GIVEN a task with two or more linked MRs, or any touch/coarse-pointer viewport regardless of MR count, WHEN the user interacts with the topbar button, THEN no hover popover is ever rendered and clicking/tapping opens the existing per-MR dropdown.
- GIVEN a task with two linked MRs, one merged and one open with a failing pipeline, WHEN the Kanban card renders its MR badge, THEN the badge shows a count of two and the open MR's (red) colour, not the merged MR's.
- GIVEN an MR or issue the user does not subscribe to, WHEN they enable notifications, THEN GitLab reports it subscribed and no Kandev watch or task is created.
- GIVEN an enabled review watch, WHEN a new matching MR requests the user as reviewer, THEN one linked task is created in the configured step and auto-starts with the configured profiles when requested.
- GIVEN an enabled issue watch, WHEN a matching issue appears, THEN one task is created with the issue URL and interpolated issue context.
- GIVEN a paused watch, WHEN scheduled polling or run-now occurs, THEN no task is created and existing dedup state is retained.
- GIVEN a watch with owned tasks, WHEN the user previews and confirms reset, THEN all shown tasks are attempted, the response reports successful deletions, dedup state is cleared, the watch remains enabled, and currently matching items can create new tasks immediately (review) or next poll (issue).
- GIVEN a watch with owned tasks, WHEN the user deletes the watch, THEN Kandev best-effort deletes those tasks and removes the watch/dedup state without recreating tasks from current matches.
- GIVEN a watch whose bound profile or repository was deleted, WHEN a match is dispatched, THEN the watch disables with a visible error and no orphan task is created.
- GIVEN a GitLab task branch with committed changes, WHEN the user creates a draft merge request, THEN the branch is pushed to the configured host, the returned MR URL is linked to that task repository, and the UI uses merge-request terminology on desktop and mobile.
- GIVEN a revoked token in Workspace A, WHEN health polling runs,
THEN Workspace A shows
auth_required, its watches stop dispatching, and Workspace B continues using its own connection.
Out of scope
- GitLab webhook ingestion; this iteration uses polling.
- Durable GitLab issue-to-task linking, issue state synchronization, or Jira/Linear-style structured issue import.
- Editing GitLab approval rules, protected branches, CI configuration, pipeline jobs/logs, or merge request templates.
- Group-wide dashboards beyond results visible to the configured user.
- OAuth-based Kandev sign-in, GitLab Duo, repository migration between hosts, and Bitbucket parity.
- Multiple named GitLab connections inside one Kandev workspace.
- A multi-MR aggregate hover preview (the popover only covers the single-MR topbar case; a task with 2+ linked MRs keeps the existing click-only dropdown).
- GitLab's per-reviewer
requested_changesstates as a three-way review label; "awaiting review" is approvals-count-based only (version/tier dependent otherwise). - Per-job CI log tailing in the auto-fix prompt.