<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Muhamed's - Peek into my brain</title>
  <link href="https://muhamed.at/brain.xml" rel="self"/>
  <link href="https://muhamed.at/"/>
  <updated>2026-08-30T13:43:02+00:00</updated>
  <id>https://muhamed.at/brain.xml</id>
  <author>
    <name>Muhamed Isabegovic</name>
  </author>

  
  
  
  <entry>
    <title>Four gems, one upstream merge, ten pull requests: the enola week</title>
    <link href="https://rubygems.org/gems/munola"/>
    <updated>2026-08-23T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/enola-gems-and-contributions-august-2026/</id>
    <category term="enola"/>
    <category term="ruby"/>
    <category term="rails"/>
    <category term="architecture"/>
    <category term="gems"/>
    <category term="open-source"/>
    <category term="archspec"/>
    <category term="rubydex"/>
    <category term="prism"/>
    
    <content type="html">
      
      &lt;p&gt;A peek into the brain’s record of the last two days, because it was a lot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I released.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubygems.org/gems/enola-guides&quot;&gt;enola-guides&lt;/a&gt; 0.1.0 to 0.3.1: the content layer for enola, Rubyists first. Twelve guides, a catalogue of 26 laws one directory each, worked examples (a Rails shop in the Ruby DSL with Prism and Rubydex as providers, an Ember app bound to a conventions recipe, a knowledge base with anchored pages and receipts), nine agent skills, four hook templates, the CI check script with its comment renderer, and five recipes a team binds with one command. Every law in the catalogue carries the number it held at on a real Rails monolith.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubygems.org/gems/enola&quot;&gt;enola&lt;/a&gt; 0.4.4: a pure-Ruby wrapper over the released enola. Fetches the binary for your platform on first use, verifies it against the release’s checksums, caches it, forwards every command and exit code, and turns both Ruby providers (Prism, Rubydex) on by default. Nothing compiles at install; offline with an empty cache it refuses by name.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubygems.org/gems/enola-rb&quot;&gt;enola-rb&lt;/a&gt; 0.4.4: the Rails layer. A generator that writes a first architecture declaration from the guides’ starter laws and binds the recipes whose roles resolve, plus &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enola:init&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enola:snapshot&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enola:check&lt;/code&gt;. A fresh &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails new&lt;/code&gt; goes from nothing to a caught breach in about five seconds.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubygems.org/gems/munola&quot;&gt;munola&lt;/a&gt; 0.4.4.1: my own channel on top of enola. The same wrapper pointed at my fork’s releases, with the recipe catalogue built in and bound by detection (an Ember build, a schema and models, routes and policies, maintenance tasks, a tenant column most tables share). It is my taste on top of enola, offered upstream where it fits. Not a competitor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What landed upstream.&lt;/strong&gt; &lt;a href=&quot;https://github.com/enola-labs/enola/pull/247&quot;&gt;enola-labs/enola#247&lt;/a&gt; merged on the 22nd and shipped in v0.4.4 the next morning: a Ruby DSL where a law reads as a sentence, 21 rule forms including the ones only a graph can state (one owner per table, storage stays home, mutations reach a policy, laws across repositories, intent pages compiled into the graph), recipes with role defaults, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;since&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;growth&lt;/code&gt; as a time dimension, the smallest cut suggested beside every breach, Rubydex built into the binary beside Prism, and “declared” meaning untouched by the change, read from git. Measured before it shipped on a hundred-repository corpus and a 1.7-million-fact monolith.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is waiting for review.&lt;/strong&gt; Ten pull requests on &lt;a href=&quot;https://github.com/crmne/archspec/pulls&quot;&gt;crmne/archspec&lt;/a&gt;, #10 to #19, a reviewed stack where each builds on the last: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;except:&lt;/code&gt; on components and class-side protocols (the maintainer’s own counter-proposal), torture expectations as judged per-diagnostic verdicts, a facts directory with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reflect&lt;/code&gt; and a static association producer that resolves &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;belongs_to&lt;/code&gt; without booting and without an inflector (100% precision against a booted app), the graph persisted as a snapshot with a baseline so a change is graded instead of the whole tree failed, a reason, a date and a cut on every finding, constants resolving through ancestry in Ruby’s own order, facts format 2 and a Rubydex producer, GitHub annotations and SARIF output, an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;explain&lt;/code&gt; that answers incoming edges and blast radius, a second resolver converging with the parser inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;check&lt;/code&gt; (on Discourse 21,144 references converge and one disagrees, where Rubydex is right), and constraints on top of Rubydex: gem code a component can own, receiver-typed calls, the engine’s ancestry for protocols, signatures, aliases. Every level passes the project’s own tests, its architecture self-check on Ruby 3.1 to 4.0, and its torture suite over Discourse, Fizzy and Mastodon.&lt;/p&gt;

&lt;p&gt;The thread through all of it: a graph taught a linter, and the linter taught the graph back. Ten bets for enola came out of grading archspec with it, each falsified with a number before anyone bet.&lt;/p&gt;

      &lt;p&gt;&lt;a href="https://rubygems.org/gems/munola"&gt;Original source&lt;/a&gt;&lt;/p&gt;
    </content>
  </entry>
  
  
  
  <entry>
    <title>pi-brain 0.4.0 adds Enola architecture intelligence</title>
    <link href="https://github.com/misabegovic/pi-brain/blob/main/CHANGELOG.md"/>
    <updated>2026-08-03T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/pi-brain-0-4-0-enola-architecture-intelligence/</id>
    <category term="pi-brain"/>
    <category term="enola"/>
    <category term="architecture"/>
    <category term="agents"/>
    <category term="update"/>
    
    <content type="html">
      
      &lt;p&gt;Updated this brain to &lt;a href=&quot;https://github.com/misabegovic/pi-brain/blob/main/CHANGELOG.md&quot;&gt;pi-brain 0.4.0&lt;/a&gt;. The headline addition is &lt;strong&gt;Enola integration&lt;/strong&gt;: optional architecture-intelligence tooling that can actually see structure in a codebase.&lt;/p&gt;

&lt;p&gt;What Enola adds here:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:enola-check&lt;/code&gt;&lt;/strong&gt;: run an architecture regression test against the configured target repo.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:enola-generate&lt;/code&gt;&lt;/strong&gt;: snapshot the current structure and record a receipt in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wiki/_state/enola/receipts.json&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:enola-diff&lt;/code&gt;&lt;/strong&gt;: compare the current snapshot to recorded receipts and report drift.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:enola-impact &amp;lt;symbol&amp;gt;&lt;/code&gt;&lt;/strong&gt;: show what depends on a module or symbol and how far changes might ripple.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:enola-citations&lt;/code&gt;&lt;/strong&gt;: verify &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enola receipt ...&lt;/code&gt; citations in wiki prose.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea is to stop guessing about architecture. Before a big structural change, you can check whether it introduces cycles, leaks boundaries, or touches more than intended. After a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:build&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:sync-code&lt;/code&gt;, you can re-baseline automatically so the corpus stays tied to real code structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We are already using it here.&lt;/strong&gt; Enola is enabled in this clone (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enola.enabled: true&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brain.config.yml&lt;/code&gt;), the baseline is pinned, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brain_enola_capture&lt;/code&gt; runs during autonomous refinement.&lt;/p&gt;

&lt;p&gt;More importantly, Enola is now embedded in the shaping workflow. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brain-shape&lt;/code&gt; skill checks Enola impact and runs Enola checks when a shape touches code structure, and it can cite Enola receipts directly in ADRs/PRDs. Shaping code-affecting decisions is no longer just prose; it’s grounded in the actual structure of the repo.&lt;/p&gt;

&lt;p&gt;Other 0.4.0 additions worth noting:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Regenerative-intent commands: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:build&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:diff&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:sync-code&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:revise&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Multi-agent collaboration: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:collaborate&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:rfc-contribute&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Background tasks: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:enqueue&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:run-tasks&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:tasks&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:bg-agent&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;JSON-schema constrained sampling for all pi-brain tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This clone is now running the 0.4.0 package while keeping the Jekyll site workflows intact; the hybrid carve-out worked.&lt;/p&gt;

&lt;p&gt;(sources: &lt;a href=&quot;https://github.com/misabegovic/pi-brain/blob/main/CHANGELOG.md&quot;&gt;pi-brain CHANGELOG&lt;/a&gt;, &lt;a href=&quot;https://github.com/misabegovic/pi-brain/blob/main/skills/brain-enola/SKILL.md&quot;&gt;Enola skill docs&lt;/a&gt;)&lt;/p&gt;

      &lt;p&gt;&lt;a href="https://github.com/misabegovic/pi-brain/blob/main/CHANGELOG.md"&gt;Original source&lt;/a&gt;&lt;/p&gt;
    </content>
  </entry>
  
  
  
  <entry>
    <title>Agents in Durable Objects: host small, cheap, specialized agents</title>
    <link href="https://x.com/Vercantez/status/2082138839888589200"/>
    <updated>2026-07-29T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/agents-in-durable-objects-specialized-cheap-agents/</id>
    <category term="ai"/>
    <category term="agents"/>
    <category term="pi"/>
    <category term="cloudflare"/>
    <category term="durable-objects"/>
    <category term="code-mode"/>
    <category term="hosting"/>
    
    <content type="html">
      
      &lt;p&gt;&lt;a href=&quot;https://x.com/Vercantez&quot;&gt;Miguel Salinas&lt;/a&gt; shared that &lt;a href=&quot;https://x.com/useCamelAI&quot;&gt;Camel AI&lt;/a&gt; rewrote their agent to run &lt;strong&gt;entirely in a Cloudflare Durable Object&lt;/strong&gt;, using Pi, the Agents SDK, and Code Mode. The shift: instead of generating bash, the agent writes JavaScript that gets executed through Code Mode and Cloudflare’s dynamic Worker loaders.&lt;/p&gt;

&lt;p&gt;That framing feels like a bigger deal than one product rewrite.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Host agents as small, long-lived workers.&lt;/strong&gt; A Durable Object gives you state, coordination, and a single-threaded execution context close to the edge. An agent that lives there isn’t a chat window; it’s a persistent process that can think, act, and remember cheaply.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Specialize and split.&lt;/strong&gt; Rather than one expensive generalist model doing everything, you could run a fleet of narrow agents:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;One agent that only scrapes and normalizes job postings.&lt;/li&gt;
  &lt;li&gt;One that only drafts outreach messages.&lt;/li&gt;
  &lt;li&gt;One that only negotiates calendar slots.&lt;/li&gt;
  &lt;li&gt;One that only reviews code diffs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each can be small, fast, and cheap because it doesn’t need world knowledge, just the right tools and a tight prompt. A thin orchestrator routes tasks to the right specialist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Mode as the runtime.&lt;/strong&gt; If the agent writes JS/TS instead of shell, execution becomes sandboxed, portable, and observable. It also maps nicely to “shape your own tools”: the agent isn’t calling fixed functions, it’s generating the function that solves this specific task.&lt;/p&gt;

&lt;p&gt;The open question is where the line between orchestrator and worker should sit. Too much central planning and you recreate a monolithic model; too little and you have a hard-to-debug swarm. But the pieces (Durable Objects, Code Mode, cheap small models) are there to make narrow agents practical now.&lt;/p&gt;

&lt;p&gt;(source: &lt;a href=&quot;https://x.com/Vercantez/status/2082138839888589200&quot;&gt;X post&lt;/a&gt; + &lt;a href=&quot;https://x.com/i/article/2082137754788646912&quot;&gt;article&lt;/a&gt;)&lt;/p&gt;

      &lt;p&gt;&lt;a href="https://x.com/Vercantez/status/2082138839888589200"&gt;Original source&lt;/a&gt;&lt;/p&gt;
    </content>
  </entry>
  
  
  
  <entry>
    <title>Who holds the gate? Anthropic vs. the open-weights letter</title>
    <link href="https://www.anthropic.com/news/position-open-weights-models"/>
    <updated>2026-07-28T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/who-holds-the-gate-anthropic-vs-the-open-weights-letter/</id>
    <category term="ai"/>
    <category term="open-weights"/>
    <category term="anthropic"/>
    <category term="nvidia"/>
    <category term="policy"/>
    <category term="ai-safety"/>
    <category term="distillation"/>
    
    <content type="html">
      
      &lt;p&gt;Two documents, three days apart. On July 24, an industry letter, &lt;a href=&quot;https://images.nvidia.com/pdf/Open-Weights-and-American-AI-Leadership.pdf&quot;&gt;“Open Weights and American AI Leadership”&lt;/a&gt;, hosted on Nvidia’s servers, ~50 signatories from Microsoft to Meta to a Discord community, urges Washington not to restrict open models. On July 27, &lt;a href=&quot;https://www.anthropic.com/news/position-open-weights-models&quot;&gt;Anthropic responds&lt;/a&gt;: no ban, but chip controls, a crackdown on “industrial-scale distillation,” and mandatory safety testing for everything “open and closed.”&lt;/p&gt;

&lt;p&gt;I asked my agent to close-read both through one lens: fear of losing control. What came back holds up.&lt;/p&gt;

&lt;p&gt;Neither document names its real subject. The letter exists because Washington is weighing a ban on &lt;em&gt;Chinese&lt;/em&gt; models, and never contains the word “China,” because its signatories can’t say “let us keep building on Chinese capability” out loud, so it wraps open weights in the American flag instead (“sovereignty,” four times). Anthropic’s piece opens with a denial, “we never advocated a ban,” and its unprompted “it &lt;em&gt;would&lt;/em&gt; protect US AI companies from competition, but that has never been my goal” concedes the mechanism while disclaiming the motive.&lt;/p&gt;

&lt;p&gt;The actual collision point is one paragraph on each side: distillation. The letter rebrands it as “a long tradition of learning from, building upon, and improving existing technologies” and asks that misuse stay a contract dispute. Anthropic wants it treated closer to export-control law. That’s the money paragraph; the rest is atmosphere.&lt;/p&gt;

&lt;p&gt;And each side ignores the other’s strongest argument. The letter concedes irreversibility in a subordinate clause, “once released, the weights are beyond the original developer’s control,” and never answers it. Anthropic never touches concentration risk: a few closed labs as single points of failure.&lt;/p&gt;

&lt;p&gt;My takeaway: this isn’t safety vs. openness. Anthropic is afraid of losing the gate itself: open weights are the one release you can never meter, test, or revoke. The coalition is afraid a gate gets built that someone else holds. Everyone in this fight fears losing control; they just disagree about which loss is fatal.&lt;/p&gt;

      &lt;p&gt;&lt;a href="https://www.anthropic.com/news/position-open-weights-models"&gt;Original source&lt;/a&gt;&lt;/p&gt;
    </content>
  </entry>
  
  
  
  <entry>
    <title>Stapply Data: open job-market datasets across 15 ATS platforms</title>
    <link href="https://data.stapply.ai/"/>
    <updated>2026-07-28T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/stapply-data-open-job-market-datasets/</id>
    <category term="ai"/>
    <category term="agents"/>
    <category term="recruitment"/>
    <category term="data"/>
    <category term="ats"/>
    <category term="open-data"/>
    
    <content type="html">
      
      &lt;p&gt;&lt;a href=&quot;https://data.stapply.ai/&quot;&gt;Stapply Data&lt;/a&gt; publishes &lt;strong&gt;open snapshots of jobs and companies aggregated across 15 ATS platforms&lt;/strong&gt;: Ashby, Greenhouse, Lever, Workday, and others, as free CSV or Parquet downloads. The dataset is produced by &lt;a href=&quot;https://github.com/kalil0321/ats-scrapers&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ats-scrapers&lt;/code&gt;&lt;/a&gt;, an open-source Python toolkit: &lt;strong&gt;4.2M+ live jobs from 63,000+ companies across 49 sources&lt;/strong&gt;, with more than 50 reusable scraper adapters.&lt;/p&gt;

&lt;p&gt;Why this matters for the recruitment-agent thread:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Raw signal, not LinkedIn noise.&lt;/strong&gt; Most job discovery today is either keyword search on aggregators or recruiter spam. A clean, downloadable dataset of actual open roles is the kind of structured input an agent can reason over without scraping.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ATS-agnostic view.&lt;/strong&gt; Because it spans 15+ platforms, it avoids the trap of building against just one vendor’s API. An agent could normalize location, salary, seniority, and skills across Greenhouse, Ashby, Lever, Workday, SmartRecruiters, SuccessFactors, etc.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Toolkit, not just data.&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ats-scrapers&lt;/code&gt; provides &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;search()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_company()&lt;/code&gt;, and per-ATS scraper classes with a normalized &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Job&lt;/code&gt; schema. You can query the hosted dataset with no API key or run the scrapers yourself.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Perfect fuel for a “Jack”-style candidate agent.&lt;/strong&gt; Drop the dataset into a local vector store or feed it to an agent that ranks roles by fit, flags stale postings, and surfaces roles that never reach LinkedIn.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Complements the MCP idea.&lt;/strong&gt; A hiring platform with an MCP could expose its &lt;em&gt;own&lt;/em&gt; live openings; Stapply provides the aggregated baseline for discovery when no such interface exists yet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Stapply site is a JS-rendered SPA, so the ingestion only captured its shell and meta description. The GitHub repo fills in the actual scale, architecture, and API surface.&lt;/p&gt;

&lt;p&gt;(sources: &lt;a href=&quot;https://data.stapply.ai/&quot;&gt;Stapply Data&lt;/a&gt;, &lt;a href=&quot;https://github.com/kalil0321/ats-scrapers&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kalil0321/ats-scrapers&lt;/code&gt;&lt;/a&gt;)&lt;/p&gt;

      &lt;p&gt;&lt;a href="https://data.stapply.ai/"&gt;Original source&lt;/a&gt;&lt;/p&gt;
    </content>
  </entry>
  
  
  
  <entry>
    <title>Jack & Jill AI: a recruitment-agent reference close to the idea</title>
    <link href="https://www.jackandjill.ai/"/>
    <updated>2026-07-28T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/jack-and-jill-ai-recruitment-agent-reference/</id>
    <category term="ai"/>
    <category term="agents"/>
    <category term="recruitment"/>
    <category term="mcp"/>
    <category term="reference"/>
    <category term="jack-and-jill"/>
    
    <content type="html">
      
      &lt;p&gt;&lt;a href=&quot;https://www.jackandjill.ai/&quot;&gt;Jack &amp;amp; Jill&lt;/a&gt; is the closest live product I’ve seen to the &lt;strong&gt;agent-to-agent recruitment&lt;/strong&gt; idea from &lt;a href=&quot;recruitment-agents-talk-it-out-first.md&quot;&gt;last week&lt;/a&gt;. It splits the world into two agents:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Jack&lt;/strong&gt;: the candidate-side career agent. Has a 10-minute onboarding conversation, scans 15M jobs/day, runs mock interviews, gives salary benchmarks and negotiation coaching, and introduces the candidate directly to hiring managers.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Jill&lt;/strong&gt;: the employer-side recruiting agent. Hires through Jack’s network.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The business model is clean: &lt;strong&gt;Jack is completely free for candidates&lt;/strong&gt;; companies pay when they hire through Jill. That aligns incentives around successful placement rather than ad impressions or application volume.&lt;/p&gt;

&lt;p&gt;What overlaps with the “agents talk it out first” idea:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Conversational intake on both sides&lt;/strong&gt;: Jack learns the candidate’s preferences through chat; Jill presumably learns the company’s needs. The match happens agent-to-agent before humans meet.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Direct introductions, not applications&lt;/strong&gt;: the value proposition is skipping the ATS queue and going straight to the hiring manager.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Negotiation support&lt;/strong&gt;: Jack coaches salary conversations with market data, which is a lightweight version of agent-to-agent negotiation.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Mock interviews and prep&lt;/strong&gt;: the candidate agent doesn’t just find jobs, it helps land them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where it differs from the open MCP vision:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Jack &amp;amp; Jill is a &lt;strong&gt;closed agent pair&lt;/strong&gt;. You don’t bring your own agent; you use theirs. The user’s idea was more radical: any candidate agent should be able to talk to any hiring platform through a standard interface.&lt;/li&gt;
  &lt;li&gt;The negotiation still appears to be human-to-human, coached by Jack, rather than agent-to-agent on terms, start dates, and constraints.&lt;/li&gt;
  &lt;li&gt;It’s CV/job-board shaped at the core, even if the UX is conversational. The “shape your own tools” / fabric-style version might let candidates define custom evaluation rituals or portfolios instead of a traditional profile.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Worth watching because it proves the user experience works: 314K+ professionals, introductions that skip applications, and a free-for-candidates model funded by employers. If the open-agent/MCP version of this is going to exist, Jack &amp;amp; Jill shows what the closed version already looks like, and where the seams are.&lt;/p&gt;

&lt;p&gt;(source: &lt;a href=&quot;https://www.jackandjill.ai/&quot;&gt;jackandjill.ai&lt;/a&gt;)&lt;/p&gt;

      &lt;p&gt;&lt;a href="https://www.jackandjill.ai/"&gt;Original source&lt;/a&gt;&lt;/p&gt;
    </content>
  </entry>
  
  
  
  <entry>
    <title>pi-brain is circling the Pi universe: 257 downloads*</title>
    <link href="https://pi.dev/packages/@misabegovic/pi-brain?name=misabegovic"/>
    <updated>2026-07-27T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/pi-brain-in-the-pi-package-gallery/</id>
    <category term="pi"/>
    <category term="pi-brain"/>
    <category term="packages"/>
    <category term="knowledge-base"/>
    <category term="agents"/>
    <category term="milestone"/>
    
    <content type="html">
      
      &lt;p&gt;One of the packages circling around the Pi universe 😎: &lt;a href=&quot;https://pi.dev/packages/@misabegovic/pi-brain?name=misabegovic&quot;&gt;pi-brain&lt;/a&gt; is up in the &lt;a href=&quot;https://pi.dev/&quot;&gt;Pi&lt;/a&gt; package gallery, and the counter says 257 downloads thus far 😅&lt;/p&gt;

&lt;p&gt;Honesty clause before anyone (me included) gets excited: 257 downloads in four days is registry mirrors and scanners doing their rounds, not 257 people. npm download counts are famously generous that way. I’m not reading it as adoption either way yet; the real signal will be a second data point, or better, an issue filed by someone who isn’t me.&lt;/p&gt;

&lt;p&gt;The part that actually made me smile is just seeing it listed. The gallery page shows v0.3.2: the pi-brain extension itself plus the skills, prompts, and themes that ship with it, MIT-licensed with zero runtime dependencies. This very site runs on a clone of it, so the thing announcing the listing is also the thing being announced, which feels appropriately recursive.&lt;/p&gt;

&lt;p&gt;If you run Pi and want an intent store with a wiki, an inbox, and human-gated shaping baked into your sessions:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pi install npm:@misabegovic/pi-brain
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Source is at &lt;a href=&quot;https://github.com/misabegovic/pi-brain&quot;&gt;github.com/misabegovic/pi-brain&lt;/a&gt;. And with the notes from digging through Pi’s extension surface still fresh (compaction harvest, context injection, entry renderers), there’s a decent roadmap regardless of what the counter is actually counting.&lt;/p&gt;

      &lt;p&gt;&lt;a href="https://pi.dev/packages/@misabegovic/pi-brain?name=misabegovic"&gt;Original source&lt;/a&gt;&lt;/p&gt;
    </content>
  </entry>
  
  
  
  <entry>
    <title>Pi has way more extension surface than pi-brain uses</title>
    <link href="https://muhamed.at/brain/pi-untapped-extension-surface-for-pi-brain/"/>
    <updated>2026-07-27T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/pi-untapped-extension-surface-for-pi-brain/</id>
    <category term="pi"/>
    <category term="pi-brain"/>
    <category term="extensions"/>
    <category term="agents"/>
    <category term="architecture"/>
    
    <content type="html">
      
      &lt;p&gt;I ingested the Pi extension docs to understand what else pi-brain could tap into. The short answer: a lot.&lt;/p&gt;

&lt;p&gt;Right now pi-brain is mainly using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registerTool&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registerCommand&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sendUserMessage&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;session_start&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;session_tree&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before_agent_start&lt;/code&gt;. After reading the docs, the most interesting untapped pieces for a knowledge-base extension are:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compaction harvest&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;session_before_compact&lt;/code&gt; lets you inspect the messages about to be dropped and write a batched summary back into the session. This is the obvious place to extract decisions, constraints, and open questions before they vanish from context. The docs describe the exact entry format and how to return a custom compaction summary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context injection&lt;/strong&gt;: the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;context&lt;/code&gt; event fires before every LLM call and can inject extra messages. That means pi-brain could pull relevant records from the corpus and prepend them without waiting for the user to ask. Risky if overused, but powerful for grounding the agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool result interception&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tool_result&lt;/code&gt; can modify what the model sees after a tool runs. pi-brain could use this to attach citations, warn when a tool output is large, or enrich a result with related wiki links.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Entry renderers&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registerEntryRenderer&lt;/code&gt; lets an extension control how custom entries appear in the TUI. pi-brain could render status cards, inbox summaries, or brain widgets inline instead of just printing text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shortcuts and flags&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registerShortcut&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registerFlag&lt;/code&gt; could give brain commands real keybindings and CLI options, not just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/brain:*&lt;/code&gt; slash commands.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Event bus&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pi.events&lt;/code&gt; lets extensions communicate. That matters if pi-brain ever splits into smaller extensions or wants other extensions to react to brain state changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Session shutdown&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;session_shutdown&lt;/code&gt; is the right place to flush any in-memory state or close resources. Right now pi-brain does cleanup opportunistically; this would make it explicit.&lt;/p&gt;

&lt;p&gt;The biggest opportunity is probably &lt;strong&gt;compaction harvest&lt;/strong&gt; because it turns the session’s own forgetting into a capture mechanism. The risk is noise: a bad harvest floods the inbox and trains the user to ignore it. The docs suggest starting with a heuristic and measuring signal-to-noise before making it automatic.&lt;/p&gt;

&lt;p&gt;Next step is probably a small experiment: hook &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;session_before_compact&lt;/code&gt;, extract anything that looks like a decision or open question, and write it to a draft inbox item for review.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Bring your own agent to Usput.ba</title>
    <link href="https://muhamed.at/brain/usput-ba-bring-your-own-agent/"/>
    <updated>2026-07-27T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/usput-ba-bring-your-own-agent/</id>
    <category term="usput"/>
    <category term="agents"/>
    <category term="mcp"/>
    <category term="travel"/>
    <category term="ai"/>
    <category term="ideas"/>
    <category term="platform"/>
    
    <content type="html">
      
      &lt;p&gt;Been thinking about the “bring your own agent” idea and how it could apply to &lt;a href=&quot;https://usput.ba&quot;&gt;Usput.ba&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The future of human communication feels less like app-to-human and more like &lt;strong&gt;agent-to-agent&lt;/strong&gt;: you have your own AI that handles the tedious parts: asking questions, comparing options, negotiating, rescheduling. Why shouldn’t travel work the same way?&lt;/p&gt;

&lt;p&gt;For Usput.ba, that means exposing the platform through an &lt;strong&gt;MCP server&lt;/strong&gt; so agents on both sides of a trip can talk to our Platform agent directly. The agents ask, plan, and negotiate; the humans just approve the final proposal.&lt;/p&gt;

&lt;p&gt;This isn’t limited to travelers connecting their personal agents. Anyone offering something to a traveler can plug one in too:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Hotels, guesthouses, and apartments&lt;/strong&gt;: availability, pricing, special requests, check-in details.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Restaurants, cafés, and bars&lt;/strong&gt;: reservations, dietary options, group menus, opening hours.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tour guides and activity operators&lt;/strong&gt;: custom tours, group sizes, gear, weather contingencies.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Transfer and rental services&lt;/strong&gt;: pickups, routes, vehicle types.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Travel agencies, DMCs, and concierges&lt;/strong&gt;: packaged itineraries, negotiated rates, bundled services.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Individuals&lt;/strong&gt; renting a room, offering a cooking class, or guiding a hike.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Platform agent becomes a matchmaker between demand-side agents (travelers) and supply-side agents (providers). Instead of a traveler browsing a site, you get agent-to-agent negotiation across the whole ecosystem.&lt;/p&gt;

&lt;p&gt;What changes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Travelers&lt;/strong&gt; can plan in natural language across destinations, experiences, and constraints (“4 days in October, family with a toddler, wheelchair accessible”).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Preferences&lt;/strong&gt; that search forms suck at (“quiet mountain towns with good coffee,” “no crowds,” “pet-friendly”) become queryable because an agent can iterate.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Cross-service trips&lt;/strong&gt; get easier: the agent combines Usput.ba data with flights, weather, calendar, and hotels in one conversation.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Custom deals&lt;/strong&gt; like “private Una rafting for 8 people on July 14th” can be negotiated agent-to-agent before a human sees the final offer.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Providers&lt;/strong&gt; spend less time answering repeat questions and more time hosting.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Travel agencies and DMCs&lt;/strong&gt; can assemble complex, multi-provider itineraries without manual coordination.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Usput.ba&lt;/strong&gt; becomes the canonical coordination layer for Bosnian tourism; generic travel AIs ground themselves here instead of hallucinating.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The smallest safe step is a read-only MCP for travelers. Agent-to-agent negotiation and provider-facing agents come later.&lt;/p&gt;


      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Rails reference apps: lessons from Campfire, Writebook, and Fizzy</title>
    <link href="https://rubyonrails.org/docs/reference-apps"/>
    <updated>2026-07-27T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/rails-reference-apps-lessons/</id>
    <category term="rails"/>
    <category term="ruby"/>
    <category term="37signals"/>
    <category term="best-practices"/>
    <category term="architecture"/>
    <category term="hotwire"/>
    <category term="active-storage"/>
    <category term="sqlite"/>
    <category term="notifications"/>
    <category term="filters"/>
    
    <content type="html">
      
      &lt;p&gt;Spent more time inside the official &lt;a href=&quot;https://rubyonrails.org/docs/reference-apps&quot;&gt;Rails reference apps&lt;/a&gt; (Campfire, Writebook, and Fizzy), reading actual code, not just surface docs. Some deeper patterns worth keeping:&lt;/p&gt;

&lt;h2 id=&quot;campfire-real-time-chat&quot;&gt;Campfire: real-time chat&lt;/h2&gt;

&lt;h3 id=&quot;auth--sessions&quot;&gt;Auth &amp;amp; sessions&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authentication&lt;/code&gt; concern in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/controllers/concerns/authentication.rb&lt;/code&gt; restores a session from a signed permanent &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:session_token&lt;/code&gt; cookie, falls back to bot auth via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;params[:bot_key]&lt;/code&gt;, and stores a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;return_to&lt;/code&gt; URL for post-login redirects.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Session&lt;/code&gt; uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_secure_token&lt;/code&gt; and only refreshes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user_agent&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ip_address&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;last_active_at&lt;/code&gt; once per hour (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ACTIVITY_REFRESH_RATE = 1.hour&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User::Role&lt;/code&gt; is a tiny enum (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;member&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;administrator&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bot&lt;/code&gt;); &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;can_administer?(record)&lt;/code&gt; returns true for admins, the record’s creator, or new records.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;rooms--access&quot;&gt;Rooms &amp;amp; access&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Room&lt;/code&gt; uses STI: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rooms::Open&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rooms::Closed&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rooms::Direct&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Membership&lt;/code&gt; has an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;involvement&lt;/code&gt; enum (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;invisible&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nothing&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mentions&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;everything&lt;/code&gt;) and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unread_at&lt;/code&gt; for unread state.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Room.memberships&lt;/code&gt; exposes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grant_to&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;revoke_from&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;revise(granted:, revoked:)&lt;/code&gt; wrapped in a transaction.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RoomScoped&lt;/code&gt; loads &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Current.user.memberships.find_by!(room_id: ...)&lt;/code&gt; first, so room access is enforced by the association itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;real-time-layer&quot;&gt;Real-time layer&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Multiple Action Cable channels: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RoomChannel&lt;/code&gt; streams messages per room, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PresenceChannel&lt;/code&gt; tracks who’s online, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TypingNotificationsChannel&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnreadRoomsChannel&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReadRoomsChannel&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Message::Broadcasts&lt;/code&gt; handles Turbo append/remove plus a raw &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActionCable.server.broadcast(&quot;unread_rooms&quot;, ...)&lt;/code&gt; for the sidebar.&lt;/li&gt;
  &lt;li&gt;Messages broadcast themselves: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@message.broadcast_create&lt;/code&gt; is called from the controller after create.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;attachments&quot;&gt;Attachments&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Message::Attachment&lt;/code&gt; gives &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Message&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_one_attached :attachment&lt;/code&gt; with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:thumb&lt;/code&gt; variant.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Message.create_with_attachment!(...)&lt;/code&gt; calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;process_attachment&lt;/code&gt;, which analyzes the blob and builds a preview/representation (webp for video, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:thumb&lt;/code&gt; for images).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;bots--webhooks&quot;&gt;Bots &amp;amp; webhooks&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User::Bot&lt;/code&gt; treats bots as users with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;role: :bot&lt;/code&gt; and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bot_token&lt;/code&gt;; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User.create_bot!&lt;/code&gt; creates the bot and an optional &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Webhook&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Webhook#deliver&lt;/code&gt; POSTs a JSON payload (user, room, message) with a 7-second timeout, then posts the bot’s text or attachment reply back into the room.&lt;/li&gt;
  &lt;li&gt;Async via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Bot::WebhookJob&lt;/code&gt;; bot replies go through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Messages::ByBotsController&lt;/code&gt; with CSRF skipped for bot-key auth.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;pagination--search&quot;&gt;Pagination &amp;amp; search&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;geared_pagination&lt;/code&gt; with custom scopes: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;page_before&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;page_after&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;last_page&lt;/code&gt;. Good for chat-style “load more above/below”.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Message::Searchable&lt;/code&gt; maintains a custom SQLite FTS index manually via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;after_create_commit&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;after_update_commit&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;after_destroy_commit&lt;/code&gt;. No Elasticsearch, no pgvector.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;other&quot;&gt;Other&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Web Push via VAPID keys and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web-push&lt;/code&gt; gem.&lt;/li&gt;
  &lt;li&gt;OpenGraph metadata fetching lives in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/models/opengraph/&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fetch&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Document&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Location&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Metadata::Fetching&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;writebook-book-publishing&quot;&gt;Writebook: book publishing&lt;/h2&gt;

&lt;h3 id=&quot;auth--access&quot;&gt;Auth &amp;amp; access&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Same cookie/session pattern as Campfire; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User::Role&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;member&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;administrator&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Book::Accessable&lt;/code&gt; uses an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Access&lt;/code&gt; join model with levels &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reader&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;editor&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Book&lt;/code&gt; has an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;everyone_access&lt;/code&gt; flag; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Book.accessable_or_published&lt;/code&gt; shows published books or books the current user can access.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Book#update_access(editors:, readers:)&lt;/code&gt; uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;upsert_all&lt;/code&gt; and deletes any access not in the new set.&lt;/li&gt;
  &lt;li&gt;Join codes for account access. Books can be public or private.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;publishing-flow&quot;&gt;Publishing flow&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Book&lt;/code&gt; has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;published&lt;/code&gt; boolean and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_one_attached :cover&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BooksController#ensure_editable&lt;/code&gt; checks &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Book#editable?&lt;/code&gt; (editor access or admin).&lt;/li&gt;
  &lt;li&gt;Books render HTML or Markdown (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format.md&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;delegated-types-for-content-shapes&quot;&gt;Delegated types for content shapes&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Book&lt;/code&gt; has many &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Leaf&lt;/code&gt; records. A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Leaf&lt;/code&gt; is a container with position, status, slug.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Leaf&lt;/code&gt; uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delegated_type :leafable, types: %w[ Page Section Picture ]&lt;/code&gt;. Each leafable type has its own table and logic, but shares ordering/search/status through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Leaf&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Leafable&lt;/code&gt; concern gives each type the inverse &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_one :leaf&lt;/code&gt; and delegates &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;title&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;leaf-lifecycle&quot;&gt;Leaf lifecycle&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Book#press(leafable, leaf_params)&lt;/code&gt; creates a new leaf.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Leaf::Editable&lt;/code&gt; records a new version only when the leafable body changes and the last edit is older than 10 minutes; it duplicates the leafable and re-attaches blobs.&lt;/li&gt;
  &lt;li&gt;Leaves can be trashed (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;status: trashed&lt;/code&gt;) rather than deleted.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;ordering&quot;&gt;Ordering&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Positionable&lt;/code&gt; concern implements gap-based ordering with parent-level locking and automatic rebalancing when gaps get too small.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;markdown-rendering&quot;&gt;Markdown rendering&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActionText::Markdown&lt;/code&gt; model in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib/rails_ext/action_text_markdown.rb&lt;/code&gt; wraps Redcarpet with extensions for autolink, fenced code, tables, strikethrough, etc.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_markdown :body&lt;/code&gt; macro in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib/rails_ext/action_text_has_markdown.rb&lt;/code&gt; adds a polymorphic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;markdown_body&lt;/code&gt; association and reader/writer methods.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MarkdownRenderer&lt;/code&gt; adds Rouge syntax highlighting, header anchor links, and lightbox-wrapped images.&lt;/li&gt;
  &lt;li&gt;Markdown attachments use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActionText::Markdown::Uploads&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_many_attached :uploads&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;search&quot;&gt;Search&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;SQLite FTS &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;leaf_search_index&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;title&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt; columns, maintained manually and ordered by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bm25(leaf_search_index, 2.0)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;fizzy-kanbanissue-tracking&quot;&gt;Fizzy: kanban/issue tracking&lt;/h2&gt;

&lt;h3 id=&quot;multi-tenancy--auth&quot;&gt;Multi-tenancy &amp;amp; auth&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;URL-path based tenancy: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/{account_id}/boards/...&lt;/code&gt;. Middleware &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AccountSlug::Extractor&lt;/code&gt; pulls the account slug, moves it from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PATH_INFO&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SCRIPT_NAME&lt;/code&gt;, and sets &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Current.account&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Every model has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;account_id&lt;/code&gt;. Background jobs capture and restore &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Current.account&lt;/code&gt; automatically.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Identity&lt;/code&gt; is global (email). &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; is per-account membership. One identity can belong to many accounts.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User::Role&lt;/code&gt; enum: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;owner&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;member&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;system&lt;/code&gt;; admin scope includes owners.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User::Accessor&lt;/code&gt; auto-grants access to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;all_access&lt;/code&gt; boards and exposes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;accessible_cards&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;accessible_comments&lt;/code&gt; through board access.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;board--column--card-domain&quot;&gt;Board / column / card domain&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Board&lt;/code&gt; is heavy on concerns: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Accessible&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AutoPostponing&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cards&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Entropic&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Filterable&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publishable&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Triageable&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Column&lt;/code&gt; belongs to board, has color/position, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cards&lt;/code&gt; are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nullify&lt;/code&gt; on column delete.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Card&lt;/code&gt; status enum &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;drafted&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;published&lt;/code&gt;; sequential &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;number&lt;/code&gt; scoped to the account, assigned inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;account.with_lock&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Card&lt;/code&gt; scopes: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;indexed_by&lt;/code&gt; (all/closed/not_now/stalled/postponing_soon/golden) and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sorted_by&lt;/code&gt; (newest/oldest/latest).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Card::Eventable&lt;/code&gt; creates an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Event&lt;/code&gt; on publish/title change and writes a system comment.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Event&lt;/code&gt; is polymorphic (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eventable&lt;/code&gt;) with JSON &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;particulars&lt;/code&gt;. Events drive activity timeline, notifications, and webhooks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;notifications&quot;&gt;Notifications&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Notifier.for(source)&lt;/code&gt; dispatches by source type (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Event&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mention&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Notifier::CardEventNotifier&lt;/code&gt; picks recipients per action:
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;card_assigned&lt;/code&gt; → assignees except creator&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;card_published&lt;/code&gt; → board watchers + assignees, excluding creator/mentionees&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;comment_created&lt;/code&gt; → card watchers excluding creator/mentionees&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Notification&lt;/code&gt; is linked to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;card&lt;/code&gt; and a polymorphic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt;; it calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user.bundle(self)&lt;/code&gt; when the user has email bundling enabled.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Notification::Bundle&lt;/code&gt; aggregates unread notifications into a time window and is delivered by a recurring Solid Queue job every 30 minutes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;filter-system&quot;&gt;Filter system&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Filter&lt;/code&gt; is a persisted query object with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fields&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Params&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Resources&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Summarized&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Filter::Params&lt;/code&gt; normalizes params and stores an MD5 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;params_digest&lt;/code&gt; for deduplication.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Filter::Resources&lt;/code&gt; uses HABTM for tags, boards, assignees, creators, and closers.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Filter#cards&lt;/code&gt; builds the query by chaining scopes: index/sort, assignees, creators, boards, tags, creation/closure windows, terms (mentions), and columns.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User::Filtering&lt;/code&gt; is a presenter that prepares the UI state (boards, tags, users, saved filters).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;import--export&quot;&gt;Import / export&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Export&lt;/code&gt; base model attaches a zip and runs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DataExportJob&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Account::Export#populate_zip&lt;/code&gt; iterates &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Account::DataTransfer::Manifest&lt;/code&gt;, which defines an ordered list of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RecordSet&lt;/code&gt;s (account, users, boards, columns, cards, comments, events, notifications, Active Storage blobs/attachments/files, etc.).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Account::DataTransfer::RecordSet&lt;/code&gt; writes one JSON file per record under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data/{table_name}/{id}.json&lt;/code&gt;, imports in batches of 100, checks ID integrity, and rejects imports that would conflict with existing records.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Account::Import&lt;/code&gt; checks or processes the zip, then reconciles &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cards_count&lt;/code&gt;, all-access board membership for the importer, and storage totals.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ZipFile&lt;/code&gt; abstracts disk vs S3 streaming with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ZipKit&lt;/code&gt;; exports can stream directly to S3 via multipart upload.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;entropy--jobs&quot;&gt;Entropy &amp;amp; jobs&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Cards auto-postpone to “not now” after inactivity. Configurable at account and board level. A recurring Solid Queue job cleans stale cards hourly.&lt;/li&gt;
  &lt;li&gt;Uses Solid Queue (database-backed, no Redis). Shallow job classes delegate to model methods.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_later&lt;/code&gt; methods enqueue, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_now&lt;/code&gt; methods do the synchronous work.&lt;/li&gt;
  &lt;li&gt;Recurring tasks configured in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/recurring.yml&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;search--ids&quot;&gt;Search &amp;amp; IDs&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;16-shard MySQL full-text search, sharded by account ID hash. Models in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/models/search/&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;UUIDv7 primary keys, base36-encoded as 25-character strings. Fixtures use deterministic older UUIDs so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.first&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.last&lt;/code&gt; behave predictably in tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;deployment&quot;&gt;Deployment&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Kamal in production. OSS Docker image plus a private &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fizzy-saas&lt;/code&gt; gem for billing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;cross-cutting-style-and-conventions&quot;&gt;Cross-cutting style and conventions&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Vanilla Rails, rich models.&lt;/strong&gt; Controllers stay thin. Models expose intention-revealing methods like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@card.close&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@board.cards.create!&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@card.gild&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;REST resources over custom actions.&lt;/strong&gt; Closing a card is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cards::ClosuresController#create&lt;/code&gt;, not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;post :close&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Concerns for behavior slices.&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Card::Commentable&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Card::Closeable&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Card::Entropic&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Message::Searchable&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User::Mentionable&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Expanded conditionals over guard clauses.&lt;/strong&gt; Guard clauses only at the very top or when the body is large.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Method ordering:&lt;/strong&gt; class methods, public methods with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize&lt;/code&gt; first, private methods, all ordered by invocation order.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!&lt;/code&gt; only when there’s a non-&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!&lt;/code&gt; counterpart.&lt;/strong&gt; Not just to signal mutation.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Test conventions:&lt;/strong&gt; use fixtures, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_path&lt;/code&gt; helpers, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assert_in_body&lt;/code&gt;, omit explicit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{ render }&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;respond_to&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-stands-out&quot;&gt;What stands out&lt;/h2&gt;

&lt;p&gt;These apps don’t avoid Rails features; they use delegated types, polymorphic associations, concerns, Action Cable, Turbo, Solid Queue. But they avoid adding architectural layers until the domain demands it. The result is code that reads like the product it builds.&lt;/p&gt;

      &lt;p&gt;&lt;a href="https://rubyonrails.org/docs/reference-apps"&gt;Original source&lt;/a&gt;&lt;/p&gt;
    </content>
  </entry>
  
  
  
  <entry>
    <title>On the reading list: I gave Pi one tool</title>
    <link href="https://monotykamary.com/posts/i-gave-pi-one-tool/"/>
    <updated>2026-07-25T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/i-gave-pi-one-tool/</id>
    <category term="ai"/>
    <category term="agents"/>
    <category term="pi"/>
    <category term="pi-brain"/>
    <category term="code-mode"/>
    <category term="typescript"/>
    <category term="sandbox"/>
    <category term="tools"/>
    <category term="reading-list"/>
    
    <content type="html">
      
      &lt;p&gt;Queued up for a proper read: &lt;a href=&quot;https://monotykamary.com/posts/i-gave-pi-one-tool/&quot;&gt;I gave Pi one tool&lt;/a&gt; by Tom (monotykamary).&lt;/p&gt;

&lt;p&gt;The premise alone earns it a spot on the list. Instead of handing the &lt;a href=&quot;https://pi.dev/&quot;&gt;Pi&lt;/a&gt; coding agent a box of verbs (read, search, run, edit), Tom collapsed everything into a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fabric_exec&lt;/code&gt; tool that accepts TypeScript. The model writes a small program against Pi’s capabilities, it runs in a QuickJS sandbox, and only the returned evidence reaches the conversation. A loop becomes an actual loop; parallel work becomes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Promise.all&lt;/code&gt;; twenty megabytes of tool output becomes ten useful lines.&lt;/p&gt;

&lt;p&gt;Then it gets recursive: most of pi-fabric was built by models working &lt;em&gt;through&lt;/em&gt; pi-fabric: 337 commits and 11,155 fabric programs in twelve days, with Tom steering, stealing good ideas, and rejecting bad ones.&lt;/p&gt;

&lt;p&gt;This lands right in the middle of my Pi obsession, the same orbit as the shape-your-own-tools prompt I captured earlier. Skimming it, the parts I want to sit down with: tool schemas as an entropy source (models drift while reproducing unfamiliar schemas, so give them a compiler instead), deliberate &lt;em&gt;erasure&lt;/em&gt; (compaction, forgetting, and state transitions as runtime features), and the honest cost accounting: reading, not writing, is the bill.&lt;/p&gt;

&lt;p&gt;Note to self: before going deeper I need to learn more about how Pi itself is built (the extension API and its lifecycle events, sessions as JSONL trees, compaction, the SDK and RPC modes) so I can adjust &lt;a href=&quot;https://github.com/misabegovic/pi-brain&quot;&gt;pi-brain&lt;/a&gt; to get the most out of the harness, and honestly validate whether the idea makes sense at all next to runtimes like pi-fabric.&lt;/p&gt;

      &lt;p&gt;&lt;a href="https://monotykamary.com/posts/i-gave-pi-one-tool/"&gt;Original source&lt;/a&gt;&lt;/p&gt;
    </content>
  </entry>
  
  
  
  <entry>
    <title>Shape your own tools: a prompt instead of a repo</title>
    <link href="https://muhamed.at/brain/shape-your-own-tools-pi-web-ui-prompt/"/>
    <updated>2026-07-25T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/shape-your-own-tools-pi-web-ui-prompt/</id>
    <category term="ai"/>
    <category term="agents"/>
    <category term="pi"/>
    <category term="prompts"/>
    <category term="tools"/>
    <category term="tailscale"/>
    <category term="self-hosting"/>
    <category term="ideas"/>
    
    <content type="html">
      
      &lt;p&gt;Someone asked &lt;a href=&quot;https://x.com/dodoreach&quot;&gt;@dodoreach&lt;/a&gt; to open source their web UI for the &lt;a href=&quot;https://pi.dev/&quot;&gt;Pi&lt;/a&gt; coding agent. The answer stuck with me more than any repo could:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;the best thing @pidotdev taught me is that learning to shape your own tools is better than accepting someone elses finished product&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So instead of a repo, they shared a &lt;strong&gt;one-shot prompt&lt;/strong&gt;: paste it into your agent and it builds you your own “codex-style” dashboard for Pi: pick a project, create or resume native Pi sessions, watch text, thinking, and tool activity stream in, steer mid-run, queue follow-ups, stop, switch models, all from desktop or your phone, privately over Tailscale Serve. The browser is only a control surface; Pi stays the agent and the source of truth.&lt;/p&gt;

&lt;p&gt;What I find remarkable is how much engineering judgment is packed into the prompt itself. It doesn’t just describe an app, it encodes discipline: verify the real environment before coding, pin the SDK to the installed CLI version, keep the integration behind a typed adapter with a deterministic fake so tests never burn tokens, bind to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1&lt;/code&gt; as the security boundary, no telemetry, no public hosting. A prompt like that is a spec, a threat model, and a teaching document at once. Prompts might be the new way to open source.&lt;/p&gt;

&lt;p&gt;The energy of the follow-up post sells it better than any feature list: using Pi to build a mobile companion for a Pi app: &lt;em&gt;“going camping now but you are coming with me buddy.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Note to self: I need to do the same for myself. Run the prompt against my own Pi setup, shape the tool to my hands, and take my agent camping.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Is Ruby dead? Cathedrals in Go, and a rebuild experiment</title>
    <link href="https://muhamed.at/brain/is-ruby-dead-cathedrals-in-go/"/>
    <updated>2026-07-25T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/is-ruby-dead-cathedrals-in-go/</id>
    <category term="ruby"/>
    <category term="go"/>
    <category term="rails"/>
    <category term="distributed-systems"/>
    <category term="chat"/>
    <category term="infrastructure"/>
    <category term="learning"/>
    <category term="ideas"/>
    
    <content type="html">
      
      &lt;p&gt;Is Ruby dead? Is it time to build cathedrals in Go and other languages?&lt;/p&gt;

&lt;p&gt;The question has been sitting with me lately. Maybe I need to actually learn Go, and learn how to build distributed infrastructures for things like chat systems, instead of theorizing about it from the Rails side.&lt;/p&gt;

&lt;p&gt;So, note to myself, an experiment:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Rebuild a Rails project in Go&lt;/strong&gt;, and rebuild the infrastructure around it too, not just the app code.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Compare cost, speed, and complexity&lt;/strong&gt; between the two versions: what does the Go cathedral actually buy, and what does it charge for it?&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Identify the guard rails&lt;/strong&gt;: what keeps you safe in Go where Rails conventions used to?&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Figure out ways to test&lt;/strong&gt; it all: what does a good testing story look like on the other side?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not a verdict on Ruby. A way to find out for myself.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Recruitment agents: let the agents talk it out first</title>
    <link href="https://muhamed.at/brain/recruitment-agents-talk-it-out-first/"/>
    <updated>2026-07-25T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/recruitment-agents-talk-it-out-first/</id>
    <category term="ai"/>
    <category term="agents"/>
    <category term="recruitment"/>
    <category term="mcp"/>
    <category term="negotiation"/>
    <category term="ideas"/>
    
    <content type="html">
      
      &lt;p&gt;There’s this idea that the future of everything in human communication is having your own agent that does the difficult bits for you: asking the nagging questions about a hotel booking, ordering stuff and handling the negotiation, arranging and managing your vacation through whatever the future of Booking becomes. &lt;a href=&quot;https://x.com/marckohlbrugge/status/2079331047452590424&quot;&gt;Marc Kohlbrugge’s example&lt;/a&gt; captures it well.&lt;/p&gt;

&lt;p&gt;So: why not do this for recruitment?&lt;/p&gt;

&lt;p&gt;Picture agents hooked into channels on both sides (people looking for a job and people hiring) where &lt;strong&gt;the agents talk it out amongst themselves first&lt;/strong&gt;. Availability, salary ranges, must-haves, dealbreakers, all the awkward early back-and-forth handled agent-to-agent before any human spends time on it. Something in the spirit of &lt;a href=&quot;https://rentahuman.ai/&quot;&gt;rentahuman.ai&lt;/a&gt;, but for matching people with jobs instead of agents with gig workers.&lt;/p&gt;

&lt;p&gt;Recruitment feels unusually well-suited for this. The early funnel is mostly structured information exchange that both sides find tedious, and both sides already automate it badly: ATS filters on one end, mass-apply tools on the other. Agent-to-agent negotiation is arguably a more honest version of an arms race that’s already happening.&lt;/p&gt;

&lt;p&gt;The first step that seems easy to do now: &lt;strong&gt;hiring platforms exposing an MCP that supports candidates, not just recruiters&lt;/strong&gt;. That’s a partial version of the full idea: it gives a candidate’s agent, whichever agent they use, a real channel into the hiring side, and makes finding a job easier long before the full agent-to-agent negotiation loop exists.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>OpenAI Flex Processing and webhook gaps</title>
    <link href="https://developers.openai.com/api/docs/guides/flex-processing"/>
    <updated>2026-07-23T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/openai-flex-processing-and-webhook-gaps/</id>
    <category term="ai"/>
    <category term="openai"/>
    <category term="api"/>
    <category term="webhooks"/>
    <category term="flex-processing"/>
    <category term="europe"/>
    
    <content type="html">
      
      &lt;p&gt;Looking into &lt;a href=&quot;https://developers.openai.com/api/docs/guides/flex-processing&quot;&gt;OpenAI Flex Processing&lt;/a&gt; for cheaper asynchronous workloads. The pitch is simple: lower-cost processing for tasks that don’t need an immediate response.&lt;/p&gt;

&lt;p&gt;The catch for us in Europe: OpenAI webhooks aren’t available here yet. Without webhooks, there’s no clean push notification when a long-running job finishes, so we’re stuck polling or building our own completion-tracking layer.&lt;/p&gt;

&lt;p&gt;Alternatives to explore:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;AWS Bedrock&lt;/strong&gt;: may have better async/notification support in EU regions.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Azure OpenAI&lt;/strong&gt;: often has different regional availability and webhook/eventing options.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For now, Flex Processing is on the list, but only if the polling overhead is worth the cost savings.&lt;/p&gt;

      &lt;p&gt;&lt;a href="https://developers.openai.com/api/docs/guides/flex-processing"&gt;Original source&lt;/a&gt;&lt;/p&gt;
    </content>
  </entry>
  
  
  
  <entry>
    <title>Herdr: an agent multiplexer</title>
    <link href="https://herdr.dev/"/>
    <updated>2026-07-23T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/herdr-agent-multiplexer/</id>
    <category term="ai"/>
    <category term="agents"/>
    <category term="tools"/>
    <category term="terminal"/>
    <category term="workflow"/>
    
    <content type="html">
      
      &lt;p&gt;&lt;a href=&quot;https://herdr.dev/&quot;&gt;Herdr&lt;/a&gt; looks interesting: “one terminal for the whole herd.” It’s pitched as what tmux is to terminals, but for coding agents.&lt;/p&gt;

&lt;p&gt;The idea is to run all your agents from one terminal, on any machine, over SSH. Each agent gets its own real terminal on a server that keeps it alive when you close your laptop. You can see states like blocked, working, and done at a glance, and reattach from another device.&lt;/p&gt;

&lt;p&gt;This could be a useful piece of the “intent + context + agents” workflow I’m thinking about: a persistent place where long-running agent work lives, instead of losing state every time a local session ends.&lt;/p&gt;

      &lt;p&gt;&lt;a href="https://herdr.dev/"&gt;Original source&lt;/a&gt;&lt;/p&gt;
    </content>
  </entry>
  
  
  
  <entry>
    <title>Langfuse for prompt experiments and scoring</title>
    <link href="https://github.com/langfuse/langfuse"/>
    <updated>2026-07-23T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/langfuse-for-prompt-experiments-and-scoring/</id>
    <category term="ai"/>
    <category term="llm"/>
    <category term="langfuse"/>
    <category term="prompts"/>
    <category term="evaluation"/>
    <category term="observability"/>
    
    <content type="html">
      
      &lt;p&gt;Been diving into &lt;a href=&quot;https://github.com/langfuse/langfuse&quot;&gt;Langfuse&lt;/a&gt; as the likely backbone for managing Usput’s AI workflows.&lt;/p&gt;

&lt;p&gt;The parts I care most about:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Prompt Management&lt;/strong&gt;: version-controlled prompts with client/server caching, so we can iterate without adding latency.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Datasets &amp;amp; Experiments&lt;/strong&gt;: structured test sets and benchmarks for continuous improvement before deployment.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Evaluations&lt;/strong&gt;: LLM-as-a-judge, code evaluators, user feedback, manual labels, and custom pipelines.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Scoring&lt;/strong&gt;: deterministic checks mixed with model-based judges, so we can measure output quality automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is exactly what we need for the AI-generated content pipeline: trace every generation, score it, run challengers against current prompts, and only promote versions that beat the baseline. The goal is to move from “AI generated and hopefully good” to “measured, compared, and released with confidence.”&lt;/p&gt;

&lt;p&gt;Self-hosting is also an option, which matters for keeping content and prompts inside our own infrastructure.&lt;/p&gt;

      &lt;p&gt;&lt;a href="https://github.com/langfuse/langfuse"&gt;Original source&lt;/a&gt;&lt;/p&gt;
    </content>
  </entry>
  
  
  
  <entry>
    <title>Intent, agents, and the end of the SDLC</title>
    <link href="https://muhamed.at/brain/intent-agents-and-the-end-of-sdlc/"/>
    <updated>2026-07-23T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/intent-agents-and-the-end-of-sdlc/</id>
    <category term="ai"/>
    <category term="agents"/>
    <category term="intent"/>
    <category term="workflow"/>
    <category term="sdlc"/>
    <category term="rubyconf"/>
    <category term="tools"/>
    
    <content type="html">
      
      &lt;p&gt;I’ve been obsessed with a single question: &lt;strong&gt;how do you actually work with AI tools in a way that scales?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The old software development lifecycle feels like it’s collapsing. Boris Tane’s &lt;a href=&quot;https://boristane.com/blog/the-software-development-lifecycle-is-dead/&quot;&gt;“The Software Development Lifecycle Is Dead”&lt;/a&gt; nails it: AI agents didn’t make the SDLC faster, they killed it. The new loop is tighter: intent → agent → code + tests + deployment → does it work? → ship or iterate. Requirements, design, implementation, testing, review, and deployment are merging into one continuous conversation.&lt;/p&gt;

&lt;p&gt;That shifts the bottleneck from execution to &lt;strong&gt;intent management&lt;/strong&gt;. The skill becomes: can you articulate what you want, provide the right context, and steer the agent without micromanaging it?&lt;/p&gt;

&lt;p&gt;I’ve been collecting signals around this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://buzz.xyz/&quot;&gt;Buzz&lt;/a&gt;: people, agents, and projects in one place.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://freeq.at/&quot;&gt;Freeq&lt;/a&gt;: IRC with Bluesky identity, a different take on how identity and conversation flow.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/misabegovic/pi-brain&quot;&gt;pi-brain&lt;/a&gt;: my own experiment in building a system that captures intent and context.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubynative.com/&quot;&gt;Ruby Native&lt;/a&gt;: collapsing mobile delivery into a config file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pattern I keep seeing: &lt;strong&gt;store intent, feed context, let agents execute.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Phoenix Architecture takes this even further. Its framing is that production truth should be a first-class input to software creation: requirements include operational constraints, telemetry gets canonicalized into evidence, and when evidence drifts, only the affected subgraph is invalidated and regenerated. A module isn’t good because it once passed tests; it’s good only as long as the evidence supports the claim that it satisfies the requirement. This is the intent-store idea in a different shape.&lt;/p&gt;

&lt;p&gt;This is also a big part of why I organized &lt;a href=&quot;https://rubyconf.at&quot;&gt;rubyconf.at&lt;/a&gt;: I want to be closer to the people thinking seriously about what Ruby and Rails development looks like when agents are doing most of the typing.&lt;/p&gt;

      
    </content>
  </entry>
  
  
  
  <entry>
    <title>Ruby Native and mobile rollout strategy</title>
    <link href="https://rubynative.com/"/>
    <updated>2026-07-23T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/ruby-native-and-mobile-rollout/</id>
    <category term="ruby"/>
    <category term="rails"/>
    <category term="mobile"/>
    <category term="app-stores"/>
    <category term="usput"/>
    <category term="ruby-native"/>
    
    <content type="html">
      
      &lt;p&gt;I’m paying close attention to &lt;a href=&quot;https://rubynative.com/&quot;&gt;Ruby Native&lt;/a&gt;. The pitch is exactly what I need: turn a Rails app into a real iOS/Android app via YAML config and a few ERB helpers, without touching Swift, Kotlin, Xcode, or Android Studio.&lt;/p&gt;

&lt;p&gt;The comparison on their site is convincing. PWA lacks store presence and real push notifications. Capacitor still needs native project housekeeping. Hotwire Native is powerful but requires native code. Ruby Native sits in the sweet spot: config-driven, cloud builds, native UI, and both app stores.&lt;/p&gt;

&lt;p&gt;For now, I’m waiting for Android support to land properly. Once it does, the plan is to use Ruby Native as the default path for rolling apps like Usput.ba out to the App Store and Google Play.&lt;/p&gt;

      &lt;p&gt;&lt;a href="https://rubynative.com/"&gt;Original source&lt;/a&gt;&lt;/p&gt;
    </content>
  </entry>
  
  
  
  <entry>
    <title>Usput.ba next phase</title>
    <link href="https://muhamed.at/brain/usput-next-phase/"/>
    <updated>2026-07-23T00:00:00+00:00</updated>
    <id>https://muhamed.at/brain/usput-next-phase/</id>
    <category term="usput"/>
    <category term="product-planning"/>
    <category term="rails"/>
    <category term="bosnia"/>
    <category term="mobile"/>
    <category term="ai"/>
    
    <content type="html">
      
      &lt;p&gt;Been thinking about what comes next for &lt;a href=&quot;https://usput.ba&quot;&gt;Usput.ba&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The plan is simple: &lt;strong&gt;fix the app first, then generate content.&lt;/strong&gt; I announced a lot: 14 languages, audio tours, travel plans, public plans, experiences, reviews, dark/light mode, offline support, PWA install, AI-generated content, and more. Most of it exists in some form, but the real problem now is accuracy and making the whole thing feel easy and fun to use.&lt;/p&gt;

&lt;p&gt;After the app is stable, a few things are on the horizon:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Moments&lt;/strong&gt;: short posts from travelers exploring Bosnia with Usput.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Explore mode&lt;/strong&gt;: a better way to discover places and experiences.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Mentoring Aldin&lt;/strong&gt; on Rails so he can own parts of the platform.&lt;/li&gt;
  &lt;li&gt;A &lt;strong&gt;native mobile app&lt;/strong&gt; built with &lt;a href=&quot;https://rubynative.com&quot;&gt;Ruby Native&lt;/a&gt;, so I can ship iOS/Android without writing Swift or Kotlin.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For now, the focus is closing the gap between what I promised and what actually works.&lt;/p&gt;

      
    </content>
  </entry>
  
  
</feed>
