The Pull Request Is the Ticket
The delivery pipeline I’m building for a web team runs on one rule: every request, every agent and every test result lands on a single pull request. Agents prepare the work. People make the decisions. Deterministic pipelines do the deploying, and no agent is allowed to start one. Asana stays the front door for the people asking for work, and GitHub is where the work proves itself.
In May I wrote about why the old process had to go before any of it could be automated: eleven handoffs, seven approval gates, four QA phases. This is the machine that replaced it, with an honest mark on every part that isn’t running yet.
GitHub is the control plane because the audit trail is already there
GitHub wins because the record of what happened lives in the same place as the thing that happened. A pull request already carries the diff, the review, the CI result and the merge. GitHub Deployments add the release: which commit went to which environment, when, and whether it worked. Every agent I care about now runs inside that same system, so their work leaves the same trail.
Three GitHub pieces do the heavy lifting.
- Copilot’s cloud agent (until recently, the coding agent). You assign it an issue the way you’d assign a developer. It creates a branch, opens a pull request, works in a short-lived cloud environment where it can run the tests, and asks for review when it’s done.
- GitHub Agentic Workflows (
gh aw), in technical preview. You write a workflow as a markdown file: a short YAML header saying when it runs and what it may write, then plain-English instructions.gh aw compileturns that into a pinned, ordinary Actions workflow. The part that sold me is safe outputs. In GitHub’s words, “agents run read-only and request actions via structured output.” A separate job with its own permissions applies only the writes you declared, like “add one comment” or “create one issue.” The engine is a setting: Copilot, Claude Code, Codex or Gemini CLI. - Environments and Deployments. Any Actions job that names an
environment:creates a Deployment record, and an environment can require a named reviewer before the job runs. That gives you the release log and the approval gate without a separate tool.
Before this, the same work lived in three tools, each wrong in a different way. Now one system records the work, the agents and the releases, and Asana reads from it.
The flow, step by step
Read the diagram top to bottom. Each step names who owns it (a person, an agent or a deterministic pipeline) and how real it is today. Dashed boxes are planned.
Request to release
Every step lands on one pull request. Agents prepare. People decide. Pipelines deploy.
- Human
- Agent
- Pipeline
- Live
- Partial
- Planned
Asana · intake
Request becomes an Asana task
Feature, bug or task, from one form. Asana is the system of record.
HumanLiveTriage: does this need a code or config change?
A decision model returns typed judgments (system, module, outage signal). Advisory, fails open. A person applies the tag.
HumanAgentLive↳ No → the work stays in Asana
Tag syncs the task to GitHub
Idempotent sync every 15 minutes; the GitHub link is posted back to the task.
PipelineLiveNext: the label opens a draft PR directly
No issue in between. The pull request is the only GitHub record.
AgentPlannedAgentic workflow writes an assessment
Routing, affected modules, complexity and risk flags, written through declared safe outputs only.
AgentPartial
GitHub · pull request
Implementation
Well-scoped work goes to Copilot’s cloud agent. Novel work goes to a developer.
AgentHumanPartialBlocking CI
Static analysis, unit tests, coding standards, secret scan, dependency audit. Red blocks the merge.
PipelineLivePreview environment per branch
Created on push, destroyed on merge. Replaces the stakeholder demo meeting.
PipelineLive/playwright-check on the preview
Plain-English verify: lines in the PR body. An agent drives Playwright and posts a pass/fail table.
AgentPartialDecision-model judge
Each check returns a probability against a threshold. Below it, or diverged, fails closed to a person.
AgentPlannedReview: code → QA → stakeholder
Three sign-offs on one PR. The last human gate before code moves.
HumanLive↳ Any reviewer → back to fix
GitHub Deployments · environments
Merge → dev syncs automatically
Code plus configuration import.
PipelineLiveDeploy to test
A person dispatches; the pipeline deploys, imports config, clears cache, runs a health check. Asana hears “Released to TEST.”
HumanPipelineLivePost-deploy Playwright smoke
The same verify: checks, rerun against test and live after each deploy.
PipelinePlannedDeploy to live
Environment with a required reviewer. Every deploy is a GitHub Deployment: SHA, time, environment, result.
HumanPipelineLive
Asana · close the loop
Task completes itself
“Released to live via PR #N (SHA …).” Nobody moves a card.
PipelineLive
Asana stays the front door, and a person still opens it
Asana stays because the people asking for work live there, and they shouldn’t have to learn GitHub to ask for a fix. A web-request form creates a feature, bug or task. Triage asks one question: does this need a code or configuration change? If not, the work stays in Asana and never touches GitHub.
If it does, a person applies a tag such as github-bug-www. That tag stays human on purpose. Before a person reads the task, a decision model (TypeSafe’s Jev) returns typed judgments: which system, which of 66 modules, whether this smells like an outage. It’s advisory and fails open, so if Jev is down, triage carries on without it.
A scheduled Action runs every 15 minutes. It finds tagged tasks, creates the matching GitHub issue, hides the Asana task ID in the issue body so a second run can’t create a duplicate, and posts the issue link back to the task. An agentic workflow then reads the task and appends an assessment: likely routing, affected modules, complexity, risk flags. It can write that comment and nothing else.
We built the sync ourselves because Asana’s own GitHub integration is a status widget, not a workflow bridge. It shows PR status, review status and build status on a task. It can’t create an issue, and it can’t complete a task when code ships. Asana publishes GitHub Actions that comment PR status on a task and attach a PR to a task through its URL, which covers the notification half. The other half is ours.
The next change removes the issue entirely. Right now one request can exist as an Asana task, a GitHub issue and a pull request, and as one of my own planning notes puts it, “the issue content goes stale while the PR stays up to date.” The proposal: the Asana tag triggers an agentic workflow that opens a draft pull request directly, with the type in the title (BUG: Fix cart total rounding). The PR becomes the only GitHub record of the work. Hence the title of this post.
The pull request is where the proof piles up
Everything that would once have been a status meeting is now a check on the PR. Well-scoped work goes to Copilot’s cloud agent, and new or unusual work goes to a developer. I estimate 40 to 50 percent of our tickets are scoped tightly enough for the agent. That’s my estimate, not a measurement, and I’ll know better in a quarter.
Then the deterministic gates run: static analysis, unit tests, coding standards, a secret scan and a dependency audit. A red check blocks the merge, whoever wrote the code. Every pushed branch gets its own preview environment from our Drupal host, built on push and torn down on merge. That one change retired a whole step called “Stakeholder: Demo.” A link in the PR replaced a meeting on the calendar.
Browser testing is where agents earn their keep. A developer adds a block like this to the PR description:
<!-- playwright-check
urls:
- /products/compare
verify:
- The comparison table shows at least two products
- Prices render with a currency symbol
- There is no "Page not found" text
-->
When QA or a reviewer comments /playwright-check, an agentic workflow finds the preview URL, drives Playwright through the pages, judges each verify: line and posts a pass/fail table as a PR comment. QA can run it themselves whenever they want. Nobody writes a .spec.ts file or waits for one to be handed over.
The last gate is human: code review, then QA, then stakeholder sign-off, on the same PR. Any of the three can send it back. It’s the slowest step in the whole flow, which tells me where the constraint has moved. Verification is the bottleneck, and it always was.
Deployments close the loop without anyone moving a card
Merging is not releasing. A merge syncs the code to a shared dev environment automatically. Deploying to test is a button a person presses. The pipeline then deploys, imports configuration, clears the cache and runs a health check. Live works the same way, behind an environment that requires a named reviewer.
Because each deploy job names its environment, every release is a GitHub Deployment record: commit, time, environment, result. Two small workflows listen for those records. A successful test deploy labels the PR and posts “Released to TEST” on the Asana task. A successful live deploy closes the loop with “Released to live via PR #N” and the commit SHA, then marks the Asana task complete. Nobody updates a status by hand. The old process had twenty manual status changes per ticket, and this is where they went.
Agents are kept out of this layer by rule. They can prepare a deploy, writing the release notes and flagging risky config changes, but they never trigger one. Deployment has to be the same steps in the same order every time, auditable after the fact, and a probabilistic system doesn’t get to choose when production changes. Rollback is still manual too, which I count as a feature until someone shows me otherwise.
QA should return a decision, not a paragraph
The weak link in /playwright-check is the judge. A frontier model reads the page and writes a verdict for each verify: line. It’s usually right. But a CI gate needs a value it can compare against something, and “looks good” in a paragraph can’t be compared to anything. I made the general case in Who Reads the Output: when code reads the answer, the answer should be a type with a calibrated probability. A merge gate is code.
Sedum was the first QA tool I found built that way, and it’s uncomfortably close to what we built by hand. It’s open source, runs on Playwright, and takes test steps in plain English, the same authoring model as our verify: block. Underneath it’s built on Jev. Every check comes back as “a probability measured against a threshold you set,” plus a separate check for whether the page contradicts the claim. When a remembered element stops matching and Sedum has to find it again, it grades the run DIVERGED instead of green, because “the test passed” and “the product changed” are different facts. An experimental --affected flag scores each test by how related it is to the diff, so a one-line CSS fix doesn’t trigger the checkout suite. Its cost claim, a 20-person team’s PR suite for $38 to $91 a month against about $4,875 on per-step platforms, is the vendor’s own math, and I haven’t run it.
The second option arrived two weeks ago. On October 1, Cloudflare released Clef and Clef-flash, open-weight decision models (27 billion and 9 billion parameters, Apache 2.0) that are API-compatible with Jev and run on Workers AI. Clef accepts images as input, which matters for QA: a screenshot of the preview becomes the input, and “Is the price visible next to the Add to Cart button?” becomes a typed question with a probability. Open weights also mean the judge can run somewhere I control.
Two more tools made the evaluation but not the shortlist. Playwright’s own test agents (planner, generator, healer) write and repair ordinary .spec.ts files, which suits the regression suite, not plain-English checks. BrowserBash runs plain-English browser tests too, but I found no calibrated threshold in it.
The plan:
- Keep the
verify:block. - Swap the frontier-model judge for a decision model.
- Run Jev and Clef side by side on the same checks.
- Put the threshold in code.
- Make anything below the threshold, or anything that diverged, fail closed to a person.
Failing open was the right default for advisory triage. Once a model’s answer can block or pass a merge, it has to fail closed.
The case against: you’ve moved the queue, not removed it
The strongest objection is Goldratt’s, from The Goal: speed up any step that isn’t the constraint and you’ve built a bigger pile in front of the one that is. Agents produce pull requests faster than people can review them. Every Copilot PR still needs a human review, and that review is now the slowest step in the flow. A pipeline that opens twice as many PRs, with three human sign-offs on each, can easily ship less.
There’s more. GitHub Agentic Workflows is a technical preview, and I’ve already pinned a minimum version to avoid a billing bug. AI credits cost money on every run, and a careless trigger can burn a day’s cap before lunch. Jev is weeks old and Clef is days old. Every speed and cost figure for both comes from the vendors. And a natural-language assertion is only as good as the sentence a tired developer wrote at 5 p.m.
All of that is right, and it’s why the plan treats review as the constraint to work on, not a box to tick. The answer isn’t fewer agents. It’s agents that make review cheaper:
- an assessment that says where to look
- a preview link instead of a meeting
- a Playwright table instead of “works on my machine”
- a typed QA verdict with a number on it
An agent that adds a pull request without adding evidence makes the queue worse.
What isn’t built yet
Here’s what the diagram’s dashed boxes, and some of its solid ones, still owe:
- Post-deploy Playwright on test and live is planned, not running.
- There’s no visual regression testing anywhere.
- Automatic deploy to test on every merge is in progress. Today it’s still a button.
- The draft-PR-instead-of-issue model is a proposal from two weeks ago, with open questions: draft PRs shouldn’t spin up preview environments or run the full security scan, and one Asana task can need more than one PR.
- The one that bothers me most: a pull request opened by an agent looks exactly like one opened by a person. If agents are going to do half the work, the record should say which half.
The target is what I call the 24-hour sprint: a low-risk request filed today is live by the same time tomorrow. Most of that day belongs to the people at the review gates, which is where it should go. We haven’t measured it yet. When I have a number, it gets its own post, including the parts where it missed.
The ticket was always a pointer
An Asana task, a Jira ticket, a GitHub issue: each was a pointer to the work, and every pointer drifts away from what it points at. The pull request is the one artifact that is the work: the diff, the tests run against it, the people who approved it and the deploy that shipped it. Make it the record, let agents add evidence to it, and keep people at the gates. Every status meeting I can remember was someone asking what was true. The pull request already knows.
Resources
- GitHub, GitHub Agentic Workflows documentation and Safe outputs reference (technical preview); source at github/gh-aw.
- GitHub Docs, Kick off a task with Copilot.
- GitHub Docs, Managing environments for deployment.
- Asana Help Center, GitHub and Asana integration.
- Playwright, Test agents.
- Sedum, sedum.dev and sedum-dev/sedum (vendor cost figures).
- TypeSafe AI, “Introducing System One Models & Jev”, September 15, 2026 (vendor).
- Cloudflare, Clef launch post and changelog, October 1, 2026 (vendor).
- BrowserBash, open-source plain-English browser testing CLI.
- Eliyahu M. Goldratt, The Goal, 1984.
About the Author
Kevin P. Davison has over 20 years of experience building websites and figuring out how to make large-scale web projects actually work. He writes about technology, AI, leadership lessons learned the hard way, and whatever else catches his attention—travel stories, weekend adventures in the Pacific Northwest like snorkeling in Puget Sound, or the occasional rabbit hole he couldn't resist.