<?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: Tomas Grasl</title>
    <description>The latest articles on DEV Community by Tomas Grasl (@freema).</description>
    <link>https://dev.to/freema</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3961521%2F1f52971d-ed17-4ad8-840e-bb48c43c9860.jpeg</url>
      <title>DEV Community: Tomas Grasl</title>
      <link>https://dev.to/freema</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/freema"/>
    <language>en</language>
    <item>
      <title>Claude Code built the app. I refused to let it publish itself.</title>
      <dc:creator>Tomas Grasl</dc:creator>
      <pubDate>Thu, 15 Oct 2026 14:38:07 +0000</pubDate>
      <link>https://dev.to/freema/claude-code-built-the-app-i-refused-to-let-it-publish-itself-718</link>
      <guid>https://dev.to/freema/claude-code-built-the-app-i-refused-to-let-it-publish-itself-718</guid>
      <description>&lt;p&gt;Claude Code can write an app, fix a broken import, and hand me a working preview. That still does not mean it should decide when the app becomes public.&lt;/p&gt;

&lt;p&gt;This is the line I kept coming back to while building &lt;a href="https://www.drobek.app/" rel="noopener noreferrer"&gt;drobek&lt;/a&gt;. The coding agent should be fast inside the build loop. Publishing should remain an explicit human decision.&lt;/p&gt;

&lt;p&gt;Drobek launched on &lt;a href="https://www.producthunt.com/products/drobek?launch=drobek" rel="noopener noreferrer"&gt;Product Hunt today&lt;/a&gt;. The short version fits into 33 seconds:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/TGG5SZ091hg" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The useful part is the contract behind the animation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The agent gets a loop, not a magic deploy button
&lt;/h2&gt;

&lt;p&gt;For the normal path, the agent works through three MCP tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;create_app
    -&amp;gt; starter, app ID, briefing, preview URL

write_files
    -&amp;gt; immutable version, compile result, preview URL

publish
    -&amp;gt; the selected compiled version becomes public
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;create_app&lt;/code&gt; gives the agent a compiling starter and the rules of the environment. &lt;code&gt;write_files&lt;/code&gt; applies the changed text files and runs esbuild on the server.&lt;/p&gt;

&lt;p&gt;The response includes &lt;code&gt;compile.ok&lt;/code&gt;. If the build fails, it also includes the file, line, column, and compiler message. The agent can use that result for its next edit without opening another terminal or asking me to paste an error back into the conversation.&lt;/p&gt;

&lt;p&gt;A failed build is still kept as a version. The preview continues to serve the latest version that compiled successfully.&lt;/p&gt;

&lt;p&gt;This is a small detail, but I like what it does to the workflow. A broken edit becomes something the agent can inspect and repair. It does not replace the working preview, and it does not erase the attempt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compile is not the same as done
&lt;/h2&gt;

&lt;p&gt;I still open the preview.&lt;/p&gt;

&lt;p&gt;A green esbuild result tells me the imports and syntax survived. It does not tell me that the button makes sense, the mobile layout works, or a destructive action is safe.&lt;/p&gt;

&lt;p&gt;The preview URL follows the newest successful build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;slug&amp;gt;--preview.drobek.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Production stays on its current version while the agent keeps editing. When I am happy with a particular compiled version, I ask the agent to publish it.&lt;/p&gt;

&lt;p&gt;That final request matters. The Drobek agent instructions say to call &lt;code&gt;publish&lt;/code&gt; only when the user asks. The OAuth grant also separates &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;write&lt;/code&gt;, and &lt;code&gt;publish&lt;/code&gt; scopes, so a client does not need deployment access just to inspect an app.&lt;/p&gt;

&lt;p&gt;Once published, the app gets its normal address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;slug&amp;gt;.drobek.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Publishing an older compiled version moves production back to that code. It does not rewind the app's stored data. Preview and production share backend data, so I treat rollback as a code operation, not a database undo button.&lt;/p&gt;

&lt;h2&gt;
  
  
  I did not give the agent a general-purpose server
&lt;/h2&gt;

&lt;p&gt;The app's JavaScript runs in the browser. Drobek compiles and serves it.&lt;/p&gt;

&lt;p&gt;Backend capabilities come from modules installed by the server operator: authentication, data, forms, email, files, and controlled API calls. The agent can use the documented module contracts through &lt;code&gt;skill_info&lt;/code&gt;, but it cannot upload arbitrary server code or install a private npm dependency for the app.&lt;/p&gt;

&lt;p&gt;That boundary is deliberate. I wanted agents to build small tools quickly without turning every generated frontend into a new server process I now have to trust and operate.&lt;/p&gt;

&lt;p&gt;It also keeps secrets out of the generated files. For an external API, the owner enters the credential in the dashboard. The proxy uses it on the server. The browser app and the coding agent do not receive the value.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I want to keep human
&lt;/h2&gt;

&lt;p&gt;I am happy to let the agent make several versions, read compiler errors, and improve the preview. Those are fast, reversible steps with visible output.&lt;/p&gt;

&lt;p&gt;Making a URL public is different. That is where I want one boring sentence from me: publish this version.&lt;/p&gt;

&lt;p&gt;The public core is &lt;a href="https://github.com/freema/drobek" rel="noopener noreferrer"&gt;AGPL-3.0 on GitHub&lt;/a&gt;, and the hosted service is available at &lt;a href="https://www.drobek.app/" rel="noopener noreferrer"&gt;drobek.app&lt;/a&gt;. If you use Claude Code, Codex, Cursor, or Claude to build small browser apps, I would like to know where you draw the publish boundary.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Your AI-built app wants your API key. I built drobek so it never gets it.</title>
      <dc:creator>Tomas Grasl</dc:creator>
      <pubDate>Mon, 12 Oct 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/freema/your-ai-built-app-wants-your-api-key-i-built-drobek-so-it-never-gets-it-4oj5</link>
      <guid>https://dev.to/freema/your-ai-built-app-wants-your-api-key-i-built-drobek-so-it-never-gets-it-4oj5</guid>
      <description>&lt;p&gt;The UI is working. Then comes the request: connect an API, save a file, let colleagues sign in. A small vibe-coded app suddenly needs decisions about credentials and access.&lt;/p&gt;

&lt;p&gt;I built drobek with a specific boundary: the agent writes the browser app, while backend modules run on the drobek server. A proxy module can attach an API key without putting that key in the generated JavaScript. The agent can configure the connection without receiving the secret value.&lt;/p&gt;

&lt;p&gt;An app can start as a useful page and gain backend capabilities as it needs them. I still want to decide what it can reach.&lt;/p&gt;

&lt;p&gt;You can see this in a &lt;a href="https://pokedex.drobek.app/" rel="noopener noreferrer"&gt;Pokédex&lt;/a&gt;, a &lt;a href="https://pixel-zed.drobek.app/" rel="noopener noreferrer"&gt;shared Pixel Wall&lt;/a&gt;, and now a game about running the hosting company: &lt;a href="https://drobek-tycoon.drobek.app/" rel="noopener noreferrer"&gt;Drobek Tycoon&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Pokédex with no backend of its own
&lt;/h2&gt;

&lt;p&gt;Open the Pokédex and select a Pokémon. The page fetches data from PokéAPI through drobek's proxy module. Its request log makes the calls visible.&lt;/p&gt;

&lt;p&gt;The app uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;drobek&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;drobek&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;drobek&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pokeapi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v2/pokemon/25&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`PokéAPI request failed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pokemon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;pokeapi&lt;/code&gt; names a registered upstream. The server knows its base URL, which methods and paths are allowed, and which apps may call it. The browser sends a request to its own app origin; the proxy checks it and forwards the permitted call.&lt;/p&gt;

&lt;p&gt;PokéAPI does not require a key. For an upstream that does, an owner enters the secret in the dashboard and the proxy injects it on the server. MCP tools report whether a secret exists, not its value.&lt;/p&gt;

&lt;p&gt;This works for an API-backed dashboard, a weather widget, or a small interface to an existing service. Configure the upstream narrowly enough for that app. A read-only widget usually has no reason to inherit every write endpoint your API exposes.&lt;/p&gt;

&lt;p&gt;The proxy also checks destinations against SSRF rules, blocks private and reserved addresses by default, and applies request limits. Those checks matter because “fetch this URL for me” becomes dangerous when the server can reach things the browser cannot. Operators can explicitly allow internal hosts on their own instance; that is an access decision to review.&lt;/p&gt;

&lt;p&gt;Keeping a key on the server protects the key itself. It does not make the data returned by the API private. If you permit public callers to read sensitive results, they can still read them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basic modules, with jobs for each
&lt;/h2&gt;

&lt;p&gt;These are the built-in pieces an app can use when the operator enables them:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Module&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;A small app that needs it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;auth&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;App-user sign-in, allowlists, roles and sessions&lt;/td&gt;
&lt;td&gt;A tool only colleagues should use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stored records with rules for each operation&lt;/td&gt;
&lt;td&gt;Saved estimates, requests, or shared task lists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;files&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User uploads, access rules, type checks and quotas&lt;/td&gt;
&lt;td&gt;Avatars or attachments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;forms&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stores submissions and emails the app's owners&lt;/td&gt;
&lt;td&gt;A contact form or feedback box&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;email&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Notifications to the app's owners&lt;/td&gt;
&lt;td&gt;Reporting an item for moderation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proxy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Controlled requests to registered APIs, with secrets added server-side&lt;/td&gt;
&lt;td&gt;The Pokédex, or an API-backed internal dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The agent can inspect a module through &lt;code&gt;skill_info('&amp;lt;name&amp;gt;')&lt;/code&gt;: its SDK types, configuration, limits and examples. It configures an installed module with &lt;code&gt;configure_module&lt;/code&gt;. Changes that broaden access can require confirmation in the dashboard.&lt;/p&gt;

&lt;p&gt;The app calls the SDK. Access rules are enforced on the server. Hiding a button in React would not protect the corresponding operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The modules behind Pixel Wall
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pixel-zed.drobek.app/" rel="noopener noreferrer"&gt;Pixel Wall&lt;/a&gt; is a shared 64 × 40 canvas. Visitors can look; signed-in people can paint. Each stroke becomes a database record.&lt;/p&gt;

&lt;p&gt;Its module-status panel explains the rest of the app: &lt;code&gt;auth&lt;/code&gt; handles email-code sign-in, &lt;code&gt;data&lt;/code&gt; stores strokes and profiles, and &lt;code&gt;files&lt;/code&gt; handles painter avatars. The message form uses &lt;code&gt;forms&lt;/code&gt;; reporting a stroke uses &lt;code&gt;email&lt;/code&gt; to notify admins.&lt;/p&gt;

&lt;p&gt;The visit and stroke counters come from &lt;code&gt;counter&lt;/code&gt;, a separate &lt;a href="https://github.com/freema/drobek-module-counter" rel="noopener noreferrer"&gt;example module&lt;/a&gt;. It is not one of the six built-ins. It sits alongside the standard modules and has its own SDK methods.&lt;/p&gt;

&lt;p&gt;You can inspect the wall without signing in. Uploading an avatar or painting requires the relevant user action; a module appearing in the panel does not mean every operation has already run.&lt;/p&gt;

&lt;h2&gt;
  
  
  A game about running drobek
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://drobek-tycoon.drobek.app/" rel="noopener noreferrer"&gt;Drobek Tycoon&lt;/a&gt; puts you in charge of a small hosting company. You build servers, hire a team, attract customers and handle incidents across three levels. Salaries, power and rent come out of the company budget.&lt;/p&gt;

&lt;p&gt;The game keeps your progress in the browser without requiring sign-in. It also has a shared leaderboard for the best company value at each level, and its gallery entry lists the &lt;code&gt;data&lt;/code&gt; module. Local game progress and a shared leaderboard serve different purposes; using a backend for one doesn't mean moving the whole game onto the server.&lt;/p&gt;

&lt;p&gt;That gives the module model another use beyond forms and internal tools: a browser game with a shared result to compare after a run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your company's SSO can fit here too
&lt;/h2&gt;

&lt;p&gt;Suppose your company has its own identity provider and expects internal apps to use it. On your own drobek instance, you can install a module that contributes a sign-in provider through the &lt;code&gt;auth.provider&lt;/code&gt; extension point.&lt;/p&gt;

&lt;p&gt;The contract has &lt;code&gt;defineAuthProvider&lt;/code&gt;, with &lt;code&gt;begin&lt;/code&gt; and &lt;code&gt;callback&lt;/code&gt; methods. The provider implements and validates the identity-provider exchange. It returns a verified identity; drobek's auth module retains the allowlist, roles, sessions and audit trail. The provider's client secret stays on the server.&lt;/p&gt;

&lt;p&gt;That gives you a place to implement company-specific OAuth/OIDC sign-in. It is an extension contract, not a claim that an adapter for every corporate provider ships ready to use. Also, this extension concerns people signing into the hosted apps. Dashboard administrator login is a separate concern.&lt;/p&gt;

&lt;p&gt;For a different requirement, such as an ERP operation with custom validation, write a platform module with its own routes and SDK slice. The scaffold starts with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create drobek-module@latest erp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A module can carry configuration, migrations, secrets and documentation for the agent. The operator installs it and selects the modules the instance runs with &lt;code&gt;DROBEK_MODULES&lt;/code&gt;. You can also replace a built-in module with your own package implementing the appropriate contract.&lt;/p&gt;

&lt;p&gt;The hosted drobek.app service uses the same public core image available to self-hosters. I keep its deployment and service-specific pieces around that core in my separate operations repository, drobek-web. The public &lt;a href="https://www.drobek.app/docs/modules" rel="noopener noreferrer"&gt;module documentation&lt;/a&gt; explains the extension mechanisms you can use for your own instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I put the security boundary
&lt;/h2&gt;

&lt;p&gt;I don't want every small generated app shipping its own server code into the hosting process. In drobek, the server compiles and serves app files; the app's JavaScript runs in the browser. Backend execution belongs to modules chosen by the operator. Apps also have separate origins from the dashboard.&lt;/p&gt;

&lt;p&gt;We can maintain and patch these backend modules centrally. When a module needs a fix, its operator updates that module or the core version containing it. Self-hosters control their own update schedule.&lt;/p&gt;

&lt;p&gt;Custom modules are trusted server code. They run in the server process with database access, not in an isolated plugin sandbox. Install and review them accordingly. The architecture reduces what an app author can put on the server; it does not turn arbitrary module code into safe code.&lt;/p&gt;

&lt;p&gt;For a new app, choose the smallest set of capabilities that does the job. Try the &lt;a href="https://pokedex.drobek.app/" rel="noopener noreferrer"&gt;Pokédex&lt;/a&gt; to see a proxy request, or the &lt;a href="https://pixel-zed.drobek.app/" rel="noopener noreferrer"&gt;Pixel Wall&lt;/a&gt; to see several modules working together. If your backend requirement is more specific, the &lt;a href="https://github.com/freema/drobek" rel="noopener noreferrer"&gt;open-source core&lt;/a&gt; and module contract give you somewhere to implement it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>opensource</category>
      <category>security</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>The AI artifact was useful. Then someone wanted to keep using it.</title>
      <dc:creator>Tomas Grasl</dc:creator>
      <pubDate>Fri, 09 Oct 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/freema/the-ai-artifact-was-useful-then-someone-wanted-to-keep-using-it-2e80</link>
      <guid>https://dev.to/freema/the-ai-artifact-was-useful-then-someone-wanted-to-keep-using-it-2e80</guid>
      <description>&lt;p&gt;A calculator comes out of an agent session. Someone sends the link to a colleague. The colleague wants to change one assumption, save a scenario, and come back next week.&lt;/p&gt;

&lt;p&gt;At that point, there are some ordinary app decisions to make. Where does the current version live? Are changes saved only in this browser? Who can see the saved data? Can you update the calculation without immediately changing the page everyone uses?&lt;/p&gt;

&lt;p&gt;drobek gives these small browser apps a place to run.&lt;/p&gt;

&lt;p&gt;I previously wrote about &lt;a href="https://dev.to/freema/i-finally-figured-out-what-claude-artifacts-are-actually-for-581o"&gt;using a Claude Artifact as a changing team document&lt;/a&gt;. The value in that example was a link colleagues could return to as the work changed. Here I want to look at a browser artifact that needs app behavior: inputs, saved records, and controlled updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the first version small
&lt;/h2&gt;

&lt;p&gt;Take a simple planning calculator. It estimates how many working days a task needs from a total number of hours and the hours available each day.&lt;/p&gt;

&lt;p&gt;For the example, 24 hours at 6 hours per day is 4 days. Those are illustrative inputs, not measurements from a project. The calculation is small enough to check while we work on the app around it.&lt;/p&gt;

&lt;p&gt;The first version can stay entirely in the browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;estimateDays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;totalHours&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hoursPerDay&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="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isFinite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;totalHours&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;totalHours&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter a non-negative number of hours.&lt;/span&gt;&lt;span class="dl"&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="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isFinite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hoursPerDay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;hoursPerDay&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hours per day must be greater than zero.&lt;/span&gt;&lt;span class="dl"&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;return&lt;/span&gt; &lt;span class="nx"&gt;totalHours&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;hoursPerDay&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;That is an arithmetic estimate, not a project forecast. It doesn't model weekends, dependencies, or people getting interrupted. The page should say what the number means instead of displaying it with more confidence than the inputs justify.&lt;/p&gt;

&lt;p&gt;If the artifact already has usable HTML or React source, give that source to your coding agent and ask it to adapt the app for drobek. Don't assume an artifact share URL is an import format or that platform-specific features transfer unchanged.&lt;/p&gt;

&lt;p&gt;A bounded request helps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Adapt this calculator's source for drobek.
Preserve the calculation and input validation.
Remove dependencies on the original artifact environment.
Keep all data in the browser for this first version.
Return the preview URL and explain any behavior you changed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent can create the app, write the files, and work from compilation feedback. You still try the actual page. A build can succeed while a text input, error message, or calculation is wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add persistence when there is something worth saving
&lt;/h2&gt;

&lt;p&gt;If people only need a quick calculation, stop there. A saved-record feature adds access decisions as well as convenience.&lt;/p&gt;

&lt;p&gt;For private saved scenarios, one possible design is to add the auth and data modules. A signed-in user creates a scenario and can later retrieve their own records. The data collection rules can be:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"owner|admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"create"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"update"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"owner|admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"delete"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"owner|admin"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the &lt;code&gt;rules&lt;/code&gt; object for a configured collection, not the complete module configuration. Here, &lt;code&gt;owner&lt;/code&gt; refers to the record owner. &lt;code&gt;admin&lt;/code&gt; also has access, so describe that honestly if the interface calls scenarios private.&lt;/p&gt;

&lt;p&gt;In the frontend, after the collection and authentication are configured, saving the example could use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;drobek&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;drobek&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;drobek&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scenarios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Example estimate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;totalHours&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;hoursPerDay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use a schema for the stored fields and validate the inputs on the page too. The &lt;a href="https://github.com/freema/drobek/blob/v0.5.0/docs/MODULES.md#the-built-in-data-module" rel="noopener noreferrer"&gt;data module&lt;/a&gt; applies its operation rules on the server. Keeping records out of the rendered list is not access control.&lt;/p&gt;

&lt;p&gt;If the next request is shared team scenarios, reconsider those rules. Making every signed-in user a reader is a different product decision from letting each person see their own records. An agent should explain that change before it is confirmed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat updates as versions
&lt;/h2&gt;

&lt;p&gt;Suppose the next change adds a maximum daily capacity or another output. The agent writes a new version, you inspect the preview, and then choose whether to publish it.&lt;/p&gt;

&lt;p&gt;The production URL can continue serving the earlier published code while you work. If the new code is wrong, publishing an older version restores that code.&lt;/p&gt;

&lt;p&gt;But the app's records are shared between preview and production. A preview test that edits real saved scenarios edits real data. Code rollback doesn't undo those writes. Use a separate app when you need an isolated data experiment.&lt;/p&gt;

&lt;p&gt;For images or other binary assets, drobek has a separate asset-upload flow. They do not belong in &lt;code&gt;write_files&lt;/code&gt; as a giant base64 string. Review which assets the original artifact depended on and provide replacements where necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  A maintained app still needs an owner
&lt;/h2&gt;

&lt;p&gt;Hosting the result doesn't make the model's original analysis correct. If an artifact contains an estimate, a recommendation, or a data-derived conclusion, keep its assumptions and source dates visible. Update the evidence as well as the UI.&lt;/p&gt;

&lt;p&gt;And some artifacts should remain documents. If the main job is preserving a decision and its reasoning, a repository or document system may be the better home. The calculator example earns an app because people interact with it and may need persistent state.&lt;/p&gt;

&lt;p&gt;In drobek, the agent's edits compile into a preview you can check before changing the production URL colleagues use. You can start with a browser-only tool and add a backend module when a specific need appears. The &lt;a href="https://www.drobek.app/gallery" rel="noopener noreferrer"&gt;gallery&lt;/a&gt; shows the scale of apps I have in mind, and the &lt;a href="https://github.com/freema/drobek" rel="noopener noreferrer"&gt;core is open source&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Drobek: bring your own AI agent, build an app, then take it outside</title>
      <dc:creator>Tomas Grasl</dc:creator>
      <pubDate>Fri, 09 Oct 2026 06:49:02 +0000</pubDate>
      <link>https://dev.to/freema/drobek-bring-your-own-ai-agent-build-an-app-then-take-it-outside-24hl</link>
      <guid>https://dev.to/freema/drobek-bring-your-own-ai-agent-build-an-app-then-take-it-outside-24hl</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I built &lt;a href="https://www.drobek.app/" rel="noopener noreferrer"&gt;Drobek&lt;/a&gt;, an open-source platform where your own AI agent can create a small web app, give you a preview, and publish it at a URL you can share.&lt;/p&gt;

&lt;p&gt;Yes, this is my project. The practical idea is simple: when someone asks an agent for a useful little app, they should be able to keep using it outside the conversation.&lt;/p&gt;

&lt;p&gt;My take on Touch Grass is that anyone should be able to make a small website for something happening outside. Organising a walk, a picnic, an outdoor workshop, or an evening of stargazing? Ask your own AI agent for an event page with the meeting point, programme, what to bring, and a registration form. Drobek gives that page a home and a URL you can send to the people coming.&lt;/p&gt;

&lt;p&gt;For example, you could start with: "Build a page for our Saturday stargazing meetup. Show the meeting place and programme, add a packing checklist and a signup form, and give me a preview before publishing." That is an example prompt, not a claim that this particular event has already happened. The forms module can store registrations and email the organiser; the app's design and content are yours to change.&lt;/p&gt;

&lt;p&gt;The useful part is having a page that fits your event. Make it, share it, then go outside together.&lt;/p&gt;

&lt;p&gt;There is already a concrete example running on it: &lt;a href="https://podzimn-obloha.drobek.app/" rel="noopener noreferrer"&gt;Podzimní obloha, the autumn sky&lt;/a&gt;. It has Czech and English text, a Moon phase display, an autumn observing calendar, and a short guide to constellations. It even tells you to grab a blanket and tea and let your eyes adjust to the dark.&lt;/p&gt;

&lt;p&gt;You can read what to look for, then put the phone away. That is the sort of app I want people to be able to make their own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://podzimn-obloha.drobek.app/" rel="noopener noreferrer"&gt;Open the autumn sky app&lt;/a&gt; to try an existing example. It demonstrates what people can publish with Drobek; it predates this challenge week.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fec1d2b1ctig0y8efeua6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fec1d2b1ctig0y8efeua6.png" alt="The autumn sky app running on Drobek" width="500" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The short video below introduces the platform and its agent-to-app workflow. It is an animated product explainer, rather than a recording of a timed build.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/TGG5SZ091hg" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;You can also &lt;a href="https://www.drobek.app/gallery" rel="noopener noreferrer"&gt;browse the app gallery&lt;/a&gt; or &lt;a href="https://drobek.app/build-with-your-agent" rel="noopener noreferrer"&gt;connect your agent&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/freema/drobek" rel="noopener noreferrer"&gt;The public Drobek repository&lt;/a&gt; contains the server, dashboard, MCP interface, SDK, platform modules, and self-hosting documentation. The core is AGPL-3.0. The &lt;a href="https://github.com/freema/drobek-plugin" rel="noopener noreferrer"&gt;agent plugins&lt;/a&gt; are MIT.&lt;/p&gt;

&lt;p&gt;The repository has older history. The main rework I am putting forward here started on October 5; I have kept that history visible. The &lt;a href="https://github.com/freema/drobek/blob/main/CHANGELOG.md" rel="noopener noreferrer"&gt;changelog&lt;/a&gt; documents the work, including version-history tools and streaming proxy support on October 5, followed by preview feedback, live data subscriptions, webhooks, and app analytics on October 6.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Drobek is the open-source tooling part of the AI workflow. The agent connects over MCP and calls tools such as &lt;code&gt;create_app&lt;/code&gt;, &lt;code&gt;write_files&lt;/code&gt;, and &lt;code&gt;publish&lt;/code&gt;. The model runs in the agent's environment; Drobek does not bundle a model or require one particular provider.&lt;/p&gt;

&lt;p&gt;When the agent writes files, the server compiles them with esbuild and returns compilation errors in the response. The agent can fix the code and try again. A successful build gets a preview URL. I can open it, test it, and decide when to publish that version.&lt;/p&gt;

&lt;p&gt;The October work adds more to that loop. People can leave feedback on a preview, and the agent can retrieve and resolve it. It can inspect version history and keep a version for later. Apps can subscribe to data changes, and the proxy supports streaming from configured upstreams.&lt;/p&gt;

&lt;p&gt;The app itself runs in the browser. Backend modules provide things like authentication, stored data, forms, files, and API access. Owners enter API secrets in the dashboard, and the proxy uses them server-side. The agent does not need the secret values.&lt;/p&gt;

&lt;p&gt;There are deliberate limits: arbitrary app server code and per-app &lt;code&gt;npm install&lt;/code&gt; are outside this model. A successful compile still needs a functional check. Preview and production also share the app's backend data, so rolling back code does not roll back records.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;The part between an AI-generated app and a usable URL is code you can inspect and run yourself. You can change the platform, add a backend module, or host it on your own server with Postgres, Redis, and Caddy.&lt;/p&gt;

&lt;p&gt;MCP gives the agent a documented interface to that platform. The build loop and deployment do not depend on a hidden conversation inside one app builder. The choice of agent and model stays with the person building the app, including whether they use an open-source agent setup.&lt;/p&gt;

&lt;p&gt;Self-hosting also gives you control over where the platform stores app data. The connected agent and its model provider have their own data handling and costs; choosing your own host does not make those disappear.&lt;/p&gt;

&lt;p&gt;For this submission, the open contribution is Drobek itself: the infrastructure people can use to turn an idea into their own small app. &lt;a href="https://podzimn-obloha.drobek.app/" rel="noopener noreferrer"&gt;Try the autumn sky example&lt;/a&gt;, and the &lt;a href="https://github.com/freema/drobek" rel="noopener noreferrer"&gt;source and self-hosting instructions&lt;/a&gt; are there if you want to build on it.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Don't blame AI before you check your architecture</title>
      <dc:creator>Tomas Grasl</dc:creator>
      <pubDate>Wed, 07 Oct 2026 19:18:43 +0000</pubDate>
      <link>https://dev.to/freema/dont-blame-ai-before-you-check-your-architecture-20hd</link>
      <guid>https://dev.to/freema/dont-blame-ai-before-you-check-your-architecture-20hd</guid>
      <description>&lt;p&gt;Give an AI coding agent a small task: add voucher activation to an existing subscription system.&lt;/p&gt;

&lt;p&gt;The generated code might look reasonable. The endpoint works, the response is correct, and the happy-path test passes. But the voucher rules now live in a controller, next to a database query and a call to a payment provider.&lt;/p&gt;

&lt;p&gt;Was the model bad? Maybe. I would also look at the examples the repository gave it.&lt;/p&gt;

&lt;p&gt;If similar rules already live in controllers, services and persistence callbacks, “follow the existing architecture” leaves quite a lot open to interpretation. A human joining that project would have questions too.&lt;/p&gt;

&lt;p&gt;This is where I think architecture deserves more attention in AI-assisted development. The structure of a project supplies context for the next change. Clear responsibilities make that change easier to describe and its mistakes easier to detect.&lt;/p&gt;

&lt;p&gt;PHP and Symfony make a useful example here. PHP sometimes gets dismissed before the discussion even starts, but typed interfaces, dependency injection and static analysis give us plenty to work with.&lt;/p&gt;

&lt;h2&gt;
  
  
  One feature, several places to put it
&lt;/h2&gt;

&lt;p&gt;Consider an illustrative subscription application. Customers can already buy a subscription through a payment provider. Now we want this feature:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Allow a customer to activate a subscription with a voucher.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sentence leaves business decisions open. Does the voucher cover a particular plan? Can it be redeemed more than once? What happens if the customer already has an active subscription?&lt;/p&gt;

&lt;p&gt;For this example, let's choose explicit rules: a voucher activates one eligible subscription, expires at a defined time, and can be redeemed once. An already-active subscription must be rejected. Voucher activation must not charge the customer.&lt;/p&gt;

&lt;p&gt;These are invented requirements for the example, not a report from a production system.&lt;/p&gt;

&lt;p&gt;Suppose the relevant code looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
  Controller/SubscriptionController.php
  Service/SubscriptionManager.php
  Service/PaymentHelper.php
  Entity/Subscription.php
  Repository/SubscriptionRepository.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is nothing inherently wrong with these directories. The difficulty starts when responsibilities overlap: the controller checks eligibility, the manager changes status, and the payment helper also persists subscriptions.&lt;/p&gt;

&lt;p&gt;Now an agent has several plausible places to add voucher validation. Reading more files may reveal the intended convention. It may also reveal three incompatible conventions.&lt;/p&gt;

&lt;p&gt;The prompt can explain the desired design, but that explanation is doing work the codebase could help with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give the change a clear home
&lt;/h2&gt;

&lt;p&gt;For an application with enough business logic to justify it, I would consider a structure like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/Subscription/
  Domain/
    Subscription.php
    Voucher.php
    SubscriptionRepository.php
    VoucherRepository.php
  Application/
    CreateSubscription.php
    ActivateSubscriptionWithVoucher.php
  Infrastructure/
    DoctrineSubscriptionRepository.php
    DoctrineVoucherRepository.php
    StripePaymentGateway.php
  UI/
    Http/ActivateSubscriptionWithVoucherController.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application use case coordinates activation. Domain objects or a domain policy enforce the business rules. Repository interfaces describe the persistence operations those rules and use cases need. Doctrine implements them at the edge.&lt;/p&gt;

&lt;p&gt;The HTTP controller handles the request, invokes the use case and maps the result to a response. It should not become a second implementation of voucher eligibility.&lt;/p&gt;

&lt;p&gt;This follows the separation behind Alistair Cockburn's &lt;a href="https://alistair.cockburn.us/hexagonal-architecture" rel="noopener noreferrer"&gt;hexagonal architecture&lt;/a&gt;: the application communicates with external systems through ports and adapters. The folder names are our choice; the useful property is that business behavior can be exercised independently of the HTTP and database adapters.&lt;/p&gt;

&lt;p&gt;For our example, the intended source-code dependencies are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UI ------------&amp;gt; Application ------------&amp;gt; Domain
Infrastructure -------------------------&amp;gt; Domain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The infrastructure classes implement interfaces defined inward. The application's wiring connects those interfaces to concrete adapters.&lt;/p&gt;

&lt;p&gt;At runtime, a use case can call a Doctrine-backed repository through an interface. Its source code still does not need to import Doctrine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://symfony.com/doc/current/service_container/autowiring.html#working-with-interfaces" rel="noopener noreferrer"&gt;Symfony's autowiring and interface aliases&lt;/a&gt; support this wiring. A constructor can request an interface while the container supplies the configured implementation. Symfony does not choose the business boundaries for us.&lt;/p&gt;

&lt;p&gt;An agent working on voucher activation now has a named use case to inspect and a dependency rule to follow. Reviewers can ask precise questions: why did this patch add an HTTP dependency to the domain? Why is a payment gateway involved in a feature that must not charge anyone?&lt;/p&gt;

&lt;h2&gt;
  
  
  The names need to mean something
&lt;/h2&gt;

&lt;p&gt;DDD is useful here because of the shared language it asks us to establish.&lt;/p&gt;

&lt;p&gt;In this example, a voucher redeems an entitlement. A payment records a financial transaction. Both may lead to an active subscription, but they have different rules. Calling both operations &lt;code&gt;process()&lt;/code&gt; hides a distinction the implementation needs to preserve.&lt;/p&gt;

&lt;p&gt;Names such as &lt;code&gt;ActivateSubscriptionWithVoucher&lt;/code&gt; put that distinction where both a developer and an agent will encounter it. &lt;code&gt;CommonService&lt;/code&gt; gives them less to work with.&lt;/p&gt;

&lt;p&gt;The names still need definitions. If “customer” means an account in one module and a billing contact in another, a consistent-looking class name can conceal a mismatch. A short glossary and examples of the intended behavior are useful companions to the code.&lt;/p&gt;

&lt;p&gt;My expectation is that this reduces the decisions an agent has to infer. I am not claiming a measured improvement in model accuracy from renaming classes or introducing DDD.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make violations visible
&lt;/h2&gt;

&lt;p&gt;A directory named &lt;code&gt;Domain&lt;/code&gt; cannot stop someone importing an HTTP client into it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://deptrac.github.io/deptrac/" rel="noopener noreferrer"&gt;Deptrac&lt;/a&gt; can check PHP dependencies against configured layers and allowed relationships, including in CI. For the structure above, I would forbid domain dependencies on application, infrastructure and UI code, and reject application imports from infrastructure or UI.&lt;/p&gt;

&lt;p&gt;The configuration needs to cover the relevant namespaces and external packages. Unclassified code can leave gaps. A green architecture check only proves that the dependencies it analyzed satisfy the rules we configured.&lt;/p&gt;

&lt;p&gt;Behavior requires separate tests. For voucher activation, I would want tests showing that an expired voucher fails, an eligible voucher activates the subscription, and repeated redemption does not grant another entitlement. The application test should also verify that the payment gateway is not called.&lt;/p&gt;

&lt;p&gt;There is a persistence problem hiding in that last requirement. Two requests can read the same unused voucher before either writes its result. A domain method that checks &lt;code&gt;isRedeemed()&lt;/code&gt; cannot prevent that race by itself.&lt;/p&gt;

&lt;p&gt;The persistence implementation needs an appropriate atomic update or locking strategy, with subscription activation and redemption committed consistently. I would include an integration test for competing redemption attempts. Neat layers do not settle transaction semantics.&lt;/p&gt;

&lt;p&gt;These checks give the agent concrete feedback during a change. A developer still needs to decide whether the tests express the right behavior and inspect changes to the checks themselves. An agent that “fixes” a failing dependency rule by weakening it has changed the architecture contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep repository instructions short and connected to code
&lt;/h2&gt;

&lt;p&gt;An &lt;code&gt;AGENTS.md&lt;/code&gt; or &lt;code&gt;CLAUDE.md&lt;/code&gt; file can tell an agent where to begin. For this example, useful instructions would point to the subscription module, explain the dependency direction and identify the commands for running its checks.&lt;/p&gt;

&lt;p&gt;I would also include the rule that voucher activation never charges a customer, with a link to the test that verifies it. That is much more actionable than “always write clean code.”&lt;/p&gt;

&lt;p&gt;Then I would ask the agent to inspect the existing activation path and propose the files it expects to change before editing. Its plan should account for the use case, domain rules, persistence behavior and tests. If it proposes putting the whole feature in the controller, there is something concrete to discuss before the patch grows.&lt;/p&gt;

&lt;p&gt;Research gives a useful, narrower reference point here. Microsoft's &lt;a href="https://arxiv.org/abs/2309.12499" rel="noopener noreferrer"&gt;CodePlan paper&lt;/a&gt; treats repository-level coding as a planning problem and combines dependency analysis with analysis of how changes affect other code. Its evaluation covers C# package migrations and Python edits. It is not evidence that hexagonal PHP applications automatically produce better AI results.&lt;/p&gt;

&lt;p&gt;My practical inference is that dependencies deserve to be explicit enough to inspect and validate. The article's voucher example illustrates that idea; it is not a benchmark.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the boundary that keeps breaking
&lt;/h2&gt;

&lt;p&gt;I would not introduce this entire structure into a small CRUD application just to accommodate an AI agent. Every additional interface and layer creates work for the people maintaining it.&lt;/p&gt;

&lt;p&gt;For an existing project, start with one recurring problem. If controllers keep acquiring subscription rules, move one coherent use case behind a clear entry point. Name its business concepts, add tests for the rules, and enforce the dependency boundary that matters.&lt;/p&gt;

&lt;p&gt;Try the next related change with the agent. Look at whether its patch uses that entry point, whether the tests catch the mistakes you care about, and how much explanation the review still needs. Expand the structure only when it earns its place.&lt;/p&gt;

&lt;p&gt;A better model or a clearer prompt may help. I would also check what the repository is teaching it: where behavior belongs, which dependencies are allowed, and how a wrong change gets rejected.&lt;/p&gt;

&lt;p&gt;For the voucher feature, success means the business rules have one clear home, redemption is safe under competing requests, and the next developer can find all of it. That remains our responsibility when an agent writes the patch.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>php</category>
      <category>symfony</category>
    </item>
    <item>
      <title>Your own agent, your own server: self-hosting drobek</title>
      <dc:creator>Tomas Grasl</dc:creator>
      <pubDate>Wed, 07 Oct 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/freema/your-own-agent-your-own-server-self-hosting-drobek-2n3j</link>
      <guid>https://dev.to/freema/your-own-agent-your-own-server-self-hosting-drobek-2n3j</guid>
      <description>&lt;p&gt;The useful part of self-hosting drobek is deciding where those small AI-built apps live. Your colleagues can keep using their own coding agents. You run the place that compiles the frontend, serves it, and provides the backend modules.&lt;/p&gt;

&lt;p&gt;You also take responsibility for the server, email delivery, backups, and updates. The setup below uses the v0.5.0 deployment files.&lt;/p&gt;

&lt;p&gt;The stack is one drobek image plus Postgres, Redis, and Caddy. Caddy handles HTTPS. Only Caddy exposes public ports in the production Compose setup; the app server and databases stay on the internal network.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with DNS and email
&lt;/h2&gt;

&lt;p&gt;For this release's prebuilt image, use a Linux amd64 server. Have Docker Engine, Compose v2, Git, OpenSSL, and Task v3 available. The repository's &lt;a href="https://github.com/freema/drobek/blob/v0.5.0/docs/SELF-HOSTING.md" rel="noopener noreferrer"&gt;self-hosting guide&lt;/a&gt; includes installation steps for a clean Ubuntu 24.04 host.&lt;/p&gt;

&lt;p&gt;Check the tools before proceeding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose version
task &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need ports 80 and 443 reachable and two DNS records pointing at the server:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Record&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Dashboard A/AAAA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;drobek.example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wildcard app A/AAAA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;*.apps.example.net&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Replace the example domains with domains you control. The separate registrable domain for apps is deliberate: those apps contain code generated for their owners and should be separated from the dashboard.&lt;/p&gt;

&lt;p&gt;You also need working email delivery. This walkthrough uses SMTP. drobek sends sign-in codes by email, so a healthy web process with broken SMTP still leaves you unable to complete that login flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get matching deployment files and image
&lt;/h2&gt;

&lt;p&gt;On a new server, in a directory you can manage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--branch&lt;/span&gt; v0.5.0 &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
  https://github.com/freema/drobek.git drobek
&lt;span class="nb"&gt;cd &lt;/span&gt;drobek
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pinning the checkout and image to the same release makes the setup easier to reason about. These commands use v0.5.0; check the release notes before selecting a newer version.&lt;/p&gt;

&lt;p&gt;Initialize the configuration with your real hosts and email settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;drobek.example.com &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;APPS_DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;apps.example.net &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;DROBEK_IMAGE_TAG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;v0.5.0 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;TLS_ACME_EMAIL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;you@example.com &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;SUPERADMIN_EMAIL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;you@example.com &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;SMTP_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;smtp.example.com &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;SMTP_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;587 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;SMTP_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no-reply@example.com &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;EMAIL_FROM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no-reply@example.com &lt;span class="se"&gt;\&lt;/span&gt;
task selfhost:init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The initializer generates secrets, writes &lt;code&gt;.env.production&lt;/code&gt; with mode 600, and renders the Caddy configuration. It can run again without replacing existing secrets.&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;.env.production&lt;/code&gt; to supply &lt;code&gt;SMTP_PASS&lt;/code&gt;. Keep the password out of a pasted shell command and out of Git. Follow the file's quoting instructions if a value contains characters such as &lt;code&gt;$&lt;/code&gt;, &lt;code&gt;#&lt;/code&gt;, or spaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decide who can publish
&lt;/h2&gt;

&lt;p&gt;Before sharing the instance, choose who may publish. The default publishing mode is &lt;code&gt;open&lt;/code&gt;. If this is a server where workspaces should need operator approval before publishing, set these values in &lt;code&gt;.env.production&lt;/code&gt; before starting it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PUBLISH_APPROVAL=approval
OPERATOR_EMAIL=you@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This controls publishing. It does not turn registration off or block creation and previewing of apps. Existing live content also isn't removed just because a workspace is later blocked from publishing.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/freema/drobek/blob/v0.5.0/docs/SELF-HOSTING.md#publish-approval" rel="noopener noreferrer"&gt;publishing policy documentation&lt;/a&gt; explains the separate workspace states and takedown controls. Choose the behavior that matches how you intend to run the instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bring the stack up
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nt"&gt;--env-file&lt;/span&gt; .env.production &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.production.yaml up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first start pulls the images and applies database migrations. Check the dashboard endpoints using your actual hostname:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsS&lt;/span&gt; https://drobek.example.com/healthz
curl &lt;span class="nt"&gt;-fsS&lt;/span&gt; https://drobek.example.com/api/version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The health response should report the database and Redis as up. The version endpoint lets you check which image is serving requests. These are expected checks, not output from your server until you run them.&lt;/p&gt;

&lt;p&gt;With a real domain, the default TLS mode issues app certificates on demand through Caddy. The first request to a new app hostname can take longer while its certificate is issued. Both the DNS wildcard and reachable validation ports matter here.&lt;/p&gt;

&lt;p&gt;Open the dashboard, sign in using &lt;code&gt;SUPERADMIN_EMAIL&lt;/code&gt;, and enter the code sent by email.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect an agent and build something small
&lt;/h2&gt;

&lt;p&gt;For Claude Code, the documented connection is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http drobek &lt;span class="se"&gt;\&lt;/span&gt;
  https://drobek.example.com/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Complete its OAuth flow. To make an app live, the connection needs the publishing scope as well as the relevant workspace permission.&lt;/p&gt;

&lt;p&gt;For a first app, keep the request easy to inspect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build a tip calculator on drobek.
Use a bill amount, tip percentage, and number of people.
Handle zero people and invalid amounts without dividing by zero.
Give me the preview URL before publishing.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the preview and try the edge cases. Ask the agent to publish only once the result behaves as expected. The returned production URL belongs to the chosen published version; later edits can stay in preview.&lt;/p&gt;

&lt;p&gt;This is frontend hosting with platform modules. It doesn't mean the agent now has permission to deploy arbitrary server code to your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Back up the keys as well as the data
&lt;/h2&gt;

&lt;p&gt;From the checkout, the supported backup task is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;task backup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It creates an archive containing the database and the relevant file, asset, module, and Caddy data. Copy the archives off the host.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;.env.production&lt;/code&gt; is not in that archive.&lt;/strong&gt; Keep a protected copy separately. In particular, the master key is needed to decrypt restored upstream secrets. A database backup without the corresponding key isn't a complete recovery plan.&lt;/p&gt;

&lt;p&gt;Redis is not backed up by this task, so users sign in again after a restore on a fresh machine. The backup runs online; if you need a quiesced snapshot across database and file operations, follow the guide's instructions for stopping writes.&lt;/p&gt;

&lt;p&gt;Rehearse restoring to a separate empty stack before depending on the backups. The &lt;a href="https://github.com/freema/drobek/blob/v0.5.0/docs/SELF-HOSTING.md#backup-and-restore" rel="noopener noreferrer"&gt;restore procedure&lt;/a&gt; checks the archive and key and normally refuses a non-empty database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep upgrades deliberate
&lt;/h2&gt;

&lt;p&gt;For an update, read the target release notes, move the deployment checkout to that tag, and set the matching &lt;code&gt;DROBEK_IMAGE_TAG&lt;/code&gt; in &lt;code&gt;.env.production&lt;/code&gt;. Then use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;task selfhost:upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The task takes a backup and runs migrations before bringing the updated application up. An app-code rollback and a server/database rollback are different operations; don't assume an older server image can understand a newer database schema.&lt;/p&gt;

&lt;p&gt;Once the first app is live, keep checking email delivery and backup recovery. Review access as people join, and keep the server updated. The &lt;a href="https://github.com/freema/drobek" rel="noopener noreferrer"&gt;repository&lt;/a&gt; contains the deployment files and the full guide when you need custom domains, other TLS modes, or external modules.&lt;/p&gt;

</description>
      <category>selfhosted</category>
      <category>docker</category>
      <category>opensource</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Stop prompting from scratch. Copy a drobek app and change the useful part.</title>
      <dc:creator>Tomas Grasl</dc:creator>
      <pubDate>Mon, 05 Oct 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/freema/stop-prompting-from-scratch-copy-a-drobek-app-and-change-the-useful-part-4n3o</link>
      <guid>https://dev.to/freema/stop-prompting-from-scratch-copy-a-drobek-app-and-change-the-useful-part-4n3o</guid>
      <description>&lt;p&gt;You find a small app that does almost what you need. The layout works. The interaction makes sense. You want a different calculation, a few extra fields, or your own version for your team.&lt;/p&gt;

&lt;p&gt;Starting with another blank prompt feels wasteful. There is already a working example to build from.&lt;/p&gt;

&lt;p&gt;drobek lets an owner make a published gallery app duplicable. Someone else can then copy it into their own workspace and ask their agent to change it. The copy gets its own identity and version history.&lt;/p&gt;

&lt;p&gt;The copy starts with the published files. You do &lt;strong&gt;not&lt;/strong&gt; inherit the original owner's users, records, or API keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  The owner chooses whether an app can be copied
&lt;/h2&gt;

&lt;p&gt;A public URL alone doesn't make an app duplicable.&lt;/p&gt;

&lt;p&gt;The server must have its gallery enabled. The owner lists a published app and turns on &lt;strong&gt;Allow duplicates&lt;/strong&gt;. The app must remain eligible for the public gallery; a private or removed listing isn't a back door to its source.&lt;/p&gt;

&lt;p&gt;In the dashboard, the Gallery section on the app's Overview page holds those choices. An agent can also use &lt;code&gt;set_gallery_listing&lt;/code&gt;, but listing the app and allowing copies require the user's explicit confirmation.&lt;/p&gt;

&lt;p&gt;The gallery entry then supplies a duplicate URL. A visitor signs in, chooses a workspace where they can edit apps, and gives the copy a name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Version one is the published app
&lt;/h2&gt;

&lt;p&gt;The new app starts unpublished, with the source app's published files as version 1. It does not take the author's newer preview edits.&lt;/p&gt;

&lt;p&gt;The person making a copy starts from what they saw running, while the original author can keep unfinished changes in preview.&lt;/p&gt;

&lt;p&gt;The copy remembers its source through &lt;code&gt;duplicated_from&lt;/code&gt;. It is still a separate app. The copy operation does not establish a Git remote, a branch, or an automatic stream of updates from the original.&lt;/p&gt;

&lt;p&gt;Through MCP, the tool is &lt;code&gt;duplicate_app&lt;/code&gt;. For a gallery app with the example slug &lt;code&gt;pixel-wall&lt;/code&gt;, the arguments look like this:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pixel-wall"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My pixel wall"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the slug or URL of a real, duplicable app on the server your agent is connected to. This is an example call shape, not a promise that a particular gallery entry is available. The tool does not import apps from another drobek server by URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  What transfers, and what needs attention
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Published source and build output&lt;/td&gt;
&lt;td&gt;Become version 1 of the new app.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Newer preview edits&lt;/td&gt;
&lt;td&gt;Stay with the original app.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Saved module settings&lt;/td&gt;
&lt;td&gt;Proposed through the copy's configuration flow, after filtering.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Settings that need confirmation&lt;/td&gt;
&lt;td&gt;Wait on the copy's Modules page.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email addresses in configuration&lt;/td&gt;
&lt;td&gt;Removed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proxy configuration and upstream assignments&lt;/td&gt;
&lt;td&gt;Not copied.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secret values, records, end users, uploaded files&lt;/td&gt;
&lt;td&gt;Not copied.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uploaded app assets, domains, gallery listing&lt;/td&gt;
&lt;td&gt;Not copied.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An image uploaded as an app asset won't appear in the copy. A frontend that calls a named proxy upstream needs a suitable upstream configured for its new workspace. The original owner's external service is not yours to use.&lt;/p&gt;

&lt;p&gt;The tool response separates module settings into &lt;code&gt;applied&lt;/code&gt;, &lt;code&gt;pending&lt;/code&gt;, and &lt;code&gt;skipped&lt;/code&gt;. An agent should inspect all three. “The copy exists” does not mean “every integration is ready.”&lt;/p&gt;

&lt;p&gt;These rules are in the &lt;a href="https://github.com/freema/drobek/blob/v0.5.0/docs/AGENT.md" rel="noopener noreferrer"&gt;agent contract&lt;/a&gt; and the &lt;a href="https://github.com/freema/drobek/blob/v0.5.0/packages/modules/src/duplicate.ts" rel="noopener noreferrer"&gt;copy configuration implementation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give the agent a small change
&lt;/h2&gt;

&lt;p&gt;For a first attempt, I would choose a simple app without private integrations. Then give the agent a bounded request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Duplicate this gallery app into my personal workspace.
Rename the copy and change its heading and color palette.
Keep the existing interaction working.

Inspect applied, pending, and skipped module settings.
Tell me which configuration or assets need replacing.
Give me the preview URL before publishing.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then change the useful part: the calculation, the workflow, or the fields. Test the copy with its own data. If it needs an external API, register the upstream in your workspace and supply your own credentials through the dashboard.&lt;/p&gt;

&lt;p&gt;When the result works, publish the copy. It gets its own production URL. Listing that copy in the gallery is another deliberate choice.&lt;/p&gt;

&lt;p&gt;This fits small tools that already do most of what you need. You can inspect a working starting point, make the change you actually need, and keep your own version. Browse the &lt;a href="https://www.drobek.app/gallery" rel="noopener noreferrer"&gt;gallery&lt;/a&gt; for apps whose owners have enabled duplication, or read the &lt;a href="https://github.com/freema/drobek" rel="noopener noreferrer"&gt;source&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>mcp</category>
      <category>webdev</category>
      <category>ai</category>
    </item>
    <item>
      <title>Your OSINT agent found a connection. Now try to prove it wrong.</title>
      <dc:creator>Tomas Grasl</dc:creator>
      <pubDate>Fri, 02 Oct 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/freema/your-osint-agent-found-a-connection-now-try-to-prove-it-wrong-366p</link>
      <guid>https://dev.to/freema/your-osint-agent-found-a-connection-now-try-to-prove-it-wrong-366p</guid>
      <description>&lt;p&gt;Give an agent a suspicious domain and it can come back with a convincing story. Several websites share infrastructure. Their pages look similar. A timeline seems to line up.&lt;/p&gt;

&lt;p&gt;You still need to know which parts are observations and which parts are the agent joining the dots.&lt;/p&gt;

&lt;p&gt;I've also worked on analysing Facebook scam ads. That experience is why I care about the distinction between a lead worth following and a connection I can defend.&lt;/p&gt;

&lt;p&gt;Ads impersonating bankers and central-bank governors make a useful example. For a cross-country investigation, the scope could cover ten large European countries and include inactive campaigns. That is a scope to define and check, not a result in itself.&lt;/p&gt;

&lt;p&gt;The workflow starts with a suspicion. Choose a test, inspect the result, and use it to decide what to ask next. That also includes dropping a suspicion when the evidence doesn't support it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the question small enough to fail
&lt;/h2&gt;

&lt;p&gt;“Find out everything about this network” is a reasonable opening request. It is a poor definition of done.&lt;/p&gt;

&lt;p&gt;For the advertising example, I would first make the scope explicit. Which countries, selected by which measure? What time window? What counts as impersonation? Which sources can actually show inactive ads, and which cannot?&lt;/p&gt;

&lt;p&gt;Those decisions affect what a missing result means. “We found no matching ads in this source during this period” is a much narrower claim than “this campaign never ran in that country.”&lt;/p&gt;

&lt;p&gt;Then separate candidate explanations. Similar pages might belong to the same operator. They might also use the same template, hosting service, or copied creative. A shared IP is an observation to investigate, not a person's identity.&lt;/p&gt;

&lt;p&gt;A prompt I would use at that point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;For each proposed connection, record:
- the exact observation and its source;
- when the observation was captured;
- the explanation it supports;
- a plausible alternative explanation;
- the next check that could weaken the connection.

Keep unsupported connections as hypotheses.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prompt sets up the investigation. It does not establish what the evidence will show.&lt;/p&gt;

&lt;h2&gt;
  
  
  Knowing the tools still matters
&lt;/h2&gt;

&lt;p&gt;An agent that can use tools saves a lot of switching between tabs. It still benefits from someone who knows which tool answers which question.&lt;/p&gt;

&lt;p&gt;For example, &lt;a href="https://urlscan.io/docs/api/" rel="noopener noreferrer"&gt;urlscan's API&lt;/a&gt; can search existing scans by attributes including domains and IPs, and retrieve scan results. Searching existing material and submitting a new scan are different actions. A submission has a visibility setting; “unlisted” is not the same as private. I would check existing scans first and decide separately whether a new submission is appropriate.&lt;/p&gt;

&lt;p&gt;The same care applies to interpreting the result. A scan is an observation from a particular run. It doesn't establish everything a site has ever served or who ultimately controls it.&lt;/p&gt;

&lt;p&gt;The human contribution is often choosing a better test. If the question concerns writing style, another infrastructure lookup may add little. Comparing text may be worth exploring, while remembering that shared templates, translation, or editing can also explain similarities. A similarity score is not authorship proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  Save evidence the next run can inspect
&lt;/h2&gt;

&lt;p&gt;A long chat is an awkward place to maintain a case. I would keep a small working directory alongside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;case/
  scope.md
  hypotheses.md
  evidence.csv
  sources/
  runs/
    2026-10-02.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An evidence row can hold a source URL, capture time, saved file, observation, and the hypothesis it relates to. Keep the original material available. A summary that says “three domains were connected” is hard to audit if the actual relationship has disappeared into an earlier conversation.&lt;/p&gt;

&lt;p&gt;A run note should record what was checked, what changed, what contradicted the current explanation, and what remains unknown. The next run can then compare new evidence with a named previous state.&lt;/p&gt;

&lt;p&gt;I would also record which actions the agent may take. Reading public sources is different from contacting people or submitting information to another service. If an agent proposes sending an email, the person running the investigation needs to decide whether that contact is appropriate. Tool access alone isn't permission to make contact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repeating a run doesn't validate its conclusion
&lt;/h2&gt;

&lt;p&gt;Forecasting raises the same questions about repeated assessments and memory. Keeping instructions in a file lets the next run assess new evidence against the same question.&lt;/p&gt;

&lt;p&gt;For a forecast, I would define the event, deadline, and resolution criteria before the next run. Save the previous probability and its reasons. Then ask which new observations justify changing it.&lt;/p&gt;

&lt;p&gt;Otherwise, you can end up with a fresh, confident paragraph each morning and no way to tell whether the forecasting process is improving. A forecast needs outcomes to compare against. Repetition alone doesn't supply them.&lt;/p&gt;

&lt;p&gt;The same applies to an investigation. A later run that repeats an earlier unsupported claim has not found independent corroboration.&lt;/p&gt;

&lt;h2&gt;
  
  
  A useful result can be “the suspicion got weaker”
&lt;/h2&gt;

&lt;p&gt;I want an OSINT agent to help me examine more possibilities while keeping the evidence behind each connection easy to inspect.&lt;/p&gt;

&lt;p&gt;I would rather finish with a narrower claim I can defend than a large network diagram whose edges mean five different things. Before sharing a conclusion, I want to be able to open the source behind each material claim and explain what would change my mind.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>security</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Your agent wrote the app. Here's what drobek actually runs.</title>
      <dc:creator>Tomas Grasl</dc:creator>
      <pubDate>Wed, 30 Sep 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/freema/your-agent-wrote-the-app-heres-what-drobek-actually-runs-3k4o</link>
      <guid>https://dev.to/freema/your-agent-wrote-the-app-heres-what-drobek-actually-runs-3k4o</guid>
      <description>&lt;p&gt;An agent can produce a working React app and a very optimistic explanation of why it is ready for production. I still want to know where its code will run, what it can access, and what happens when the next edit breaks the build.&lt;/p&gt;

&lt;p&gt;Those questions shaped drobek. I &lt;a href="https://dev.to/freema/i-wanted-vibe-coded-apps-not-another-ai-credit-bill-so-i-built-drobek-3elc"&gt;introduced it here&lt;/a&gt;: a place to host small apps built by your own coding agent. The build and deployment flow is worth a closer look.&lt;/p&gt;

&lt;p&gt;drobek compiles and serves the app's frontend. The app's JavaScript runs in the browser. Backend work goes through platform modules installed by the server operator.&lt;/p&gt;

&lt;h2&gt;
  
  
  A write includes the build result
&lt;/h2&gt;

&lt;p&gt;The agent connects over MCP and calls &lt;code&gt;create_app&lt;/code&gt;. It gets a starter, an app ID, a preview URL, and a briefing describing the environment. It can ask &lt;code&gt;skill_info&lt;/code&gt; for the current module contracts instead of guessing an SDK from an old example.&lt;/p&gt;

&lt;p&gt;The next step is &lt;code&gt;write_files&lt;/code&gt;. The call contains changed text files and a short reason for the edit. drobek applies those changes to the latest version and compiles the result in-process with esbuild.&lt;/p&gt;

&lt;p&gt;The response includes &lt;code&gt;compile.ok&lt;/code&gt; and compiler errors with file, line, column, and message. The agent can make its next correction from that response. It doesn't need a separate terminal session to discover that an import failed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A failed compile is still stored as a version&lt;/strong&gt;. The preview keeps serving the last version that compiled successfully. The broken source isn't lost, and the agent can fix it with another write.&lt;/p&gt;

&lt;p&gt;This is the documented flow, rather than a transcript from a particular run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;create_app
    -&amp;gt; app ID, starter, briefing, preview URL
write_files
    -&amp;gt; numbered version, compile result
    -&amp;gt; on failure: fix the source and write again
    -&amp;gt; on success: inspect the preview
publish
    -&amp;gt; chosen compiled version becomes production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://github.com/freema/drobek/blob/v0.5.0/docs/AGENT.md" rel="noopener noreferrer"&gt;agent contract&lt;/a&gt; describes the tools and their return values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preview and production have different jobs
&lt;/h2&gt;

&lt;p&gt;App hosts look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;slug&amp;gt;.&amp;lt;APPS_DOMAIN&amp;gt;             production
&amp;lt;slug&amp;gt;--preview.&amp;lt;APPS_DOMAIN&amp;gt;    latest successful build
&amp;lt;slug&amp;gt;--v&amp;lt;N&amp;gt;.&amp;lt;APPS_DOMAIN&amp;gt;       a specific version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Publishing moves a pointer to a compiled version. Publishing an older version rolls production back. &lt;code&gt;restore_version&lt;/code&gt; does something slightly different: it creates a new working version from older files. It doesn't rewrite history or publish that result automatically.&lt;/p&gt;

&lt;p&gt;I want that distinction to stay visible. An agent can keep editing while the production URL serves the version people have already checked.&lt;/p&gt;

&lt;p&gt;But preview is not an isolated staging environment. &lt;strong&gt;Preview and production share the app's backend data.&lt;/strong&gt; If you test a delete operation in preview, changing the production code pointer later will not bring the record back. Use separate test data or a separate app for that kind of experiment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backend behavior comes from modules
&lt;/h2&gt;

&lt;p&gt;The built-in modules cover authentication, records, forms, email, files, and API proxying. The frontend imports the browser SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;drobek&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;drobek&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;drobek&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That import resolves to drobek's SDK. You don't install a separate package into each hosted app.&lt;/p&gt;

&lt;p&gt;The collection's server-side configuration controls access. For example, this configuration lets signed-in users create records, then read and modify their own records. App admins can work across those records:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"collections"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"todos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"owner|admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"create"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"update"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"owner|admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"delete"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"owner|admin"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;owner&lt;/code&gt; means the record's owner. It does not mean every person who can edit the app in the dashboard. App-user authentication and dashboard workspace roles are separate concerns.&lt;/p&gt;

&lt;p&gt;The module checks these rules when a request reaches the backend. A frontend that forgets to hide a button doesn't grant permission. Broader access changes can require owner confirmation through the dashboard. The &lt;a href="https://github.com/freema/drobek/blob/v0.5.0/docs/MODULES.md" rel="noopener noreferrer"&gt;module documentation&lt;/a&gt; lists those cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keys don't belong in the agent's output
&lt;/h2&gt;

&lt;p&gt;For an external API, the workspace administrator registers an upstream. Its secret goes into the dashboard, and the proxy uses it server-side. MCP can report whether a secret is present without returning its value.&lt;/p&gt;

&lt;p&gt;Each app also has its own origin, separate from the dashboard. These are useful boundaries, but they don't make an arbitrary generated frontend trustworthy. It can still have bugs, render misleading information, or make requests allowed by an overly broad configuration.&lt;/p&gt;

&lt;p&gt;The modules themselves are trusted server code. Installing a third-party module is an operator decision, not something an app's agent can do by writing frontend files.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this leaves out
&lt;/h2&gt;

&lt;p&gt;drobek isn't intended for arbitrary server applications, per-app background workers, or a private dependency installation for every app. If your app needs those, this hosting model may not fit.&lt;/p&gt;

&lt;p&gt;For the smaller tools my colleagues build, I want a workflow where the agent gets useful build feedback, the person gets a preview, and publishing selects a version we have actually looked at. You can inspect the implementation in the &lt;a href="https://github.com/freema/drobek/blob/v0.5.0/docs/ARCHITECTURE.md" rel="noopener noreferrer"&gt;architecture guide and source&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>architecture</category>
      <category>typescript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I wanted vibe-coded apps, not another AI credit bill. So I built drobek.</title>
      <dc:creator>Tomas Grasl</dc:creator>
      <pubDate>Mon, 28 Sep 2026 07:49:21 +0000</pubDate>
      <link>https://dev.to/freema/i-wanted-vibe-coded-apps-not-another-ai-credit-bill-so-i-built-drobek-3elc</link>
      <guid>https://dev.to/freema/i-wanted-vibe-coded-apps-not-another-ai-credit-bill-so-i-built-drobek-3elc</guid>
      <description>&lt;p&gt;Vibe coding is everywhere. So are the credit meters.&lt;/p&gt;

&lt;p&gt;Ever read the spending threads on r/lovable? &lt;a href="https://www.reddit.com/r/lovable/comments/1l6igpm/how_much_have_you_spent_creating_apps_on_lovable/" rel="noopener noreferrer"&gt;This one&lt;/a&gt; has people burning through hundreds of credits while still trying to get their apps working. Other people are happy with what they shipped. But the question stuck with me: &lt;strong&gt;if I already have a coding agent, why do I need another platform's AI credits?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://lovable.dev/pricing" rel="noopener noreferrer"&gt;Lovable bundles building and cloud services into its platform&lt;/a&gt;. I wanted to keep using my own agent and have somewhere to deploy the result.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;drobek&lt;/strong&gt;, an open-source hosting platform for small web apps. Bring Claude Code, Cursor, or Codex, connect over MCP, and publish what you build. &lt;strong&gt;drobek doesn't sell you AI tokens.&lt;/strong&gt; Your model usage stays with your agent's provider. Hosting still costs money, whether you use a hosted service or run your own server.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/freema/drobek" rel="noopener noreferrer"&gt;source is on GitHub&lt;/a&gt;, under AGPL-3.0. There is a hosted version at &lt;a href="https://www.drobek.app/" rel="noopener noreferrer"&gt;drobek.app&lt;/a&gt;, and you can self-host it.&lt;/p&gt;

&lt;p&gt;At work, almost everyone vibecodes now. People build landing pages, internal tools, and reports for colleagues. The next question is usually where to put them, who gets access, and what to do with the API keys. That's the part I wanted drobek to handle.&lt;/p&gt;

&lt;p&gt;The agent connects to drobek over MCP, creates an app, and sends the source files. drobek compiles them and returns errors the agent can fix. A successful build gets a preview URL. You try it, ask for changes, and publish when you're ready. Each successful build is a numbered version, so you can also publish an older one.&lt;/p&gt;

&lt;p&gt;The app's JavaScript runs in the browser. drobek compiles and serves it; it doesn't execute agent-written application code on the server. Backend features come from platform modules: login, data storage, forms, file uploads, owner notifications, and API proxying.&lt;/p&gt;

&lt;p&gt;That gives me a clearer security boundary for these small apps. Each app has its own origin. Owners enter API secrets in the dashboard, and the relevant module uses them on the server. The agent doesn't need the secret values. Data access rules belong in the backend module, where hiding a button in the frontend can't bypass them. You still need to configure those rules and review what you publish.&lt;/p&gt;

&lt;p&gt;Here's a small example from the public gallery: a &lt;a href="https://pokedex.drobek.app/" rel="noopener noreferrer"&gt;Pokédex&lt;/a&gt; with search, stats, and a live log of requests through drobek's proxy module. PokéAPI is public, so this demo doesn't need a secret API key.&lt;/p&gt;

&lt;p&gt;This is the size of app I have in mind: something useful enough to share, without maintaining a custom backend for every little tool. drobek targets browser apps; it isn't a place to run arbitrary server code or background workers. Also, preview and production share an app's backend data. Rolling back the code doesn't roll back the database.&lt;/p&gt;

&lt;p&gt;I'll follow up with the technical details, copying and sharing apps, self-hosting, and turning an artifact into something people can keep using. For now, &lt;a href="https://www.drobek.app/gallery" rel="noopener noreferrer"&gt;browse the gallery&lt;/a&gt; or &lt;a href="https://github.com/freema/drobek" rel="noopener noreferrer"&gt;have a look at the repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Where do the little apps your colleagues vibe code end up today?&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>opensource</category>
      <category>selfhosted</category>
      <category>ai</category>
    </item>
    <item>
      <title>Anthropic deleted 80% of Claude Code's system prompt. Here is what I changed in my setup.</title>
      <dc:creator>Tomas Grasl</dc:creator>
      <pubDate>Mon, 07 Sep 2026 07:14:16 +0000</pubDate>
      <link>https://dev.to/freema/anthropic-deleted-80-of-claude-codes-system-prompt-here-is-what-i-changed-in-my-setup-1j7h</link>
      <guid>https://dev.to/freema/anthropic-deleted-80-of-claude-codes-system-prompt-here-is-what-i-changed-in-my-setup-1j7h</guid>
      <description>&lt;p&gt;Are you burning tokens in Claude Code and getting worse results than you should? Your old habits might be the reason.&lt;/p&gt;

&lt;p&gt;Anthropic removed over 80% of Claude Code's system prompt. No measurable loss on their coding evals. Thariq Shihipar from Anthropic wrote it up on July 24 in "The new rules of context engineering for Claude 5 generation models". It was not quiet at all, most people just missed it.&lt;/p&gt;

&lt;p&gt;The number is not the interesting part. The reason is. Those instructions were there to keep weaker models in line. On Opus 5 and Fable 5 they only get in the way. The model burns tokens figuring out which of your contradicting rules wins, and only then touches the code.&lt;/p&gt;

&lt;p&gt;Eight things I would rethink in my workflow after reading it.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Role-play prompting is over
&lt;/h2&gt;

&lt;p&gt;"You are a senior developer with 20 years of experience" does nothing. Neither does "focus 100%". The model does not need it.&lt;/p&gt;

&lt;p&gt;What works better:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context and goal.&lt;/strong&gt; Say what you are working on and who it is for, not just what you want generated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Show, do not describe.&lt;/strong&gt; Instead of "write informally", point at a real reference file with &lt;code&gt;@&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measurable scope.&lt;/strong&gt; "Max 5 sentences" beats "do not be too long".&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Switching models mid-session
&lt;/h2&gt;

&lt;p&gt;Claude Code sends the whole history with every request. Caching is what saves you: it is a prefix cache with an exact match from the start of the request, so the already processed part costs a fraction.&lt;/p&gt;

&lt;p&gt;The mistake: switching from Opus to Sonnet to save money.&lt;/p&gt;

&lt;p&gt;What actually happens: each model has its own cache. The next request reads the entire history with zero cache hits. Same for changing effort level, with one exception, Fable 5.1 on an API key or a subscription keeps the cache.&lt;/p&gt;

&lt;p&gt;And here is the part almost nobody mentions. If you genuinely need more than one model, do not switch. Hang them off as subagents or a workflow. A subagent starts its own conversation with its own cache, and from the parent side only the call and the result get appended, so the parent prefix stays intact. In a workflow fan-out Claude Code goes further and holds same-prefix agents for a few seconds so the first one can warm the cache and the rest read from it.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Prohibitions confuse the model
&lt;/h2&gt;

&lt;p&gt;Flood it with a list of things it must not do and every rule has to be reconciled against the others. Anthropic describes this on their own prompt: one line said leave documentation where appropriate, another said DO NOT ADD COMMENTS, and the model spent reasoning on that before it touched a file.&lt;/p&gt;

&lt;p&gt;Fix: "answer in max 5 sentences" instead of "do not write long answers". "Write in full sentences" instead of "do not use bullets".&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Hard rules belong in hooks
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; (not &lt;code&gt;.claudemd&lt;/code&gt;, that file does not exist) is context for Claude, not enforced configuration. The docs say it plainly: if you want to block an action regardless of what the model decides, that is a &lt;code&gt;PreToolUse&lt;/code&gt; hook.&lt;/p&gt;

&lt;p&gt;A hook is a plain deterministic script Claude Code runs at a given lifecycle event. It can check encoding of non-ASCII characters or kill an attempt to read credentials. You create one by asking Claude for it in the terminal.&lt;/p&gt;

&lt;p&gt;One gotcha: editing &lt;code&gt;CLAUDE.md&lt;/code&gt; mid-task does not apply. Claude keeps working with the version loaded at session start. The new content loads on the next &lt;code&gt;/clear&lt;/code&gt;, &lt;code&gt;/compact&lt;/code&gt; or restart.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Infinite exploration
&lt;/h2&gt;

&lt;p&gt;A vague task in a big repo means Claude starts opening files blind. Every file it reads lands in the context window in full. You are not just burning tokens, you are filling context with noise. Navigate it to the exact place where the answer lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Effort level
&lt;/h2&gt;

&lt;p&gt;There is no slider in the corner of the window, Claude Code is a terminal. Effort is set in config or in session, and the levels are low, medium, high, xhigh and max. Default is high. ("Ultra Code", which floats around on blogs, is not an official level.)&lt;/p&gt;

&lt;p&gt;The mistake is running max on ordinary tasks. Anthropic says themselves that max is for genuinely frontier problems and that on most workloads it mainly adds cost, and on structured output it can lead to overthinking. And since every effort level has its own cache, changing it mid-session costs you a recompute of the whole history.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. A full context window is not free
&lt;/h2&gt;

&lt;p&gt;A million tokens fits. That is not the same as the model recalling what it needs from them. Anthropic published MRCR v2 numbers with Opus 4.6: 93% at 256K tokens, 76% at 1M.&lt;/p&gt;

&lt;p&gt;Careful with the reading though. MRCR measures retrieval of a planted fact, not instruction following. Still a clear signal: reliability drops long before you run out of window. After a finished subtask run &lt;code&gt;/clear&lt;/code&gt;, at a natural break &lt;code&gt;/compact&lt;/code&gt;, and on long work have it write a handoff to a file and start a fresh session.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Regular hygiene
&lt;/h2&gt;

&lt;p&gt;You do not have to guess what to cut from &lt;code&gt;CLAUDE.md&lt;/code&gt;. Claude Code has &lt;code&gt;/doctor&lt;/code&gt; and it proposes the trims for you. It cuts what Claude can derive from the repo (directory layout, dependency lists, architecture overview) and keeps what it cannot (gotchas, generated files, the non-obvious build step).&lt;/p&gt;

&lt;p&gt;And check your auto memory. It lives in &lt;code&gt;~/.claude/projects/&amp;lt;project&amp;gt;/memory/&lt;/code&gt;. Each session loads the first 200 lines or 25 KB of &lt;code&gt;MEMORY.md&lt;/code&gt;, and topic files are read on demand. Browse it with &lt;code&gt;/memory&lt;/code&gt;, and &lt;code&gt;/context&lt;/code&gt; shows what actually loaded into the current session. When Claude behaves strangely and you do not know why, it is usually a contradicting note sitting right there.&lt;/p&gt;

&lt;h2&gt;
  
  
  The summary
&lt;/h2&gt;

&lt;p&gt;Coding with AI in 2026 is not about who writes the longer system prompt. It is context management, deterministic verification through hooks, and cleaning history as you go.&lt;/p&gt;

&lt;p&gt;How do you handle context limits? Hooks already, or still a giant &lt;code&gt;CLAUDE.md&lt;/code&gt;?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>llm</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Same skeleton, different last gate: reading Anthropic's commerce-agents with my own agent open in the other window</title>
      <dc:creator>Tomas Grasl</dc:creator>
      <pubDate>Thu, 03 Sep 2026 19:50:12 +0000</pubDate>
      <link>https://dev.to/freema/same-skeleton-different-last-gate-reading-anthropics-commerce-agents-with-my-own-agent-open-in-3n9a</link>
      <guid>https://dev.to/freema/same-skeleton-different-last-gate-reading-anthropics-commerce-agents-with-my-own-agent-open-in-3n9a</guid>
      <description>&lt;p&gt;Anthropic shipped commerce-agents: a reference shopping agent and merchant agent, three runtime paths, a Claude Code plugin.&lt;/p&gt;

&lt;p&gt;I do not build e-shops. I have an agent over Jira, GitLab, Sentry, mail and around 120 repositories. It writes analyses into Jira, reviews merge requests, checks mailer templates, audits worklogs. Code is the smaller part of the job.&lt;/p&gt;

&lt;p&gt;I read their repo for a full day and kept thinking: I have this too. Just as a sentence in a markdown file.&lt;/p&gt;

&lt;p&gt;The skeleton is the same. What is not the same is who stands at the end of the chain, and that one question decides how much of your agent is prose and how much of it is code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four layers, both sides
&lt;/h2&gt;

&lt;p&gt;Strip the domain away and both repos are the same four layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Policy&lt;/strong&gt;: who the agent is, what it must never do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capabilities&lt;/strong&gt;: what it can call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Procedures&lt;/strong&gt;: how repeated work gets done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge and state&lt;/strong&gt;: what it knows about the domain, and where the work currently stands.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my repo that maps to &lt;code&gt;CLAUDE.md&lt;/code&gt; (8 hard rules), a folder of commands, a few local skills plus plugins from an internal marketplace, and &lt;code&gt;docs/&lt;/code&gt; plus &lt;code&gt;tasks/&lt;/code&gt;. There is not a single line of production code in it. Production code lives in the 120 repos. This one is the cockpit.&lt;/p&gt;

&lt;p&gt;In theirs it maps to the agent prompt, tool definitions, flows and the vertical knowledge packs. Retail, travel, telecom, ticketing.&lt;/p&gt;

&lt;p&gt;Different domain, same shape. That part was easy to accept.&lt;/p&gt;

&lt;h2&gt;
  
  
  The difference: who is the last gate
&lt;/h2&gt;

&lt;p&gt;At my keyboard, I am the last gate. Before the agent writes anything into Jira or posts a review comment, it shows me the draft and waits. Before anything gets deployed, it runs into the fact that I am the one who clicks.&lt;/p&gt;

&lt;p&gt;Their agent talks to a customer who has never heard of Claude Code. Nobody sits in between.&lt;/p&gt;

&lt;p&gt;That is the whole difference, and everything else follows from it. Every rule I keep as a sentence in &lt;code&gt;CLAUDE.md&lt;/code&gt;, they have as code:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Provenance gate.&lt;/strong&gt; The agent can only write against an ID it actually read in this session. Not one it remembers from an earlier run, and definitely not one that appeared inside some text it was given. Product descriptions, reviews, customer mail: all of that is input, and input can contain instructions that look exactly like yours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Staged change.&lt;/strong&gt; The model never applies anything. It proposes, a human commits. The interesting part is that this is not a permission check somewhere in the flow, it is the shape of the API: there is no code path where the model's output goes straight through.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fencing untrusted text.&lt;/strong&gt; Every piece of foreign content gets wrapped so it cannot be read as instruction. Boring, mechanical, and the thing everyone skips because "the model is smart enough".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tests that run against prompt bytes, not against the model.&lt;/strong&gt; This is the one that took me longest to appreciate. They are not testing whether the model behaves correctly. They are testing that the assembled prompt does not contain what it should not contain. Deterministic, no model in the loop, runs in CI in milliseconds.&lt;/p&gt;

&lt;p&gt;One line from their docs is the most useful thing I read in a month: a rule in the prompt is one injection or one bad sample away from being skipped.&lt;/p&gt;

&lt;p&gt;I have that verified from the other side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three weeks of prose quietly leaking
&lt;/h2&gt;

&lt;p&gt;Because a human sits at my keyboard, prose felt like enough. For three weeks my advisory rules leaked, in small ways I only saw when I went looking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The rule says "when I say go, it is go". The agent asked a second time anyway, after I had already approved a draft. That is not caution, that is a stall.&lt;/li&gt;
&lt;li&gt;The rule says every status transition gets a worklog and every note gets a ticket link. Some notes went in without links.&lt;/li&gt;
&lt;li&gt;The rule says short messages. Long status walls kept coming.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these is dramatic on its own. Together they are the same failure mode the Anthropic docs describe, just without an attacker: a sentence in a prompt is a suggestion the model is free to deprioritize when the context gets long.&lt;/p&gt;

&lt;p&gt;So I ported two things the same day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A hook instead of a sentence.&lt;/strong&gt; Writing to Jira now goes through a hook that only lets the call through if the issue key was read in this session, and always via a dialog I click. Same rule as before, except now it is not a rule, it is the only available path.&lt;/p&gt;

&lt;p&gt;Roughly, in pseudo-code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// simplified: the real one lives in a Claude Code hook&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;beforeJiraWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ToolCall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SessionLog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issueKey&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="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readIssueKeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;deny&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; was not read in this session. Fetch it first.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;askUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Write to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// no silent path exists&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A check script for my own text.&lt;/strong&gt; Commands validated against the README, the team roster against the people files, regexes for secrets, in-progress tasks without a ticket link. First run: 26 findings. I had spent three weeks writing rules about verification and had never once verified my own repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the two designs diverge, and why
&lt;/h2&gt;

&lt;p&gt;The differences are not about anyone being smarter. They fall out of a single constraint: their agent talks to a stranger, mine talks to me.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;commerce-agents&lt;/th&gt;
&lt;th&gt;my cockpit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gates&lt;/td&gt;
&lt;td&gt;hooks and CI tests&lt;/td&gt;
&lt;td&gt;confirmation before every write&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning&lt;/td&gt;
&lt;td&gt;evals&lt;/td&gt;
&lt;td&gt;one dated line with a ticket number per failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verification&lt;/td&gt;
&lt;td&gt;evals written by the same agent that wrote the flow&lt;/td&gt;
&lt;td&gt;e2e verifier gets only acceptance criteria and a URL, never the diff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subagents&lt;/td&gt;
&lt;td&gt;avoided, latency matters in a chat&lt;/td&gt;
&lt;td&gt;used for heavy reads, waiting is fine, context bloat is not&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Their "skills over subagents" advice is correct for a customer chat where every second is visible. In a cockpit I do not care if a check takes 40 seconds. I care that a worklog dump is 30k tokens and has no business being in the main session.&lt;/p&gt;

&lt;p&gt;The learning mechanism is the one I am keeping as it is. Every failure ends up as one line with a date and a ticket number in &lt;code&gt;docs/conventions.md&lt;/code&gt;. Two real ones, so this is not abstract:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A deploy tag shaped like a number with a dot is a valid YAML float. The ConfigMap rejected it and production went down. That is now a documented trap with a fix pattern that every other repo points at.&lt;/li&gt;
&lt;li&gt;A monthly worklog check reported 45 hours missing. Reality was 19. The cause: the MCP endpoint returns at most 20 worklogs per issue and does not paginate. The lesson line now says where totals may come from and where they may not.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repo learns, the model does not. That is the entire mechanism, and it costs one line per screw-up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part worth taking
&lt;/h2&gt;

&lt;p&gt;The interesting question about agents is not whether the model can write the code. It can, and that debate is boring now.&lt;/p&gt;

&lt;p&gt;The interesting question is where the gates stand and who is last in the chain. Anthropic had to answer it in code because nobody sits between their agent and a stranger. I assumed prose was enough because I sit right there. Prose is enough right up to the moment you notice it is not, and you usually notice from a diff you did not want.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/anthropics/commerce-agents" rel="noopener noreferrer"&gt;github.com/anthropics/commerce-agents&lt;/a&gt;. It says "reference implementation, not maintained, no contributions" right at the top. Honest. Take the mechanisms, not the code.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>claude</category>
      <category>llm</category>
    </item>
  </channel>
</rss>
