CI/CD generation workshop
A guided walkthrough of one full crew run: the Code Reviewer reads a service repo, the DevOps Engineer drafts a CI workflow and deploy artifacts, the artifacts are validated and judged by the policy gate, a pull request opens, and a gap analysis scores the result.
The point is not just to make a PR happen. It is to see what each agent does, why, and where the trust boundaries are: agents propose, policy decides, humans approve.
This workshop needs a local development environment. It uses the
seeded sample repos, the in-cluster Gitea and ArgoCD, and the trustacks
CLI, which talks to a local or self-hosted control plane (the CLI sends no
credentials, so it cannot drive the hosted service). Set up with
Local development first.
On the hosted product? You can follow most of it in the app against your own repos. Steps 2, 3 and 5 are app screens; for steps 1 and 4 use Run analysis and Promote to proposal on the service; for step 6, promote without dry run and the pull request opens in your GitHub platform repo; for step 8, merge there and sync in your own ArgoCD; step 9 is in the app. Step 7 needs an overlay bundle, which hosted runners cannot load yet. The product guide covers each screen.
What you will learn
- How the Code Reviewer identifies a framework, and why known frameworks are detected deterministically before the agent runs.
- What the DevOps Engineer reads before it writes (read-and-preserve).
- The two checks before any PR: artifact validation, then the policy gate.
- What a TruStacks pull request tells its reviewer.
- What a gap analysis and maturity score look like for the sample stack.
Time required
About 45 minutes. Each crew run takes a minute or two; most of the time is reading what the agents produced.
Setup
Bring up the local stack and seed it, as described in Local development:
make up
make seed
make urls # every service reachable on *.localtest.me:8080make seed creates four sample service repos in Gitea (FastAPI, Spring
Boot, .NET and Go), a platform repo, an overlay repo, the matching
connection profiles, and a polyglot-demo Application that binds the four
samples to the platform repo. This workshop uses the FastAPI sample, whose profile is
named sample-app-gitea.
Open http://ui.localtest.me:8080. You should see the app with the sidebar
(Overview, Get set up, Workspace, Signals, Applications, …).
Walkthrough
Step 1. Run the Code Reviewer from the CLI
cd runner && uv sync
uv run trustacks review sample-app-giteaThe CLI streams the run’s events live. The shape:
started → cloned → framework_detected (python_fastapi)
→ tool_call / tool_result (the agent reading files) ×N
→ finding ×N
→ doneWhat is happening: before the agent starts, a deterministic detector
matches the repo against TruStacks’ framework packs (here, fastapi in
pyproject.toml). The agent is told the framework as a fact, so it does
not guess. It then reads files with a small, explicit set of tools, and
submits a typed RepositoryAnalysis. Each tool_call is the agent asking
to read something; each tool_result is the answer.
Note the analysis id at the top of the output, and keep it in a shell variable for the next steps:
read -r -p "Analysis id printed above: " ANALYSIS_IDStep 2. See the same run in the app
Go to Applications → View all services across applications, open
sample-app-gitea, and look at the Analyses tab. Your CLI run is there.
open → shows the same events as an Activity log, with the
Coordinator narrating and the Code Reviewer’s tool calls attributed to it.
When the run is done, the Repository Analysis card shows framework,
language, package manager, runtime version, entrypoints, and whether tests,
a Dockerfile and CI exist.
You can start an analysis from here too: Run analysis with ref main.
Step 3. Look at the declared stack
Open Stack. This is the environment profile the agents read: the
environment-profile.yaml in the seeded overlay repo. Note which tooling
categories are declared (CI platform, GitOps controller) and which are
not. Only declared tools will get pipeline steps in the next step. The
agents never add a tool you did not declare; recommendations go into the
gap analysis instead.
Step 4. Promote the analysis to a proposal
From the CLI, with a dry run first:
uv run trustacks plan "$ANALYSIS_ID" --dry-runOr in the app: on the service’s Analyses tab, promote →, tick dry-run (skip PR), then Promote (dry-run).
The event shape:
started → cloned_app → cloned_platform → analysis_done → profile_loaded
→ agent_started (DevOps Engineer)
→ tool_call read_file (repo="platform") ×N read-and-preserve
→ artifact_emitted ×N
→ proposal_built
→ artifact_validation does it render and parse?
→ policy_check (allow | deny) the gate
→ gap_check (advisory)
→ dry_run_skipped
→ doneWatch for three things:
- The platform repo is read first. The DevOps Engineer reads the current version of each file it is about to write. On a first proposal, nothing exists yet and it emits fresh files.
- Validation runs before policy. Helm (or Kustomize) must render, workflows must parse, names must be legal Kubernetes names. If anything fails, or a check could not run, the run stops here.
- The Constitution speaks last.
policy_checkis OPA evaluating the proposal against the constitution and the overlay. On the proposal page it reads clean, no violations, or denied with the rule ids.
Step 5. Read the proposal
Open the proposal (the CLI prints its id; the app navigates there). Below the timeline:
- Explanation: summary, blast radius, rollback, rules cited, and which profile entries were used.
- Files: one tab per emitted file. Expect a reusable CI workflow
(
.github/workflows/ci-<service>.yaml,on: workflow_callonly), a minimal Helm chart (Chart.yaml,values.yaml, a Deployment and a Service) undergitops/<application>/<cluster>/<service>/, and an ArgoCD Application underargo-apps/.
Look at the workflow’s uses: lines. Every one is pinned to a 40-character
commit SHA taken from a TruStacks-curated pack. An action that is not in a
pack is left out and recorded as a TODO, never guessed.
Step 6. Open the real pull request
Run it again without dry run:
uv run trustacks plan "$ANALYSIS_ID"The run ends with pr_opened and a link to the PR in Gitea. The PR body
carries the reviewer’s context: Summary, Blast radius,
Rollback, Rule citations, EnvironmentProfile entries used, any
Preservation notes and Files removed, and the file list. The
structure exists so a reviewer can answer why was this proposed, and what
does it touch? before reading the diff.
If the platform repo already matches the proposal exactly, the run ends with “nothing to change” instead of opening a duplicate PR.
Step 7. See a deny
The easiest way to watch the gate refuse something is to add a rule that the
emitted workflow will not satisfy. The
policy authoring workshop does exactly that:
a rule requiring top-level permissions: on every workflow. After landing it,
re-run Step 4. policy_check reports denied, cites your rule id, and no
PR opens. The Overview’s Policy in force card starts
counting it.
Remove the rule before Step 8. Step 8 promotes the same analysis again,
and with your rule still loaded that proposal is denied too. Delete the
rule’s directory from your overlay repo, run trustacks rule sign again to
publish the overlay without it, and restart the runner
(kubectl -n trustacks-system rollout restart deployment runner-runner).
Or do Step 8 first and come back to this step.
Step 8. Review, merge, and see read-and-preserve
Merge the PR in Gitea. In local development, make seed has already
pointed the in-cluster ArgoCD at the platform repo; open
http://argocd.localtest.me:8080 to see the new Application. (Building and
loading the sample image for an actual running pod is covered in the
project’s workshop guide under Close the deploy loop.)
Now edit the generated workflow in Gitea (add a harmless step, for example
an echo in its own step) and merge that edit. Promote the same analysis
again. In the new PR:
- your added step is still there, because the DevOps Engineer read the file before re-emitting it;
- a Preservation notes section says what it kept and why.
Step 9. Score it
In the app, open Gap analyses and Run new gap analysis (or type
/gaps in the Activity chat at #/activity). Watch the Coordinator
consult Baseline Security and SOC2, each replying in its own voice.
Then open Maturity. You will see a tier, a bar split into verified, credited and potential, and the open gaps. Note that the score is computed deterministically by the runner from your profile and the rules; the language model writes the prose, not the number. See Verified, claimed and disputed.
What you should take away
- The gate is policy, not the agent’s judgement. Validation and OPA decide whether a proposal can become a PR.
- The environment profile is the platform’s memory. Change it, and the next proposal changes.
- The PR is the hand-off. Everything before the merge is agents proposing. The merge is a human committing. ArgoCD deploys.
- Re-runs are safe. Read-and-preserve keeps your team’s edits.
Where to go next
- Policy authoring workshop: write the rule that produces the deny in Step 7.
- Agents propose. Policy decides. Humans approve
- Applications, services and proposals
- Rule CLI