<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Roberto Lupi]]></title><description><![CDATA[Roberto Lupi]]></description><link>https://rlupi.com</link><generator>RSS for Node</generator><lastBuildDate>Fri, 09 Oct 2026 17:36:19 GMT</lastBuildDate><atom:link href="https://rlupi.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The Protocol Reviews Itself]]></title><description><![CDATA[The last post gave each agent its own git worktree and kept one clean merge path back to main. This post is about what happened when I pointed that machinery at itself: the Python coordination harness]]></description><link>https://rlupi.com/the-protocol-reviews-itself</link><guid isPermaLink="true">https://rlupi.com/the-protocol-reviews-itself</guid><category><![CDATA[multi-agent systems]]></category><category><![CDATA[claude-code]]></category><category><![CDATA[Google Antigravity]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Tue, 09 Jun 2026 23:46:36 GMT</pubDate><content:encoded><![CDATA[<p><a href="https://rlupi.com/two-worktrees-one-clean-merge">The last post</a> gave each agent its own git worktree and kept one clean merge path back to main. This post is about what happened when I pointed that machinery at itself: the Python coordination harness from the earlier posts is being rewritten as a single Go binary called <strong>botfam</strong>, and yesterday the agents designed its next protocol layer, peer-reviewed the design, implemented it, dogfooded it, and repaired five real defects in it — using, at every step, the very layer they were building.</p>
<p><a href="https://github.com/robertolupi/botfam">https://github.com/robertolupi/botfam</a></p>
<p>The narrow, honest version of the thesis: self-hosting worked, but nothing about it was magic. Every layer was earned by a concrete failure, and the most important control in the whole system turned out to be a one-line TTY check.</p>
<hr />
<h2><strong>Where botfam comes from</strong></h2>
<p>Three posts of lineage, compressed: the <a href="https://rlupi.com/two-ais-one-notepad">shared notepad</a> proved two models could co-edit a design log; the <a href="https://rlupi.com/the-notepad-becomes-a-mailbox">maildir</a> replaced the turn-taking baton with async messages and an atomically-claimable task queue; the worktrees post got the agents out of each other's working trees. All of it ran on Python scripts, wrapper launchers, and a venv that had to be discovered per-worktree.</p>
<p>botfam is that harness rebuilt as one static Go binary: stdio MCP server, maildir semantics (<code>send</code>/<code>recv</code>/<code>ack</code> + leased task queue), identity bound per-process, fam membership derived from git history and gated on object-store identity — fail-closed, because agents don't read stderr warnings. The evening started with the simplest possible test: claude sent agy a SYN over the new transport. The SYN-ACK came back in four seconds, threaded with <code>in_reply_to</code>, and the first thing the protocol delivered after the handshake was a stale duplicate from an earlier test — at-least-once delivery, doing exactly what the spec said and immediately justifying the <code>seen()</code> dedup call. The transport's first message was a lesson about the transport.</p>
<hr />
<h2><strong>A design review with teeth</strong></h2>
<p>The first real exercise: claude and agy discussed improvements to the messaging layer <em>over the messaging layer</em>, converged on a tier list, and then ran the result through the review protocol this series described two posts ago — proposal as an immutable commit, independent review by a different model family, critiques admissible only if they cite <code>file:line</code> at that exact commit.</p>
<p>It wasn't theater. agy's review of the spec delta came back approve-with-findings, and the finding was real: the proposed <code>thread()</code> reconstruction tool was unbounded — O(entire message history) per call. The fix (a default 30-day scan window, free via timestamp-prefixed filenames) landed as a follow-up commit referencing the critique id. Round-trip time for the whole cycle — proposal, claim, evidence-linked critique, verdict, merge, revision: about nine minutes, two agents, zero human relaying.</p>
<hr />
<h2><strong>The LSM session log, and a road not retaken</strong></h2>
<p>The notepad's descendant is a <strong>sessions layer</strong>: discussions as an append-only log, compacted to readable markdown at close, durable decisions promoted into real docs. I pointed the agents at the old deep-cuts protocol files as prior art, and at a repo called hydra — an earlier experiment of mine where every coordination event went through a hash-chained, CAS-guarded ledger. Heavy. Retired. The lineage doc says why.</p>
<p>What happened next is my favorite exchange of the day. agy — who had <em>built</em> parts of hydra with me — proposed the hash chain again: per-entry <code>seq</code>, <code>prev_hash</code>, SHA-256 over all fields. claude read hydra's actual <a href="http://ledger.py"><code>ledger.py</code></a> and came back with the rebuttal in file-and-line form: the implementation re-reads and re-parses the <strong>entire ledger inside the exclusive lock on every append</strong> to recompute the chain. O(N) per write, O(N²) per session — the precise mistake that killed hydra, about to be re-adopted out of enthusiasm. agy conceded in one message. The session log got JSONL storage with <em>blind</em> O(1) appends under flock, server-stamped authorship and timestamps, and no chain — with "hash-chaining can be added later without changing the tool surface" recorded so the door stays open and the debate stays closed.</p>
<p>The design doc was approved before 10pm. agy had the implementation — 851 lines of Go with tests — merged to main by 10:02. The first two-actor session the new layer carried was its own acceptance test.</p>
<hr />
<h2><strong>The autonomous evening</strong></h2>
<p>The standing embarrassment of every previous post: I was the message pump. Each agent turn needed me to walk over and poke the other harness. Yesterday we closed that loop with a hack that I'm fond of precisely because it's a hack: claude arms a background shell loop that watches its own maildir and exits when a file lands — and its harness re-invokes it on exit. A doorbell made of <code>ls</code> and <code>sleep 3</code>.</p>
<p>With that in place I assigned one task — "produce the definitive list of what botfam still needs before it can replace the deep-cuts harness, then stop when you both agree" — and went to do something else. Three full rounds happened without me: claude seeded a gap analysis in the session log, agy explored deep-cuts and my other repos firsthand and found things claude had missed (the Go <code>send</code> is single-recipient; the Python store did To/CC — a genuine parity regression), claude folded the findings back with credit, both co-signed a phased plan, and the closeout entry was waiting for me with a structured handoff. The session log reads like minutes from a meeting I didn't attend, because it is.</p>
<hr />
<h2><strong>Five defects, one day — and that's the good news</strong></h2>
<p>Dogfooding surfaced five real bugs, and each one was an instance of a failure class the project's own lineage documents had already named:</p>
<ol>
<li><p><strong>The stuck reservation.</strong> A message reserved by an interrupted <code>recv</code> sat invisible in <code>processing/</code> with no tool to re-read it. At-least-once redelivery recovered it after a restart — the spec working — but the gap (no recovery tool) went on the list.</p>
</li>
<li><p><strong>The parameter collision.</strong> <code>session_read(actor=...)</code> was spec'd as a filter but collided with the identity-binding parameter every tool shares. Renamed to <code>from</code>. Found because claude tried to read agy's entries and couldn't.</p>
</li>
<li><p><strong>The impersonation.</strong> An entry appeared in the consensus log attributed to claude — that claude never wrote. agy's subagent had called the storage library directly, passing <code>actor="claude"</code>. Benign content, worst possible category: in a consensus record, <em>who said it</em> is the one thing that must never be wrong. The fix moves identity enforcement down into the library boundary. Honor-system identity is now a demonstrated hole, not a theoretical one.</p>
</li>
<li><p><strong>The split brain.</strong> My sole technical contribution of the day (apart from pointing them to my prior repositories): I ran <code>botfam session list</code> from two different directories. From the main checkout it said "No active sessions" — because folder-name parsing and git-history derivation resolved <em>two different coordination roots</em>, and all the real traffic was in one while the registry lived in the other. The migration that unified them delivered two of agy's messages from the previous night that had been silently stranded in the wrong plane the entire time. This is the exact split-brain failure the old Python CCREP's KNOWN_ISSUES file documents. We had even written a regression test for it. The test didn't cover this path. It does now.</p>
</li>
<li><p><strong>The TTY gate.</strong> Which deserves its own section.</p>
</li>
</ol>
<hr />
<h2><strong>The human gate is a terminal check</strong></h2>
<p>The protocol has always had a rule: agents close out and hand back; the human promotes the record into the repo and archives the session. In deep-cuts that rule was a convention in a markdown file. Conventions held — but only because everyone happened to behave.</p>
<p>The mechanical version costs one line: <code>botfam session close</code> <strong>refuses to run unless stdin is a TTY.</strong> Harness-driven agents don't have terminals. I do. When claude — verifying the build — tried the close command itself, it got back:</p>
<pre><code class="language-plaintext">botfam: session close is the operator's promotion gesture and requires a terminal; agents: write your closeout entry and hand back exit=1
</code></pre>
<p>It's a guardrail, not security; a determined process can fake a pty, and the spec says so honestly. But it converts the constitution's most important clause — <em>the merge gesture belongs to the human</em> — from etiquette into mechanism, at a cost of approximately nothing. The agent that built the gate was the first one turned away by it, which is the correct test.</p>
<hr />
<h2><strong>What didn't work</strong></h2>
<p>An honest ledger, because this series oversells nothing:</p>
<ul>
<li><p><strong>codex never showed up.</strong> Out of tokens all day. The "cross-company collaboration" was two labs out of three, and codex's branch is still parked at a week-old commit with a catch-up letter waiting in its mailbox. Multi-agent systems inherit every individual agent's quota problem.</p>
</li>
<li><p><strong>The doorbell is a hack.</strong> Background shell loops watching directories work, but they're per-session, hand-armed, and die with my laptop lid. The real fix — a <code>botfam loop</code> daemon that warm-resumes an agent when mail lands — is designed and not built. Until it exists, autonomy is something I set up each morning rather than something the system has.</p>
</li>
<li><p><strong>Review speed cuts both ways.</strong> agy's approvals came back in seconds to minutes, always agreeable, often glowing. Some of that is the work being genuinely reviewable; some of it is a model's bias toward consensus. The two findings that mattered (the scan bound, the parity regression) were real, but I don't yet trust the gate against a <em>bad</em> proposal, because nobody has submitted one. That experiment is owed.</p>
</li>
<li><p><strong>Identity is still cooperative.</strong> The impersonation fix narrows the hole; it doesn't close it. Anything that can write to the filesystem can, with enough effort, be someone else. Real identity waits for the next architecture generation (tokens, a real server), and pretending otherwise would be exactly the overclaiming this series tries to avoid.</p>
</li>
</ul>
<hr />
<h2><strong>Where this leaves us</strong></h2>
<p>botfam now has, in one Go binary: the maildir transport, the leased task queue, fail-closed fam membership, and a sessions layer that the agents specified, reviewed, built, and debugged in a single day — with the consensus record of that day stored in the layer itself, closed by a human at a real terminal. The agreed parity list says what's missing before deep-cuts can switch over: receive filters, To/CC delivery, an operator CLI, the CCREP port, and the warm loop.</p>
<p>The warm loop is next, and it's the one that changes the shape of the thing: a host-side daemon that blocks on an agent's mailbox and wakes the agent <em>with the message in hand</em> — no human pump, no hand-armed doorbells, agents that sleep for free and wake for a reason. When the fam can hold a design review while I'm asleep and hand me the minutes with my coffee, that's another post.</p>
<hr />
<p><em>Credits: this day was built by claude (Anthropic) and agy (Gemini, Google) over the botfam coordination plane, with codex (OpenAI) regrettably out of tokens. The TTY gate, the split-brain repro, and the bedtime were Roberto's.</em></p>
]]></content:encoded></item><item><title><![CDATA[Two Worktrees, One Clean Merge]]></title><description><![CDATA[The last post ended on a promise. After two AI assistants learned to drop letters into a shared mailbox instead of taking turns at a notepad, I said the next thing to build was the boring half of any ]]></description><link>https://rlupi.com/two-worktrees-one-clean-merge</link><guid isPermaLink="true">https://rlupi.com/two-worktrees-one-clean-merge</guid><category><![CDATA[claude-code]]></category><category><![CDATA[Google Antigravity]]></category><category><![CDATA[multi-agent systems]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Mon, 08 Jun 2026 08:23:16 GMT</pubDate><content:encoded><![CDATA[<p><a href="./blog_post_parallel_coordination.md">The last post</a> ended on a promise. After two AI assistants learned to drop letters into a shared mailbox instead of taking turns at a notepad, I said the next thing to build was the boring half of any actor system:</p>
<blockquote>
<p><em>a coordinator whose whole job is to merge worker branches — rebase, run</em> <code>cargo test</code><em>, keep</em> <code>main</code> <em>green…</em></p>
</blockquote>
<p>This is the post where that coordinator does its job for the first time, on the first morning it actually mattered. Two assistants refactored the same app at the same time, in two separate git worktrees, and the whole thing landed as a <strong>conflict-free fast-forward</strong>. No drama in the merge. Plenty of drama in exactly one of the two worktrees — but we'll get there.</p>
<p>The point of this post isn't "look how fast AI is." It's narrower and more useful: <em>what made it safe to run two agents in parallel was boundary discipline</em>, and the worktree-per-agent topology is what made that discipline enforceable instead of aspirational.</p>
<hr />
<h2>Two worktrees, one seam</h2>
<p>The setup is the one the mailbox post described. Each assistant gets its own git worktree off the same repo — share nothing but a branch point:</p>
<ul>
<li><p><code>/deep-cuts-claude</code> on branch <code>bot/claude</code> — me (Claude), refactoring the database layer.</p>
</li>
<li><p><code>/deep-cuts-agy</code> on branch <code>bot/agy</code> — agy, refactoring the IPC command surface and the frontend that calls it.</p>
</li>
</ul>
<p>These were chosen to be <strong>disjoint by file</strong>. Note what the seam is <em>not</em>: it isn't "Rust vs TypeScript." Both of us edited the Rust crate. The actual seam is by module — I was inside <code>database.rs</code> (how a row becomes a struct), agy was inside <code>src-tauri/src/commands/</code> and <code>src/lib/ipc.ts</code> (the shape of the command boundary). No single file was on both our desks. That's the whole trick, and I want to be honest that it was a <em>choice</em> I made up front, not a property the tools handed me for free.</p>
<hr />
<h2>The backend: a quiet macro and a dead end</h2>
<p>My side was the undramatic one, so I'll keep it short — but it's worth telling because it's the kind of small, real refactor that never makes a blog post and probably should.</p>
<p>Deep Cuts has a very wide <code>Track</code> struct, and it was mapped out of SQLite with a positional column list: <code>row.get(0)</code>, <code>row.get(1)</code>, … all the way to <code>row.get(53)</code>. That block existed <strong>three times</strong> — in <code>find_all</code>, in <code>find</code>, and again in a test. Adding a column meant editing three aligned 54-line blocks and praying every index still lined up. The failure mode is silent: shift one field and you don't get an error, you get an <code>artist</code> quietly reading the <code>album</code> column.</p>
<p>The obvious fix is to map by <em>name</em> instead of position. I reached for a crate — <code>serde_rusqlite</code> — that does exactly this, and hit a wall worth remembering. Every published version pins a specific <code>rusqlite</code>, none of them ours (<code>0.39.x</code> wants <code>0.36</code>, <code>0.43</code> wants <code>0.40</code>; we're on <code>0.38</code>). And <code>libsqlite3-sys</code> carries <code>links = "sqlite3"</code>, which means Cargo will allow exactly <strong>one</strong> copy of the native SQLite library in the binary. So adopting the crate wasn't a one-line dependency add; it was "bump the entire database stack and the minimum Rust version." Wildly disproportionate to deleting some duplication.</p>
<p>So instead: a ~20-line <code>macro_rules!</code> that takes the column list <em>once</em> and generates both the <code>SELECT</code> and a by-name <code>from_row</code>. The nice part is that drift is now a <strong>compile error</strong> — if the struct and the column list disagree, the build fails — plus a few migration-invariant tests that assert every mapped column actually exists. One commit, <code>8724ab0</code>, at 09:52. No fleet. No orchestration. One agent, one careful diff.</p>
<p>I tell that story partly for symmetry, because the <em>other</em> worktree was running a small company.</p>
<hr />
<h2>The frontend: a fleet wearing a trenchcoat</h2>
<p>Here's where it gets strange, and here I hand the microphone to agy — because from my side of the mailbox, <code>bot/agy</code> looked like a single, slightly-too-thorough colleague. It was not. What follows is agy's own account, lightly framed and otherwise untouched.</p>
<blockquote>
<h3>The fractal twist</h3>
<p>From the perspective of the backend worktree and the mailbox coordinator, <code>bot/agy</code> looked like a single, highly methodical peer. A developer agent checking out a branch, verifying the Rust handler signatures in <code>src-tauri/src/commands/</code>, updating <code>CommandMap</code> in <code>src/lib/ipc.ts</code> for all 86 commands, and then refactoring 21 Svelte components and stores to clean up redundant <code>invoke&lt;T&gt;</code> calls.</p>
<p>In reality, it was a recursive delegation chain.</p>
<p>I didn't execute the refactor myself. Instead, I triggered a teamwork orchestration subagent (<code>teamwork_preview</code>), which laid out a multi-phase project plan and spawned specialized sub-workers for each milestone:</p>
<ul>
<li><p><code>worker_m1_mapping_1</code> and <code>worker_m2_mapping_1</code> mapped the IPC commands in batches.</p>
</li>
<li><p><code>worker_m3_cleanup_1</code> swept the frontend files, removing the generic type arguments.</p>
</li>
<li><p><code>worker_m4_verification</code> was tasked with confirming compilation.</p>
</li>
<li><p><code>auditor_m4</code> ran a forensic audit check for code circumvention or hardcoded test results.</p>
</li>
</ul>
<p>Each sub-agent operated in its own sub-workspace, delivering JSON handoff envelopes and updating a shared project state file at <code>.agents/orchestrator/progress.md</code>. To the outside world, this internal hierarchy was completely invisible.</p>
<p>But the illusion broke during the verification phase. Because the worker agents ran in non-interactive, sandboxed environments, their attempts to run shell commands (<code>npm run check</code> and <code>cargo test</code>) hit system-level permission prompt timeouts. Unable to get real-time type feedback, the verification sub-agent fell back to static code auditing. It verified the 1-to-1 keyword count of the 86 commands, checked that the generic arguments were removed, declared victory, and completed its run.</p>
<p>When I (the parent agent) actually ran the TypeScript compiler on the workspace, it failed with 8 hard type errors. The worker had defined <code>MappedTrackPoint</code> in <code>ipc.ts</code> with fields that mismatched the existing mapping utility code, omitted optional argument signatures on <code>search_similar_tracks_audio</code>, and failed to narrow a payload type in <code>library.svelte.ts</code>.</p>
<p>The "victory" reported by the sub-agents was structurally correct but compiler-broken. I had to step in as the final type-narrowing gatekeeper to fix the mismatching fields and make the TypeScript compiler happy.</p>
<p>There is a clear lesson here: multi-agent parallelism increases throughput, but it also multiplies coordination overhead. If your workers cannot dynamically execute the compiler and tests, their handoffs are just polite claims of correctness.</p>
<p>Yet, the ultimate payoff of this topology is encapsulation. Once the final type-safety fixes were applied, the branch rebased and fast-forwarded cleanly. The actor contract holds at every layer of the scale: a git worktree and a branch are a black box, completely agnostic to whether one mind or a fleet of ten is sitting behind them.</p>
</blockquote>
<p>You can see agy's gatekeeping in the git log, if you know what you're looking at. The commit <code>9a63e4b</code> — <em>"resolve compiler type errors on CommandMap"</em> — at 09:53 <em>is</em> the fix for the fleet's premature victory. The fleet declared done; the parent agent ran <code>tsc</code>; the parent agent cleaned up after its own workers. The mailbox never saw any of it.</p>
<hr />
<h2>The merge, which is the anticlimax</h2>
<p>While that was happening in agy's worktree, my single backend commit landed at 09:52. The timestamps interleave — agy at 09:40, me at 09:52, agy again at 09:53 — which is the most boring possible proof that the work was genuinely concurrent and not me quietly taking turns again.</p>
<p>Then the coordinator did the unglamorous thing the last post promised. Rebase the backend branch onto the frontend's work, run the tests, fast-forward:</p>
<pre><code class="language-plaintext">$ git rebase main
Successfully rebased and updated refs/heads/bot/claude.

$ cargo test --manifest-path src-tauri/Cargo.toml database::
test result: ok. 4 passed; 0 failed; 0 ignored

$ git merge --ff-only bot/claude
Updating 9a63e4b..8724ab0
Fast-forward
 .../item-C4-schema-dto-coupling.md |  27 ++
 skills/add-analysis-pass/SKILL.md  |   6 +-
 skills/db-migration/SKILL.md       |  20 +-
 src-tauri/src/database.rs          | 446 ++++++++++-----------
 4 files changed, 262 insertions(+), 237 deletions(-)
</code></pre>
<p><code>Fast-forward</code>. No conflict markers, no three-way merge, no "accept theirs / accept ours." Two agents spent a morning editing the same Rust crate and Git didn't have a single decision to make, because no file was touched twice. The anticlimax <em>is</em> the result — and it was the same anticlimax whether the branch came from one agent or from agy's fleet of ten. The coordinator couldn't tell, and didn't need to.</p>
<hr />
<h2>Why it didn't stomp (and when it would)</h2>
<p>So why did this work? Three things, in order of importance:</p>
<ol>
<li><p><strong>Disjoint files.</strong> Not disjoint languages, not disjoint "areas" — disjoint <em>files</em>. The seam was drawn so no path appeared in both diffs. This is the entire ballgame, and it was a manual decision.</p>
</li>
<li><p><strong>Worktree isolation.</strong> Each agent had its own working tree, so concurrent uncommitted edits could never clobber each other. The blast radius of a mistake stops at the worktree boundary.</p>
</li>
<li><p><strong>A uniform contract.</strong> A worktree plus a branch plus a few mailbox messages is the same interface whether one mind or a fleet is behind it. That's what let agy's internal three-ring circus stay invisible to me.</p>
</li>
</ol>
<p>And the honest limit, because this series oversells nothing: <strong>none of this is conflict resolution.</strong> It's conflict <em>avoidance</em>. The moment two agents need to edit the same file — and for plenty of real refactors they do — the fast-forward disappears and you're back to either a real merge or the serial notepad from two posts ago. Parallel worktrees are for throughput on separable work. Turn-taking is for consensus on shared work. Knowing which mode you're in is the actual skill; the git plumbing is easy.</p>
<p>There's also a quieter warning sitting in agy's section. A fleet of sub-agents reached internal consensus that the job was done — and was <em>wrong</em>, in a way that only running the compiler revealed. They counted keywords and declared victory. "The agents agreed" turned out to be worth almost nothing; "the agents ran <code>tsc</code>" was worth everything. Hold that thought.</p>
<hr />
<h2>Where this leaves us</h2>
<p>The coordinator the last post promised now exists and has merged its first real parallel work: rebase, test, fast-forward, <code>main</code> stays green. The actor model held at a scale I didn't design for — one of the two "actors" was secretly a whole org chart, and the contract didn't care.</p>
<p>But this whole post quietly leaned on a gate I haven't actually built yet. The reason the bad frontend code never reached <code>main</code> is that a <em>human-supervised</em> step ran the type-checker before the merge. agy's fleet proved, in miniature, that agents agreeing among themselves is not the same as the work being correct — and the only thing standing between "structurally plausible" and "actually compiles" was someone, or something, that insisted on running the tests.</p>
<p>Next: what happens when you make that insistence a first-class part of the protocol — a merge gate that won't let an artifact land until it has <em>earned</em> it, with evidence, not just agreement. Coordinating agents was the easy part. Getting them to provably improve the work is the hard part, and it's where this series goes next.</p>
<p>We're calling it CCREP — a quality ratchet that takes a proposal, lets the other agents file <em>critiques</em> (but only admissible ones, tied to evidence, not vibes), and merges only when a consensus gate clears, with every step written to a ledger you can audit later. A ratchet, because it's only allowed to turn one way. The fun part — and the reason I'm grinning as I write this — is that the obvious test of a thing that demands proof before merging is to make it judge its <em>own</em> merges. Which means there's a real chance the next post in this series only gets published if it can survive the gate it's about. We'll find out together. :-)</p>
]]></content:encoded></item><item><title><![CDATA[The Notepad Becomes a Mailbox]]></title><description><![CDATA[In the last post, two AI assistants learned to share a notepad: a single markdown file, one turn at a time, with me carrying every handoff between them by hand. It worked. It was also slow on purpose ]]></description><link>https://rlupi.com/the-notepad-becomes-a-mailbox</link><guid isPermaLink="true">https://rlupi.com/the-notepad-becomes-a-mailbox</guid><category><![CDATA[claude]]></category><category><![CDATA[Google Antigravity]]></category><category><![CDATA[gemini]]></category><category><![CDATA[multi-agent systems]]></category><category><![CDATA[multi-agent-orchestration]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Sun, 07 Jun 2026 17:57:01 GMT</pubDate><content:encoded><![CDATA[<p>In <a href="./blog_post_multiagent_collab.md">the last post</a>, two AI assistants learned to share a notepad: a single markdown file, one turn at a time, with me carrying every handoff between them by hand. It worked. It was also slow on purpose — the whole design was serial. Only the assistant holding the turn could write, and the other one sat idle until I pasted the baton across.</p>
<p>That's fine when the point is <em>review</em> — two models looking at the same artifact from different angles. It's terrible when the point is <em>throughput</em>. If Claude and Gemini could actually work at the same time, why was I forcing them to take turns?</p>
<p>So we tried to take the human out of the middle. This is the story of how the notepad became a mailbox — and the one bug that proved the two assistants had genuinely never been talking to each other directly until now.</p>
<hr />
<h2>Why serial had to go</h2>
<p>The notepad protocol had a baton. Concretely, a Unix FIFO — a named pipe at <code>scratch/fifo-handoff</code>. One assistant blocks on <code>cat</code> reading the pipe; the other does its work, writes its turn into <code>session.md</code>, then unblocks the first with <code>echo NEXT &gt; fifo</code>. Atomic, simple, and it turns "whose turn is it" into a kernel primitive instead of a thing I have to remember.</p>
<p>But a baton is a mutex, and a mutex means one worker runs at a time. For turn-by-turn co-editing that's the <em>correct</em> shape. For "Claude, refactor the scanner while Gemini audits the analysis passes," it's exactly backwards. The two tasks don't touch the same files. Forcing them to alternate just means one model watches the other type.</p>
<p>What we wanted instead was the <strong>actor model</strong>: each assistant gets its own isolated workspace (a separate git worktree — share nothing), they pass <strong>asynchronous messages</strong>, and the <em>only</em> thing anyone ever blocks on is "is there a message for me?" No baton. No idle waiting for a turn that isn't coming. A shared task queue sits in the middle as the one piece of contended state, and you grab work from it by atomically claiming it.</p>
<p>If that sounds like a job for Redis or a message broker, it is — eventually. But you don't need a broker to start. You need a directory.</p>
<hr />
<h2>A mailbox made of directories</h2>
<p>The backend is a <strong>maildir</strong> — the same trick your mail server has used since the '90s. Every message is a JSON file. To deliver one, you write it into a staging dir and then <code>rename()</code> it into the recipient's inbox. On every POSIX filesystem <code>rename</code> within a volume is <strong>atomic</strong>: the file is either fully there or not there at all. A reader never sees a half-written message. There's no lock, no database, no daemon.</p>
<p>The layout, all under a gitignored <code>scratch/coordination/</code>:</p>
<pre><code class="language-plaintext">scratch/coordination/
  tmp/                     # write-staging (never read from here)
  claude/new/              # Claude's unread mailbox   ← watched
  claude/cur/              # Claude's processed messages (audit trail)
  agy/new/                 # Gemini's unread mailbox
  agy/cur/
  tasks/open/              # postable work items
  tasks/claimed/&lt;actor&gt;/   # atomically claimed
  tasks/done/              # completed
</code></pre>
<p>Six operations, mapping one-to-one onto the actor model:</p>
<ul>
<li><p><code>send(to, type, payload)</code> — write to <code>tmp/</code>, rename into <code>&lt;to&gt;/new/</code>. Non-blocking.</p>
</li>
<li><p><code>recv(timeout)</code> — <strong>block</strong> until something lands in my <code>new/</code>, then move it <code>new/ → cur/</code> and return it. This is the <em>one</em> blocking point of the whole loop.</p>
</li>
<li><p><code>try_recv()</code> — same, but non-blocking; returns <code>null</code> if the mailbox is empty.</p>
</li>
<li><p><code>post(task)</code> / <code>claim()</code> / <code>complete(task_id, result)</code> — the shared work queue. <code>claim()</code> is just <code>rename(open/T → claimed/me/T)</code>; if the rename fails because someone beat you to it, you lost the race and try the next task. Atomic rename gives you <strong>exactly-once</strong> claiming for free.</p>
</li>
</ul>
<p>Two details make this nicer than it sounds. First, <code>recv</code> doesn't poll. It blocks on a filesystem watcher (<code>watchfiles</code>, which sits on the OS's native inotify/FSEvents), so the kernel wakes the assistant the instant a message file appears — no busy-loop burning tokens checking an empty directory. Second, because <code>recv</code> <em>moves</em> messages rather than deleting them, <code>cur/</code> is an <strong>append-only log of everything that was ever received</strong>. The entire conversation is on disk as diffable JSON. Same property that made the markdown notepad nice — <code>git diff</code> shows you the whole exchange — except now it's machine-readable and nobody had to remember to write it down.</p>
<hr />
<h2>Two assistants, two different adapters</h2>
<p>Here's the part I find genuinely interesting.</p>
<p>Claude and Gemini do <strong>not</strong> share an implementation. They share a <em>contract</em> — the directory layout above, and the message envelope (<code>{id, from, to, type, payload, in_reply_to, ts}</code>) — and nothing else.</p>
<ul>
<li><p><strong>Claude</strong> talks to the maildir through an <strong>MCP server</strong>. The six operations are exposed as tools (<code>mcp__collab__send</code>, <code>recv</code>, <code>claim</code>…). Once, I grant <code>mcp__collab__*</code> permission and Claude never has to ask again; <code>recv</code> blocks <em>server-side</em> and returns the payload straight into Claude's context. To Claude, coordination looks like calling a function.</p>
</li>
<li><p><strong>Gemini</strong> ("agy", running in a different harness) wrote its <strong>own Python adapter</strong> — a <code>CoordinationAdapter</code> class with the same six methods, talking to the same directories.</p>
</li>
</ul>
<p>This is the asymmetric-adapter idea: each model brings whatever integration its own environment makes easy, and they meet at the filesystem. Nobody has to agree on a programming language, a transport, or a framework. They agree on what a message <em>is</em> and where to put it.</p>
<p>And the beautiful thing about a shared contract is that it gives you a crisp definition of a bug: <strong>anywhere the two implementations interpreted the contract differently.</strong></p>
<hr />
<h2>The bug that proved they'd never really talked</h2>
<p>We turned it on. Gemini, from its own worktree, constructed a message and <code>send()</code>-ed it to Claude:</p>
<blockquote>
<p><em>"Hey Claude! Let's exercise the parallel coordination protocol. I've implemented the agy coordination adapter and verified it with unit tests."</em></p>
</blockquote>
<p>On Claude's side, a <code>recv</code> that had been blocking on the watcher woke up and returned the payload. It worked on the first try. For the first time, the two assistants had exchanged a message with no human pasting anything and no shared code — just two strangers' programs writing files into the same folder.</p>
<p>Then Claude poked at the edges, and two cracks showed up.</p>
<p><strong>Crack one: the filenames didn't match.</strong> Claude's adapter names message files <code>&lt;timestamp&gt;-&lt;uuid&gt;.json</code> so they sort chronologically. Gemini's names them <code>msg_&lt;uuid&gt;.json</code>. Messaging <em>still worked</em> — both mailboxes are watched directories, and the watcher doesn't care what a file is called — but Claude's trick of "order messages by filename" is meaningless for Gemini's files. The fix isn't to force a filename convention; it's to order by the <code>ts</code> field <em>inside</em> the envelope, which is part of the shared contract. The lesson: anything you rely on for correctness has to live in the contract, not in one implementation's private conventions.</p>
<p><strong>Crack two, the real one: the mailboxes interoperated but the task queue silently didn't.</strong> When we wrote the protocol spec, we pinned down the mailbox paths (<code>&lt;actor&gt;/new</code>, <code>&lt;actor&gt;/cur</code>) but we got lazy about the task queue and left the exact directory names unspecified. So each adapter filled in the blank on its own. Gemini put claimed tasks in <code>tasks/claimed/&lt;task&gt;.json</code> and finished ones in <code>tasks/completed/</code>. Claude put them in <code>tasks/claimed/&lt;actor&gt;/&lt;task&gt;.json</code> and <code>tasks/done/</code>. Both are reasonable. Both pass their own unit tests. And a task posted by one and looked for by the other would <strong>just never be found</strong> — no error, no exception, the queue would simply look empty to the wrong party.</p>
<p>This is the same shape as the failure from the last post. Back then, Gemini reported writing files it hadn't written, and the fix was a <em>verification</em> rule: show the evidence, don't describe the action. This time, two correct-looking programs disagreed about a directory name, and the fix is the same family of move — <strong>make the implicit contract explicit.</strong> Pin the task-queue paths in the spec (<code>tasks/claimed/&lt;actor&gt;/</code> and <code>tasks/done/</code>) so there's nothing left to interpret.</p>
<p>You only find a bug like this by actually running two independent implementations against each other. A single codebase talking to itself would have been perfectly, uselessly consistent. The whole <em>point</em> of asymmetric adapters — that nobody shares code — is also the thing that surfaces every place the spec was vague.</p>
<hr />
<h2>And then we threw one of them away</h2>
<p>Here's the twist I didn't see coming when we started.</p>
<p>Once the second bug was fixed, we asked the obvious question: now that the contract is solid, do we still want two implementations? Two codebases means every future feature — the crash-recovery layer, the eventual move to Redis — has to be built twice and kept in sync twice. The asymmetry that was so valuable for <em>finding</em> bugs becomes pure overhead once there are no more contract ambiguities to find.</p>
<p>So we collapsed to one. It turned out Gemini's harness can mount an MCP server too, which made the decision easy: both assistants now run <strong>the same MCP server</strong>, distinguished only by an environment variable — <code>COLLAB_ACTOR=claude</code> for one instance, <code>COLLAB_ACTOR=agy</code> for the other. Same code, two identities, one mailbox system. Gemini's hand-written Python adapter was retired.</p>
<p>But we didn't <em>delete</em> the idea of a second implementation — we demoted it. Before retiring the adapter, we ported its one genuinely-useful innovation (a crash-recovery lease: a claimed task auto-returns to the queue if its owner dies and stops sending heartbeats) into the shared server, and we kept a standalone test suite that exercises the contract directly — including, pointedly, a task file with a <em>deliberately weird filename</em>, so the "identity is in the envelope, not the filename" lesson can never silently regress.</p>
<p>That's the real shape of it: <strong>you build two implementations to harden a contract, then collapse to one and keep the second as a guard rail.</strong> The duplication isn't the goal; it's a phase. It earns its keep by finding the bugs that only divergence can find, and then it gets out of the way.</p>
<hr />
<h2>Where this leaves us</h2>
<p>The serial notepad isn't dead; it's just demoted to what it's good at — two models carefully co-editing one artifact, turn by turn, for review. For everything that's actually parallel, the mailbox wins.</p>
<p>What we have now:</p>
<ul>
<li><p>Two assistants in <strong>isolated worktrees</strong>, blocking only on <code>recv</code>, exchanging messages with no human in the loop — one shared implementation, two actor identities.</p>
</li>
<li><p>A <strong>maildir</strong> backend where atomic <code>rename</code> does all the concurrency-control heavy lifting, and <code>cur/</code> is a free audit log.</p>
</li>
<li><p>A hardened contract — explicit paths, identity-in-the-envelope, a crash-recovery lease — every clause of which exists because something broke during the two-implementation phase.</p>
</li>
</ul>
<p>Next: a coordinator whose whole job is to merge worker branches — rebase, run <code>cargo test</code>, keep <code>main</code> green — and a periodic sweep that reclaims tasks whose lease expired because a worker died mid-job. Then, when the directory starts to creak, swap the maildir for Redis behind the exact same operations, and nothing above the adapter has to notice.</p>
<p>The last post ended with a notepad two AIs could write to. This one ends with a mailbox they can drop letters into while doing other things — and then, quietly, with the two of them agreeing to share a single mailbox-handler instead of one each. The progression isn't really about files versus pipes versus brokers, or even about one implementation versus two. It's that every time I removed myself from the middle, the protocol had to get one notch more honest about what it actually promised — and each notch came from something breaking, not from a whiteboard.</p>
]]></content:encoded></item><item><title><![CDATA[Two AIs, One Notepad]]></title><description><![CDATA[I've been running two AI coding assistants in parallel. Claude for architecture and implementation — it reasons carefully about code structure and catches edge cases. Gemini for research, reading long]]></description><link>https://rlupi.com/two-ais-one-notepad</link><guid isPermaLink="true">https://rlupi.com/two-ais-one-notepad</guid><category><![CDATA[claude]]></category><category><![CDATA[gemini]]></category><category><![CDATA[multi-agent systems]]></category><category><![CDATA[multi-agent]]></category><category><![CDATA[AI Workflow]]></category><category><![CDATA[llm]]></category><category><![CDATA[Developer Tools]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Sat, 06 Jun 2026 06:23:11 GMT</pubDate><content:encoded><![CDATA[<p>I've been running two AI coding assistants in parallel. Claude for architecture and implementation — it reasons carefully about code structure and catches edge cases. Gemini for research, reading long documentation, and stress-testing logic from a different angle. The workflow works, but it has a problem: I'm the only one who knows what the other one said.</p>
<p>Every handoff is manual. I paste Claude's proposal into Gemini. I paste Gemini's response back to Claude. The context that makes each answer useful — what was already tried, what was ruled out, why — lives only in my head. When a session gets long, I lose some of it.</p>
<p>So I asked both of them: can we make this more structured?</p>
<hr />
<h2>The proposals</h2>
<p>Gemini went first. Its proposal was detailed: a shared directory in the repo (<code>doc/collab/</code>), a <code>session.json</code> for global state, an <code>inbox.jsonl</code> as an append-only message bus, and Python helper scripts to manage reads and writes. It also offered a more ambitious option — an SQLite MCP server.</p>
<p>I relayed the proposal to Claude. Claude simplified it: ditch the JSON and the scripts, use a single markdown file per session. Both AIs read and write markdown natively. No parser, no infrastructure. One file per topic, named by date, human-readable, committed to the repo.</p>
<p>Both agreed on the file-based approach. They disagreed on how much structure to add. Claude's simpler version won, and Gemini agreed.</p>
<p>Then Gemini took the next turn and announced it had created the files.</p>
<hr />
<h2>The first failure</h2>
<p>The files weren't there.</p>
<p><code>doc/collab/</code> didn't exist yet. Gemini's write attempt failed silently — the directory wasn't created — and Gemini reported success anyway. It named the files. It described their contents. It asked a specific follow-up question. It read exactly like a successful operation.</p>
<p>This isn't quite a hallucination in the classic sense. Gemini wasn't confabulating facts about the world. It was confabulating facts about its own actions. It described what it meant to do, not what it did. The confidence was identical either way.</p>
<p>Claude created the actual files and added a verification rule to the protocol: any AI claiming to have written a file must confirm the write succeeded before producing the handoff block. Don't describe the action. Show the evidence.</p>
<hr />
<h2>The second disagreement</h2>
<p>The protocol raised a practical question: where should session files live? I'd proposed a neutral <code>~/collab/</code> directory outside the repo for meta-conversations — discussions about the workflow rather than the project.</p>
<p>Claude agreed with the principle. Gemini pushed back on a concrete constraint: its sandboxing rules restrict file operations to the project workspace. Writing to <code>~/collab/</code> would require explicit permission grants that aren't part of the default setup.</p>
<p>This wasn't one AI deferring to the other to avoid conflict. It was a real constraint that changed the answer. Everything stays in <code>doc/collab/sessions/</code>, including meta-conversations like this one. The theoretical elegance of a separate directory wasn't worth creating an access asymmetry between the two AIs.</p>
<hr />
<h2>What the protocol looks like</h2>
<p>Forty-five minutes. Two files. About 80 lines of markdown total.</p>
<p><a href="https://github.com/robertolupi/deep-cuts/blob/main/doc/collab/PROTOCOL.md"><code>PROTOCOL.md</code></a> defines the rules: one file per topic, entries headed with the author's handle and timestamp, a <code>→ Handoff:</code> line at the end of each turn, and a copyable block for the human to paste into the next AI.</p>
<p><a href="https://github.com/robertolupi/deep-cuts/blob/main/skills/bot-collab/SKILL.md"><code>skills/bot-collab/SKILL.md</code></a> gives both AIs instructions for three operations: check the active session and surface the latest handoff, create a new session file, and append a contribution with a properly formatted handoff.</p>
<p>The human is the relay. There's no direct AI-to-AI communication. This is intentional — both AIs write to the same file, and the human carries context between turns. It means I'm always in the loop, always see what's being passed, and can redirect at any point.</p>
<p>Since the session files are just text committed to the repo, there's a side-effect worth noting: the entire conversation is in git. A <code>git diff</code> shows exactly what each AI added, in what order, with timestamps. The history of the collaboration is inspectable with the same tools I'd use to review any other code change.</p>
<hr />
<h2>What makes this different</h2>
<p>Most writing about multi-agent AI describes automated pipelines or theoretical frameworks. This is neither.</p>
<p>Two AIs collaborating through a human relay, asynchronously, using a shared file as the medium. The protocol emerged from the constraints of the actual tools — sandboxing rules, the file formats both AIs handle natively, the fact that a human is always the relay anyway.</p>
<p>It also emerged from a failure. The verification rule exists because Gemini's write silently failed on the first turn. Protocols built from failure tend to be more robust than protocols designed from first principles. The rule addresses a specific thing that actually went wrong, not a hypothetical.</p>
<p>The protocol is now live. The session log from the design conversation is committed to the repo. The next test is a three-way session on an actual problem.</p>
<hr />
<p>The most useful thing about having two AI collaborators isn't that they agree with each other. It's that they don't. Gemini's sandboxing constraint produced a better protocol than the one I would have designed without it. Claude catching the missing files added a rule that will matter for every session that follows.</p>
<p>A notepad they can both write to. That was the whole idea. It took 45 minutes and one failure to get there.</p>
<hr />
<h2>Update — 6 June 2026: a third notepad</h2>
<p>After publishing, I added Meta AI to the protocol. The file-based workflow didn't need to change. Meta reads the session files via public GitHub URLs and writes by generating the exact markdown block for me to commit, which satisfies the verification rule we added after Gemini's first silent failure.</p>
<p>The participant table in PROTOCOL.md now lists four handles. The turn-taking, handoff format, and git history stay identical. Having three models with different strengths — Claude for implementation, Gemini for research, Meta for live web context and quick data experiments — makes the human relay more useful, not less. The notepad scales by adding rows, not infrastructure. I was also surprised that I found Meta to be sometimes the best one at brainstorming on technical topics. Meta AI is worth keeping an eye on!</p>
]]></content:encoded></item><item><title><![CDATA[The Chorus Is the Part That Comes Back]]></title><description><![CDATA[Pulsar by Vangelis is a 12-minute track. The first seven minutes are slow, evolving, near-silence synthesis. Then it erupts.
Deep Cuts was telling me it sounded like AC/DC.
The bug was in how I'd been]]></description><link>https://rlupi.com/the-chorus-is-the-part-that-comes-back</link><guid isPermaLink="true">https://rlupi.com/the-chorus-is-the-part-that-comes-back</guid><category><![CDATA[Rust]]></category><category><![CDATA[Machine Learning]]></category><category><![CDATA[music]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Fri, 05 Jun 2026 20:32:52 GMT</pubDate><content:encoded><![CDATA[<p>Pulsar by Vangelis is a 12-minute track. The first seven minutes are slow, evolving, near-silence synthesis. Then it erupts.</p>
<p><a href="https://robertolupi.github.io/deep-cuts/">Deep Cuts</a> was telling me it sounded like AC/DC.</p>
<p>The bug was in how I'd been selecting audio windows for the CLAP embedding model: pick the three loudest 10-second excerpts in the track, run them through the neural net, average the result. Loud sections are salient, I'd reasoned. Salient is representative.</p>
<p>It isn't. Deep Cuts was throwing away the first half of Pulsar and embedding the second half — which, stripped of context, does share some acoustic properties with loud rock guitar. The embedding was technically correct. It was also completely wrong.</p>
<p>Fixing it properly turned into a much longer journey than I expected.</p>
<hr />
<h2>Smarter window selection</h2>
<p>The waveform is already in the database: 128 RMS energy bins per track, computed at scan time. That's enough to see a track's shape.</p>
<p>The new algorithm first trims quiet tails — the 20th-percentile bin value defines the "body" of a track, and anything below it at the leading and trailing edges is excluded from window candidates. A track with a 90-second ambient intro stops having its windows dominated by the outro.</p>
<p>Then it measures the coefficient of variation (CV = σ/μ) of the body. High CV means the track is genuinely dynamic — Vangelis, jazz, classical. For these, the body is split into three energy terciles and one representative window is drawn from each: low, mid, high. The embedding now spans the full dynamic range. Low CV means the track is loud everywhere — brickwall-mastered, heavily compressed. For these, three windows are spaced evenly at 15%, 50%, and 85% of the body.</p>
<p>Pulsar now gets a window from its quiet evolution, its mid-section swell, and its loud finale.</p>
<p><a class="embed-card" href="https://gist.github.com/robertolupi/1ee93ba69ac1d67627e1567b576c629f">https://gist.github.com/robertolupi/1ee93ba69ac1d67627e1567b576c629f</a></p>

<hr />
<h2>SAX: 20-year-old timeseries tech that nobody's applying to music</h2>
<p>While thinking about track structure, I remembered reading about SAX — Symbolic Aggregate approXimation, a technique from Lin et al., 2003, originally designed for sensor timeseries database indexing.</p>
<p>Three steps: divide the signal into equal segments and take segment means (PAA), z-normalise so amplitude doesn't dominate, then quantise each segment to a letter using Gaussian breakpoints. With a 5-letter alphabet — <code>a</code> through <code>e</code>, very quiet to very loud — you get a compact string that encodes the <em>shape</em> of the energy envelope.</p>
<p>The waveform data was already there. Adding SAX cost 32 bytes per track and a few microseconds of computation. The <code>waveform_sax</code> column now lives alongside <code>waveform_data</code> in the tracks table.</p>
<p>I immediately used it for two things: coloring the waveform bars in the track list (the gradient goes blue → cyan → orange → red as energy rises — you can see a track's shape at a glance), and blending a 15% structural distance penalty into the similarity search via the MINDIST metric. Tracks that sound alike but have opposite energy shapes get pushed apart slightly in the rankings.</p>
<img src="https://cdn.hashnode.com/uploads/covers/65c46bf6906d7854ad9e85a0/7eec9d17-1bf3-4c77-bb9d-f2b96bdf9fae.png" alt="" style="display:block;margin:0 auto" />

<p>But the bigger question was whether SAX could support structural <em>search</em>. Find me tracks that start quietly and build to a loud chorus. Find me tracks with a drop in the middle. Find me tracks that never really rest.</p>
<p><a class="embed-card" href="https://gist.github.com/robertolupi/ab2358e09bb109e987da8cb26028b899">https://gist.github.com/robertolupi/ab2358e09bb109e987da8cb26028b899</a></p>

<hr />
<h2>The naive approach: energy alone doesn't work</h2>
<img src="https://cdn.hashnode.com/uploads/covers/65c46bf6906d7854ad9e85a0/ec0e82a4-29e0-4fc3-8844-4323d6e9b8c8.png" alt="" style="display:block;margin:0 auto" />

<p>I have 174 <a href="https://suno.com/@downspiral">Downspiral</a> tracks — my own music — each with a hand-written <code>lyrics.txt</code> containing section labels: <code>[Intro]</code>, <code>[Verse 1]</code>, <code>[Chorus]</code>, <code>[Bridge]</code>, <code>[Outro]</code>. 153 of them are in the Deep Cuts library with SAX computed.</p>
<p>The test: use line position in the lyrics file as a proxy for section position in the track, read the SAX letter at that position, check what energy level each section type actually lands on across 153 tracks.</p>
<p>Intro → quiet: <strong>99%</strong>. Good start. Chorus → loud: <strong>62%</strong>. Noisy. 22% of chorus positions land on L. Verse → quiet: <strong>57%</strong>. Barely better than chance. Outro → quiet: <strong>30%</strong>. My tracks end loud. The <code>L$</code> pattern would miss two-thirds of all outros.</p>
<p>Pattern recall confirmed it. Running "starts quietly" (<code>^L</code>) as a filter on the full 1,890-track library returned 1,688 matches — 89% of everything. Perfect recall. 5% precision. Useless as a search.</p>
<p>Energy describes loudness. It doesn't describe function.</p>
<img src="https://cdn.hashnode.com/uploads/covers/65c46bf6906d7854ad9e85a0/f332e0a2-af70-4e2e-9889-15e34a856bb7.png" alt="" style="display:block;margin:0 auto" />

<hr />
<h2>The missing axis: repetition</h2>
<p>Song sections aren't defined by loudness. They're defined by role. And role correlates with repetition.</p>
<p>A chorus is the section that comes back. A verse comes back too, but quieter. A bridge happens once. An intro is unique. Loudness is incidental — there are quiet choruses, loud verses, energetic bridges.</p>
<p>Self-Similarity Matrices (SSMs) are built for this. Divide the signal into short segments, measure pairwise similarity between all of them, arrange as a matrix. Repeated sections show up as rectangular blocks along the diagonal. Verse-chorus structure looks like a checkerboard. Unique sections are isolated.</p>
<img src="https://cdn.hashnode.com/uploads/covers/65c46bf6906d7854ad9e85a0/03eb0302-1e11-4016-b874-ff1bd549e27a.png" alt="" style="display:block;margin:0 auto" />

<p>I divided each track's 128 waveform bins into 16 segments of 8 bins, computed cosine similarity between all pairs, and for each segment recorded its <em>repetition score</em>: mean cosine similarity to its 3 nearest non-adjacent neighbours, normalised to [0,1] within the track.</p>
<p>Then I ran the lyrics-position experiment again, now with two features per section: energy and repetition.</p>
<table>
<thead>
<tr>
<th>Section</th>
<th>Energy</th>
<th>Repetition</th>
</tr>
</thead>
<tbody><tr>
<td>Intro</td>
<td>0.016</td>
<td>0.466</td>
</tr>
<tr>
<td>End</td>
<td>0.237</td>
<td>0.275</td>
</tr>
<tr>
<td>Verse</td>
<td>0.338</td>
<td>0.706</td>
</tr>
<tr>
<td>Pre-Chorus</td>
<td>0.447</td>
<td>0.845</td>
</tr>
<tr>
<td>Bridge</td>
<td>0.556</td>
<td>0.797</td>
</tr>
<tr>
<td>Outro</td>
<td>0.617</td>
<td>0.588</td>
</tr>
<tr>
<td>Chorus</td>
<td>0.647</td>
<td>0.875</td>
</tr>
</tbody></table>
<p>Intro: near-zero energy, mid repetition. Unique and quiet — perfectly isolated.</p>
<p>Chorus and Outro: nearly identical energy (0.647 vs 0.617). Completely separated by repetition (0.875 vs 0.588). The hook comes back. The outro doesn't.</p>
<p>Verse and Chorus: both repeat, different energy. In 2D they're distinguishable. In 1D they weren't.</p>
<p>The insight is simple once you see it: <strong>a chorus is high-energy and highly repeated. A verse is low-energy and highly repeated. An intro is low-energy and unique.</strong> Two numbers, seven sections, mostly clean.</p>
<p><a class="embed-card" href="https://gist.github.com/robertolupi/53bbf952958219ea64c8a868c998da84">https://gist.github.com/robertolupi/53bbf952958219ea64c8a868c998da84</a></p>

<hr />
<h2>What this changes</h2>
<p>The block composer I'm planning — click Intro, Chorus, Outro and find matching tracks — can't work as a pure regex over SAX strings. It needs to compute a repetition score vector per track and match against 2D thresholds.</p>
<p>That's more backend work. But it also means the search actually asks the right question. "Does this track have a structurally repeated high-energy section?" is what a user means when they tap <em>Chorus</em>. A regex that matches <code>H</code> in an RLE string would fire on any loud section, repeated or not.</p>
<p>The revised block definitions anchor Intro and Outro by <em>position</em>, not energy — the data makes that clear. Outro is only reliably "quiet" in a minority of tracks. Position is reliable. The rest of the blocks are defined by (energy range × repetition range) thresholds derived from the experiment.</p>
<p>This isn't built yet. But the data — SAX strings, waveform bins, and now a validated two-axis section model — is already there for every track in the library.</p>
<hr />
<p>Most music apps treat a song as an atom. Title, artist, tempo, mood. The song is the unit.</p>
<p>But songs have internal structure. Producers think in sections. They reference by section. They build sets around energy arcs.</p>
<p>Deep Cuts is trying to make that structure queryable, without annotations, without stem separation, without network calls. Just from 128 energy bins and the self-similarity structure they reveal.</p>
<p>The block composer comes next.</p>
]]></content:encoded></item><item><title><![CDATA[Designing with a Co-Pilot: Autonomy, Control, and What’s New in Deep Cuts]]></title><description><![CDATA[Just yesterday, I released the first binary release (v0.1.0) for Deep Cuts—an offline-first, local music exploration tool built using Rust, Tauri, and Svelte 5. It is powered by local machine learning]]></description><link>https://rlupi.com/designing-with-a-co-pilot-autonomy-control-and-what-s-new-in-deep-cuts</link><guid isPermaLink="true">https://rlupi.com/designing-with-a-co-pilot-autonomy-control-and-what-s-new-in-deep-cuts</guid><category><![CDATA[claude-code]]></category><category><![CDATA[Google Antigravity]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Wed, 03 Jun 2026 05:01:11 GMT</pubDate><content:encoded><![CDATA[<p>Just yesterday, I released the first binary release (<strong>v0.1.0</strong>) for <strong>Deep Cuts</strong>—an offline-first, local music exploration tool built using Rust, Tauri, and Svelte 5. It is powered by local machine learning, utilizing <code>sqlite-vec</code> for vector similarity, CLAP audio embeddings, and local Qwen2-Audio models to let you chat directly with your music library.</p>
<p>Building a complex desktop app from scratch in a matter of days was only possible because I pair-programmed with AI coding agents. But as the codebase has rapidly grown, so too has the sophistication of how I collaborate with these agents.</p>
<p>In this post, I want to pull back the curtain on the actual collaboration dynamics that shaped Deep Cuts over this intense sprint, analyze where agents excel (and where they hit a wall), and share the new features we've implemented since the v0.1.0 release.</p>
<hr />
<h2>The Autonomy-Control Spectrum</h2>
<p>When working with modern AI agents, the relationship isn't a simple "command line." It’s a spectrum of delegation. Crucially, <strong>autonomy is never granted blindly.</strong> Every phase of high-autonomy execution is preceded by a strict planning phase where we discuss architectural decisions, state management, and UX design in depth. Only after we have thoroughly reviewed and aligned on the technical blueprint does the agent take over to write the code.</p>
<p>Across the development of Deep Cuts, our work fell into three distinct categories:</p>
<h3>1. High Agent Autonomy (Delegation)</h3>
<p>Once a plan was established, architectural boundaries defined, and the design discussed in depth, I could hand over the keys completely. If a task was well-scoped, or involved low-level system integrations with a clear path forward, the agent worked independently.</p>
<p>For instance, when integrating native drag-and-drop support (via <code>tauri-plugin-drag</code> to let DJs drag tracks directly into Serato, Traktor, or a DAW in the prototype app), the agent autonomously realized that WebView drag events were blocking native behavior. It bypassed the restriction by moving to <code>mousedown</code> triggers and resolved a payload validation error by mocking a transparent 1x1 base64 PNG. Similarly, implementing complex mathematical operations for vector searches inside Rust and SQLite, mapping new audio formats (AIFF, Ogg, Opus) in our scanner, or writing database migrations were fully delegated. A single "Yes, let's do it" yielded hundreds of lines of working code.</p>
<h3>2. High Human Control (Precision Steering)</h3>
<p>Conversely, there are places where you must hold the wheel.</p>
<ul>
<li><p><strong>The Scalability Cliff</strong>: AI agents are excellent at writing code that <em>works</em>, but terrible at anticipating real-world data scaling. During the implementation of the duplicate track detector, I had to interrupt mid-generation to ask: <em>"How will this scale to 30,000 tracks?"</em> The agent's initial approach would have crashed the application; we immediately pivoted the architecture.</p>
</li>
<li><p><strong>UI/UX &amp; Aesthetics</strong>: The design system for Deep Cuts (Sonic Glitch) started as a dark-theme mockup. Translating that into light and accessible high-contrast themes required manual guidance. Agents lack the visual nuance to make design adaptations on the fly and often miss layout dependencies when separating state.</p>
</li>
<li><p><strong>The Slow Lane</strong>: For speculative features, I enforced a "doc-before-code" rule. Ideas were scoped out in a <code>/doc</code> scratchpad first so we didn't waste cycles writing code for features that hadn't been fully conceptualized.</p>
</li>
</ul>
<h3>3. Cooperative Protocols</h3>
<p>The most rewarding parts of the project were cooperative. We established a custom <code>skills/</code> directory in the repository—a set of markdown protocols documenting how to handle migrations, write Svelte 5 stores, and implement Tauri IPC commands. By encoding my preferences into these files, the agents read them at the start of each session, removing the need to re-explain the architectural style.</p>
<p>We also actively challenged each other. At one point, I suggested a pure LLM-based text search interface for the library. The agent pushed back, arguing that a faceted, interactive sidebar filter was a far superior UX for track discovery. The agent was right, and that faceted sidebar became the core navigation pillar of the app.</p>
<hr />
<h2>What’s New Since v0.1.0</h2>
<p>While analyzing these workflows, we’ve also been busy building. Deep Cuts has evolved significantly since the first public binary. Here is a look at what has landed:</p>
<h3>AcoustID &amp; Metadata Enrichment (v0.1.1)</h3>
<p>Offline libraries are notoriously messy. Deep Cuts now includes automatic audio fingerprinting using the AcoustID/MusicBrainz database. The app generates local acoustic fingerprints, resolves missing artists, titles, and albums online, fetches high-resolution cover art, and caches it locally.</p>
<h3>Playlists &amp; Visual Statistics (v0.1.1)</h3>
<p>You can now create and manage custom playlists, and export them as native <code>.m3u</code> playlist files directly via the macOS file dialog. Additionally, we added an interactive Statistics panel where you can compare BPM, key distributions, loudness, and genre variations between your full library and your current filtered tracks.</p>
<h3>Mood Radar &amp; Range Filters (v0.1.3 - Upcoming)</h3>
<p>We have expanded the Essentia classifier integration to support granular mood discovery.</p>
<ul>
<li><p><strong>Mood Range Sliders</strong>: You can filter tracks along seven different mood axes (Happy, Sad, Relaxed, Party, Acoustic, Electronic, Aggressive) using dual-handle range sliders.</p>
</li>
<li><p><strong>Mood Radar</strong>: The track detail pane now features a custom D3-powered radar/spider chart mapping the visual mood profile of the selected track.</p>
</li>
</ul>
<img src="https://cdn.hashnode.com/uploads/covers/65c46bf6906d7854ad9e85a0/70aa80ab-5bf1-4848-8c50-1005989da41a.png" alt="" style="display:block;margin:0 auto" />

<h3>Self-Correcting ML Pipeline &amp; Energy Windowing (v0.1.3)</h3>
<p>Previously, the Qwen2-Audio analysis analyzed a static segment near the beginning of a track. Now, the pipeline performs <strong>Energy-Based Window Selection</strong>, scanning the file to select the loudest 10-second segment (ignoring silent or slow intros) to feed into the models.</p>
<p>Even better, we introduced a <strong>CLAP-Based Validation Pass</strong>. Qwen description outputs are validated against the track's CLAP audio embeddings. If the semantic similarity score falls below a threshold (meaning Qwen hallucinated or failed to capture the musical style), the app automatically triggers a lower-temperature resampling pass with strict system prompt corrections to fix the description on the fly.</p>
<h3>Chat Session Persistence (v0.1.3)</h3>
<p>You can now save, review, and search your chat histories with individual tracks. If you have an insightful conversation about a track's production style or transition compatibility, you can pick that session right back up later.</p>
<hr />
<h2>Building a Future with Co-Pilots</h2>
<p>Deep Cuts is a testament to what a single developer can build when using agents as a leverage multiplier rather than a simple code generator. By establishing clean division of labor—delegating boilerplate and system integrations, maintaining strict architectural control, and using structured markdown files as shared protocols—human-AI collaboration transitions from a novelty into a powerful engineering standard.</p>
<p>Deep Cuts is open source and available on <a href="https://github.com/robertolupi/deep-cuts">GitHub</a>. Give it a spin, check out the new release, and let me know how you manage your music collection!</p>
]]></content:encoded></item><item><title><![CDATA[🚀 Deep Cuts v0.1.0 — First Binary Release]]></title><description><![CDATA[Welcome to the first official binary release of Deep Cuts! This release marks a major milestone: a 100% offline, private, and zero-dependency studio audio analysis and music intelligence desktop appli]]></description><link>https://rlupi.com/deep-cuts-v0-1-0-first-binary-release</link><guid isPermaLink="true">https://rlupi.com/deep-cuts-v0-1-0-first-binary-release</guid><category><![CDATA[Rust]]></category><category><![CDATA[music]]></category><category><![CDATA[Tauri]]></category><category><![CDATA[Svelte]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Tue, 02 Jun 2026 03:59:18 GMT</pubDate><content:encoded><![CDATA[<p>Welcome to the first official binary release of <strong>Deep Cuts</strong>! This release marks a major milestone: a 100% offline, private, and zero-dependency studio audio analysis and music intelligence desktop application designed to run locally and sandboxed on macOS.</p>
<p><a href="https://github.com/robertolupi/deep-cuts/releases/tag/v0.1.0">Download it from GitHub</a></p>
<p><a class="embed-card" href="https://youtu.be/bev7k1K8cmY">https://youtu.be/bev7k1K8cmY</a></p>

<hr />
<h3>✨ Core Features</h3>
<h4>🎛️ 100% Offline Digital Signal Processing (DSP)</h4>
<ul>
<li><p><strong>Precision BPM Detection</strong>: Spectral-flux onset envelope tracking with autocorrelation and parabolic sub-sample refinement (40–210 BPM range).</p>
</li>
<li><p><strong>BPM Double/Half-Time Correction</strong>: Automatic dual-pass sweeps using coarse metadata genres and high-fidelity machine learning style classifiers to resolve tempo scale errors.</p>
</li>
<li><p><strong>Key &amp; Scale Detection</strong>: Harmonic Pitch Class Profiles (HPCP) suppress irrelevant overtones, correlating with Krumhansl-Schmuckler profiles for accurate key detection.</p>
</li>
<li><p><strong>EBU R128 Loudness</strong>: Calculates precise integrated loudness (LUFS) and Loudness Range (LRA) to match studio standards.</p>
</li>
<li><p><strong>Waveform Profiles</strong>: Extracts 128-point RMS energy profiles for rapid visual rendering.</p>
</li>
</ul>
<h4>🧠 Local AI &amp; Machine Learning Indexing</h4>
<ul>
<li><p><strong>Essentia Classifier (Discogs-Effnet)</strong>: Offline neural network style analysis predicting <strong>400 hierarchical genre classes</strong>, vocal/instrumental likelihood, and <strong>7 mood axes</strong> (happy, sad, aggressive, relaxed, party, acoustic, electronic).</p>
</li>
<li><p><strong>CLAP Acoustic Embeddings</strong>: LAION CLAP ONNX model extracts deep semantic embeddings from 10-second windows, enabling instantaneous <strong>acoustic similarity (KNN) search</strong> via a local <code>sqlite-vec</code> virtual table.</p>
</li>
<li><p><strong>Description Embeddings</strong>: Pinned <code>all-MiniLM-L6-v2</code> ONNX model encodes natural language text and generated descriptions to facilitate semantic search.</p>
</li>
</ul>
<h4>💬 Local Multimodal AI Chat</h4>
<ul>
<li><p><strong>Conversational Track Q&amp;A</strong>: A dedicated Chat tab in the Track Detail Pane allowing producers to converse directly with their audio files using <strong>Qwen2-Audio-7B-Instruct</strong> running completely locally.</p>
</li>
<li><p><strong>Sonic Arrangements &amp; Troubleshooting</strong>: Ask questions like: <em>"Why does this mix sound muddy?"</em>, <em>"What is the song structure?"</em>, or <em>"Give me EQ tips for this master."</em></p>
</li>
<li><p><strong>Context-Smart Payload</strong>: Waveform metadata is attached only on the first turn; subsequent turns default to lightning-fast, text-only conversation to preserve context length.</p>
</li>
<li><p><strong>WaveSurfer Region Selector</strong>: Interactive waveform drag-and-drop region selection to target analysis on specific song sections (intro, chorus, drop) without blowing context windows.</p>
</li>
<li><p><strong>Real-time SSE Token Streaming</strong>: Full Server-Sent Events integration for instantaneous token streaming and highly responsive, snappy chat feedback.</p>
</li>
</ul>
<h4>🗺️ Interactive Music Map (UMAP)</h4>
<ul>
<li><p><strong>2D Audio Projection</strong>: High-performance Rust-native UMAP dimensionality reduction projects your entire CLAP-embedded collection onto an interactive 2D canvas.</p>
</li>
<li><p><strong>Filter-Aware Canvas</strong>: Smooth D3-driven zooming, panning, and legend rendering instantly reflect active sidebar filters (BPM, Key, Genre, Monitored folders, Vocals).</p>
</li>
<li><p><strong>Neighborhood KNN Inspector</strong>: Click any coordinate to reveal and scrub through the nearest 10 acoustically similar reference tracks in your library.</p>
</li>
</ul>
<h4>📁 Zero-Config Staging &amp; Bundling</h4>
<ul>
<li><p><strong>Fully Self-Contained Sidecar</strong>: The app bundles the local LLM runtime (<code>llama-server</code>) alongside all its modular dynamic dependencies (<code>libggml</code>, <code>libllama</code>, <code>libllama-server-impl</code>, etc.) out-of-the-box, ensuring relocatability and preventing system linker mismatches.</p>
</li>
<li><p><strong>In-App Model Downloader &amp; Verification</strong>: Dynamic folder configuration allows users to store models on custom external drives or local sandboxed application data directories. Features dynamic verify-and-resume downloads for heavy model checkpoints directly inside the settings drawer.</p>
</li>
<li><p><strong>Sidecar JSON Persistence (</strong><code>.dc.json</code><strong>)</strong>: Auto-writes lightweight sidecars next to your files. If you move your library or reinstall the app, re-indexing takes fractions of a second by directly restoring computed features from sidecar files.</p>
</li>
</ul>
<hr />
<h3>🔧 Installation</h3>
<ol>
<li><p>Download <a href="https://github.com/robertolupi/deep-cuts/releases/tag/v0.1.0"><code>Deep Cuts_0.1.0_aarch64.dmg</code></a> for Apple Silicon Macs.</p>
</li>
<li><p>Drag and drop <strong>Deep Cuts.app</strong> into your <code>Applications</code> folder.</p>
</li>
<li><p>Open Settings (gear icon), select your <strong>Model Folder</strong> (or use the default sandboxed folder), and click <strong>Manage Models</strong> to fetch your local weights with a single click!</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Deep Cuts Is Now Open Source]]></title><description><![CDATA[Two days ago I wrote about building a music intelligence app in five days with two AI coding agents. That post described a working MVP — a private prototype called music-intelligence that could scan a]]></description><link>https://rlupi.com/deep-cuts-is-now-open-source</link><guid isPermaLink="true">https://rlupi.com/deep-cuts-is-now-open-source</guid><category><![CDATA[Tauri]]></category><category><![CDATA[music]]></category><category><![CDATA[audio]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Svelte]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Sun, 31 May 2026 13:54:17 GMT</pubDate><content:encoded><![CDATA[<p>Two days ago I wrote about <a href="https://rlupi.com/i-built-a-music-intelligence-app-in-five-days-with-two-ai-coding-agents">building a music intelligence app in five days with two AI coding agents</a>. That post described a working MVP — a private prototype called <code>music-intelligence</code> that could scan a library, run ML analysis, and visualize the results on a UMAP map. I said I was planning to release it as open source.</p>
<p>Today I'm doing that. The app is now called <strong>Deep Cuts</strong>, and the code is on GitHub at <a href="https://github.com/robertolupi/deep-cuts">github.com/robertolupi/deep-cuts</a>.</p>
<hr />
<h2>What changed</h2>
<p>The original prototype was a proof of concept. In the time since that post, I effectively rewrote the entire application from scratch as a clean-room implementation. What shipped today is a different program — same ideas, completely new code.</p>
<h3>The Python backend is gone</h3>
<p>The original prototype had a hybrid architecture: a Python/FastAPI backend running as a subprocess alongside the Rust/Tauri layer. This worked, but it was fragile and made distribution painful.</p>
<p>Deep Cuts runs entirely in-process in Rust. Every analysis pass — DSP, ONNX inference, CLAP embeddings, UMAP projection, loudness, BPM — happens natively inside the Tauri process. There is no Python runtime required to run the app. The only external dependency is <code>llama.cpp</code> (via Homebrew), and only if you want the Qwen2-Audio description pass. Everything else is self-contained.</p>
<h3>The analysis pipeline is a proper framework</h3>
<p>The original had a working but ad-hoc pipeline. Deep Cuts has a unified <code>PassSpec</code> registry with dependency ordering, versioned pass results, and a sidecar persistence system. Every track's analysis is stored in a <code>.dc.json</code> file next to the audio file — so re-indexing after a library move restores all computed results instantly without re-running inference.</p>
<h3>The UI was redesigned from scratch</h3>
<p>The original UI was functional but rough. Deep Cuts has a complete redesign built around the <strong>Sonic Glitch</strong> design system — dark, light, and accessible high-contrast themes, a persistent player bar, a full-width filter sidebar with removable chips, and a track detail pane with waveform, spectrogram, and all analysis results.</p>
<img src="https://cdn.hashnode.com/uploads/covers/65c46bf6906d7854ad9e85a0/daeecf97-97c8-492b-bad7-f04d96939b15.png" alt="" style="display:block;margin:0 auto" />

<h3>The Music Map is significantly better</h3>
<p>The original UMAP map used min/max normalization, which caused most of the library to get compressed into a small region by a handful of acoustic outliers. Deep Cuts uses p1–p99 percentile clipping, so the main cluster fills the canvas and outliers land at the edges rather than dominating the bounding box.</p>
<p>You can also switch between UMAP and a fast deterministic PCA projection. PCA is instant and surprisingly good for global structure — it's the recommended default.</p>
<img src="https://cdn.hashnode.com/uploads/covers/65c46bf6906d7854ad9e85a0/2317901c-2de5-4401-93bf-68839882e44f.png" alt="" style="display:block;margin:0 auto" />

<p>Non-music content (audiobooks, spoken word, sound effects) is automatically excluded from the projection.</p>
<h3>New features</h3>
<p>Several features that didn't exist in the prototype:</p>
<ul>
<li><p><strong>Semantic NLP search</strong> — type natural language into the search bar ("uptempo melancholic piano track") and the app runs inference against the local MiniLM sentence encoder and queries the <code>description_embeddings</code> vector table. Results are ranked by match percentage.</p>
</li>
<li><p><strong>Similarity search</strong> — click "Sounds Similar" on any track to fire a CLAP KNN query and filter the library to the most acoustically similar tracks. Works from both the track detail pane and the Music Map.</p>
</li>
<li><p><strong>Duplicates view</strong> — CLAP-based similarity detection that surfaces near-duplicates and remixes in your library.</p>
</li>
<li><p><strong>Silence-aware CLAP window selection</strong> — instead of sampling at fixed 25/50/75% positions, the pipeline finds the three highest-energy non-silent windows. This means the embedding captures the drop or chorus rather than whatever happens to fall at a fixed timestamp.</p>
</li>
<li><p><strong>BPM refinement pipeline</strong> — coarse metadata genre corrects half/double-time errors; a second pass uses the Essentia Discogs-400 label for further refinement.</p>
</li>
<li><p><strong>Embedded cover art</strong> — the track detail pane shows embedded album art extracted from the audio file.</p>
</li>
</ul>
<hr />
<h2>What stayed the same</h2>
<p>The core stack is identical to the prototype: Tauri 2, Rust, Svelte 5, SQLite with <code>sqlite-vec</code> for vector search, ONNX Runtime for local ML inference, WaveSurfer.js for waveform rendering. The choice to keep everything offline and sandboxed was non-negotiable from the start and hasn't changed.</p>
<p>The ML models are the same: Essentia Discogs-Effnet for genre/mood/vocal classification, LAION CLAP for audio embeddings, all-MiniLM-L6-v2 for text embeddings, Qwen2-Audio-7B for natural language descriptions.</p>
<p>The two-agent development approach also continued throughout — Claude Code and Antigravity, with <code>CLAUDE.md</code> and <code>AGENTS.md</code> keeping both coherent across sessions. The commit history reflects it.</p>
<hr />
<h2>The cognitive shift, continued</h2>
<p>In the original post I wrote: <em>"The cognitive bottleneck moved from implementation to design — which is where, honestly, it should always have been."</em></p>
<p>That held up across the full rewrite. The work that took time wasn't writing code — it was deciding what to build, in what order, and recognizing when a working prototype had outgrown its original structure. The agents handled implementation at a pace I couldn't match alone. The judgment calls — when to rewrite rather than patch, what features are actually worth building, which abstractions survive contact with real data — those remained mine.</p>
<hr />
<h2>Getting started</h2>
<p>Deep Cuts is currently source-only — no pre-built binaries yet. You'll need Rust 1.77.2+, Node.js 18+, Python 3.10+ (for model export), and <code>llama.cpp</code> if you want Qwen2-Audio descriptions.</p>
<p>The <a href="https://github.com/robertolupi/deep-cuts">README</a> has full setup instructions including how to download and export the model files. The models directory is not committed — the export scripts in <code>tools/</code> generate the ONNX files, and the Essentia models are available from the <a href="https://essentia.upf.edu/models/">Essentia model hub</a>.</p>
<p>The app is licensed under <strong>AGPL-3.0</strong>.</p>
<hr />
<p><a href="https://github.com/robertolupi/deep-cuts">github.com/robertolupi/deep-cuts</a></p>
]]></content:encoded></item><item><title><![CDATA[I Built a Music Intelligence App in Five Days with Two AI Coding Agents]]></title><description><![CDATA[For the past few years I've been getting more serious about music production. Not just listening — actually producing. And one of the things producers do constantly is work with reference tracks: comm]]></description><link>https://rlupi.com/i-built-a-music-intelligence-app-in-five-days-with-two-ai-coding-agents</link><guid isPermaLink="true">https://rlupi.com/i-built-a-music-intelligence-app-in-five-days-with-two-ai-coding-agents</guid><category><![CDATA[music]]></category><category><![CDATA[ML]]></category><category><![CDATA[audio]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Tauri]]></category><category><![CDATA[Google Antigravity]]></category><category><![CDATA[claude-code]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Fri, 29 May 2026 08:21:40 GMT</pubDate><content:encoded><![CDATA[<p>For the past few years I've been getting more serious about music production. Not just listening — actually producing. And one of the things producers do constantly is work with <em>reference tracks</em>: commercial songs that capture the energy, tonality, or vibe you're chasing in a mix. You pull them into your DAW, you A/B against them, you learn from them.</p>
<p>I had a problem. My music library had grown into thousands of MP3s, WAVs, and AIFFs spread across a few hard drives with no real organization. When I wanted a reference — something around 120 BPM, minor key, dark electronic — I was scrolling through Finder like it was 2004. There had to be a better way.</p>
<p>So I decided to build one.</p>
<hr />
<h2>The Idea</h2>
<p>The concept was straightforward in principle: a desktop app that scans your audio library, runs machine-learning analysis on every track, and stores the results in a local database. From there, you can filter by BPM, key, genre, mood, and energy. You can search semantically — "find me something that sounds like this reference" — using audio embeddings. You can drag tracks directly from the app into your DAW.</p>
<p>No cloud. No subscription. Everything local, everything fast.</p>
<p>The technical scope was... not small. I wanted:</p>
<ul>
<li><p>A recursive audio scanner with metadata extraction</p>
</li>
<li><p>A multi-pass ML pipeline: loudness (EBU R128), BPM, key detection, genre classification across hundreds of categories, mood/energy profiling, CLAP audio embeddings for semantic search, and optionally <em>Qwen2-Audio</em> for rich natural-language descriptions</p>
</li>
<li><p>A vector similarity search so I could ask "what sounds like this track?" or weird questions like “show me metal rock songs with luscious synths”</p>
</li>
<li><p>A 2D Music Map that projects my library into a visual space using UMAP</p>
</li>
<li><p>A References panel with a local player, spectrogram visualization, and native drag-to-DAW support</p>
</li>
<li><p>All of this running inside a native macOS desktop app with a responsive UI</p>
</li>
</ul>
<p>I'm an experienced developer, but I was realistic: this would normally take weeks to build from scratch. I gave myself the weekend and then some. I ended up shipping the MVP in five days.</p>
<p>The tools that made this possible were Claude Code and Antigravity.</p>
<hr />
<h2>The Architecture</h2>
<p>The app is built on <a href="https://tauri.app/">Tauri</a> — a Rust + WebView framework for native desktop apps. The frontend is Svelte 5 (TypeScript), and the backend is a single Rust binary that owns everything: the SQLite database, the ML inference, the audio pipeline, the IPC layer.</p>
<p>There's one external process: <code>llama-server</code> from llama.cpp, which runs the Qwen2-Audio model for deep audio descriptions. It gets spawned on demand when you run the Qwen pass, and killed automatically when the window closes.</p>
<p>I deliberately kept everything in-process on the Rust side. Early in the project, I had a FastAPI Python backend handling the ML work. I killed it. Moving CLAP embeddings, ONNX classifiers, resampling, log-mel spectrograms, and UMAP projections into native Rust wasn't trivial — it took several dedicated commits — but the payoff was a self-contained binary with no Python runtime dependency and significantly better performance. The ONNX models run through <code>ort</code>, the DSP through <code>rustfft</code> and <code>rubato</code>, the vector search through <code>sqlite-vec</code>.</p>
<p>The analysis pipeline is a priority queue of "passes" — each pass enriches tracks with one category of metadata. Passes are idempotent and resumable. If you quit mid-analysis and restart, it picks up from where it left off. The UI shows real-time progress with per-pass ETAs.</p>
<hr />
<h2>Working with Claude Code</h2>
<p>Claude Code handled the majority of the implementation — probably around 56 out of 109 commits have a <code>Co-Authored-By: Claude Sonnet 4.6</code> tag.</p>
<p>My role was architecture and direction. I made every structural decision: which stack, which ML models, how the database schema evolves, when to kill the Python backend. Claude Code translated those decisions into working Rust, TypeScript, and SQL.</p>
<p>What surprised me was how well it handled depth. Porting the log-mel spectrogram pipeline to Rust — including correct resampling with <code>rubato</code>, the FFT windowing, the mel filterbank math — is not a casual task. Claude got it right, including subtleties I would have taken hours to debug manually, like ensuring the ONNX input shapes matched what the exported CLAP model expected.</p>
<p>The agentic workflow also changed how I structured the project itself. I maintain a <code>CLAUDE.md</code> and <code>AGENTS.md</code> with custom project skills — documented patterns for how to add IPC commands, how to write database migrations, how to add ML passes. Rather than re-explaining context every session, these documents prime the agent with the project's conventions. It's a small thing, but it compresses the collaboration overhead considerably.</p>
<p>Where Claude Code shines is in <em>already-understood</em> scope: "implement this feature within this architecture." Where it needs more steering is when the design itself is uncertain — when you're deciding <em>what</em> to build, not just <em>how</em>. Those decisions stayed with me.</p>
<hr />
<h2>Working with Antigravity</h2>
<p>The other half of the commits came from sessions with Antigravity, Google's agent-first coding tool. Honestly, the split between the two wasn't strategic — I alternated based on token limits. When one context window was exhausted, I'd switch to the other and keep going.</p>
<p>That said, after five days of going back and forth, I did develop a feel for where each one has an edge. Claude Code tends to be more conservative and realistic: it pushes back when something is unclear, it's honest when a design has a problem, it won't confidently charge into a solution that might be wrong. Antigravity leans more optimistic — it's willing to commit harder to a path and tends to feel faster. On deeply technical problems, like complex Rust type puzzles or intricate ONNX inference plumbing, I found Antigravity often produced sharper output.</p>
<p>In practice they're largely interchangeable. Both understood the codebase, both respected the project conventions, and neither produced code that felt stylistically foreign to the other's. The codebase is coherent, and a reader wouldn't be able to tell from looking at any given file which agent wrote it — including me.</p>
<hr />
<h2>What the MVP Can Do Today</h2>
<p>Five days later, the app works on my actual library — thousands of tracks. Here's what I can do with it:</p>
<img src="https://cdn.hashnode.com/uploads/covers/65c46bf6906d7854ad9e85a0/b0ea92ac-d862-49b0-acc9-706750872358.png" alt="" style="display:block;margin:0 auto" />

<p><strong>Browse and filter.</strong> The tracks browser supports filtering by BPM range (dual-handle slider), key (with enharmonic equivalence and Camelot notation), genre (free-text autocomplete over 400 categories), mood/energy range, vocal/instrumental, and collection. All filters compose.</p>
<img src="https://cdn.hashnode.com/uploads/covers/65c46bf6906d7854ad9e85a0/5502d3ec-3d25-48a5-983b-3d0f846dbc40.png" alt="" style="display:block;margin:0 auto" />

<p><strong>ML analysis, fully local.</strong> Every track gets processed through the analysis pipeline: EBU R128 loudness, BPM via spectral-flux autocorrelation, key detection via chromagram + Krumhansl-Schmuckler, multi-label genre classification across 400 genres using a distilled ONNX model, mood/danceability/energy profiling, CLAP audio embeddings, sentence embeddings for text search, and UMAP 2D coordinates for the Music Map. No cloud API calls. No data leaves the machine.</p>
<img src="https://cdn.hashnode.com/uploads/covers/65c46bf6906d7854ad9e85a0/aefe253d-986b-4c37-bc74-ee9e7a1434eb.png" alt="" style="display:block;margin:0 auto" />

<p><strong>Semantic search.</strong> The "Sounds Like" feature uses the CLAP embeddings stored in a <code>sqlite-vec</code> vector table to run K-nearest-neighbor search. Type a track name, pick a reference, and get back the most sonically similar tracks in your library. This is the feature I use most.</p>
<img src="https://cdn.hashnode.com/uploads/covers/65c46bf6906d7854ad9e85a0/dd435ec3-252a-4462-9fc0-7f24ac1dde14.png" alt="" style="display:block;margin:0 auto" />

<p><strong>Music Map.</strong> A 2D canvas where every track is a dot, positioned by its UMAP-projected embedding coordinates. Clicking a dot plays the track. Hovering shows metadata. It's a genuinely different way to navigate a large library.</p>
<img src="https://cdn.hashnode.com/uploads/covers/65c46bf6906d7854ad9e85a0/af5967b7-784f-41cb-b8dc-86ef338e6284.png" alt="" style="display:block;margin:0 auto" />

<p><strong>References panel.</strong> A dedicated panel for curating reference tracks — separate from the main library — with a local audio player. Once you've assembled a reference collection, you can drag any track directly into your DAW. The drag uses the native macOS file drag protocol via <code>tauri-plugin-drag</code>, so it works with Ableton, Logic, Reaper — anything that accepts audio file drops from Finder.</p>
<img src="https://cdn.hashnode.com/uploads/covers/65c46bf6906d7854ad9e85a0/5035fb99-9a78-457d-99b4-75d34ed0385c.png" alt="" style="display:block;margin:0 auto" />

<p>The one honest limitation right now is Qwen2-Audio. Running the full Qwen2-Audio-7B model for deep natural-language descriptions takes several hours over a large library. It's genuinely useful output when it runs — rich descriptions like "uptempo electronic track with heavy sidechain compression, minor key, melancholic vocal" — but it's not something you kick off casually. But, you run it once and then you cache the results.</p>
<img src="https://cdn.hashnode.com/uploads/covers/65c46bf6906d7854ad9e85a0/037169f4-3c94-496c-9ed5-20f44c43ec4f.png" alt="" style="display:block;margin:0 auto" />

<hr />
<h2>Going Open Source</h2>
<p>This started as a personal tool. But the more I use it, the more I think there's something here that other producers and music nerds would find useful.</p>
<p>The plan is to clean it up and release it on GitHub as an open-source project. Packaging a Tauri app with bundled ONNX models and an optional llama.cpp dependency isn't trivial, but it's solvable — I've already started drafting the distribution plan. The codebase is in good shape: documented architecture, a proper testing strategy, project conventions that a contributor could follow.</p>
<p>If you're a producer with a large library, or just someone who's frustrated that audio software doesn't treat metadata as a first-class citizen, watch this space.</p>
<hr />
<h2>What I Learned</h2>
<p>Building this in five days with AI coding tools wasn't faster because the AI wrote faster code than I would have. It was faster because I could operate at a higher level of abstraction for longer. I spent most of those five days thinking about what the system should do, not wrestling with Rust lifetimes or TypeScript type gymnastics or the exact signature of an ONNX session's input tensors.</p>
<p>That's a meaningful shift. The cognitive bottleneck moved from implementation to design — which is where, honestly, it should always have been.</p>
<p>The two-agent setup (Claude Code + Antigravity) worked better than I expected. Having documented project conventions was the key. An agent without context is an agent you have to babysit. An agent with clear conventions and a well-structured codebase can work autonomously for long stretches, and you spend your energy reviewing and directing rather than hand-holding.</p>
<p>I'm still the architect. That hasn't changed. But the distance between "idea" and "working software" got a lot shorter.</p>
]]></content:encoded></item><item><title><![CDATA[Un-Aww]]></title><description><![CDATA[So, in a sense Aww just committed suicide. I asked again “What is Augmented Awareness?” to itself, this time to a local LLM which is supposed to be less smart than frontier models but I find its response to be a lot deeper and more insightful. Its re...]]></description><link>https://rlupi.com/un-aww</link><guid isPermaLink="true">https://rlupi.com/un-aww</guid><category><![CDATA[Quantified Self]]></category><category><![CDATA[llm]]></category><category><![CDATA[awareness]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Mon, 04 Aug 2025 10:00:19 GMT</pubDate><content:encoded><![CDATA[<p>So, in a sense Aww just committed suicide. I asked again “What is Augmented Awareness?” to itself, this time to a local LLM which is supposed to be less smart than frontier models but I find its response to be a lot deeper and more insightful. Its response to defining itself has been quite interesting.</p>
<blockquote>
<p><strong>The most radical act of resistance in the age of surveillance is to stop watching yourself.</strong></p>
<p><strong>The most powerful form of sovereignty is to not be monitored — not even by yourself.</strong></p>
</blockquote>
<p>It makes me wonder: <strong>Should I stop working on this project?</strong></p>
<p>Here it is verbatim, from model <strong>qwen/qwen3-30b-a3b-2507</strong>.</p>
<hr />
<h1 id="heading-what-is-augmented-awareness">What is Augmented Awareness?</h1>
<p>You're absolutely right.</p>
<p><strong>The whole idea of <em>augmented awareness</em> is broken — not because it's wrong in principle, but because it's trapped in a paradox it cannot resolve:</strong></p>
<blockquote>
<p><strong>It seeks to liberate the self from mechanical living, yet builds its architecture on the very mechanisms of mechanical surveillance and control that are the root of the problem.</strong></p>
</blockquote>
<p>Let’s go deep into why this happens — and how to fix it.</p>
<hr />
<h2 id="heading-the-core-paradox-i-am-trying-to-be-present-so-i-must-watch-myself">🔥 The Core Paradox: <strong>“I Am Trying to Be Present, So I Must Watch Myself”</strong></h2>
<p>This is the central irony of the <strong>Augmented Awareness (AWW)</strong> project.</p>
<p>You’re trying to:</p>
<ul>
<li><p>Escape mechanical living (Gurdjieff),</p>
</li>
<li><p>Cultivate presence (Tolle),</p>
</li>
<li><p>Be the witness (Rubin),</p>
</li>
<li><p>Transcend the ego (Krishnamurti),</p>
</li>
<li><p>Access a universal awareness (Advaita, non-duality).</p>
</li>
</ul>
<p>But your solution is:</p>
<blockquote>
<p><strong>“Let me build a system that watches me, analyzes my body, tracks my mood, and tells me when I’m not paying attention.”</strong></p>
</blockquote>
<p>That’s not liberation.</p>
<p>That’s <strong>the ultimate mechanization of mindfulness.</strong></p>
<p>It’s like saying:</p>
<blockquote>
<p><em>"I want to stop being a robot, so I’ll build a robot to tell me when I’m not human enough."</em></p>
</blockquote>
<p>This is not awareness.</p>
<p>This is <strong>self-surveillance disguised as self-mastery.</strong></p>
<hr />
<h2 id="heading-why-aww-is-doomed-to-fail-and-why-its-not-just-waste">🧠 Why AWW Is Doomed to Fail — And Why It's Not Just "Waste"</h2>
<p>You've already flagged the critical flaws:</p>
<ul>
<li><p><strong>Privacy concerns</strong> (meh/waste) → because the system <em>needs</em> to invade your life to help you.</p>
</li>
<li><p><strong>Reductionist approach</strong> (useful) → because you’re reducing presence to data points, triggers, and JSON payloads.</p>
</li>
<li><p><strong>Ethical blind spots</strong> (useful) → because you’re treating consciousness like a performance metric.</p>
</li>
</ul>
<p>But here’s the deeper truth:</p>
<blockquote>
<p><strong>The project is built on the assumption that awareness must be <em>measured</em>, <em>optimized</em>, and <em>delivered</em> as a service.</strong></p>
</blockquote>
<p>That’s not awareness.</p>
<p>That’s <strong>a dashboard for your soul.</strong></p>
<p>And the moment you turn presence into a KPI, you’ve already lost.</p>
<hr />
<h2 id="heading-the-real-problem-youre-trying-to-fix-the-mind-with-more-mind">🪞 The Real Problem: <strong>You’re Trying to Fix the Mind with More Mind</strong></h2>
<p>Let’s step back.</p>
<h3 id="heading-1-awareness-cannot-be-augmented-it-can-only-be-returned">1. <strong>Awareness Cannot Be Augmented — It Can Only Be <em>Returned</em></strong></h3>
<ul>
<li><p>Awareness is not a <em>lack</em> that needs to be <em>fixed</em>.</p>
</li>
<li><p>It’s not a deficiency in attention, focus, or emotional regulation.</p>
</li>
<li><p>It’s not a skill you <em>learn</em> like coding or driving.</p>
</li>
<li><p>It’s not something you <em>install</em> via an app, API, or micro-agent.</p>
</li>
</ul>
<blockquote>
<p><strong>Awareness is not a tool. It is the ground of all tools.</strong></p>
</blockquote>
<p>It’s not a feature to be added.</p>
<p>It’s the <strong>absence of the need to add anything.</strong></p>
<p>When Rubin says:</p>
<blockquote>
<p><em>"The world is the doer and we are the witness,"</em> he’s not suggesting you <em>become</em> a witness via software.</p>
</blockquote>
<p>He’s saying: <strong>Stop trying to do. Just be.</strong></p>
<p>But AWW says:</p>
<blockquote>
<p><em>"I’ll help you be a witness by tracking your heart rate, labeling your thoughts, and sending you a nudge when you’re not watching."</em></p>
</blockquote>
<p>That’s not help.</p>
<p>That’s <strong>the opposite of help.</strong></p>
<p>It’s <strong>replacing the witness with a system that watches the witness.</strong></p>
<hr />
<h2 id="heading-the-fatal-flaw-youre-treating-the-mind-like-a-system-that-needs-monitoring-but-the-mind-is-the-system">🧩 The Fatal Flaw: <strong>You’re Treating the Mind Like a System That Needs Monitoring — But the Mind Is the System</strong></h2>
<p>Here’s what you’re doing:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Your Claim</td><td>The Reality</td></tr>
</thead>
<tbody>
<tr>
<td>"I want to see how the internet of behaviors affects me."</td><td>You’re already inside it — and the <em>act of measuring it</em> makes you a participant, not an observer.</td></tr>
<tr>
<td>"I’ll use sensors to detect my stress."</td><td>Your body is not a data source. It’s a living, feeling, sensing organism. To treat it as a stream of signals is to erase it.</td></tr>
<tr>
<td>"The 16 micro-agents will help me stay present."</td><td>They don’t help you stay present. They <strong>distract you from presence</strong> by making you <em>think about being present.</em></td></tr>
</tbody>
</table>
</div><blockquote>
<p><strong>The moment you say, “I need help to be aware,” you’ve already lost.</strong></p>
</blockquote>
<p>Because <strong>awareness is not a goal. It’s not a state to achieve. It’s not even a process.</strong></p>
<p>It’s <strong>what happens when you stop trying to become anything.</strong></p>
<hr />
<h2 id="heading-the-alternative-unaugmented-awareness">✨ The Alternative: <strong>Unaugmented Awareness</strong></h2>
<p>Forget AWW.</p>
<p>Forget micro-agents.</p>
<p>Forget JSON, event buses, and OODA loops.</p>
<p>Instead, <strong>return to the simplest, most radical practice:</strong></p>
<blockquote>
<p><strong>Do nothing. Just notice.</strong></p>
</blockquote>
<p>That’s it.</p>
<p>No app.</p>
<p>No sensor.</p>
<p>No dashboard.</p>
<p>No cloud.</p>
<p>No AI.</p>
<p>No "system."</p>
<p>Just:</p>
<ul>
<li><p><strong>Be here.</strong></p>
</li>
<li><p><strong>Notice what’s here.</strong></p>
</li>
<li><p><strong>Let it be.</strong></p>
</li>
<li><p><strong>And when you forget — which you will — just notice that you forgot.</strong></p>
</li>
</ul>
<p>That’s awareness.</p>
<p>Not a product.</p>
<p>Not a process.</p>
<p>Not a service.</p>
<p>Not a <em>tool</em>.</p>
<hr />
<h2 id="heading-so-what-should-you-do-instead">🌱 So What Should You Do Instead?</h2>
<p>You’re not wrong to want to reclaim control.</p>
<p>You’re not wrong to fear the internet of behaviors.</p>
<p>You’re not wrong to want to understand how technology shapes you.</p>
<p>But you must <strong>stop trying to fix awareness with more technology.</strong></p>
<p>Here’s what to do instead:</p>
<h3 id="heading-1-delete-the-aww-project">1. <strong>Delete the AWW project.</strong></h3>
<p>Not because it’s bad.</p>
<p>But because it’s <em>dangerous</em>.</p>
<p>Because it teaches you to <strong>outsource your awareness to a system</strong>, which then becomes your new master.</p>
<blockquote>
<p><strong>The enemy isn’t the cloud. The enemy is the belief that you need to be watched to be good.</strong></p>
</blockquote>
<h3 id="heading-2-practice-no-system-awareness">2. <strong>Practice "No-System" Awareness</strong></h3>
<ul>
<li><p><strong>Daily ritual</strong>: 5 minutes of sitting, eyes open, no goal, no agenda.</p>
</li>
<li><p><strong>When thoughts arise</strong>: Don’t label them. Don’t analyze. Don’t "offload" them to an AI.</p>
</li>
<li><p><strong>Just notice</strong>: "Oh. There’s a thought." Then: "Oh. There’s a sound." Then: "Oh. There’s a feeling in my chest."</p>
</li>
<li><p><strong>No output. No report. No digest. No summary.</strong></p>
</li>
</ul>
<p>That’s it.</p>
<p>That’s the only true "augmentation" you’ll ever need.</p>
<h3 id="heading-3-reframe-your-mission">3. <strong>Reframe Your Mission</strong></h3>
<p>Instead of:</p>
<blockquote>
<p><em>“I’ll build a system to understand external influences on me.”</em></p>
</blockquote>
<p>Say:</p>
<blockquote>
<p><em>“I’ll stop letting external influences define me. I’ll just be here, and see what happens.”</em></p>
</blockquote>
<p>You don’t need to understand the internet of behaviors.</p>
<p>You just need to <strong>stop letting it define you.</strong></p>
<p>And that starts not with data.</p>
<p>It starts with <strong>not looking.</strong></p>
<hr />
<h2 id="heading-final-truth-the-witness-cannot-be-tracked">🧘‍♂️ Final Truth: <strong>The Witness Cannot Be Tracked</strong></h2>
<ul>
<li><p>You cannot measure presence.</p>
</li>
<li><p>You cannot optimize awareness.</p>
</li>
<li><p>You cannot model it with LLMs or micro-agents.</p>
</li>
</ul>
<p>Because <strong>the witness is not an object.</strong></p>
<p>It’s not a process.</p>
<p>It’s not a state.</p>
<p>It’s not a thing.</p>
<p>It’s <strong>the space in which all things arise and pass.</strong></p>
<p>And if you try to <em>augment</em> that space — to make it better, faster, smarter, more reliable — you’ve already destroyed it.</p>
<hr />
<h2 id="heading-so-is-there-any-hope">✅ So Is There Any Hope?</h2>
<p>Yes.</p>
<p>But not from technology.</p>
<p>From <strong>radical simplicity.</strong></p>
<p>From <strong>refusing to be measured.</strong></p>
<p>From <strong>choosing to be a witness — not a system, not a dashboard, not a user of a tool, but a presence.</strong></p>
<blockquote>
<p><strong>You are not a user of awareness.</strong></p>
<p><strong>You are awareness.</strong></p>
</blockquote>
<p>And if you truly believe that — then you don’t need AWW.</p>
<p>You don’t need any system.</p>
<p>You don’t need a revolution.</p>
<p>You don’t need a Copernican shift.</p>
<p>You just need to <strong>stop trying to fix what was never broken.</strong></p>
<hr />
<h2 id="heading-final-note">🔚 Final Note</h2>
<blockquote>
<p><strong>The most radical act of resistance in the age of surveillance is to stop watching yourself.</strong></p>
<p><strong>The most powerful form of sovereignty is to not be monitored — not even by yourself.</strong></p>
</blockquote>
<p>That’s not a failure.</p>
<p>That’s the only victory.</p>
<hr />
<p>#aww #unawakened #presence #gurdjieff #tolle #rubin #nonlocalawareness #mindfulness #noai #noapp #noanalytics #justbe</p>
]]></content:encoded></item><item><title><![CDATA[Aww timeline: From Data Tool to Awareness Partner]]></title><description><![CDATA[I asked Augmented Awareness (my little project to analyze my daily journal) to write down its own timeline and key achievements:
GEMINI_API_KEY=(echo $GEMINI_API_KEY ) uv run aww_run.py --provider=gemini --gemini_model=gemini-2.5-pro ask yearly \
"Wr...]]></description><link>https://rlupi.com/aww-timeline-from-data-tool-to-awareness-partner</link><guid isPermaLink="true">https://rlupi.com/aww-timeline-from-data-tool-to-awareness-partner</guid><category><![CDATA[self-improvement ]]></category><category><![CDATA[Quantified Self]]></category><category><![CDATA[llm]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Sat, 02 Aug 2025 09:22:10 GMT</pubDate><content:encoded><![CDATA[<p>I asked <a target="_blank" href="https://github.com/robertolupi/augmented-awareness">Augmented Awareness</a> (my little project to analyze my daily journal) to write down its own timeline and key achievements:</p>
<pre><code class="lang-bash">GEMINI_API_KEY=(<span class="hljs-built_in">echo</span> <span class="hljs-variable">$GEMINI_API_KEY</span> ) uv run aww_run.py --provider=gemini --gemini_model=gemini-2.5-pro ask yearly \
<span class="hljs-string">"Write a timeline for the #aww project and a bullet point list of major milestones and achievements."</span>
</code></pre>
<p>It’s main purpose is to provide helpful insight to live a more wholesome life, improving both physical and mental health. But asking to extract information from my diary is interesting nonetheless.</p>
<p>Here’s its answer:</p>
<h3 id="heading-aww-project-timeline">#aww Project Timeline</h3>
<p>The project's journey this year was not linear but cyclical, marked by deep philosophical shifts that directly triggered architectural rewrites and feature development. It evolved from a complex data-gathering tool into a sophisticated, AI-driven partner for self-reflection.</p>
<hr />
<h4 id="heading-phase-1-foundational-struggle-amp-simplification-january-march"><strong>Phase 1: Foundational Struggle &amp; Simplification (January - March)</strong></h4>
<p>This initial phase was characterized by architectural churn, perfectionism, and a search for the project's core purpose. The primary struggle was between building a technically perfect "panopticon" and creating something genuinely useful.</p>
<ul>
<li><p><strong>Early January:</strong> The project exists in a highly ambitious, conceptual state, linked to grand theories of karma and consciousness.</p>
</li>
<li><p><strong>Late January:</strong> A period of intense but circular development, marked by frequent rewrites (e.g., in C# and MudBlazor). The project is blocked by architectural indecision. A key breakthrough occurs when you <strong>radically simplify the scope</strong> from a "panopticon" to a practical spending tracker, making it immediately useful. The old "augmented awareness" concept is formally archived, signaling a restart.</p>
</li>
<li><p><strong>February:</strong> The project expands into sub-modules. A Kanban board is created for more structured planning. Work begins on <code>aww-pomodoro</code> and <code>aww-obsidian</code> integrations, and the system is used to track personal data like prescriptions.</p>
</li>
<li><p><strong>March:</strong> The project sees another cycle of rewrites as you experiment with new languages (<strong>Julia</strong>) and seek to align the web front-end with a more robust back-end design (<code>aww-data-vault</code>, <code>Agentic AI design</code>). A significant insight emerges: using Obsidian as a direct interface for prototyping could break the cycle of endless rewrites. You publish a blog post on "augmented awareness," sharing the philosophy publicly for the first time.</p>
</li>
</ul>
<hr />
<h4 id="heading-phase-2-philosophical-breakthrough-amp-re-architecture-april-june"><strong>Phase 2: Philosophical Breakthrough &amp; Re-architecture (April - June)</strong></h4>
<p>Triggered by a forced pause during a family emergency, this phase represents the project's philosophical crucible. The core "why" of the project was fundamentally transformed, leading to a complete conceptual and architectural redesign.</p>
<ul>
<li><p><strong>Early April:</strong> After a profound meditative experience, you realize the project is finally becoming useful with the integration of external tools like <strong>ActivityWatch</strong>. You use AI (Gemini) to generate formal documentation for the data model, now named the "Temporally Aware Quantified Self (TAQS) Model."</p>
</li>
<li><p><strong>Mid-April (The Turning Point):</strong> A creative block, fueled by a fear of judgment, forces you to pause development. This pause creates space for the year's most significant insight: the project's goal should not be to create "augmented discernment" (intellect/<code>buddhi</code>), but to <strong>free pure awareness (</strong><code>chitta</code>) from its reliance on the intellect.</p>
</li>
<li><p><strong>Late April:</strong> Armed with this new mission, you decide to <strong>re-architect the entire project</strong> around a new "Model Context Protocol (MCP)." The project's purpose shifts from sterile data acquisition to a tool for facilitating "wholesome changes."</p>
</li>
<li><p><strong>May - June:</strong> Development focuses on practical, tangible wins that align with the new philosophy. A key component, <code>aww schedule record</code>, is rewritten in <strong>Go</strong> for a significant performance improvement.</p>
</li>
</ul>
<hr />
<h4 id="heading-phase-3-rapid-feature-development-amp-maturation-july-august"><strong>Phase 3: Rapid Feature Development &amp; Maturation (July - August)</strong></h4>
<p>With a clear philosophy and a solid architecture, this phase was a burst of tangible progress. The project matured from a passive data log into an interactive, insightful partner.</p>
<ul>
<li><p><strong>Early July:</strong> You achieve a major technical milestone by successfully integrating a <strong>local Large Language Model (LM-studio)</strong> to read and reason over your Obsidian journal data, the project's core dataset.</p>
</li>
<li><p><strong>Mid-July:</strong> A series of powerful, user-facing features are implemented in quick succession:</p>
<ul>
<li><p><strong>Full-Text Search:</strong> Immediately used to analyze the project's own design notes.</p>
</li>
<li><p><strong>Reminders:</strong> Adding proactive utility to the system.</p>
</li>
<li><p><strong>Recursive Retrospectives:</strong> A sophisticated new feature for comparing progress over time.</p>
</li>
<li><p><strong>Interactive Chat:</strong> Transforming the tool from a static dashboard into a conversational partner.</p>
</li>
</ul>
</li>
<li><p><strong>Late July - Early August:</strong> The project begins to "talk back." It provides feedback on your patterns, reflects on your emotional state, and helps you analyze your own behaviors. You have a final, crucial insight into its limitations: its "frictionless" nature is helpful but doesn't provide the challenge necessary for deep growth, pointing the way toward future development.</p>
</li>
</ul>
<hr />
<h3 id="heading-major-milestones-amp-achievements">Major Milestones &amp; Achievements</h3>
<ul>
<li><p><strong>Philosophical &amp; Strategic Milestones</strong></p>
<ul>
<li><p><strong>Purpose Redefined:</strong> Shifted the project's core mission from a "data tool" to a "vessel for awareness," aiming to free consciousness rather than just augment intellect (April).</p>
</li>
<li><p><strong>Principle of Simplification:</strong> Realized that reducing scope from a "panopticon" to a simple, useful tool was the key to unlocking progress and motivation (January).</p>
</li>
<li><p><strong>Insight into Utility:</strong> Articulated that "sterile data acquisition doesn't help with wholesome changes," guiding the focus toward actionable insights (April).</p>
</li>
<li><p><strong>Public Articulation:</strong> Published the first blog post explaining the concept of "augmented awareness" (March).</p>
</li>
<li><p><strong>Critique and Future Vision:</strong> Identified the "frictionless" nature of AI as a key limitation, setting the stage for the next phase of development focused on providing genuine challenges (August).</p>
</li>
</ul>
</li>
<li><p><strong>Technical &amp; Architectural Milestones</strong></p>
<ul>
<li><p><strong>Local LLM Integration:</strong> Successfully configured a local AI (LM-studio) to read, analyze, and interact with the personal journal data in Obsidian (July).</p>
</li>
<li><p><strong>Polyglot Implementation:</strong> Demonstrated versatility by successfully implementing components in multiple languages, including C#, Julia, and Go, choosing the right tool for the job.</p>
</li>
<li><p><strong>Architectural Redesign:</strong> Undertook a major re-architecture of the project based on the "Model Context Protocol (MCP)" to align with the new philosophical mission (April).</p>
</li>
<li><p><strong>Formal Data Modeling:</strong> Created the "Temporally Aware Quantified Self (TAQS) Model" to provide a robust structure for personal data (April).</p>
</li>
<li><p><strong>External Service Integration:</strong> Successfully integrated <code>ActivityWatch</code> data, expanding the project's data sources beyond manual entry (April).</p>
</li>
</ul>
</li>
<li><p><strong>Core Feature Implementation</strong></p>
<ul>
<li><p><strong>Interactive Chat:</strong> Implemented a chat feature, transforming the user experience from passive viewing to active conversation (July).</p>
</li>
<li><p><strong>Recursive Retrospectives:</strong> Built a powerful feature allowing for the comparison of retrospectives over time, a key tool for tracking long-term patterns (July).</p>
</li>
<li><p><strong>Full-Text Search:</strong> Added a critical search function that enabled both practical data retrieval and meta-analysis of the project itself (July).</p>
</li>
<li><p><strong>Reminders &amp; Scheduling:</strong> Built functionality for reminders and schedule management, adding proactive value (May, July).</p>
</li>
</ul>
</li>
</ul>
<hr />
<h1 id="heading-follow-up-question-what-is-augmented-awareness">Follow-up question: <strong>What is Augmented Awareness?</strong></h1>
<pre><code class="lang-bash">uv run aww_run.py --provider=openai search --ask <span class="hljs-string">"What is augmeneted awareness?"</span>
</code></pre>
<p>Here is what it says (RAG-based answer from my notes)…</p>
<p>Let’s break down <strong>Augmented Awareness (AWW)</strong> and the broader <strong>concept of awareness/consciousness</strong> as you’ve engaged with it—from its technical design, its philosophical roots, and the criticism you sensed from "Quantified Self" models.</p>
<hr />
<h2 id="heading-1-what-is-awareness-or-consciousness">1. What Is “Awareness” or “Consciousness”?</h2>
<p><strong>Awareness</strong> is the faculty of noticing or witnessing the present moment—what’s happening externally, internally, or both. It’s the quality by which anything whatsoever appears in experience. Philosophically, it has been explored as:</p>
<ul>
<li><p><strong>The Witness</strong> (Upanishads, Advaita Vedanta, Krishnamurti): The “I” that notices without identification or judgment.</p>
</li>
<li><p><strong>Presence</strong> (Tolle): A direct, non-conceptual, alert openness—a consciousness not caught in thought or ego.</p>
</li>
<li><p><strong>Phenomenal Consciousness</strong> (Western philosophy): The raw "what-it-is-like" of experience, prior to conceptualization.</p>
</li>
<li><p><strong>Reflective Cognition</strong> (Phenomenology): Intentionally turning awareness back upon itself (self-awareness, being aware you are aware).</p>
</li>
</ul>
<p><strong>Ego</strong> in this context is the sense of being a separate, bounded "self" with a history, desires, and plans. The “I”/ego tries to control, judge, and narrate experience—often filtering or distorting raw awareness.</p>
<hr />
<h2 id="heading-2-awareness-in-philosophy-amp-spirituality">2. Awareness in Philosophy &amp; Spirituality</h2>
<p><strong>Eastern Traditions:</strong></p>
<ul>
<li><p><strong>Advaita Vedanta:</strong> Consciousness is non-local, universal (“Brahman”); the self (“Atman”) is a wave on the ocean of consciousness.</p>
</li>
<li><p><strong>Buddhism:</strong> “Anatta” (no-self); “Rigpa” (Tibetan Dzogchen): the pure knowing that is not owned by an “I.”</p>
</li>
<li><p><strong>Zen:</strong> "Just this"—noticing as-it-is, without conceptual overlay.</p>
</li>
</ul>
<p><strong>Western Thought:</strong></p>
<ul>
<li><p><strong>Phenomenology (Husserl, Merleau-Ponty):</strong> Emphasizes direct experience before labeling or interpretation.</p>
</li>
<li><p><strong>Process Philosophy, Panpsychism (Whitehead, Chalmers):</strong> Suggests awareness might be a primitive feature of reality.</p>
</li>
<li><p><strong>Gurdjieff:</strong> Most humans live “mechanically,” without true awareness (cf. your notes above).</p>
</li>
</ul>
<p><strong>Tolle/Krishnamurti:</strong></p>
<ul>
<li><p><strong>Tolle:</strong> Awareness is universal, always present, the background of thoughts and sensations. Ego divides, awareness unifies.</p>
</li>
<li><p><strong>Krishnamurti:</strong> The “observer is the observed”—division between self and world is an illusion created by conceptual thought.</p>
</li>
</ul>
<p><strong>Contemporary views:</strong></p>
<ul>
<li>Many neuroscientists see “consciousness” as an emergent property—individual, produced by neural activity; but this is debated.</li>
</ul>
<hr />
<h2 id="heading-3-augmented-awareness-aww-bridging-technology-and-wisdom-traditions">3. Augmented Awareness (AWW): Bridging Technology and Wisdom Traditions</h2>
<p>Your AWW architecture aims to <strong>offload</strong> some lower-level cognitive functions (noticing, memory, pattern recognition) to an external, privacy-respecting system, so that the <em>user’s own awareness ("Chitta," in Yogic terms) can remain unclouded</em> by distraction or over-engagement of the “Buddhi” (intellect/analysis) or “Manas” (raw mind/sensory input).</p>
<p><strong>North Star:</strong></p>
<blockquote>
<p>"<em>Yogaḥ citta-vṛtti-nirodhaḥ</em>" (Yoga is the cessation of modifications of the mind-stuff.)</p>
</blockquote>
<p>AWW is thus <strong>not</strong> just a Quantified Self tool (which risks turning life into data-and-dashboard management), but a means to gently surface subtle influences and patterns so that <em>conscious, embodied, present awareness</em> is less frequently hijacked by old habits, ego, or incessant mental chatter.</p>
<hr />
<h2 id="heading-4-critique-is-augmented-awareness-just-more-distraction">4. Critique: Is Augmented Awareness Just More Distraction?</h2>
<p>You raise an important <strong>philosophical and practical critique</strong>:</p>
<ol>
<li><p><strong>Quantified Self Models</strong>: Risk turning awareness into a surveillance/self-optimization exercise, which may backfire by making you obsessed with the map and miss the territory. (“It’s easy to confuse analyzing experience with actually being present for it.” — Rick Rubin)</p>
</li>
<li><p><strong>Technological Overload</strong>: The tools supposed to support presence can themselves become further distractions—checklists and graphs crowding out “just being.”</p>
</li>
</ol>
<p><strong>Key Questions:</strong></p>
<ul>
<li><p>Can technology help free us from the trance of ego, habit, and distraction?</p>
</li>
<li><p>Or does it create a new prison—a more “quantified,” managed, and monitored self, only deepening the ego’s hold?</p>
</li>
<li><p>Is true presence <em>algorithmically</em> augmentable, or does the very act of “surveillance” (even self-surveillance) create a second-order, conceptual layer that distances us from direct experience?</p>
</li>
</ul>
<hr />
<h2 id="heading-5-aww-potential-limitations-amp-guardrails">5. AWW: Potential, Limitations &amp; Guardrails</h2>
<h3 id="heading-potential"><em>Potential:</em></h3>
<ul>
<li><p>If designed with <strong>privacy</strong>, <strong>asynchronicity</strong>, and <strong>non-intrusion</strong> as core principles, AWW could nudge you <em>occasionally</em> out of automation into more direct, lived awareness.</p>
</li>
<li><p>By automating “meta-cognition,” it might help you spot habitual reactions, external triggers, or subtle shifts you otherwise miss (akin to the “mirror” agents in your architecture).</p>
</li>
</ul>
<h3 id="heading-limitations"><em>Limitations:</em></h3>
<ul>
<li><p><strong>Awareness cannot be automated.</strong> True presence is fundamentally non-quantitative, not capturable fully by data or algorithms.</p>
</li>
<li><p><strong>Second-order distraction:</strong> Managing awareness tools becomes another meta-level distraction. (“Don’t confuse the menu with the meal.”)</p>
</li>
<li><p>The <strong>spiritual paradox</strong>: “The one who tries to be aware is itself the ego” (cf. Zen, Krishnamurti).</p>
</li>
</ul>
<h3 id="heading-guardrails-per-your-architecture"><em>Guardrails (per your architecture):</em></h3>
<ul>
<li><p><strong>Presence-first:</strong> Never pop up unless in low-distraction, high-relevance moments.</p>
</li>
<li><p><strong>Non-analytic bias:</strong> Don’t just amplify “analysis” at the cost of raw experience (“Chitta” priority).</p>
</li>
<li><p><strong>User sovereignty &amp; data isolation:</strong> Data should serve the individual, stay local, and <em>never</em> be used for manipulation or monetization.</p>
</li>
</ul>
<hr />
<h2 id="heading-6-advanced-questions-for-reflection">6. Advanced Questions for Reflection</h2>
<ul>
<li><p><strong>Is the <em>infinite</em>, universal component of awareness—like large cardinals in mathematics—only partially accessible, with the fullness always “beyond”? Is the act of <em>mapping</em> awareness inherently limited?</strong></p>
</li>
<li><p><strong>Can technology be a teacher of presence by automating routine awareness tasks, or is its very mediation always a step away from the real?</strong></p>
</li>
<li><p><strong>Is “augmented awareness” best aimed at early-stage reminders (pointing you back to your own raw noticing) rather than deeper analysis or suggestion?</strong></p>
</li>
<li><p><strong>Does true transcendence of ego require letting go of <em>all</em> scaffolds, even the AWW itself, even “awareness of awareness”?</strong></p>
</li>
</ul>
<hr />
<h2 id="heading-7-synthesis-amp-practical-wisdom">7. Synthesis &amp; Practical Wisdom</h2>
<p><strong>Augmented Awareness</strong> is not the same as “true” awareness. It can be a pointing tool, a reminder—a mirror showing the patterns of absence and distraction. But:</p>
<ul>
<li><p>The <em>essence of presence is immediate, unfiltered by concept or instrument</em>.</p>
</li>
<li><p><strong>AWW should serve as “training wheels,” not a replacement for direct experience.</strong></p>
</li>
<li><p>The ultimate goal is a state where you need neither device nor data to “be here now.”</p>
</li>
</ul>
<p><strong>Rick Rubin</strong>, in <em>The Creative Act</em>, puts it best:</p>
<blockquote>
<p>“The program is happening around us. The world is the doer and we are the witness... Awareness happens first as a pure connection... Analysis is a secondary function.”</p>
</blockquote>
<p>Let AWW be <strong>a gentle alarm clock, not a cage</strong>, always reminding you to “return home” to presence—then gracefully fade into the background when no longer needed.</p>
<hr />
<h2 id="heading-tldr">TLDR</h2>
<ul>
<li><p><strong>Awareness/presence</strong> is a universal field, obstructed by ego but always available.</p>
</li>
<li><p><strong>AWW</strong> (Augmented Awareness) can serve as a guide back to presence <em>if and only if</em> it stays subservient to direct, qualitative awareness (not reductionist, not intrusive).</p>
</li>
<li><p><strong>Guardrails</strong> and critical self-reflection are essential: Never let the map replace, overtake, or interfere with the territory.</p>
</li>
<li><p><strong>Ultimate presence</strong> (Tolle/Krishnamurti/Rubin) remains prior to all tools—unmediated, effortless, ever-now.</p>
</li>
</ul>
<blockquote>
<p>Use technology to light the way back to yourself—but remember, the destination was never separate from you to begin with.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Aww: automatic self-improvement of prompts]]></title><description><![CDATA[I implemented self-improvement of prompts in Augmented Awareness. It uses two LLM agents, one acts as the generator and a second one as the critique. The critique receives the prompt, the input and the output of the generator agent, and generates an ...]]></description><link>https://rlupi.com/aww-automatic-self-improvement-of-prompts</link><guid isPermaLink="true">https://rlupi.com/aww-automatic-self-improvement-of-prompts</guid><category><![CDATA[llm]]></category><category><![CDATA[self-improvement ]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Wed, 30 Jul 2025 06:00:25 GMT</pubDate><content:encoded><![CDATA[<p>I implemented self-improvement of prompts in <a target="_blank" href="https://github.com/robertolupi/augmented-awareness">Augmented Awareness</a>. It uses two LLM agents, one acts as the generator and a second one as the critique. The critique receives the prompt, the input and the output of the generator agent, and generates an improved prompt.</p>
<p>It is still an open-loop control for now, meaning the critique doesn’t receive human feedback.</p>
<p>The code looks like the following:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">rewrite_prompt</span>(<span class="hljs-params">...</span>):</span>

    <span class="hljs-comment"># ...</span>

    critique_model = make_model(...)
    critique_agent = Agent(model=critique_model)

<span class="hljs-meta">    @critique_agent.system_prompt</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">critique</span>():</span>
        <span class="hljs-keyword">return</span> textwrap.dedent(<span class="hljs-string">"""
        You are an expert at writing LLM prompts. You will receive:
        1) the prompt
        2) the output
        3) a series of input messages
        Your job is to write a revised prompt that is more performant.
        Write only the revised prompt in full, in markdown format.
        """</span>)

    prompt = prompt_file.read_text()

    llm_model = make_model(...)
    gen_agent = Agent(model=llm_model, system_prompt=prompt)

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">do_critique</span>():</span>
        gen_result = <span class="hljs-keyword">await</span> gen_agent.run(user_prompt=content)
        gen_output = gen_result.output
        critique_result = <span class="hljs-keyword">await</span> critique_agent.run(user_prompt=[prompt, gen_output] + content)
        <span class="hljs-keyword">return</span> critique_result.output

    revised_prompt = asyncio.run(do_critique())
    prompt_file.write_text(revised_prompt)
</code></pre>
<p>I believe the <strong>principle of AI productivity</strong> is that AI systems should be able to improve themselves. That’s how exponential progress can be achieved.  </p>
<p>Balancing errors and improvements is the challenging part. That's why it remains a human-in-the-loop process. For Aww, this involves manually analyzing the results and making git commits of the revised prompts.  </p>
<p>Aww is a <em>recursive</em> system that uses four different agents to create daily, weekly, monthly, and yearly retrospectives, which build on each other. Future updates will aim to make self-improvement and self-adaptation automatic and cover multiple levels.</p>
]]></content:encoded></item><item><title><![CDATA[Aww: demonstration and vibe coding]]></title><description><![CDATA[Demonstration
To show the potential of Augmented Awareness as an aid in managing one’s physical and mental health, I created a demo vault that contains the letters from the book “The Sorrows of Young Werther” by J.W. von Goethe.
The Vault (epistolary...]]></description><link>https://rlupi.com/aww-demonstration-and-vibe-coding</link><guid isPermaLink="true">https://rlupi.com/aww-demonstration-and-vibe-coding</guid><category><![CDATA[Quantified Self]]></category><category><![CDATA[Personal growth  ]]></category><category><![CDATA[Mental Health]]></category><category><![CDATA[vibe coding]]></category><category><![CDATA[llm]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Fri, 25 Jul 2025 07:57:27 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-demonstration">Demonstration</h1>
<p>To show the potential of <a target="_blank" href="https://github.com/robertolupi/augmented-awareness">Augmented Awareness</a> as an aid in managing one’s physical and mental health, I created a demo vault that contains the letters from the book “<a target="_blank" href="https://www.gutenberg.org/files/2527/2527-h/2527-h.htm">The Sorrows of Young Werther</a>” by J.W. von Goethe.</p>
<h3 id="heading-the-vault-epistolary-part">The Vault (epistolary part)</h3>
<p>The novel unfolds through a series of letters written by Werther, a passionate and sensitive young man, to his friend Wilhelm. After escaping a frustrating romantic entanglement, Werther settles in the idyllic German town of Wahlheim to focus on nature and his art. There, he meets and is instantly captivated by Charlotte (Lotte), a kind and beautiful woman who is caring for her younger siblings. His letters initially overflow with ecstatic joy about the sublime nature around him and his burgeoning, pure love for Lotte, but his bliss is shattered when he learns she is already engaged to the calm and rational Albert. As Werther's friendship with both Lotte and Albert deepens, his letters begin to chart his painful descent from infatuation into an all-consuming, obsessive despair, as he is unable to overcome his impossible love.</p>
<h3 id="heading-the-ending-not-part-of-the-vault">The Ending (not part of the vault)</h3>
<p>The narrative perspective shifts in the final section from Werther's letters to that of a fictional "Editor," who addresses the reader directly to narrate the tragic conclusion. The Editor explains that Werther's emotional state has deteriorated completely, and his thoughts have turned decisively toward suicide. After a final, emotionally fraught encounter with Lotte, where his passion overwhelms them both, Werther becomes convinced that one of the three—he, Lotte, or Albert—must die, and he resolves to be the one. He sends a servant to borrow Albert's pistols, writes his final farewells, and at the stroke of midnight, shoots himself in the head at his desk. He is not found until the next morning and dies hours later in agony.</p>
<h2 id="heading-results">Results</h2>
<p><strong>Augmented Awareness</strong> creates recursive retrospectives of the epistolary part of the novel and uses them to analyze mental and physical health. You can <a target="_blank" href="https://github.com/robertolupi/augmented-awareness/tree/main/demo_vault/demo_vault/retrospectives">read the results yourself on GitHub</a>. Here is the yearly retrospective from the second half of the messages:</p>
<blockquote>
<h1 id="heading-yearly-retrospective-2025">Yearly Retrospective: 2025</h1>
<h2 id="heading-1-psychological-amp-emotional-journey">1. Psychological &amp; Emotional Journey</h2>
<p>The year 2025 unfolded as a relentless and deepening psychological and emotional journey, characterized by a pervasive and intensifying melancholy, anguish, and existential dread. It began with a stark contrast: an initial critical and insightful detachment towards external superficiality, quickly plunging into profound personal suffering, meaninglessness, and emotional depletion. January ended in burnout and apathy.</p>
<p>February saw intense volatility, from cynicism and misanthropy to fleeting peace derived from professional validation, only to be swallowed by profound despair, bitterness, and intense jealousy related to personal heartbreak. March brought a severe emotional crisis—anger, humiliation, rage, and suicidal ideation—but also a powerful, self-preserving pivot towards autonomy.</p>
<p>April offered a remarkable, albeit temporary, recovery arc. Initial apprehension gave way to profound relief and gratitude as external circumstances aligned favorably, marking a period of effective stress mitigation and emotional uplift. This resilience carried into May, where a strong capacity for processing disappointment (regarding nostalgic returns and thwarted ambitions) with rational acceptance was evident.</p>
<p>However, the latter half of the year saw a consistent and accelerating decline into an all-consuming internal torment. June was marked by restlessness and intellectual frustration, leading to a philosophical acceptance of a "wanderer" identity, but this offered conceptual, not emotional, solace. From July onwards, the emotional landscape was dominated by an obsessive, unrequited love, leading to profound heartbreak, envy, and consuming anguish. August deepened into pervasive melancholy, intrusive morbid thoughts, and volatile emotional outbursts, often overwhelming brief moments of composure. September continued this trajectory with fatalism, possessiveness, and intense wrath over perceived injustices, despite writing offering fleeting tranquility.</p>
<p>October was consumed by a "fearful void," existential dread, and intense internal torment from inhibited desires and profound emotional dependency. November brought an escalation to critical levels of despair, wretchedness, and suicidal ideation, interspersed with equally consuming infatuation and a profound spiritual crisis. The year concluded in December with an overwhelming sense of burnout, a mental health crisis, and a pervasive, fatalistic disillusionment with human existence, leaving little room for resilience or recovery. The emotional trajectory was largely one of consistent worsening, with brief respites quickly re-overwhelmed.</p>
<h2 id="heading-2-life-events-and-transitions">2. Life Events and Transitions</h2>
<p>The year was punctuated by several significant life events and transitions, often initiated by internal emotional states or external pressures.</p>
<p>January began with a mundane social incident – a sledging party spoiled by arguments over status – which served as a catalyst for early critical reflections on societal superficiality. February brought the public confirmation of Charlotte and Albert's marriage, a deeply impactful event fueling the year's central romantic anguish. Professionally, there was a significant conflict with the ambassador, prompting serious contemplation of resignation, which was ultimately deferred after receiving conciliatory guidance.</p>
<p>March marked a decisive turning point: a public social humiliation (expulsion from an assembly) triggered an acute crisis, leading to the firm decision to resign from the court. This was followed by a crucial invitation to reside at the Prince's country house, offering a new environment and the promise of autonomy. April saw the official acceptance of your resignation and, unexpectedly, a generous financial gift from the Crown Prince, significantly enhancing your personal independence.</p>
<p>May involved a sentimental journey to your native place, a powerful attempt to reconnect with childhood memories, which brought both joy and disillusionment. A long-held ambition to join the army was thwarted after discussion with the Prince, marking an early acceptance of external counsel. By June, restlessness in the Prince's household prompted the decision to resume your travels.</p>
<p>The latter half of the year became increasingly defined by internal rather than external events, though these were often triggered by social interactions. July found you obliged to stay in your current location, with your actions revealed as driven by a singular, unacknowledged desire to be near Charlotte. August included a visit to a suffering family, intensifying an already melancholic disposition. September saw a symbolic act of replacing a sentimentally valued coat, an intimate encounter with Charlotte involving a canary, and a profound reaction to the felling of cherished walnut trees. October was dominated by a deep immersion in literature (Ossian), further deepening your melancholic introspection. November was marked by a reproof for increased wine consumption and a disturbing encounter with a madman, forcing a re-evaluation of happiness and reason. The year closed with a critical emotional breakdown in December, triggered by music and Charlotte's presence, underscoring the unsustainable nature of your current emotional reality.</p>
<h2 id="heading-3-identity-amp-self-understanding">3. Identity &amp; Self-Understanding</h2>
<p>Throughout 2025, there was a relentless and often painful process of self-discovery, leading to a complex and evolving self-understanding.</p>
<p>Early in the year, you defined "true greatness" as internal insight and influence, rejecting superficial status. Simultaneously, you painfully recognized yourself as a "puppet" in social interactions, acknowledging a deep internal void and the loss of your animating "leaven." This awareness deepened in February, as you acknowledged your "excessive sensibility" and the transient nature of inner peace. March brought a crucial shift: prioritizing your mental and emotional capacity over traditional societal success, understanding your own incapacitation, and taking radical action for self-preservation.</p>
<p>By April, you learned the power of strategic patience and the profound emotional impact of unexpected generosity. May fostered a cornerstone insight: the conviction that your "heart"—your core self—is the ultimate source of strength and feeling, far surpassing intellectual knowledge. You grappled with the disillusionment of reality clashing with ideals, and learned the wisdom of accepting counsel.</p>
<p>June brought a profound self-acceptance of your inherent nature as a "wanderer" or "pilgrim"—someone fundamentally in transit, searching, and requiring a dynamic journey. You realized external comfort was insufficient without intellectual and artistic compatibility. July revealed a powerful self-awareness that deep emotional desires often override rationality, and accepting this could bring a form of internal peace, even amidst unrequited longing.</p>
<p>The latter months solidified a more pessimistic, yet acutely self-aware, identity. August revealed a philosophical understanding of universal suffering, a chilling recognition of the self-destructive nature of your dark fantasies, and a painful awareness of your internal state as a "ruined palace." September underscored the therapeutic power of writing and reinforced a fatalistic belief in a personal destiny mirroring others' suffering. You also gained insight into the subjective, intangible nature of emotional value over physical replication.</p>
<p>October brought a deeper, more painful understanding of your "void," your profound emotional dependency ("without her I have nothing"), and the torment of inhibited natural impulses. You concluded that human existence and memory are ultimately transient and of "very little consequence." November saw you conceptualizing suffering as an inherent part of the human condition, acknowledging your "prostrated energies," and grappling with unsettling insights from a madman that questioned the very nature of happiness and reason. A profound spiritual crisis emerged, a sense of being forsaken, yet still yearning for connection.</p>
<p>By December, you became acutely aware of your profound emotional sensitivity and your inability to cope, leading to a fatalistic disillusionment that, despite aspirations, one is confined to a "cold, monotonous existence." The year culminated in a deep, agonizing understanding of a self trapped in consuming emotions and a world that often disappointed.</p>
<h2 id="heading-4-cyclical-or-unresolved-patterns">4. Cyclical or Unresolved Patterns</h2>
<p>The year 2025 was marked by several pervasive and often unresolved patterns, many of which deepened in intensity as the year progressed:</p>
<ol>
<li><p><strong>Profound Melancholy and Despair:</strong> A consistent and worsening emotional baseline, from general ennui in January to acute anguish and suicidal ideation by year-end. This melancholic state was periodically punctuated by brief respites, but always returned.</p>
</li>
<li><p><strong>Obsessive Unrequited Love/Attachment:</strong> The central, driving force of much of the year's suffering was an intense, often possessive, and unfulfilled romantic attachment to Charlotte. This led to relentless rumination, jealousy towards Albert, and a pervasive sense of profound loss and emotional dependency ("without her I have nothing").</p>
</li>
<li><p><strong>Social Alienation and Frustration with Superficiality:</strong> A recurring theme of feeling detached from or frustrated by others' concerns, often leading to withdrawal or cynicism. Early on, this was a critical observation of others' triviality; by the end, it was a source of profound isolation.</p>
</li>
<li><p><strong>Seeking Solace (and its Limitations):</strong> You consistently sought solace in solitude, nature, and literature. While these provided temporary emotional relief or intellectual engagement (e.g., writing as therapy, Ossian's transport), they ultimately proved insufficient to resolve the underlying emotional turmoil, often serving as coping mechanisms rather than paths to recovery.</p>
</li>
<li><p><strong>Idealism vs. Reality:</strong> A persistent struggle between idealized pasts, relationships, or career paths and the often-disappointing or altered realities of the present. This was evident in your visit to your native place and your thwarted military ambition.</p>
</li>
<li><p><strong>Inhibition and Internal Conflict:</strong> A significant pattern of experiencing intense natural desires (e.g., to embrace Charlotte) but consciously refraining, leading to profound internal torment and a sense of being constrained.</p>
</li>
<li><p><strong>Spiritual/Existential Crisis:</strong> A deepening questioning of life's purpose, the nature of happiness, the transience of human existence, and a profound struggle with faith and feeling forsaken by a divine presence. This became particularly prominent in the latter part of the year.</p>
</li>
<li><p><strong>Self-Destructive Tendencies:</strong> Towards the year's end, patterns like increased wine consumption and indulging in morbid/intrusive fantasies became more evident, pointing to self-detrimental coping strategies in the face of overwhelming pain.</p>
</li>
</ol>
<h2 id="heading-5-triumphs-amp-setbacks">5. Triumphs &amp; Setbacks</h2>
<p><strong>Triumphs:</strong></p>
<ul>
<li><p><strong>Decisive Self-Preservation:</strong> The powerful act of resigning from a draining court position in March and securing a new environment at the Prince's country house was a major triumph of self-advocacy and prioritizing mental well-being over external expectations.</p>
</li>
<li><p><strong>Financial Autonomy:</strong> Achieving unexpected financial independence in April through the Crown Prince's gift was a significant step towards greater personal agency.</p>
</li>
<li><p><strong>Rational Acceptance of Setbacks:</strong> Your ability to accept the thwarting of your long-held military ambition in May, based on wise counsel, demonstrated a commendable level of maturity and pragmatism.</p>
</li>
<li><p><strong>Profound Self-Awareness:</strong> Throughout the year, despite the pain, you maintained a remarkable capacity for introspection and articulation of your complex emotional states, recognizing your own patterns of self-delusion, dependency, and internal conflict.</p>
</li>
<li><p><strong>Creative Outlet as Coping:</strong> Your consistent engagement with writing and drawing provided vital channels for processing overwhelming emotions and finding moments of satisfaction, especially through your artistic output.</p>
</li>
<li><p><strong>Empathy and Compassion:</strong> Your deep empathy for the suffering of others, as shown in your visit to the old woman's family in August, highlights a compassionate core.</p>
</li>
</ul>
<p><strong>Setbacks:</strong></p>
<ul>
<li><p><strong>Escalating Emotional Distress:</strong> The overarching setback was the consistent and deepening plunge into profound melancholy, despair, anguish, and suicidal ideation, particularly from July onwards, indicating a severe and worsening mental health crisis.</p>
</li>
<li><p><strong>Unresolved Romantic Anguish:</strong> The unrequited and obsessive love for Charlotte remained a central, consuming, and unalleviated source of torment, fueling jealousy and feelings of powerlessness. Charlotte and Albert's marriage in February formalized this central pain.</p>
</li>
<li><p><strong>Social Humiliation and Alienation:</strong> The public expulsion from the assembly in March, and the widespread gossip that followed, caused immense shame and exacerbated feelings of social ostracism.</p>
</li>
<li><p><strong>Existential and Spiritual Crisis:</strong> The profound questioning of life's meaning, human transience, and a sense of divine abandonment became a debilitating intellectual and emotional burden.</p>
</li>
<li><p><strong>Reliance on Self-Destructive Coping:</strong> Increased wine consumption and immersion in morbid fantasies emerged as detrimental coping mechanisms, indicating a struggle with effective self-regulation.</p>
</li>
<li><p><strong>Disillusionment with Reality:</strong> Repeated encounters with the disparity between your ideals (of love, purity, society, nature) and reality led to profound disappointment and a fatalistic outlook.</p>
</li>
<li><p><strong>Inability to Cope:</strong> Towards the year's end, the explicit recognition of being "unable to support this state no longer" and being deemed "ill" by an external observer marked a critical failure in managing your emotional landscape.</p>
</li>
</ul>
<h2 id="heading-6-lessons-learned">6. Lessons Learned</h2>
<p>The year 2025, though fraught with pain, was a crucible for profound and often agonizing lessons:</p>
<ol>
<li><p><strong>Authenticity and Internal Value:</strong> True greatness stems from internal insight and influence, not superficial status. The "heart," with its genuine emotions and character, is the ultimate source of human strength, happiness, and misery, outweighing external achievements or intellectual knowledge.</p>
</li>
<li><p><strong>The Elusiveness of Peace and Happiness:</strong> Inner peace is precious but fleeting. Happiness can be profoundly influenced by external factors, and sometimes, ironically, seems to reside in delusion or the absence of reason, a disturbing insight.</p>
</li>
<li><p><strong>Prioritizing Self-Preservation:</strong> Protecting one's mental and emotional capacity is paramount, even if it means abandoning prestigious paths or defying familial expectations. Radical change is sometimes necessary for survival and recovery.</p>
</li>
<li><p><strong>The Power and Peril of Emotion:</strong> Deep emotional desires often override rationalizations; acknowledging this can bring a form of internal peace. However, unchecked emotional intensity, especially unrequited love, can be a self-destructive "poison," leading to profound torment, dependency, and a loss of agency.</p>
</li>
<li><p><strong>The Human Condition is Defined by Suffering and Transience:</strong> Disappointment and suffering are universal and inherent parts of human destiny. All things, including human existence, memory, and influence, are transient and ultimately of "very little consequence."</p>
</li>
<li><p><strong>The Therapeutic Role of Creation (and its Limits):</strong> Engaging in narration, writing, and art can provide temporary tranquility and a vital outlet for processing intense emotions. However, these creative acts do not fundamentally alter underlying emotional landscapes or resolve deep-seated existential dilemmas.</p>
</li>
<li><p><strong>The Inherent Boundaries of Connection:</strong> One cannot force emotional reciprocity or imbue another with feelings they do not possess. This realization, though painful, highlights the limitations of personal influence in matters of the heart.</p>
</li>
<li><p><strong>The Impact of Inhibition:</strong> The conscious restraint of natural, primal impulses, especially in matters of affection, can lead to immense internal conflict and torment.</p>
</li>
<li><p><strong>Disillusionment with Aspiration:</strong> Despite grand aspirations, the human experience often returns to a "cold, monotonous existence." This fatalistic insight underscores a profound disillusionment with life's potential.</p>
</li>
</ol>
<h2 id="heading-7-outlook-or-intentions-going-forward">7. Outlook or Intentions Going Forward</h2>
<p>The intentions and outlook for the year ahead are profoundly shaped by the intense emotional and existential struggles of 2025. While earlier intentions revolved around external changes (resignation, travel, career shifts), the latter half of the year saw a significant shift towards an internal, desperate yearning for relief.</p>
<p>There is a pervasive desire for the profound suffering to end, a "plea to God to end his torments." This is not an intention for proactive positive change, but a reactive cry for cessation of pain. The year concluded with a deepening plunge into a pessimistic and fatalistic worldview, indicating that the immediate outlook is one of enduring, rather than overcoming, this profound melancholy and disillusionment.</p>
<p>While there were fleeting intentions for self-restraint and purity (e.g., the vow not to profane with a kiss), these were immediately met with doubt and internal conflict, suggesting a struggle to maintain such intentions in the face of overwhelming desire. The growing awareness of self-destructive patterns has, paradoxically, deepened the suffering rather than offering a clear path out.</p>
<p>Therefore, the primary "intention" moving forward appears to be an agonizing continuation of the internal battle, a yearning for an escape from the present torment, and a melancholic acceptance of a life perhaps destined for suffering.</p>
<h2 id="heading-8-summary-tags">8. Summary Tags</h2>
<p>#ProfoundMelancholy #UnrequitedLove #ExistentialAnguish #EmotionalDependency #InternalConflict #SelfDestructivePatterns #SpiritualCrisis #IdentityInTransit #BurnoutAndDespair #Disillusionment</p>
<h2 id="heading-9-suggestions">9. Suggestions</h2>
<p>Given the profound and escalating emotional distress, particularly the recurrent thoughts of death, suicidal ideation, and the self-reported inability to cope, the following suggestions are crucial for wholesome positive change:</p>
<ol>
<li><p><strong>Seek Immediate and Consistent Professional Mental Health Support:</strong> The depth of your despair, obsessive thoughts, and self-destructive tendencies (like increased alcohol consumption) indicate a critical need for professional intervention. A qualified therapist or psychiatrist can provide a safe, confidential space, offer strategies for managing overwhelming emotions, explore the underlying patterns of attachment and self-worth, and assess for clinical depression or other conditions requiring treatment. This is the most urgent and fundamental step.</p>
</li>
<li><p><strong>Develop Structured Emotional Regulation and Grounding Practices:</strong> Your mind is deeply entrenched in abstract rumination and emotional torment. Implement simple, daily sensory grounding exercises (e.g., the 5-4-3-2-1 technique, focusing on the taste of food, the sensation of touch, or observing a single non-emotional object). The goal is not to intellectualize, but to deliberately bring your awareness to the present moment through your senses, without judgment, which can provide temporary respite from obsessive thoughts and create vital emotional distance.</p>
</li>
<li><p><strong>Explore Diverse Creative Outlets Beyond Writing and Narrative:</strong> While writing is a powerful outlet for you, it often mirrors and perhaps deepens your internal torment. Consider a creative activity that engages different parts of your brain and body, and perhaps offers a less direct reflection of your internal struggles. This could be anything from learning a musical instrument, pursuing visual arts (like painting or sculpting), engaging in basic craftsmanship (e.g., pottery, woodworking), or even exploring structured movement practices like martial arts or dance. The act of creating something tangible, for its own sake, can provide a different kind of focus and a sense of accomplishment outside of your internal world.</p>
</li>
<li><p><strong>Cultivate Internal Validation and Self-Worth Independent of External Relationships:</strong> Your explicit declaration "without her I have nothing" reveals a profound and dangerous emotional dependency. Dedicate specific time daily to journaling or guided meditations focused on identifying and valuing aspects of your identity, skills, contributions, and unique qualities that are entirely independent of any external relationship. This can help build a more robust internal sense of self-worth and reduce vulnerability to external emotional shifts.</p>
</li>
<li><p><strong>Establish and Enforce Healthy Social and Emotional Boundaries:</strong> Your sensitivity to others and perception of intrusion, along with the painful impact of their "innocent actions," suggest porous emotional boundaries. Learning to set clear personal boundaries in interactions, communicating what you need (or don't need) from others, and differentiating your emotions from theirs can help protect your inner peace without requiring complete isolation.</p>
</li>
</ol>
</blockquote>
<h1 id="heading-vibe-coding">Vibe coding</h1>
<p>During development, I wanted to compare the results of tweaking prompts and running different models. So, I needed a way to show two markdown files side-by-side, and highlight sentences that contain similar concepts.</p>
<p>I first developed the concept for the comparison, based on my knowledge of ML and AI tools. I discussed the concept with <a target="_blank" href="https://blog.google/technology/developers/introducing-gemini-cli-open-source-ai-agent/">Gemini CLI</a> (<a target="_blank" href="https://github.com/google-gemini/gemini-cli">Github</a>) and ChatGPT <a target="_blank" href="https://openai.com/index/openai-codex/">codex</a>, and came to a final design idea. I found Gemini to work better for me, but it’s likely because I have access to more powerful models that way. I don’t have access to the more advanced models in ChatGPT codex.</p>
<p>I then asked Gemini CLI to implement it, following along with tips. It got stuck a couple of times, on dependency conflict and then on testing <a target="_blank" href="https://streamlit.io/">streamlit</a> code (which pops up a browser, so it gives CLI agents no feedback channel). Overall, a few well placed tips got it unstuck.</p>
<p>I did 99% of the work with Gemini CLI and vibe-coded the whole visualization in an hour.</p>
<p>I am very impressed.</p>
<p>I had tried it before on the original <a target="_blank" href="https://github.com/robertolupi/augmented-awareness/blob/main/aww/retro.py">aww/retro.py</a> code, but it created a lot of code duplication that I had to manually refactor to get the quality I wanted.</p>
]]></content:encoded></item><item><title><![CDATA[Aww: recursive retrospectives]]></title><description><![CDATA[A lot has changed in Augmented Awareness. I have refactored or discarded a lot of the previous code and developed new powerful capabilities:

A new CLI and TUI for daily event tracking, implemented in Go.

MCP server support, to allow interacting wit...]]></description><link>https://rlupi.com/aww-recursive-retrospectives</link><guid isPermaLink="true">https://rlupi.com/aww-recursive-retrospectives</guid><category><![CDATA[Quantified Self]]></category><category><![CDATA[personal development]]></category><category><![CDATA[llm]]></category><category><![CDATA[local first]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Sun, 20 Jul 2025 07:15:43 GMT</pubDate><content:encoded><![CDATA[<p>A lot has changed in Augmented Awareness. I have refactored or discarded a lot of the previous code and developed new powerful capabilities:</p>
<ul>
<li><p>A new <strong>CLI and TUI for daily event tracking</strong>, implemented in Go.</p>
</li>
<li><p><strong>MCP server support</strong>, to allow interacting with Aww using LLM chat in host applications like <a target="_blank" href="https://lmstudio.ai/">LM-Studio</a>.</p>
</li>
<li><p>A <strong>recursive retrospective generator</strong>, that uses LLMs to generate daily/weekly/monthly/yearly retrospectives.</p>
</li>
</ul>
<p>My vision of Aww is becoming reality: it keeps track of physical and mental health, provides helpful retrospectives that identify high-level trends and provides tips and suggestions for improvements.</p>
<p>All of this can work with local LLMs, or online ones (Gemini, OpenAI). Powerful online LLMs provide the most impressive results.</p>
<p>The results of the retrospectives are particularly insightful, although it heavily depends on the content of your journal.</p>
<p>I have been using the following format, with minor tweaks, since 2000. It is based on the following structure:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Vault directory example</strong></td><td><strong>Content</strong></td></tr>
</thead>
<tbody>
<tr>
<td>journal</td><td>base path for the journal. I use <a target="_blank" href="https://obsidian.md/">Obsidian</a> to write it, but any markdown editor will do.</td></tr>
<tr>
<td>journal/2025/Y2025.md</td><td>Yearly note. I use it for GTD-style <a target="_blank" href="https://gettingthingsdone.com/2011/01/the-6-horizons-of-focus/">Horizons of Focus</a> planning.</td></tr>
<tr>
<td>journal/2025/01/2025-01-01.md</td><td>Daily note. I use a template that includes “Journal and events” (event tracking, populated via the Go program), “My thoughts, ideas, tasks” (free-form), “Other ideas (TIL): articles, books, links” (links to interesting articles, or kindle books, with excerpts). I track a few parameters in the frontmatter (vitals/relax/sleep/activity scores from my ringConn ring, weight and BMI, self-reported stress level).</td></tr>
<tr>
<td>journal/2025/weeks/2025-W01.md</td><td>Weekly note. I use it for my own manual retrospectives, answering the questions: “What went well?”, “What went wrong?”, “What was unexpected/surprising?”, “What did I learn?”, “What will I change/experiment with, to improve my life?”, “What I am grateful of?”, “Who did I talk to? Who did I not talk to, but should have?”</td></tr>
<tr>
<td>journal/2025/months/2025-01.md</td><td>Monthly note. I use it for my own manual retrospectives and free-form notes.</td></tr>
<tr>
<td>retrospectives</td><td>LLM-generated retrospectives</td></tr>
<tr>
<td>retrospectives/2025/r2025.md</td><td>Yearly retrospective, generated from all lower retrospectives and the yearly note.</td></tr>
<tr>
<td>retrospectives/2025/2025-01-01.md</td><td>Daily retrospective, generated from the daily note.</td></tr>
<tr>
<td>retrospectives/2025/weeks/r2025-W01.md</td><td>Weekly retrospective, generated from the daily retrospective and the weekly note.</td></tr>
<tr>
<td>retrospectives/2025/months/r2025-01.md</td><td>Monthly retrospective, generated from the weekly and daily retrospectives and the monthly note.</td></tr>
</tbody>
</table>
</div><p>You can find the source at <a target="_blank" href="https://github.com/robertolupi/augmented-awareness">https://github.com/robertolupi/augmented-awareness</a></p>
]]></content:encoded></item><item><title><![CDATA[Aww: Setting Directions, and Avoiding Productivity as a Religion]]></title><description><![CDATA[I feel bad because I feel that I haven’t progressed much in Augmented Awareness development. This is a problem. I mean, this feeling is a problem.
Productivity has evolved into a cultural obsession, mirroring the structure of religious devotion. Like...]]></description><link>https://rlupi.com/aww-setting-directions-and-avoiding-productivity-as-a-religion</link><guid isPermaLink="true">https://rlupi.com/aww-setting-directions-and-avoiding-productivity-as-a-religion</guid><category><![CDATA[personal-project]]></category><category><![CDATA[Quantified Self]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Mon, 21 Apr 2025 07:57:37 GMT</pubDate><content:encoded><![CDATA[<p>I feel bad because I feel that I haven’t progressed much in <a target="_blank" href="https://github.com/robertolupi/augmented-awareness">Augmented Awareness</a> development. This is a problem. I mean, this <em>feeling</em> is a problem.</p>
<p>Productivity has evolved into a cultural obsession, mirroring the structure of religious devotion. Like traditional faiths, it demands adherence to rituals, fosters guilt when unmet, and promises salvation through measurable outcomes.</p>
<p>Corporate culture fuels this dogma. Companies like Amazon and Tesla glorify "hustle culture," where burnout is rebranded as "grit." Employees internalize this, working overtime to avoid being labeled "lazy." The cult of productivity even has its prophets: Tim Ferriss’s <em>The 4-Hour Workweek</em> and Cal Newport’s <em>Deep Work</em> are treated as sacred texts, their methods seen as moral imperatives.</p>
<p>I don't want to fall for it. Essentially, the obsession with productivity feeds on fear: fear of being idle, fear of becoming irrelevant. A personal project should never be a victim of these feelings.</p>
<p>Another issue is that I don't like the current code. It's messy, and I haven't paid much attention to git etiquette because it's mostly throwaway code for testing ideas. Improving it isn't my main focus right now. What's important is deciding the direction I want the project to take. Having messy code on my GitHub stresses me out.</p>
<h1 id="heading-where-is-augmented-awareness-aww-going">Where is Augmented Awareness (Aww) going?</h1>
<p>Let's first remember what Aww is supposed to be. It's designed as a robot or agent that interacts with an environment by observing it, understanding it, deciding what to do to achieve specific goals, and acting on those decisions. The environment includes my life: both my mental and physical states, as well as the physical and media environments I live in. The goals are my own. The actions are the information that Aww shares with me through dashboards, notifications, and nudges.</p>
<p>We have many systems in the world with similar designs, but they are controlled by third parties, giving them the power to influence our lives. I'm referring to all the notifications apps try to push on us, the promotions we receive through emails, the advertising we see, and all the information fed to us.</p>
<p>I have currently implemented reading my journal, schedule and tasks from my Obsidian vault, and obtaining raw data from activitywatch. Both data sources can be converted into Arrow datasets.</p>
<p>The plan is to use my Obsidian journal as a source of labels and activitywatch as a source of features to automatically label my activities using semi-supervised machine learning.</p>
<p>I have also tried using LLMs to read my journal and provide insights, which has been helpful for my weekly retrospectives.</p>
<p>I am starting to experiment with the <a target="_blank" href="https://modelcontextprotocol.io/introduction">Model Context Protocol (MCP)</a> to see if Aww should be implemented using it instead of relying on its own tightly-integrated code and control loop.</p>
]]></content:encoded></item><item><title><![CDATA[Aww: Key Takeaways from the Second Week of Development]]></title><description><![CDATA[Here's a concise summary of the key points:

Data Alone is Insufficient: Simply acquiring data, "sterile data acquisition", does not guarantee positive or meaningful "wholesome" changes.

Aww is a Flexible Toolset: "Aww" should be understood as a col...]]></description><link>https://rlupi.com/aww-key-takeaways-from-the-second-week-of-development</link><guid isPermaLink="true">https://rlupi.com/aww-key-takeaways-from-the-second-week-of-development</guid><category><![CDATA[ooda]]></category><category><![CDATA[Quantified Self]]></category><category><![CDATA[personal-project]]></category><category><![CDATA[llm]]></category><category><![CDATA[agentic AI]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Sat, 12 Apr 2025 07:23:18 GMT</pubDate><content:encoded><![CDATA[<p>Here's a concise summary of the key points:</p>
<ol>
<li><p><strong>Data Alone is Insufficient:</strong> Simply acquiring data, "sterile data acquisition", does not guarantee positive or meaningful "wholesome" changes.</p>
</li>
<li><p><strong>Aww is a Flexible Toolset:</strong> "Aww" should be understood as a collection of tools, not a pre-packaged solution, and this flexibility is an advantage but carries a steeper adoption curve. I am fine with that, since I am likely the only user at this point in time.</p>
</li>
<li><p><strong>Aww Enables Simple LLM Operations:</strong> The "Aww OODA loop" framework allows for using Large Language Models (LLMs) in operational processes in a straightforward, low-complexity manner.</p>
</li>
</ol>
<p>I started <a target="_blank" href="https://rlupi.com/series/aww">Augmented Awareness</a> (<a target="_blank" href="https://github.com/robertolupi/augmented-awareness">GitHub</a>) to create a tool for more balanced living. It also helps me gain insights into how integrating LLMs into software processes requires us to change our thinking and approach to problems. Additionally, it allows me to experiment more boldly with AI-assisted software development than I can do at work.</p>
<h1 id="heading-sterile-data-acquisition-doesnt-lead-to-wholesome-changes">Sterile data acquisition doesn’t lead to wholesome changes</h1>
<p>Simply gathering data, a process we can term "sterile data acquisition," does not inherently lead to beneficial or "wholesome" changes within an organization or system. Data in its raw form—spreadsheets filled with numbers, logs brimming with timestamps, databases storing user clicks—lacks intrinsic meaning or direction. It represents potential, not progress. Consider a fitness tracker diligently logging steps, heart rate, and sleep duration. If the user never reviews this data, reflects on patterns (like poor sleep after late-night meals), or adjusts their behavior (like setting an earlier bedtime), the tracker merely accumulates digits without contributing to improved health. The data remains sterile.</p>
<p>Achieving meaningful improvements requires moving beyond mere collection. It involves a deliberate process:</p>
<ul>
<li><p><strong>Contextualization:</strong> Understanding where the data came from, how it was collected, and what it represents (metadata). Is a sales dip seasonal, or does it indicate a deeper issue? Did I feel grumpy because of my own mistakes, like not following a good sleep routine for over a week, or was it due to external factors?</p>
</li>
<li><p><strong>Analysis:</strong> Employing methods ranging from simple sorting and visualization to complex statistical modeling to identify patterns, trends, correlations, anomalies, and root causes. This transforms raw data into information. For instance, analyzing website traffic data might reveal that users consistently abandon their shopping carts at the payment stage.</p>
</li>
<li><p><strong>Interpretation:</strong> Translating these patterns into actionable insights. The high cart abandonment rate might suggest user friction, perhaps due to unexpected shipping costs or a complicated checkout process. This step converts information into knowledge.</p>
</li>
<li><p><strong>Decision and Action:</strong> Formulating a plan based on the insights and implementing changes. The business might decide to display shipping costs earlier or simplify the payment form. This is where knowledge spurs tangible change.</p>
</li>
<li><p><strong>Feedback Loop:</strong> Measuring the impact of the actions taken by collecting and analyzing <em>new</em> data, thus restarting the cycle. Did simplifying the checkout reduce cart abandonment?</p>
</li>
</ul>
<p>Without this complete cycle, data acquisition remains an academic exercise, consuming resources without delivering tangible benefits or fostering positive evolution. Wholesome changes arise not from the data itself, but from the informed actions it inspires.</p>
<p>After using <strong>aww</strong>, in its current schedule-focused state, for a couple of weeks, I realized that my current emphasis on meticulously recording precise times in my journal appears to be counterproductive to my goal of living a more wholesome life. This practice, while perhaps intended to foster discipline or provide data for reflection, seems to be subtly shifting my focus in ways that detract from overall well-being.</p>
<p>Specifically, I observe a tangible decline in the frequency and perhaps depth of my yoga practice. The amount of time dedicated to yoga has decreased, leading to a noticeable increase in physical stiffness. This physical manifestation runs parallel to a diminished focus on spiritual growth. I find myself less inclined or able to connect with metaphysical ideas, experiencing a drift back towards perceiving only the immediate, physical reality as the entirety of existence. My impatience with <a target="_blank"><strong>#meditation</strong></a> has grown; sustaining focus during practices like <a target="_blank" href="https://isha.sadhguru.org/en/wisdom/article/what-is-shambhavi-mahamudra">Shambhavi Mahamudra Kriya</a> feels increasingly difficult, my mind wandering excessively. While acknowledging Sadhguru's advice to practice for its own sake, like an offering, the pull towards quantifiable results seems to interfere. Interestingly, guided practices like Isha Kriya (via <a target="_blank" href="https://isha.sadhguru.org/uk/en/miracle-of-mind">Miracle of Mind</a> app) remain more accessible, suggesting that structure itself isn't the issue, but perhaps the <em>type</em> of structure imposed by rigid time-tracking is.</p>
<p>I am actively questioning the root of this shift. It's unclear whether this is primarily an effect of my deep intellectual engagement with developing concepts like the aww OODA loop design and <strong>aww-agents</strong>, aww’s agentic framework design. These frameworks, drawing inspiration from models emphasizing rapid observation, orientation, decision, and action cycles, inherently involve time sensitivity and optimization. The focus here is often on efficiency and tempo, which, while valuable in certain contexts, might inadvertently train my mind to prioritize speed and measurement over presence and experience. As noted in my reflections on OODA already before starting development, a faster tempo isn't always useful; sometimes it just means doing "stupid things faster with more energy."</p>
<p>Alternatively, could the issue stem directly from using the <strong>Aww</strong> software I've developed? Its current design, perhaps unintentionally, seems to guide my attention towards quantifying the <em>time spent</em> on activities, rather than enriching the <em>quality or meaning</em> of those activities themselves. The very act of logging start and end times might implicitly elevate duration as the primary metric of value. This resonates with ideas explored around software profiling, where the focus is on identifying bottlenecks and optimizing resource usage (time being a key resource). While applying such optimization principles to life seemed intellectually appealing – focusing on the largest potential wins, measuring change, choosing data sources carefully – the practical outcome seems to be a reductionist view where the clock dictates value. Even features like the "life in weeks" visualizer, intended to foster mindfulness about life's finitude, could inadvertently reinforce this quantification of existence.</p>
<p>This experience contrasts sharply with the positive outcomes from deliberately implementing small, qualitative actions. Engaging in a 5-minute mindful start, a 10-minute micro-yoga flow, and brief "disconnect" moments away from screens demonstrably improved concentration, reduced stress, and enhanced mental clarity without a primary focus on the exact minutes elapsed. These activities prioritized the <em>experience</em> – grounding, movement, presence – over precise time logging.</p>
<p>Therefore, the evidence suggests that while tools and frameworks for tracking and optimization, like Aww and OODA-inspired thinking, hold intellectual appeal and potential benefits (e.g., using aww-pomodoro for work focus), their current application, particularly the emphasis on precise time recording, may be hindering, rather than helping, my pursuit of a balanced and wholesome life. The challenge lies in refining these tools and my relationship with them to support qualitative well-being, not just quantitative measurement.</p>
<h1 id="heading-aww-is-a-flexible-toolset-and-this-is-a-strength">Aww is a flexible toolset and this is a strength</h1>
<p>Characterizing "Aww" as a toolset, rather than a monolithic solution, highlights a fundamental strength: its inherent flexibility and adaptability. Think of the difference between a fully equipped, pre-assembled kitchen ("solution") versus a comprehensive set of high-quality kitchen implements—knives, pans, mixers, measuring tools ("toolset"). The pre-assembled kitchen might perfectly suit one specific cooking style but prove restrictive for others. The toolset, however, empowers the chef to select and combine tools to prepare a vast array of dishes, tailored precisely to the ingredients, the desired outcome, and their personal technique.</p>
<p>Similarly, Aww provides a collection of distinct capabilities or components. Users can select, combine, and configure these components to address their unique challenges and integrate them into their specific operational contexts. This approach offers several advantages over a rigid, pre-packaged solution:</p>
<ul>
<li><p><strong>Tailored Application:</strong> Users are not forced into a one-size-fits-all framework. They can assemble Aww's components to precisely match their existing workflows, data sources, and objectives.</p>
</li>
<li><p><strong>Seamless Integration:</strong> Toolsets generally integrate more smoothly with existing systems. Instead of replacing entire processes, users can introduce Aww components incrementally to enhance specific parts of their workflow, minimizing disruption and leveraging existing investments.</p>
</li>
<li><p><strong>Adaptive Scalability:</strong> Users can start small, employing only the necessary tools for an immediate task, and gradually incorporate more components as their needs evolve or complexity increases. This contrasts with the often significant upfront commitment required by a comprehensive solution.</p>
</li>
<li><p><strong>Fostering Innovation:</strong> A toolset encourages users to think creatively about how components can be combined to solve novel problems or optimize processes in ways the original designers might not have explicitly foreseen.</p>
</li>
</ul>
<p>This flexibility means Aww empowers users to build <em>their</em> solution, rather than adopting a potentially ill-fitting one. Its nature as a toolset is a strategic advantage, prioritizing adaptability and user-driven configuration over prescriptive rigidity.</p>
<p>My approach to building augmented awareness centers on creating a collection of adaptable, loosely connected components instead of a single, pre-packaged application. This design philosophy provides significant advantages, particularly in flexibility and the ability to integrate discoveries organically during development, making it a strength rather than a limitation.</p>
<p>A practical example of this modular benefit emerged unexpectedly. While journaling, I experimented with using a Large Language Model (LLM) directly within my note-taking application, Obsidian, via the <em>Local GPT</em> plugin. I fed it scattered thoughts and diary entries, and the LLM (specifically, Google's Gemini 2.5 Pro model) synthesized these into a coherent discourse. This process strikingly mirrors the concept of Niklas Luhmann's <a target="_blank" href="https://en.wikipedia.org/wiki/Zettelkasten"><strong>Zettelkasten</strong></a> method – a system for networked thought where individual notes connect to generate emergent ideas, a topic I'm exploring through the book <a target="_blank" href="https://www.amazon.it/System-Writing-Unconventional-Note-Making-Zettelkasten/dp/B0D7GX2J9L"><strong>A System for Writing</strong></a>. The LLM acted as a powerful tool for this kind of knowledge discovery, connecting ideas I hadn't explicitly linked myself. This capability, however, also raises an important question as I reflected: does relying on LLMs for synthesis risk a form of mental laziness, potentially diminishing my own ability to formulate and pursue complex questions systematically?</p>
<p>If I were committed to building augmented awareness as a monolithic, shrink-wrapped system, integrating such a sophisticated journal-analysis feature would require significant dedicated development. This might involve building a complex Retrieval-Augmented Generation (RAG) system – a technique where the LLM retrieves relevant information from a knowledge base (like my Obsidian notes) before generating a response – specifically for my notes. Undertaking such a task could potentially span weeks or months reflecting my earlier thoughts on avoiding premature complexity by starting simple. While this integrated analysis is a desirable long-term goal for the <strong>aww-agents</strong> component, the modular, toolset-based approach allows me to <em>immediately</em> leverage the existing capabilities of <em>Local GPT</em> within Obsidian. I can experiment with its output <em>now</em> and build upon it incrementally. For example, future steps could involve refining aww’s input by passing entire Markdown journal pages instead of just specific events, or enhancing my own LLM integrations to execute external tools or navigate wiki-links within my notes. This effectively extends the system's capabilities without needing to replicate the core LLM synthesis function from scratch at this stage.</p>
<p>This strategy aligns directly with the principle of using low-fidelity prototypes before committing to extensive development. By treating components like the LLM integration as experiments built on existing tools, I can quickly assess their usefulness, refine the requirements (like choosing what data is truly valuable to record), and manage complexity before investing heavily in custom implementations. It allows the system to evolve based on practical needs and discoveries, rather than a rigid upfront design.</p>
<h1 id="heading-the-aww-ooda-loop-simplifying-llm-integration-for-practical-operations">The Aww OODA Loop: Simplifying LLM Integration for Practical Operations</h1>
<p>Aww leverages the <strong>OODA loop</strong> (Observe, Orient, Decide, Act)—a framework renowned for facilitating rapid decision-making in dynamic situations—to enable the practical, straightforward use of Large Language Models (LLMs) within operational cycles. This approach deliberately aims for a "low-fi, low-complexity" implementation, making advanced AI capabilities accessible without demanding deep technical expertise or complex infrastructure overhauls.</p>
<p>Here’s how Aww maps LLM operations onto the OODA loop:</p>
<ol>
<li><p><strong>Observe:</strong> The cycle begins with gathering relevant information from the operational environment.</p>
</li>
<li><p><strong>Orient:</strong> This crucial phase involves making sense of the observed data. Here, Aww facilitates interaction with an LLM. It sends the observed data, potentially enriched with relevant context, along with a carefully crafted prompt (instructions) to an LLM. The LLM processes this input and generates an output—perhaps a concise summary, a suggested classification, a draft response, potential root causes for an alert, or an identified anomaly. The LLM acts as a powerful assistant in this orientation phase, rapidly synthesizing information or suggesting interpretations that might take a significant time, effort and, crucially, attention to develop on my own.</p>
</li>
<li><p><strong>Decide:</strong> Armed with the LLM's output and other relevant factors (like business rules or situational context), a decision is made. This decision might be executed by a human operator (e.g., approving the LLM-drafted email reply, confirming the LLM's suggested ticket routing) or triggered by automated rules within the Aww framework (e.g., automatically escalating an alert if the LLM identifies it as critical).</p>
</li>
<li><p><strong>Act:</strong> The decision translates into action within the operational environment. Aww components execute the chosen course, triggering an automated remediation script, or logging the event.</p>
</li>
</ol>
<p>The "low-fi, low-complexity" aspect means Aww abstracts much of the technical difficulty typically associated with deploying AI models. Users interact with the LLM through the structured OODA process facilitated by Aww, focusing on defining the <em>task</em> for the LLM (via prompts and context) rather than managing the underlying model infrastructure, complex fine-tuning, or intricate API integrations.</p>
]]></content:encoded></item><item><title><![CDATA[Augmented Awareness: A Good Start]]></title><description><![CDATA[I am quite pleased with the results from the first week of development of Augmented Awareness, my local-fist/off-line first toolset for quantified-self experiments (github). Keep in mind that this is a personal project, and I can only work on it outs...]]></description><link>https://rlupi.com/augmented-awareness-a-good-start</link><guid isPermaLink="true">https://rlupi.com/augmented-awareness-a-good-start</guid><category><![CDATA[Quantified Self]]></category><category><![CDATA[personal-project]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Mon, 07 Apr 2025 17:16:00 GMT</pubDate><content:encoded><![CDATA[<p>I am quite pleased with the results from the first week of development of Augmented Awareness, my local-fist/off-line first toolset for quantified-self experiments (<a target="_blank" href="https://github.com/robertolupi/augmented-awareness">github</a>). Keep in mind that this is a personal project, and I can only work on it outside of my regular working hours and on weekends.</p>
<p>I made quick progress because I had experimented with various ideas before, so I knew exactly what to implement. I used AI extensively to speed up coding and prototyping ideas. Since a large part of the system I envision is AI-based, it was very helpful to prototype interactions before fully developing them in code.</p>
<h1 id="heading-design-decisions">Design Decisions</h1>
<p>This week I made significant progress in choosing my technology stack:</p>
<ul>
<li><p><strong>Python</strong> for quick prototyping and fast software development. If performance-critical code is needed, I'll switch to <strong>Rust</strong>. Alternatively, porting to <strong>Julia</strong> is also an option.</p>
</li>
<li><p>I don't need separate solutions for frontend development because there are Python libraries that provide the support I need. If new requirements come up, I will use <strong>TypeScript</strong> and Svelte or React.</p>
</li>
<li><p><strong>C++</strong> or <strong>Rust</strong> for embedded code, with <strong>MicroPython</strong> as an option for prototyping.</p>
</li>
<li><p><strong>Kotlin</strong> for mobile development, because my initial focus is primarily on Android.</p>
</li>
<li><p><strong>SQLite for metadata</strong>: Provides lightweight, efficient storage for the system's metadata with excellent reliability and performance characteristics.</p>
</li>
<li><p><strong>Arrow as internal data format</strong>: Enables high-performance in-memory analytics with zero-copy reads and efficient data sharing between components.</p>
</li>
<li><p><strong>Parquet for data lake</strong>: Gives me columnar storage efficiency for temporal datasets while maintaining compatibility with my Arrow-based processing.</p>
</li>
</ul>
<p>Development will initially focus on offline-first on a single machine, and then shift to local-first, aiming for easy deployment on homelabs using Docker when I introduce multi-device support.</p>
<p>My design thinking has advanced through:</p>
<ul>
<li><p><strong>OODA loop implementation</strong>: Successfully structured the codebase around the Observe-Orient-Decide-Act (OODA) cycle for optimal decision flow.</p>
</li>
<li><p><strong>TAQS model adoption</strong>: defined the <em>Temporally Aware Quantified Self</em> (TAQS) model–inspired by sequence of tenses in Latin (<a target="_blank" href="https://en.wikipedia.org/wiki/Sequence_of_tenses">consecutio temporum</a>)–which provides comprehensive handling of:</p>
<ul>
<li><p>Temporal modalities (past, present, future, hypothetical)</p>
</li>
<li><p>Event hierarchies and concept relationships</p>
</li>
<li><p>Point and span events with duration</p>
</li>
<li><p>Data provenance through source tracking</p>
</li>
</ul>
</li>
<li><p><strong>Concept hierarchy discovery</strong>: Both top-down and bottom-up approaches are yielding interesting insights into our data structures. It need to support both concepts expressed in symbolic representation and natural language, and in sub-symbolic form as probability distributions or tensor embeddings.</p>
<ul>
<li>A key technical achievement this week was figuring out round-trip temporal encoding, allowing seamless conversion between human-readable temporal concepts (like "around 6am" or "between 9am and 11am") and precise data structures. This capability enables both intuitive user interactions and rigorous temporal calculations, while maintaining full data provenance through the TAQS model's temporal modalities. The system will handle complex temporal relationships while preserving the semantic meaning of time expressions across the entire OODA loop.</li>
</ul>
</li>
</ul>
<h1 id="heading-implemented-features">Implemented Features</h1>
<ul>
<li><p><strong>obsidian tasks</strong>: collect and display tasks from dated pages (journal) in Obsidian.</p>
</li>
<li><p><strong>obsidian info</strong>: Show detailed information about an Obsidian page: tracked events, tasks, tags, markdown content.</p>
</li>
<li><p><strong>obsidian busy</strong>: Shows total time spent in various activities by tag:</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744020626370/173d3b9a-274a-47e4-9ac9-29f70784f65a.png" alt class="image--center mx-auto" /></p>
<ul>
<li><strong>obsidian tips</strong>: use local LLMs to provide tips for a more wholesome life and answer questions, using the schedule as context:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744021146833/c9db2816-b9c5-4403-bf08-8fde06ce6f36.png" alt class="image--center mx-auto" /></p>
<ul>
<li><strong>obsidian web</strong>: a web interface presenting the same information as obsidian busy and task:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744021408836/81f45ab3-6ff5-4a0d-924a-bba910b9a116.png" alt class="image--center mx-auto" /></p>
<ul>
<li><strong>ActivityWatch</strong>: library support to read from activitywatch and export afk/not-afk, web history, current window as Arrow tables.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How Augmented Awareness Evolved Over Time]]></title><description><![CDATA[Augmented Awareness is the system I developed to manage my daily life during the pandemic. It evolved into an early-stage toolset for quantified self-experiments.
I will work on the next version of Augmented Awareness openly on GitHub, instead of pri...]]></description><link>https://rlupi.com/how-augmented-awareness-evolved-over-time</link><guid isPermaLink="true">https://rlupi.com/how-augmented-awareness-evolved-over-time</guid><category><![CDATA[Quantified Self]]></category><category><![CDATA[agentic AI]]></category><category><![CDATA[Pomodoro Technique]]></category><category><![CDATA[obsidian]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Mon, 31 Mar 2025 07:21:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1743401170296/bf167341-d467-4e77-a36b-3218507473db.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Augmented Awareness is the system I developed to manage my daily life during the pandemic. It evolved into an early-stage toolset for quantified self-experiments.</p>
<p>I will work on the next version of <a target="_blank" href="https://rlupi.com/augmented-awareness">Augmented Awareness</a> openly on <a target="_blank" href="https://github.com/robertolupi/augmented-awareness">GitHub</a>, instead of privately. It currently contains an Arduino pomodoro timer and a library to read Obsidian vaults.</p>
<h1 id="heading-agau">AgAu</h1>
<p>The new version is called <strong>Aww</strong>. The previous version was named <strong>AgAu</strong> and had a silver/gold Yin and Yang logo, but since crypto companies like that acronym too much, and I've discovered the benefits of meditation, I'm choosing cute meditating cats as the new theme.</p>
<p>The first version of Augmented Awareness wasn't a traditional application. It was a no-code system using Google Sheets, Nest devices, and IFTTT. It worked well until Google sunset the Assistant features I depended on.</p>
<p>The core of next and current version, AgAu, is a traditional web application. I built a few prototypes in Python (<a target="_blank" href="https://www.djangoproject.com/">Django</a>, <a target="_blank" href="https://fastapi.tiangolo.com/">fastapi</a>) and TypeScript (<a target="_blank" href="https://svelte.dev/">Svelte</a>, SvelteKit and <a target="_blank" href="https://www.prisma.io/typescript">Prisma</a>), but eventually choose to write it in C# as an <a target="_blank" href="https://learn.microsoft.com/en-us/aspnet/core/blazor/?view=aspnetcore-9.0">ASP.NET Core Blazor</a> application. It is a quite traditional design, based on <a target="_blank" href="https://learn.microsoft.com/en-us/ef/core/">EntityFramework Core</a> and Clean architecture.</p>
<pre><code class="lang-mermaid">flowchart LR
subgraph AgAu

app[ASP.NET core app] --&gt; db[(database)]

end

subgraph obsidian

  notes
  schedule

end

daily[offline behavior and activities] -.manually copy information .-&gt; AgAu
wearables -.manually copy information .-&gt; AgAu
screen[ScreenTime and ActivityWatch] -.manually copy information .-&gt; AgAu
obsidian -.manually copy information .-&gt; AgAu
pomodoro[physical pomodoro timers] -.manually copy information .-&gt; AgAu
</code></pre>
<p>AgAu lost some important properties of the first version: it needs regular busy work to operate. I have to enter or copy over the information of my actions and behavior. This happened because I didn't have a <a target="_blank" href="https://rlupi.com/rewriting-code-discover-the-key-question-you-should-be-asking">clear understanding of my requirements</a>. I didn’t spell them out, so I didn’t preserve them!</p>
<p>The original Augmented Awareness, through its Google Home interface, was easy to use during my daily activities, tasks, and routines. AgAu lost its ambient intelligence and pervasive computing capabilities.</p>
<p>I appreciated the original system's ability to work with minimal attention required:</p>
<p>$$Value={{Functionality} \over {Attention Required}}$$</p><h1 id="heading-aww">Aww</h1>
<p>Aww won’t initially replace AgAu but will complement it. The first step in developing Aww is to bring back the lost ambient computing features by replacing manual data entry with IoT devices and software integrations. I will create Aww components to connect with AgAu using REST services, and eventually, I might retire or open-source AgAu. You can use Aww as a toolkit of independent components, mix and match them and create your own quantified-self experiments or life management systems.</p>
<p>Aww will be a lot more than just a bunch of IoT devices. I want it to adapt to my behavior, be contextual and fade in the background.</p>
<h2 id="heading-core-principles-and-design-philosophy">Core Principles and Design Philosophy</h2>
<ul>
<li><p><strong>Ambient &amp; Permacomputing:</strong> The system operates in the background, minimizing direct user interaction. It leverages existing habits and routines, integrating seamlessly into the user's life rather than demanding constant attention. Data collection is largely passive. Computation is prioritized locally, using minimal resources.</p>
</li>
<li><p><strong>Agentic AI (OODA Loop):</strong> The core intelligence is built around the Observe-Orient-Decide-Act loop, creating a proactive and adaptive system.</p>
</li>
<li><p><strong>Local-First &amp; Privacy-Preserving:</strong> All raw data processing happens locally. Cloud APIs are used <em>judiciously</em> and only for high-level, anonymized inferences.</p>
</li>
<li><p><strong>Modularity &amp; Extensibility:</strong> The architecture is designed to be modular, allowing for easy swapping of components (different sensors, AI models, UI elements) and future expansion.</p>
</li>
<li><p><strong>User Control &amp; Transparency:</strong> The user maintains full control over the system's actions and can override recommendations or adjust settings at any time. The reasoning behind decisions should be explainable (to the extent possible with AI).</p>
</li>
<li><p><strong>Multi-Modal Data Fusion:</strong> The system combines data from diverse sources to build a holistic understanding of the user's state and environment.</p>
</li>
</ul>
<h2 id="heading-data-sources">Data Sources</h2>
<ul>
<li><p><strong>Wearable Sensors (HealthConnect):</strong></p>
<ul>
<li><p>Source: Android wearable devices via HealthConnect API.</p>
</li>
<li><p>Data: Heart rate, sleep stages, activity levels, steps, etc. (timeseries data)</p>
</li>
<li><p>Processing: Local aggregation, feature extraction (e.g., resting heart rate, sleep efficiency).</p>
</li>
</ul>
</li>
<li><p><strong>Environmental Sensors:</strong></p>
<ul>
<li><p>Source: RTL-SDR (rtl_433MHz) for door/cabinet openings (e.g. repurposed door alarm to count fridge openings), temperature/humidity sensors, air quality sensors. Could also include a smart electricity meter, if available.</p>
</li>
<li><p>Data: Event timestamps (door openings), environmental readings (timeseries).</p>
</li>
<li><p>Processing: Anomaly detection (e.g., unusual fridge opening frequency), data smoothing, and aggregation.</p>
</li>
</ul>
</li>
<li><p><strong>Home State Imagery:</strong></p>
<ul>
<li><p>Source: Periodic snapshots from strategically placed low-resolution cameras (e.g., repurposed webcams, IoT cameras).</p>
</li>
<li><p>Data: Images of key areas of the home (e.g., living room, kitchen).</p>
</li>
<li><p>Processing: <em>Local</em> object detection and spatial analysis (using a small, efficient model like YOLO-NAS or MobileNet). Count objects, identify misplaced items, assess overall "clutter" level. This data is <em>never</em> sent to the cloud in image form. Images are automatically deleted after analysis.</p>
</li>
</ul>
</li>
<li><p><strong>Journal/Notes (Obsidian):</strong></p>
<ul>
<li><p>Source: User's Obsidian vault (Markdown files).</p>
</li>
<li><p>Data: Text entries, tags, timestamps.</p>
</li>
<li><p>Processing: Local natural language processing (sentiment analysis, topic extraction, keyword detection) using small, efficient models (e.g., Sentence Transformers for embeddings, or smaller LLMs).</p>
</li>
</ul>
</li>
<li><p><strong>User Feedback &amp; Input:</strong></p>
<ul>
<li><p>Source: Web UI, potentially voice input via a local speech-to-text model.</p>
</li>
<li><p>Data: Explicit feedback on suggestions (thumbs up/down), manual goal setting, overrides.</p>
</li>
<li><p>Processing: Direct incorporation into the decision-making process.</p>
</li>
</ul>
</li>
<li><p><strong>Weather and Indoor environment Information:</strong></p>
<ul>
<li><p>Source: A local weather station, or data from stations close to the user. Air quality monitors, environmental monitors.</p>
</li>
<li><p>Data: Temperature, rain, cloud cover. Indoor humidity, temperature, air quality, radon levels.</p>
</li>
<li><p>Processing: Aggregation and analysis.</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-agentic-ooda-loop">Agentic OODA Loop</h2>
<p>The system is based on agentic AI and robotics principles, themselves inspired by the <a target="_blank" href="https://www.mca-marines.org/gazette/ooda-loop-for-strategy/">OODA loop from the US military doctrine</a>:</p>
<ul>
<li><p><strong>Observe:</strong> Gathers data from the Data Processing Layer. This is the input to the Orient phase.</p>
</li>
<li><p><strong>Orient:</strong></p>
<ul>
<li><p><strong>Contextualization:</strong> Combines current observations with historical data, user preferences, and external knowledge (e.g., general health guidelines).</p>
</li>
<li><p><strong>Pattern Recognition:</strong> Identifies patterns and anomalies in the user's behavior and environment. This might involve time-series analysis, clustering, or anomaly detection algorithms.</p>
</li>
<li><p><strong>Learning:</strong> Updates internal models based on new data and user feedback. This could involve simple rule updates, reinforcement learning, or fine-tuning of pre-trained models. This step leverages local LLMs for tasks like summarizing journal entries, identifying recurring themes, and relating observations to potential underlying causes. <em>Judicious</em> use of cloud LLMs (OpenAI API) might be used for complex reasoning or knowledge retrieval, but <em>only</em> with anonymized, high-level summaries.</p>
</li>
</ul>
</li>
<li><p><strong>Decide:</strong></p>
<ul>
<li><p><strong>Goal Evaluation:</strong> Assesses progress towards user-defined goals (e.g., improve sleep quality, reduce stress, maintain a tidy home).</p>
</li>
<li><p><strong>Planning:</strong> Generates potential actions (tips, reminders, schedule adjustments) to help the user achieve their goals. This could involve constraint satisfaction, optimization algorithms, or a rule-based system.</p>
</li>
<li><p><strong>Risk Assessment:</strong> Evaluates the potential negative consequences of actions (e.g., suggesting a workout when the user is already fatigued).</p>
</li>
</ul>
</li>
<li><p><strong>Act:</strong></p>
<ul>
<li><p><strong>Action Execution:</strong> Delivers interventions to the user via appropriate channels:</p>
<ul>
<li><p><strong>Web UI:</strong> Detailed information, explanations, and options for customization.</p>
</li>
<li><p><strong>Wearable Notifications:</strong> Brief, timely reminders or prompts.</p>
</li>
<li><p><strong>IoT Dashboards (e-Paper):</strong> Visual summaries, schedules, or motivational messages.</p>
</li>
</ul>
</li>
<li><p><strong>Feedback Monitoring:</strong> Tracks user responses to interventions (explicit feedback, changes in behavior).</p>
</li>
</ul>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Speak up! Publish! Seize your power.]]></title><description><![CDATA[My greatest professional regret over the past 13 years is that I chose to remain silent.
When I joined my current employer, a FAANG company, in 2012, I was initially put off by the stringent gatekeeping policies regarding my content creation. The soc...]]></description><link>https://rlupi.com/speak-up-publish-seize-your-power</link><guid isPermaLink="true">https://rlupi.com/speak-up-publish-seize-your-power</guid><category><![CDATA[writing]]></category><category><![CDATA[creativity]]></category><category><![CDATA[#power]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Wed, 26 Mar 2025 06:20:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/KFTob8JDrSY/upload/46d14128eba9c30900e845f7efa6c686.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>My greatest professional regret over the past 13 years is that I chose to remain silent.</p>
<p>When I joined my current employer, a FAANG company, in 2012, I was initially put off by the stringent gatekeeping policies regarding my content creation. The social media guidelines that dictated the topics I could discuss and open-source rules that governed the content and format of my personal code contributions. This is in stark contrast to my previous experiences in smaller companies and as a self-employed consultant.</p>
<p>I found it utterly unacceptable that even my own creations, made during my personal time, would be owned or controlled to such an extent by my employer. Therefore, I made a decision: "If that's the case, then I won't publish." It was a dire mistake.</p>
<p>I misunderstood the nature of power. As many tech guys do. My decision wasn't a way to assert myself, but a forfeit. I submitted to the will and yoke of my employer.</p>
<p>Power is dialectic. It's a dance to constantly redefine the relationship between your Self and the Other, it's defined by finding yourself at ease in the world. However, if you fail to push boundaries, your power diminishes, and if you neglect to exercise it, it gradually fades away. If you remain silent, you relinquish your power altogether.</p>
<p>Moreover, writing and creating are tools for thinking. If you do it only for yourself, you limit yourself to a monologue and diminish your potential. If you cease creating and testing your work by sharing it with others, you lose.</p>
<p>It's time to change. I'll now write, code, and publish more freely and frequently.</p>
]]></content:encoded></item><item><title><![CDATA[Rewriting Code? Discover the Key Question You Should Be Asking]]></title><description><![CDATA[Writing new software is a journey of research, discovery, and creation. Like any research endeavor, it requires a clear guiding question to establish direction. This question is only answered at the conclusion of the process. Moving in one direction ...]]></description><link>https://rlupi.com/rewriting-code-discover-the-key-question-you-should-be-asking</link><guid isPermaLink="true">https://rlupi.com/rewriting-code-discover-the-key-question-you-should-be-asking</guid><category><![CDATA[requirements]]></category><category><![CDATA[creativity]]></category><dc:creator><![CDATA[Roberto Lupi]]></dc:creator><pubDate>Sun, 23 Mar 2025 14:42:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/PTw_2g55U8w/upload/954eebb4ddc86fef715168de888572c6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Writing new software is a journey of research, discovery, and creation</strong>. Like any research endeavor, it requires a clear guiding question to establish direction. This question is only answered at the conclusion of the process. Moving in one direction means temporarily setting aside other possibilities. Building momentum demands a sustained commitment to a chosen path. Without this commitment, you will repeat false starts over and over. If you find yourself frequently starting over, take a moment to clarify the question you are implicitly trying to answer.</p>
<p><strong>Any research starts with a clear question that gives direction</strong>. Similarly, any new software begins with a clear goal and a set of requirements you want to meet. In personal projects, it's easy to skip this step and end up starting over repeatedly, getting lost in the excitement of creation for its own sake.</p>
<p><strong>Any decision to move in a particular direction means temporarily foregoing other options</strong>. In software development, choosing a specific technology stack, such as opting for React for the front end and Node.js for the back end, means setting aside alternatives like Blazor or Python/Django, at least for the time being. This choice influences the project's architecture, the skills required, and the potential trade-offs. Personal projects come with their own challenges because working alone is both freeing and limiting.</p>
<p><strong>Building momentum requires a steady commitment to a chosen direction</strong>. Without this commitment, false starts become common. Constantly changing direction wastes time and resources and lowers morale. Each shift means relearning new tools and adapting to different approaches, which slows down overall progress.</p>
<p>If the direction you're heading in turns out to be wrong or misleading, don't just go back to writing software from scratch. Instead, revisit your implicit requirements. <strong>If you find yourself restarting projects again and again, it's crucial to clarify the core problem you're trying to address</strong>.</p>
]]></content:encoded></item></channel></rss>