From 6d9421308ea189f5dab2eefd77b720277042c773 Mon Sep 17 00:00:00 2001 From: kjgbot Date: Wed, 16 Sep 2026 07:14:37 -0700 Subject: [PATCH] fix(flows): ask Shortcut users for their Team, not a Project MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Shortcut onboarding field asked for a project NAME ("Web app"), and the Cloud launcher compared it against the story's numeric `project_id` — a test that cannot ever be true, so every story was silently dropped. Shortcut scopes work by Group, branded "Team" in its UI. Projects are legacy: `/api/v3/projects` returns an empty list in a modern workspace and a story's `project_id` is null there, so no spelling of a project filter could match one. A story names its team by `group_id`, which Cloud now resolves to a team name (or matches against the group id directly). So ask for the thing a story actually carries. `ISSUE_OPTIONAL_FIELDS` already declares `team`, so the generated Issue type and filter need no other change. Pairs the generated-filter test's positive with a same-workspace, different-team negative, so a change that widened the filter into "always match" cannot pass. REQUIRES AgentWorkforce/cloud#3690 TO MERGE FIRST. That PR is what teaches the launcher to accept a `team` setting and to stop mis-comparing `project`; until it ships, a flow deployed from this field would send a setting Cloud ignores. Co-Authored-By: Claude Opus 5 (1M context) --- web/lib/flow-sources.ts | 8 +++++++- web/lib/test/flow-sources.test.ts | 10 +++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/web/lib/flow-sources.ts b/web/lib/flow-sources.ts index b3eed86..ffa4daa 100644 --- a/web/lib/flow-sources.ts +++ b/web/lib/flow-sources.ts @@ -10,7 +10,13 @@ export const ISSUE_SOURCES = [ ] }, { id: 'shortcut', label: 'Shortcut', fields: [ { key: 'workspace', label: 'Workspace', placeholder: 'Your workspace' }, - { key: 'project', label: 'Project', placeholder: 'Web app' }, + // Shortcut scopes work by Group, branded "Team" in its UI. Projects are + // legacy: `/api/v3/projects` is empty in a modern workspace and a story's + // `project_id` is null there, so a project filter could never match one — + // and the launcher compared the name typed here against a numeric id, which + // silently dropped every story. Cloud now matches this against the team + // name (or its group id), so ask for the thing a story actually carries. + { key: 'team', label: 'Team (group)', placeholder: 'Platform' }, { key: 'labels', label: 'Required labels', placeholder: 'ready-for-agent' }, ] }, { id: 'jira', label: 'Jira', fields: [ diff --git a/web/lib/test/flow-sources.test.ts b/web/lib/test/flow-sources.test.ts index f5fb913..8cc74e3 100644 --- a/web/lib/test/flow-sources.test.ts +++ b/web/lib/test/flow-sources.test.ts @@ -27,13 +27,17 @@ describe('generated issue source filters', () => { it('combines source-specific filters and allows any selected source', () => { const matches = matcher(['linear', 'shortcut', 'jira'], { linear: { team: 'Engineering', project: 'Web', labels: 'ready' }, - shortcut: { workspace: 'acme', project: 'App', labels: 'bug, ready' }, + shortcut: { workspace: 'acme', team: 'Platform', labels: 'bug, ready' }, jira: { project: 'ENG', labels: 'ready' }, }); expect(matches({ ...issue, team: 'Engineering', project: 'Web' })).toBe(true); expect(matches({ ...issue, team: 'Design', project: 'Web' })).toBe(false); - expect(matches({ ...issue, source: 'shortcut', workspace: 'acme', project: 'App', labels: ['ready', 'bug'] })).toBe(true); - expect(matches({ ...issue, source: 'shortcut', workspace: 'other', project: 'App', labels: ['ready', 'bug'] })).toBe(false); + expect(matches({ ...issue, source: 'shortcut', workspace: 'acme', team: 'Platform', labels: ['ready', 'bug'] })).toBe(true); + expect(matches({ ...issue, source: 'shortcut', workspace: 'other', team: 'Platform', labels: ['ready', 'bug'] })).toBe(false); + // Paired negative on the field itself: same workspace, different team. A + // Shortcut story names its team by `group_id`, never by a project name, so + // this is the scoping that has to hold — and has to still reject. + expect(matches({ ...issue, source: 'shortcut', workspace: 'acme', team: 'Growth', labels: ['ready', 'bug'] })).toBe(false); expect(matches({ ...issue, source: 'jira', project: 'ENG' })).toBe(true); expect(matches({ ...issue, source: 'github' })).toBe(false); });