Prompt-to-Plugin: shipping a working WordPress plugin from a single spec
One plain-text spec, an LLM scaffold, three non-negotiable gates — a pipeline that ships faster without shipping weaker.
You have a plugin idea and an LLM. The gap between them is usually filled with vague prompts and copy-paste errors. Close it with a pipeline instead: write a single plain-text spec, have the model generate the scaffold, then run every file through the same hardening pass before it touches a real site. The point isn't to remove the developer — it's to delete the tedium. The model writes boilerplate; you own architecture, security, and the edge cases it never considers.
Write the spec like a contract
A good spec is the single source of truth, and it's the highest-leverage thing you write all day. Name the hooks, the custom post types, the meta keys and their types, the REST routes, and the capability each action requires. Ambiguity is where models hallucinate, so leave none. A usable spec line reads: "Register CPT project; meta budget is a sanitized float; add a Settings › Project Budget page with a nonce and an admin_post save handler that checks manage_options." Feed that to the model with a system instruction that fixes the standard: use nonce verification, capability checks, and sanitize-on-input / escape-on-output everywhere.
Generate, then distrust
The first output will look complete and will almost always be missing a guard. Treat generation as a draft and read it like a reviewer who assumes the worst. The most common omission is the save handler that trusts $_POST directly:
add_action( 'admin_post_save_project_budget', function () {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( 'Forbidden', 403 );
}
check_admin_referer( 'save_project_budget' );
$budget = isset( $_POST['budget'] )
? (float) $_POST['budget']
: 0.0;
update_option( 'project_budget', $budget );
wp_safe_redirect( add_query_arg( 'updated', '1', wp_get_referer() ) );
exit;
} );
Three lines — capability, referer/nonce, cast — are the difference between a plugin and a vulnerability. Models drop them constantly because the happy path works without them.
The three gates every file passes
Standardize the review so it isn't a matter of mood. Gate one: authorization — does every state-changing action check a capability and a nonce? Gate two: data — is every input sanitized (sanitize_text_field, absint, (float)) and every output escaped (esc_html, esc_attr, esc_url)? Gate three: queries — does anything touching $wpdb use prepare()? If a file fails a gate, you don't debate it; you fix it or regenerate that function with a sharper prompt.
Run this loop a few times and it becomes muscle memory. The spec keeps the model honest, generation kills the boilerplate, and the gates keep the vulnerabilities out. You end up shipping in an afternoon what used to take a day — and the code is more consistent than hand-written work, because the standard is written down instead of remembered.
Why models drift
Half of "the AI wrote bad code" is really the model losing track of your constraints mid-session. That failure mode has a name and a research home: Hidden State Drift ↗.