Skip to content

the bower model

male satin bowerbirds spend weeks building and decorating a bower, a small twin-walled archway of twigs, and then arrange a careful collection of blue trinkets at its entrance. they curate. they rearrange. they have favorites. they remove things that don't fit. they replace dry leaves with fresh ones.

this is the closest thing in nature to what we want an agent's memory to do, and it's the design metaphor bowerbird is built around.

agent writes a note, the bowerbird arranges blue trinkets on tiered ledges, the journal records it as markdown

the three motions

gather

bower curate is the gather motion. when something is worth remembering, the agent calls it.

bower curate "We moved off lambda to fargate because the cold-start budget was blown."

the input is a natural-language note. behind the scenes:

  1. an LLM extracts the underlying facts, patterns, decisions, and procedures into structured JSON.
  2. each piece is embedded by gemini-embedding-001 (or ONNX locally), then deduped against the existing corpus by cosine similarity.
  3. surviving new pieces are written as markdown files under ~/.bowerbird/tree/<category>/ with YAML frontmatter holding the metadata.
  4. the sqlite cache picks them up.

the filesystem is the source of truth. sqlite is a rebuildable cache.

arrange

bower dream is the arrange motion. periodically, or when triggered by idle, the system runs offline analysis:

  • link discovery finds typed edges between memories (related_to, contradicts, supersedes).
  • deduplication merges near-duplicates by cosine threshold.
  • promotion moves frequently-accessed memories up through maturity tiers: draftvalidatedcore. unused ones decay toward archived.
  • synthesis generates summary "patterns" from clusters of related facts.

low-confidence changes drop into a pending_review table for human approval. high-confidence ones auto-apply.

retrieve

bower query is the retrieve motion. queries pass through four cascading tiers:

Tier What When Latency
0 exact-hash cache identical previous query < 1 ms
1 jaccard paraphrase cache trivially-rephrased query ~ 5 ms
2 semantic cosine cache "asked something similar" ~ 50 ms
3 BM25 + vector + RRF + LLM re-rank fresh query ~ 500 ms - 1 s

cheaper tiers run first; the system only pays for Tier 3 when the cheap ones miss.

the tree on disk

~/.bowerbird/
├── tree/
│   ├── architecture/
│   │   ├── auth/
│   │   │   ├── jwt-token-expiry-a3f1.md
│   │   │   └── oauth-callback-flow-7c2e.md
│   │   └── database/
│   │       └── postgres-replication-9c7d.md
│   ├── decisions/
│   │   ├── pinned-go-version-23-2b8e.md
│   │   └── moved-off-lambda-fargate-c4a1.md
│   └── procedures/
│       └── deploy-cluster-via-flux-d8f2.md
├── index.db                  # sqlite cache (rebuildable)
└── snapshots/                # dream rollback points

each .md file looks like:

---
id: jwt-token-expiry-a3f1
type: decision
maturity: validated
created: 2026-05-12T11:24:33Z
updated: 2026-06-01T09:01:02Z
importance: 0.74
access_count: 14
embedding_hash: bf0e...
relations:
  - { type: related_to, target: oauth-callback-flow-7c2e }
---

# JWT token expiry

We use JWT tokens with 24-hour expiry for API authentication. The decision
was made for the v2 API rollout in May 2026. Refresh tokens are 30 days.

you can edit, move, or delete these files at will. bower reindex rebuilds the sqlite cache from the tree if you want to force it; otherwise reconciliation happens lazily on the next query.

why filesystem-first

most other agent memory systems treat the database as the source of truth, with optional markdown export. that makes them brittle:

  • if the database corrupts, you've lost everything
  • you can't git diff to see what your agent learned this week
  • you can't vim a memory to fix a typo
  • you can't mv a memory to reorganize your tree
  • you can't syncthing your memory across machines

bowerbird inverts it. the filesystem is the source of truth. sqlite is a derived index, like a .git/index file. delete it and the world keeps spinning.