❯ Guarded calls
Calling one ability is an upward write.
It does not look like a deployment, which is exactly why it is worth guarding like one: cwp ability against a live site changes that site, immediately, with no review step in between.
Which guards fire
- Direction is read from the annotation. A declared read-only ability is a read. Everything else is an upward write, and the 46 that declare nothing are in “everything else”.
- A protected environment refuses. Same rule as a push, same exit code, and it fires under
--dry-runtoo. - It needs a source in the repository.
--from <path>names the committed file the payload came from. Without one, the call is refused. - Or it records that it had none.
--adhocwrites the payload to.cwp/adhoc/and says the change is not reproducible. On a protected environment it is refused outright. - A destructive design-system call snapshots first. Global classes are not revisioned — a delete is immediate and final — so the design system is exported before the call. Locally only: a remote is covered by the provider backup.
Why silence counts as destructive
46 of the 175 abilities on a real site declare neither readonly nor destructive nor idempotent. Reading that as “probably safe” would be the cheap choice and the wrong one: an annotation that is absent tells you nothing about the code behind it, and the cost of being wrong is asymmetric.
So null stays distinct from false throughout. An ability is permitted to be read-only. It is not permitted to be vague about it and still be treated as one.
What this does not cover
Everything above describes calls made through cwp. An agent connected to the site's own MCP server talks to WordPress directly, and none of these guards is in that path — which is why the connection cwp bricks mcp writes should point at a local copy.