Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions docs/CLOUD.md
Original file line number Diff line number Diff line change
Expand Up @@ -455,9 +455,12 @@ decides which form is meant.
`contains`, `events`, and the pull-request subscription opt-outs `reviews`,
`checks`, `comments`), `gitlab` (`project`, `labels`, `contains`, `events`),
`slack` (`channel`, `contains`), `linear` (`team`, `project`, `labels`,
`contains`), `jira` (`project`, `labels`, `contains`) or `shortcut`
`contains`, `events`), `jira` (`project`, `labels`, `contains`) or `shortcut`
(`workspace`, `team`, `labels`, `contains`), each at most once. A Linear
`team` matches the team's name or its key. A GitHub source without
`team` matches the team's name or its key. A Linear `events` is `issues` (the
default — `issue.create`), `assigned` — `AppUserNotification.issueAssignedToYou`,
issues assigned to the connected app user, so assigning a ticket delegates it —
or `all` for both. A GitHub source without
`repository` is
scoped to `--repo`. `events` is `issues` (the default: `issues.opened` and
`issues.labeled`) or `pull_request` — `merge_request` for `gitlab` — which wakes on a pull request being
Expand Down
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ flow straight from this repo, shows its steps, and asks you to connect whatever
| Flow | Trigger | Deploy |
|---|---|---|
| [pr-review-pipeline](pr-review-pipeline/) — three review lenses in parallel, then a consensus pass | every `pull_request` | [![Deploy Flow](https://agentrelay.com/launch-agent_small.svg)](https://agentrelay.com/cloud/flows/deploy?flow=https%3A%2F%2Fgithub.com%2FAgentWorkforce%2Fflows%2Fblob%2Fmain%2Fexamples%2Fpr-review-pipeline%2Fpr-review-pipeline.flow.ts&on=github%3Aevents%3Dpull_request) |
| [software-factory](software-factory/) — implement → test → adversarial review → open a PR | every Linear issue in team `ENG` | [![Deploy Flow](https://agentrelay.com/launch-agent_small.svg)](https://agentrelay.com/cloud/flows/deploy?flow=https%3A%2F%2Fgithub.com%2FAgentWorkforce%2Fflows%2Fblob%2Fmain%2Fexamples%2Fsoftware-factory%2Fsoftware-factory.flow.ts&on=linear%3Ateam%3DENG) |
| [software-factory](software-factory/) — implement → test → adversarial review → open a PR | Linear issues assigned to the connected app user | [![Deploy Flow](https://agentrelay.com/launch-agent_small.svg)](https://agentrelay.com/cloud/flows/deploy?flow=https%3A%2F%2Fgithub.com%2FAgentWorkforce%2Fflows%2Fblob%2Fmain%2Fexamples%2Fsoftware-factory%2Fsoftware-factory.flow.ts&on=linear%3Aevents%3Dassigned) |
| [stale-issues](stale-issues/) — triage every open issue, post a Slack digest | schedule (`flows schedule … --cron "0 9 * * 1-5"`) | — |

Or from a checkout: `flows deploy <flow.ts> --repo <owner/name> --on <provider[:k=v]> --approver <you>`.
Expand Down
19 changes: 11 additions & 8 deletions examples/software-factory/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# software-factory

[![Deploy Flow](https://agentrelay.com/launch-agent_small.svg)](https://agentrelay.com/cloud/flows/deploy?flow=https%3A%2F%2Fgithub.com%2FAgentWorkforce%2Fflows%2Fblob%2Fmain%2Fexamples%2Fsoftware-factory%2Fsoftware-factory.flow.ts&on=linear)
[![Deploy Flow](https://agentrelay.com/launch-agent_small.svg)](https://agentrelay.com/cloud/flows/deploy?flow=https%3A%2F%2Fgithub.com%2FAgentWorkforce%2Fflows%2Fblob%2Fmain%2Fexamples%2Fsoftware-factory%2Fsoftware-factory.flow.ts&on=linear%3Aevents%3Dassigned)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update the other Software Factory deploy badge

Users deploying from the top-level examples index instead of this README still follow examples/README.md:9, whose description and URL select the default issue-create events and hard-code team=ENG. Consequently, assigning a ticket will not wake deployments launched through that equally prominent badge; update that index entry to use linear:events=assigned and describe assignment-based delegation as well.

Useful? React with 👍 / 👎.


One click deploys this flow to [Agent Relay Cloud](https://agentrelay.com/cloud), running on new Linear issues — set your team on the deploy page.
One click deploys this flow to [Agent Relay Cloud](https://agentrelay.com/cloud), running when a Linear issue is assigned to the connected app user — set your team on the deploy page.

A ticket becomes a pull request: implementation agent → deterministic tests →
adversarial review agent → PR opened for a human. The review verdict is a file
Expand All @@ -12,16 +12,19 @@ neither can be talked into a green result.
```sh
flows check examples/software-factory/software-factory.flow.ts
flows deploy examples/software-factory/software-factory.flow.ts \
--repo acme/api --on linear:team=TECH --approver you
--repo acme/api --on linear:events=assigned,team=TECH --approver you
flows deployments
```

`--on` also takes `github:labels=agent`, `jira:project=OPS`, `shortcut:workspace=…`
or `slack:channel=#eng`, and `linear` accepts `team`, `project`, `labels` and
`contains` (`linear:team=TECH,labels=agent`); `team` matches the Linear team
name or its key, so `team=Engineering` and `team=TECH` are the same filter. The
deploy page shows every filter as an editable field — the badge only fixes the
provider; team, project and labels are filled in there.
or `slack:channel=#eng`, and `linear` accepts `team`, `project`, `labels`,
`contains` and `events` (`linear:team=TECH,labels=agent`); `team` matches the
Linear team name or its key, so `team=Engineering` and `team=TECH` are the same
filter. `events` is `issues` (the default — new issues), `assigned` (issues
assigned to the connected app user — the badge's choice, so assigning a ticket
delegates it), or `all` for both. The deploy page shows every filter as an
editable field — the badge only fixes the provider and `assigned`; team,
project and labels are filled in there.

Each matching ticket launches one Cloud run in a fresh
`relayflow/software-factory-<id>` branch of `--repo`; a passing review opens a
Expand Down
9 changes: 7 additions & 2 deletions packages/sdk/src/cloud-deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ const PROVIDER_SETTINGS: Record<FlowTriggerProvider, readonly string[]> = {
github: ['repository', 'labels', 'contains', 'events', 'reviews', 'checks', 'comments'],
gitlab: ['project', 'labels', 'contains', 'events'],
slack: ['channel', 'contains'],
linear: ['team', 'project', 'labels', 'contains'],
// `events`: `issues` (default), `assigned` (issues assigned to the agent's
// connected app user), or `all` — which Linear records wake the listener.
linear: ['team', 'project', 'labels', 'contains', 'events'],
jira: ['project', 'labels', 'contains'],
shortcut: ['workspace', 'team', 'labels', 'contains'],
};
Expand Down Expand Up @@ -137,7 +139,10 @@ export function parseTriggerSource(value: string): FlowTriggerSource {
if (key === 'events') {
// Cloud's enum is lowercase; send it that way whatever the shell typed.
const events = setting.toLowerCase();
const valid = provider === 'gitlab' ? ['issues', 'merge_request'] : ['issues', 'pull_request'];
const valid =
provider === 'gitlab' ? ['issues', 'merge_request']
: provider === 'linear' ? ['issues', 'assigned', 'all']
: ['issues', 'pull_request'];
if (!valid.includes(events)) {
throw new CloudFlowError('invalid_input',
`${provider} events must be ${valid.map(v => `"${v}"`).join(' or ')}, got "${setting}".`);
Expand Down
4 changes: 3 additions & 1 deletion packages/sdk/tests/cloud-deploy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ describe('trigger source and repository parsing', () => {
['slack:channel=#eng', { provider: 'slack', settings: { channel: '#eng' } }],
['linear:team=ENG', { provider: 'linear', settings: { team: 'ENG' } }],
['linear:team=Engineering,labels=agent,contains=urgent', { provider: 'linear', settings: { team: 'Engineering', labels: 'agent', contains: 'urgent' } }],
['linear:events=assigned', { provider: 'linear', settings: { events: 'assigned' } }],
['linear:events=ALL,team=TECH', { provider: 'linear', settings: { events: 'all', team: 'TECH' } }],
['jira:project=OPS,labels=agent', { provider: 'jira', settings: { project: 'OPS', labels: 'agent' } }],
['shortcut:workspace=acme,team=Platform', { provider: 'shortcut', settings: { workspace: 'acme', team: 'Platform' } }],
['gitlab:project=acme/platform/web', { provider: 'gitlab', settings: { project: 'acme/platform/web' } }],
Expand All @@ -75,7 +77,7 @@ describe('trigger source and repository parsing', () => {
expect(parseTriggerSource(value)).toEqual(expected);
});

it.each(['github:channel=x', 'github:labels=', 'github:labels=a,labels=b', 'slack:labels=x', 'github:events=releases', 'gitlab:events=pull_request', 'gitlab:repository=acme/web', 'github:reviews=maybe', 'linear:events=issues'])
it.each(['github:channel=x', 'github:labels=', 'github:labels=a,labels=b', 'slack:labels=x', 'github:events=releases', 'gitlab:events=pull_request', 'gitlab:repository=acme/web', 'github:reviews=maybe', 'linear:events=mentions', 'linear:events=pull_request'])
('refuses %s', (value) => {
expect(() => parseTriggerSource(value)).toThrow(expect.objectContaining({ code: 'invalid_input' }));
});
Expand Down
Loading