Route Duo in Slack to the slack_assistant flow behind a flag

What does this merge request do and why?

Warning

This flow is not billed yet. Credits key on the flow reference, and CustomersDot has no entry for slack_assistant/v1. Unmapped keys do not error — they fall through to success — so usage is metered under a key nobody counts.

Decided: register slack_assistant/v1 in CustomersDot mapped to the Duo Developer credit pool, tracked in #628678. Untangling the billing key from the flow reference (#599841) needs cross-team agreement with Fulfillment and is deliberately deferred; a Rails-only attempt was dropped (!254800 (closed)) because it covered only the pre-flight check, leaving a session checked under one key and metered under another.

No code change here when that lands — keying on the flow's own reference is already what the mapping expects. Acceptable while the flag is on for our own group only; #628678 must be live before enabling anywhere else. Blocker on the rollout issue (#628672).

Routes Duo in Slack to the API-only slack_assistant flow when slack_duo_api_flow is enabled for the user's default Duo namespace.

Stacked on !254799 (merged), which registers the flow. Review that first.

Today every Slack mention runs Duo Developer, so a CI job starts and clones a repository before the agent can answer. Most Slack requests are questions, searches, or creating an issue, and none of that needs a repository.

The flagged path also sends no preamble. The flow's own system prompt carries the identity, the Slack formatting rules and what happens to the final message; the <conversation> and <channel_context> blocks describe themselves. The split we settled on is: the client sends data, the flow sends instruction. A prompt that describes the client's goal format dates the moment the client changes it, and breaks when an older instance renders the goal differently.

SLACK_CONTEXT_PREAMBLE is untouched for the unflagged path, because Duo Developer's prompt knows nothing about Slack. So with the flag off, both the flow and the goal are byte-identical to today. That is the kill switch.

The resolved reference is threaded through goal assembly rather than memoized, so the preamble cannot disagree with the flow it was built for. (An ivar would also trip Gitlab/ModuleWithInstanceVariables in an EE module.)

Related to #628428 (closed)

How to set up and validate locally

Needs the GitLab for Slack app wired to your GDK.

  1. Check out this branch and restart Rails.
  2. Run the Duo Workflow Service with the slack_assistant flow available, and point GITLAB_DUO_WORKFLOW_SERVICE_URL at it. The flow config lives in the AI gateway: use main if ai-assist!6867 has merged, otherwise check out that branch.
  3. Enable the flag, seed the entry and enable the flow for your group — steps 2–4 of !254799 (merged).
  4. Mention the bot in a Slack thread.
  5. Session list — the session runs slack_assistant/v1, not developer/v1.
  6. Session detail — the goal starts at <channel_context> or <conversation>, with no preamble prose above it.
  7. Tools and reply — the agent reads GitLab through gitlab_api_get / gitlab_graphql, and its final message appears in the Slack thread.
  8. CI job log — the "Getting source from Git repository" section still appears, but inside it you should see Skipping Git repository setup, Skipping Git checkout and Skipping Git submodules setup, and DUO_WORKFLOW_DEFINITION=slack_assistant/v1. That is coding_environment: none working. Worth checking rather than assuming: CodingEnvironment.resolve falls back to :full on anything unrecognised, so a mistake looks like a normal run.
  9. Disable the flag and mention again: Duo Developer runs, with today's preamble intact.

Verified locally

Run against GDK with the real Slack app. Drop the screenshots into the slots below.

Session list — the right flow ran pi-clipboard-8318881f-8b7c-4a6e-a54f-978362ed2078

Session detail — no preamble, the goal is data only pi-clipboard-a35233a9-8a67-4049-bd01-6eaef2a86b78

The agent used its API tools and answered in the thread pi-clipboard-a37e5dfe-ef9a-488c-b20e-dae8b8516608

CI job log — the clone is skipped pi-clipboard-93cd4f63-b193-4162-a035-defc75b4703f

Specs

  • Flag on: the API reference reaches the resolver and the TriggerBundle, and the goal carries no preamble.
  • Flag off: every other example in app_mentioned_service_spec.rb runs with the flag stubbed off, so they are the guarantee that today's path is unchanged. Specs enable all flags by default, so that stub had to be made explicit.
  • assemble_goal takes the preamble as its first argument now, so the fail-closed overflow guard's wrapper was updated to match.

51 examples, 0 failures.


Stack — merge in this order:

  1. !254799 (merged) — registry entry and feature flag.
  2. !254801 (merged) — routing. The only MR that changes behaviour.

Billing is deliberately out of scope; see the warning above. !254800 (closed) is closed pending that decision.

Edited by Thomas Schmidt

Merge request reports

Loading
Loading