AI Coding Agent on a Live Server: Guardrails That Held
An AI agent migrated our live site and 8 app APIs in one evening. The mistakes it made, the checks that caught them, and what stayed with humans.
AI coding agents are now good enough to do real operations work: read a server, write migration scripts, run them. The question is no longer whether one can touch production, but what has to be true for that to be a reasonable thing to allow.
On 2026-11-08 an AI coding agent (Claude Code, running in a terminal session) moved tlaboratory.net from WordPress to a static site and moved eight mobile-app API folders under /api/v1/. A human approved each live step. The site serves 16 installed apps, 24 store listings point at its privacy page, and nothing went down. This post is not about the AI being clever. It is about the guardrails, and the five times they caught the agent.
The setup in one table
| Guardrail | What it did |
|---|---|
| Read-only exploration first | FTP listings, the WordPress REST API, curl, and a sub-agent that scanned 20 app source trees for every server URL they call |
| A written plan, approved before edits | Phases, rollback, what not to touch (ad-config API, smartlink pages) |
| Full backup before the first write | 1,067 files / 19 MB of API folders, store pages and .htaccess |
| A response baseline | 95 API endpoints saved with status, headers and body hash |
| Dry-run by default | Deploy scripts print what they would do; --go is required to act |
| Allow-list + "foreign file" stop | Refuses paths outside the blog, refuses to overwrite files it did not upload |
| Throwaway sandbox on the real server | Rewrite rules tested in a temporary subfolder that a finally block deletes |
| Staged rollout | One API first, the other seven 23 minutes later |
| Human-only steps | Policy text approval, cutover time, CDN purge, Search Console, device tests |
None of this is AI-specific. It is what you would ask of a careful new teammate. The difference is that the agent wrote most of these tools itself, before doing the risky part, and then had to pass them.
The five catches
1. Code blocks with whitespace silently changed (31 posts)
The agent converted 67 WordPress posts to Markdown. Its converter cleaned up trailing spaces and collapsed blank lines, including inside code blocks. Nothing looked wrong on screen. A verifier that compared every one of 479 code blocks byte-for-byte against the WordPress originals reported:
MISMATCH 1190 16 16
'using System;\nusing System.Collections.Generic;\n\n\nusing System.Linq;...'
'using System;\nusing System.Collections.Generic;\n\nusing System.Linq;...'
...
code blocks 479 posts mismatched 31
The fix confined cleanup to text outside code fences. The rerun gave 0 mismatches, and the verifier stayed in the repo.
2. A double-escaped space in a rewrite rule
The rule generator turned a space into \\ instead of \. Simulating every registered store URL against the generated rules exposed it, and the simulator itself crashed on the pattern. Had it shipped, the iOS support URL of one app would have stopped matching. The agent switched to \s, which no configuration parser can misread.
3. A URL variant with no rule at all
One store listing registered a page as US Lottery Infomation (capitals, spaces); WordPress used us-lottery-infomation. The generator treated them as the same page and emitted a rule for only one form. A check that ran all 196 old WordPress URLs and asked whether the first matching rule pointed at the right target found the gap. The fix: [\s-] in place of spaces.
4. A preview build that contradicted itself
The local preview marks every page noindex but still wrote a sitemap listing them. The content checker flagged 38 sitemap entries as "noindex but in sitemap". It was harmless locally, but exactly the kind of signal search engines punish. Preview builds now write no sitemap.
5. A comment mistaken for code
While rewriting 101 PHP includes, the agent's safety check refused to continue because one file still mentioned lib/AppConfig.php. The mention was in a comment. Here the guardrail was too strict, which is the right direction to fail. It was narrowed to actual require/include statements.
The pattern across all five: the agent made the mistake, and an agent-written check caught it before the server did. That is the reason to have the agent build the verifier first.
The agent's own permission system said no once
At one point the agent tried to read another project's .env file to compare settings. That file held database passwords. The tool's permission layer refused it as credential exposure, and the agent changed approach: it compared the ten copies of the database config by hash instead of reading them, which answered the actual question ("are these identical?") without printing a secret.
5ae45e5d7d2f3e0c15e99acf1b40d61c Converters/lib/AppConfig.php
5ae45e5d7d2f3e0c15e99acf1b40d61c DreamApp/lib/AppConfig.php
... (10 identical)
Testing on production without risking production
Shared hosting has no staging server. The agent wrote rwtest.py instead. It creates /_rwtest_YYYYMMDD/ and aborts if that folder already exists. It uploads a .htaccess that only applies inside the folder, runs a list of requests, and deletes everything in finally. It ran four times: once for URL-matching behaviour (15/15), and three times with the complete generated root file rewritten to the sandbox base (12/12 each). That last run is what ruled out a syntax error that would have taken down every app endpoint on the domain.
Where the human was essential
- Content with legal weight. The privacy policy covers 16 apps. The agent built it from what each app actually includes: SDKs from
pubspec.yaml, permissions fromInfo.plistandAndroidManifest.xml. A human still had to approve the text before upload, because uploading alone changes what 24 store listings show. - Timing and blast radius. Cutover time, and the decision to move one API alone before the other seven.
- Things only a dashboard can do. CDN purge, Search Console sitemap, opening the apps on a phone.
- Personal information. The agent's first About page described the operator by nationality and location. The human asked for it to be removed, and the agent then found the same details in the author box, the home page, the privacy policy and the structured data, and removed them everywhere.
Takeaways
- Make the agent produce a baseline and a verifier before it produces the change.
- Default every script to dry-run, and make the dangerous flag explicit.
- Give it a sandbox on the real system when there is no staging. Production quirks such as decoded URL matching or the CDN caching API responses only show up there.
- Stage the rollout and keep rollback to one command.
- Keep approvals, timing and anything legal with a human.
The agent did the work in hours. The guardrails are what made those hours safe to allow, and most of them can now be reused for the next change.
FAQ
Did the AI agent make mistakes?
Yes, five that would have reached production. It stripped whitespace inside 31 posts' code blocks, double-escaped a space in a rewrite rule, missed a URL variant, generated a contradictory sitemap and tripped on a comment. Each was caught by a check the agent had written before making the change.
What should stay with a human?
Decisions with external consequences: approving the privacy-policy text that 24 store listings point to, choosing the cutover time, purging the CDN, Search Console, and testing on real devices. The agent asked before each live change.
What was the single most useful guardrail?
A response baseline taken before any change. Every later step was judged against it, so 'it seems fine' was never the standard.