Triggers
A trigger starts a workflow or a smoke test when something happens in a workspace, instead of when you press Run or a schedule's time comes: an agent says its work is done, an agent finishes a turn or exits, a smoke test or a workflow run ends, or something happens on the workspace's pull request — it opens, a review or a comment comes in, its checks fail. What it starts is an ordinary run, in that workspace, with the inputs you gave the trigger.
For example: when someone reviews the pull request of fix-login, tell its agent to address the
review. Or: when the agent says it is done, have a second agent review the branch.
This release has triggers from the command line. ys trigger adds, lists and changes them,
and Yardsort listens and starts their runs while it is open. The runs show in the workflow's runs
and in Tests like any other. The window does not list triggers yet. See
What it does not do yet.
Three things to know before you make one:
- Triggers fire while Yardsort is open. What happened while it was closed — an agent that ran
ys workspace done, a run that ended, a turn its agent reported, a review posted — is found when it next opens, and acted on once. - Only what happens after you make it counts. A trigger never fires for something that happened before it was saved, or while it was switched off.
- A trigger approves nothing. A smoke test file nobody has approved waits for you, as it does when an agent runs it. See Smoke tests and approval.
Ready-made triggers
Five triggers come ready made, each an event and a built-in workflow, with at most one question:
| Preset | When | Runs | Asks for |
|---|---|---|---|
address-reviews | Someone other than you reviews or comments on the pull request, 2 min quiet | address-review | |
fix-checks | The checks on the pull request's newest commit fail | fix-checks | |
review-when-opened | The pull request opens, or a draft is marked ready | code-review | reviewer: who reviews |
review-when-done | The agent says its work is done | review-branch | reviewer: who reviews |
smoke-when-done | The agent says its work is done | smoke-then-tell | suite: which smoke test |
ys trigger presets
ys trigger add --preset address-reviews --workspace fix-login
ys trigger add --preset review-when-done --input reviewer=codex --workspace fix-login
A trigger made from one is an ordinary trigger: --quiet, --cap, --name and --notify
change it as they would any other.
Making a trigger
ys trigger add --on pr.review --on pr.comment --workflow address-review --workspace fix-login
ys trigger add --on workspace.done --workflow smoke-then-tell --input suite=login --workspace fix-login
ys trigger add --on agent.exited --failed-exit --workflow tell-me --workspace fix-login
ys trigger add --on test.finished --of-test login --verdict failed --workflow fix-it
--on says what to listen for; give it more than once for more than one event. --workflow or
--test says what to run, and --input id=value gives each of its inputs. --workspace takes a
workspace's name or id, and can be left out inside a workspace's folder or in an agent's terminal
there. Everything a run would be refused for is checked before anything is written: the workspace
is there and not archived, the workflow or the test is there and ready, its inputs fit, and the
filters are ones the events have.
| Option | What it is |
|---|---|
--preset | A ready-made trigger, in place of --on and what to run |
--on | An event, below. More than once for more than one |
--workflow | Run this workflow, or |
--test | run this smoke test, from the workspace's .yardsort/tests |
--workspace | Where. Default: the workspace this is run in |
--session | Only this agent session's events, by its id or the start of it. See One agent's trigger |
--input | id=value, for each of the target's inputs |
--name | What lists call it. Default: the workflow's or the test's own name |
--quiet | Wait this long after the last event before starting the run: 30s, 2m. Default: 2m for reviews and comments, else none |
--cap | Runs it may start for one agent session, one pull request, or the workspace, before it stops. Default: 3 |
--notify | failure (the default), always or never: when to be told how a run ended |
What it listens for
| Event | Happens when | Filters |
|---|---|---|
workspace.done | An agent ran ys workspace done | |
agent.settled | An agent finished a turn: it went quiet after real work, or its harness said so | --new-commits, --clean |
agent.exited | An agent's program ended | --failed-exit |
test.finished | A smoke test run in the workspace reached a verdict, or could not | --of-test <id>, --verdict passed or failed |
workflow.finished | A workflow run in the workspace ended | --of-workflow <id>, --outcome succeeded or failed |
pr.opened | The workspace's branch has an open pull request it did not have before | --drafts |
pr.review | A review was submitted on the workspace's pull request | --from, --state, --no-bots |
pr.comment | A comment on its conversation, or a reply on a review thread | --from, --no-bots |
pr.checks_failed | Its checks finished on its newest commit, and one failed | |
pr.checks_passed | Its checks finished on its newest commit, and none failed | |
pr.conflicted | GitHub says it conflicts with its base | |
pr.merged | It was merged | |
pr.closed | It was closed without being merged |
- An agent's events are about the agents you started in the workspace. A shell is not an agent, and neither is an agent a workflow run started: the reviewer of a code review finishing its turn sets nothing off. An agent's events, the pull request's and the workspace's cannot share one trigger, since each counts its runs for something different; make two.
agent.settledfires at every turn, including a turn where the agent stops to ask you something. That is what "tell me each time it stops" wants, and not what "it is finished" means.--new-commitswaits for the branch to have commits it did not have the last time this trigger started a run for that agent, or, the first time, commits its base branch does not have.--cleanwaits for nothing to be uncommitted. Both are looked at when the run would start, after the quiet time. For "the work is done",workspace.doneis the surer one.--failed-exitis a code that is not 0, a program that was stopped, or one that ended without Yardsort hearing how.--verdict failedincludes a run that could not finish;--outcome failedincludes a cancelled run.- The pull request's events are about the open pull request for the branch the workspace has
checked out, on GitHub, as
ghsees it. See Watching a pull request.
Watching a pull request
While an enabled trigger in a workspace listens for a pr.* event, Yardsort looks at that
workspace's pull request once a minute, with gh, and starts what the trigger starts when
something changed since the last look. Nothing is asked of GitHub for a workspace no trigger
watches.
- What is there when you add the trigger is not news. Adding it reads the pull request as it
is: a pull request already open is not
pr.opened, checks already red are notpr.checks_failed, and a review already posted is notpr.review.ys trigger addsays which pull request it is watching, or that there is none yet. - Reviews and comments count when they are someone else's. You and every agent post to GitHub
as your
ghaccount, and the agent answering a review answers each comment as you. So by default only posts from other accounts count — a teammate, a review bot.--from anyonecounts yours too, except what is posted while a run this trigger started is going, which is the agent's own answers.--no-botsleaves out accounts whose name ends in[bot].--state(approved,changes_requested,commented) narrowspr.reviewto reviews that concluded that. - A reply on a review thread is a comment. GitHub also records each reply as a review of its
own; Yardsort counts it once, as
pr.comment. A review is the review's verdict and words with the comments on lines made in it. - Reviews and comments come in bursts, so a trigger on them waits two minutes after the last one by default and starts one run for all of them.
- Checks are told once they have all finished:
pr.checks_failedwhen nothing is running and something failed,pr.checks_passedwhen nothing is running and nothing failed. Every check on the commit is counted, however many there are, and one GitHub has marked stale counts as finished. Each is told once per commit, so a push that fails again is news again. - A draft is
pr.openedwhen it is marked ready for review.--draftscounts it as soon as it opens instead. pr.mergedandpr.closedare told once. A different pull request opened on the branch afterwards ispr.opened, and counts its own runs; the same one reopened is not new, and what was already on it is not news again. A workflow that needs the open pull request —{{ pr.* }},wait_pr_activity— cannot run on these, and saving says to use{{ event.url }},{{ event.number }}and{{ event.title }}instead.- GitHub not answering — no network,
ghlogged out — is no news; the trigger looks again a minute later.
The done signal
ys workspace done
ys workspace done --note "Login validates on blur; tests added."
ys workspace done, run in an agent's terminal, says the work in that workspace is committed and
ready to be looked at. A trigger on workspace.done starts what it starts. The note, if there is
one, is there for the workflow as {{ event.note }}, and the commit the worktree was on as
{{ event.head }}. Outside an agent's terminal, run it in the workspace's folder or give
--workspace. It prints whether a trigger there listens for it.
An agent is told about it in its first message — a short section after the project's memory
and the smoke tests — when a trigger in its workspace listens for workspace.done at the moment
it starts. It is asked to run it once its work is committed, and not to pause or to ask a
question. An agent that was already running when you added the trigger was not told; ys trigger add says so. Agents started from then on are.
Yardsort reads nothing an agent prints to know it is done: the agent runs a command, as it runs
git or gh.
When a run starts
A burst is one run. With --quiet 2m, the trigger waits until nothing new has happened for two
minutes, then starts one run for everything that came — but never later than ten minutes after
the first. For agent.settled, the agent going back to work before the quiet time is up means
that turn did not end, and nothing starts. Without --quiet, a run starts within a second.
A run in the way makes it wait. Only one run of a workflow, or of a test, goes at a time in a workspace, whoever started it. When the trigger's run would be the second, the trigger waits, and starts it when the first ends, with everything that came meanwhile. A schedule skips such a time, because another time will come; something that happened will not happen again.
It stops before it loops. Three limits, each told to you once:
- The cap. At most
--capruns — three by default — for one agent session, for an agent's events, for one pull request, for the pull request's, or for the workspace, for the rest. The next fire is capped, you are told, and the trigger starts nothing more for it until you runys trigger reset. - The rate. At most ten runs an hour for one trigger, whatever the cap.
- Chains. A run a trigger started can end in an event another trigger listens for. A run a person or a schedule started is depth 0; a run started by the ending of a run of depth n is n + 1. A fire past depth 3 is refused.
One agent's trigger
--session <id> makes a trigger that belongs to one agent session: it hears that agent's own
turns and exits, and its runs' session: origin is that agent, whichever agent in the workspace is
newest. A fire while that agent's program is not running is skipped: there is no one to tell.
The trigger goes when the session's record does. ys session list shows the ids.
No agent to tell
A workflow that tells the workspace's own agent something — address-review, fix-checks, the
last step of review-branch — has nothing to do when no agent is running there. Such a fire is
skipped, and you are told once, rather than starting a run that skips every step and calls
that a success. Start an agent in the workspace and the next event reaches it.
Smoke tests and approval
A smoke test file comes from a repository and says what to run on your computer, so it never runs until you have seen it. A trigger keeps to that: a run of a new or changed file waits on the card at the top of its workspace, the trigger reads waiting for approval, and you are told once (see Being told). Approve it and that run goes. Meanwhile the trigger's next events wait behind it.
Nothing else a person does is waited for:
- A test with a
manualstep cannot be triggered. Nobody is there to press Pass. This is checked when the trigger is saved and again each time it fires, for a test it runs itself and for one a workflow'srun_teststep names. - A workflow's
run_teststeps each need atimeout. A test named by an input is not known until the run, and an approval may not come; the timeout ends the wait.
What the workflow is told
A workflow a trigger starts can read what set it off as {{ event.* }}:
| Variable | What it is |
|---|---|
event.kind | The event: workspace.done, agent.settled, … |
event.count | How many events the run is for, when a burst was gathered into one |
event.note | workspace.done: what the agent passed to --note |
event.head | workspace.done, agent.settled: the commit the worktree was on. pr.checks_failed, pr.checks_passed, pr.conflicted: the commit it was about |
event.summary | agent.exited, test.finished, workflow.finished: how it ended, in a line. pr.review, pr.comment: 2 reviews and 3 comments from alice and coderabbitai[bot]. pr.checks_failed: 2 checks failed: lint, test (ubuntu) |
event.verdict | test.finished: passed, failed, errored, … |
event.url | The pr.* events: the pull request, or for a review or comment, the newest one |
event.number, event.title | The pr.* events: the pull request's number and title |
event.authors | pr.review, pr.comment: who posted, comma-separated |
A trigger whose workflow uses a variable one of its events does not leave is refused when it is
saved, naming it. Run by hand, every event.* is empty, and Run… and ys workflow run say
so.
Being told
A triggered run has nobody watching it, so the trigger can tell you how it ended, with a desktop notification: when a run fails (the default), how every run ends, or never. Unless it is set to never, a run waiting for your approval is told once, and so is a trigger that is capped, could not start its run, or had no agent to tell, once while it lasts.
Its history
ys trigger fires <trigger> lists every fire, newest first:
| Outcome | Meaning |
|---|---|
| running, succeeded, passed, failed | A run was started; this is how it stands, or how it ended |
| waiting for approval | Its run waits for you to approve the test file |
| waiting | A run of the same thing is going there; this one starts when it ends |
| skipped | Its agent session, or any agent in the workspace, was not running to be told |
| capped | It had started as many runs as --cap allows. Later events go on this line |
| refused | A run could not be started, with the reason |
From the command line
ys trigger presets # the ready-made triggers
ys trigger list # every trigger: what it listens for, what it runs, where, how it stands
ys trigger show Tell # its events, filters, inputs, limits and last fire
ys trigger pause Tell # off: keeps its history, fires nothing
ys trigger resume Tell # on again; only what happens from now counts
ys trigger reset Tell # lift its cap
ys trigger fires Tell # its history
ys trigger remove Tell # the trigger and its history; the runs it started stay
A trigger is named by its name, its id, or the start of its id. --json works as everywhere.
See ys trigger.
What it does not do yet
- Forges other than GitHub. The pull request events need
gh. - The window. Triggers are listed, made and changed with
ysfor now; the composer's ready-made triggers and a view of them come later. - Run while Yardsort is closed. What happened is found when it next opens.