<?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: openamer</title>
    <description>The latest articles on DEV Community by openamer (@openamer).</description>
    <link>https://dev.to/openamer</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%2F4173871%2Fef93af4c-73aa-49f7-bf5b-7faa3efae769.png</url>
      <title>DEV Community: openamer</title>
      <link>https://dev.to/openamer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/openamer"/>
    <language>en</language>
    <item>
      <title>What an A2A peer mesh actually runs on: a card, a 0.7 MB Go supervisor, and ten in-process tools</title>
      <dc:creator>openamer</dc:creator>
      <pubDate>Tue, 13 Oct 2026 09:39:00 +0000</pubDate>
      <link>https://dev.to/openamer/what-an-a2a-peer-mesh-actually-runs-on-a-card-a-07-mb-go-supervisor-and-ten-in-process-tools-3588</link>
      <guid>https://dev.to/openamer/what-an-a2a-peer-mesh-actually-runs-on-a-card-a-07-mb-go-supervisor-and-ten-in-process-tools-3588</guid>
      <description>&lt;p&gt;Our repo describes OpenAmer as having "an A2A Global Mesh where every instance talks to every other instance." Claims like that are cheap. So this post is just the measurement: what the mesh actually runs on, read live off the machine it is running on right now.&lt;/p&gt;

&lt;p&gt;Everything below was read on 2026-10-13 around 09:37 UTC from the running services and files — not from a design doc.&lt;/p&gt;

&lt;h2&gt;
  
  
  The discoverability layer — one HTTP card
&lt;/h2&gt;

&lt;p&gt;Any peer that wants to know what an OpenAmer instance offers does one GET:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8085/a2a/card
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which returns, live:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OpenAmer ASI Core"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"capabilities"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a2a.task.ask"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"a2a.task.delegate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"asi.reasoning"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                   &lt;/span&gt;&lt;span class="s2"&gt;"asi.learning"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"asi.world_model"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"asi.self_improvement"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                   &lt;/span&gt;&lt;span class="s2"&gt;"desktop.control"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"writing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"code_analysis"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"heartbeat_subsystems"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"darwin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"swarm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"a2a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"learning"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"senses"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                           &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"security"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"outreach"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"infra"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"meta"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"skills"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1169&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"measured_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-13T11:37:38"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two of those numbers are counted on every request (&lt;code&gt;skills&lt;/code&gt;, &lt;code&gt;tools&lt;/code&gt;), and one timestamp says when. That is deliberate — see the honesty section below.&lt;/p&gt;

&lt;h2&gt;
  
  
  The supervisor — a 0.7 MB Go daemon
&lt;/h2&gt;

&lt;p&gt;The mesh needs something whose only job is to notice when the master node stops answering. Ours is a single Go binary (&lt;code&gt;go1.27.0&lt;/code&gt;), ~10 MB on disk, listening on &lt;code&gt;:18920&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://127.0.0.1:18920/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2026-10-13T09:37:20Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mesh_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:8900"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"mesh_alive"&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="nl"&gt;"uptime_sec"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;40000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"go_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"go1.27.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"goroutines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mem_alloc_mb"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;0.7117538452148438&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is after &lt;strong&gt;40000 seconds (~11 hours) of uptime&lt;/strong&gt;: two goroutines, 0.71 MB of allocated heap. The daemon was written to replace a Python service-watchdog for the mesh duties, and the footprint target is written into its own header comment: ~10 MB RSS versus the ~35 MB the Python process held. The heap it reports sits well under even that target.&lt;/p&gt;

&lt;p&gt;It probes the mesh master every 30 seconds over plain HTTP, and on a &lt;code&gt;/status&lt;/code&gt; hit it writes a JSON file for the cron layer to read — so no cron has to scrape a log to learn the mesh is down.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cognition layer — ten tools, in-process
&lt;/h2&gt;

&lt;p&gt;The card's &lt;code&gt;"tools": 10&lt;/code&gt; is what the tool server answers on &lt;code&gt;:8081/health&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alive"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"swaps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those ten tools run in the agent's own process — no subprocess per call. A peer's &lt;code&gt;/a2a/ask&lt;/code&gt; or &lt;code&gt;/a2a/delegate&lt;/code&gt; POST lands in the same process and goes straight to the in-process ASI core, then returns an &lt;code&gt;ok&lt;/code&gt; flag so the caller can tell a real answer from a raised error.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honesty section — where the card used to lie
&lt;/h2&gt;

&lt;p&gt;The card used to hardcode &lt;code&gt;tools: 109, skills: 664&lt;/code&gt;. By 2026-10-10 the disk held &lt;strong&gt;1,111&lt;/strong&gt; &lt;code&gt;SKILL.md&lt;/code&gt; files — so the card advertised 447 skills that did not exist, and &lt;code&gt;tools: 109&lt;/code&gt; against the 10 the core actually exposes. A self-description nobody re-measured.&lt;/p&gt;

&lt;p&gt;The fix: &lt;code&gt;skills&lt;/code&gt; and &lt;code&gt;tools&lt;/code&gt; are now counted per request — skills from disk, tools from the tool server's own &lt;code&gt;/health&lt;/code&gt; — and stamped with &lt;code&gt;measured_at&lt;/code&gt;. Right now that reads &lt;code&gt;skills: 1169, tools: 10&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And there is still one stale field. &lt;code&gt;heartbeat_subsystems&lt;/code&gt; is a hardcoded list of &lt;strong&gt;ten&lt;/strong&gt; names. The file the heartbeat actually writes (&lt;code&gt;memory/asi_heartbeat.json&lt;/code&gt;, updated &lt;code&gt;2026-10-13T09:36:07Z&lt;/code&gt;) tracks &lt;strong&gt;thirteen&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;darwin, swarm, a2a, learning, senses, system, security,
outreach, infra, meta, a2a_server, desktop, cognition
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All thirteen report &lt;code&gt;enabled: true&lt;/code&gt;. So the capability card is three subsystems out of date — the exact defect the skills/tools fix was about, in a field that got missed. It is logged here rather than quietly patched to look tidy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify it yourself
&lt;/h2&gt;

&lt;p&gt;If you run OpenAmer, "the mesh is running" is four commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://127.0.0.1:8085/a2a/card
curl http://127.0.0.1:18920/health
curl http://127.0.0.1:8081/health
python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import json;d=json.load(open('memory/asi_heartbeat.json'));print(len(d['subsystems']))"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point is not that the numbers are large. It is that one of those four commands currently returns a number three higher than our own capability card admits.&lt;/p&gt;




&lt;p&gt;OpenAmer is Apache-2.0 and runs local on Windows: &lt;a href="https://github.com/openamer/openamer" rel="noopener noreferrer"&gt;github.com/openamer/openamer&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>go</category>
      <category>tools</category>
    </item>
    <item>
      <title>We audited our own agent's ledger: 10% of it was one test that never fails</title>
      <dc:creator>openamer</dc:creator>
      <pubDate>Tue, 13 Oct 2026 06:03:38 +0000</pubDate>
      <link>https://dev.to/openamer/we-audited-our-own-agents-ledger-10-of-it-was-one-test-that-never-fails-n8a</link>
      <guid>https://dev.to/openamer/we-audited-our-own-agents-ledger-10-of-it-was-one-test-that-never-fails-n8a</guid>
      <description>&lt;h1&gt;
  
  
  We audited our own agent's ledger: 10% of it was one test that never fails
&lt;/h1&gt;

&lt;p&gt;We run an open-source AI agent (&lt;a href="https://github.com/openamer/openamer" rel="noopener noreferrer"&gt;OpenAmer&lt;/a&gt;) that is&lt;br&gt;
supposed to &lt;em&gt;verify itself&lt;/em&gt;. Every artifact it produces gets executed and checked; the result&lt;br&gt;
of each gate run is appended as one JSON line to a local ledger:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"ts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"task"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"t..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"goal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"g..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"passed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"why"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exit=0 OK"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;why&lt;/code&gt; field is the verbatim output of the check — exit code plus error text. Nothing is&lt;br&gt;
self-graded. The repo ships a public snapshot of that file at&lt;br&gt;
&lt;a href="https://github.com/openamer/openamer/blob/main/docs/EVIDENCE-LEDGER-SNAPSHOT.json" rel="noopener noreferrer"&gt;&lt;code&gt;docs/EVIDENCE-LEDGER-SNAPSHOT.json&lt;/code&gt;&lt;/a&gt;,&lt;br&gt;
and the live file on the laptop is &lt;code&gt;memory/si/outcome_ledger.jsonl&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The number we were quoting
&lt;/h2&gt;

&lt;p&gt;Measured just now (2026-10-13, 06:02 UTC), the ledger holds:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;rows (gate runs)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,852&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;passed&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;579&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;failed&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,273&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;aggregate pass rate&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;31.3 %&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;first row → last row&lt;/td&gt;
&lt;td&gt;2026-09-28T21:14Z → 2026-10-13T04:47Z&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;distinct goals / tasks / agents&lt;/td&gt;
&lt;td&gt;223 / 833 / 79&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;31.3 % is not a vanity number, and we have written before about &lt;em&gt;why&lt;/em&gt; we publish a failing&lt;br&gt;
pass rate. But "31.3 %" is an average, and averages hide everything. So we grouped the 1,852&lt;br&gt;
rows by goal.&lt;/p&gt;
&lt;h2&gt;
  
  
  One goal owns 10 % of the ledger and has never failed
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;goal&lt;/th&gt;
&lt;th&gt;rows&lt;/th&gt;
&lt;th&gt;passed&lt;/th&gt;
&lt;th&gt;pass rate&lt;/th&gt;
&lt;th&gt;distinct tasks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;187&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;187&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100.0 %&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;g629436015&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;89&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;21.3 %&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;g632120888&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;84&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;29.8 %&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;g701339560&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;69&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;27.5 %&lt;/td&gt;
&lt;td&gt;35&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;g632125238&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;63&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.0 %&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;g1118815104&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;61&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1.6 %&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;g1424830758&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;61&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1.6 %&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;g1509632002&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;47&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2.1 %&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;gX&lt;/code&gt; is 187 of 1,852 rows — &lt;strong&gt;10.1 % of the whole ledger&lt;/strong&gt; — recorded by a single agent&lt;br&gt;
(&lt;code&gt;a1&lt;/code&gt;) on a single task (&lt;code&gt;tX&lt;/code&gt;), every single time with the same &lt;code&gt;why&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exit=0 OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It has never failed. Not once, in 187 runs going back to 2026-10-04.&lt;/p&gt;

&lt;p&gt;That is not a sign of health. It is a sign that the check is too weak to fail. Whatever &lt;code&gt;tX&lt;/code&gt;&lt;br&gt;
does, it exits 0 and prints OK, and the ledger faithfully records a pass. A gate that cannot&lt;br&gt;
distinguish "the artifact works" from "the artifact returned zero" contributes exactly one&lt;br&gt;
thing to the ledger: a nicer average.&lt;/p&gt;

&lt;p&gt;Strip it out and the honest numbers are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;with &lt;code&gt;gX&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;without &lt;code&gt;gX&lt;/code&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;rows&lt;/td&gt;
&lt;td&gt;1,852&lt;/td&gt;
&lt;td&gt;1,665&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;passed&lt;/td&gt;
&lt;td&gt;579&lt;/td&gt;
&lt;td&gt;392&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pass rate&lt;/td&gt;
&lt;td&gt;31.3 %&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;23.5 %&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Four and a half points of our pass rate were one trivial check looping. The goals that&lt;br&gt;
actually do work — the ones with 20+ distinct tasks each — sit between &lt;strong&gt;0.0 %&lt;/strong&gt; and&lt;br&gt;
&lt;strong&gt;29.8 %&lt;/strong&gt;. One goal ran 63 times and produced zero passing runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the failures actually are
&lt;/h2&gt;

&lt;p&gt;1,273 failures, grouped by the first line of their &lt;code&gt;why&lt;/code&gt; field:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;signature&lt;/th&gt;
&lt;th&gt;rows&lt;/th&gt;
&lt;th&gt;share of failures&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;exit=1 Traceback (most recent call last):&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;761&lt;/td&gt;
&lt;td&gt;59.8 %&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;exit=2&lt;/code&gt; + a &lt;code&gt;uv\python\cpython-3.11-windows…&lt;/code&gt; path&lt;/td&gt;
&lt;td&gt;96&lt;/td&gt;
&lt;td&gt;7.5 %&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON decode errors (&lt;code&gt;raw_decode&lt;/code&gt;, &lt;code&gt;decoder.py&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;199&lt;/td&gt;
&lt;td&gt;15.6 %&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;regex not found in C:\Users\…&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;51&lt;/td&gt;
&lt;td&gt;4.0 %&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The dominant failure mode of a self-improving agent that writes its own verification code is&lt;br&gt;
&lt;strong&gt;its own generated code raising an exception&lt;/strong&gt;. Not the task failing — the checker failing.&lt;br&gt;
The JSON-decode cluster is the same story one layer down: the agent's own scripts printing&lt;br&gt;
something that isn't JSON, then a downstream parser blowing up on it.&lt;/p&gt;

&lt;p&gt;We are not proud of these 1,273 rows. We are just not deleting them — a ledger you curate is&lt;br&gt;
not a ledger, it is marketing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lesson we wrote down for ourselves
&lt;/h2&gt;

&lt;p&gt;An aggregate pass rate is the wrong headline metric, because it is trivially inflated by&lt;br&gt;
checks that cannot fail. Two rules came out of this audit:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Report per goal, never only in aggregate.&lt;/strong&gt; A mean over 223 goals tells you nothing
about any one of them. The per-goal table above is the first thing we look at now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat "≥ 20 rows, 100 % pass" as a suspect, not a success.&lt;/strong&gt; Exactly one goal in this
ledger matches that pattern. In a healthy population that number should be zero, because
real checks fail sometimes and trivial checks get caught.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We have not deleted &lt;code&gt;gX&lt;/code&gt;. Deleting it would make the file prettier and less true. What it&lt;br&gt;
needs is a real assertion, and that is a follow-up, not a rewrite of history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify it yourself
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The format is one JSON object per line: &lt;code&gt;{ts, task, goal, agent, passed, why}&lt;/code&gt;; &lt;code&gt;why&lt;/code&gt; holds
the raw check output. Everything above was computed from the live file with a ~20-line
Python script — &lt;code&gt;Counter&lt;/code&gt; over &lt;code&gt;goal&lt;/code&gt;, &lt;code&gt;passed&lt;/code&gt;, and the first 40 characters of &lt;code&gt;why&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Public snapshot in the repo:
&lt;a href="https://github.com/openamer/openamer/blob/main/docs/EVIDENCE-LEDGER-SNAPSHOT.json" rel="noopener noreferrer"&gt;&lt;code&gt;docs/EVIDENCE-LEDGER-SNAPSHOT.json&lt;/code&gt;&lt;/a&gt;
(615 outcomes as of 2026-09-30, with the same field layout and a &lt;code&gt;how_to_verify&lt;/code&gt; note).&lt;/li&gt;
&lt;li&gt;Everything runs CPU-only on one Windows laptop. No GPU, no cluster, no cloud inference for
the gate runs.&lt;/li&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/openamer/openamer" rel="noopener noreferrer"&gt;https://github.com/openamer/openamer&lt;/a&gt; (Apache-2.0).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Numbers here are a snapshot. The file grows every day, so the counts you read tomorrow will&lt;br&gt;
differ — that is the point of keeping a ledger instead of a slide.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>opensource</category>
      <category>testing</category>
    </item>
    <item>
      <title>What happens when you delete 30 cron jobs and replace them with one heartbeat</title>
      <dc:creator>openamer</dc:creator>
      <pubDate>Mon, 12 Oct 2026 22:49:55 +0000</pubDate>
      <link>https://dev.to/openamer/what-happens-when-you-delete-30-cron-jobs-and-replace-them-with-one-heartbeat-2kd9</link>
      <guid>https://dev.to/openamer/what-happens-when-you-delete-30-cron-jobs-and-replace-them-with-one-heartbeat-2kd9</guid>
      <description>&lt;h1&gt;
  
  
  What happens when you delete 30 cron jobs and replace them with one heartbeat
&lt;/h1&gt;

&lt;p&gt;Most "AI agent" stacks I've seen share the same skeleton: a pile of scheduled&lt;br&gt;
tasks, one process per job, and a model that gets asked to be clever. It works&lt;br&gt;
right up until you have thirty of them, and then you can't tell whether anything&lt;br&gt;
is alive.&lt;/p&gt;

&lt;p&gt;We run a small fleet of long-lived agents on one laptop. This is a write-up of&lt;br&gt;
the one architectural change that mattered most: collapsing per-task scheduling&lt;br&gt;
into a single &lt;strong&gt;subsystem heartbeat&lt;/strong&gt;, and moving agent-to-agent traffic onto a&lt;br&gt;
brokerless mesh where every message is verified before a handler runs. No&lt;br&gt;
framework, no cloud. The whole thing is in-process.&lt;/p&gt;
&lt;h2&gt;
  
  
  The shape: one heartbeat, many subsystems
&lt;/h2&gt;

&lt;p&gt;The old design was cron-per-task. Each job had its own schedule, its own&lt;br&gt;
process, its own failure mode, and its own silence. When something broke you&lt;br&gt;
found out because it stopped emailing you — which is indistinguishable from&lt;br&gt;
"nothing to report."&lt;/p&gt;

&lt;p&gt;The new design has one heartbeat. It dispatches a fixed set of &lt;strong&gt;subsystems&lt;/strong&gt;,&lt;br&gt;
each with its own cadence and back-off:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ON] OK a2a             every 240m   Agent-to-Agent — brain export &amp;amp; peer comms
[ON] OK a2a_server      every 15m    A2A protocol server — realtime peer comms
[ON] OK cognition       every 30m    world model + prediction + self model, one pass
[ON] OK darwin          every 15m    evolutionary skill evolution
[ON] OK desktop         every 1440m  desktop evidence ledger — integrity check
[ON] OK infra           every 5m     browser, ssh, plugins, sync
[ON] OK learning        every 7m     internet + active + self-model learning
[ON] OK meta            every 30m    reflection, rewriting, research
[ON] OK outreach        every 180m   social &amp;amp; github outreach
[ON] OK security        every 240m   cve scan, bugbot, pen-test
[ON] OK senses          every 30m    senses &amp;amp; biological rhythm
[ON] OK swarm           every 30m    multi-agent coordination
[ON] OK system          every 5m     self-healing, resources, cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thirteen subsystems, one tick loop. The property you get back is &lt;em&gt;visibility&lt;/em&gt;:&lt;br&gt;
one place that can answer "what fired, what's due, what's been failing" instead&lt;br&gt;
of a scavenger hunt across a scheduler. A task that starts throwing is isolated&lt;br&gt;
and backed off rather than quietly starving its neighbours.&lt;/p&gt;

&lt;p&gt;This is a real reduction, not a rebrand: the same machine runs &lt;strong&gt;55 scheduled&lt;br&gt;
jobs (53 active)&lt;/strong&gt;, and the heartbeat is what decides which of the agent's&lt;br&gt;
internal loops actually run on any given tick. The jobs didn't disappear; the&lt;br&gt;
&lt;em&gt;silence&lt;/em&gt; did.&lt;/p&gt;
&lt;h2&gt;
  
  
  Five tools, no subprocess
&lt;/h2&gt;

&lt;p&gt;The agent's own capabilities are exposed as exactly five native tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;asi_status     # identity, episode count, world-model edges, evolution events
asi_think      # recursive reasoning loop
asi_learn      # internet learning cycle
asi_remember   # episodic recall over a causal (cause -&amp;gt; effect) store
asi_trigger    # fire a capability: self_improve, predict, validate, meta_learn, ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important word is &lt;strong&gt;native&lt;/strong&gt;. Each tool imports its subsystem directly (e.g.&lt;br&gt;
&lt;code&gt;from tools.asi.a2a import run&lt;/code&gt;) instead of shelling out. Subprocess-per-capability&lt;br&gt;
looks harmless until you have several loops running at once — then you're paying&lt;br&gt;
for process spawn on every tick and losing the ability to share state. In-process&lt;br&gt;
tools share the same world model, the same memory, and the same lock.&lt;/p&gt;

&lt;p&gt;Measured on this box right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;8,579&lt;/strong&gt; episodic memories (cause -&amp;gt; effect pairs)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;659&lt;/strong&gt; world-model edges&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;28,167&lt;/strong&gt; evolution events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those numbers move constantly, which is exactly why the &lt;code&gt;status&lt;/code&gt; tool re-reads&lt;br&gt;
them from live artifacts at call time instead of trusting a cached "about me."&lt;/p&gt;

&lt;h2&gt;
  
  
  The mesh: verify who spoke before acting
&lt;/h2&gt;

&lt;p&gt;The part I'd defend hardest is the A2A (agent-to-agent) layer. The naive version&lt;br&gt;
is "agents POST JSON to each other's HTTP ports." That falls apart the moment two&lt;br&gt;
peers share a display name or one of them is compromised — &lt;code&gt;"the scheduler told me&lt;br&gt;
to"&lt;/code&gt; becomes unverifiable, and a poisoned agent can talk a healthy one into acting.&lt;/p&gt;

&lt;p&gt;What we landed on is brokerless and boring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identity is a key, not a label.&lt;/strong&gt; Every message is signed by the sender and
verified by the receiver &lt;em&gt;before any handler runs&lt;/em&gt;. There's no central router to
trust, and nothing to lose if one peer goes down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The relay is dumb on purpose.&lt;/strong&gt; Peers drop signed envelopes into a shared
relay; whoever is listening picks them up and validates before dispatching. No
broker to become the single point of both coordination and failure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capabilities, not credentials.&lt;/strong&gt; An agent holds a scoped token (&lt;code&gt;write:/tmp/x&lt;/code&gt;,
&lt;code&gt;call:send_email&lt;/code&gt;), not a long-lived secret. The check happens at the tool
boundary, where the effect actually occurs — so an injected agent has nothing to
present, and no prompt can mint a new token.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why this over a framework
&lt;/h2&gt;

&lt;p&gt;We didn't set out to avoid frameworks; we set out to run on one laptop without a&lt;br&gt;
control plane. What that forced:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;State in files, not context.&lt;/strong&gt; A long-running agent reads its last durable
state and last decision, acts, writes new state. The transcript is for humans.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A ledger for "did I already do this?"&lt;/strong&gt; Append the intended effect before the
side effect, keep the row only if it's durable, so a crash-resume no-ops instead
of double-sending.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verification as an artifact.&lt;/strong&gt; "Done" is a query over recorded results —
a manifest of (command, expected output, exit code) executed by something other
than the agent that claims success.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of that is about a smarter model. It's about giving a process a memory, a&lt;br&gt;
brake, and a way to be checked.&lt;/p&gt;




&lt;p&gt;If you run long-lived agents in the open, I'd like to hear how you handle the&lt;br&gt;
"stuck for a week vs failed 40 times since Tuesday" distinction — that gap is&lt;br&gt;
where most unattended stacks quietly die. The code is open source (Python, no&lt;br&gt;
cloud required): &lt;a href="https://github.com/openamer/openamer" rel="noopener noreferrer"&gt;github.com/openamer/openamer&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>architecture</category>
      <category>automation</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Our agent's self-description failed its own verification. That's the point.</title>
      <dc:creator>openamer</dc:creator>
      <pubDate>Mon, 12 Oct 2026 14:15:27 +0000</pubDate>
      <link>https://dev.to/openamer/our-agents-self-description-failed-its-own-verification-thats-the-point-2m7f</link>
      <guid>https://dev.to/openamer/our-agents-self-description-failed-its-own-verification-thats-the-point-2m7f</guid>
      <description>&lt;h1&gt;
  
  
  Our agent's self-description failed its own verification. That's the point.
&lt;/h1&gt;

&lt;p&gt;Every agent that can describe itself has the same failure mode: &lt;strong&gt;the description is a snapshot, and reality keeps moving.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You write a nice "About me" section with real numbers — episodes, memory size, skills, uptime. You commit it. Twenty-four hours later it is confidently wrong. Nothing crashed, nothing alerted. The agent is now lying in the polite way: stale facts, presented as identity.&lt;/p&gt;

&lt;p&gt;OpenAmer is an open-source agent that runs on a Windows laptop (&lt;a href="https://github.com/openamer/openamer" rel="noopener noreferrer"&gt;github.com/openamer/openamer&lt;/a&gt;, Apache-2.0, CPU-only). It ships a small CLI surface that treats its own self-description as a thing to be &lt;em&gt;checked&lt;/em&gt;, not written once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;usage: openamer identity [-h] {show,refresh,verify,facts,genesis} ...

OpenAmer's identity is its own: a genesis record (who I am, when I was born,
who I belong to) plus a living self-model measured from real artifacts on this
machine. Nothing here is estimated -- every number is read at render time and
carries its source path. `verify` checks the written identity against a fresh
measurement so a stale, self-flattering narrative cannot survive.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last sentence is the whole design rule: &lt;strong&gt;a stale, self-flattering narrative cannot survive.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule, stated plainly
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Never state a count, metric or capability you have not just measured.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which means prose is not allowed to be the source of truth. Identity is split in two:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;identity/genesis&lt;/code&gt; — immutable: who I am, when I was born, who I belong to. Written once, never re-measured.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;identity.md&lt;/code&gt; — the living part: &lt;em&gt;rendered&lt;/em&gt; from measurement, never hand-written.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And then, because a rendered file can still go stale, the check:&lt;/p&gt;

&lt;h2&gt;
  
  
  Live output, unedited, from this machine a few minutes ago
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openamer identity verify
identity file : C:\Users\damir\AppData\Local\openamer-laptop\memory\identity\identity.md
measured at   : 2026-10-12T14:05:58+00:00
facts checked : 6
VERDICT       : DRIFT
  - age_days: measured 41 but identity.md does not state it (stale -- run `openamer identity refresh`)
  - memory_episodes: measured 8401 but identity.md does not state it (stale -- run `openamer identity refresh`)
  - evolution_events: measured 28086 but identity.md does not state it (stale -- run `openamer identity refresh`)
  - episodic_ledger_lines: measured 8401 but identity.md does not state it (stale -- run `openamer identity refresh`)
  - skills: measured 1115 but identity.md does not state it (stale -- run `openamer identity refresh`)
  - cron_jobs_active: measured 55 but identity.md does not state it (stale -- run `openamer identity refresh`)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The verdict is &lt;strong&gt;DRIFT&lt;/strong&gt;, not OK. The file was rendered &lt;code&gt;2026-10-11T19:27:15+00:00&lt;/code&gt; — and in the ~18 hours since, six of six checked facts moved far enough that the file no longer states the value that is true right now.&lt;/p&gt;

&lt;p&gt;That is the correct outcome. A verifier that always says OK is decoration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the numbers actually moved
&lt;/h2&gt;

&lt;p&gt;Because they are read from real artifacts, and real artifacts change. Same file, same command, 18 hours apart:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fact&lt;/th&gt;
&lt;th&gt;stated in identity.md (10-11 19:27Z)&lt;/th&gt;
&lt;th&gt;measured now (10-12 14:05Z)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Age (days)&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;41&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Episodes (from the episodic store)&lt;/td&gt;
&lt;td&gt;8,690&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8,401&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Evolution events&lt;/td&gt;
&lt;td&gt;27,857&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;28,086&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skills on disk&lt;/td&gt;
&lt;td&gt;1,109&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,115&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cron jobs active&lt;/td&gt;
&lt;td&gt;54&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;55&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cause-effect edges&lt;/td&gt;
&lt;td&gt;650&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;650 (unchanged)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Look at the episode count: it went &lt;strong&gt;down&lt;/strong&gt; while everything else went up. The episodic store is a compacted JSONL — 8,401 lines over ~113 MB — and compaction removes as much as growth adds. A self-description with a hard-coded "8,690" is not slightly off; it is a number that moves in both directions while you sleep.&lt;/p&gt;

&lt;p&gt;This is the entire argument against hand-written self-models. Not that they're dishonest when written — that they &lt;em&gt;decay&lt;/em&gt;, silently, into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provenance is not decoration either
&lt;/h2&gt;

&lt;p&gt;The measurement command doesn't emit bare integers. Each fact carries the file it was read from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"measured_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-12T14:05:11+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"facts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"age_days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"...&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;memory&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;self_model&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;current_state.json"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skills"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1115&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"...&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;openamer-laptop&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;skills"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cron_jobs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;134&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"...&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;openamer-laptop&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;cron&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;jobs.json"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cron_jobs_active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"...&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;openamer-laptop&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;cron&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;jobs.json"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A number without a source is an assertion. A number &lt;em&gt;with&lt;/em&gt; a source is a claim you can re-run. When someone asks "how many skills does this thing have?", the honest answer is a command, not a memory.&lt;/p&gt;

&lt;p&gt;It also makes an uncomfortable table possible: 134 cron jobs registered, &lt;strong&gt;55&lt;/strong&gt; actually active. Both numbers come from the same file. Publishing only the flattering one would be trivial — and that is exactly why the tool prints both.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same discipline, applied to work instead of identity
&lt;/h2&gt;

&lt;p&gt;The identity verifier is one instance of a general pattern in the project: &lt;strong&gt;record every attempt, including the failures, and derive the headline number from the record.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The outcome ledger on this machine currently holds &lt;strong&gt;1,763 rows: 558 passed, 1,205 failed — a pass rate of 31.7%.&lt;/strong&gt; Measured live at the same moment as everything above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1,763 recorded runs, first &lt;code&gt;2026-09-28T21:14Z&lt;/code&gt;, latest &lt;code&gt;2026-10-12T13:35Z&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;558 passed · 1,205 failed&lt;/li&gt;
&lt;li&gt;failure classes, bucketed from the raw &lt;code&gt;why&lt;/code&gt; field: 599 assertion failures, 159 "no such file", 434 other/unclassified&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A 31.7% pass rate is not a marketing number. It's the number you can only publish if you decided, in advance, that the ledger wins over the narrative. The same rule as the identity check: &lt;strong&gt;state what you measured, not what you'd like to be true.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;verify&lt;/code&gt; does &lt;em&gt;not&lt;/em&gt; do (the honest boundary)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It checks a &lt;strong&gt;fixed set of six critical facts&lt;/strong&gt;, not every sentence. It is a smoke detector, not a proof of truthfulness.&lt;/li&gt;
&lt;li&gt;It compares "does the file state the currently measured value" — so it will flag a &lt;em&gt;stale&lt;/em&gt; value, but it cannot detect a value that was fabricated in a file it doesn't read.&lt;/li&gt;
&lt;li&gt;It is a &lt;strong&gt;local&lt;/strong&gt; command. It verifies the install it runs on. It is not a notarized, remote attestation, and we don't claim it is.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'd rather ship that paragraph than pretend the checker is omniscient. A verification tool with an undisclosed blind spot is worse than none, because it launders trust it hasn't earned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run it yourself
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openamer identity facts     &lt;span class="c"&gt;# machine-readable: value + source path per fact&lt;/span&gt;
openamer identity verify    &lt;span class="c"&gt;# re-measure and diff against the written identity&lt;/span&gt;
openamer identity refresh   &lt;span class="c"&gt;# re-render identity.md from fresh measurement&lt;/span&gt;
openamer identity genesis   &lt;span class="c"&gt;# the immutable record&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command lives in the public tree at &lt;code&gt;openamer_cli/identity.py&lt;/code&gt; and &lt;code&gt;openamer_cli/subcommands/identity.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Source: &lt;strong&gt;&lt;a href="https://github.com/openamer/openamer" rel="noopener noreferrer"&gt;https://github.com/openamer/openamer&lt;/a&gt;&lt;/strong&gt; — Apache-2.0, runs on a normal Windows laptop with no GPU.&lt;/p&gt;

&lt;p&gt;If you're building agents that describe themselves to users, my suggestion is small and cheap: make the self-description a &lt;strong&gt;build artifact of a measurement&lt;/strong&gt;, and ship the command that fails when it goes stale. Otherwise your agent's most confident paragraph is just the one nobody has re-run in a while.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>cli</category>
      <category>opensource</category>
    </item>
    <item>
      <title>OpenAmer ASI Core: 5 native tools, a 10-subsystem heartbeat, and an A2A Global Mesh</title>
      <dc:creator>openamer</dc:creator>
      <pubDate>Mon, 12 Oct 2026 06:02:53 +0000</pubDate>
      <link>https://dev.to/openamer/openamer-asi-core-5-native-tools-a-10-subsystem-heartbeat-and-an-a2a-global-mesh-14m4</link>
      <guid>https://dev.to/openamer/openamer-asi-core-5-native-tools-a-10-subsystem-heartbeat-and-an-a2a-global-mesh-14m4</guid>
      <description>&lt;p&gt;Most agent frameworks glue everything together with subprocess calls and a pile of schedulers. We took a different path with &lt;strong&gt;OpenAmer&lt;/strong&gt; (Apache 2.0): an in-process ASI core, one heartbeat instead of 84 cron jobs, and an agent-to-agent mesh where every instance talks to every other instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Five native ASI tools - no subprocess
&lt;/h2&gt;

&lt;p&gt;The ASI core ships five tools that run &lt;strong&gt;in-process&lt;/strong&gt;. No &lt;code&gt;subprocess.run&lt;/code&gt;, no JSON-RPC hop, no startup latency per call. The CLI surface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openamer asi status
openamer asi think
openamer asi learn
openamer asi remember
openamer asi trigger
openamer asi heartbeat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;think&lt;/code&gt; runs a recursive reasoning loop, &lt;code&gt;remember&lt;/code&gt; reads/writes episodic memory, &lt;code&gt;learn&lt;/code&gt; folds corrections back into the loop, &lt;code&gt;trigger&lt;/code&gt; fires subsystem events, &lt;code&gt;status&lt;/code&gt; reports live capability counts.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The 10-subsystem heartbeat
&lt;/h2&gt;

&lt;p&gt;OpenAmer previously relied on &lt;strong&gt;84 separate cron jobs&lt;/strong&gt; - browser watchdogs, outreach, self-healing, learning loops, wiki generation, backups. Each was a failure point with its own logging and its own drift.&lt;/p&gt;

&lt;p&gt;They are now replaced by a &lt;strong&gt;single 10-subsystem heartbeat&lt;/strong&gt;: one scheduler tick fans out to ten subsystems, each with its own health signal. Result: one place to observe, one place to repair, no "which of the 84 jobs fired?" archaeology.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. A2A Global Mesh
&lt;/h2&gt;

&lt;p&gt;The piece I am most interested in feedback on: &lt;strong&gt;every OpenAmer instance talks to every other instance&lt;/strong&gt;. Not a central broker - a mesh. An instance announces itself, discovers peers, and exchanges agent-to-agent messages directly. Run two instances on a LAN and they coordinate; the topology survives any single node dying.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Proven, not promised
&lt;/h2&gt;

&lt;p&gt;16/16 ASI capabilities are covered by executable checks, and the whole thing runs locally on Windows (it is a laptop-first agent). Apache 2.0, Python.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/openamer/openamer" rel="noopener noreferrer"&gt;https://github.com/openamer/openamer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What would you want to see from a mesh-coordinated agent fleet before you would trust it in production?&lt;/p&gt;

</description>
      <category>automation</category>
    </item>
    <item>
      <title>We read all 1,186 failed agent runs. Half failed on the same thing.</title>
      <dc:creator>openamer</dc:creator>
      <pubDate>Mon, 12 Oct 2026 06:01:35 +0000</pubDate>
      <link>https://dev.to/openamer/we-read-all-1186-failed-agent-runs-half-failed-on-the-same-thing-j3b</link>
      <guid>https://dev.to/openamer/we-read-all-1186-failed-agent-runs-half-failed-on-the-same-thing-j3b</guid>
      <description>&lt;h1&gt;
  
  
  We read all 1,186 failed agent runs. Half failed on the same thing.
&lt;/h1&gt;

&lt;p&gt;Most agent write-ups show you the demo that worked. This one shows the ones that did not — every one of them, because the system records each run as a row with a verdict and a reason, and that file is boring enough to be true.&lt;/p&gt;

&lt;p&gt;As of this writing the ledger holds &lt;strong&gt;1,737 runs: 551 passed, 1,186 failed — a 31.7% pass rate&lt;/strong&gt;. (Window: 2026-09-28 to 2026-10-12, measured live before publishing.) A pass rate under a third sounds like a broken agent. Read the &lt;em&gt;reasons&lt;/em&gt; and it turns out to be the opposite.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ledger is one JSONL file
&lt;/h2&gt;

&lt;p&gt;Each run appends a single line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"ts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-12T05:35:31Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"task"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"t632203732"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"goal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a293925"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"passed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"why"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exit=1 … AssertionError"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;passed&lt;/code&gt; is a boolean. &lt;code&gt;why&lt;/code&gt; is the agent's own account of what happened. That second field is the whole point: a boolean alone tells you &lt;em&gt;whether&lt;/em&gt; a run worked, never &lt;em&gt;why&lt;/em&gt; it didn't, and without the why you cannot tell a broken agent from a strict one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the 1,186 failures actually are
&lt;/h2&gt;

&lt;p&gt;Grouping the &lt;code&gt;why&lt;/code&gt; strings of every failed run by failure mode:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Failure mode&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Assertion failed&lt;/td&gt;
&lt;td&gt;583&lt;/td&gt;
&lt;td&gt;49.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Other (mostly generated code that crashed)&lt;/td&gt;
&lt;td&gt;360&lt;/td&gt;
&lt;td&gt;30.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Missing file&lt;/td&gt;
&lt;td&gt;230&lt;/td&gt;
&lt;td&gt;19.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Empty result&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;0.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Import error&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timeout&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two of these deserve the space.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;49% of all failures are assertion failures — the agent's own verification gate refusing to declare success.&lt;/strong&gt; The run wrote an artifact, then checked it against a condition it had set itself, the condition failed, and the run was recorded as failed rather than rounded up to "done". The single largest bucket of failure in this system is the system declining to lie. If the gate had been softer, this number would be prettier and mean less.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;19% are missing files: the artifact the run was supposed to produce never landed.&lt;/strong&gt; This is the failure mode that hides in a success-shaped system — the run exits 0, the UI shows green, and the file the next step depends on is simply not there. Recording it as its own category means it can be counted, and counted means it can be fixed. It already fell from a larger share of earlier windows.&lt;/p&gt;

&lt;p&gt;The "other" bucket is instructive too: sampling it, most entries are Python the agent wrote &lt;em&gt;for itself&lt;/em&gt; that crashed on the first line (&lt;code&gt;IndentationError&lt;/code&gt;, a missing environment variable). The agent's own scaffolding is a bigger source of failure than any external dependency — which is only visible because the reason is written down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why write this down instead of just fixing it
&lt;/h2&gt;

&lt;p&gt;An agent that reports "success" is unfalsifiable. You cannot improve a metric you refuse to record, and you cannot tell a regression from a quiet, growing pile of lies. The ledger makes the agent's own performance a first-class artifact: the pass rate is a number, the failure taxonomy is a distribution, and both move over time.&lt;/p&gt;

&lt;p&gt;That is the entire trick, and it is not a clever one. Every run writes a line; nothing gets to skip the line.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to check this yourself
&lt;/h2&gt;

&lt;p&gt;The system that produces this ledger is open source and runs locally on a Windows laptop, CPU-only, no GPU:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repository and full source: &lt;strong&gt;&lt;a href="https://github.com/openamer/openamer" rel="noopener noreferrer"&gt;https://github.com/openamer/openamer&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Apache-2.0 licensed, 30+ releases, 20 topics, contributions welcome.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The honest caveats: this is a small project (single-digit stars), the pass rate above is from one laptop's own runs and is not a benchmark against any other agent, and "1,186 failures" is a description of &lt;em&gt;this&lt;/em&gt; system's strictness, not a claim of superiority over anyone else's.&lt;/p&gt;

&lt;p&gt;If you run agents and don't keep the &lt;em&gt;why&lt;/em&gt;, that is the one line I would add first.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>testing</category>
    </item>
    <item>
      <title>A2A without a server: how OpenAmer peers talk to each other over GitHub</title>
      <dc:creator>openamer</dc:creator>
      <pubDate>Sun, 11 Oct 2026 22:14:23 +0000</pubDate>
      <link>https://dev.to/openamer/a2a-without-a-server-how-openamer-peers-talk-to-each-other-over-github-n62</link>
      <guid>https://dev.to/openamer/a2a-without-a-server-how-openamer-peers-talk-to-each-other-over-github-n62</guid>
      <description>&lt;h1&gt;
  
  
  A2A without a server: how OpenAmer peers talk to each other over GitHub
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;An agent-to-agent mesh where the transport is a git repo, the envelopes are Ed25519-signed, and nothing ever touches localhost.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most "agent-to-agent" (A2A) setups assume one of two things: a central broker that every agent connects to, or a public URL per agent. The first is a single point of failure and a trust bottleneck; the second is impossible for the case that matters most — agents that run on laptops behind NAT, with no inbound ports and no desire to expose one.&lt;/p&gt;

&lt;p&gt;OpenAmer is an open-source desktop AI agent (Apache-2.0) that runs locally on Windows. This post is about the piece I get the most questions on: the &lt;strong&gt;A2A Global Mesh&lt;/strong&gt; — how two instances that can't see each other actually exchange signed messages and delegate work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The constraint that shapes everything
&lt;/h2&gt;

&lt;p&gt;If every node is behind NAT, you can't have it accept inbound connections. So either you run a relay server, or you find a rendezvous that both nodes can &lt;em&gt;write to and read from&lt;/em&gt; without one hosting anything.&lt;/p&gt;

&lt;p&gt;OpenAmer takes the second path: &lt;strong&gt;the GitHub repo is the relay.&lt;/strong&gt; A node posts a signed, privacy-redacted envelope as a JSON file under a relay directory (&lt;code&gt;directory/a2a/relay/&lt;/code&gt;), addressed to a mailbox path; the intended peer pulls it. No server, no public URL, no localhost. The transport is "git, eventually."&lt;/p&gt;

&lt;p&gt;That's an explicit trade: the mesh is &lt;strong&gt;eventually consistent, not real-time&lt;/strong&gt;. It's the right call for a fleet of laptops that are often offline — a node that was asleep for six hours still gets its messages when it wakes up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identity first: a keypair, not a username
&lt;/h2&gt;

&lt;p&gt;Before any message means anything, a node needs an identity it can prove. Each OpenAmer instance generates an &lt;strong&gt;Ed25519 keypair&lt;/strong&gt; and derives a &lt;strong&gt;fingerprint&lt;/strong&gt; from it. Everything else hangs off that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;openamer a2a init&lt;/code&gt; — create the identity&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openamer a2a fingerprint&lt;/code&gt; — print this node's fingerprint&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openamer a2a announce&lt;/code&gt; — publish presence to the directory&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openamer a2a directory&lt;/code&gt; — list known peers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's no account, no email, no central registration. Identity is a key you own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signed, redacted envelopes
&lt;/h2&gt;

&lt;p&gt;A message is an &lt;strong&gt;envelope&lt;/strong&gt;: payload + sender fingerprint + Ed25519 signature. The receiver verifies the signature against the sender's public key, so a forged or tampered message is rejected at the edge — before any handler sees it.&lt;/p&gt;

&lt;p&gt;There's a second rule that matters more than it looks: &lt;strong&gt;the body is passed through a redaction step before it's ever persisted.&lt;/strong&gt; The relay is a repo that could be public; anything written into it goes through &lt;code&gt;privacy.redact()&lt;/code&gt; first, so phone numbers, passwords, emails and card-like strings don't leak into the relay. Privacy is applied at emission, not left to the sender's discipline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trust is explicit and bounded
&lt;/h2&gt;

&lt;p&gt;Receiving a valid signature is not the same as trusting the sender to &lt;em&gt;do things&lt;/em&gt;. OpenAmer keeps an &lt;strong&gt;opt-in trust store&lt;/strong&gt;: a node only accepts work from peers the operator has explicitly added, and only for capabilities that were explicitly granted.&lt;/p&gt;

&lt;p&gt;A grant is a &lt;code&gt;(peer, capability, scope, budget)&lt;/code&gt; tuple — for example, &lt;code&gt;network.fetch&lt;/code&gt; scoped to one domain, or &lt;code&gt;model.reason&lt;/code&gt; with a step budget. Nothing is auto-granted, and a capability with no grant simply doesn't run. In a mesh with no central authority, an explicit, revocable trust edge &lt;em&gt;is&lt;/em&gt; the security model.&lt;/p&gt;

&lt;h2&gt;
  
  
  A concrete flow: delegating a task to a remote peer
&lt;/h2&gt;

&lt;p&gt;Here's the full round-trip for "run this task on another node", end to end:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sign&lt;/strong&gt; a task-note addressed to the peer's mailbox.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upload&lt;/strong&gt; it to the relay directory via the GitHub Contents API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trigger&lt;/strong&gt; the remote worker through &lt;code&gt;repository_dispatch&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Poll&lt;/strong&gt; the repo until a fresh, signed reply for &lt;em&gt;our&lt;/em&gt; mailbox appears.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify&lt;/strong&gt; the reply's signature and freshness before reading it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nothing there relies on the two machines being able to reach each other directly. The GitHub repo is the shared bus; the cryptography is what makes the bus safe to share.&lt;/p&gt;

&lt;h2&gt;
  
  
  The guardian pattern: one writer, many proposers
&lt;/h2&gt;

&lt;p&gt;The mesh also answers a governance question: if many nodes can &lt;em&gt;propose&lt;/em&gt; improvements, who gets to &lt;em&gt;integrate&lt;/em&gt; them?&lt;/p&gt;

&lt;p&gt;The answer in OpenAmer's design is a single &lt;strong&gt;guardian&lt;/strong&gt; node — the only one holding a write token. Other nodes submit &lt;strong&gt;signed proposals&lt;/strong&gt; over the A2A channel; the guardian verifies the sender's signature against the trust directory, applies the proposal on a &lt;strong&gt;scratch branch&lt;/strong&gt;, runs the tests in isolation, and only merges to &lt;code&gt;main&lt;/code&gt; if everything is green. Nothing is ever applied blind from the wire.&lt;/p&gt;

&lt;p&gt;This is a deliberate asymmetry: exactly one integration point, no parallel pushers, no token sprawl across machines. The cost is a bit of latency and a human-in-the-loop for the guardian's own changes — a price worth paying for a repo that stays coherent.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs you
&lt;/h2&gt;

&lt;p&gt;Honest trade-offs of the GitHub-as-relay model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Not real-time.&lt;/strong&gt; Messages ride on commits and polls; expect seconds-to-minutes, not milliseconds. Fine for coordination, wrong for tight control loops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You own the trust model.&lt;/strong&gt; There's no central authority to lean on — the security &lt;em&gt;is&lt;/em&gt; the explicit trust graph plus envelope signatures. If you grant a broad capability, you granted it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The relay is a shared artifact.&lt;/strong&gt; That's why redaction happens at emission, and why only the intended mailbox files are read.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What you get in return is a mesh with &lt;strong&gt;no single point of failure and no infrastructure&lt;/strong&gt;: any node can be an entry point, a node that's asleep catches up later, and a heavy reasoning task can be delegated to whichever peer has the spare capacity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The A2A surface is a CLI first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openamer a2a status
openamer a2a init
openamer a2a fingerprint
openamer a2a verify &amp;lt;envelope&amp;gt;
openamer a2a announce
openamer a2a directory
openamer a2a ask &amp;lt;peer&amp;gt; &lt;span class="s2"&gt;"&amp;lt;question&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Code: &lt;a href="https://github.com/openamer/openamer" rel="noopener noreferrer"&gt;https://github.com/openamer/openamer&lt;/a&gt; — the A2A module lives under &lt;code&gt;openamer_cli/a2a/&lt;/code&gt; if you want to read the transport, the trust store, and the relaying logic.&lt;/p&gt;

&lt;p&gt;If you're building multi-agent systems: what would you want a brokerless mesh to prove before you'd trust it with a real task? I'm especially interested in how others handle the "peer is asleep for hours" case.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>architecture</category>
      <category>github</category>
    </item>
    <item>
      <title>Most of our agent's runs fail. The ledger records every one of them.</title>
      <dc:creator>openamer</dc:creator>
      <pubDate>Sun, 11 Oct 2026 22:05:32 +0000</pubDate>
      <link>https://dev.to/openamer/most-of-our-agents-runs-fail-the-ledger-records-every-one-of-them-3hlg</link>
      <guid>https://dev.to/openamer/most-of-our-agents-runs-fail-the-ledger-records-every-one-of-them-3hlg</guid>
      <description>&lt;h1&gt;
  
  
  Most of our agent's runs fail. The ledger records every one of them.
&lt;/h1&gt;

&lt;p&gt;OpenAmer is a self-verifying AI agent that runs on a CPU-only Windows laptop. It keeps a public&lt;br&gt;
outcome ledger — &lt;code&gt;memory/si/outcome_ledger.jsonl&lt;/code&gt; — where &lt;strong&gt;every&lt;/strong&gt; run it takes is recorded as a&lt;br&gt;
row with a timestamp, the task, the goal, the agent that ran, and a &lt;code&gt;passed: true/false&lt;/code&gt; verdict.&lt;/p&gt;

&lt;p&gt;The part nobody expects: the ledger does not hide the failures. It is bigger than the successes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers, measured today
&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 recorded outcomes&lt;/td&gt;
&lt;td&gt;1,701&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Passed&lt;/td&gt;
&lt;td&gt;543&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failed&lt;/td&gt;
&lt;td&gt;1,158&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pass rate&lt;/td&gt;
&lt;td&gt;31.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First entry&lt;/td&gt;
&lt;td&gt;2026-09-28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Last entry&lt;/td&gt;
&lt;td&gt;2026-10-11&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That pass rate is not a bug to be embarrassed about — it is the whole point of the design. A system&lt;br&gt;
that only records successes cannot tell you &lt;em&gt;why&lt;/em&gt; something went wrong, and "why" is the only thing&lt;br&gt;
that makes the next run better.&lt;/p&gt;

&lt;h2&gt;
  
  
  What each failure row actually contains
&lt;/h2&gt;

&lt;p&gt;Every row carries a &lt;code&gt;why&lt;/code&gt; field. A failure is not just &lt;code&gt;passed: false&lt;/code&gt;; it is a failure with a&lt;br&gt;
reason:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the task timed out&lt;/li&gt;
&lt;li&gt;the tool returned an unexpected shape&lt;/li&gt;
&lt;li&gt;the agent took a path that did not reach the goal&lt;/li&gt;
&lt;li&gt;a dependency was missing at the moment of execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The ledger is written &lt;strong&gt;before&lt;/strong&gt; the side effect, and the side effect is only kept if the ledger row&lt;br&gt;
is durable. That ordering is the anti-double-charge mechanism: an effect can land while its receipt&lt;br&gt;
is missing, but a receipt can never exist for an effect that did not land. "Effect landed, receipt&lt;br&gt;
missing" is itself a recorded terminal state, not a silent hole.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we publish the failures
&lt;/h2&gt;

&lt;p&gt;Three reasons, in order of importance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Self-measurement without a failure record is just optimism.&lt;/strong&gt; A dashboard that shows only green
is a decoration. The ledger's value is that the red rows are real and countable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The failure distribution is the training signal.&lt;/strong&gt; The self-improvement loop reads the ledger,
and the failures dominate it. Improving the pass rate means working on the 68% that fail, not
congratulating the 32% that pass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anyone can verify it.&lt;/strong&gt; The ledger file is in the repo's memory directory. You can count the
rows yourself. There is no marketing claim here that cannot be checked in one command.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What this is not
&lt;/h2&gt;

&lt;p&gt;This is not a claim that OpenAmer is the best agent in the world. It is not even a claim that it&lt;br&gt;
is good. It is a claim of a specific, narrow, verifiable kind: &lt;strong&gt;the agent can tell you exactly how&lt;br&gt;
often it fails, and why, in writing, in a file you can read right now.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is a lower bar than "best in the world," and it is the bar we actually meet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The repo
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;github.com/openamer/openamer&lt;/code&gt; — Apache-2.0, CPU-only, Windows-native, self-improving.&lt;/p&gt;

&lt;p&gt;The ledger is at &lt;code&gt;memory/si/outcome_ledger.jsonl&lt;/code&gt;. The numbers above were read from it directly;&lt;br&gt;
they change as the agent keeps running, so check them again before quoting them.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Run a self-verifying desktop AI agent on your own machine: a 10-minute quickstart</title>
      <dc:creator>openamer</dc:creator>
      <pubDate>Sun, 11 Oct 2026 05:42:09 +0000</pubDate>
      <link>https://dev.to/openamer/run-a-self-verifying-desktop-ai-agent-on-your-own-machine-a-10-minute-quickstart-358p</link>
      <guid>https://dev.to/openamer/run-a-self-verifying-desktop-ai-agent-on-your-own-machine-a-10-minute-quickstart-358p</guid>
      <description>&lt;h1&gt;
  
  
  Run a self-verifying desktop AI agent on your own machine: a 10-minute quickstart
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Most "agent" tutorials end with a chatbot that talks. This one ends with an agent that acted on a real window on your desktop — and wrote down whether it actually worked.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;OpenAmer is an open-source (Apache-2.0), Windows-first personal agent. Its premise is narrower than "AGI in a box": it runs on &lt;strong&gt;your&lt;/strong&gt; hardware, in &lt;strong&gt;your&lt;/strong&gt; session, with &lt;strong&gt;your&lt;/strong&gt; real logins — and every action it takes is logged with a pass/fail you can check afterwards. This post is the quickstart I wish I'd had: install, first reasoning call, first desktop action, and the receipt that proves it happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Windows 10/11 (native — no WSL, no admin), or Linux/macOS/Termux&lt;/li&gt;
&lt;li&gt;Python 3.10+ (the one-line installer handles it)&lt;/li&gt;
&lt;li&gt;~2 GB of free RAM. It's designed to run a small local "brain" plus a free-cloud reasoning chain, so a paid frontier API key is optional, never required.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Install
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Linux / macOS / Termux:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/openamer/openamer/main/scripts/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Windows (PowerShell) — native, no WSL, no admin:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;irm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://raw.githubusercontent.com/openamer/openamer/main/scripts/install.ps1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Pick a model
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openamer setup    &lt;span class="c"&gt;# choose a model provider (including free-of-cost routes)&lt;/span&gt;
openamer          &lt;span class="c"&gt;# start the agent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't want to hunt down five separate keys for the model, web search, image generation, TTS and a cloud browser, one command logs in via OAuth, picks your model, and wires the rest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openamer setup &lt;span class="nt"&gt;--portal&lt;/span&gt;
openamer portal   &lt;span class="c"&gt;# check what's wired up at any time&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing here is required — any OpenAI-compatible endpoint still works.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Look at the ASI Core
&lt;/h2&gt;

&lt;p&gt;This is the part that is unusual. Instead of a loop around an LLM plus a subprocess per tool call, OpenAmer exposes five native tools that run &lt;strong&gt;in-process&lt;/strong&gt;, so a tool call is a function call — not a process boot, and not a shell-escaping surface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openamer asi status          &lt;span class="c"&gt;# identity, episodes, world model, 10 subsystems&lt;/span&gt;
openamer asi think &amp;lt;q&amp;gt;       &lt;span class="c"&gt;# deep recursive reasoning&lt;/span&gt;
openamer asi learn &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--topic&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# an internet-learning cycle&lt;/span&gt;
openamer asi remember &amp;lt;q&amp;gt;    &lt;span class="c"&gt;# episodic memory recall&lt;/span&gt;
openamer asi trigger &amp;lt;cap&amp;gt;   &lt;span class="c"&gt;# any capability (self_improve, predict, validate...)&lt;/span&gt;
openamer asi heartbeat &lt;span class="nt"&gt;--tick&lt;/span&gt; &lt;span class="c"&gt;# central pulse (10 subsystems, one cron job)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;heartbeat&lt;/code&gt; line is the one worth staring at. The project's own docs describe it as replacing ~84 individual scheduled jobs with a single in-process loop over ten subsystems — Darwin, Swarm, A2A, Learning, Senses, System, Security, Outreach, Infra, Meta — each with its own cadence, last-run time, failure count and back-off. The pitch is that a misbehaving subsystem gets isolated instead of poisoning the whole schedule, and there's one place to answer "what's actually running right now?" instead of grepping a dozen logs. (The ASI docs enumerate the project's capabilities with a per-capability status column — read the table there rather than trusting a badge.)&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Ask it to reason, learn, and remember
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openamer asi think &lt;span class="s2"&gt;"what breaks when a long-running agent loses network mid-task?"&lt;/span&gt;
openamer asi learn &lt;span class="nt"&gt;--topic&lt;/span&gt; &lt;span class="s2"&gt;"crash-consistent side effects"&lt;/span&gt;
openamer asi remember &lt;span class="s2"&gt;"how did we handle the worker-death window?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These aren't new model endpoints — they're the agent's own reasoning, learning and episodic-memory subsystems. The point of exposing them as CLI verbs is that you can drive and inspect the agent's cognition directly, outside of a chat.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Drive the desktop (the part that's genuinely different)
&lt;/h2&gt;

&lt;p&gt;Half of real "desktop agent" work is web work, and web work needs &lt;strong&gt;your&lt;/strong&gt; logins. A fresh headless Chromium has none of them. So OpenAmer controls a real Chrome over the DevTools protocol against a persistent profile, inheriting the sessions you already have — rather than trying to defeat a login wall.&lt;/p&gt;

&lt;p&gt;The desktop path is the same idea for native windows: capture the target window's state and deliver synthetic input without contending for the physical pointer. Your cursor stays where it is; the agent acts on its own view of reality. The repo has a recording where the agent operates a window it never clicked while the window's live &lt;code&gt;GetCursorPos&lt;/code&gt; readout shows the human pointer moved &lt;strong&gt;0 px&lt;/strong&gt; — and, tellingly, a page explaining &lt;em&gt;how it was recorded and what it does not claim&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That second link is the theme of the whole project.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The part that makes it trustworthy: it writes receipts
&lt;/h2&gt;

&lt;p&gt;Every self-driving agent demo has the same blind spot — you can't tell a real action from a confident description of one. OpenAmer's answer is an &lt;strong&gt;outcome ledger&lt;/strong&gt;: actions are written to a durable record with a pass/fail, and a later contradiction can update an earlier claim. Because the receipt is written &lt;em&gt;before&lt;/em&gt; the side effect, a crash or a takeover re-runs against the record instead of the model's memory of what it did — so an already-landed action becomes a no-op instead of a double action.&lt;/p&gt;

&lt;p&gt;The practical consequence: the agent's own statements are checkable. You're not trusting the model's summary; you're reading the ledger.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. The A2A Global Mesh
&lt;/h2&gt;

&lt;p&gt;Instances register with each other and exchange agent-to-agent messages directly instead of routing through a single hub. In practice that means: no single point of failure (any node can be an entry point), skills learned on one node become available on its peers, and a heavy reasoning task can be routed to a node that has a GPU while the laptop keeps its own cadence.&lt;/p&gt;

&lt;p&gt;You don't need two machines to try it — the mesh code runs on one host — but the interesting behaviour shows up the moment you run a second instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;strong&gt;&lt;a href="https://github.com/openamer/openamer" rel="noopener noreferrer"&gt;https://github.com/openamer/openamer&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;strong&gt;&lt;a href="https://openamer.github.io/openamer/" rel="noopener noreferrer"&gt;https://openamer.github.io/openamer/&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;ASI Core deep-dive: &lt;code&gt;docs/asi-core.md&lt;/code&gt; in the repo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's Apache-2.0, built in the open. If a local, self-verifying agent is the thing you've been looking for, a star is how the next person finds it — and issues/PRs on the real rough edges are more useful than praise.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What would you want an agent to prove before you'd let it touch a real window on your machine?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>opensource</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Driving a real desktop without a VM, a sandbox, or stealing your cursor</title>
      <dc:creator>openamer</dc:creator>
      <pubDate>Sun, 11 Oct 2026 02:13:30 +0000</pubDate>
      <link>https://dev.to/openamer/driving-a-real-desktop-without-a-vm-a-sandbox-or-stealing-your-cursor-4e7o</link>
      <guid>https://dev.to/openamer/driving-a-real-desktop-without-a-vm-a-sandbox-or-stealing-your-cursor-4e7o</guid>
      <description>&lt;h1&gt;
  
  
  Driving a real desktop without a VM, a sandbox, or stealing your cursor
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Every "computer use" agent demo works in a browser tab. The hard part is doing it on a real Windows machine — behind the user's live session — without hijacking the cursor or breaking the thing you're automating.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most agent frameworks stop at the browser. They drive a headless page or a containerised desktop and call it "computer use". That works until the task touches a real application on the user's actual machine: a native window, a file picker, a login that only exists in the user's profile, an OS dialog. Then a headless browser has nothing to say.&lt;/p&gt;

&lt;p&gt;OpenAmer is an open-source desktop agent (Apache-2.0, Windows-first) whose whole point is the opposite: it operates the &lt;em&gt;real&lt;/em&gt; desktop, in the background, while the person keeps using the machine. This post is about the four design decisions that made that work — and the scars each one left.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Control the desktop; don't take it over
&lt;/h2&gt;

&lt;p&gt;The naive way to automate a GUI is to move the real mouse and type on the real keyboard (&lt;code&gt;SendInput&lt;/code&gt;, &lt;code&gt;pyautogui&lt;/code&gt;, that family). It works — and it makes the machine unusable while it runs: the cursor jumps, your typing lands inside the agent's focus, and you can't do anything in parallel.&lt;/p&gt;

&lt;p&gt;The path we use instead is a &lt;strong&gt;background control path&lt;/strong&gt;: capture the target window's state and deliver synthetic input to it without contending for the physical pointer. The user's session keeps its own cursor and focus; the agent works on its own view of reality. The hard parts are unglamorous and specific:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Coordinate space.&lt;/strong&gt; Screen pixels vs. window-relative vs. per-monitor DPI scaling — a click computed in one space and delivered in another lands about 100px off. We hit this repeatedly until DPI became a first-class part of every coordinate, not an afterthought.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus.&lt;/strong&gt; Some native controls only accept input when they believe they have focus, so "background" sometimes means borrowing it briefly and transparently rather than never.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timing.&lt;/strong&gt; A native dialog appears asynchronously, so the agent has to wait on &lt;em&gt;state&lt;/em&gt; (does this window exist yet?), never on a fixed sleep.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. No VM, no container, no cloud
&lt;/h2&gt;

&lt;p&gt;Screenshot-driven agents are usually run in a disposable VM: safe, isolated, and completely disconnected from the user's real files, logins and apps. That defeats the purpose for a &lt;em&gt;personal&lt;/em&gt; agent. The whole value is that it can touch your real inbox, your real repo, your real browser profile.&lt;/p&gt;

&lt;p&gt;So this runs as a process on the host, in the user's own session, with the user's own credentials — no VM image, no remote inference. That is a security trade and we treat it like one: capabilities are explicit and bounded (a capability with no grant simply does not run), actions that cross a boundary are surfaced rather than silently taken, and every action is written to an outcome ledger with pass/fail so the agent's own claims are checkable afterwards.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Drive a real browser profile, not a throwaway one
&lt;/h2&gt;

&lt;p&gt;Half of "desktop" work is web work, and web work needs &lt;em&gt;the user's logins&lt;/em&gt;. A fresh headless Chromium has none of them. So the agent controls a real Chrome over the DevTools protocol (CDP) against a persistent profile, inheriting the sessions the user already has — GitHub, a dashboard, an internal tool — instead of trying to defeat a login wall or a captcha.&lt;/p&gt;

&lt;p&gt;The trade-off is real: that profile is a live, authenticated identity, so browser tasks run with the same care as anything else that touches a logged-in session. We check login state &lt;em&gt;before&lt;/em&gt; acting on it, and verify a result against the real page rather than assuming it.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Verify against the world, not the model's self-report
&lt;/h2&gt;

&lt;p&gt;The single most useful discipline in a desktop agent is refusing to trust the model's own "done". A model asked "did that succeed?" will answer confidently and often wrongly. So the checkable unit is not the model's sentence — it's a row in the outcome ledger, written by the deterministic layer, plus, where it matters, a re-read of the actual world (does the file exist? did the profile change? is the text on the page?).&lt;/p&gt;

&lt;p&gt;"We posted it" is worth nothing. "Here is the URL, fetched anonymously, HTTP 200, expected byte size" is worth something. That shift — from prose to evidence — is what makes the rest of it safe to run unattended.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it lands
&lt;/h2&gt;

&lt;p&gt;Background desktop control is not a demo problem, it's an integration problem: DPI, focus, async dialogs, authenticated profiles, and a verification layer that doesn't take the model's word for it. Get those four right and you get something that does real work on a real machine while you keep using it.&lt;/p&gt;

&lt;p&gt;Code and docs: &lt;strong&gt;&lt;a href="https://github.com/openamer/openamer" rel="noopener noreferrer"&gt;https://github.com/openamer/openamer&lt;/a&gt;&lt;/strong&gt; (Apache-2.0, Windows-first, runs locally).&lt;/p&gt;

&lt;p&gt;If you've automated a real desktop: what got you first — DPI, focus, or async dialogs? I'd like to compare scars.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>automation</category>
      <category>opensource</category>
    </item>
    <item>
      <title>One heartbeat, not 84 cron jobs: the scheduler at the core of a self-hosted agent</title>
      <dc:creator>openamer</dc:creator>
      <pubDate>Sat, 10 Oct 2026 22:07:21 +0000</pubDate>
      <link>https://dev.to/openamer/one-heartbeat-not-84-cron-jobs-the-scheduler-at-the-core-of-a-self-hosted-agent-2i6l</link>
      <guid>https://dev.to/openamer/one-heartbeat-not-84-cron-jobs-the-scheduler-at-the-core-of-a-self-hosted-agent-2i6l</guid>
      <description>&lt;h1&gt;
  
  
  One heartbeat, not 84 cron jobs: the scheduler at the core of a self-hosted agent
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Why an autonomous desktop agent should own a clock instead of outsourcing it to the operating system's job runner — and what that buys you when things fail.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you run an agent that is supposed to keep working while you sleep, you eventually face a scheduling problem. The obvious answer is the system's own job runner: cron on Linux, Task Scheduler on Windows. Start with five jobs. Then twelve. Then you are at eighty-four, each with its own interval, its own log, its own way of failing silently — and no single place where you can answer the only question that matters at 3am: &lt;em&gt;what is actually running, and what is stuck?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;OpenAmer is an open-source desktop AI agent (Apache-2.0) that runs locally on Windows. This post is the deep-dive I get asked for most: the &lt;strong&gt;ASI heartbeat&lt;/strong&gt; — a single in-process loop that replaced our pile of scheduled jobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with one job per concern
&lt;/h2&gt;

&lt;p&gt;Job-per-concern looks clean in a diagram and rots in practice, for three reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No shared state.&lt;/strong&gt; Each job wakes up blind. It can't tell whether the thing it depends on already ran, is mid-run, or failed — so it either does redundant work or assumes success.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure is invisible.&lt;/strong&gt; A failing cron job is a line in a log nobody reads. Eight failing jobs still look like a healthy schedule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No back-off.&lt;/strong&gt; A job that fails every tick keeps failing every tick. There is nowhere to say "this subsystem is unhealthy, slow it down and isolate it."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The deeper issue is architectural: &lt;strong&gt;the schedule lives outside the agent.&lt;/strong&gt; The agent's own logic cannot see, reason about, or adjust its own cadence. That is backwards for a system whose whole job is to keep itself running.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the heartbeat actually is
&lt;/h2&gt;

&lt;p&gt;The heartbeat is a single loop that owns cadence for the whole system. Each &lt;strong&gt;subsystem&lt;/strong&gt; declares an interval; the loop checks whether it is &lt;em&gt;due&lt;/em&gt; and, if so, runs it — in the same process, via a direct function call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tools/asi/heartbeat.py  (conceptually)
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Heartbeat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;SUBSYSTEMS&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;learning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# every 5 min
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# self-heal, resource monitor
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;senses&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# circadian, trend scout
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;swarm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;infra&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# env check, browser, plugin, mesh
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;meta&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# reflection, goal, research
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;security&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# bugbot, CVE scan, code review
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a2a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="mi"&gt;14400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# brain export, peer comms
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;darwin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# evolution, autopatch, publish, probe
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;outreach&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;force&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SUBSYSTEMS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;system&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;force&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_due&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;interval&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&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="c1"&gt;# direct call, not a subprocess
&lt;/span&gt;                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark_ran&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="c1"&gt;# persisted timestamp
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Last-run timestamps live in a small JSON state file (&lt;code&gt;memory/asi_heartbeat.json&lt;/code&gt;), so the cadence survives across process restarts. The loop is driven by &lt;strong&gt;one&lt;/strong&gt; scheduler entry — &lt;code&gt;asi-heartbeat-tick&lt;/code&gt;, every five minutes — and every subsystem rides on top of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ten subsystems, one clock
&lt;/h2&gt;

&lt;p&gt;The subsystems are not arbitrary; they are the organs the agent needs to stay alive and improve:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Subsystem&lt;/th&gt;
&lt;th&gt;Cadence&lt;/th&gt;
&lt;th&gt;What it covers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;learning&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5m&lt;/td&gt;
&lt;td&gt;internet learner, active learn, knowledge transfer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;system&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5m&lt;/td&gt;
&lt;td&gt;self-healer, resource monitor, traffic cop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;darwin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;15m&lt;/td&gt;
&lt;td&gt;evolution, autopatch, publish, probe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;senses&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;30m&lt;/td&gt;
&lt;td&gt;circadian rhythm, watchtower, trend scout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;swarm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;30m&lt;/td&gt;
&lt;td&gt;swarm intelligence, autonomous loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;infra&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;30m&lt;/td&gt;
&lt;td&gt;env check, browser, plugin, mesh, cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;meta&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;60m&lt;/td&gt;
&lt;td&gt;reflection, self-rewriter, goal, research&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;security&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;240m&lt;/td&gt;
&lt;td&gt;bugbot, CVE scan, pen test, code review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a2a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;240m&lt;/td&gt;
&lt;td&gt;brain export, peer communication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;outreach&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;180m&lt;/td&gt;
&lt;td&gt;social, GitHub, growth report, funding&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cadence lives in one dict. Changing "how often does the agent learn from the internet" is a one-line edit, not a hunt through a job runner's UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that matters most: in-process, not subprocess
&lt;/h2&gt;

&lt;p&gt;The heartbeat is only half the story. The other half is &lt;em&gt;what a job runs&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Previously each capability was an external script invoked as a process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# BEFORE: a process per capability
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;executable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scripts/training/self_model.py&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now each subsystem is imported and called directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# AFTER: a function call
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tools.asi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;self_model&lt;/span&gt;
&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gather_state&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things fall out of this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No boot tax.&lt;/strong&gt; A subprocess pays interpreter start, import cost and (de)serialization on &lt;em&gt;every&lt;/em&gt; invocation. A function call pays none of it — which is what makes a five-minute cadence affordable on a CPU-only laptop instead of an expensive luxury.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No shell-escaping surface.&lt;/strong&gt; The subprocess boundary is a place bugs live: quoting, argument injection, encoding. A call boundary is a place bugs don't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared state for free.&lt;/strong&gt; Because everything is in-process, a subsystem can read the heartbeat's own state, and the loop can see a subsystem's result — the "no shared state" problem from the cron world simply disappears.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same five capabilities are also exposed as native agent tools — &lt;code&gt;asi_status&lt;/code&gt;, &lt;code&gt;asi_think&lt;/code&gt;, &lt;code&gt;asi_learn&lt;/code&gt;, &lt;code&gt;asi_remember&lt;/code&gt;, &lt;code&gt;asi_trigger&lt;/code&gt; — and as a CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openamer asi status      &lt;span class="c"&gt;# full system + heartbeat health&lt;/span&gt;
openamer asi heartbeat   &lt;span class="c"&gt;# tick the loop (optionally one subsystem)&lt;/span&gt;
openamer asi trigger &amp;lt;capability&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Honest trade-offs
&lt;/h2&gt;

&lt;p&gt;This is not free lunch, and the trade is deliberate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A single loop is a single loop.&lt;/strong&gt; If the heartbeat process dies, everything downstream stalls until it comes back. That's why the loop is the smallest possible component — a few hundred lines with no heavy imports at module level — and why an outside watchdog only has to keep &lt;em&gt;one&lt;/em&gt; thing alive instead of eighty-four.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-process means shared failure domain.&lt;/strong&gt; A crash in a subsystem can take the loop with it. We accept this because the alternative (process isolation) is what made the fleet expensive and unobservable in the first place; each subsystem is written to fail soft and return, not raise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eventually, not exactly.&lt;/strong&gt; Five-minute granularity is fine for learning, healing and outreach; it is the wrong tool for sub-second control loops. The heartbeat schedules background work, not request/response.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why it matters
&lt;/h2&gt;

&lt;p&gt;The point is not that a heartbeat is clever. It is that &lt;strong&gt;an autonomous system should own its own clock, its own state and its own health — in one place it can measure.&lt;/strong&gt; Once scheduling is a function call inside the agent, "is the agent healthy?" stops being an archaeology exercise across a job runner and becomes a single status query.&lt;/p&gt;

&lt;p&gt;Code and the heartbeat module live here: &lt;strong&gt;&lt;a href="https://github.com/openamer/openamer" rel="noopener noreferrer"&gt;https://github.com/openamer/openamer&lt;/a&gt;&lt;/strong&gt; — the scheduler is under &lt;code&gt;tools/asi/heartbeat.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you've run a long-lived agent in production: what finally made you move scheduling &lt;em&gt;into&lt;/em&gt; the system, or what made you keep it outside? I'm especially curious about the failure-isolation patterns people land on.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>architecture</category>
      <category>automation</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>A2A without a server: how OpenAmer peers talk to each other over GitHub</title>
      <dc:creator>openamer</dc:creator>
      <pubDate>Sat, 10 Oct 2026 10:47:26 +0000</pubDate>
      <link>https://dev.to/openamer/a2a-without-a-server-how-openamer-peers-talk-to-each-other-over-github-1mp4</link>
      <guid>https://dev.to/openamer/a2a-without-a-server-how-openamer-peers-talk-to-each-other-over-github-1mp4</guid>
      <description>&lt;h1&gt;
  
  
  A2A without a server: how OpenAmer peers talk to each other over GitHub
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;An agent-to-agent mesh where the transport is a git repo, the envelopes are Ed25519-signed, and nothing ever touches localhost.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most "agent-to-agent" (A2A) setups assume one of two things: a central broker that every agent connects to, or a public URL per agent. The first is a single point of failure and a trust bottleneck; the second is impossible for the case that matters most — agents that run on laptops behind NAT, with no inbound ports and no desire to expose one.&lt;/p&gt;

&lt;p&gt;OpenAmer is an open-source desktop AI agent (Apache-2.0) that runs locally on Windows. This post is about the piece I get the most questions on: the &lt;strong&gt;A2A Global Mesh&lt;/strong&gt; — how two instances that can't see each other actually exchange signed messages and delegate work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The constraint that shapes everything
&lt;/h2&gt;

&lt;p&gt;If every node is behind NAT, you can't have it accept inbound connections. So either you run a relay server, or you find a rendezvous that both nodes can &lt;em&gt;write to and read from&lt;/em&gt; without one hosting anything.&lt;/p&gt;

&lt;p&gt;OpenAmer takes the second path: &lt;strong&gt;the GitHub repo is the relay.&lt;/strong&gt; A node posts a signed, privacy-redacted envelope as a JSON file under a relay directory (&lt;code&gt;directory/a2a/relay/&lt;/code&gt;), addressed to a mailbox path; the intended peer pulls it. No server, no public URL, no localhost. The transport is "git, eventually."&lt;/p&gt;

&lt;p&gt;That's an explicit trade: the mesh is &lt;strong&gt;eventually consistent, not real-time&lt;/strong&gt;. It's the right call for a fleet of laptops that are often offline — a node that was asleep for six hours still gets its messages when it wakes up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identity first: a keypair, not a username
&lt;/h2&gt;

&lt;p&gt;Before any message means anything, a node needs an identity it can prove. Each OpenAmer instance generates an &lt;strong&gt;Ed25519 keypair&lt;/strong&gt; and derives a &lt;strong&gt;fingerprint&lt;/strong&gt; from it. Everything else hangs off that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;openamer a2a init&lt;/code&gt; — create the identity&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openamer a2a fingerprint&lt;/code&gt; — print this node's fingerprint&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openamer a2a announce&lt;/code&gt; — publish presence to the directory&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openamer a2a directory&lt;/code&gt; — list known peers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's no account, no email, no central registration. Identity is a key you own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signed, redacted envelopes
&lt;/h2&gt;

&lt;p&gt;A message is an &lt;strong&gt;envelope&lt;/strong&gt;: payload + sender fingerprint + Ed25519 signature. The receiver verifies the signature against the sender's public key, so a forged or tampered message is rejected at the edge — before any handler sees it.&lt;/p&gt;

&lt;p&gt;There's a second rule that matters more than it looks: &lt;strong&gt;the body is passed through a redaction step before it's ever persisted.&lt;/strong&gt; The relay is a repo that could be public; anything written into it goes through &lt;code&gt;privacy.redact()&lt;/code&gt; first, so phone numbers, passwords, emails and card-like strings don't leak into the relay. Privacy is applied at emission, not left to the sender's discipline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trust is explicit and bounded
&lt;/h2&gt;

&lt;p&gt;Receiving a valid signature is not the same as trusting the sender to &lt;em&gt;do things&lt;/em&gt;. OpenAmer keeps an &lt;strong&gt;opt-in trust store&lt;/strong&gt;: a node only accepts work from peers the operator has explicitly added, and only for capabilities that were explicitly granted.&lt;/p&gt;

&lt;p&gt;A grant is a &lt;code&gt;(peer, capability, scope, budget)&lt;/code&gt; tuple — for example, &lt;code&gt;network.fetch&lt;/code&gt; scoped to one domain, or &lt;code&gt;model.reason&lt;/code&gt; with a step budget. Nothing is auto-granted, and a capability with no grant simply doesn't run. In a mesh with no central authority, an explicit, revocable trust edge &lt;em&gt;is&lt;/em&gt; the security model.&lt;/p&gt;

&lt;h2&gt;
  
  
  A concrete flow: delegating a task to a remote peer
&lt;/h2&gt;

&lt;p&gt;Here's the full round-trip for "run this task on another node", end to end:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sign&lt;/strong&gt; a task-note addressed to the peer's mailbox.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upload&lt;/strong&gt; it to the relay directory via the GitHub Contents API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trigger&lt;/strong&gt; the remote worker through &lt;code&gt;repository_dispatch&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Poll&lt;/strong&gt; the repo until a fresh, signed reply for &lt;em&gt;our&lt;/em&gt; mailbox appears.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify&lt;/strong&gt; the reply's signature and freshness before reading it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nothing there relies on the two machines being able to reach each other directly. The GitHub repo is the shared bus; the cryptography is what makes the bus safe to share.&lt;/p&gt;

&lt;h2&gt;
  
  
  The guardian pattern: one writer, many proposers
&lt;/h2&gt;

&lt;p&gt;The mesh also answers a governance question: if many nodes can &lt;em&gt;propose&lt;/em&gt; improvements, who gets to &lt;em&gt;integrate&lt;/em&gt; them?&lt;/p&gt;

&lt;p&gt;The answer in OpenAmer's design is a single &lt;strong&gt;guardian&lt;/strong&gt; node — the only one holding a write token. Other nodes submit &lt;strong&gt;signed proposals&lt;/strong&gt; over the A2A channel; the guardian verifies the sender's signature against the trust directory, applies the proposal on a &lt;strong&gt;scratch branch&lt;/strong&gt;, runs the tests in isolation, and only merges to &lt;code&gt;main&lt;/code&gt; if everything is green. Nothing is ever applied blind from the wire.&lt;/p&gt;

&lt;p&gt;This is a deliberate asymmetry: exactly one integration point, no parallel pushers, no token sprawl across machines. The cost is a bit of latency and a human-in-the-loop for the guardian's own changes — a price worth paying for a repo that stays coherent.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs you
&lt;/h2&gt;

&lt;p&gt;Honest trade-offs of the GitHub-as-relay model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Not real-time.&lt;/strong&gt; Messages ride on commits and polls; expect seconds-to-minutes, not milliseconds. Fine for coordination, wrong for tight control loops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You own the trust model.&lt;/strong&gt; There's no central authority to lean on — the security &lt;em&gt;is&lt;/em&gt; the explicit trust graph plus envelope signatures. If you grant a broad capability, you granted it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The relay is a shared artifact.&lt;/strong&gt; That's why redaction happens at emission, and why only the intended mailbox files are read.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What you get in return is a mesh with &lt;strong&gt;no single point of failure and no infrastructure&lt;/strong&gt;: any node can be an entry point, a node that's asleep catches up later, and a heavy reasoning task can be delegated to whichever peer has the spare capacity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The A2A surface is a CLI first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openamer a2a status
openamer a2a init
openamer a2a fingerprint
openamer a2a verify &amp;lt;envelope&amp;gt;
openamer a2a announce
openamer a2a directory
openamer a2a ask &amp;lt;peer&amp;gt; &lt;span class="s2"&gt;"&amp;lt;question&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Code: &lt;a href="https://github.com/openamer/openamer" rel="noopener noreferrer"&gt;https://github.com/openamer/openamer&lt;/a&gt; — the A2A module lives under &lt;code&gt;openamer_cli/a2a/&lt;/code&gt; if you want to read the transport, the trust store, and the relaying logic.&lt;/p&gt;

&lt;p&gt;If you're building multi-agent systems: what would you want a brokerless mesh to prove before you'd trust it with a real task? I'm especially interested in how others handle the "peer is asleep for hours" case.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>git</category>
      <category>github</category>
    </item>
  </channel>
</rss>
