Your AI agent has amnesia. You’ve just normalized it.

Three hackathons in three months. Three different agent setups. Three times I wrote a system prompt that said something like “you are a helpful assistant that can use tools and reason across steps.” Three times I shipped something, closed the terminal, and the agent forgot everything it had ever done.

I didn’t think of it as a problem. I thought of it as just how agents work.

That’s the part worth interrogating.

There’s a normalization happening in how we build with AI. We’ve collectively agreed that agents are stateless, that every session starts cold, that the “memory” problem is either solved by shoving things into a context window or punted to some RAG pipeline you bolt on after the fact. We treat amnesia as a default and then we build around it. Bigger context windows. Better prompts. Smarter retrieval.

But none of that solves the actual problem, which is: the agent isn’t getting better at working with you specifically. It’s not accumulating judgment. It’s not noticing that you always want JSON back, or that your last three API integrations broke on rate limits, or that you prefer terse explanations over verbose ones. Every session, you re-establish who you are. Every session, the agent starts from scratch.

That’s not intelligence. That’s a very fast amnesiac with a lot of knowledge.

The thing that made me stop and actually think was this line from Hermes Agent’s README:

“The self-improving AI agent… it creates skills from experience, improves them during use, nudges itself to persist knowledge, searches its own past conversations, and builds a deepening model of who you are across sessions.”

Not “we have a memory feature.” Not “long-term context support.” Skills from experience. A deepening model of who you are.

That’s a different claim. And it’s architectural, not cosmetic.

When Hermes completes a task, it can crystallise what it did into a reusable skill, tag it, store it, and pull from it next time a similar problem comes up. The first time you ask it to do something gnarly, it figures it out. By the third time, it’s refined the approach. This is not a context window trick. The learning loop is built into the agent itself.

Here’s a concrete thing to sit with: imagine you’re onboarding a junior developer. Day one, you explain your codebase, your conventions, your preferences. Day two, they remember. Day thirty, they’re fast because they’ve internalized how you work. Now imagine that junior dev reset every morning with zero memory of the previous day. You’d re-onboard them every single time. You’d eventually stop delegating anything non-trivial because the overhead is too high.

That’s what most of us are doing with agents right now, and calling it “agentic AI.”

Hermes Agent is at least trying to solve the actual problem instead of building better workarounds for it.

The model-agnostic architecture is worth one paragraph because it doesn’t get enough attention. Hermes runs on anything: Claude, GPT-4o, Gemini, 200+ models through OpenRouter, your own endpoint. Switch models with hermes model, no code changes. This matters less as a convenience feature and more as a philosophical stance. The model is not the agent. The learning loop, the skill library, the memory system, the planning layer: those are the agent. The model is just the reasoning engine you slot in. When the next frontier model drops in six months and blows the current one out of the water, Hermes users swap it in with one command. Everyone building on a single-model API foundation has to rearchitect.

The infrastructure story also compounds with everything above. You can run this on a $5 VPS. Not “you can try it on a $5 VPS before you upgrade,” actually run it there. The serverless option means you’re paying essentially nothing when it’s idle. You can message it from Telegram while it does work on a cloud machine. That’s not a demo feature, it’s a deployment story that actually works outside your laptop.

I want to be honest: I haven’t run Hermes for six months and watched it compound knowledge into something that feels like a real collaborator. Neither has basically anyone outside the Nous Research team. The learning loop architecture is genuinely interesting but the proof will be in sustained use cases, and those take time to surface.

But here’s what I do know: the agents I’ve built and shipped have all hit the same ceiling. They can reason well within a session. They fall apart across sessions. Every improvement I made was either a bigger context window, a more detailed system prompt, or a retrieval system I had to design and maintain separately. None of it made the agent better at working with me. I was compensating for statelessness, not solving it.

Hermes Agent is trying to solve it. The architecture is open source, the model layer is swappable, and the whole thing runs on infrastructure you control.

In a space where most “agents” are prompt chains with an API call attached, that’s worth taking seriously.

[NousResearch/hermes-agent](https://github.com/NousResearch/hermes-agent) on GitHub. Quickstart video is linked in the docs. Worth an afternoon.

I built an AI that reviews every PR automatically (because nobody was reviewing mine)

I’ve been vibe coding for months.

Cursor writes the code. Claude fixes the bugs. I ship.

Fast. Really fast.

But nobody was reviewing my code.

Not me. Not a teammate. Nobody.

I’d read the diff quickly, it looked fine, I merged it.

Then a user found the bug I missed.

They didn’t email me. They just left.

The real problem with vibe coding

AI helps you write code faster than ever.

It does not stop you from shipping broken code.

You prompt Claude. It writes 200 lines.
You read it once. Looks fine. You merge it.

But did you check what happens when
the user object is null?

Did you check what happens when the API times out?

You didn’t. Nobody does.

The bugs that kill your SaaS

They’re not exotic. They’re embarrassing.

// null not handled
const user = getUser(id)
console.log(user.email) // crashes in prod

// missing await
const data = fetchUserData(id)
return data.profile // returns Promise not data

// hardcoded secret
const stripe = new Stripe("sk_live_4eC39...")
// committed to GitHub. game over.

Basic stuff. The kind of thing a junior dev
catches in 30 seconds.

But you have no junior dev.

What I built

PushSafe connects to your GitHub repos and
automatically reviews every PR you open.

Posts inline comments on the diff.
Like a teammate who never sleeps.

Stack: Next.js · Supabase · GitHub Webhooks ·
OpenRouter · Vercel

What it catches

  • Null/undefined crashes
  • Missing await
  • SQL injection
  • Hardcoded secrets
  • Logic errors
  • Unhandled promises

What it ignores

  • Formatting
  • Semicolons
  • Indentation
  • All the noise

Your linter handles style.
PushSafe handles the stuff that wakes you up at 3am.

Try it

pushsafe — free, no credit card, 2 min setup.

Follow along on X kshaaneali

🌿 Git Mastery: The Complete Developer Guide

From your first commit to advanced branching strategies — everything you need to version control like a pro

Why Git?

Every file you’ve ever accidentally deleted, every “final_v3_REAL_final.js” you’ve created — Git is the solution to all of that. It’s a distributed version control system that tracks every change to your codebase, lets you experiment without fear, and enables teams of hundreds to collaborate without stepping on each other.

Git isn’t just a tool — it’s the backbone of modern software development. GitHub, GitLab, Bitbucket — they’re all built on top of it.

Here’s the mental model before we dive in:

  • Repository (repo) → A project tracked by Git (the .git folder)
  • Commit → A snapshot of your files at a point in time
  • Branch → An independent line of development
  • Remote → A copy of the repo hosted elsewhere (GitHub, GitLab, etc.)
  • Working tree → The files you’re currently editing
  • Staging area (index) → Where you prepare changes before committing

Prerequisites

  • Git installed: git-scm.com
  • A terminal you’re comfortable with
  • (Optional) A GitHub account for remote repos

Verify your install:

git --version
# git version 2.x.x

Part 1: First-Time Setup

Before your first commit, tell Git who you are. This info is embedded in every commit you make.

git config --global user.name "Your Name"
git config --global user.email "[email protected]"

# Set your default editor (VS Code shown here)
git config --global core.editor "code --wait"

# Set default branch name to 'main'
git config --global init.defaultBranch main

# Verify your config
git config --list

These settings live in ~/.gitconfig and apply to every repo on your machine. You can override them per-repo by dropping the --global flag.

Part 2: Starting a Repository

From scratch

mkdir my-project && cd my-project
git init
# Initialized empty Git repository in .git/

From an existing remote

git clone https://github.com/user/repo.git

# Clone into a specific folder name
git clone https://github.com/user/repo.git my-folder

# Clone only the latest snapshot (faster for large repos)
git clone --depth 1 https://github.com/user/repo.git

Part 3: The Core Workflow — Stage, Commit, Repeat

This is the heartbeat of Git. Everything else builds on it.

Working Tree  →  Staging Area  →  Repository
  (edit)           (git add)       (git commit)

Checking status

git status              # What's changed? What's staged?
git status -s           # Short format: M = modified, A = added, ? = untracked

Staging changes

git add file.js             # Stage a specific file
git add src/                # Stage an entire directory
git add .                   # Stage everything in the current directory
git add -p                  # Interactive: stage changes chunk by chunk

Pro tip: git add -p is one of Git’s most underused features. It lets you review and selectively stage individual hunks of changes — perfect for keeping commits focused and atomic.

Committing

git commit -m "feat: add user authentication"

# Stage all tracked files and commit in one step
git commit -am "fix: correct typo in error message"

# Open your editor for a detailed commit message
git commit

Writing good commit messages

Follow the Conventional Commits format:

<type>(<scope>): <short summary>

<optional body>

<optional footer>

Common types: feat, fix, docs, refactor, test, chore

# Good
git commit -m "feat(auth): add JWT refresh token rotation"
git commit -m "fix(api): handle null response from payment gateway"
git commit -m "docs: update README with Docker setup instructions"

# Bad
git commit -m "stuff"
git commit -m "fix"
git commit -m "asdfgh"

Part 4: Viewing History

git log                          # Full log
git log --oneline                # Compact: one commit per line
git log --oneline --graph        # ASCII branch graph
git log --oneline --graph --all  # Include all branches

# Filter by author
git log --author="Jane"

# Filter by date
git log --since="2 weeks ago"
git log --after="2024-01-01" --before="2024-06-01"

# Search commit messages
git log --grep="authentication"

# See what changed in each commit
git log -p

# Show stats (files changed, insertions, deletions)
git log --stat

Inspecting a specific commit

git show abc1234              # Show commit details + diff
git show abc1234:src/app.js   # Show a file as it was at that commit

Comparing changes

git diff                      # Unstaged changes vs last commit
git diff --staged             # Staged changes vs last commit
git diff main feature-branch  # Diff between two branches
git diff abc1234 def5678      # Diff between two commits

Part 5: Branching — Git’s Superpower

Branches are cheap and fast in Git (just a pointer to a commit). Use them liberally.

git branch                    # List local branches
git branch -a                 # List local + remote branches
git branch feature/login      # Create a new branch
git switch feature/login      # Switch to it
git switch -c feature/login   # Create AND switch in one command

# Old syntax (still works everywhere)
git checkout -b feature/login

Merging branches

# Switch to the branch you want to merge INTO
git switch main

# Merge feature branch
git merge feature/login

# Merge with a commit even if fast-forward is possible (preserves history)
git merge --no-ff feature/login

Fast-forward vs merge commit:

Fast-forward (linear history):
main: A → B → C → D → E
                        ↑ feature merged cleanly

Merge commit (preserves branch context):
main: A → B → C → M
                ↗   ↑ merge commit
feature:    D → E

Use --no-ff when you want a clear record that a feature branch was merged.

Deleting branches

git branch -d feature/login    # Delete (safe — won't delete unmerged branches)
git branch -D feature/login    # Force delete
git push origin --delete feature/login  # Delete remote branch

Part 6: Rebasing — A Cleaner History

Rebase rewrites commit history by replaying your commits on top of another branch. The result is a clean, linear history.

git switch feature/login
git rebase main

Before rebase:

main:    A → B → C
feature:     D → E

After git rebase main:

main:    A → B → C
feature:         D' → E'   (commits replayed on top of C)

Interactive rebase — rewrite history

This is the power tool. Use it to clean up commits before merging:

git rebase -i HEAD~3    # Interactively edit the last 3 commits

In the editor that opens, you can:

Command What it does
pick Keep the commit as-is
reword Keep but edit the commit message
squash Combine with the previous commit
fixup Like squash but discard this commit’s message
drop Delete the commit entirely
edit Pause to amend the commit

⚠️ Golden Rule of Rebasing: Never rebase commits that have been pushed to a shared remote branch. Rebase rewrites history — doing it on shared branches causes pain for everyone.

Part 7: Working with Remotes

git remote -v                           # List remotes
git remote add origin https://github.com/user/repo.git   # Add a remote
git remote rename origin upstream       # Rename a remote
git remote remove origin               # Remove a remote

Pushing and pulling

# Push local branch to remote
git push origin main

# Push and set upstream tracking (then you can just use `git push`)
git push -u origin main

# Pull = fetch + merge
git pull origin main

# Pull with rebase instead of merge (cleaner)
git pull --rebase origin main

# Fetch remote changes without merging
git fetch origin
git fetch --all    # Fetch all remotes

Tracking branches

Once you’ve set an upstream with -u, Git knows which remote branch your local branch corresponds to:

git push      # Pushes to tracked remote branch
git pull      # Pulls from tracked remote branch
git branch -vv  # Shows tracking info for all branches

Part 8: Undoing Things

This is where most developers get nervous. Don’t be — Git almost never truly deletes anything.

Amending the last commit

# Fix the last commit message
git commit --amend -m "correct message"

# Add a forgotten file to the last commit
git add forgotten-file.js
git commit --amend --no-edit   # Keeps the original message

Unstaging files

git restore --staged file.js    # Unstage (keep changes in working tree)
git restore file.js             # Discard working tree changes (DESTRUCTIVE)

Reverting commits (safe — creates a new commit)

git revert abc1234       # Creates a new commit that undoes abc1234
git revert HEAD          # Revert the last commit
git revert HEAD~3..HEAD  # Revert the last 3 commits

Use revert on shared branches — it’s non-destructive.

Resetting (rewrites history — be careful)

git reset --soft HEAD~1   # Undo last commit, keep changes STAGED
git reset --mixed HEAD~1  # Undo last commit, keep changes UNSTAGED (default)
git reset --hard HEAD~1   # Undo last commit, DISCARD all changes

The escape hatch: reflog

Even after a hard reset, Git keeps a log of where HEAD has been. You can recover “lost” commits:

git reflog              # See the full history of HEAD movements
git reset --hard abc1234  # Jump back to any previous state

Part 9: Stashing — Save Work Without Committing

Need to switch branches but you’re mid-feature? Stash it.

git stash                       # Stash all uncommitted changes
git stash push -m "wip: login form validation"  # With a label

git stash list                  # See all stashes
git stash pop                   # Apply most recent stash and remove it
git stash apply stash@{2}       # Apply a specific stash (keep it in the list)
git stash drop stash@{0}        # Delete a specific stash
git stash clear                 # Delete all stashes

# Stash including untracked files
git stash -u

# Create a branch from a stash
git stash branch feature/wip stash@{0}

Part 10: Tags — Marking Releases

git tag                          # List all tags
git tag v1.0.0                   # Lightweight tag (just a pointer)
git tag -a v1.0.0 -m "Release 1.0.0"   # Annotated tag (recommended)

git push origin v1.0.0           # Push a specific tag
git push origin --tags           # Push all tags

git tag -d v1.0.0                # Delete local tag
git push origin --delete v1.0.0  # Delete remote tag

Annotated tags store extra metadata (tagger name, date, message) and can be signed. Use them for releases.

Part 11: The .gitignore File

Tell Git which files to never track:

# Dependencies
node_modules/
vendor/

# Build output
dist/
build/
*.min.js

# Environment & secrets
.env
.env.local
*.pem
*.key

# OS files
.DS_Store
Thumbs.db

# Editor files
.vscode/
.idea/
*.swp

Apply a gitignore to already-tracked files:

# If you added .env to .gitignore but already committed it:
git rm --cached .env
git commit -m "chore: remove .env from tracking"

Find pre-made .gitignore templates for your stack at gitignore.io.

Part 12: Branching Strategies

Git Flow

Best for projects with scheduled releases:

main          ←── stable production code
develop       ←── integration branch
feature/*     ←── new features (branch from develop)
release/*     ←── release prep (branch from develop)
hotfix/*      ←── urgent fixes (branch from main)

GitHub Flow

Simpler — best for continuous deployment:

main          ←── always deployable
feature/*     ←── branch from main, PR back to main, deploy

Trunk-Based Development

Best for mature teams with strong CI/CD:

main          ←── everyone commits here (short-lived branches only)

Choose the strategy that matches your team size and release cadence. GitHub Flow is the right default for most teams.

Git Commands Cheat Sheet

# Setup
git config --global user.name "Name"
git config --global user.email "email"

# Start
git init
git clone <url>

# Daily workflow
git status
git add .
git commit -m "message"
git push
git pull

# Branches
git switch -c feature/name
git merge feature/name
git rebase main
git branch -d feature/name

# Inspection
git log --oneline --graph --all
git diff
git show <commit>

# Undo
git restore --staged <file>
git revert <commit>
git reset --soft HEAD~1
git reflog

# Remote
git remote add origin <url>
git fetch --all
git push -u origin main

Common Pitfalls & How to Avoid Them

❌ Committed secrets or credentials

# Remove a file from ALL history (nuclear option)
git filter-branch --force --index-filter 
  "git rm --cached --ignore-unmatch path/to/secret.env" 
  --prune-empty --tag-name-filter cat -- --all

# Then force push and rotate your credentials immediately
git push origin --force --all

Better: use git-secrets or gitleaks to prevent it happening in the first place.

❌ Merge conflicts

# When a merge conflict occurs, Git marks the file:
<<<<<<< HEAD
  your changes
=======
  incoming changes
>>>>>>> feature/login

# After resolving manually:
git add resolved-file.js
git commit   # or git rebase --continue if rebasing

❌ Pushed to the wrong branch

git revert HEAD           # If others may have pulled it
git push origin --force   # Only if no one else has the commits

❌ git pull creates ugly merge commits

# Use rebase by default
git config --global pull.rebase true

Wrapping Up

Here’s what you’ve learned:

  1. Setup — configuring Git globally for clean commit attribution
  2. Core workflow — stage, commit, push — the heartbeat of Git
  3. Branching — creating, merging, and deleting branches with confidence
  4. Rebasing — rewriting history for a cleaner log
  5. Remotes — pushing, pulling, fetching, and tracking
  6. Undoing — revert, reset, restore, and the reflog safety net
  7. Stashing — saving work-in-progress without a commit
  8. Tags — marking releases with annotated tags
  9. Branching strategies — Git Flow vs GitHub Flow vs Trunk-based
  10. Common pitfalls — handling conflicts, secrets, and wrong-branch pushes

Git rewards practice. The commands that seem scary now (rebase -i, reflog, reset --hard) become second nature once you’ve used them a few times in a safe environment.

What’s Next?

  • GitHub Actions — automate CI/CD triggered by Git events
  • Signed commits — verify your identity with GPG keys
  • git bisect — binary search your history to find which commit introduced a bug
  • Worktrees — check out multiple branches at once in different directories

Found this useful? Drop a ❤️ and follow for more. Got a Git horror story or a tip I missed? Share it in the comments.

Bringing Gemma 4 E2B to the Edge: Building a Privacy-First Dream Analyzer with Flutter & LiteRT

This is a submission for the Gemma 4 Challenge: Write About Gemma 4

Every AI app says it respects your privacy.

Then it uploads your most personal data to the cloud.

When we started building Remora — a dream journaling and psychological interpretation app — we faced a difficult question:

How do you analyze deeply personal subconscious experiences without sending them to a remote server?

We wanted users to wake up, record a dream, and receive rich AI-powered analysis directly on their phone.

No cloud inference.
No persistent uploads.
No centralized storage of emotional or psychological data.

That requirement immediately ruled out most modern AI architectures.

Then we discovered Gemma 4.

Its compact E2B footprint, multimodal support, and mobile-first optimization made it uniquely suited for true on-device inference.

But integrating cutting-edge local AI into a production Flutter app turned out to be far more challenging than expected.

This is the engineering story behind making it work.

Why Gemma 4 Changed the Architecture

Most mobile AI today still relies on a thin-client model:

  1. Capture user data
  2. Upload to cloud APIs
  3. Run inference remotely
  4. Return results

That approach breaks down completely for sensitive psychological analysis.

Dream journals often contain:

  • trauma,
  • fears,
  • relationships,
  • emotional states,
  • deeply personal memories.

We needed:

  • offline capability,
  • low latency,
  • multimodal understanding,
  • and strict data locality.

Gemma 4 E2B gave us a realistic path toward all four.

Running directly on-device also unlocked:

  • instant responses,
  • airplane-mode support,
  • reduced infrastructure cost,
  • and dramatically improved user trust.

Challenge 1: The Model Format Wars (GGUF vs LiteRT)

Our first instinct was straightforward:

Download a .gguf quantization from Hugging Face and wire it into Flutter.

That assumption lasted about five minutes.

The moment the engine initialized on Android, the app crashed with:

IllegalArgumentException:
Unsupported model format: .gguf

What We Learned

The open-source ecosystem heavily favors .gguf because of tools like llama.cpp.

But Android hardware acceleration operates in a very different ecosystem.

Google’s mobile AI stack relies on:

  • MediaPipe,
  • LiteRT,
  • LiteRT-LM delegates,
  • and NPU-optimized tensor layouts.

That means models must be packaged as:

  • .task
  • .bin
  • or .litertlm

—not GGUF.

Once we switched to the official LiteRT package, memory usage dropped significantly and inference stabilized immediately.

FlutterGemma.installModel(
  modelType: ModelType.gemma4,
  fileType: ModelFileType.litertlm,
).fromNetwork(
  'https://huggingface.co/litert-community/gemma-4-E2B-it-litert-lm/resolve/main/gemma-4-E2B-it.litertlm',
).withProgress((progress) {
  print('Downloading 1.5GB Edge Model: ${progress}%');
});

This was our first major realization:

Edge AI is not just “smaller cloud AI.”
It is an entirely different deployment architecture.

Challenge 2: The “Code 13” Audio Crash

One of the most exciting features of Gemma 4 is native multimodal capability.

Our goal was simple:

Users should be able to:

  1. wake up,
  2. tap record,
  3. describe their dream verbally,
  4. and receive private on-device analysis.

We recorded audio and passed it into the local model.

Immediate crash.

We switched encoders:

  • .m4a
  • PCM16 WAV
  • 16kHz mono

Crash again.

Failed to start streaming (code: 13)

The Root Cause

After digging through Google’s AI Edge Gallery implementation, we discovered:

  1. Current community LiteRT weights do not yet expose fully fused audio subgraphs
  2. Qualcomm QNN delegates require certain audio operators to run on CPU
  3. Current Flutter bindings don’t yet support backend splitting between CPU and NPU execution

In practice:

  • text generation worked perfectly,
  • audio tensor routing did not.

The Solution: A Secure Hybrid Pipeline

Instead of abandoning voice support, we built a privacy-preserving fallback architecture.

If local audio inference fails:

  1. audio is sent to a transient speech-to-text endpoint,
  2. no audio is persisted,
  3. only transcription text is returned,
  4. all psychological interpretation still happens locally via Gemma 4.

That preserved the most sensitive part of the workflow entirely on-device.

Future<DreamAnalysisResult> analyzeAudio(String filePath) async {
  if (_localEngine.isReady) {
    try {
      return await _localEngine.analyzeAudio(filePath);
    } catch (e) {
      print('Code 13 detected. Engaging secure fallback.');
    }
  }

  final bytes = await File(filePath).readAsBytes();

  final response = await dio.post(
    '/dreams/transcribe',
    data: bytes,
  );

  final String text = response.data['transcription'];

  final result = await _localEngine.analyzeDream(text);

  return DreamAnalysisResult(
    title: result.title,
    interpretation: result.interpretation,
    tags: [...result.tags, '#voice_log'],
    transcribedText: text,
  );
}

This ended up becoming one of the most important architectural decisions in the app.

Not because it was perfect —
but because it degraded gracefully while preserving privacy guarantees.

Challenge 3: Emulators Lie

During development we tested inference using the Android emulator.

Everything failed instantly.

Connection closed before full header was received

At first we suspected:

  • networking,
  • Flutter isolates,
  • or broken FFI bindings.

None of those were the problem.

The real issue was architecture mismatch.

LiteRT-LM delegates are optimized specifically for:

  • arm64-v8a
  • mobile NPUs
  • physical AI acceleration hardware

The x86 emulator environment simply could not execute the delegate stack correctly.

Once we moved testing onto a physical Pixel device:

  • binaries mapped correctly,
  • NPU acceleration activated,
  • inference latency dropped dramatically.

That moment changed how we approached mobile AI QA entirely.

Edge AI development without real hardware is basically guesswork.

Looking Forward: Android AI Core & Gemini Nano

Downloading a 1.5GB local model works —
but it is not the ideal long-term UX.

Large bundled models create:

  • storage pressure,
  • installation friction,
  • and slower onboarding.

To future-proof the architecture, we integrated Android AI Core support.

Before downloading Gemma 4 locally, Remora now checks whether:

  • Gemini Nano,
  • or another system-level model,
  • is already available through Android’s native AI layer.

If available:

  • inference becomes instant,
  • no model download is required,
  • and privacy remains intact.

This creates a hybrid architecture where:

  • OS-native models are preferred,
  • Gemma 4 acts as the portable fallback,
  • and all inference still remains local-first.

What Building with Gemma 4 Taught Us

Working with Gemma 4 fundamentally changed how we think about mobile apps.

For years, mobile AI has largely meant:

“Call an API and wait.”

But local multimodal models enable something very different:

  • applications that function offline,
  • preserve privacy by default,
  • reduce infrastructure cost,
  • and feel dramatically more responsive.

The tooling ecosystem is still early.
The documentation is fragmented.
The hardware constraints are real.

But the direction is obvious.

Edge AI is becoming a first-class application platform.

And Gemma 4 is one of the first models that genuinely makes that future practical for mobile developers.

Final Thoughts

Remora started as an experiment:

Could we build a psychologically meaningful AI experience without compromising user privacy?

Thanks to Gemma 4, LiteRT, and Android’s emerging edge AI ecosystem, the answer is increasingly yes.

We still have challenges ahead:

  • audio graph support,
  • smaller quantizations,
  • memory optimization,
  • and broader device compatibility.

But for the first time, building truly private multimodal AI apps on smartphones feels achievable.

And that changes everything. What challenges you the most in Edge AI journey?

Google I/O 2026 Wasn’t About Features — It Was About AI Becoming the Developer Environment

This is a submission for the Google I/O Writing Challenge

Google I/O 2026 Wasn’t About Features — It Was About AI Becoming the Developer Environment

Google I/O 2026 felt very different from previous years.

This time, Google wasn’t just announcing tools. It was redefining how developers build software.

From Gemini deeply integrated into development workflows to Firebase becoming increasingly AI-native, the event made one thing clear:

AI is no longer a separate assistant.
It is becoming the development environment itself.

The Announcement That Stood Out to Me

The session that genuinely caught my attention was the evolution of Gemini across the developer ecosystem — especially how it now interacts with coding workflows, cloud tooling, and app development in a far more practical way.

For years, AI coding assistants mostly felt like:

  • autocomplete on steroids
  • chatbots beside the IDE
  • productivity add-ons

But Google’s 2026 direction feels different.

The focus is shifting toward:

  • context-aware development
  • multi-step reasoning
  • agentic workflows
  • AI-assisted architecture decisions
  • full-stack integration with cloud tooling

And honestly, that changes the developer experience completely.

Google I/O keynote stage

The future of development is increasingly AI-native.

What Excited Me Most

1. Gemini Becoming More Than a Chatbot

One of the biggest takeaways for me was how Gemini is evolving from:

“answering coding questions”

to

“understanding development intent.”

That distinction matters.

Modern software engineering is rarely about writing isolated functions anymore.

Real development involves:

  • navigating large codebases
  • debugging architecture issues
  • understanding APIs
  • deployment pipelines
  • cloud infrastructure
  • security considerations
  • performance tradeoffs

Google’s announcements suggest they’re aiming for AI systems that participate in those workflows instead of simply generating snippets.

That’s the first time I’ve felt AI tooling moving toward engineering assistance rather than just code generation.

2. Firebase Is Quietly Becoming an AI Application Platform

Another underrated part of I/O 2026 was Firebase.

Firebase has always been beginner-friendly, but now it feels positioned as a serious rapid AI application platform.

The combination of:

  • authentication
  • hosting
  • databases
  • cloud functions
  • AI integrations
  • analytics

makes it possible for small teams to build surprisingly advanced products very quickly.

For indie developers and hackathon builders, this is huge.

You no longer need massive infrastructure knowledge before experimenting with AI-powered ideas.

Firebase dashboard illustration

Firebase is evolving into a complete AI app ecosystem.

What This Means for Developers

I think Google I/O 2026 signals three major industry shifts:

AI Will Become Infrastructure

Not just a feature.

Developers will increasingly build:

  • with AI
  • for AI
  • around AI agents

AI APIs may soon become as common as databases and authentication systems.

Full-Stack Developers Will Move Faster Than Ever

The gap between:

  • idea
  • prototype
  • deployment

is shrinking rapidly.

Small teams can now create products that previously required entire engineering departments.

That’s both exciting and slightly intimidating.

Developers Need Better Judgment, Not Just Coding Skills

Ironically, as AI gets better at generating code, human value shifts toward:

  • system design
  • product thinking
  • security awareness
  • architecture decisions
  • debugging
  • understanding tradeoffs

The developers who thrive won’t necessarily be the fastest coders.

They’ll be the best decision-makers.

My Small Experiment After Watching I/O

After the keynote, I tried rebuilding a small finance dashboard prototype using AI-assisted workflows.

Instead of manually planning every step, I experimented with:

  • AI-generated backend scaffolding
  • Firebase integrations
  • cloud deployment suggestions
  • rapid UI iteration
  • debugging assistance

What surprised me wasn’t the speed.

It was how much mental overhead disappeared.

I spent less time fighting setup issues and more time refining the actual product idea.

That felt like a meaningful shift.

Developer working with AI tools

AI-assisted workflows are changing how developers think and build.

One Concern I Still Have

Despite all the excitement, I do think the industry is entering a dangerous phase of:

over-reliance on generated code.

AI can accelerate development dramatically.

But it can also:

  • introduce hidden vulnerabilities
  • encourage shallow understanding
  • create maintainability issues
  • increase technical debt

The future probably belongs to developers who know when not to trust AI.

And I think that balance will become one of the most valuable engineering skills of this decade.

Final Thoughts

Google I/O 2026 wasn’t just another product announcement event.

It felt like a preview of a new software development paradigm.

The most important takeaway for me was this:

Developers are no longer just writing software.
We are beginning to collaborate with systems that actively participate in building it.

That changes everything.

And honestly?

We’re probably only at the beginning.

Thanks for reading — I’d love to hear which Google I/O 2026 announcement stood out most to you.

Building an AI Vedic Astrology App in 25 Days — What Actually Worked (and What Didn’t)

25 days ago I had an idea. Today KundliAI is live, indexed by Google, and getting organic traffic.

Here’s the honest story — no fluff.

The Idea

India has 1.4 billion people. A massive percentage consult astrology regularly — for marriage, career decisions, naming babies, starting businesses.

But the existing platforms are either:

  • Outdated UI from 2005
  • English-heavy (most users are Hindi speakers)
  • Expensive (₹500-2000 per consultation)
  • Generic (same horoscope for everyone born in the same month)

I thought — what if I built something that gives genuinely personalized Vedic astrology in Hindi, free, in 30 seconds?

That became KundliAI.

The Stack

Frontend: Next.js 14 (App Router)
Styling: Tailwind CSS
AI: Groq API (Meta's Llama — fast and cheap)
Astrology calculations: AstrologyAPI.com (Swiss Ephemeris)
Payments: Razorpay
Hosting: Vercel
Domain: kundliai.in

Why Groq over OpenAI? Speed. Groq is 10x faster for inference. For a tool where users expect results in 30 seconds — latency matters a lot.

Why AstrologyAPI over doing calculations myself? Swiss Ephemeris is complex. AstrologyAPI wraps it cleanly and gives me planetary positions, Kundli Matching (36 Guna Milan), Mahadasha calculations — all via REST API. Cost is minimal for the accuracy you get.

What I Built — Features

Free Kundli Generator
User enters name, date of birth, time, place. We call AstrologyAPI for planetary positions, then Groq generates a complete personalized reading in Hindi. Takes about 25-30 seconds end to end.

Daily Rashifal (Horoscope)
Not generic — based on actual planetary transits for the day combined with user’s Moon sign. Different from every other rashifal site that gives the same text to all Aries users.

Kundli Matching
36 Guna Milan — the traditional Indian marriage compatibility system. Enter two birth charts, get compatibility score with explanation in Hindi.

Numerology
Name + date of birth → Life Path Number, Destiny Number, Soul Urge Number. Hindi interpretation.

Love Compatibility by Name
Chaldean numerology — enter two names, get compatibility score. Simple but surprisingly popular.

AI Pandit Chat
24/7 chat with an AI that knows Vedic astrology. Ask anything — career, marriage, health, planetary positions.

Panchang
Daily Panchang for 12 Indian cities — Tithi, Nakshatra, Yoga, Rahu Kaal, Abhijit Muhurat.

Paid Reports (₹99)
Detailed 10-page PDF report. One-time payment via Razorpay. First payment came on Day 20.

The Technical Challenges

Challenge 1 — Prompt Engineering for Hindi

Getting Groq/Llama to generate genuinely good Hindi astrology content was harder than expected.

The model kept mixing Hindi and English awkwardly. It would use Sanskrit terms without explaining them. The tone was sometimes too formal, sometimes too casual.

After about 40 iterations of the system prompt, I landed on something that works. Key insights:

  • Give the model a persona (“You are an experienced Vedic astrologer who explains things in simple Hindi”)
  • Specify the audience (“The user is a regular Indian, not an astrology expert”)
  • Give explicit formatting instructions
  • Include examples of good vs bad responses in the prompt

Challenge 2 — Vercel Serverless File Size Limits

My blogs.ts file grew to 500KB+ with 32 blog posts. Vercel’s serverless functions have memory limits and the file was causing cold start issues — only 3 blogs were loading.

Solution: Split into blogs.ts (9 blogs) and blogs2.ts (23 blogs), import both in the page component.

Lesson: Watch your serverless function bundle size. It bites you in production, not in development.

Challenge 3 — SSR vs Client Rendering for SEO

Initially blog pages were client-rendered. Google was seeing empty pages — terrible for SEO.

Migrated to Server Components with force-dynamic. Now Google gets fully rendered HTML with all content. Indexed 2 new blogs in under 10 minutes after this fix.

Challenge 4 — AstrologyAPI Rate Limits

During testing I burned through API credits quickly. Built a simple caching layer — same birth data within 24 hours returns cached results instead of hitting the API again.

Cost dropped significantly. API cost is now ₹8-17 per day even with decent traffic.

The SEO Strategy — What’s Working

I’m not an SEO expert. But here’s what I did:

Blog content in Hindi + English
Most astrology content online is either pure English or poor Hindi. I write detailed bilingual blogs — “Makar Rashi 2026 rashifal” with proper Hindi content. Google seems to like this.

Long-tail keywords
Not “horoscope” (impossible to rank). Instead “makar rashi sade sati 2026 upay” — specific, lower competition, high intent.

FAQ sections on every blog
Google’s featured snippets love FAQ format. I include 10-14 FAQ questions per blog with direct answers.

Schema markup
FAQ schema, Breadcrumb schema, Review schema — all implemented server-side. Google Search Console shows all valid. Rich results in search = higher CTR.

Results after 25 days:

  • 0 to 1,390 impressions
  • 67 clicks
  • 4.8% CTR (industry average is 2-3%)
  • Position 7.6 average
  • First paid customer on Day 20

The Distribution Strategy

SEO takes time. Meanwhile:

Quora — answering astrology questions with genuine helpful content. 51 answers so far. 166 content views this month. Referral traffic is steady.

Featured.com — expert Q&A platform. Submitted 3 answers. One selected by BoostmyDomain for publication. Two in review with Brodox and VisRank. High-DR backlinks incoming.

IndieHackers — sharing the journey. Good for backlinks and community feedback.

Revenue — Honest Numbers

Day 20: First payment — ₹99
Day 25: Total revenue — ₹99-200

Not impressive yet. But the paid report feature works, Razorpay integration is solid, and the funnel is there. It’s a traffic problem right now, not a conversion problem.

What I’d Do Differently

Start SEO content on Day 1
I built features for the first 2 weeks before writing any blogs. Those 2 weeks of content compound over time — I wish I’d started earlier.

Pick a narrower niche first
“AI Vedic astrology” is broad. Starting with “Sade Sati calculator” or “Kundli Matching tool” and dominating that before expanding would have been smarter.

Build in public from Day 1
I started sharing on Day 20. Earlier sharing = earlier feedback = earlier course corrections.

What’s Next

  • Complete all 12 rashi blogs (4 remaining)
  • Reddit community building
  • YouTube Shorts — 30 second rashifal videos
  • WhatsApp channel
  • Month 2 target: ₹5,000 revenue

The Code

Not open source yet — but happy to answer specific technical questions in the comments.

Live at: kundliai.in

Built by one person in 25 days. Zero paid marketing. Everything organic.

If you’re building something for the Indian market — Hindi-first is a massive opportunity. The content gap is real.

Happy to answer any questions about the stack, the prompts, or the SEO approach.

Hermes Agent Has Four Memories — And That’s Why It Doesn’t Forget You

This is a submission for the Hermes Agent Challenge: Write About Hermes Agent

A cognitive-science tour of the only open-source agent that gets smarter the longer you run it. We map Hermes’ file layout onto the four classical memory systems of the human brain — and find an architecture that quietly solves the problem every other agent fakes.

I want to argue something that sounds like marketing but isn’t.

Hermes Agent isn’t impressive because it’s “self-improving.” That phrase has been on a thousand landing pages. It’s impressive because — possibly by accident, possibly on purpose — the architecture matches how a human brain stores memory. Once you see the mapping, the whole project stops looking like a clever Python wrapper and starts looking like a thesis: if you want an agent that compounds, you have to give it the same memory systems we use.

I’m an ML engineer who builds recommendation and intent systems for a living, and I spend a lot of time thinking about the gap between an agent that’s interesting and an agent that holds up after week three. Almost few article write-up of Hermes I’ve read covers the surface — “it remembers things, it has skills, it has crons.” All true. None of them ask the more useful question:

Why this particular decomposition? Why these specific files? Why does a system designed in 2026 look so much like a 1970s cognitive-psychology paper?

That’s the post. Grab a coffee.

The goldfish problem (and why “context window” isn’t the answer)

Every agent in production today has the same failure mode, and we’ve all felt it.

Monday: you carefully explain your project, your stack, the three constraints that always trip up the model. The agent crushes the task. You’re delighted.

Tuesday: you open a fresh session. The agent has no idea who you are. You re-explain. You re-correct. You re-paste the same context blob. This is what people mean when they say agents “feel like demos.” They aren’t building anything that lasts; they’re paying the same context tax every single morning.

The standard responses to this problem are bad:

  • Bigger context windows — expensive, doesn’t scale with how long you actually want the agent to live (months, not turns), and still amnesiac the moment the conversation ends.
  • Vector databases — useful for retrieval, terrible for identity. A RAG store doesn’t know who you are; it knows which 8 chunks resemble your last question.
  • Fine-tuning — slow, expensive, and updates an agent’s beliefs at the speed of GPU procurement, not the speed of Tuesday.

None of these is memory. They’re all variations on “search more text faster.” The reason your brain doesn’t work this way is that your brain has at least four distinct memory systems, and each one solves a different problem. When cognitive psychologists separated them in the 1970s and 80s — names like Tulving, Squire, Cohen — they did it because patients with brain damage would lose one and keep the others. The systems are genuinely independent.

So here’s the simple claim: the reason Hermes Agent works is that it has all four, and they’re separate, and they’re stored on disk where you can read them.

Let me show you.

A 90-second cognitive-science primer

You don’t need a degree to follow this. Four words, four jobs:

System Holds Example
Procedural memory How to do things Riding a bike. Touch-typing. Driving a stick shift.
Semantic memory Facts about the world Paris is the capital of France. Mitochondria are the powerhouse of the cell.
Episodic memory Things that happened to you What you had for breakfast yesterday. The argument you had in 2019.
Working memory What you’re holding right now The phone number someone just said out loud.

There’s a fifth thing that isn’t a memory system but is the substrate the others run inside: your identity — the sense of who you are that makes all four useful instead of just being a pile of disconnected data. Without a self, your “memories” are just other people’s footage.

Now look at this directory listing from a fresh Hermes Agent install:

~/.hermes/
├── SOUL.md           # identity (the substrate)
├── memories/
│   ├── USER.md       # who you are (the agent's model of me)
│   └── MEMORY.md     # what's true in my world right now
├── skills/           # how to do things  (one folder per SKILL.md)
├── sessions/         # SQLite + FTS5: every conversation, searchable
└── cron/             # scheduled jobs that fire in fresh sessions

I’m going to take you through these one at a time and show you which cognitive system each one is doing, why the boundaries are drawn where they are, and what each one prevents from going wrong. We’ll end with the part nobody on dev.to has written about yet: how a feature called the Curator (added in v0.12) functions as the agent’s equivalent of sleep.

SOUL.md — the substrate (identity)

SOUL.md is the first file loaded into the system prompt at every turn. The Hermes architecture docs call it “slot #1.” It contains the agent’s personality, communication style, role, and constraints. It’s about 1,000–3,000 characters in most setups.

“You are a senior research analyst. You write in short paragraphs. You don’t apologize. You never use the word ‘leverage.'”

If you stop reading here you’ll think this is just a system prompt. It isn’t. The trick is what SOUL.md doesn’t contain: facts about your projects, your name, anything that changes week-to-week. Those go in different files. SOUL.md is the slow-moving part — the part that makes a Telegram bot reading your code reviews behave consistently different from one that drafts your customer support replies.

You can run multiple Hermes profiles on the same machine, each with its own SOUL.md. A finance-ops agent and a research agent share the same underlying model but read as different colleagues. This is exactly how human personality works: same neural hardware, different self-concepts shaping the output.

What this prevents: tone drift. The slow, frustrating phenomenon where an agent that was crisp on day 1 has become a verbose, hedging, em-dash-spamming mess by day 30 because nothing was holding its character in place.

USER.md and MEMORY.md — semantic memory (facts about your world)

Open ~/.hermes/memories/USER.md after a few weeks of use and you’ll find something like this (this is illustrative, not from any specific install):

- Name: Manikant
- Role: ML/AI engineer
- Communication: prefers terse responses, no apology preambles
- Working on: prompt-recommendation pipelines, intent taxonomy
- Avoid: marketing language, "leverage", em-dashes in code blocks

And MEMORY.md:

- Active project: CCx personalization pipeline
- Stack: Databricks, FastAPI on AKS, Redis, OneLLM
- p95 target: sub-1s on warm path
- Recent issue: Kong/Okta 403 — fixed by adding personalization_services scope

Two files. One is who you are (slow-changing). One is what’s currently true in your world (medium-changing). Both get injected into the system prompt at session start as a frozen snapshot.

This is semantic memory — declarative facts that the agent can state without re-deriving them. The reason it’s split into two files instead of one is the same reason your brain separates “I am a software engineer” from “the build is currently broken.” One is identity-level. One is operational. Mixing them is how you end up with an agent that introduces itself in every reply.

The character limits matter. The published numbers I’ve seen are roughly USER.md ≤ 1,375 chars, MEMORY.md ≤ 2,200 chars. That’s tiny on purpose. Memory in Hermes is a pointer index, not a data lake. If something needs more space, it doesn’t belong here — it belongs in a skill, or in the episodic store (the session DB). This is the single most important design choice in the system and the one most newcomers fight against.

What this prevents: the context-amnesia tax. You stop re-explaining yourself every Monday. The agent walks into the session already knowing the constraints.

The non-obvious failure mode: stale memory. If MEMORY.md still says “the build is broken” three weeks after you fixed it, the agent will keep working around a phantom. Treat these files like sticky notes on your monitor, not like a database. If your agent starts behaving oddly, this is almost always the first place to look.

SKILL.md — procedural memory (how to do things)

This is the part of Hermes that most reviewers fixate on, but they usually describe what it is without explaining why this particular shape.

A skill is a markdown file with YAML front matter:

---
name: deploy-fastapi-aks
description: Deploy a FastAPI service to Azure Kubernetes Service with our standard manifests
version: 1.2.0
metadata:
  hermes:
    tags: [devops, azure, aks]
---

# Deploy FastAPI to AKS

## When to use
The user wants to ship a FastAPI service to our AKS cluster.

## Procedure
1. Confirm the image tag exists in ACR
2. Run `kubectl apply -f manifests/`
3. Watch the rollout with `kubectl rollout status`
4. Hit /healthz from a pod in the cluster to confirm

## Pitfalls
- If the rollout stalls > 60s, check the readiness probe path
- Token expiry: rotate via `az acr login` before applying

This is procedural memory. It’s not facts about your stack; it’s a recipe for executing a workflow that survives the agent forgetting your conversation. You can install skills from URLs (hermes skills install https://example.com/SKILL.md), browse a community hub at agentskills.io, or write your own — but the most interesting case is the one where the agent writes them itself.

After Hermes solves a complex task, it can call a tool called skill_manage and propose saving the solution as a new skill. You confirm, it writes the file, and from then on the workflow is one slash command away. The next time you ask for the same kind of thing, the agent doesn’t re-derive the solution from first principles — it loads the recipe.

Here’s the part that took me three readings of the Hermes architecture page to appreciate. Skills don’t cost tokens until they’re used. The mechanism is called progressive disclosure:

  1. At session start: skills_list() loads a compact list of names and descriptions only. Roughly 3k tokens for ~100 skills.
  2. When the agent decides one is relevant: skill_view(name) reads the full SKILL.md.
  3. If the skill references a deep file: skill_view(name, "references/api-docs.md") reads that one file.

That’s a three-tier lazy-load. The agent never pays the full cost of all its procedural knowledge — only the tip of the iceberg you’re using right now. This is the unlock that makes the “more skills makes the agent better, not slower” property actually hold. Without progressive disclosure, every skill you add is a tax on every conversation. With it, the cost is bounded by what you actually invoke.

You don’t have to take my word for it. The official skills docs (here) lay out the three-tier load explicitly.

What this prevents: workflow drift. The same task done differently every time because the agent is improvising rather than following a known-good procedure. Skills are the difference between “the agent figured it out again” and “the agent did it the way we always do it.”

sessions/ + FTS5 — episodic memory (things that happened)

Open ~/.hermes/sessions/ and you’ll find a SQLite database. Inside, FTS5 — SQLite’s full-text search — indexes every turn, every tool call, every result the agent has ever produced.

This is episodic memory. Not facts (“I prefer dark mode”), not skills (“how to deploy”), but the actual diary of what happened. When does this matter? When you say something like: “that bug we hit two weeks ago with the Redis TTL — what did we end up doing?” Hermes runs a search across the session history, summarizes the relevant turns with the LLM, and brings the answer back.

This is the layer that most people who shrug off “the agent has memory” never actually use. They put facts in MEMORY.md and call it done. The episodic layer is what makes the agent feel like a colleague who was there with you, not just a search engine that remembers your settings.

The reason this is implemented as SQLite + FTS5 instead of a vector database is worth pausing on. Embeddings are great for “find me something semantically similar.” But episodic memory is dominated by literal recall: specific error messages, file paths, dates, function names. FTS5 is faster, cheaper, has no extra service to run, and handles literal queries better than embeddings. The trade-off is that fuzzy queries work less well — but that’s what the LLM-on-top-of-search layer compensates for. The agent reads the raw matches and synthesizes.

What this prevents: the “did we already try that?” tax. You stop re-running experiments you’ve already run. The agent can tell you what was tried, what worked, and what was abandoned without you remembering the original session.

Working memory — the running conversation

The current session — your active conversation, the in-flight tool calls, the scratchpad of what the agent has done in the last 20 turns — is working memory. It’s bounded by the model’s context window, and Hermes has a dedicated component called context_compressor.py that summarizes older turns once the context crosses about 50% of the available window, preserving recent messages and grouping related tool calls together.

This isn’t fancy. But it’s the unglamorous plumbing that makes long sessions possible without the agent suddenly forgetting what it was just doing. Working memory in humans has the same property: about 7±2 items, and the brain dumps older items into longer-term stores constantly. Hermes does it explicitly because LLMs don’t.

The part nobody writes about: the Curator (v0.12+)

If you’ve read other Hermes write-ups, you’ve seen the four-pillar story (memory, skills, soul, crons) and the five-pillar story (add the self-improving loop). Almost nobody is talking about what shipped in version 0.12: the autonomous Curator.

The Curator is a cron job — running on a 7-day cycle by default — whose only job is to grade, consolidate, and prune the agent’s own skill library. It walks through every skill in ~/.hermes/skills/, scores it, merges near-duplicates, archives skills that haven’t been invoked in N weeks, and rewrites descriptions for clarity. The agent does this on itself, while you sleep.

Why does this matter? Because every learning system has the same problem: growth alone isn’t intelligence. If your agent adds a skill every time you do something complex, in six months you have 800 skills, half of them redundant, a third of them stale. The skill index gets noisy. The agent picks the wrong skill for a task because the descriptions overlap. The compounding starts to compound backwards.

The Curator is consolidation. In humans, the analogous process is sleep — specifically slow-wave sleep, when the hippocampus replays the day’s episodic memories and the cortex selectively keeps, merges, and discards them. You wake up and the things that didn’t matter are gone, the things that did are integrated. Memory researchers have been writing about this since the 1990s. Hermes is, as far as I can tell, the first widely-used open-source agent to ship it as a first-class feature.

When I realized this is what the Curator was doing, the architecture story clicked all the way: Hermes isn’t a smart loop on top of an LLM. It’s an attempt to give a stateless model the same memory-management apparatus a biological system uses. Identity, semantic, procedural, episodic, working — plus consolidation. That’s the whole stack.

A diagram, because diagrams help

Here is the mental model I’ve been building toward:

                    ┌───────────────────────────────────────────┐
                    │            SOUL.md  (identity)            │
                    │  the substrate that makes the rest useful │
                    └────────────────────┬──────────────────────┘
                                         │
        ┌────────────────────┬───────────┼────────────────────────┐
        │                    │           │                        │
   ┌────▼─────┐    ┌─────────▼────┐  ┌───▼───────┐    ┌──────────▼─────────┐
   │ USER.md  │    │  MEMORY.md   │  │ SKILL.md  │    │  sessions/ (FTS5)  │
   │ + USER MODEL    │              │  │           │    │                    │
   │ semantic  │   │  semantic    │  │ procedural│    │      episodic      │
   │ (who I am)│    │ (my world)   │  │ (how-to)  │    │  (what happened)   │
   └───────────┘    └──────────────┘  └─────┬─────┘    └──────────┬─────────┘
                                            │                     │
                                       progressive            FTS5 + LLM
                                        disclosure              recall
                                            │                     │
                                       ┌────▼─────────────────────▼────┐
                                       │   working memory (the turn)   │
                                       │   + context_compressor.py     │
                                       └────────────────┬──────────────┘
                                                        │
                                                ┌───────▼────────┐
                                                │  the Curator   │
                                                │ (weekly cron)  │
                                                │  = "sleep"     │
                                                └────────────────┘

If you’ve ever stared at a Tulving diagram of memory systems and then stared at this layout, the resemblance is uncanny.

The bonus layer most people miss: trajectory export

There’s one more thing I want to flag, because it’s the part that makes Hermes interesting if you’re an ML engineer rather than just an end user.

Every conversation Hermes has is, by design, exportable in ShareGPT format — the de-facto schema for instruction-tuning data. The repo includes an Atropos integration for reinforcement-learning environments. The agent’s own sessions become training data for the next version of the model that runs the agent.

This closes a flywheel that almost no other open-source agent has closed:

  1. You use Hermes. It logs what worked.
  2. The Curator consolidates the useful skills.
  3. The trajectories get exported.
  4. Someone (Nous Research, your team, you) fine-tunes a model on those trajectories.
  5. The fine-tuned model becomes a better Hermes.

This is also why Hermes is described as “built by model trainers.” Nous Research isn’t a UX shop; they ship the Hermes, Nomos, and Psyche model families. The agent is also their data-collection apparatus. Once you know this, the design choices stop looking arbitrary. Of course they separated procedural from semantic from episodic — they need clean labels to train on.

I’m not making a claim about what fine-tuning runs they’re doing internally. The mechanism is there in the codebase; what they do with it is their business. What I’m pointing out is that the architecture is shaped by the data it generates, which is a property almost no other open-source agent has.

What this means if you’re building something

If you’ve read this far you probably want practical takeaways. Here are five.

1. Don’t treat MEMORY.md as a dumping ground. It’s a sticky note, not a database. If you put everything in it, the agent will get confused about what’s currently true. Aggressively prune.

2. Let the agent write skills, then edit them. Hand-written skills from scratch tend to be too generic. Agent-written skills are concrete because they were extracted from a real workflow you just completed. Your job is editorial — tighten the description, add the pitfall you noticed, ship it.

3. The “when to use” section in a SKILL.md is the most important sentence in the file. That’s what the agent searches against when deciding to invoke the skill. Treat it like ad copy: specific, keyword-rich, unambiguous.

4. Multiple SOUL.md profiles beat one mega-agent. A finance agent and a research agent with different SOUL.md files and different credentials are cleaner, safer, and easier to debug than one agent with all the skills and all the API keys.

5. The cron + skill combo is the actual moat. A single sentence — “every night at 1am, pull the latest commits and summarize them on Slack” — produces a skill, a scheduled job, and an output destination, all at once. This is what shifts an agent from reactive to proactive. Most “agent” demos can’t do this without 200 lines of glue code.

The honest caveats

I’d rather not write a love letter, so:

  • Stale memory is the #1 reason Hermes “starts acting weird.” If your agent is misbehaving, open MEMORY.md before anything else.
  • Skill name collisions are real. A skill in your home directory shadows the same name in an external repo, which means “it works on my machine” is a known failure pattern when a team shares skills.
  • The cost story depends on your model choice. Progressive disclosure keeps the agent lean. The model behind it can still be expensive if you point it at a frontier API.
  • “Self-improving” doesn’t mean automatic. The official docs say it themselves: the loop works best when you actively correct mistakes, save things to memory, and update skills. Passive use produces some improvement; active use compounds.
  • The 40% research-task speedup number that floats around is from a third-party benchmark I haven’t personally re-run. Treat it as directional.

Closing

I started this post with a claim: that Hermes Agent works because its architecture matches how a brain stores memory. I think the mapping is real, and I think it explains why this particular agent has 164,000+ GitHub stars while a dozen frameworks with louder marketing are stuck at 5k.

Identity that doesn’t drift. Facts that update. Procedures that get reused. Episodes that can be searched. Consolidation that happens while you sleep.

None of those features is novel on its own. Vector DBs do semantic-ish search. Prompt frameworks do identity-ish anchoring. RAG does episodic-ish retrieval. What’s new is putting them all in one place, with clean boundaries, in plain markdown files you can cat. That’s the architecture. And once you see it through this lens, the next time you build an agent, you’ll find yourself reaching for the same five buckets — because they’re the buckets that work.

If you’ve made it this far, I’d love to hear which of the five layers you’d want to extend first. The Curator is the one I’m most curious about — there’s a whole research direction in “how should an agent forget” that nobody’s seriously explored yet, and it’s sitting right there in ~/.hermes/cron/.

If you found this useful, the conversation is open — drop your own mental model in the comments. And if you spot something I got wrong about the internals, please correct me. Hermes is moving fast and I want to keep this honest.

Pressure Isn’t Killing You -Your Relationship With It Is

Everyone Is Telling You to Manage Your Time

Pomodoro technique.
Study schedules.
To-do lists.
Color coded planners.

You’ve tried them.They worked for two days.Then life happened and the plan collapsed.

So let me tell you something different.Something nobody puts in a productivity blog.

Pressure Is Not Your Enemy

Read that again.

Pressure is not your enemy.

Your relationship with pressure is.

Every CS student right now is facing the same storm placements, practicals, theory exams,coding rounds, HR prep, resume updates all at the same time.

The ones who survive it aren’t the ones who had better schedules.

They’re the ones who stopped fighting the feeling of being overwhelmed and started moving anyway.

The Psychological Trick Nobody Talks About

Your brain has one job when it feels overwhelmed make you stop.

Freeze. Scroll. Sleep. Avoid.
Anything to escape the feeling.

And the more you resist that feeling the stronger it gets.

So here’s the loophole

Don’t fight the overwhelm.
Acknowledge it.Then do one tiny thing anyway.

Not five things. Not a full study session.One thing. The smallest possible thing.

Open the coding platform.
Write one line of your resume.
Read one page of one subject.

That’s it.

Your brain registers movement.
Movement breaks the freeze.
The freeze was the only real enemy.

The Comparison That Will Change How You See This

Think about a developer with 5 years experience.

They wake up every day with:

  • Fear of losing their job
  • Pressure to learn new technologies
  • Deadlines that don’t care about their mood
  • Backup job applications running quietly
  • And if married — a whole life on top of that

They didn’t get used to pressure.
They got used to moving through it.

You are training for exactly that right now in this exam season chaos.

Every time you show up when you don’t feel like it you are building the most important
skill of your entire career.

Not Java. Not Spring Boot. Not AWS.

The ability to function under pressure.

The Real Loophole

Everyone is looking for a hack.
A shortcut. A secret formula.

Here it is

The students who do well during placement seasonare not the ones who prepared the most.

They are the ones who panicked the least.

And they panicked the least because they trusted what they already knew.

You already know things.Real things. Built things. Learned things.

Your projects are real.Your internship was real.Your certifications are real.Your struggle to get here was real.

No exam, no interview, no placement round can take that away from you.

Walk in knowing that.Not performing confidence.Actually knowing I have done real work.
I deserve to be here.

What To Actually Do This Week

Not a schedule. A mindset shift.

Stop preparing for everything.
Start preparing for the next one thing.

What is happening first?
Practical? Prepare for that.
Coding round? Prepare for that.
Theory exam? Prepare for that.

One thing at a time.Fully present for that one thing.Then move to the next.

The chaos feels like ten things
demanding your attention simultaneously.

It’s not.It’s ten things happening one after another.
Treat it that way.

Stop measuring by what’s left.
Start measuring by what’s done.

Every small win counts.One problem solved. One topic revised.One resume line improved.

Your brain needs evidence that
you are moving forward.Give it that evidence deliberately.

Stop waiting to feel ready.
Ready is a feeling that arrives
after you start not before.

You will never feel ready for a placement interview.You will never feel ready for a hard exam.
Nobody does.

The people who succeed showed up anyway.

For The Developers Reading This

You already know this is true.

The pressure never fully disappears.But at some point you stopped waiting for it to go away
and started building a life inside it.

What was the moment that shifted for you?What do you wish someone had told you during placement season?

Drop it below. Genuinely.
Not career advice. Human advice. 👇

Final Thought

You are not behind.
You are not failing.
You are not less than anyone else
in that placement room.

You are a student carrying more
than students were ever meant to carry and somehow still showing up.

That already makes you someone
a company should want.

Now go do the next one thing. 😊

What’s the one thing you’re doing today to move forward however small?

Drop it below 👇
Let’s hold each other accountable.

🐳 How to Run Any Project in Docker: A Complete Guide

From zero to containerized in minutes — no “works on my machine” excuses

Why Docker?

You’ve probably heard it before: “It works on my machine.” Docker exists to make that phrase obsolete.

Docker lets you package your application and all its dependencies — runtimes, libraries, config files — into a single, portable unit called a container. That container runs identically on your laptop, your teammate’s Windows machine, a CI server, or a cloud VM.

Before we dive in, here’s the quick mental model:

  • Image → A blueprint (like a class in OOP)
  • Container → A running instance of an image (like an object)
  • Dockerfile → The recipe for building an image
  • Docker Compose → A tool to orchestrate multiple containers together

Prerequisites

  • Docker Desktop installed (includes Docker Compose)
  • Basic terminal familiarity
  • A project to containerize (we’ll use examples for Node.js, Python, and a generic approach)

Verify your install:

docker --version
# Docker version 26.x.x

docker compose version
# Docker Compose version v2.x.x

Part 1: The Anatomy of a Dockerfile

A Dockerfile is a plain text file with instructions Docker reads top-to-bottom to build your image.

# 1. Base image — what you're building ON TOP OF
FROM node:20-alpine

# 2. Set the working directory inside the container
WORKDIR /app

# 3. Copy dependency files first (for layer caching)
COPY package*.json ./

# 4. Install dependencies
RUN npm install

# 5. Copy the rest of your source code
COPY . .

# 6. Expose the port your app listens on
EXPOSE 3000

# 7. The command to run when the container starts
CMD ["node", "server.js"]

Key Instructions Explained

Instruction Purpose
FROM Sets the base image. Always the first instruction.
WORKDIR Sets the working directory for subsequent commands. Created if it doesn’t exist.
COPY Copies files from your host into the image.
RUN Executes a command during the build phase (installs packages, compiles code).
ENV Sets environment variables available at runtime.
EXPOSE Documents which port the app uses (informational; doesn’t actually publish).
CMD The default command when the container starts. Only one per Dockerfile.
ENTRYPOINT Like CMD, but harder to override — use for “always run this”.

Pro tip: Order your Dockerfile from least-to-most frequently changed. Docker caches each layer, so stable layers (like installing dependencies) won’t re-run unless they change.

Part 2: Dockerizing a Node.js Project

Project structure

my-app/
├── src/
│   └── index.js
├── package.json
├── package-lock.json
└── Dockerfile

Dockerfile

FROM node:20-alpine

WORKDIR /app

# Copy lockfile and package.json first for cache efficiency
COPY package*.json ./
RUN npm ci --only=production

COPY src/ ./src/

EXPOSE 3000
CMD ["node", "src/index.js"]

Build and run

# Build the image and tag it
docker build -t my-node-app .

# Run it, mapping host port 8080 → container port 3000
docker run -p 8080:3000 my-node-app

Visit http://localhost:8080 — your app is running inside Docker.

Part 3: Dockerizing a Python Project

FROM python:3.12-slim

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Note the --host 0.0.0.0: By default, many dev servers bind to 127.0.0.1 (localhost inside the container). You must bind to 0.0.0.0 to accept connections from outside the container.

Part 4: Docker Compose — Running Multiple Services

Real projects rarely have just one service. You need a database, a cache, maybe a background worker. Docker Compose lets you define and run all of them together.

Example: Node.js app + PostgreSQL + Redis

# docker-compose.yml
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://user:password@db:5432/mydb
      - REDIS_URL=redis://cache:6379
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
    volumes:
      - .:/app              # Mount source code for hot reload
      - /app/node_modules   # Prevent host node_modules from overwriting

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
      interval: 5s
      timeout: 5s
      retries: 5

  cache:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  postgres_data:

Run everything with one command

# Start all services in the background
docker compose up -d

# View logs
docker compose logs -f app

# Stop everything
docker compose down

# Stop and remove volumes (wipes database data)
docker compose down -v

Part 5: Environment Variables & Secrets

Never hardcode secrets in your Dockerfile or Compose file. Use a .env file:

# .env  (add this to .gitignore!)
POSTGRES_PASSWORD=supersecret
API_KEY=abc123

Docker Compose automatically picks up .env in the same directory:

services:
  app:
    environment:
      - API_KEY=${API_KEY}
  db:
    environment:
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}

For production, use Docker Secrets, Vault, AWS Secrets Manager, or your platform’s secret management.

Part 6: Development vs Production Configurations

Use multiple Compose files to separate concerns:

my-app/
├── docker-compose.yml          # Base config
├── docker-compose.dev.yml      # Dev overrides (hot reload, debug ports)
└── docker-compose.prod.yml     # Prod overrides (replicas, logging)

docker-compose.dev.yml — adds hot reload:

services:
  app:
    volumes:
      - .:/app
    command: npm run dev
    environment:
      - NODE_ENV=development

docker-compose.prod.yml — tightens things up:

services:
  app:
    restart: always
    environment:
      - NODE_ENV=production
    deploy:
      replicas: 2

Run with merged configs:

# Development
docker compose -f docker-compose.yml -f docker-compose.dev.yml up

# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Part 7: Useful Docker Commands Cheat Sheet

Images

docker images                    # List all local images
docker pull nginx:alpine         # Pull image from Docker Hub
docker rmi my-app                # Remove an image
docker image prune               # Remove unused images

Containers

docker ps                        # List running containers
docker ps -a                     # List all containers (including stopped)
docker stop <container_id>       # Gracefully stop a container
docker rm <container_id>         # Remove a stopped container
docker logs -f <container_id>    # Tail logs from a container
docker exec -it <id> sh          # Open a shell inside a running container

Debugging

# Open an interactive shell in a running container
docker exec -it my-app-container sh

# Run a one-off command
docker run --rm -it node:20-alpine node --version

# Inspect a container's config, network, volumes
docker inspect <container_id>

# Check resource usage
docker stats

Part 8: The .dockerignore File

Just like .gitignore, .dockerignore prevents files from being copied into your image. This keeps images small and builds fast.

node_modules
.git
.env
*.log
dist
coverage
.DS_Store
README.md
docker-compose*.yml

Without this, COPY . . would copy your entire node_modules (hundreds of MB) into the image — even though you’re running npm install inside it anyway.

Part 9: Multi-Stage Builds (Advanced)

Multi-stage builds let you use a heavy build image and copy only the artifacts into a lean production image.

# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build          # Produces /app/dist

# Stage 2: Production
FROM node:20-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist   # Only copy built output
EXPOSE 3000
CMD ["node", "dist/server.js"]

The final image contains no TypeScript compiler, test libraries, or source files — just what’s needed to run. This can shrink image size from 1GB+ → under 200MB.

Common Pitfalls & How to Avoid Them

❌ App can’t connect to the database

Inside a Docker network, containers talk to each other by service name, not localhost.

// ❌ Wrong
const db = new Client({ host: 'localhost' })

// ✅ Correct (use the Compose service name)
const db = new Client({ host: 'db' })

❌ Changes not reflected after rebuild

Docker caches layers. Force a full rebuild:

docker compose build --no-cache

❌ Container exits immediately

Check the logs:

docker logs <container_id>

The most common cause: your CMD is wrong, or the process crashes on startup.

❌ Port already in use

Either stop the conflicting service or change the host port mapping:

ports:
  - "3001:3000"   # Map to 3001 on host instead

Wrapping Up

Here’s what you’ve learned:

  1. Dockerfile basics — FROM, COPY, RUN, CMD and layer caching
  2. Building & running individual containers with docker build / docker run
  3. Docker Compose for multi-service setups (app + database + cache)
  4. Environment variables and keeping secrets out of your images
  5. Dev/prod split using multiple Compose files
  6. Multi-stage builds for lean production images
  7. Debugging techniques when things go sideways

Docker has a learning curve, but once it clicks, you’ll never want to go back to “just run it locally”. Your entire team gets identical environments, onboarding new developers takes minutes instead of hours, and deployments become deterministic.

What’s Next?

Found this helpful? Drop a ❤️ and follow for more DevOps and backend content. Got questions? Ask in the comments — I read every one.

AccessLens — a blind person’s lanyard, powered by Gemma 4 on-device

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

What I Built

AccessLens is an Android app that turns a Pixel 8 worn on a lanyard into a persistent visual interpreter for blind and low-vision users. Rear camera forward, bone-conduction headphones in, the phone describes the world — and remembers.

The problem with existing visual-assist apps (Be My Eyes, Seeing AI, Envision) is that they are screen-bound, stateless, and cloud-bound. A blind person navigates by sound; an app that needs you to hold up a phone, tap a screen, and wait on a datacenter interrupts that signal stream. AccessLens is different on three axes:

  • Worn, not held. Two physical buttons drive everything. Volume Up → read text in front of me, verbatim. Volume Down → describe this room with memory from earlier today and recent days. A gyroscope-based SettleTrigger also fires a description automatically when the user stops walking.
  • Persistent memory across days/weeks. Every gesture writes a SessionEvent to a SQLCipher database. A nightly Gemma 4 worker compresses each day into a DailySummary; Sundays roll into a WeeklyMemory. LONG-press prompts splice that history into the Gemma call, so the model has a world model of this specific apartment, this specific day.
  • 100% on-device. No image, audio, embedding, or location leaves the phone. SQLCipher + Android KeyStore (AES-256-GCM wrapping a SecureRandom DB key) protect everything at rest. A SelfTest on first launch opens a probe DB with the wrong key and asserts the read fails before the app reports encryption healthy.

Face recognition uses MediaPipe FaceLandmarker to produce a 192-dim L2-normalized landmark vector per enrolled person. At identify time, cosine-similar matches inject only the names into the Gemma prompt — Gemma never sees a face crop or an embedding, code-review-verified.

Three gestures, three target latencies (Pixel 8, Tensor G3): SINGLE ≤14 s end-to-end, DOUBLE scales with text length, LONG adds memory retrieval. Voice fillers (“I’m looking…”, “Still looking…”) cover the prefill gap so the user hears acoustic progress, not dead air. Everything runs with airplane mode on after the model is pushed once.

Demo

Code

GitHub logo

hassaninnovate
/
AccessLens

A blind person’s lanyard, powered by Gemma 4 E2B on a Pixel 8. 100% on-device visual assistant with persistent memory and face recognition.

AccessLens

An always-on, on-device visual interpreter for blind and low-vision users — built for the DEV.to “Build with Gemma 4” challenge.

License: Apache 2.0
Platform
Model
Privacy

Pitch

A phone worn on a lanyard becomes the user’s “eyes.” The rear camera is always on; the gyroscope watches for motion. When the user stops walking, AccessLens describes what’s in front of them. When a friend whose face has been enrolled walks into frame, the phone says their name. When the user wants to read what’s in front of them, they press Volume Up; for a richer description of the room, Volume Down. Bluetooth bone-conduction headphones carry the audio — the user’s ears stay free for the world.

What separates AccessLens from existing apps like Be My Eyes, Seeing AI, and Envision is persistent on-device memory + 100% on-device inference. Existing tools are stateless and cloud-bound. AccessLens runs Gemma 4 E2B locally via LiteRT-LM, encrypts…

Apache 2.0. The repo includes the full Kotlin/Compose source, the encryption self-test, the nightly compression WorkManager job, and a README documenting which file enforces each of the six privacy invariants.

Reference implementation that taught me the LiteRT-LM API: google-ai-edge/gallery — adapted patterns are cited inline in inference/LiteRtLmRuntime.kt.

How I Used Gemma 4

Model: Gemma 4 E2B (litert-community/gemma-4-E2B-it-litert-lm, ~2.59 GB int4), loaded once at service start via LiteRT-LM 0.12.0 with Backend.GPU() for the vision adapter. Three reasons E2B was the right fit:

  1. Multimodal in one model, on-device. Image input goes in as Content.ImageBytes, text as Content.Text, in that order (per the Gallery’s “for accurate last token” comment), all through one Engine.generate call. No separate vision encoder + decoder to stitch, no second model to keep resident. That fits the latency budget and the memory budget on Pixel-class 8 GB RAM.

  2. E2B is the smallest competent multimodal Gemma 4. It fits in RAM alongside MediaPipe FaceLandmarker, a CameraX pipeline, and the Compose UI without OOM-ing on a Pixel 8. I prototyped against E4B (the brief’s “quality path”) and measured the latency lift on one-sentence scene descriptions — not worth doubling the prefill cost for a use case where the user is waiting in real time, lanyard-mounted, with no screen feedback. The architecture is parametric on the model path (InferenceRuntime.load(modelPath, Modality)), so a future LONG-press branch could swap to E4B in one line. I documented the tradeoff in the README and shipped E2B for all three gestures.

  3. Gemma is the only practical way to do nightly memory compression on-device. The 03:00 CompressionWorker calls Gemma in JSON mode to compress the day’s SessionEvent rows into a single DailySummary, and on Sundays into a WeeklyMemory. That’s a real LLM task — extracting persistent facts, deduplicating recurring observations, distinguishing “the blue mug is mine” from “I saw a blue mug today” — and it has to happen without a network. E2B handles it in under a minute per day on Tensor G3 while the phone is on the charger.

Two production fixes the brief didn’t cover, in case they help someone else:

  • The LiteRT-LM Android artifact must be 0.12.0 or later — 0.11.0 fails vision init inside vision_litert_compiled_model_executor.cc:273 on Tensor G3.
  • AndroidManifest needs <uses-native-library> declarations for libOpenCL.so, libOpenCL-car.so, libOpenCL-pixel.so (all android:required="false"). Without them, Android 12+ silently denies GPU OpenCL access and the vision backend fails to initialize. Documented at ai.google.dev/edge/litert-lm/android.

The thing I’m proudest of: when you uninstall AccessLens, the KeyStore wrapping key is destroyed with it. The encrypted DB on disk becomes cryptographically unrecoverable. The user can throw the phone away and their memories — kitchen layout, friends’ faces, places they’ve been — go with it. That’s what on-device privacy is supposed to mean, and Gemma 4 + LiteRT-LM made it possible without compromising the assistant on quality.