






In the first article in this series I gave a coding agent access to a real environment. We connected a laptop to a live Kubernetes cluster, had an agent build a feature against real dependencies, and debugged a cross-service bug with traffic recording and local overrides. Everything there was still open-loop. The agent had the tools, while a human ran curls, checked the UI, and decided whether each step worked.
An executable pass/fail check closes that loop. The agent can then spin up a sandbox, run the check, read the failure, fix the code, and re-run until it passes. The human only shows up when there's something worth reviewing.
We'll do that with two Signadot building blocks: Plans and Skills. Then we'll deliberately break a service contract to watch the loop close on camera.
The bugs that hurt in a microservices system are the ones that cross service boundaries. An agent renames a field in one service. The build and unit tests pass, and the linters are happy. By every signal available inside that service, the change is done.
And then the frontend, which still reads the old field name, renders an empty screen. Nothing in the change set says so, because no unit test crosses the service boundary. This is the class of bug that survives until staging or production, and it's getting worse: when agents open pull requests at agent speed, "we'll catch it in staging" stops being a strategy.
Agents need an executable way to express "this user-visible behavior must keep working" and run that check against a real environment before a human reviews anything.
A plan is a small, reusable validation workflow that runs against a live environment. It defines "correct" for one user-visible behavior and returns a pass or fail when an agent runs it.
Plans are built from actions: typed, deterministic building blocks like "execute an HTTP request," "run a Playwright browser flow," "run a k6 load test," or "assert on a result." Each action has defined inputs, outputs, and behavior, so you compose plans instead of writing the mechanics of driving a browser or firing load.
Ownership is split:
When a skill drafts a plan, it reads those contracts as an API reference and composes actions from documented argument names.
A good plan has three properties:
Over time you accumulate a library of executable expectations agents can pick from based on the diff in front of them.
There are two skills because authoring a plan and running one are different jobs. Keeping them separate keeps each skill simple.
signadot-plan is the author. You describe what to validate in plain language, such as "drive the booking flow and assert the itinerary renders." The skill reads the action catalog, drafts the plan spec, runs it once against the baseline cluster to prove the plan itself works, and tags it under a stable name. You author once; you replay anytime.signadot-validate is the runner. It reads your diff, picks the right plan automatically using the selection hint, spins up a sandbox with your change wired into the live cluster, and runs the plan against it. When the plan fails, it reads the failure, traces it to the real cause (even if that's in a different service than the one you changed), fixes the code, and re-runs until the plan passes.Rendering diagram…
The skills work with GitHub Copilot agent mode, Claude Code, Cursor, or anything that supports the skills format. They use the Signadot MCP server when it's available and fall back to the CLI otherwise.
This builds directly on the first article: same HotROD app, same cluster, same signadot local connect. On top of that you'll need:
signadot version)hotrod namespace, with signadot local connect healthyjq for inspecting CLI outputPlans require a Plan Runner Group in the cluster. Enable it per cluster from the dashboard: one toggle, no YAML. The runner executes plan steps inside the cluster, right next to the services they validate.
Open app.signadot.com/platform/managed-runners and confirm your cluster shows 1/1 runner ready. On the same page, the Plan Runner Actions panel controls which actions are allowed to run on the cluster; the platform team uses it to decide what agents can compose with.
The catalog is open source. Browse github.com/signadot/actions and open an ACTION.md (for example, playwright/ACTION.md) to see the typed inputs and outputs. You can list what's enabled on your cluster from the CLI:
signadot plan action list
From the HotROD repo root:
npx skills add signadot/agent-skills
This drops signadot-plan and signadot-validate into the repo. Confirm they landed:
Then ask your agent "What Signadot skills do you have available?" It should report both.
A user picks a pickup and a dropoff, requests a ride, and gets an itinerary showing both location names. This flow touches the frontend, the route service, the location service, and the database. A plan for the flow catches contract breaks that stop the itinerary from showing both names.
In your agent panel, prompt (replace <your-cluster> with your cluster name):
Create a Signadot plan tagged hotrod-e2e-ride on cluster <your-cluster> that drives the HotROD frontend with Playwright: pick a pickup and dropoff location, click Request Ride, and assert the itinerary shows both location names. Take an optional routing key param so the same plan can validate against a sandbox, and make the plan fail when the test fails.
After the prompt, the signadot-plan skill runs four steps:
baggage and tracestate) when the routing key param is set. These are the same headers the Chrome extension injects for sandbox routing. The plan uses the same routing mechanism as the extension. It also wires in a check step that asserts the Playwright run exited zero, so the plan's overall result reflects the test result.The optional routing key makes the plan reusable. Without a key, it validates the baseline cluster. With a sandbox's key, the same plan validates that sandbox.
When it's done, verify from the CLI:
signadot plan tag get hotrod-e2e-ride -o json | jq '{name, plan: .plan.id, selectionHint: .plan.spec.selectionHint}'
You'll see the tag pointing at the plan, with a populated selectionHint: the one-line, agent-readable description of what this plan validates. The validate skill later uses this hint to find the plan automatically.
In the dashboard under Plans, the plan has a spec and a run history with that green baseline run. The Playwright action captures artifacts, so you can open a run and watch the browser trace. Because the plan is tagged, anyone or any agent can replay it with one command:
signadot plan run --tag hotrod-e2e-ride
We'll make a change that slips through every single-service check, then hand it to signadot-validate.
Ask the agent for a perfectly reasonable refactor:
In services/location/interface.go, rename Name on the Location struct to LocationName. Update the json tag to match and make sure the build is clean.
The agent does the rename, updates the JSON tag, fixes the call sites inside the location service, and the build comes back clean. Run the location service unit tests:
go test ./services/location/...
They pass. git diff --stat shows the change is scoped entirely to services/location/. By every signal the agent has inside this service, the change is done.
But the frontend still reads the old field. The location service now serves locationName; the React app reads name. Nothing in the change set says so because no unit test crosses the service boundary. The failure appears only under real, end-to-end traffic.
Rendering diagram…
Before this becomes a PR, one prompt (replace <your-cluster>):
Validate this location-service change against the hotrod-e2e-ride plan on cluster <your-cluster>.
Everything from here is signadot-validate working autonomously, in phases:
local connect session, creates a sandbox that local-maps the location workload to your machine, builds the binary from your changed source, pulls the real cluster environment via signadot sandbox get-env, and starts hotrod location locally.hotrod-e2e-ride with the sandbox's routing key, so the Playwright traffic in the cluster routes to your changed service. Playwright walks the booking flow, the itinerary never renders, and the visibility assertion times out. Within minutes, the plan catches a bug that the service-local build and tests missed.name field, updates each to locationName, rebuilds the frontend, and adds frontend to the sandbox as a second locally-mapped workload because both changed services now need validation together.Then open the plan's run history in the dashboard. This is my favorite frame of the whole demo:
Note
green (baseline) → green (replay) → red (the rename hit a real environment) → green (after the fix)
The agent completed that red-to-green transition inside its session, before any human saw a PR.
Two commands, straight from the skill's report (use the actual sandbox name it printed):
pkill -f '/tmp/hotrod-bin'
signadot sandbox delete validate-location-rename
In the first article we debugged this exact class of bug manually with traffic recording, response payloads, and an override server. Those tools still work well for interactive debugging. Here, the plan runs the loop. The agent iterates against a real environment until the plan passes, and the run history gives a reviewer an auditable record.
For platform teams, the action catalog is the control plane. Your platform team owns what's allowed to run against the cluster; developers and agents compose plans on top of approved primitives. Agents get autonomy; you keep control. The catalog limits each validation loop to approved primitives.
Everything here is still the inner loop: one developer or agent working locally. The PR validation article covers the outer loop, where CI creates a stable sandbox for every pull request, runs tests as in-cluster Jobs, and gives reviewers a live preview. It also uses disposable Neon branches to keep schema changes and test data away from the baseline database.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。