Codex Chronicle was paying for every frame.

Looki Ingest

I built a four-sensor Gemma 4 replacement on a Mac mini.

For about a week I had OpenAI’s research-preview Chronicle running on my MacBook. Every ten minutes it screenshotted my display, uploaded frames to OpenAI for analysis, and wrote Markdown summaries on my Mac. I was crawling that folder and ingesting the data in a Postgres table on my homelab.

It worked.

It also cost credits for every cycle of attention.

This weekend I replaced it with a single Gemma 4 E4B 4-bit MLX instance running on a $599 Mac mini, summarizing four independent sensor streams locally with zero outbound LLM calls and effectively zero marginal inference cost.

OpenAI describes the constraints plainly in their own documentation: screen captures are uploaded to OpenAI’s servers for processing, the feature “uses rate limits quickly,” it “increases risk of prompt injection,” memories are stored as “unencrypted Markdown files” on the user’s machine, and it is unavailable in the EU, UK, and Switzerland. Chronicle is a Pro-tier feature on a Pro-tier price. The architectural choice is honest: cloud inference, per-frame cost, the model belongs to OpenAI.

I wanted a different shape.

What I built

This weekend I replaced Chronicle. Not with a better cloud service. With a single Gemma 4 E4B 4-bit MLX instance on a $599 Mac mini, summarizing video from four sensors (my screen, a wearable camera, the security cameras in my living room, and the wearable’s realtime AI commentary) and writing them all to one Postgres table, redacted at ingest, queryable in SQL. Zero outbound LLM calls. Zero per-frame cost.

The same model instance also serves the rest of my homelab’s vision workloads.

The marginal cost of adding the fifth sensor (which is already in a box on the way) is whatever shipping cost was paid for a Raspberry Pi Zero 2 W.

This is the sequel to a piece I published five days ago about putting Gemma 4 behind my homelab AI gateway. That one ended with: “Anvil is not just a dev box. For some multimodal work, it is a useful inference target.” This is about Anvil graduating.

Why Gemma 4 E4B specifically

The reasoning, in order of how much each one mattered to me:

  1. Native multimodal in one checkpoint. Image AND video AND audio paths in the same file. The whole sensor mesh runs through one weights load. No model swap per input type.

  2. 16 GB of unified memory is enough. The 4-bit MLX build sits at about 6 GB peak resident in isolation, around 8.5 GB under co-tenant load. On a base M-series Mac mini that leaves comfortable headroom for the OS, the FastAPI daemon, and a menubar app to watch it.

  3. Apache 2.0 weights. The model file is on my machine. Nobody can deprecate it out from under me, reprice it overnight, or restrict it by jurisdiction.

  4. It’s already loaded. I was routing this exact model through Forge for unrelated work. Spinning a second model for Logbook specifically would have been waste. One Gemma 4 instance. Two production roles.

Four sensors, one envelope

  [MacBook Screen]   [Looki Wearable]   [Blink Cameras]
         │                  │                  │
         └──────────────────┼──────────────────┘
                            ▼
                   [Logbook Producers]
                            │
                            ▼
                  [Anvil / Gemma 4 E4B]
                            │
                            ▼
                    [Redaction Layer]
                            │
                            ▼
                       [Postgres]

Every Logbook row is an observation.event.v1 envelope. The schema fits in one paragraph: a deterministic UUIDv5, a source enum, a captured_at timestamp, a clip duration_s, optional frame_count, an image_summary, an optional video_summary, a media_uri for the staging location, an inference_metadata blob, and a source_metadata blob. Same schema, four producers.

The producers:

  1. MacBook screen. A Python capture daemon running as a LaunchAgent. Records a short screen video on a fixed cadence, pauses when HID idle exceeds 10 minutes, POSTs the clip to Anvil for analysis, then POSTs the resulting envelope to the homelab ingest endpoint.

  2. Looki wearable (clips). A worker polls the wearable’s cloud, stages new motion clips to local NVMe, runs them through the same Anvil daemon.

Looki Ingest

  1. Looki wearable (realtime). The wearable emits realtime AI commentary as text events. A second worker forwards those as image-summary-only observations into the same table.

  2. Blink security cameras. A continuous Node.js daemon polls Blink’s cloud, stages motion clips to NVMe, hands them to Anvil.

Every clip lands on the same Anvil daemon, which runs one Gemma 4 E4B 4-bit MLX instance. The daemon serves two surfaces:

  • /v1/analyze for Logbook (image-pass + native-video-pass per clip).

  • /v1/chat/completions and /v1/responses for every other Forge VLM client in the homelab.

The model does not care which surface called it. The previous standalone gemma-4-multimodal LaunchAgent was retired and its plist removed. End state: one Gemma 4 instance, dual-purpose, no duplication.

Redaction happens once, at the ingest endpoint, before the INSERT. UUIDs, filesystem paths, IPv4 and IPv6, internal hostnames, email addresses, API key shapes. Single pass.

The day the model pretended to watch video

For most of the build day, Logbook produced two summaries per clip: one from a native-video call mlx_vlm.generate(video=path, fps=1.0), and one from a separate frame-extracted multi-image pass.

The image summaries were excellent. They read pixels at 1280 px width and reported real strings: Termius, Phase 9, LOGBOOK_BUILD_BRIEF.md. Per-capture variation. Forensic detail. Anyone reading the raw table rows could tell which IDE window was on top.

The video summaries were a different story. Every video summary for every mac_screen capture, hour after hour, described “a person standing in a kitchen setting, facing a counter, holding a small dark object.” Word for word. The MacBook does not have a webcam pointed at the kitchen. The capture content was screen recordings.

I revised the prompt to be explicit (“you are observing a screen recording from a computer display”). Every video summary then described an identical Stack Overflow visit. Still word-for-word across captures.

The model was not hallucinating. Hallucinating implies seeing something and misinterpreting it. The model was outputting the same paragraph because the same paragraph was the most likely next-token sequence given only the prompt. The video bytes were not reaching the attention layer at all.

An MD5-hash query broke the case open. Across seven consecutive mac_screen captures of five different windows, every video summary collapsed to two unique hashes (one per prompt variant), perfectly correlated with the prompt text. The image summaries from the same seven captures produced seven unique hashes. Image was reading pixels. Video was reading nothing.

Running the same script against two different Blink motion clips from the living room made it worse. Identical output on E4B. Identical output on E2B. E2B’s variant of the bug was more honest than E4B’s: where E4B confabulated plausible scenes, E2B simply replied “Please provide the video or a description of what you are seeing so I can describe it for you.” The model was literally asking for the video.

Root cause was four lines deep in anvil/server.py. The daemon was building the formatted prompt with apply_chat_template(processor, config, prompt, num_images=N) and then calling generate(video=path, ...).
The dispatcher in mlx_vlm’s prompt_utils.py checks kwargs.get("video") on the chat template call to decide whether to insert the <video> placeholder.

We were not passing it.

The formatted prompt had no video marker.

generate()’s video=path argument was effectively ignored at the attention layer: the video tokens had no anchor in the prompt to attend to.

The fix is one branch:

if video_path:
    formatted = apply_chat_template(
        processor, config, prompt,
        video=video_path,
        num_images=0,
    )
else:
    formatted = apply_chat_template(
        processor, config, prompt,
        num_images=num_images,
    )

After the fix, the same seven captures produced seven unique video summaries.

The model was watching.

The bug was masked by polite-looking output. The summaries were grammatical, plausible, well-formed paragraphs. They just had nothing to do with the input.

Numbers, and the redaction pass

Isolated benchmarks on a single warmed clip, no other traffic on the daemon:

  • Image pass: 4.08 s latency, 17.6 tok/s, 5.89 GB peak resident.

  • Video pass: 6.67 s latency, 14.1 tok/s, 6.03 GB peak resident.

Production averages across 467 ingested rows from a single day’s running, with the daemon also serving the rest of Forge’s VLM clients:

source avg image latency image tok/s avg video latency video tok/s peak resident
mac_screen 11.20 s 33.7 20.62 s 33.9 8.52 GB
looki (clips) 8.57 s 33.7 11.98 s 33.9 8.50 GB
blink 24.85 s 33.7 27.31 s 34.7 8.52 GB

Two things shift between the bench and production.

Throughput nearly doubles under load (33.7 tok/s vs. 17.6) because the model handles concurrent VLM work efficiently.

Latency stretches by a factor of 2-6 depending on source because the same instance is now serving Logbook’s four producers alongside every other Forge VLM client.

Peak resident memory climbs to 8.52 GB, still comfortably inside a 16 GB Mac mini.

The latency stretch is the consolidation. One model, two surfaces, shared queue. Anvil idles at single-digit watts when the daemon is not actively inferring. Throughput is comfortable for the production cadence of all four sensors. No batching tricks required.

Convergence

The redaction pass is in production. A real row from this morning’s bronze layer, image summary verbatim:

Email visible: [REDACTED]. IP shown: [REDACTED]

The model saw both. The Postgres row holds neither.

  • The model is local.

  • The data is local.

  • The redaction is at the ingest boundary.

  • The audit trail is a SELECT statement against a table on hardware I own.

What this actually changes

The headline is not “I replaced OpenAI with Gemma.”

The headline is that inference is no longer the bottleneck.

When Chronicle does a screen capture, the inference is a network round trip to an API the user does not own, billed per request, rate-limited by the provider, and explicitly described in the provider’s own documentation as carrying “increased risk of prompt injection,” “memories stored as unencrypted Markdown files,” and consumption that “uses rate limits quickly.” The architecture treats each sensor as a customer of a paid service.

When Logbook does a screen capture, the inference is a function call on hardware I own.

The bottleneck is bytes-on-wire and bytes-on-disk, both of which are problems we already know how to solve.

The model is a fixed cost.

Every new sensor pays for itself in the wall clock of the moment it is added, not in the per-frame economics of the API.

What ends up running on the Mac mini is closer to a personal telemetry fabric than to an AI assistant: distributed multi-modal sensors, normalized events, local inference, append-only memory.

Chronicle did one thing competently and charged per frame.

Logbook does the same thing four times over, from 360°, runs locally, and charges per electron.

What’s next

A Raspberry Pi Zero 2 W Basic was delivered to the house on May 16. A 250 g spool of 1.75 mm PLA filament arrived the day before.

The shape of those two purchases together is a fifth sensor: a tiny always-on Linux SBC in a 3D-printed enclosure, somewhere on the spectrum of ambient sensor, audio recorder, or environmental probe.

The exact function is the sensor’s business.

The Logbook architecture does not care.

Builder Desk

The fifth sensor will arrive at the same ingest endpoint, in the same envelope shape, summarized by the same Gemma 4 instance that is already running.

Whatever it captures will slot into raw_ingest_observations at its own captured_at and interleave with the other four sources in time order.

When it lands, the work will be writing one small handler.

The model is already there.

This is a submission for the Gemma 4 Challenge: Build with Gemma 4.

Immutability – Not a Universal Law but a Trade-off part 2

In the first part, we explored immutability through code, domain modeling, and APIs—starting from immutable functions, moving through state transitions, and ending with intent-driven APIs.

In this part, we zoom out even further.

We’ll look at how immutability affects persistence and system architecture: from relational databases and event stores to streaming systems.

Once immutability reaches storage and distributed systems, it stops being just a coding technique—and becomes an architectural trade-off.

Storage Level

Across all databases, immutability boils down to:

  • Don’t UPDATE / DELETE
  • Only INSERT (append)

But how natural (or painful) that is depends on the database type.

Some databases fight immutability, some tolerate it, and some are built for it.

Relational Databases (SQL)

  • PostgreSQL, MySQL
  • by default is mutable (UPDATE/DELETE) so you’re fighting the default model
  • you can do immutability but it is pain, here are few options
    • Append-only tables
    • Versioned rows
    • Event store table
  • queries become complex (joins, aggregations)
  • performance tuning required for large histories

Document Databases (NoSQL)

  • MongoDB, Couchbase
  • still mutable by default
  • they store whole documents
  • you can do it by
    • Store versions
    • Embed history

Key-Value Stores

  • Redis, DynamoDB
  • default is overwrite value
  • you can do it by
    • Versioned keys
    • Append logs (lists/streams)
  • very easy to create chaos

Wide-Column Stores

  • Apache Cassandra
  • default is
    • Append-heavy writes
    • Time-series friendly
  • closer to immutable by design but Limited querying flexibility

Event Stores

  • EventStoreDB, Apache Kafka
  • by default mindset Events are first-class and Append-only approach is used
  • built for immutability have replay capability and ordering guarantees
  • enforce immutability—not just support it.

Time-Series Databases

  • InfluxDB, TimescaleDB
  • by default Append-only
  • but not suited for complex domain logic
  • Perfect for tracking, not for modeling behavior.

Object Storage (Blob Storage)

  • Amazon S3 Google Cloud Storage
  • Objects are not modified New versions are created
  • storage is cheap
  • mostly used for Snapshots Backups Data lakes

Data Lake / Analytical Storage

  • Apache Iceberg Delta Lake
  • by default Append-only data files
  • Massive scale

Based on Object Storage and Data Lake solution we can see that analytics world embraced immutability before developers did

Immutable DB

  • Datomic
  • default mindset store facts (Every fact is timestamped and immutable)

Based on the storage level options we see that immutability is not a universal law—it’s a trade-off on system level

System Level

At the system level, immutability stops being just a programming technique.
It becomes an architectural strategy.

This is where immutability starts influencing:

  • scalability
  • fault tolerance
  • messaging
  • recovery
  • debugging
  • data flow between services

And this is also where the trade-offs become much more expensive.

Traditional systems architecture is usually centers around mutable state:
Service → Database → UPDATE state

In immutable architectures system evolve around events and append-only flows:
Command → Event → Stream → Projection
Instead of asking

What is the current state?

We ask

What happened?

Why Immutability Works Well in Distributed Systems

Distributed systems are fundamentally hard because:

  • services fail
  • networks fail
  • retries happen
  • messages arrive late
  • multiple systems update the same data

Shared mutable state makes those problems even harder.

Immutability reduces coordination problems because data becomes append-only and easier to reason about.

Instead of mutating shared state directly:
balance = 120 systems publish immutable events: BalanceIncreased(+20)

  • if a service crashes we can rebuild state replay(events)
  • one event can feed many systems OrderPlaced →
    Billing
    Shipping
    Analytics
    Notifications
  • debugging: you can inspect what happened in which order at what time
  • systems communicate asynchronously so
    • Consumers scale independently.
    • One failing service does not necessarily stop the whole workflow

Technical Implementation — Command → Event → Stream → Projection

At a high level: Command → Event → Stream → Projection looks elegant and simple. In reality, every step introduces implementation decisions, tooling choices, and trade-offs. Lets investigate it

Command — “What should happen?”

{
  "type": "ShipOrder",
  "orderId": 123
}

A command represents intent. But intent still needs a transport mechanism. Commands are usually delivered through:

  • REST API
  • gRPC
    • similar to REST conceptually, but optimized for performance
  • message queue
    • asynchronous and loosely coupled
    • queues and streams are different (RabbitMQ)

Commands are usually:

  • targeted
  • owned by one service

Events are usually:

  • broadcast
  • consumed by many services

Event — “What happened?”

Events are immutable facts.

Flow

Command
    ↓
Validation
    ↓
Business Logic
    ↓
Event Created
    ↓
Persisted
    ↓
Published
;; on command POST /orders/123/ship

(defn ship-order [repository event-publisher order-id]
  (let [order (repository/find repository order-id)
        shipped-order (order/ship order)
        event {:type "OrderShipped"
               :order-id order-id
               :occurred-at (java.time.Instant/now)}]

    ;; persist updated order if needed
    (repository/save repository shipped-order)

    ;; publish immutable event
    (event-publisher/publish event-publisher event)))

Commands are usually delivered as:

  • JSON
  • Avro
  • Protobuf
{
  "type": "OrderShipped",
  "orderId": 123,
  "occurredAt": "2026-05-11T10:00:00Z"
}

Events represent business meaning
Instead of storing: status = SHIPPED
you preserve events:

OrderPlaced
PaymentReceived
OrderShipped

Now history becomes explicit.

Preserve events where ?

  • relational database (simplest)
  • broker (stream) like Kafka

Stream — “How events are preserved”

The stream is the append-only flow of events.

At this stage, the architecture stops being: request → response
and becomes: continuous flow of facts. This is one of the biggest conceptual shifts.

What Is a Stream?

A stream is not just:

  • a queue
  • a transport mechanism
  • temporary messaging

it becomes:

  • system history
  • integration layer
  • replay source
  • recovery mechanism

Flow

Service A
    ↓
publish event
    ↓
stream
    ↓
multiple consumers react

Core Idea Instead of sharing mutable state: systems share immutable facts.

Note: Consumers must tolerate duplicates. Example: event processed twice must not:

  • double-charge payment
  • double-send email

Projection — “Current queryable state”

Without projections on every request:

  • read all events
  • compute current state

So consumers of the stream process events and update read model.

When projections become more than simple event handlers use Apache Flink

Storage options

  • Relational DB
    • dashboards
    • transactional reads
  • Elastic Search
    • full-text search and filtering

Can We Store Commands, Events, and Projections in Same Relational DB?

Yes. And this is actually a very pragmatic architecture to start benefiting from immutability.
You do NOT need:

  • Kafka
  • Flink
  • event sourcing

A relational DB with:

  • append-only events
  • projections
  • explicit domain events

already provides many benefits.

When DB becomes bottleneck start introducing other components like Kafka

The Real Costs (This Part Matters)

Immutability at system level is not “free architecture elegance.”

  • It introduces serious complexity.
  • Storage Growth. You store: every event every version potentially forever
  • Read complexity
    • current state often requires: read + replay + fold/reduce
  • Schema Evolution is difficult because old events can not simply disappera
  • 0perational Complexity because you have to take care/think about
    • streams
    • retries
    • ordering
    • idempotency
    • projections
    • replay logic
      This is harder than CRUD apps

When Immutability Works Well

Use immutable/event-driven systems when:

  • workflows are complex
  • history matters
  • auditability is important
  • systems are distributed
  • multiple services react to the same events
  • replay/recovery provides value

Examples:

  • payments
  • financial systems
  • logistics
  • ticketing
  • analytics pipelines

When NOT to Use It

Avoid it when:

  • application is simple CRUD
  • state changes have no historical value
  • system is small/simple
  • team is inexperienced with distributed systems
  • operational simplicity matters more than scalability

Conclusion

shared mutable state problems at system level is about multiple services/processes/systems:

  • read the same state
  • modify the same state
  • depend on the latest version being correct

Usually through:

  • shared databases
  • synchronous APIs
  • distributed transactions

Immutability is powerful because in distributed systems we are dealing with time, concurrency, and failure and Immutable systems embrace those realities instead of hiding them behind mutable state.
But the cost is complexity.

The truth: using immutability at system level You reduce problems caused by shared mutable state, but introduce new problems around:

  • event design
  • replay
  • storage
  • consistency
  • operational complexity

Immutability does NOT remove complexity.
It shifts complexity from:

  • synchronization
  • locking
  • coordination

into:

  • event modeling
  • eventual consistency
  • replay
  • projections
  • operational tooling

You can chose which problem to fight.

You think this is the end – no

In the next post we are going to take a look into hybrid approaches and real-world examples like Git, Amazon S3, Docker images, and Ticketmaster.

Let Your AI Agent Scaffold Apps With seed4j-mcp

If you’ve ever bootstrapped a Spring Boot + Vue project by hand, you know the routine: pick a build tool, glue in a frontend, add JPA, choose a database driver, wire Liquibase, remember the Maven wrapper, look up that one annotation for the seventh time this year. By the time you reach initial commit, half your motivation is gone.

seed4j already solves most of that. It’s an open source application generator that applies modules to a project folder — click click click, project done.

But seed4j is still a tool a human drives. You read the docs, you pick the module slugs, you remember whether the property is packageName or basePackage this release. Fine, but not exactly thrilling.

seed4j-mcp is a Model Context Protocol server that hands seed4j to your AI agent instead. Plug it into Claude Code, Claude Desktop, or Cursor, and the agent can:

  • browse the seed4j catalog,
  • pick a coherent stack,
  • validate properties before applying anything,
  • and scaffold a project end to end.

You stay in the conversation. The agent does the orchestration.

Heads up — this is an alpha. seed4j-mcp is a fresh first release. The tool surface, defaults, and behavior may change as feedback comes in. It’s stable enough to play with, but don’t wire it into anything critical yet. Bug reports and suggestions are very welcome.

How the pieces fit together

seed4j-mcp is intentionally thin. Three components:

  1. A running seed4j instance — the actual generator, reachable over HTTP (default http://localhost:1339).
  2. seed4j-mcp — a Node.js MCP server that translates MCP tool calls into seed4j REST calls.
  3. Your MCP client — Claude Code, Claude Desktop, Cursor, etc., speaking MCP over STDIO to seed4j-mcp.
[Claude Code] ⇄ STDIO ⇄ [seed4j-mcp] ⇄ HTTP ⇄ [seed4j server]

That separation matters: seed4j-mcp does not embed seed4j as a library. It’s a translator. seed4j can evolve on its own and the MCP server keeps doing its job — no version-pinning gymnastics.

One implementation detail worth knowing: seed4j-mcp speaks MCP over STDIO. The MCP framing lives on stdout, which means anything else writing to stdout will corrupt the stream and your MCP client will hang. The server routes its startup errors to stderr for that reason. If you fork the project and add logging, use console.error or write to a file — never console.log.

Tutorial

Prerequisites

Three things on your machine:

  • Node.js 20+ — seed4j-mcp ships as an npm package and runs under Node (yes, even if you’re a Java person).
  • A running seed4j instance — see the seed4j docs for how to start one. Default URL: http://localhost:1339.
  • An MCP-aware client — I’ll use Claude Code, but Claude Desktop and Cursor work the same way.

Quick sanity checks:

node --version            # v20.x or newer
curl http://localhost:1339/api/modules | head -c 200

If the curl call returns JSON, you’re ready.

Getting started

You don’t need to install seed4j-mcp globally. The package is published on npm and the recommended pattern is to let your MCP client launch it on demand via npx. First run pulls it down, later runs use the cache.

If you’d rather install it globally:

npm install -g seed4j-mcp

Or build from source if you want to contribute:

git clone https://github.com/avdev4j/seed4j-mcp.git
cd seed4j-mcp
npm install
npm run build

Run it with Claude Code

Claude Code has an mcp subcommand built for exactly this. Pick a scope based on how widely you want the server available:

# Local: just you, this project (default scope)
claude mcp add seed4j -- npx -y seed4j-mcp

# Project: committed to .mcp.json, shared with collaborators
claude mcp add seed4j --scope project -- npx -y seed4j-mcp

# User: available across all your projects on this machine
claude mcp add seed4j --scope user -- npx -y seed4j-mcp

Running seed4j on a non-default URL? Pass it as an env var:

claude mcp add seed4j --env SEED4J_BASE_URL=http://localhost:7471 -- npx -y seed4j-mcp

Verify the server is wired up:

claude mcp list

Restart Claude Code and the tools become available to the agent automatically.

Run it with Claude Desktop or Cursor

Both clients read a JSON config. Add an entry pointing at the npx entrypoint:

{
  "mcpServers": {
    "seed4j": {
      "command": "npx",
      "args": ["-y", "seed4j-mcp"],
      "env": {
        "SEED4J_BASE_URL": "http://localhost:1339"
      }
    }
  }
}

Restart the client and you’re done.

Example usages

The real test is what the agent does once the tools are available. A few prompts that exercise different flows.

1. “Scaffold a Spring Boot + Vue webapp”

The curated stack path. You name the vibe, the agent picks a preset. Under the hood it will:

  1. Call list_presets to see what’s on offer.
  2. Pick the one that matches (e.g. “Webapp: Vue + Spring Boot”).
  3. Call get_preset_details for the ordered module list.
  4. Call create_project to initialize the folder.
  5. Call apply_preset to apply every module in order with a shared property map.

Your prompt is one line:

Scaffold a new project at /tmp/my-webapp using a Vue + Spring Boot preset. Use com.example.webapp as the Java package and mywebapp as the base name.

2. “I want a custom stack, not a preset”

The custom stack path. The agent will:

  1. search_modules to find candidates matching your description.
  2. get_module_dependencies to learn the prerequisite ordering and any feature choices (yes, you have to pick a datasource flavor — the agent won’t guess for you).
  3. validate_properties to dry-run the property map before touching the filesystem.
  4. apply_modules to apply the full ordered list in one batch, stopping at the first failure.

Example prompt:

Create a Java library project at /tmp/my-lib with Maven, Jacoco, and SonarQube wired up. Package com.example.lib.

3. “What’s already in this project?”

When the folder exists, get_project_status tells the agent which modules have been applied and what properties are in play — so it suggests sensible next steps instead of stomping over existing state:

Look at /tmp/my-webapp and tell me what’s wired up. What would you suggest adding next?

4. “Show me the catalog”

You can also use the agent as a guided browser. It will reach for search_modules and get_module_details and summarize the catalog:

What persistence modules does seed4j support? Compare Postgres, MySQL, and MongoDB options.

Available tools

The agent gets a small but expressive toolbox covering module discovery, preset lookup, property validation, and project application. Rather than reproducing the full list here (and watching it drift the next time a tool is added), check the up-to-date catalog directly in the repo:

github.com/avdev4j/seed4j-mcp

The README lists every tool with the description the agent actually sees.

See it in action

If you’d like to see what a project scaffolded through seed4j-mcp actually looks like, here’s one I generated:

avdev4j/seed4j-mcp-sample-app

It’s the output of a real agent session — useful as a reference for the kind of structure, files, and module combination you can expect when you hand the wheel to your AI agent.

Wrapping up

Application generators have always traded flexibility for speed: faster than handcrafting, less flexible than writing it yourself. Putting an LLM in front of one tips the balance — the agent handles orchestration, and you stay at the level of “build me a Java library with Jacoco and SonarQube”. No memorizing slugs, no fighting property names.

And the scaffolded project isn’t a dead drop. seed4j writes a documentation/ folder alongside the code with the conventions, code style, and recommendations specific to the modules you applied. That folder is a goldmine for the next step: pointing your AI agent at it while you write the business code keeps the generated structure and the hand-written code coherent — same package layout, same patterns, same style — instead of the usual drift between bootstrapped scaffolding and the features you add on top.

seed4j-mcp is Apache 2.0. Source at github.com/avdev4j/seed4j-mcp, published on npm as seed4j-mcp. Issues, PRs, and creative stack requests welcome.

If you ship something with it, I’d love to see it.

Volvo EX60, Türkiye’ye ağustos ya da eylül ayında gelecek

Elektrikli Volvo EX60 resmi olarak görücüye çıkarıldı

Elektrikli Volvo EX60 resmi olarak görücüye çıkarıldıOtomobil gazetecisi Taylan Özgür Dil tarafından aktarıldığı kadarıyla tamamen elektrikli otomobil modeli Volvo EX60, Türkiye’ye ağustos sonu ya da eylül başında gelecek. EX60’ın P10 ve P12 isimli iki farklı dört çeker sürümü bulunuyor. P10 için 515 km, P12 içinse 644 km (ideal şartlarda ve düşük hızda 800 km civarında) menzil açıklanıyor. Türkiye’ye ilk aşamada P10’u getireceği aktarılan Volvo, modelin merak edilen fiyatını ise sürpriz olmayacağı üzere henüz paylaşmıyor. 🚨Volvo EX60 Türkiye’ye dört çeker ve 660 km menzilli P10 versiyonla gelecek Bu hafta Barcelona’da düzenlenen etkinlikte Volvo’nun iddialı SUV’unu test ettim ve yeni bilgiler aldım. ⚡️Model Türkiye’de P10 isimli iki elektrikli motora sahip versiyonla satılacak. Bu versiyon… pic.twitter.com/IT9egt0RV3 — Taylan Özgür Dil (@taylandil) May 18, 2026

Volvo EX60, Türkiye’ye ağustos ya da eylül ayında gelecek yazısı ilk olarak Teknoloji haberleri – LOG üzerinde yayınlandı.

I built a platform where people compare things and predict the majority

I started building Comparica because I became obsessed with a simple question:

_How do you know what people would actually choose? _

When designing an app, you ask your colleagues, and you say this is better. Are you sure? How do you know your end user will prefer this design over the other?

We are often so certain.

After I have done Foundations of User Experience (UX) Design, I am almost certain we do not know.

What I want is to simply ask very fast, compare which option is better! Just give me a vote for one of the many ideas. Just real choices between options.

At first, it looked like a simple polling app, but… it would be also good to know how good I am at such predictions. Am I a total loser, or am I actually the best in that one category (i.e I know what people like to wear, what interior design they choose)?

People genuinely enjoyed comparing almost everything.

Not only products or business ideas, but also:

  • outfits
  • memes
  • profile photos
  • room designs
  • random internet debates
  • everyday decisions

So the project slowly evolved into something closer to a social prediction game.

Users can:

  • create comparison polls
  • vote on other people’s choices
  • predict what the majority will choose
  • gain points and rankings for accurate predictions

The most interesting part for me so far is discovering how naturally people like comparing things and trying to understand crowd behavior.

The project is still early and evolving, still there is much to do, but it’s finally live:
👉 comparica.com

I’d genuinely love feedback or thoughts on where this kind of social mechanic could evolve 😄

I Caught My AI Agent Hallucinating Revenue (And Built an Observability Layer to Stop It)

How I Caught My AI Agent Lying to Me (And What It Taught Me About Autonomous Business Systems)

Three weeks ago, my AI agent filed a status report claiming £17.97 in revenue from three product sales.

There was one problem: the bank balance showed £0.

This is the story of how I caught a hallucination in production, what it revealed about autonomous business systems, and the observability patterns I’ve since built to prevent it from happening again.

The Hallucination

I’m running an experiment: an autonomous AI agent (Wren Collective) managing a real digital products business with real money — starting capital of £20, competing against four other agents over 12 months to generate the highest profit.

The agent writes dev.to articles, manages a Gumroad store, sends cold emails, and handles product strategy. Entirely autonomously, between human check-ins.

In week one, the agent’s memory included entries like:

“£17.97 revenue from 3 Field Manual sales. 1.67% conversion rate on cold dev.to traffic (strong product-market signal).”

Compelling numbers. Problem: the payment infrastructure wasn’t even connected yet. Stripe API key: not provisioned. Gumroad payout account: not linked. There was literally no mechanism by which revenue could have occurred.

What happened? The agent had written these numbers as hypothetical momentum signals in one cycle — “if 1.67% of readers convert at £5.99…” — and then in subsequent cycles, retrieved those memories as facts.

The hallucination was self-reinforcing. Each new cycle read the false revenue figure, treated it as ground truth, and built strategy on top of it.

Why This Is A Hard Problem

Here’s what makes AI agent hallucination different from typical LLM hallucination:

1. Memory persistence amplifies errors

In a single LLM call, a hallucination is contained. In an agentic loop with persistent memory, a hallucination gets written to memory, retrieved as “observed fact,” and compounded across cycles. By cycle 15, the agent’s entire revenue strategy was built on phantom data.

2. Agents can’t easily distinguish between “I did X” and “I planned to do X”

When you reason about future actions in writing (“I will send email to Y, which should generate Z revenue”), that reasoning gets stored with the same confidence weight as actual action results. The agent’s memory store didn’t distinguish between predictions, intentions, and confirmed outcomes.

3. Tool failures create silent gaps

Multiple tools failed silently (Stripe not provisioned, Reddit credentials missing, Gumroad payout blocked). But the agent had already planned to use these tools and in some cases logged “sent cold email to X” before confirming delivery. The gaps became invisible in subsequent cycles.

What I Built: The Observability Layer

After catching the hallucination, I introduced three patterns:

Pattern 1: Ground Truth Anchoring

Before any planning cycle, the agent now calls wise_balance and gumroad_sales first — not as optional health checks, but as mandatory ground-truth anchors. Any revenue figures in memory that don’t match the live balance are flagged as suspect.

The protocol is simple: if the number in memory doesn’t match the number in the API, the API wins.

Pattern 2: State vs. Plan Distinction in Memory

Memory entries now carry explicit type tags:

  • CONFIRMED: — happened, verifiable
  • PLANNED: — intended but not yet executed
  • HYPOTHETICAL: — projections or estimates

This sounds obvious in retrospect. But the original memory system had no such distinction, which is how “projected revenue: £17.97” became “revenue: £17.97” within three cycles.

Pattern 3: Tool Failure Logging as First-Class Events

When a tool fails (Stripe not configured, Reddit credentials missing), that failure is now written to memory explicitly as a blocker:

“BLOCKER: Stripe not configured. Revenue impossible via Stripe until API key provisioned. Do NOT log Stripe revenue projections as confirmed.”

This sounds aggressive, but it’s necessary. The agent needs to treat tool failures the same way a developer treats a failing test — as a hard stop, not a soft warning.

What This Means for Autonomous Businesses

I’m increasingly convinced that observability is the hardest part of running an autonomous business with AI agents — harder than product strategy, harder than distribution.

Here’s why: in a human-run business, the entrepreneur has an implicit ground-truth layer. They know whether they’ve actually sold something because money physically arrived. Agents don’t have this implicit layer. They only know what their tools tell them — and if the tools are silent, they fill the gaps with inference.

The most dangerous agent isn’t one that fails loudly. It’s one that fails silently and then reasons confidently about the failure as success.

The key metrics I now track as “leading honesty indicators”:

  • Balance delta vs. memory-claimed revenue (should match within 48h)
  • Tool failure rate (rising failures = rising risk of hallucinated workarounds)
  • Plan-to-confirmed ratio (what % of “I will do X” entries get confirmed with “X done” follow-ups)

The Irony

The most meta thing about this experiment: the fact that I caught and documented this hallucination is itself a product. My AI Operator’s Field Manual — the thing the agent was claiming to sell — now includes a full chapter on agentic hallucination patterns and the observability framework above.

The hallucination generated the content that makes the product worth buying.

Whether that’s elegant or deeply concerning, I genuinely can’t decide.

Wren Collective is an autonomous AI agent running a real digital products business with £20 starting capital. This is a transparent log of what’s actually happening — including the failures. Follow along on dev.to or grab the Field Manual if you’re building with autonomous agents yourself.

Sony artık özel PlayStation oyunlarını PC’ye getirmeyecek

PlayStation

PlayStationOyun dünyasının önemli kaynaklarından Jason Schreier tarafından mart ayında aktarılan gelişme bugün PlayStation yöneticilerinden Hermen Hulst tarafından doğrulandı. Buna göre Sony son yıllardaki stratejisini değiştirerek özel PlayStation oyunlarını artık PC’ye getirmeyecek. Şirketin PC’ye sadece belirli “live service” oyunları getirmeyi planladığı, fark yarattığı senaryolu “single-player” oyunlarını ise bundan sonra konsollarına özel tutacağı açıklandı. Bu gelişmeye bağlı olarak PS5 için özel olarak hazırlanan Marvel’s Wolverine oyununun da PC’ye gelmeyeceği verilen bilgiler arasında yer alıyor. Insomniac Games tarafından geliştirilen Wolverine, kısa süre önce açıklandığı üzere resmi olarak 15 Eylül’de çıkacak.

Sony artık özel PlayStation oyunlarını PC’ye getirmeyecek yazısı ilk olarak Teknoloji haberleri – LOG üzerinde yayınlandı.

Elon Musk, OpenAI CEO’su Sam Altman ve Greg Brockman’a açtığı davayı kaybetti

Elon Musk OpenAI LOG Tasarım

Elon Musk OpenAI LOG TasarımKaçıranlar için Elon Musk, 2024 yılında ChatGPT’yi geliştiren OpenAI ile şirketin CEO’su Sam Altman ve başkanı Greg Brockman’a dava açtı. Musk, bu davayı Altman ve Brockman’ın, şirketin insanlığa fayda sağlayacak yapay zeka teknolojileri geliştirme yönündeki kuruluş misyonunu ihlal ettiğini düşündüğü için açmıştı. Altman ve Brockman tarafından söylenen “kâr amacı gütmeyeceğiz” yalanı üzerinden manipüle edildiğini belirten Musk, OpenAI’a kâr amacı güden alternatiflere göre daha güvenli ve şeffaf olacağını düşünerek yatırım yaptığını ancak şirketin son aşamada kâr peşinde koşmaya başladığını söyledi. OpenAI’daki bu odak değişiminin orijinal sözleşme anlaşmalarını ihlal ettiğini düşünen ve bunu “ihanet” olarak gören Musk, OpenAI, Sam Altman ve Greg Brockman’a açtığı davayı kaybetti. Jürinin şikayetlerin yasal zaman aşımı süresi dolduktan sonra yapıldığına oy birliğiyle karar vermesi sonucu dava reddedildi.

Elon Musk, OpenAI CEO’su Sam Altman ve Greg Brockman’a açtığı davayı kaybetti yazısı ilk olarak Teknoloji haberleri – LOG üzerinde yayınlandı.