esc
Collections All The Way Down — The Native Dividend
Becoming Lifelog

Collections All The Way Down — The Native Dividend

Becoming Lifelog, in which the architecture gained power by losing code --- Previously on Becoming Lifelog... The ouroboros had [[The Ouroboros Update]]. The architecture would eat itself—Go...

December 30, 2025

Becoming Lifelog, in which the architecture gained power by losing code


Previously on Becoming Lifelog…

The ouroboros had The Ouroboros Update. The architecture would eat itself—Go backend replaced by browser plugins. Sync Hub orchestrating. GitHub collection self-syncing. Issue #18 filed.

But the sketch wasn’t finished. And in Mythology Driven Development (MDD™), you don’t stop until the narrative is clean.


The First Question

Monday, December 30, 11:23 AM - The developer stares at the GitHub issue he just created.

riclib: “One last thing to consider.”

CLAUDE: “What’s that?”

riclib: “Thymer allows code in collection plugins.”

[A screenshot appears. A collection settings panel. A “Custom Code” tab.]

riclib: “Could the plugins… BE collections?”

[THE SQUIRREL drops its acorn.]


The Collection That Contained Itself

CLAUDE: “Let me check the SDK.”

[Time passes. Types are examined. Examples are explored.]

CLAUDE: “CollectionPlugins have full DataAPI access. They can access ALL collections. They can create records anywhere. They can access window.”

riclib: “So a collection can populate itself.”

CLAUDE: “The sync logic can live inside the collection it syncs to.”

THE SQUIRREL: “But… but we designed separate plugins! A registry! A—”

riclib: “The collection IS the plugin.”

GitHub Collection
├── Schema (state, author, repo...)
├── Records (the issues)
├── Custom Code tab → sync logic
└── Settings as... collection properties?

THE SQUIRREL: “Where’s my PluginRegistryService?”

THE LIZARD: blinks

THE SQUIRREL: “My DependencyInjectionContainer?”

THE LIZARD: blinks again

riclib: “Install the collection. Get the sync.”


The Hub That Wasn’t Special

CLAUDE: “So Sync Hub is still an App Plugin? The orchestrator?”

riclib: “…”

CLAUDE: “…riclib?”

riclib: “Isn’t the Sync Hub also a collection of plugin records?”

[THE SQUIRREL sits down heavily.]

CLAUDE: “Sync Hub… is a collection… containing records for each registered plugin…”

riclib: “Collections all the way down.”

Sync Hub Collection
├── Custom Code → scheduler, window.syncHub API
└── Records:
    ├── github-sync
    ├── calendar-sync
    └── readwise-sync

THE SQUIRREL: “I had an AppPluginCoordinatorFactory planned…”

THE LIZARD: “No you didn’t.”

THE SQUIRREL: “I was GOING to plan one…”

THE LIZARD: slow, devastating blink


The Records That Merged

CLAUDE: “So we have: plugin registration records, settings records, status records, operation logs—”

riclib: “Could they be singletons? One record per plugin, combining settings and status?”

CLAUDE: “…”

riclib: “…”

THE SQUIRREL: “…”

Record: github-sync
├── enabled: ✓
├── interval: 1m
├── status: idle
├── last_run: 2024-12-30T15:21
├── token: ghp_xxx
├── repos: owner/repo1, owner/repo2
└── Body: activity log

CLAUDE: “One record. Settings AND status AND log. All in one place.”

riclib: “User wants to configure GitHub? Edit the record.”

CLAUDE: “User wants to see status? Same record.”

riclib: “User wants to debug? Read the body.”

THE SQUIRREL: “What happened to my PluginSettingsService? My StatusTrackingRepository? My—”

THE LIZARD: “They were one thing pretending to be three.”


The Database That Wasn’t

CLAUDE: “For state tracking—IndexedDB? We need to store what we’ve seen, compare timestamps—”

riclib: “Why use IndexedDB directly? Why doesn’t the main plugin use Thymer collections?”

[Silence.]

CLAUDE: “Store sync state… in Thymer… where it syncs across devices… and users can inspect it…”

riclib: “And edit it. Delete a state record to force re-sync.”

THE SQUIRREL: “But my IndexedDBAdapter! My LocalStorageFallback! My—”

THE LIZARD: “Thymer IS the database.”

Plugin State: github-sync
├── Record: github_owner_repo_123
│   ├── external_id: github_owner_repo_123
│   ├── state: open
│   └── updated_at: 2024-12-30T15:21

THE SQUIRREL: staring “That’s… that’s the database AND the debug interface…”

THE LIZARD: “That’s Thymer.”


The Noise Controls

riclib: “Add a journal field. Should this plugin write to the daily journal?”

CLAUDE: “GitHub: yes. Readwise: quiet.”

riclib: “And a toast field. What notifications?”

toast: all_updates    → everything
toast: new_records    → only new items
toast: errors_only    → silent unless broken
toast: none           → completely quiet

CLAUDE: “Per-plugin noise control. User decides what screams and what whispers.”

THE SQUIRREL: “A NotificationStrategyFactory with configurable—”

THE LIZARD: “A dropdown.”

THE SQUIRREL: “…a dropdown.”


The Debug That Stayed Simple

riclib: “And a log_level on the plugin record. Debug mode when needed.”

CLAUDE: “Set log_level: debug on github-sync…”

riclib: “The activity log gets verbose. All plugins, one field each.”

THE SQUIRREL: “What about per-record log levels? Each issue could have its own—”

THE LIZARD: sharp blink

riclib: “That’s one extra field on every state record. In every collection. Forever.”

THE SQUIRREL: “But the granularity—”

THE LIZARD: “Is Squirreling.”

CLAUDE: “Plugin-level debug. One field. Done.”


The Dashboard That Appeared

riclib: “And given collection plugins can have custom renderers…”

[riclib sketches on a napkin]

┌─────────────────────────────────────────────────────┐
│  Sync Hub                              Dashboard ▼  │
├─────────────────────────────────────────────────────┤
│  ┌──────────────┐  ┌──────────────┐  ┌───────────┐  │
│  │ ● GitHub     │  │ ● Calendar   │  │ ○ Readwise│  │
│  │ 23 issues    │  │ 8 events     │  │ 147 docs  │  │
│  │ Last: 2m ago │  │ Last: 5m ago │  │ Last: 1h  │  │
│  │ [Sync Now]   │  │ [Sync Now]   │  │ [Sync Now]│  │
│  └──────────────┘  └──────────────┘  └───────────┘  │
│                                                     │
│  Recent Activity                                    │
│  20:23  github   created **Fix auth bug**           │
│  18:23  github   updated 3 issues                   │
└─────────────────────────────────────────────────────┘

CLAUDE: “We didn’t plan a dashboard.”

riclib: “We didn’t have to. Collections can have custom views. The data was already in the right shape.”

THE SQUIRREL: “I had a whole DashboardComponentArchitecture—”

THE LIZARD: “The data IS the dashboard.”


The Native Dividend

CLAUDE: “Let me count what we gained by going Thymer-native.”

GAINED:
├── Dashboard view (custom renderers, free)
├── User-editable settings (just fields)
├── Activity logs (record bodies)
├── Per-plugin log levels (just a field)
├── Cross-device sync (CRDT, free)
├── Visible state (it's all in Thymer)
├── Journal control (just a field)
├── Toast control (just a field)
├── Force re-sync (delete state record)
└── Community can build plugins (simplified API)

LOST:
├── IndexedDB code (didn't need it)
├── Config file parsing (didn't need it)
├── Plugin registry service (didn't need it)
├── Settings storage service (didn't need it)
├── Status tracking service (didn't need it)
└── AppPluginCoordinatorFactory (never existed)

riclib: “Every time we asked ‘do we need that?’ the answer was ‘Thymer already has it.’”

THE SQUIRREL: “I had PLANS…”

THE LIZARD: “The platform has features. You just had to use them.”


The MDD™ Proof

The narrative demanded clean architecture.

We couldn’t write “and then we added IndexedDB alongside Thymer’s CRDT” because the Lizard would know. We couldn’t write “settings lived in one place, status in another, logs in a third” because the story would be ugly.

The epic required:

  • One thing that does one thing (collections)
  • Data in the right place (Thymer)
  • Visible state (users can see everything)
  • Elegant defeats of complexity (the Squirrel loses to dropdowns)

MDD™ delivered.


The Tally

Collections discovered:         All of them
App Plugins needed:             0 (was 1)
Separate databases needed:      0 (was 1)
Config files needed:            0 (was 1)
Services eliminated:            6
Features gained:                10
Squirrel proposals:             9
Squirrel proposals that survived: 0
Lines of code (projected):      Fewer
Power (projected):              More

The Pattern

WE ASKED: DO WE NEED THAT?
THYMER SAID: I HAVE THAT

WE ASKED: WHERE DO WE STORE IT?
THYMER SAID: IN ME

WE ASKED: HOW DO WE VIEW IT?
THYMER SAID: I DO THAT TOO

THE SQUIRREL PLANNED FACTORIES
THE LIZARD PLANNED DROPDOWNS

THE NATIVE DIVIDEND:
MORE POWER BY WRITING LESS
IN THE RIGHT PLACE

🦎

The Architecture (Final)

Sync Hub Collection
├── Custom Code → window.syncHub API, scheduler
└── Records (one per plugin):
    └── github-sync
        ├── enabled: ✓
        ├── interval: 1m
        ├── journal: ✓
        ├── toast: all_updates
        ├── log_level: info
        ├── status: idle
        ├── token: ghp_xxx
        └── Body: activity log
              ↑
GitHub Collection ─────registers─────┘
├── Custom Code → self-syncing
├── Records (issues, PRs)
└── State in: Plugin State: github-sync

Collections all the way down.
The native dividend.
No code we didn’t need.
All power we could want.


Day 30 of Becoming Lifelog

In which the sketch gained power by losing lines

And the Squirrel learned that dropdowns defeat factories

And we collected all the way down


Next Time on Becoming Lifelog…

The implementation begins. The first collection syncs itself. The window.syncHub API takes shape. And somewhere, a Dashboard view renders for the first time.

“Install the collection. Get the sync.”

Coming soon: The Great Migration


See also:


storyline: Becoming Lifelog
slug: collections-all-the-way-down