req2prod
HOW IT WORKS · GITHUB ACTIONS + CREWAI

A requirement goes in one end.
A deployed change comes out the other.

Unattended, LLM-agnostic, agentic SDLC orchestration

Everything in Req2Prod hinges on a single artifact in the middle: the pull request. Two quite different things can open one, and from that point on nothing downstream can tell them apart. This page follows a change the whole way — from the moment you type a requirement, through the agents that challenge it and build it, to the review loop that gates it and the deploy that puts it live.

The shape of it

The pull request is the seam

One entry point is the product: you describe a feature, and a product manager, an architect and an engineer turn it into code. The other is you in a Claude Code session, building the tool itself. They converge on the same artifact and are held to the same gate.

The two entry points converging on a pull request Both the Requirements Challenge product path and a Claude Code session open a pull request against main. From there the same review and deploy pipeline picks it up. Requirements Challenge the product: PM, architect, engineer A Claude Code session you, building the tool itself Pull request against main nothing downstream knows which one made it Reviewed, merged, deployed the same path for both
The symmetry is deliberate. build_feature() only ever opens a pull request — it never pushes to main and never merges — so the engineer's work arrives exactly like a human-authored branch.

The seam also marks where you change roles. Before the pull request you are in the loop: the product manager and architect challenge your requirement, hand back open questions, and nothing proceeds until you have answered them. After it you are deliberately out of the loop: the reviewer, the fixer and the arbiter converge or escalate among themselves, and you are only ever informed of the outcome.

That is the position the whole system takes. You are the authority on what to build, and explicitly not the judge of how it was built.

Before the pull request

Where you're in the loop

The front half is a conversation, not a form. You type a feature request, and challenge_requirement() sends the entire thread — your original idea plus every answer you have given since — to the product manager and the architect. They re-run fresh against that whole accumulated conversation every time rather than relying on agent memory, so picking a session back up days later challenges from the same context.

The requirements challenge sequence, from a typed request to an open pull request You type a feature request into the Streamlit admin page. challenge_requirement sends the whole conversation to product_manager and software_architect. If either has open questions you answer them and the loop runs again. Once both mark it ready for development, a button appears that calls build_feature, which runs software_engineer inside a throwaway clone of the repo and opens a pull request against main. throwaway clone YOU STREAMLIT APP CREWAI AGENTS Type a feature request Requirements Challenge page challenge_requirement() sends the whole thread product_manager requirements, open questions software_architect technical direction You answer and it all runs again Push to Software Engineer the button only now appears software_engineer builds in a fresh copy Pull request against main never pushed, never self-merged questions remain — the only human step both ready
Delivery flow
Clarification — the only human step
Throwaway clone, deleted when the build ends
The button is gated twice. It only renders when both agents' latest verdicts are ready, and build_feature() re-checks both flags itself before running — defense in depth, not the primary gate.

The loop is the point. Neither agent gives you a plan and stops; they come back with what they still need to know, and the Push to Software Engineer button does not exist until both have set ready_for_development. It also disappears the moment anything new is said, so a stale verdict can't be pushed twice.

Once you press it, software_engineer builds — and it can stop and ask the product manager a functional question or the architect a technical one mid-build rather than guessing. It works inside a throwaway clone of the repo that is deleted afterward regardless of outcome. That isolation was learned the hard way: the file and git tools used to run wherever the process happened to be, which in production is the directory serving live traffic. One malformed write left streamlit_app.py containing the word "test" and the live server parked on a half-built branch — a full outage from a single bad tool call.

Once the pull request exists

Three ways in, and no fixed order

From here the path is identical whichever half produced the branch. The pipeline is not one sequence: it contains three jobs, and which of them runs depends entirely on which event fired — code_review and deploy never execute in the same run. The one that always runs is resolve_backend, which decides api versus subscription in a single place so the runner choice and the backend handed to the scripts can never disagree.

Req2Prod pipeline job routing Three events — pull_request, push to main, and workflow_dispatch — are raised by GitHub itself. They all enter the resolve_backend job, which routes to either the code_review job or the deploy job. All three jobs sit inside a dashed boundary marking the temporary GitHub runner. temporary GitHub runner · wiped after every run pull_request opened, synchronize push to main after a merge lands workflow_dispatch you click Run workflow resolve_backend picks api or subscription runner code_review job runs only if the event was pull_request deploy job runs on dispatch, or on push if AUTO_DEPLOY_ON_MERGE is true
Delivery flow
Path to production
Temporary runner — a fresh VM, wiped after every run
The same dashed-boundary idea as the build clone, in a second place. The events sit outside it because GitHub raises them from its own service — no compute of yours is involved until a job is assigned a runner.

Nothing in this file enforces review before deploy — there is no needs: linking the two jobs. What guarantees a change was reviewed before it reaches production is main's branch protection requiring an approving review. GitHub refuses the merge, so the push event that would trigger a deploy never happens. The ordering lives in your repo settings, not in the YAML.

One caveat the boundary hides: it is only temporary in api mode. Flip AGENT_BACKEND to subscription and those jobs relocate to your laptop, which persists — which is why the deploy job's cleanup step matters. resolve_backend also enforces a security boundary: a fork's PR silently falls back to api no matter what the toggle says, because subscription would otherwise run an outsider's code on your machine.

When the event is a pull request

Where you're out of the loop

GitHub is only the trigger and the gate here — it does no reviewing itself. The workflow checks out the actual branch rather than the merge ref, because the fix agent needs somewhere real to push, then pipes gh pr diff into a file and runs one Python module.

The PR code review sequence A pull request triggers the workflow, which runs code_review_runner.py, which drives the code_reviewer, pr_fix_agent and pr_arbiter CrewAI agents, then approves and squash-merges the PR as MarcoAIagent. The Python driver and CrewAI agent lanes sit inside a dashed boundary marking the temporary GitHub runner. temporary GitHub runner GITHUB PYTHON DRIVER CREWAI AGENTS Pull request opened from either half Req2Prod pipeline gh pr diff to /tmp/pr.diff code_review_runner.py owns the loop and git code_reviewer gates every merge pr_fix_agent applies fixes · 2 rounds max pr_arbiter final merge / hold decision Not safe: left open emails you why gh pr review, gh pr merge as MarcoAIagent Squash-merged to main required review satisfied findings clean
Delivery flow
Fix loop → pr_arbiter
Not safe to merge → you are notified
Merged
Only two arrows cross the dashed line: the diff comes in at the top, the verdict and any fix commits go out at the bottom.

code_review_runner.py is the orchestrator, and it is plain Python with no AI in it. It owns the review and fix loop, all git operations, and the final review and merge calls. The agents never touch git: pr_fix_agent only edits files in the working tree, and the runner commits everything it changed exactly once at the very end. Pushing mid-loop would fire a fresh synchronize event and spawn a second, racing copy of the same job.

Each agent returns a typed result rather than prose, which is what lets the plain-Python driver branch on result.passed and verdict.safe_to_merge instead of parsing text. If code_reviewer passes, the runner approves and squash-merges immediately. If it returns findings, those exact findings go to pr_fix_agent, which edits the named files; the runner then re-diffs against origin/main and re-reviews the new diff. If it still hasn't converged, pr_arbiter makes a genuine independent call: merge it, or leave the PR open and email you a plain-language explanation.

The review is submitted as MarcoAIagent rather than the workflow's default token, because GitHub won't let a PR's author approve its own PR — the reviewer has to be a different account. The default token is read-only anyway, so it couldn't push fixes even if it were allowed to approve.

When it's a merge or a manual run

Reaching out of a disposable machine

The deploy job genuinely is one long ordered sequence, and its whole purpose is reaching out of the throwaway VM to a machine that isn't throwaway.

The deploy job's ordered steps All six deploy steps run inside a dashed boundary marking the temporary GitHub runner. Three of them — snapshot production, update the server, and prod_tester — reach out over ssh to the Lightsail production server, which sits outside the boundary and persists between runs. temporary GitHub runner Prepare the runner checkout, python, deps, chromium Get into AWS credentials, then a short-lived ssh cert Snapshot production record the commit running there Update the server git pull, restart only if code changed prod_tester, rollback_agent if broken a real headless browser hits the live site Clean up key material always, even if a step above failed Lightsail server your real production box it persists between runs, so everything the steps do here actually sticks
Step order inside the job
Reaches production over ssh
Temporary runner — everything else persists
Every deploy re-fetches a short-lived SSH certificate rather than storing a key, because the runner has no memory between runs — there is nothing to store into.

The snapshot step near the top is what makes the bottom half work: it records production's current commit before anything changes, and hands that value to rollback_agent as PREVIOUS_COMMIT. Without it there would be nothing to roll back to.

The restart is conditional — it only fires if the diff touched .py files or requirements.txt — and when it does, dependencies are installed into the server's persistent venv, not just the runner's throwaway one. That step exists because its absence once restarted the app straight into a ModuleNotFoundError. Installing a new dependency on the runner only affects the box that is about to be deleted.

Two neighbours

What lives outside the pipeline file

deploy-to-prod.yml is a separate, manual-only deploy that predates the merge; the pipeline's own deploy job largely supersedes it. devops-agent.yml watches both of them and fires automatically when a deploy fails: devops_agent reads that run's logs, applies a fix, pushes it, and re-triggers the workflow.

It ignores pull request failures, since the review path already has pr_arbiter as its escape hatch. It also carries two guards against runaway cost — it stops and emails you if the last commit on main was already one of its own fixes, or if it has fired too many times in a short window.

Requirement in, software out

Collapses the whole SDLC — analysis, build, review, release — into one autonomous, auditable flow.

Self-correcting by design

The review loop runs a fix agent, then an independent arbiter decides; failed prod tests auto-rollback or invoke DevOps.

Model-tiered & controlled

Each agent runs its own Claude tier from a model registry. The sole human step is requirement clarification.