<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: David</title>
    <description>The latest articles on DEV Community by David (@david786).</description>
    <link>https://dev.to/david786</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F99mvlsfu5tfj9m7ku25d.png</url>
      <title>DEV Community: David</title>
      <link>https://dev.to/david786</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/david786"/>
    <language>en</language>
    <item>
      <title>I built 35+ PDF tools that never upload your files (Rust + WebAssembly)</title>
      <dc:creator>David</dc:creator>
      <pubDate>Fri, 13 Nov 2026 20:14:31 +0000</pubDate>
      <link>https://dev.to/david786/i-built-38-pdf-tools-that-never-upload-your-files-rust-webassembly-3j24</link>
      <guid>https://dev.to/david786/i-built-38-pdf-tools-that-never-upload-your-files-rust-webassembly-3j24</guid>
      <description>&lt;p&gt;Most online PDF tools work the same way: you upload your file, a server processes it, and you download the result. That's fine for a flyer. It's less fine for a contract, a bank statement, or a medical report.&lt;/p&gt;

&lt;p&gt;I wanted to know how far you can get with no server at all. The answer turned out to be: almost all the way. The result is &lt;a href="https://inbrowserpdf.app" rel="noopener noreferrer"&gt;InBrowserPDF&lt;/a&gt;, a set of 38 PDF tools where every file is processed entirely on your own device.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rust compiled to WebAssembly&lt;/strong&gt; does the heavy work: parsing, merging, compressing, and encrypting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Workers&lt;/strong&gt; run that code off the main thread, so the page stays buttery responsive while a multi-megabyte file is being crunched.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The file never leaves the tab.&lt;/strong&gt; You can check this yourself: open DevTools, go to the Network tab, and run any tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Under the hood, I leveraged a few key Rust libraries compiled to WASM. For low-level PDF object parsing, manipulation, and structural editing, I relied on &lt;code&gt;lopdf&lt;/code&gt;, which gives fine-grained control over document dictionaries and streams without dragging in massive C++ dependencies. For page rendering and visual operations, I integrated lightweight bindings over &lt;code&gt;pdfium&lt;/code&gt;, compiled down to WebAssembly to allow pixel-accurate rendering and thumbnail generation directly inside browser memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was hard
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Memory
&lt;/h3&gt;

&lt;p&gt;WebAssembly memory management in the browser has strict boundaries. By default, WASM modules operate within a linear memory buffer that must be explicitly allocated and grown. When dealing with large PDF files, passing large &lt;code&gt;ArrayBuffers&lt;/code&gt; back and forth between JavaScript and WASM can cause GC pauses and out-of-memory crashes on mobile devices or lower-RAM laptops. &lt;/p&gt;

&lt;p&gt;That's why heavy operations like merging are capped at 100 MB total input size. If a file or combined set exceeds this threshold, the Web Worker catches the memory allocation failure gracefully and throws an explicit warning rather than letting the tab silently crash.&lt;/p&gt;

&lt;h3&gt;
  
  
  Office conversions
&lt;/h3&gt;

&lt;p&gt;Converting Word (&lt;code&gt;.docx&lt;/code&gt;), Excel (&lt;code&gt;.xlsx&lt;/code&gt;), and PowerPoint (&lt;code&gt;.pptx&lt;/code&gt;) documents entirely client-side is notoriously tricky because true layout fidelity requires a heavy document engine layout pass. To achieve this without a backend conversion server, I utilized browser-side WASM ports of document parsers combined with styled HTML/CSS layout templates rendered inside hidden DOM containers and printed via headless canvas/PDF generation APIs. Because of this DOM layout dependency, these conversions require modern desktop browser engines with hardware acceleration enabled to compute complex table structures and font metrics accurately.&lt;/p&gt;

&lt;h3&gt;
  
  
  OCR
&lt;/h3&gt;

&lt;p&gt;For text extraction on scanned documents, I integrated Tesseract compiled to WebAssembly. While it works remarkably well offline, client-side OCR is computationally expensive. To keep memory footprints reasonable and processing times under a few seconds per page, the current WASM build is restricted to lightweight data packs optimized strictly for printed English text rather than heavy multilingual neural nets.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;p&gt;If I could rewrite one part of the architecture, I would have decoupled the larger WASM worker tasks earlier. Initially, I tried bundling too many disparate utility functions into a single monolithic Web Worker script, which bloated the initial worker initialization chunk and complicated error propagation. Splitting them into isolated, single-responsibility worker modules loaded on-demand via dynamic imports saved me days of debugging data-clone serialization bottlenecks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it can do today
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Merge, split, organize, extract, and remove pages&lt;/li&gt;
&lt;li&gt;Compress, repair, and OCR&lt;/li&gt;
&lt;li&gt;Convert Word, Excel, PowerPoint, JPG, and HTML to PDF, and back&lt;/li&gt;
&lt;li&gt;Edit, sign, fill forms, watermark, crop, and add page numbers&lt;/li&gt;
&lt;li&gt;Protect with AES-256, unlock, redact, compare, and remove metadata&lt;/li&gt;
&lt;li&gt;Convert PDF to Markdown and summarize&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's free and needs no account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Known limits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Merge handles up to 100 MB in total&lt;/li&gt;
&lt;li&gt;Compare handles up to 20 pages&lt;/li&gt;
&lt;li&gt;Batch handles up to 20 files&lt;/li&gt;
&lt;li&gt;OCR is printed English only&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  I'd like your feedback
&lt;/h2&gt;

&lt;p&gt;If you try it, I'd like to know:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Did anything fail or slow down on a large or unusual file?&lt;/li&gt;
&lt;li&gt;How did the Office conversions handle your real documents?&lt;/li&gt;
&lt;li&gt;What would you want next?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Try it at &lt;a href="https://inbrowserpdf.app" rel="noopener noreferrer"&gt;inbrowserpdf.app&lt;/a&gt;, and tell me what breaks.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>webassembly</category>
      <category>webdev</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why LLMs Write Insecure WordPress Code — and the Architecture We Built to Fix It.</title>
      <dc:creator>David</dc:creator>
      <pubDate>Sun, 28 Jun 2026 19:36:48 +0000</pubDate>
      <link>https://dev.to/david786/why-llms-write-insecure-wordpress-code-and-the-architecture-we-built-to-fix-it-1lme</link>
      <guid>https://dev.to/david786/why-llms-write-insecure-wordpress-code-and-the-architecture-we-built-to-fix-it-1lme</guid>
      <description>&lt;h2&gt;
  
  
  The Naked Truth About Chatbots and PHP
&lt;/h2&gt;

&lt;p&gt;If you've ever asked a general-purpose LLM to "write me a WordPress plugin," you already know how this story ends. You get a single 400-line file. It registers a hook with the wrong priority. It writes directly to &lt;code&gt;$wpdb&lt;/code&gt; with an un-sanitized &lt;code&gt;$_POST&lt;/code&gt; value. It calls a function that was deprecated three major versions ago. And the AJAX handler it just generated would happily execute for any unauthenticated visitor who knows the action name.&lt;/p&gt;

&lt;p&gt;The frustrating part isn't that the model is "dumb." It's that it's doing exactly what it was built to do.&lt;/p&gt;

&lt;p&gt;A general-purpose model is a statistical next-token predictor. It was trained on a giant pile of internet text, which includes excellent code, mediocre code, and a frankly alarming amount of insecure 2014-era StackOverflow snippets that someone copy-pasted into a tutorial. When you ask it for a plugin, it isn't reasoning about the WordPress Plugin Handbook. It's predicting what the &lt;em&gt;next plausible character&lt;/em&gt; is, weighted by everything it ever absorbed — good and bad alike. Security isn't a constraint in that process. It's just one more pattern competing with thousands of insecure ones.&lt;/p&gt;

&lt;p&gt;So the question we set out to answer wasn't "how do we make the model smarter?" It was a software engineering question: &lt;strong&gt;how do you wrap a probabilistic text generator inside a deterministic system that refuses to emit code violating a fixed set of rules?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This post is a walkthrough of the architecture we landed on — a multi-step agentic system that plans, structures, and audits its own output before it ever reaches the developer. No magic, no hand-waving. Just the design patterns that made the output trustworthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Vulnerability Vector: Why "Autocompleted" Plugins Are Dangerous
&lt;/h2&gt;

&lt;p&gt;Let's be specific about &lt;em&gt;why&lt;/em&gt; raw LLM output is a security liability in a CMS context, because the failure mode is different from general application code.&lt;/p&gt;

&lt;p&gt;WordPress is a hook-heavy environment. Almost everything meaningful happens by attaching callbacks to actions and filters, and those callbacks frequently touch the database, render HTML, or perform privileged operations. That architecture has two consequences for AI-generated code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The blast radius of one missing check is enormous.&lt;/strong&gt; A REST endpoint registered without a &lt;code&gt;permission_callback&lt;/code&gt; isn't a minor bug — it's an open door. An AJAX handler that skips &lt;code&gt;wp_verify_nonce()&lt;/code&gt; is a CSRF vulnerability waiting for a crawler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The dangerous lines look harmless.&lt;/strong&gt; A model will confidently generate &lt;code&gt;$wpdb-&amp;gt;query("UPDATE ... WHERE id = " . $_GET['id'])&lt;/code&gt;. It compiles. It "works" in a demo. And it's a textbook SQL injection.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The three checks that matter most in WordPress — input sanitization, output escaping, and authorization — are also the three things a next-token predictor is most likely to drop, because in its training distribution they're frequently &lt;em&gt;absent&lt;/em&gt;. Plenty of working tutorial code omits them.&lt;/p&gt;

&lt;p&gt;So the real engineering challenge crystallized into a single sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do we force the system to treat &lt;code&gt;sanitize_text_field()&lt;/code&gt;, &lt;code&gt;wp_verify_nonce()&lt;/code&gt;, and &lt;code&gt;current_user_can()&lt;/code&gt; as non-negotiable compilation requirements rather than optional stylistic suggestions?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can't solve that with a better prompt alone. Prompts are persuasion; they're probabilistic nudges. We needed structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture: A Domain-Specific Grounding Engine
&lt;/h2&gt;

&lt;p&gt;The core idea is to stop treating code generation as a single text-completion call and start treating it as a pipeline of specialized agents, each with one job, passing structured data between them. We landed on a three-tier agentic cycle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1 — Intent Analysis &amp;amp; Blueprinting (the "PM" agent)
&lt;/h3&gt;

&lt;p&gt;The first agent never writes a line of PHP. Its only job is to convert the user's plain-English request into a structured JSON manifest. So a prompt like &lt;em&gt;"a plugin that lets editors flag posts for review"&lt;/em&gt; becomes something closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;"plugin_slug"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"post-review-flags"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"capabilities_required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"edit_others_posts"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"admin_menu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"callback"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"register_review_page"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"ajax"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"flag_post"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"logged_in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"nonce"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"data_layer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;"storage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"post_meta"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"meta_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"_needs_review"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"post-review-flags.php"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"includes/class-flag-controller.php"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"admin/views/review-list.php"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This manifest is the contract. By forcing the system to &lt;em&gt;declare&lt;/em&gt; its capabilities, hooks, and nonce requirements up front — before any code exists — we make security a planning decision instead of a generation accident. If the AJAX handler is declared with &lt;code&gt;"nonce": true&lt;/code&gt; here, downstream agents are obligated to honor it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2 — Context-Aware Generation (the "Developer" agent)
&lt;/h3&gt;

&lt;p&gt;The second agent compiles the manifest into actual files, one module at a time. The important architectural choice here is &lt;strong&gt;separation of concerns enforced by the file tree&lt;/strong&gt;: data-layer logic, controllers, and view templates are generated as distinct files rather than one monolith.&lt;/p&gt;

&lt;p&gt;The agent doesn't generate each file in isolation, though. It maintains an active reference map of the multi-file dependency graph (more on how that works in the context-window section below), so a function declared in the controller is known to the view that calls it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3 — The Deterministic Audit Loop (the "Security" agent)
&lt;/h3&gt;

&lt;p&gt;This is the part that actually buys the trust. After generation, a dedicated agent parses the produced code — not by "asking the model if it looks safe," but through a deterministic execution path that walks the structure and verifies invariants:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every registered AJAX/REST callback has a corresponding capability check and (where state-changing) a nonce verification.&lt;/li&gt;
&lt;li&gt;Every value originating from a superglobal passes through a sanitization function before use.&lt;/li&gt;
&lt;li&gt;Every dynamic value reaching output is escaped at the point of echo (late escaping).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If an invariant fails, the code is rejected and routed back for regeneration with the specific violation flagged. The loop closes. Nothing ships that hasn't passed the audit.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When we were designing the core engine behind the &lt;a href="https://wpcoder.ai/" rel="noopener noreferrer"&gt;WPCoder AI plugin generator&lt;/a&gt;, we realized that standard semantic embeddings weren't enough. To eliminate hallucinations, we had to anchor the generation loop within a custom-built WordPress Core parsing layer. This ensures the model dynamically validates hooks against active Core APIs before writing the final PHP strings — so a hallucinated function name fails fast instead of shipping silently.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Code Breakdown: Chatbot Output vs. Grounded Agent Output
&lt;/h2&gt;

&lt;p&gt;Architectural claims are cheap. Here's the difference made concrete.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example A — typical generic AI output.&lt;/strong&gt; Looks fine at a glance. It's a CSRF hole with a side of SQL injection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wp_ajax_save_setting'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'save_setting'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;save_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="nv"&gt;$wpdb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$wpdb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;"UPDATE {$wpdb-&amp;gt;prefix}options SET option_value = '$value' WHERE option_name = 'my_setting'"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Saved: "&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;wp_die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No nonce. No capability check. Raw &lt;code&gt;$_POST&lt;/code&gt; interpolated straight into SQL. Unescaped output. And &lt;code&gt;wp_ajax_save_setting&lt;/code&gt; only covers logged-in users by luck of the hook name — the developer never reasoned about authorization at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example B — grounded agent output.&lt;/strong&gt; Same feature, every invariant satisfied.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wp_ajax_save_setting'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'save_setting'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;save_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;check_ajax_referer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'save_setting_action'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'nonce'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;wp_send_json_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Invalid request.'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;current_user_can&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'manage_options'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;wp_send_json_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Insufficient permissions.'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sanitize_text_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;wp_unslash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;update_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'my_setting'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;wp_send_json_success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Saved: %s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;esc_html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nonce verified, capability checked, input unslashed and sanitized, output escaped, and the unsafe raw &lt;code&gt;$wpdb&lt;/code&gt; write replaced with the Options API. None of that is the model "remembering" to be careful. It's the manifest from Phase 1 being enforced by the audit in Phase 3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solving the Context Window Wall for Multi-File Plugins
&lt;/h2&gt;

&lt;p&gt;Here's the problem that bites everyone who tries to generate real multi-file projects: the model forgets.&lt;/p&gt;

&lt;p&gt;You generate &lt;code&gt;core.php&lt;/code&gt;, which registers a script handle via &lt;code&gt;wp_enqueue_script('my-admin-bundle', ...)&lt;/code&gt;. Three files later you generate the admin dashboard, and the model — having moved past that text in its context — invents a &lt;em&gt;different&lt;/em&gt; handle, or enqueues an asset that was never registered. The dashboard silently loads nothing. Classic context-window amnesia.&lt;/p&gt;

&lt;p&gt;Our fix is a &lt;strong&gt;shared internal state registry&lt;/strong&gt; that lives outside any single model call. As each sub-agent produces a file, it writes structural facts into this registry — registered script handles, declared class names, public method signatures, meta keys, hook names. Before another sub-agent generates a file that depends on those facts, the relevant registry slice is injected into its context.&lt;/p&gt;

&lt;p&gt;So the dashboard agent doesn't &lt;em&gt;recall&lt;/em&gt; the handle from a thousand tokens ago. It's &lt;em&gt;handed&lt;/em&gt; the exact string &lt;code&gt;'my-admin-bundle'&lt;/code&gt; as a hard fact, because the core agent wrote it to the registry the moment it was created. The dependency graph becomes shared state rather than something each agent has to hold in working memory. That single change is what made plugins beyond a couple of files actually coherent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shifting from Code Writer to Code Auditor
&lt;/h2&gt;

&lt;p&gt;What this architecture really changes is the developer's day.&lt;/p&gt;

&lt;p&gt;When boilerplate becomes reliable, you stop spending cognitive energy typing &lt;code&gt;add_action&lt;/code&gt;, wiring up &lt;code&gt;wp_enqueue_script&lt;/code&gt;, or hand-rolling the same nonce dance for the hundredth time. That work doesn't disappear — it gets generated correctly and handed to you for review.&lt;/p&gt;

&lt;p&gt;So the job shifts. Your primary role moves from &lt;em&gt;manual typing&lt;/em&gt; to &lt;em&gt;architectural auditing&lt;/em&gt;. You're reading the generated manifest and asking the questions only a human should own: Is this the right capability for this action? Is &lt;code&gt;post_meta&lt;/code&gt; actually the right storage layer here, or should this be a custom table? Does this hook priority interact badly with that other plugin?&lt;/p&gt;

&lt;p&gt;To be clear, this is emphatically not a replacement for developers. A system that audits its own output still produces output that a human needs to judge in context, against requirements the model can't see. What agentic generation gives you is leverage: it removes the tedious, error-prone mechanical layer so your attention lands where it's actually valuable — verification, integration, and design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion &amp;amp; Community Discussion
&lt;/h2&gt;

&lt;p&gt;Building a code-generation tool you can actually trust means giving up on the single-prompt chat script. A raw next-token predictor will always be a coin flip on security. The reliability comes from the system you wrap around it: structured blueprinting up front, modular generation with shared state, and a deterministic audit loop that refuses to ship code violating its invariants.&lt;/p&gt;

&lt;p&gt;None of these pieces are exotic on their own — they're ordinary software engineering patterns. The trick is applying them to constrain a probabilistic generator inside hard, domain-specific rules.&lt;/p&gt;

&lt;p&gt;So I'll turn it over to the community: &lt;strong&gt;how are you handling security validation when you integrate AI tools into your development stack?&lt;/strong&gt; Prompt-level guardrails, post-generation static analysis, human review gates, something else entirely? Let's talk about it in the comments.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>ai</category>
      <category>php</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Style href links in Tailwind CSS</title>
      <dc:creator>David</dc:creator>
      <pubDate>Sun, 24 Apr 2022 19:27:18 +0000</pubDate>
      <link>https://dev.to/david786/style-href-links-in-tailwind-css-244g</link>
      <guid>https://dev.to/david786/style-href-links-in-tailwind-css-244g</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://www.infopediya.com/style-href-links-in-tailwind/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;infopediya.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>tailwindcss</category>
    </item>
  </channel>
</rss>
