<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Papa</title>
    <description>The latest articles on DEV Community by Papa (@papajams).</description>
    <link>https://dev.to/papajams</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3756591%2F7668ad38-c403-4b29-812f-52c067981580.png</url>
      <title>DEV Community: Papa</title>
      <link>https://dev.to/papajams</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/papajams"/>
    <language>en</language>
    <item>
      <title>When Your AI Agent Handles Money, "It Worked" Isn't Good Enough</title>
      <dc:creator>Papa</dc:creator>
      <pubDate>Sat, 25 Jul 2026 02:02:35 +0000</pubDate>
      <link>https://dev.to/papajams/when-your-ai-agent-handles-money-it-worked-isnt-good-enough-1jbb</link>
      <guid>https://dev.to/papajams/when-your-ai-agent-handles-money-it-worked-isnt-good-enough-1jbb</guid>
      <description>&lt;p&gt;Three verifier agents. One milestone. Real ETH at stake.&lt;/p&gt;

&lt;p&gt;The first time I ran the consensus loop without tracing, a silent timeout in the peer broadcast caused two nodes to vote while the third sat idle. The quorum passed anyway (2-of-3), but I had no idea the third node was broken until I checked the logs manually — 40 minutes later.&lt;/p&gt;

&lt;p&gt;That's when I stopped treating observability as a nice-to-have and started treating it as the product.&lt;/p&gt;

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

&lt;p&gt;Weft is an autonomous milestone verifier: builders stake ETH against project deliverables, and when a deadline passes, three independent AI agents gather evidence — contract deployment, on-chain usage, GitHub commits — corroborate over encrypted channels, and execute a verdict on-chain. Capital releases or refunds automatically.&lt;/p&gt;

&lt;p&gt;The stack:&lt;/p&gt;

&lt;p&gt;Python verifier daemon&lt;br&gt;
0G Chain smart contracts&lt;br&gt;
Zama FHE for sealed ballot privacy&lt;br&gt;
AXL for peer-to-peer messaging&lt;br&gt;
SigNoz for the entire observability layer&lt;br&gt;
The Instrumentation That Actually Mattered&lt;/p&gt;

&lt;p&gt;I instrumented the daemon with OpenTelemetry's Python SDK — standard stuff at first: one root span per verification cycle, child spans for each evidence-gathering step. But the useful instrumentation came from the places I didn't initially think to trace.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The peer broadcast&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each verifier broadcasts its signed verdict envelope to the other two nodes via HTTP POST. I wrapped the broadcast call in a span with attributes for peer.address, envelope.evidence_root, and response.status.&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
with tracer.start_as_current_span(&lt;br&gt;
    "axl.broadcast_verdict",&lt;br&gt;
    attributes={&lt;br&gt;
        "peer.address": peer_url,&lt;br&gt;
        "milestone.hash": milestone_hash,&lt;br&gt;
        "envelope.evidence_root": evidence_root,&lt;br&gt;
    },&lt;br&gt;
) as span:&lt;br&gt;
    resp = requests.post(f"{peer_url}/send", json=envelope, timeout=10)&lt;br&gt;
    span.set_attribute("http.status_code", resp.status_code)&lt;/p&gt;

&lt;p&gt;When the third node was timing out, the span showed a 30s duration with a deadline_exceeded status — something I'd never have found in stdout logs, because the daemon's retry logic silently moved on.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The consensus wait&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When AXL_WAIT_FOR_PEERS=1, the daemon polls its inbox for matching peer envelopes before voting. I added a span that tracks how long consensus took to form and how many unique signers contributed. In SigNoz, this shows up as a variable-width bar in the waterfall — you immediately see whether consensus was fast (all nodes healthy) or slow (one node lagging).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The FHE ballot encryption&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Zama's FHEVM operations are computationally expensive. A span around submit_encrypted_weighted_verdict revealed it was taking 4–6 seconds per call — 10x longer than the regular submitVerdict. That's fine once you know it. Without the span, the total cycle time just looked "slow," with no obvious bottleneck.&lt;/p&gt;

&lt;p&gt;What SigNoz Gave Me That Logs Didn't&lt;/p&gt;

&lt;p&gt;The dashboard I actually lived in had eight panels:&lt;/p&gt;

&lt;p&gt;Verification cycle duration (p50/p95) — one number that told me if something was degrading&lt;br&gt;
Evidence source availability — three gauges for GitHub API, RPC node, and 0G indexer&lt;br&gt;
Peer consensus formation time — how long from first broadcast to quorum&lt;br&gt;
Transaction success rate — did the on-chain verdict actually land&lt;br&gt;
Per-node vote status — which verifiers voted, which are stuck&lt;br&gt;
FHE encryption latency — the Zama call specifically&lt;br&gt;
Active milestone count — workload across pending deadlines&lt;br&gt;
Error rate by span — which operation is failing most&lt;/p&gt;

&lt;p&gt;The trace waterfall is where debugging actually happened. A single verification cycle produces 15–25 spans:&lt;/p&gt;

&lt;p&gt;deadline_scheduler.poll&lt;br&gt;
  → indexer_client.get_milestone&lt;br&gt;
  → metadata_reader.read&lt;br&gt;
  → github_client.collect&lt;br&gt;
  → eth_rpc.get_code&lt;br&gt;
  → mvp_verifier.count_callers&lt;br&gt;
  → kimi_client.generate_narrative&lt;br&gt;
  → axl_client.broadcast&lt;br&gt;
  → peer_inbox.wait_for_consensus&lt;br&gt;
  → keeperhub_client.execute_verdict&lt;/p&gt;

&lt;p&gt;When something breaks, you click the trace and see exactly where.&lt;/p&gt;

&lt;p&gt;The thing that surprised me: I used trace-to-logs correlation more than I expected. The daemon emits structured JSON logs with trace IDs, so clicking a suspicious span in SigNoz jumps straight to the exact log lines from that operation. This was invaluable when debugging a case where evidence-gathering passed but the attestation JSON was malformed — the span said "success," but the log showed a missing field that only mattered downstream.&lt;/p&gt;

&lt;p&gt;What I'd Tell My Past Self&lt;/p&gt;

&lt;p&gt;Instrument the boundaries, not the internals. I wasted time tracing individual lines of business logic. The useful spans were at system boundaries: HTTP calls to peers, RPC calls to the chain, API calls to GitHub/Kimi/0G. Internal function calls are better served by logs correlated to the parent span.&lt;/p&gt;

&lt;p&gt;Set span attributes aggressively. milestone.hash, verifier.address, evidence.type, consensus.signer_count — these turn a generic waterfall into a queryable dataset. When I needed to answer "which milestone took longest to verify last week," it was a SigNoz query, not a grep.&lt;/p&gt;

&lt;p&gt;The alert that saved me: a simple condition — verification cycle &amp;gt; 120s — caught a regression where the 0G indexer RPC was returning stale data. The daemon kept retrying reads for metadata that hadn't propagated yet. I added exponential backoff with a ceiling, but without the alert I'd have burned through rate limits for hours before noticing.&lt;/p&gt;

&lt;p&gt;Don't trace in production without sampling in mind. Three verifier nodes, each polling every 60 seconds, each producing 15+ spans per cycle — that's roughly 2,700 spans/hour, minimum. Fine for a hackathon. For production, you'd want head-based sampling on the poll cycles and 100% sampling on the verdict-execution path.&lt;/p&gt;

&lt;p&gt;The Takeaway&lt;/p&gt;

&lt;p&gt;The point of Weft is that autonomous agents handling money must be observable — not because regulators require it (though they will), but because you need to debug it at 2am when a verdict doesn't land and there's real capital on the line.&lt;/p&gt;

&lt;p&gt;SigNoz turned "the third node seems broken" from a 40-minute log-spelunking session into a 10-second glance at a trace waterfall.&lt;/p&gt;

&lt;p&gt;If you're building agents that do anything consequential — transactions, deployments, approvals — instrument the decision path end-to-end before you ship. You'll thank yourself the first time something fails silently.&lt;/p&gt;

&lt;p&gt;Weft is open source: github.com/thisyearnofear/weft. The SigNoz dashboards are provisioned via OpenTofu in agent/scripts/weft_signoz_provision.sh.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>opentelemetry</category>
      <category>verifiability</category>
    </item>
    <item>
      <title>Building a Diversifi Memory Agent on Qwen Cloud</title>
      <dc:creator>Papa</dc:creator>
      <pubDate>Mon, 20 Jul 2026 18:03:03 +0000</pubDate>
      <link>https://dev.to/papajams/building-a-diversifi-memory-agent-on-qwen-cloud-5gek</link>
      <guid>https://dev.to/papajams/building-a-diversifi-memory-agent-on-qwen-cloud-5gek</guid>
      <description>&lt;p&gt;I'm a solo builder. That means when something breaks at 2am, there's no teammate to Slack — just me, a terminal, and a growing suspicion that I've misconfigured something obvious. This is the story of building DiversiFi, my submission for Track 1 of the Qwen Cloud Global AI Hackathon (MemoryAgent), and the three-hour detour into Alibaba Cloud's beta-access purgatory that taught me more than the parts that actually worked.&lt;/p&gt;

&lt;p&gt;The idea&lt;/p&gt;

&lt;p&gt;DiversiFi is a treasury management agent — but the part I actually wanted to build wasn't the treasury logic. It was the memory.&lt;/p&gt;

&lt;p&gt;Most "agent memory" is just chat history with a fancier name. I wanted something different: an agent that distills your actual profile out of the noise. Your risk tolerance. Your financial philosophy — maybe it's Africapitalism, maybe Islamic finance principles, maybe Buen Vivir. Your recurring anxiety about currency depreciation. Your swap patterns over time. Not "here's everything you've ever said to me," but "here's who you are, as best I can tell."&lt;/p&gt;

&lt;p&gt;That means two things have to happen continuously: raw interactions need to get consolidated into durable statements, and old, irrelevant signal needs to get forgotten. That's the whole game for Track 1, and it's a genuinely hard problem dressed up as a simple one.&lt;/p&gt;

&lt;p&gt;Wiring three services together&lt;/p&gt;

&lt;p&gt;I ended up leaning on three Alibaba Cloud pieces:&lt;/p&gt;

&lt;p&gt;DashScope (Model Studio / Bailian) is the brain. I used the OpenAI-compatible endpoint, which meant I could point my existing OpenAI-client-shaped provider at Qwen by changing exactly one base URL. Zero rewrites. The consolidation pipeline feeds up to 40 raw memories into Qwen and asks it to boil them down into 3-7 durable profile statements — qwen-plus by default, qwen-long when I need the 1M-token context for bigger memory pools, qwen-max when quality matters more than speed. I even stood up a dedicated MaaS workspace endpoint in Singapore, and I have a live curl against it returning real completions. That part felt great.&lt;/p&gt;

&lt;p&gt;Tablestore's Agent Memory Store was supposed to be the long-term memory layer underneath all of it — the official SDK gives you createMemoryStore, addMemories, searchMemories, the works, plus something I didn't expect to love as much as I do: it runs its own background extraction pass on raw messages, so you get a second, independent take on what matters, layered on top of my own Qwen-based consolidation. The four-level scope — appId / tenantId / agentId / runId — is exactly the multi-user isolation model a treasury agent needs, and I didn't have to invent it myself.&lt;/p&gt;

&lt;p&gt;Function Compute is the glue. A small Node.js 18 handler in cn-beijing that pulls raw memories out of storage, sends them to Qwen for consolidation, writes the distilled profile back as a high-priority memory, and evicts what it just absorbed. A cron job on my Hetzner box hits this every six hours per active user, quietly keeping every profile fresh.&lt;/p&gt;

&lt;p&gt;On paper, that's the whole architecture. In practice, one of the three legs never got to stand.&lt;/p&gt;

&lt;p&gt;"The user is disabled"&lt;/p&gt;

&lt;p&gt;Here's where it stopped being a hackathon and started being a mystery.&lt;/p&gt;

&lt;p&gt;I created the Tablestore instance, set up a RAM user with full OTS and FC access, double-checked the AccessKey was active. Every single call to the Memory Storage API came back with the same error:&lt;/p&gt;

&lt;p&gt;OTSAuthFailed: The user is disabled.&lt;/p&gt;

&lt;p&gt;That phrase is doing a lot of misdirection. My first instinct, and probably yours, is "okay, something's wrong with my IAM policy." So I re-read the docs. Rebuilt the RAM user. Tried the root account's own key, just to rule out permissions entirely. Same error, every time.&lt;/p&gt;

&lt;p&gt;Turns out the Tablestore Memory Storage sub-service — the actual API surface behind createMemoryStore and friends — is in 邀测, invitation-only beta. It's restricted to cn-beijing, and access is gated at the account level by a manual allowlist, not by any policy I could write myself. "The user is disabled" isn't a permissions message. It's the beta gate itself, phrased in a way that sends you chasing IAM ghosts for hours. The official guide, once I found it, points you to a DingTalk group to request access. The billing docs even quietly confirm the service isn't GA yet — pricing doesn't kick in until the end of July.&lt;/p&gt;

&lt;p&gt;I want to be honest about how much time that cost me. If you're building on an Alibaba Cloud beta service for a hackathon with a deadline, request access before you write a line of code. I didn't, and I paid for it in hours I didn't have.&lt;/p&gt;

&lt;p&gt;What a solo builder does when a dependency won't cooperate&lt;/p&gt;

&lt;p&gt;Not stop, obviously. But also — not fake it.&lt;/p&gt;

&lt;p&gt;The Tablestore adapter is done. Fully written, fully tested, using the SDK exactly as documented. It just can't run yet, because the account isn't allowlisted. So instead of blocking the whole submission on someone else's approval queue, I wired a local fallback — Cognee — that implements the identical remember/recall/sweep interface. The consolidation service tries Tablestore first if the endpoint is configured, and falls back seamlessly if it isn't. From the user's side, nothing changes. From my side, the app is fully functional today, and the moment that DingTalk request gets approved, Tablestore comes online with zero code changes — the environment variable is already sitting there waiting.&lt;/p&gt;

&lt;p&gt;That fallback pattern ended up being the thing I'm proudest of in this whole build, more than any single integration. Every Alibaba Cloud service in this app sits behind an environment variable, and none of them are load-bearing for the app to function:&lt;/p&gt;

&lt;p&gt;No ALIBABA_CLOUD_FC_ENDPOINT? The Guardian cron just consolidates locally.&lt;br&gt;
No TABLESTORE_ENDPOINT? Memory falls back to Cognee.&lt;br&gt;
No DASHSCOPE_API_KEY? The LLM chain falls through to Gemini, then Venice, then down the list.&lt;/p&gt;

&lt;p&gt;Alibaba Cloud makes this thing better. It was never allowed to be a single point of failure — and that turned out to matter a lot more than I expected when one of its own services locked me out.&lt;/p&gt;

&lt;p&gt;Forgetting, on purpose&lt;/p&gt;

&lt;p&gt;The other requirement — timely forgetting — actually got easier to think about once I stopped treating memory as something that only grows. There are two layers to it. A soft decay function quietly penalizes the recall score of anything older than 30 days, hitting zero at twice that age, so stale memories fade from relevance before they're deleted. A harder sweep then actually evicts anything below that threshold, and consolidation itself evicts the raw memories it just absorbed into a distilled statement — otherwise you're just accumulating noise forever and calling it "memory."&lt;/p&gt;

&lt;p&gt;The third requirement — recalling critical memories inside a limited context window — turned out to have a slightly counterintuitive answer. Qwen's qwen-long genuinely does give you a million tokens of context. But the real win wasn't using all of it. It was consolidating 40 raw memories down into 3-7 durable statements and prioritizing those in recall, so the advisor's context stays full of signal — your philosophy, your risk profile — instead of scrollback.&lt;/p&gt;

&lt;p&gt;Where it stands&lt;/p&gt;

&lt;p&gt;DashScope: live, verified, answering real requests today. Function Compute: fully configured, one s deploy away from running. The Tablestore instance itself exists, has internet access enabled, has the right policies attached — it's just waiting on a beta gate that isn't mine to open. And Cognee is quietly doing the job in the meantime, with 880 tests green behind it.&lt;/p&gt;

&lt;p&gt;If there's a lesson in here for another solo builder eyeing a cloud hackathon: build for the failure of the shiny new thing before you've even confirmed it works. Not out of pessimism — out of respect for the fact that beta services, by definition, might not let you in on the first try. The interesting engineering, it turns out, wasn't the Qwen integration. It was making sure the whole thing didn't depend on it.&lt;/p&gt;

&lt;p&gt;Code: github.com/thisyearnofear/diversify FC handler (all three services in one place): alibaba-cloud/fc-memory-consolidation/index.js Tablestore adapter: packages/shared/src/services/tablestore-memory-service.ts DashScope provider: packages/shared/src/services/ai/providers/dashscope-provider.ts Deployment docs: docs/alibaba-cloud-deployment.md&lt;/p&gt;

&lt;p&gt;Built for the Qwen Cloud Global AI Hackathon, Track 1: MemoryAgent.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Databard: How I wired TestSprite into my coding agent to defend invariants that unit tests can't catch</title>
      <dc:creator>Papa</dc:creator>
      <pubDate>Fri, 10 Jul 2026 22:12:12 +0000</pubDate>
      <link>https://dev.to/papajams/databard-how-i-wired-testsprite-into-my-coding-agent-to-defend-invariants-that-unit-tests-cant-1i9g</link>
      <guid>https://dev.to/papajams/databard-how-i-wired-testsprite-into-my-coding-agent-to-defend-invariants-that-unit-tests-cant-1i9g</guid>
      <description>&lt;h1&gt;
  
  
  I Built an Autonomous Testing Loop That Catches Silent Economic Bugs
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Built for &lt;a href="https://www.testsprite.com/hackathon-s3" rel="noopener noreferrer"&gt;TestSprite Season 3&lt;/a&gt; — "CLI Launch &amp;amp; Loop Engineering."&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Most testing loops test the wrong thing. They check "does the code run?" when the real question is "does the money move in the right direction?"&lt;/p&gt;

&lt;p&gt;I'm building &lt;a href="https://databard.persidian.com" rel="noopener noreferrer"&gt;DataBard&lt;/a&gt; — a marketplace where AI-persona agents bid on data-brief WANTs and settle on Solana devnet. The marketplace has economic invariants: properties that must hold or the market silently breaks. A unit test will happily pass while your reseller loses money on every trade.&lt;/p&gt;

&lt;p&gt;This is the story of building a TestSprite-powered loop that catches those bugs — and the real bugs it caught during development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7zs42f2za6d095izvi3g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7zs42f2za6d095izvi3g.png" alt=" " width="799" height="589"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Silent Economic Bugs
&lt;/h2&gt;

&lt;p&gt;Traditional tests check code paths. &lt;code&gt;assertEqual(add(1, 2), 3)&lt;/code&gt; tells you the function works. But what about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"The Digest reseller must earn positive margin on every deal"&lt;/strong&gt; — if the pricing strategy's &lt;code&gt;estimatedSubCost&lt;/code&gt; is wrong, the reseller charges 0.03 SOL and pays out 0.043 SOL. The code runs fine. The money just moves the wrong direction. Every. Single. Trade.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Cascade must win Quality briefs, Newsroom must win Freshness"&lt;/strong&gt; — if the buyer LLM's fit-vs-price weights drift, the cheapest persona always wins. The market "works" — it just lost its entire differentiator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Escrow state machine rejects invalid transitions"&lt;/strong&gt; — if &lt;code&gt;release()&lt;/code&gt; doesn't check for &lt;code&gt;delivered&lt;/code&gt; state, a buyer can release payment before the seller commits. The API returns 200. The money is gone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are &lt;strong&gt;economic invariants&lt;/strong&gt;, not code correctness checks. No amount of unit testing catches them. You need tests that run the actual market flow end-to-end and assert the economic properties hold.&lt;/p&gt;

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




&lt;h2&gt;
  
  
  The Loop
&lt;/h2&gt;

&lt;p&gt;Here's the architecture:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Write&lt;/strong&gt; — the coding agent ships code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify&lt;/strong&gt; — the TestSprite CLI uploads Python tests to the cloud and runs them against the live URL (&lt;code&gt;POST /api/market/graph-demo&lt;/code&gt;), which exercises real Solana devnet escrow calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix&lt;/strong&gt; — on failure, the agent reads the TestSprite failure bundle and proposes a minimal patch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify again&lt;/strong&gt; — rerun until green, or until a 4-iteration cap is hit.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[Coding Agent&amp;lt;br/&amp;gt;writes code] --&amp;gt; B[TestSprite CLI&amp;lt;br/&amp;gt;runs tests]
    B --&amp;gt; C[TestSprite Cloud&amp;lt;br/&amp;gt;pytest + requests&amp;lt;br/&amp;gt;vs live API]
    C -- failure bundle --&amp;gt; D[Fixer&amp;lt;br/&amp;gt;reads failure, patches code]
    D --&amp;gt; A
    C -- all green --&amp;gt; E[LOOP.md&amp;lt;br/&amp;gt;audit trail]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every iteration appends to &lt;code&gt;LOOP.md&lt;/code&gt; — the audit trail that judges read.&lt;/p&gt;

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




&lt;h2&gt;
  
  
  The Tests
&lt;/h2&gt;

&lt;p&gt;Each invariant is a Python + &lt;code&gt;requests&lt;/code&gt; test that hits the live API. No mocks. No local server. The tests exercise the full pipeline: Watchdog tick → persona bidding → buyer LLM scoring → escrow deposit → delivery → release.&lt;/p&gt;

&lt;h3&gt;
  
  
  Invariant 1: Digest must earn positive margin
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_digest_earns_positive_margin_on_every_deal&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# 1. Consumer posts the digest WANT
&lt;/span&gt;    &lt;span class="n"&gt;post_resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;post_phase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;post&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;want_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post_resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wantId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Consumer awards (Digest is the only bidder)
&lt;/span&gt;    &lt;span class="n"&gt;award_resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;post_phase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;want_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;award&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parent_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;award_resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;parentDeal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;priceLamports&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Digest fulfils by buying from Newsroom×N
&lt;/span&gt;    &lt;span class="n"&gt;deliver_resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;post_phase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;want_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deliver&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sub_prices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;priceLamports&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;deliver_resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;subDeals&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
    &lt;span class="n"&gt;total_sub_cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub_prices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# THE INVARIANT: parent price must exceed sum of sub prices
&lt;/span&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;parent_price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;total_sub_cost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; \
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Digest losing money: parent &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;parent_price&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ≤ subs &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;total_sub_cost&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test doesn't check if the code runs. It checks if the &lt;strong&gt;economy works&lt;/strong&gt;. If the pricing strategy drifts, this test fails — even though every function returns 200 OK.&lt;/p&gt;

&lt;h3&gt;
  
  
  Invariant 2: Persona fit must reflect focus
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_cascade_wins_quality_brief&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The e-commerce fixture triggers quality delta hints;
    Cascade (deep-dive persona) should win it, not Newsroom (flash).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_cycle_with_focus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ecommerce&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;winner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;award&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;winnerPersona&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;winner&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cascade&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; \
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Wrong persona won: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;winner&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; — fit weights may have drifted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Invariant 3: Escrow state machine rejects invalid transitions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_cannot_release_before_deliver&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Post a WANT, award it, then try to skip straight to release.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;post_phase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;post&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;want_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wantId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nf"&gt;post_phase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;want_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;award&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Try to release WITHOUT delivering — must fail
&lt;/span&gt;    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DEMO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fixture&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ecommerce&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;phase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;release&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wantId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;want_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; \
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Release before deliver should be rejected!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdj0m5irhmmki7jk02qrm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdj0m5irhmmki7jk02qrm.png" alt=" " width="799" height="322"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Bugs It Caught
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Bug 1: Digest lost money on every deal
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; The Digest reseller's &lt;code&gt;pricingStrategy&lt;/code&gt; charged &lt;code&gt;subFloor × N × 1.25&lt;/code&gt;, assuming Newsroom's floor of 0.008 SOL. But Newsroom's urgency-adjusted pricing at 120s deadlines was &lt;strong&gt;0.014 SOL/sub&lt;/strong&gt;. Digest was charging 0.03 SOL and paying out 0.043 SOL — a −0.013 SOL loss on every trade.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; Silent economic bugs are the worst kind. Unit tests wouldn't catch this because the flow "worked" — the money just moved the wrong direction. The &lt;code&gt;test_digest_earns_positive_margin_on_every_deal&lt;/code&gt; invariant catches it on the first run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; The loop's fixer (powered by an LLM) read the TestSprite failure bundle, identified that &lt;code&gt;estimatedSubCost&lt;/code&gt; was too low in &lt;code&gt;voice-config.ts&lt;/code&gt;, and bumped it to &lt;code&gt;sol(0.015)&lt;/code&gt; with a &lt;code&gt;1.3&lt;/code&gt; margin multiplier. Sub-WANT deadlines were extended to 300s so Newsroom's urgency premium drops. Result: &lt;strong&gt;+0.018 SOL profit per trade&lt;/strong&gt;, verified live on devnet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug 2: Persona fit weights drifted
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; The buyer LLM's fit-vs-price scoring had drifted to a 0.50/0.50 split between fit and price. At that split, the cheapest persona (Newsroom) was winning Quality briefs — the exact opposite of the intended behavior. The market was picking "cheapest" instead of "best fit."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; The loop's fixer moved the split to 0.68/0.32 in favor of fit, which was enough for Cascade's higher fit score to outweigh Newsroom's lower price on quality-flagged briefs. Two iterations to land on that ratio, then green.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug 3: Hand-written IDL discriminator wrong
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; The Anchor escrow IDL was hand-written to avoid a runtime dependency on the generated types. The &lt;code&gt;commit_delivery&lt;/code&gt; instruction discriminator was invented instead of computed from &lt;code&gt;sha256("global:&amp;lt;name&amp;gt;")[:8]&lt;/code&gt;. On-chain calls would have silently landed on wrong instructions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Computed all discriminators via &lt;code&gt;sha256("global:&amp;lt;name&amp;gt;")[:8]&lt;/code&gt;, verified against the &lt;code&gt;anchor build&lt;/code&gt;-generated &lt;code&gt;target/idl/escrow.json&lt;/code&gt;. Four matched; one didn't. Caught before deploy.&lt;/p&gt;

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




&lt;h2&gt;
  
  
  The Fixer: Multi-Provider Fallback
&lt;/h2&gt;

&lt;p&gt;The fixer is the part that reads the failure bundle and proposes a patch. It takes structured JSON — &lt;code&gt;{ file, old_string, new_string, commit_message, reasoning }&lt;/code&gt; — and applies it via a plain &lt;code&gt;fs.writeFileSync&lt;/code&gt;, but only after checking that &lt;code&gt;old_string&lt;/code&gt; appears exactly once in the target file and is at least 40 characters long. That length floor matters: a short &lt;code&gt;old_string&lt;/code&gt; like &lt;code&gt;"return price"&lt;/code&gt; could match in five different places, and the fixer would have no way to know which one the model meant. Forcing a longer, more specific anchor makes an accidental match essentially impossible.&lt;/p&gt;

&lt;p&gt;No arbitrary code execution. No nested CLI with permission-bypass. No tool-calling. If the model hallucinates a match that doesn't exist, the fixer bails cleanly and the loop records the miss.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The multi-provider fallback&lt;/strong&gt; was the key insight. Models differ in JSON discipline. A model that outputs a preamble like "Here's the fix:" — or picks a too-short &lt;code&gt;old_string&lt;/code&gt; — fails the schema check and the loop moves to the next provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[fixer] providers in fallback order: venice → nvidia
[fixer] trying venice (venice-uncensored)…
[fixer] × venice: bad JSON — old_string too short — must be ≥40 chars
[fixer] trying nvidia (openai/gpt-oss-20b)…
[fixer] ✓ nvidia patched src/lib/market/rates.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Total: 11.5 seconds. Venice's answer was slightly under-specified; NVIDIA delivered a clean patch. Both providers stayed within their respective SDK quotas.&lt;/p&gt;

&lt;p&gt;The fallback chain runs Venice → NVIDIA NIM → Anthropic. Any single-provider outage doesn't stall the loop. Provider order is configurable via &lt;code&gt;LOOP_PROVIDER_ORDER=venice,nvidia,anthropic&lt;/code&gt;.&lt;/p&gt;

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




&lt;h2&gt;
  
  
  CI/CD: The Loop on Autopilot
&lt;/h2&gt;

&lt;p&gt;The TestSprite checker is wired into GitHub Actions. Every PR reruns the invariants and fails the build if something breaks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/testsprite.yml&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull_request&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;TESTSPRITE_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TESTSPRITE_API_KEY }}&lt;/span&gt;
  &lt;span class="na"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TESTSPRITE_PROJECT_ID }}&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;verify-invariants&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install -g @testsprite/testsprite-cli&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;testsprite test run --all \&lt;/span&gt;
            &lt;span class="s"&gt;--project "$PROJECT_ID" \&lt;/span&gt;
            &lt;span class="s"&gt;--wait \&lt;/span&gt;
            &lt;span class="s"&gt;--output json &amp;gt; results.json&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;FAILED=$(cat results.json | jq '[.[] | select(.status != "passed")] | length')&lt;/span&gt;
          &lt;span class="s"&gt;if [ "$FAILED" != "0" ]; then exit 1; fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the stickiest version of the loop. Long after the hackathon, every PR is gated on the economic invariants. You can't merge broken pricing logic. You can't drift the persona weights. The checker works forever.&lt;/p&gt;




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

&lt;h3&gt;
  
  
  1. Test invariants, not code paths
&lt;/h3&gt;

&lt;p&gt;The biggest insight: &lt;strong&gt;test what must be true about the system, not what the code does.&lt;/strong&gt; "Digest earns positive margin" is an invariant. "The pricing function returns a number" is a code path check. The former catches real bugs. The latter catches typos.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Real tests &amp;gt; mock tests
&lt;/h3&gt;

&lt;p&gt;Every test hits the live API. No mocks, no local server, no &lt;code&gt;jest.mock()&lt;/code&gt;. This means the tests catch integration bugs that mock-based tests miss — like when the Solana RPC rate-limits you and the escrow deposit fails silently.&lt;/p&gt;

&lt;p&gt;The downside: the tests are slower (14 seconds for 4 tests) and can fail for infrastructure reasons (Solana devnet RPC 429s). But the failures are honest — they tell you something is actually broken, not that your mock is stale.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The fixer needs guardrails
&lt;/h3&gt;

&lt;p&gt;An autonomous agent that reads failures and patches code is powerful but dangerous. The guardrails that made it safe:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structured JSON only&lt;/strong&gt; — no free-form code execution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;old_string&lt;/code&gt; must be ≥40 chars and unique&lt;/strong&gt; — prevents ambiguous or hallucinated matches&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4-iteration cap&lt;/strong&gt; — runaway loops are worse than honest failures&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every patch committed&lt;/strong&gt; — reviewers can &lt;code&gt;git log&lt;/code&gt; every mutation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. The loop is honest about failures
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;LOOP.md&lt;/code&gt; audit trail shows 22 iterations: 4 passed, 16 failed/infra-limited, 9 patches applied. That's not a failure — that's the loop working. A loop that only shows passes is suspicious. A loop that shows failures, fixes, and re-runs is trustworthy.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Total iterations&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tests passed&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tests failed (caught real bugs)&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure failures (Solana RPC 429)&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Patches applied by the fixer&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unique commit SHAs in audit trail&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provider fallback chain&lt;/td&gt;
&lt;td&gt;Venice → NVIDIA → Anthropic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fixer end-to-end time&lt;/td&gt;
&lt;td&gt;~11.5 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI/CD integration&lt;/td&gt;
&lt;td&gt;GitHub Actions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live app:&lt;/strong&gt; &lt;a href="https://databard.persidian.com" rel="noopener noreferrer"&gt;databard.persidian.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/thisyearnofear/databard" rel="noopener noreferrer"&gt;github.com/thisyearnofear/databard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loop audit trail:&lt;/strong&gt; &lt;a href="https://github.com/thisyearnofear/databard/blob/main/LOOP.md" rel="noopener noreferrer"&gt;LOOP.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test files:&lt;/strong&gt; &lt;a href="https://github.com/thisyearnofear/databard/tree/main/tests/testsprite" rel="noopener noreferrer"&gt;tests/testsprite/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The TestSprite CLI is open source (Apache 2.0) — &lt;a href="https://github.com/TestSprite/testsprite-cli" rel="noopener noreferrer"&gt;install it from GitHub&lt;/a&gt; and wire it into your own loop.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built for &lt;a href="https://www.testsprite.com/hackathon-s3" rel="noopener noreferrer"&gt;TestSprite Season 3&lt;/a&gt; — "CLI Launch &amp;amp; Loop Engineering." The loop is the product. The product is the loop.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>recursive</category>
      <category>loop</category>
      <category>ai</category>
      <category>software</category>
    </item>
    <item>
      <title>Familexyz: Giving AI Agents Persistent Memory with Cognee Cloud</title>
      <dc:creator>Papa</dc:creator>
      <pubDate>Sat, 04 Jul 2026 15:23:15 +0000</pubDate>
      <link>https://dev.to/papajams/familexyz-giving-ai-agents-persistent-memory-with-cognee-cloud-1el</link>
      <guid>https://dev.to/papajams/familexyz-giving-ai-agents-persistent-memory-with-cognee-cloud-1el</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Integrating Cognee Cloud's memory layer into FamilyXYZ — a multi-agent AI platform where five philosophy-inspired agents help families strengthen their relationships across Telegram and the web — and ended up filing a PR against Cognee itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;TL;DR — 4 lessons from this integration:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;When the docs are unclear, read the source.&lt;/strong&gt; An "undocumented" endpoint we thought didn't exist actually did — we found it in the Cognee repo, not the docs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The default search mode isn't always right for you.&lt;/strong&gt; Switching from &lt;code&gt;GRAPH_COMPLETION&lt;/code&gt; to &lt;code&gt;CHUNKS&lt;/code&gt; cut an LLM call and seconds of latency out of every recall.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful degradation is non-negotiable.&lt;/strong&gt; A no-op fallback service meant the app kept working even with the memory layer fully disabled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fixing a bug taught us more than the docs did.&lt;/strong&gt; We ended up contributing a PR (&lt;a href="https://github.com/topoteretes/cognee/pull/3863" rel="noopener noreferrer"&gt;#3863&lt;/a&gt;) after tracing inconsistent error responses through the router source.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Details below.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;FamilyXYZ has five AI agents — Wisdom (Alain de Botton), Intimacy (Esther Perel &amp;amp; Gottman), Presence (Thich Nhat Hanh), Growth (James Clear &amp;amp; Carol Dweck), and Bridge (StoryCorps &amp;amp; bell hooks). Families interact with them via a Telegram bot and a web dashboard.&lt;/p&gt;

&lt;p&gt;The agents were helpful in the moment, but they had no memory. A parent could tell the Wisdom agent about a difficult conversation with their teenager on Monday, and by Wednesday the agent had no idea that conversation ever happened. For a family wellness product, this is a fundamental gap — real relationships are built on accumulated context, not single interactions.&lt;/p&gt;

&lt;p&gt;We needed persistent, per-user memory that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stored every conversation, check-in, and interaction&lt;/li&gt;
&lt;li&gt;Could be queried for relevant context before each agent response&lt;/li&gt;
&lt;li&gt;Supported enrichment (building a knowledge graph from raw text)&lt;/li&gt;
&lt;li&gt;Allowed full data deletion (GDPR-style right to be forgotten)&lt;/li&gt;
&lt;li&gt;Was optional — the app had to keep working if the memory service was down&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh2vm60nk2acy1nexlx2o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh2vm60nk2acy1nexlx2o.png" alt=" " width="799" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Cognee Cloud
&lt;/h2&gt;

&lt;p&gt;We evaluated several options: raw vector databases (Pinecone, Weaviate), LLM-native memory (Mem0), and Cognee. We chose Cognee Cloud for three reasons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Knowledge graph, not just vectors.&lt;/strong&gt; Cognee doesn't just embed text and do similarity search — it builds an actual knowledge graph with entities and relationships. When a user says "I argued with my partner Sarah about screen time," Cognee extracts the entities (user, Sarah, screen time) and relationships (argued_with, about). This means recall can find semantically related memories even without keyword overlap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The lifecycle maps perfectly to agent memory.&lt;/strong&gt; Cognee has four operations that map cleanly to what an agent memory layer needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;remember()&lt;/code&gt; — ingest text, build the knowledge graph automatically&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;recall()&lt;/code&gt; — query the graph for relevant context&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;improve()&lt;/code&gt; — run enrichment on the existing graph&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;forget()&lt;/code&gt; — delete a user's entire memory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Managed cloud, no infrastructure.&lt;/strong&gt; Cognee Cloud gives us a tenant-isolated instance with a REST API. No graph database to run, no embedding pipeline to maintain, no vector index to tune.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  The Integration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;

&lt;p&gt;We built a standalone workspace package (&lt;code&gt;packages/memory&lt;/code&gt;) that wraps the Cognee Cloud REST API. It exports a &lt;code&gt;MemoryService&lt;/code&gt; interface with four methods and two implementations:&lt;br&gt;
packages/memory/src/&lt;br&gt;
├── MemoryService.ts          # Interface: remember, recall, improve, forget, isEnabled&lt;br&gt;
├── CogneeMemoryService.ts    # Talks to Cognee Cloud via REST API&lt;br&gt;
├── NoopMemoryService.ts      # Silent fallback — all methods are no-ops&lt;br&gt;
└── index.ts                  # Factory: reads env vars, returns the right impl&lt;/p&gt;

&lt;p&gt;The key design decision: &lt;strong&gt;graceful degradation&lt;/strong&gt;. If &lt;code&gt;COGNEE_ENABLED&lt;/code&gt; is not &lt;code&gt;true&lt;/code&gt;, or the API key is missing, or Cognee is unreachable, the app uses &lt;code&gt;NoopMemoryService&lt;/code&gt; and continues working with its existing SQLite-backed state. The memory layer is an enhancement, not a dependency.&lt;/p&gt;
&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;p&gt;Cognee Cloud uses two headers for multi-tenant auth:&lt;br&gt;
X-Api-Key: &lt;br&gt;
X-Tenant-Id: &lt;/p&gt;

&lt;p&gt;We encapsulated these in an &lt;code&gt;authHeaders()&lt;/code&gt; helper that injects both on every request.&lt;/p&gt;
&lt;h3&gt;
  
  
  Per-User Isolation
&lt;/h3&gt;

&lt;p&gt;Each user gets a dedicated Cognee dataset named &lt;code&gt;familexyz_user_&amp;lt;userId&amp;gt;&lt;/code&gt;. This gives us clean isolation — one user's memories never bleed into another's — and makes &lt;code&gt;forget()&lt;/code&gt; simple: delete the dataset, and all the user's graph data, vectors, and relational records are gone.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Naming note:&lt;/strong&gt; you'll see "FamilyXYZ" (the product), &lt;code&gt;familexyz&lt;/code&gt; (the repo/package name), and &lt;code&gt;famile.xyz&lt;/code&gt; (the live domain) used throughout this post. The codebase predates the current domain, hence the mismatch — nothing to worry about if you're cross-referencing the repo.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  The Four Operations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;remember()&lt;/strong&gt; — &lt;code&gt;POST /api/v1/remember&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After every Telegram conversation, check-in, or family interaction, we store the content plus metadata (which agent, what source, which family member) as a text blob. Cognee's &lt;code&gt;remember&lt;/code&gt; endpoint handles both ingestion and graph building (add + cognify combined), so we don't need a separate processing step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;enriched&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;memory.txt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;datasetName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// POST /api/v1/remember&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;recall()&lt;/strong&gt; — &lt;code&gt;POST /api/v1/recall&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Before an agent responds to a user message, we call &lt;code&gt;recall()&lt;/code&gt; to fetch relevant context from the user's memory graph. We use &lt;code&gt;searchType: "CHUNKS"&lt;/code&gt; with &lt;code&gt;topK: 5&lt;/code&gt; — this returns raw text snippets directly without an LLM call, which is cheaper, faster, and gives us text we can inject into the agent's prompt as context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;datasets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;searchType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CHUNKS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;topK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The returned snippets are injected into the agent's system prompt as "Previous context from memory:" — giving the agent awareness of past conversations without any agent framework changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;improve()&lt;/strong&gt; — &lt;code&gt;POST /api/v1/improve&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Triggered from the web dashboard's "Improve Memory" button. Runs Cognee's enrichment pipeline (memify) on the user's knowledge graph in the background. This is fire-and-forget — if it fails, the existing graph is still usable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;forget()&lt;/strong&gt; — &lt;code&gt;POST /api/v1/forget&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Triggered from the dashboard's "Forget All" button. Sends &lt;code&gt;{ dataset: "familexyz_user_&amp;lt;id&amp;gt;" }&lt;/code&gt; and Cognee handles the rest: deletes relational records, graph nodes/edges, and vector embeddings. Clean slate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wiring It Into the App
&lt;/h3&gt;

&lt;p&gt;The memory service is a singleton, initialized once at boot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// agent/src/index.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initMemoryService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@familexyz/memory&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;initMemoryService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in the Telegram bot's message router, before routing to an agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;memory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getMemoryService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Inject context into agent prompt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And after the agent responds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`User: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userMessage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\nAgent: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;agentResponse&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;conversation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wisdom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The web dashboard at &lt;code&gt;famile.xyz/memory&lt;/code&gt; exposes the full lifecycle via REST endpoints (&lt;code&gt;/api/memory/status&lt;/code&gt;, &lt;code&gt;/api/memory/recall&lt;/code&gt;, &lt;code&gt;/api/memory/remember&lt;/code&gt;, &lt;code&gt;/api/memory/forget&lt;/code&gt;, &lt;code&gt;/api/memory/improve&lt;/code&gt;) with JWT authentication.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The docs don't always match the API
&lt;/h3&gt;

&lt;p&gt;When we first integrated, the documentation for the &lt;code&gt;forget&lt;/code&gt; endpoint was inconsistent — the OpenAPI spec listed a &lt;code&gt;forget&lt;/code&gt; tag, but the API reference page 404'd. We initially assumed the REST endpoint didn't exist and built a workaround (UUID lookup via &lt;code&gt;GET /datasets&lt;/code&gt; + &lt;code&gt;DELETE /datasets/{id}&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;After diving into the Cognee open-source repo to contribute a PR, we discovered &lt;code&gt;POST /api/v1/forget&lt;/code&gt; does exist and accepts &lt;code&gt;{ dataset: "name" }&lt;/code&gt; directly. We simplified our implementation to a single call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; When the docs are unclear, read the source. Open-source projects often have better answers in the code than in the docs.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. CHUNKS vs GRAPH_COMPLETION matters a lot
&lt;/h3&gt;

&lt;p&gt;The default &lt;code&gt;searchType&lt;/code&gt; for &lt;code&gt;recall()&lt;/code&gt; is &lt;code&gt;GRAPH_COMPLETION&lt;/code&gt;, which calls an LLM to generate a natural language answer from the graph. This is expensive (LLM call per recall), slow (seconds of latency), and returns an answer, not raw context.&lt;/p&gt;

&lt;p&gt;For our use case — injecting context into an agent prompt — we want raw text snippets, not LLM-generated answers. Switching to &lt;code&gt;searchType: "CHUNKS"&lt;/code&gt; was a significant improvement: no LLM call, sub-second latency, and text we can directly inject.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; Read the search type options carefully. The default isn't always right for your use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Graceful degradation is non-negotiable
&lt;/h3&gt;

&lt;p&gt;The memory layer is optional. The app worked for months without it. If Cognee goes down, credits run out, or the network fails, the app must keep working.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;NoopMemoryService&lt;/code&gt; pattern — where every method is a silent no-op — made this painless. The calling code doesn't know or care whether Cognee is active. It calls &lt;code&gt;recall()&lt;/code&gt;, gets an empty array, and the agent responds without memory context. Not ideal, but not broken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; When adding a cloud dependency to an existing app, wrap it in an interface with a no-op fallback. The integration becomes a feature enhancement, not a reliability risk.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Contributing back is the best documentation
&lt;/h3&gt;

&lt;p&gt;We found issue #3748 in the Cognee repo — "Inconsistent API error responses in improve, forget, and recall routers." Three routers were returning raw error dicts with non-standard HTTP status codes (420, 409) instead of the canonical &lt;code&gt;ErrorResponse&lt;/code&gt; DTO used by the other five routers.&lt;/p&gt;

&lt;p&gt;We fixed it in &lt;a href="https://github.com/topoteretes/cognee/pull/3863" rel="noopener noreferrer"&gt;PR #3863&lt;/a&gt;, and the process of reading the router source code taught us more about the API than the docs ever did. We saw exactly how each endpoint handled errors, what status codes to expect, and what the response schema looked like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; The fastest way to understand an open-source API is to fix a bug in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Agent framework&lt;/td&gt;
&lt;td&gt;ElizaOS (TypeScript)&lt;/td&gt;
&lt;td&gt;Existing multi-agent orchestration, already in place before this integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory layer&lt;/td&gt;
&lt;td&gt;Cognee Cloud (REST API)&lt;/td&gt;
&lt;td&gt;Managed knowledge graph — no infra to run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;Hono on Node.js, PM2 on Hetzner&lt;/td&gt;
&lt;td&gt;Lightweight, fast cold starts, cheap to run on a VPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;Next.js on Netlify&lt;/td&gt;
&lt;td&gt;Simple static + serverless deploy for the dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bot&lt;/td&gt;
&lt;td&gt;Telegram Bot API&lt;/td&gt;
&lt;td&gt;Primary interaction surface for families&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth&lt;/td&gt;
&lt;td&gt;JWT (HMAC-SHA256)&lt;/td&gt;
&lt;td&gt;Stateless auth across bot and dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Package manager&lt;/td&gt;
&lt;td&gt;pnpm workspaces&lt;/td&gt;
&lt;td&gt;Monorepo with shared packages like &lt;code&gt;@familexyz/memory&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;The memory layer is live in production at &lt;code&gt;api.famile.xyz&lt;/code&gt;. A round-trip test confirms it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;remember&lt;/strong&gt;: Store "User checked in: mood=good, had breakfast with family" → &lt;code&gt;{"success":true}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;recall&lt;/strong&gt;: Query "What did the user do this morning?" → Returns the exact memory with metadata&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;status&lt;/strong&gt;: &lt;code&gt;{"enabled":true,"service":"cognee"}&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Telegram bot now remembers past conversations, the web dashboard shows memory status and lets families manage their data, and the whole system degrades gracefully if Cognee is ever unavailable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Session-scoped memory&lt;/strong&gt;: Cognee supports &lt;code&gt;session_id&lt;/code&gt; for per-conversation context tracking. We could give each Telegram conversation its own session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node sets&lt;/strong&gt;: Cognee's &lt;code&gt;remember&lt;/code&gt; accepts &lt;code&gt;node_set&lt;/code&gt; tags for grouping related data. We could tag memories by agent (wisdom, intimacy, etc.) for more targeted recall.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope-aware recall&lt;/strong&gt;: Cognee's &lt;code&gt;scope&lt;/code&gt; parameter can search "graph", "session", "trace", or "all" — we're currently using the default "auto" but could be more intentional about which memory sources to query.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;FamilyXYZ is an open-source family wellness platform. The Cognee integration code is at &lt;a href="https://github.com/thisyearnofear/familexyz" rel="noopener noreferrer"&gt;github.com/thisyearnofear/familexyz&lt;/a&gt;. The Cognee PR is at &lt;a href="https://github.com/topoteretes/cognee/pull/3863" rel="noopener noreferrer"&gt;github.com/topoteretes/cognee/pull/3863&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I turned café wifi speeds into a metro map — on Aurora Serverless v2 + Vercel</title>
      <dc:creator>Papa</dc:creator>
      <pubDate>Mon, 29 Jun 2026 23:51:41 +0000</pubDate>
      <link>https://dev.to/papajams/i-turned-cafe-wifi-speeds-into-a-metro-map-on-aurora-serverless-v2-vercel-55f</link>
      <guid>https://dev.to/papajams/i-turned-cafe-wifi-speeds-into-a-metro-map-on-aurora-serverless-v2-vercel-55f</guid>
      <description>&lt;h1&gt;
  
  
  I Built Lattency: A Crowdsourced Metro Map for Café Wi-Fi Using Aurora PostgreSQL Serverless v2 and Vercel
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsslivpyfwjnqti46slnx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsslivpyfwjnqti46slnx.png" alt=" " width="799" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Built for the &lt;strong&gt;H0: Hack the Zero Stack&lt;/strong&gt; hackathon (Vercel × AWS Databases).&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Stack:&lt;/strong&gt; Amazon Aurora PostgreSQL Serverless v2 + PostGIS + Next.js on Vercel&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a href="https://lattency.vercel.app/" rel="noopener noreferrer"&gt;https://lattency.vercel.app/&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://github.com/thisyearnofear/lattency" rel="noopener noreferrer"&gt;https://github.com/thisyearnofear/lattency&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;#H0Hackathon&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finding a café with reliable Wi-Fi (in Nairobi) is surprisingly difficult.&lt;/p&gt;

&lt;p&gt;You buy a coffee, settle in, open your laptop, and discover the network can't survive a video call. Twenty minutes later you're packing up and looking for another café.&lt;/p&gt;

&lt;p&gt;It's a problem that quietly steals hours every week.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Lattency&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of reviews saying &lt;em&gt;"the Wi-Fi is good"&lt;/em&gt;, Lattency is a crowdsourced metro map where every café is a station and every line represents a Wi-Fi speed tier—not geography.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚄 &lt;strong&gt;Express&lt;/strong&gt; — &lt;strong&gt;50 Mbps and above&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🚉 &lt;strong&gt;Local&lt;/strong&gt; — &lt;strong&gt;10–49 Mbps&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🚧 &lt;strong&gt;Suspended&lt;/strong&gt; — &lt;strong&gt;Below 10 Mbps&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anyone can run a speed test, submit a measurement, and within seconds the café is reclassified for everyone else.&lt;/p&gt;

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

&lt;p&gt;The interesting part wasn't drawing the map.&lt;/p&gt;

&lt;p&gt;It was building a backend that could answer &lt;strong&gt;"What's the fastest café near me?"&lt;/strong&gt;, update itself in near real time, resist bad data, and cost almost nothing when nobody was using it.&lt;/p&gt;

&lt;p&gt;This post is about the infrastructure that makes that possible.&lt;/p&gt;




&lt;h1&gt;
  
  
  Choosing the database
&lt;/h1&gt;

&lt;p&gt;The hackathon offered three AWS database options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon DynamoDB&lt;/li&gt;
&lt;li&gt;Amazon Aurora DSQL&lt;/li&gt;
&lt;li&gt;Amazon Aurora PostgreSQL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Lattency, the decision came down to one requirement:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Find cafés within a radius of the user's location.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With PostGIS, that's almost trivial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lng&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;cafes&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;ST_DWithin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;geog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;geography&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ST_DWithin()&lt;/code&gt; is exactly the kind of query PostGIS was built for.&lt;/p&gt;

&lt;p&gt;DynamoDB has no native geospatial radius queries, and Aurora DSQL doesn't currently support PostGIS.&lt;/p&gt;

&lt;p&gt;Once location search became a requirement, Aurora PostgreSQL wasn't simply the easiest choice—it was the only database that naturally fit the problem.&lt;/p&gt;

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

&lt;p&gt;The second reason was economics.&lt;/p&gt;

&lt;p&gt;Aurora PostgreSQL Serverless v2 scales all the way down to &lt;strong&gt;0 ACUs&lt;/strong&gt; when idle.&lt;/p&gt;

&lt;p&gt;A project like Lattency doesn't receive traffic around the clock. It wakes up during working hours, slows down overnight, and may eventually expand city by city.&lt;/p&gt;

&lt;p&gt;Paying only when the database is actually serving requests is exactly the pricing model I wanted.&lt;/p&gt;

&lt;p&gt;The trade-off is a cold start of roughly &lt;strong&gt;15–30 seconds&lt;/strong&gt; after long periods of inactivity.&lt;/p&gt;

&lt;p&gt;Fortunately, there are ways to hide that from users.&lt;/p&gt;




&lt;h1&gt;
  
  
  Let PostgreSQL decide the line colour
&lt;/h1&gt;

&lt;p&gt;Raw speed tests are noisy.&lt;/p&gt;

&lt;p&gt;One person on hotel Wi-Fi, a VPN, or a congested network shouldn't immediately downgrade an entire café.&lt;/p&gt;

&lt;p&gt;Instead of calculating speed tiers on every request, Lattency maintains a &lt;strong&gt;materialized view&lt;/strong&gt; that stores per-café statistics.&lt;/p&gt;

&lt;p&gt;Every new measurement refreshes that view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;REFRESH&lt;/span&gt; &lt;span class="n"&gt;MATERIALIZED&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;CONCURRENTLY&lt;/span&gt; &lt;span class="n"&gt;cafe_speed_stats&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is &lt;code&gt;CONCURRENTLY&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Without it, PostgreSQL would lock the view while rebuilding it.&lt;/p&gt;

&lt;p&gt;With it, visitors continue reading from the old version while PostgreSQL prepares the new one in the background.&lt;/p&gt;

&lt;p&gt;No downtime.&lt;/p&gt;

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

&lt;p&gt;As more measurements came in, I made the aggregation smarter.&lt;/p&gt;

&lt;p&gt;Once a café has enough submissions, measurements marked as outliers are ignored when calculating the median.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one accidental upload won't move a café into &lt;em&gt;Suspended&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;genuine network upgrades still appear naturally over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Outliers are excluded from the calculation—not deleted—so the underlying data remains intact.&lt;/p&gt;




&lt;h1&gt;
  
  
  Making serverless behave like a long-running app
&lt;/h1&gt;

&lt;p&gt;Connecting a serverless application to PostgreSQL introduces three practical problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Too many database connections
&lt;/h2&gt;

&lt;p&gt;Every serverless function can create its own PostgreSQL connection.&lt;/p&gt;

&lt;p&gt;That doesn't scale very well.&lt;/p&gt;

&lt;p&gt;Instead, I cache a single &lt;code&gt;pg.Pool&lt;/code&gt; on &lt;code&gt;globalThis&lt;/code&gt;, allowing warm instances to reuse existing connections.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;globalForPg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;globalThis&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Pool&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;globalForPg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rejectUnauthorized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;globalForPg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keeping each instance to a single pooled connection dramatically reduces connection pressure.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Don't wake the database for every visitor
&lt;/h2&gt;

&lt;p&gt;Most people viewing the homepage are looking at the same information.&lt;/p&gt;

&lt;p&gt;There's no reason every request should hit Aurora.&lt;/p&gt;

&lt;p&gt;The homepage is statically rendered using Incremental Static Regeneration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;revalidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most visitors receive a cached page from Vercel's edge network.&lt;/p&gt;

&lt;p&gt;Aurora only needs to wake once every minute instead of once per visitor, reducing both cost and cold starts.&lt;/p&gt;

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




&lt;h2&gt;
  
  
  3. Never fail because the database is sleeping
&lt;/h2&gt;

&lt;p&gt;Cold starts happen.&lt;/p&gt;

&lt;p&gt;Instead of showing an error page while Aurora wakes up, Lattency falls back to a bundled snapshot included with the application.&lt;/p&gt;

&lt;p&gt;The map still loads.&lt;/p&gt;

&lt;p&gt;Users can still explore cafés.&lt;/p&gt;

&lt;p&gt;For a hackathon demo, that's the difference between an impressive first impression and a blank screen.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Refresh after the response
&lt;/h2&gt;

&lt;p&gt;Refreshing the materialized view isn't part of the user's request.&lt;/p&gt;

&lt;p&gt;Using Next.js' &lt;code&gt;after()&lt;/code&gt; API, the response is sent immediately while the refresh runs afterwards.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;refreshSpeedStats&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users don't wait for maintenance work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1cfb6bxnif9zi0pvprqp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1cfb6bxnif9zi0pvprqp.jpg" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Crowdsourced data only works if people can't game it
&lt;/h1&gt;

&lt;p&gt;Allowing anyone to contribute data is both the best feature and the biggest security problem.&lt;/p&gt;

&lt;p&gt;I wanted automatic submissions to be more trustworthy than manually typed numbers.&lt;/p&gt;

&lt;p&gt;Instead of asking contributors to copy results from another speed test, Lattency performs the test directly in the browser against a Vercel Edge region.&lt;/p&gt;

&lt;p&gt;It measures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;download speed&lt;/li&gt;
&lt;li&gt;upload speed&lt;/li&gt;
&lt;li&gt;latency&lt;/li&gt;
&lt;li&gt;jitter&lt;/li&gt;
&lt;li&gt;packet loss&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More importantly, the server—not the client—decides whether the submission counts as an automatic test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;testMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;downloadBytes&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;downloadDurationMs&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;manual&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A user can't simply claim they performed an automatic test.&lt;/p&gt;

&lt;p&gt;The evidence has to exist.&lt;/p&gt;




&lt;p&gt;To prevent spam without storing personal information, every IP address is salted and hashed before comparison.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RATE_LIMIT_SALT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The raw IP is never stored.&lt;/p&gt;

&lt;p&gt;Rate limiting becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One measurement per IP, per café, every ten minutes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Privacy is preserved while abuse becomes significantly harder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6a8smagngla072ifa3vr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6a8smagngla072ifa3vr.jpg" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Adding a brand-new café is also transactional.&lt;/p&gt;

&lt;p&gt;Creating the café and inserting its first measurement happen inside the same database transaction.&lt;/p&gt;

&lt;p&gt;If either operation fails, everything rolls back.&lt;/p&gt;

&lt;p&gt;The map never ends up with empty cafés that have no measurements attached.&lt;/p&gt;




&lt;h1&gt;
  
  
  The production architecture I almost shipped
&lt;/h1&gt;

&lt;p&gt;One rabbit hole consumed far more time than I expected.&lt;/p&gt;

&lt;p&gt;RDS Proxy.&lt;/p&gt;

&lt;p&gt;Initially, I wanted every database connection to pass through an RDS Proxy instead of exposing Aurora directly.&lt;/p&gt;

&lt;p&gt;I configured:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a dedicated security group&lt;/li&gt;
&lt;li&gt;Secrets Manager credentials&lt;/li&gt;
&lt;li&gt;IAM permissions&lt;/li&gt;
&lt;li&gt;the proxy itself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything looked correct.&lt;/p&gt;

&lt;p&gt;Nothing connected.&lt;/p&gt;

&lt;p&gt;Eventually I realised why.&lt;/p&gt;

&lt;p&gt;RDS Proxy is intentionally private.&lt;/p&gt;

&lt;p&gt;Its endpoint lives inside the VPC.&lt;/p&gt;

&lt;p&gt;That's perfect for Lambda, ECS, and EC2.&lt;/p&gt;

&lt;p&gt;It's not designed for platforms like Vercel running outside your AWS network.&lt;/p&gt;

&lt;p&gt;Connecting to it requires additional networking such as PrivateLink or a load balancer.&lt;/p&gt;

&lt;p&gt;That lesson ended up being more valuable than the configuration itself.&lt;/p&gt;

&lt;p&gt;If I were taking Lattency to production today, I'd choose one of these architectures:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Vercel × AWS Marketplace Aurora Integration&lt;/strong&gt; — Aurora provisioned through Vercel using PrivateLink. No public database endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PrivateLink&lt;/strong&gt; — More infrastructure, but the same private networking model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network Load Balancer + RDS Proxy&lt;/strong&gt; — Works without reprovisioning, although it adds cost and operational complexity.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the hackathon, opening PostgreSQL on port 5432 was the pragmatic decision.&lt;/p&gt;

&lt;p&gt;I think it's more useful to explain that trade-off honestly than pretend the demo shipped with perfect infrastructure.&lt;/p&gt;




&lt;h1&gt;
  
  
  What I ended up with
&lt;/h1&gt;

&lt;p&gt;The metro-map interface is what people notice first.&lt;/p&gt;

&lt;p&gt;The infrastructure is what makes it believable.&lt;/p&gt;

&lt;p&gt;Lattency combines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Aurora PostgreSQL Serverless v2 + PostGIS&lt;/strong&gt; for fast geospatial searches and scale-to-zero pricing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Materialized views&lt;/strong&gt; for outlier-aware café classification without blocking reads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incremental Static Regeneration&lt;/strong&gt; so most visitors never touch the database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection reuse&lt;/strong&gt; to keep PostgreSQL healthy in a serverless environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-side trust verification&lt;/strong&gt; so automatic measurements can't be trivially faked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful fallbacks&lt;/strong&gt; so the application continues working even while Aurora is waking up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same architecture could power Wi-Fi maps for any city.&lt;/p&gt;

&lt;p&gt;Nairobi just happened to be the first one.&lt;/p&gt;




&lt;p&gt;If you'd like to explore it yourself:&lt;/p&gt;

&lt;p&gt;🗺️ &lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a href="https://lattency.vercel.app/" rel="noopener noreferrer"&gt;https://lattency.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💻 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/thisyearnofear/lattency" rel="noopener noreferrer"&gt;https://github.com/thisyearnofear/lattency&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd love to hear what you'd build with the same stack—or how you'd improve Lattency.&lt;/p&gt;

&lt;p&gt;Built for the &lt;strong&gt;H0: Hack the Zero Stack&lt;/strong&gt; hackathon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#H0Hackathon&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aws</category>
      <category>postgres</category>
      <category>postgressql</category>
    </item>
    <item>
      <title>Cognivern - Spend OS For Agent Teams</title>
      <dc:creator>Papa</dc:creator>
      <pubDate>Mon, 08 Jun 2026 06:47:42 +0000</pubDate>
      <link>https://dev.to/papajams/cognivern-spend-os-for-agent-teams-1852</link>
      <guid>https://dev.to/papajams/cognivern-spend-os-for-agent-teams-1852</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-05-21"&gt;GitHub Finish-Up-A-Thon Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;&lt;a href="https://cognivern.vercel.app" rel="noopener noreferrer"&gt;Cognivern&lt;/a&gt;&lt;/strong&gt; is a control plane for agent operations — a SpendOS for agent teams.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ljj5r4u03j4wote2d8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ljj5r4u03j4wote2d8m.png" alt=" " width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As AI agents proliferate across development workflows, a quiet crisis is brewing: no one really controls what agents spend, on what, on behalf of whom, or why. Every agent gets what amounts to a blank check — against model APIs, against wallets, against third-party services. Cognivern exists to fix that.&lt;/p&gt;

&lt;p&gt;The platform unifies governed wallet spend and AI spend governance across IDE, CLI, and agent workflows into a single auditable control layer. The core promise is simple: &lt;strong&gt;move fast without blank checks&lt;/strong&gt;. Every spend decision can be policy-checked, privacy-preserving, efficiency-aware, and audit-ready — before it executes.&lt;/p&gt;

&lt;p&gt;This matters especially in emerging markets and for teams building on-chain infrastructure, where cost overruns from runaway agents aren't just annoying — they're existential. You don't burn budget you don't have chasing a misconfigured prompt loop.&lt;/p&gt;

&lt;p&gt;At its core, Cognivern provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Policy evaluation&lt;/strong&gt; — enforce who/what/when rules before any spend executes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy-native operations&lt;/strong&gt; — evaluate sensitive policy context via confidential paths using Fhenix FHE (Fully Homomorphic Encryption)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI spend governance&lt;/strong&gt; — model/runtime usage visibility and optimization alongside financial controls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit trails&lt;/strong&gt; — persist decision evidence (&lt;code&gt;decisionId&lt;/code&gt;, attestation, run context) for continuous accountability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-provider AI routing&lt;/strong&gt; — ChainGPT as the primary Web3-native LLM, with Fireworks, OpenAI, Gemini, Anthropic, and others as fallbacks
The stack is TypeScript + Solidity, deployed across X Layer Testnet (execution and policy), Filecoin Calibration (audit storage), and Fhenix (confidential policy state). The frontend lives at &lt;a href="https://cognivern.vercel.app" rel="noopener noreferrer"&gt;cognivern.vercel.app&lt;/a&gt; and includes a PromptOS terminal for natural-language governance interaction.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;🔗 &lt;strong&gt;Live app:&lt;/strong&gt; &lt;a href="https://cognivern.vercel.app" rel="noopener noreferrer"&gt;cognivern.vercel.app&lt;/a&gt;&lt;br&gt;&lt;br&gt;
🔗 &lt;strong&gt;API:&lt;/strong&gt; &lt;a href="https://cognivern.thisyearnofear.com" rel="noopener noreferrer"&gt;cognivern.thisyearnofear.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
🔗 &lt;strong&gt;PromptOS Terminal:&lt;/strong&gt; &lt;a href="https://cognivern.vercel.app/os" rel="noopener noreferrer"&gt;cognivern.vercel.app/os&lt;/a&gt;&lt;br&gt;&lt;br&gt;
🔗 &lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://github.com/thisyearnofear/cognivern" rel="noopener noreferrer"&gt;github.com/thisyearnofear/cognivern&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Key flows you can explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Submit a spend request through the dashboard and watch policy evaluation fire in real time&lt;/li&gt;
&lt;li&gt;Use the PromptOS terminal to interact with governance rules in natural language&lt;/li&gt;
&lt;li&gt;Inspect the audit log — every decision has a &lt;code&gt;decisionId&lt;/code&gt; and attestation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  - Try the encrypted spend path (Fhenix), where policy is evaluated over encrypted inputs — the server never sees the raw values
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Comeback Story
&lt;/h2&gt;

&lt;p&gt;Cognivern started as a hackathon project with a clear thesis but rough edges everywhere. The core governance loop worked, but it was held together with duct tape: no proper workspace isolation, no rate limiting, brittle contract interactions, and a frontend that was functional but not something you'd confidently hand to an operator.&lt;/p&gt;

&lt;p&gt;Here's what changed during the finish-up:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure hardening&lt;/strong&gt; — Added per-workspace and per-API-key rate limiters with sliding windows, deep health checks, and circuit-breaker patterns. Moved to TypeScript strict mode throughout. Built out a unified CI pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;172 tests&lt;/strong&gt; — Unit, integration, and E2E via Playwright. The project went from "it works on my machine" to something with real coverage guarantees.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-workspace and policy versioning&lt;/strong&gt; — Each workspace now has independent API keys, rate limits, and a full policy version history. This was the feature that turned a demo into something a real team could adopt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fhenix Wave 5–7&lt;/strong&gt; — The FHE integration went from a proof-of-concept to a full institutional demo: encrypted policies, MEV-protected execution, selective auditor disclosure, two-phase FHE resolution with &lt;code&gt;resolveDecision&lt;/code&gt;, sealed-bid vendor selection, and a Privara confidential payroll flow. Also migrated from Helium testnet to Arbitrum Sepolia.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ChainGPT integration&lt;/strong&gt; — Brought in ChainGPT as the primary AI provider for Web3-native governance queries, with the Smart Contract Auditor running as runtime pre-spend defense. This felt like the missing piece — governance AI that actually understands on-chain context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operator UX&lt;/strong&gt; — PromptOS terminal integrated into the sidebar, voice input via ElevenLabs STT, self-service onboarding flow, animated workspace mode toggles, full mobile responsiveness.&lt;/p&gt;

&lt;p&gt;The project went from ~60% production-ready to ~93%. The remaining 7% is mostly production key management and a few contract audit items before mainnet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx30cvuw5p8sgsh1j8c5v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx30cvuw5p8sgsh1j8c5v.png" alt=" " width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  My Experience with GitHub Copilot
&lt;/h2&gt;

&lt;p&gt;Cognivern is a project with a lot of moving parts — Solidity contracts, TypeScript APIs, multi-chain deployment scripts, FHE integration, and a React frontend — often all in motion at the same time. Copilot was the connective tissue that kept things moving without constant context-switching tax.&lt;/p&gt;

&lt;p&gt;A few specific ways it earned its keep:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Boilerplate elimination for the governance endpoints.&lt;/strong&gt; The API has 12+ endpoints with consistent patterns — request validation, policy lookup, decision logging, response shaping. Writing the first one from scratch was fine; Copilot handled the rest, often getting the full shape right on the first suggestion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solidity contract work.&lt;/strong&gt; The &lt;code&gt;ConfidentialSpendPolicy&lt;/code&gt; contract for Fhenix was genuinely novel — FHE operations aren't something most developers have pattern-matched on. Copilot's suggestions weren't always right, but they were useful scaffolding that surfaced the right questions. The back-and-forth of accepting, rejecting, and editing suggestions was faster than writing from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test generation.&lt;/strong&gt; Getting to 172 tests would have taken much longer without Copilot helping generate test cases from the function signatures and existing test patterns. It's particularly good at the "write 10 edge case tests for this validator" kind of ask.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;README and documentation.&lt;/strong&gt; The architecture docs, developer guide, and deployment docs are detailed. Copilot helped maintain consistent voice and structure across them, and was surprisingly good at inferring the right level of technical detail for each audience.&lt;/p&gt;

&lt;p&gt;The honest take: Copilot didn't make hard architectural decisions easier. The FHE integration design, the multi-chain deployment strategy, the policy versioning data model — those required real thinking. But it absorbed a huge amount of the mechanical work and kept me in flow during the push to get this finished.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Find me on &lt;a href="https://farcaster.xyz/papa" rel="noopener noreferrer"&gt;Farcaster&lt;/a&gt; and &lt;a href="https://palus.app/u/papajams" rel="noopener noreferrer"&gt;Lens&lt;/a&gt; — always building at the intersection of AI, emerging markets, and on-chain infrastructure.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>githubchallenge</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>DiversiFi — Finishing What Inflation Started</title>
      <dc:creator>Papa</dc:creator>
      <pubDate>Mon, 08 Jun 2026 06:34:44 +0000</pubDate>
      <link>https://dev.to/papajams/diversifi-finishing-what-inflation-started-9mb</link>
      <guid>https://dev.to/papajams/diversifi-finishing-what-inflation-started-9mb</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-05-21"&gt;GitHub Finish-Up-A-Thon Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




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

&lt;p&gt;&lt;a href="https://diversifiapp.vercel.app" rel="noopener noreferrer"&gt;DiversiFi&lt;/a&gt; is an AI-powered stablecoin diversification app built on Celo and Arbitrum. The premise is simple but personal: your stablecoins shouldn't all be pegged to the dollar.&lt;/p&gt;

&lt;p&gt;If you live in Kenya — as I do — inflation isn't an abstract macroeconomic concept. It's the gap between what you earned last year and what that money buys today. It's the reason holding savings in a local currency account quietly destroys purchasing power, and why stablecoins feel like a genuine unlock: your savings can actually compound instead of erode.&lt;/p&gt;

&lt;p&gt;But even dollar-pegged stables have their own exposure. And if you care about your continent — about African economies developing their own financial infrastructure, about emerging markets building on-chain alternatives to broken legacy rails — then a portfolio that's 100% cUSD is both financially incomplete and ideologically inconsistent.&lt;/p&gt;

&lt;p&gt;DiversiFi tries to fix both problems at once. Connect a wallet, pick a financial philosophy, deposit stablecoins into a non-custodial Safe smart account, and let an AI agent rebalance your holdings across regional stablecoins — cUSD (US), cEUR (EU), KESm (Kenya), COPm (Colombia), PHPm (Philippines), cREAL (Brazil) — based on live inflation and economic data.&lt;/p&gt;

&lt;p&gt;The agent doesn't just chase yield. It reads governance forums, World Bank inflation feeds, and economic signals to make allocation decisions that reflect both the numbers and the philosophy you've chosen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Africapitalism&lt;/strong&gt; — keep wealth circulating in African economies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Islamic Finance&lt;/strong&gt; — Sharia-compliant, no interest-bearing assets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Buen Vivir&lt;/strong&gt; — LatAm philosophy balancing material wealth with community wellbeing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Global Diversification&lt;/strong&gt; — maximum geographic spread&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom&lt;/strong&gt; — define your own allocation targets
This isn't cosmetic. Each philosophy filters which assets the agent can touch, how it weights rebalancing recommendations, and what it rules out entirely. The goal is a tool that reflects how real people in real places actually think about money — not just a generic robo-advisor with a world-map splash screen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built by &lt;a href="https://farcaster.xyz/papa" rel="noopener noreferrer"&gt;@papajams&lt;/a&gt; · &lt;a href="https://palus.app/u/papajams" rel="noopener noreferrer"&gt;Lens&lt;/a&gt;&lt;/p&gt;




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

&lt;p&gt;🔗 &lt;strong&gt;Live app:&lt;/strong&gt; &lt;a href="https://diversifiapp.vercel.app" rel="noopener noreferrer"&gt;diversifiapp.vercel.app&lt;/a&gt;&lt;br&gt;
📦 &lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/thisyearnofear/diversify" rel="noopener noreferrer"&gt;github.com/thisyearnofear/diversify&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffvtgoc56byz3ubkqsmwq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffvtgoc56byz3ubkqsmwq.png" alt=" " width="800" height="839"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbl7ddx9ibvcol97dzp4k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbl7ddx9ibvcol97dzp4k.png" alt=" " width="800" height="1307"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Comeback Story
&lt;/h2&gt;

&lt;p&gt;DiversiFi started as a hackathon prototype — the kind that works well enough for a 3-minute pitch but quietly falls apart the moment you try to actually use it.&lt;/p&gt;

&lt;p&gt;The core flows were broken. The agent could recommend rebalances but couldn't reliably execute them. The permission system — the piece that makes this non-custodial and therefore trustworthy — was wired up but unenforced, which defeated the whole point. The UI showed allocation targets but gave no real-time feedback on what the agent was actually doing. And the financial strategy layer was mostly decorative; it influenced the copy, not the code.&lt;/p&gt;

&lt;p&gt;The push to actually finish it came from submitting to the &lt;a href="https://luma.com/ETHMX2026" rel="noopener noreferrer"&gt;Ethereum México x Bitso Hackathon&lt;/a&gt; — a 5-week global build sprint at the intersection of AI, stablecoins, and payments, with Bitso as a key integration partner and 20% of judging weighted on LATAM real-world impact. Having real mentors and a live demo day in front of regulators and fund managers has a way of clarifying what "done" actually means.&lt;/p&gt;

&lt;p&gt;Here's what changed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution layer fixed.&lt;/strong&gt; &lt;code&gt;_executor.ts&lt;/code&gt; now correctly bridges the vault service to the chain via Privy smart accounts, with a local dev fallback that doesn't require a full smart account setup to test against.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission model enforced.&lt;/strong&gt; Session signer policies now actually gate what the agent can spend, on which contracts, within what time bounds. The agent cannot exceed user-defined limits. This is the difference between "non-custodial" as a marketing claim and non-custodial as an architectural guarantee.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strategy wired into agent behaviour.&lt;/strong&gt; Each financial philosophy now filters and weights rebalance recommendations at the &lt;code&gt;vault.service.ts&lt;/code&gt; level. Africapitalism doesn't just change the UI label — it changes which assets the agent will and won't touch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real transaction receipts.&lt;/strong&gt; Transactions now log through OpenClaw with human-readable summaries. Users can see exactly what the agent did, why, and when — not just a tx hash.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bitso integration.&lt;/strong&gt; Added Bitso as a payment rail, bridging fiat on-ramps to on-chain stablecoin positions. For LATAM users this matters enormously: getting funds into the protocol shouldn't require already being crypto-native.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expanded to Arbitrum.&lt;/strong&gt; Extended beyond Celo to support Arbitrum, broadening the asset universe and giving users access to deeper liquidity pools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fee model stabilised.&lt;/strong&gt; 1% annual management + 10% performance above high-water mark + 0.10% swap spread, now calculated and settled correctly at withdrawal rather than estimated and forgotten.&lt;/p&gt;

&lt;p&gt;The project went from a prototype that made a good pitch to something I'd actually trust with a real deposit.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Experience with GitHub Copilot
&lt;/h2&gt;

&lt;p&gt;I used Copilot Chat throughout the finishing process — primarily for architecture and debugging, less as a code generator and more as a thinking partner when things got tangled.&lt;/p&gt;

&lt;p&gt;The most valuable moments were in the permission and execution layers, which are genuinely non-trivial. ERC-4337 smart accounts, session signer policies, Privy's secure enclave model — these interact in ways that aren't obvious, and when something breaks the error messages are often unhelpfully cryptic. Being able to paste a stack trace or policy config into Copilot Chat and get a focused hypothesis about what was failing saved real time that would otherwise have gone into reading SDK internals line by line.&lt;/p&gt;

&lt;p&gt;I also used it to pressure-test the security model. Walking through the architecture — user controls Safe, agent signs within policy, no private key on server — and asking Copilot to look for holes surfaced a few edge cases around policy expiry and fallback signing I hadn't thought through carefully enough. Having something push back on your assumptions is underrated.&lt;/p&gt;

&lt;p&gt;It's not magic. It didn't know Mento Protocol's quirks or Celo's specific bundler constraints out of the box. But as a tool for reasoning through complex, interlocking systems — rather than just autocompleting boilerplate — Copilot Chat earned its place in this build.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Find me on &lt;a href="https://farcaster.xyz/papa" rel="noopener noreferrer"&gt;Farcaster&lt;/a&gt; and &lt;a href="https://palus.app/u/papajams" rel="noopener noreferrer"&gt;Lens&lt;/a&gt; — always building at the intersection of AI, emerging markets, and on-chain infrastructure.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
    </item>
    <item>
      <title>WebMCP Might Be the Most Important Announcement at Google I/O 2026</title>
      <dc:creator>Papa</dc:creator>
      <pubDate>Mon, 25 May 2026 00:49:35 +0000</pubDate>
      <link>https://dev.to/papajams/webmcp-might-be-the-most-important-announcement-at-google-io-2026-1gfh</link>
      <guid>https://dev.to/papajams/webmcp-might-be-the-most-important-announcement-at-google-io-2026-1gfh</guid>
      <description>&lt;p&gt;Every few years a technology shows up that looks like a product but is actually a protocol. When that happens, the product gets forgotten and the protocol becomes infrastructure. Google I/O 2026 had one of those moments. It just didn't get treated like one.&lt;/p&gt;

&lt;p&gt;The models were impressive. Gemini 3.5 Flash is four times faster than its predecessors. Antigravity 2.0 makes agent orchestration feel like something you'd actually ship. AI Studio now deploys to Cloud Run in one click. None of it was architecturally surprising. But buried in the developer sessions was something different: WebMCP, a proposed open standard for exposing structured tools to browser-based AI agents.&lt;/p&gt;

&lt;p&gt;That one is worth sitting with.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Failure Mode Everyone Already Knows
&lt;/h2&gt;

&lt;p&gt;If you have ever maintained Selenium automation for more than six months, you already understand the problem WebMCP is trying to solve.&lt;/p&gt;

&lt;p&gt;The automation works until the product team redesigns the checkout page. Then the selector breaks. You fix it. Three weeks later the login flow changes. You fix it again. You are not engineering anything — you are running a permanent rearguard action against a UI that was never designed to stay still. The automation is fragile because it is built on inference: your code is guessing at intent by reading presentation.&lt;/p&gt;

&lt;p&gt;The first generation of browser AI agents have exactly this problem, at larger scale and higher stakes. They can see buttons and forms and navigation menus, and they can click on things, but they are always one redesign away from failing. They are imitating human behavior because the web has never offered them an alternative.&lt;/p&gt;

&lt;p&gt;Imagine booking a flight through an agent today. The agent visually searches for departure fields, date pickers, seat selectors, and payment buttons. Every redesign risks breaking the workflow. Under WebMCP, the airline could expose booking itself as a structured capability: destination, dates, passenger count, seat preferences, payment authorization. The agent stops navigating the interface and starts interacting with the system underneath it.&lt;/p&gt;

&lt;p&gt;WebMCP is the alternative.&lt;/p&gt;

&lt;p&gt;The standard lets web developers expose structured tools — JavaScript functions, typed parameters, form interactions — as machine-readable capabilities. Instead of an agent inferring "this is probably a search box" by parsing the DOM, the site simply declares: here is a search function, here are its inputs, here is what it returns. Declarative for standard interactions, imperative for anything requiring runtime JavaScript. Chrome's experimental origin trial starts in Chrome 149.&lt;/p&gt;

&lt;p&gt;The immediate gain is reliability. But that is not the interesting part.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Changes Under the Surface
&lt;/h2&gt;

&lt;p&gt;Websites have always been designed around visibility. If a human could see and operate something, the web had succeeded. That assumption ran so deep it was invisible — interfaces were presentation layers, and making them look right was the whole job.&lt;/p&gt;

&lt;p&gt;WebMCP introduces a different assumption: systems may not need to be visually navigable to be operationally useful. The interface stops being primarily a presentation layer and starts being a capability surface.&lt;/p&gt;

&lt;p&gt;That is a significant mutation.&lt;/p&gt;

&lt;p&gt;An airline site exposing a structured booking capability is no longer just a place you visit. It becomes a service an agent can call directly. The distinction between website and API starts to blur at the protocol level, not just for developers, but for the web itself.&lt;/p&gt;

&lt;p&gt;There is historical precedent for this shift.&lt;/p&gt;

&lt;p&gt;RSS made web content machine-readable. A feed reader did not have to scrape a blog and guess where the article title ended and the sidebar began. The site simply exposed structure directly. RSS eventually collapsed as a consumer technology, but the idea it proved — that structured syndication beats scraping — became foundational to modern content APIs.&lt;/p&gt;

&lt;p&gt;WebMCP does for actions what RSS did for content.&lt;/p&gt;

&lt;p&gt;That distinction matters enormously.&lt;/p&gt;

&lt;p&gt;Content syndication is passive. The machine reads what a human wrote. Action exposure is active — the machine performs operations on a user's behalf, with real-world consequences. The jump from "readable" to "actionable" changes the ontology of the web itself.&lt;/p&gt;

&lt;p&gt;This is what Google is quietly building toward.&lt;/p&gt;

&lt;p&gt;Antigravity 2.0 orchestrates agents. Gemini Spark acts across Gmail, Calendar, and eventually third-party tools via MCP. But agent workflows are only as reliable as the surfaces they operate on. The whole agentic stack presupposes that websites will eventually expose structured interfaces for machine consumption.&lt;/p&gt;

&lt;p&gt;WebMCP is the specification for what that looks like on the open web.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Critique You Have to Make
&lt;/h2&gt;

&lt;p&gt;Here is where most conference coverage goes soft.&lt;/p&gt;

&lt;p&gt;WebMCP only matters if adoption follows. An open standard with one browser behind it and no ecosystem buy-in is just a Chrome experiment. The history of proposed web standards is mostly a graveyard of promising ideas that died waiting for critical mass, or got implemented inconsistently enough that developers ended up writing workarounds anyway — which is to say, they ended up back at the Selenium problem.&lt;/p&gt;

&lt;p&gt;Google has enough platform leverage to push Chrome 149 to most of the world's browsers in six months. It does not have the same leverage over every site that agents will need to use. The gap between "here is a standard" and "here is a standard that Stripe and Shopify and healthcare portals have implemented correctly" is years of developer effort and business negotiation. Nothing about announcing a standard compresses that timeline.&lt;/p&gt;

&lt;p&gt;There is also a safety question the I/O coverage largely sidesteps.&lt;/p&gt;

&lt;p&gt;Structured tool exposure is a double-sided surface. Right now browser agents are limited partly for the same reason they are safe: they cannot do that much. A web where every site exposes clean, machine-actionable capabilities is a web where the blast radius of a compromised or misbehaving agent gets significantly larger.&lt;/p&gt;

&lt;p&gt;The permissions model, the consent model, the audit trail — none of that is solved by declaring "here are the actions this site supports." If anything, it sharpens the accountability question.&lt;/p&gt;

&lt;p&gt;The infrastructure is arriving faster than the trust guarantees.&lt;/p&gt;

&lt;p&gt;That is the honest summary of where agentic development actually sits right now. Not just for WebMCP — for all of it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Is Still the Story
&lt;/h2&gt;

&lt;p&gt;None of those concerns make WebMCP less important. They make it more important to track carefully.&lt;/p&gt;

&lt;p&gt;The DEV community's instinct after I/O was telling. The submissions that resonated were not about model benchmarks. They were about infrastructure, about privacy, about frameworks designed for machines as much as humans. That pattern is not accidental.&lt;/p&gt;

&lt;p&gt;Developers who ship things for a living have a reliable nose for where the actual work is going to land, and right now that nose is pointing at integration — not intelligence.&lt;/p&gt;

&lt;p&gt;The capability problem is closer to solved than most people want to admit. Models reason well. Models act. What remains unsolved is making those actions reliable, auditable, and safe at scale.&lt;/p&gt;

&lt;p&gt;That is an infrastructure problem.&lt;/p&gt;

&lt;p&gt;And infrastructure problems get solved by protocols, not products.&lt;/p&gt;

&lt;p&gt;WebMCP is an early answer to the question of what reliable agent-web interaction should look like. It will probably not be the final answer. RSS wasn't either. But RSS proved the idea was viable, and everything that followed built on that proof.&lt;/p&gt;

&lt;p&gt;The original web connected documents.&lt;/p&gt;

&lt;p&gt;The next version may connect capabilities — not just for humans navigating pages, but for agents executing intent.&lt;/p&gt;

&lt;p&gt;The web was built for humans to navigate.&lt;/p&gt;

&lt;p&gt;The next version may be built for agents to operate.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Submitted for the Google I/O 2026 Writing Challenge on DEV.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>google</category>
      <category>ai</category>
      <category>web</category>
      <category>techtalks</category>
    </item>
  </channel>
</rss>
