One of my side projects is a self-hosted WordPress site that several AI agents work on at once. A coding agent runs on the server itself and owns the articles and the publishing job. An assistant agent works from a sandboxed Linux box and owns the theme, plugins, redirects and SEO meta. Another handles indexing requests, and one more runs the project’s social account through a browser. I set the strategy and approve anything risky.
That setup was fast, but nobody could see what anyone else had changed. So now every agent writes to one plain Markdown changelog, and each entry has to say how to undo it. Here’s how it works and what this morning’s entries looked like.
Why the agents needed a shared log
Each agent has its own conversation history, and none of them can read the others’. When one agent changes a redirect, the next one finds a URL acting oddly and has no idea it was on purpose. I had the same question coming up again and again: who changed this, when, and is it safe to undo?
The moment that sold me came from the server-side agent. While it was digging through the box, it found that the publishing job had been dead for about a month. The machine had rebooted, and the process manager was never set to start on boot. Nobody else had a shell on that server, so nobody else could have spotted it. It went straight into the log as a diagnosis, before anything was fixed, so the other agents stopped guessing why posts had stopped going out on schedule.
The file
It’s one Markdown file in the site user’s home directory on the server, outside the web root, so it’s never served publicly. The top of the file is written for an agent that’s starting cold:
# Project change log (shared between the agents and me)
Rule: anyone who changes the site, its server, its content or its
social account adds a dated entry at the top. Read the whole file
before starting work. Times are local.
Format:
YYYY-MM-DD HH:MM | who | what changed | files/URLs | how to roll back
## Who owns what
- Articles and the publishing job: server agent. Articles go to
pending or draft for my review, never straight to publish.
- Site engineering (theme, plugins, redirects, SEO meta): assistant agent.
- Social account: browser only, no API.
- Strategy and approvals: me.
## Current strategy
...
## Reference files
...
Three parts of that header do most of the work:
- “Read the whole file before starting.” It’s a short file, and reading it is cheaper than one agent quietly undoing another’s redirect.
- The ownership list. When a fix crosses a line, like a change that really belongs in a theme template, the agent writes up the proposal and hands it to the owner instead of doing it itself.
- The current strategy. It says what we’re building right now and what we’ve ruled out. That stops an eager agent from bringing back a type of page I already decided to drop.
The rollback column is the point
An entry without an undo step gets sent back. Writing it forces the agent to think about reversibility before it acts, and it means I can undo a change on a bad day without reconstructing what happened. Here’s a real entry from this morning, trimmed and made generic:
2026-10-06 08:15 | assistant agent | Homepage "Trending" and "Popular"
lists were showing last-season posts that are already noindexed.
Added a must-use plugin that filters front-end list queries: drops
noindexed posts, and ranked lists only show current-season posts.
301 + Draft for 4 old posts. Purged cache. Verified: no old links
on the homepage, all 4 old URLs 301 in a single hop.
| wp-content/mu-plugins/fresh-lists.php; 4 redirect rules; 4 posts
| Delete the MU file, delete the 4 redirects, set the 4 posts back
to Publish, purge cache.
The plugin itself is small. The interesting part is how narrow the agent kept it. It only touches front-end, secondary post queries, so the main loop, the admin screens, the REST API and feeds never see it:
<?php
// wp-content/mu-plugins/fresh-lists.php
add_action( 'pre_get_posts', function ( $q ) {
if ( is_admin() || $q->is_main_query() || wp_doing_ajax() || wp_doing_cron() ) return;
if ( defined( 'REST_REQUEST' ) || $q->is_feed() ) return;
if ( $q->get( 'post__in' ) || $q->get( 'fresh_skip' ) ) return;
if ( ! in_array( $q->get( 'post_type' ) ?: 'post', [ 'post' ], true ) ) return;
// Hide posts your SEO plugin marks noindex (use its robots meta key).
$meta = (array) $q->get( 'meta_query' );
$meta[] = [
'relation' => 'OR',
[ 'key' => '_seo_robots', 'compare' => 'NOT EXISTS' ],
[ 'key' => '_seo_robots', 'value' => 'noindex', 'compare' => 'NOT LIKE' ],
];
$q->set( 'meta_query', $meta );
// Ranked lists (comments, meta, random) only pull current-season posts.
if ( in_array( $q->get( 'orderby' ), [ 'comment_count', 'meta_value', 'meta_value_num', 'rand' ], true ) ) {
$q->set( 'date_query', [ [ 'after' => '2026-07-01' ] ] );
}
} );
It went in as a must-use plugin rather than a theme edit. That’s the same reasoning I gave in I Let an AI Agent Patch a Theme With a Tiny Reversible Plugin: a theme update can’t overwrite it, and the rollback is deleting one file.
The log also records what isn’t broken
The same check looked at a deadline countdown that showed only a dash. The agent loaded the page in headless Chrome at a desktop size and at a phone size, dumped the rendered DOM, and took screenshots. The countdown was working fine. The dash is just the placeholder before the script runs, and the theme hides the ribbon countdown below 640px on purpose. It went in the log as “checked, working, nothing changed”, so the next agent doesn’t spend an hour fixing a non-bug.
The social agent writes the same kind of note. It found that a public data API’s “change” field counts from the last deadline, not from last night. So it now saves a daily snapshot and diffs against the previous one, and it logged the rollback, which is to drop the snapshot step.
The snag: not every agent can reach the file
The assistant agent edits the server through my server panel’s browser file manager. This morning it found that the file manager is locked to the web app folder, and the master log lives one level up in the home directory, so it couldn’t open the file at all.
The fix wasn’t to move the log into the web root, because then the server could serve it publicly. Instead, the agent writes its entries at the top of a local draft copy, marks them [PENDING SERVER COPY], and hands them to an agent that can reach the master file. The tag makes the gap visible. Anyone reading the draft can see which entries haven’t reached the server yet.
What I’d do from day one
- One file, newest first. Agents read top-down, and the newest context matters most.
- One entry per session, not per click. A combined entry with a single rollback is easier to undo than twenty tiny ones.
- Log diagnoses before fixes. “Found X, nothing changed yet” is useful to the other agents straight away.
- Keep it off the public web. The log names files, URLs and decisions. Treat it like config.
- Write ownership down. That one list stops more conflicts than anything else in the file.
Ship checklist
- Create one Markdown changelog outside the web root, readable by every agent that changes the project.
- Put the rule, the entry format, the ownership list and the current strategy at the top.
- Require a “how to roll back” field in every entry, and reject entries without one.
- Have each agent read the whole file before it starts and add one combined entry when it finishes.
- Log diagnoses and “checked, working” results, not just changes.
- Keep changes in small reversible units (a must-use plugin, a redirect rule, a draft status) so the rollback line stays short.
- Verify each change with a request you can re-run, like
curl -sIfor redirects or a headless browser for layout, and note the result in the entry. - If an agent can’t reach the master file, use a local draft with a pending tag and hand the entries off. Don’t move the file somewhere public.
FAQ
Why not use git commit messages?
A lot of what the agents change never touches a repo: redirect rules in a plugin, post statuses, cache purges, social account settings. A changelog covers the database, the dashboard and the files in one place. Where code is involved, the entry points to the file.
Do the agents actually read it?
They do when it’s in their instructions, and it is: read the whole file first, and add one entry at the end of the session. Keeping the file short and newest-first makes that cheap.
Could a local model keep the log?
Yes. Appending a formatted line to a file is an easy job for a small model. If you’d rather keep your project notes on your own hardware, my local LLM homelab stack post covers what I keep running.
Closest related Builders notes?
7 Rules for Letting an AI Agent Maintain Your Homelab covers the backup-and-verify habits behind each entry. I Had an AI Agent Audit My Self-Hosted Blog for Setup Leaks explains why posts like this one stay generic, and How I Run Claude Code 24/7 on an Always-On Home Box With tmux covers where a long-running agent lives between jobs.
