Teodor’s public-personal journal


Q: Hey! Should I read this?

A: Perhaps not. Perhaps. I use this page as a low-filter, low-threshold way to get ideas out. Capture ideas, incomplete as they are. Growth and nurturing is a separate process. Filtering, curating, organizing — that’s for later. The reading experience might feel like a stream-of-consciousness endeavor — with less flow, less feelings, less poetic content. In other words, messy, without the good sides of messy. Still, I choose to make this public, because I believe public should be the norm, not the exception.



On product and indirection.

I feel shackled by pressure for specificity. Always be specific. Specific is better. Don’t be vague.

Specificity expels indirection. Indirection is core to design.

If we lazer-focus on the specific, we’ll ignore problems with a longer feedback cycle time than some arbitrary limit.

Emacs ⟂ Product

Statement: “don’t build something for yourself – you’ll ignore users”

Response: “no – DO build product for yourself – but design in the flexibility as indirection. Don’t force your users into your workflow. Doing it for yourself is not workflow design - it’s capability design.”

By building in flexibility — indirection — at the right layer, we decouple the product, and give ourselves leverage.

On fun

I’m better when I have fun.

I have fun when I build stuff for myself and others.

∴ I should build stuff for myself.

Fully integrated feedback loops

Commonly discussed feedback loops:

  1. Cycle time for recompilation
  2. Cycle time for running the tests
  3. Cycle time for a deploy

Uncommonly discussed feedback loops:

  1. Idea-to-sharable-paper
  2. Idea-to-team


Pandoc code blocks

(moved from separate page)

We explore how Pandoc supports embedded code.

We can write Clojure:

(defn act [& commands]
  ;; code

Or Python:

def act(*commands):
    # code

Or even javascript:

function act(commands...) {
    // code


Another morning.

I want trailing whitespace.

Possible solutions:

  1. Find org-mode native solution
  2. Lol, I can just use a big verse. Problem solved. See the bottom of this file.

What about tags?

Outcome - less messy front page.

Right now we’ve got this:

Intent: bring ideas to life. Discuss, sharpen, play.

Status: very much work in progress. Please advance at your own peril.


Possible next steps:

Possible improvements:

  1. Get real title out, don’t use file name as link name.
  2. Separate into sections with tags
    1. “Quite finished stuff” up top
    2. Then categorized content
    3. Then “draft” content
    4. Then untagged pages.
  3. Considerations
    1. Perhaps hide untagged content
    2. Perhaps introduce a tag for content that should not be indexed
      1. Or just ignore content without a play.edn file?

Migration path - opt in.

Backwards compatible path - existing behavior prevails, title is “settable” from play.edn.

Concern - performance.

The root HTML file will now depend on all the play.edn files. Perhaps use SQLite for caching? Not now. I want good performance under scale. Performance is important. I can just opt for manual indexing if the need arises.

Implementation path

Outcome - better titles. Order. First create a play.edn file manually. Then create the proper title for that file. Run index.clj by hand.

On reactivity, speed and whole-system changes


  1. Having a fast Makefile is super nice for when I’m working on a single piece of content.
  2. Just rerunning the whole system is preferable when I’m changing the system. Also keeping track of changes at the same time is probably not required.
  3. When I’m changing the whole system, I don’t really want a file watcher. I’d rather apply the change manually.

So …

I’d like a teod/apply-on-save-mode.

Perhaps I made it work?


Nope. Back to manual teod/apply it is.


Morning / afternoon / whatever.


Retrospective - play.teod.eu

How are we coming along?

  1. I like working with Babashka
  2. But the current process of multiple “thingies” is getting painful
    1. Perhaps I need to consolidate into a single play.clj script with a CLI.
  3. Tags are nice
    1. It’s nice to always think outcome. What do we want to achieve right now?

Next steps?

  1. Have the page command automatically categorize stuff as “don’t mind me”
  2. Group the remaining English content
  3. I kinda wanna explore SQLite
  4. I wanna try out portal
  5. Do I want to be able to run my stuff with both JVM Clojure and Babashka?

Current pages

Page Category aka
https://play.teod.eu/emacs/ Rambling
https://play.teod.eu/aphorisms/ Page
https://play.teod.eu/feedback-loops-api-design-how-it-works/ Article draft
https://play.teod.eu/hourglass-architecture/ Ideas & capture Narrow waist
https://play.teod.eu/opt-in-hierarchies/ Ideas & capture
https://play.teod.eu/orthogonality-enables-optionality/ Article draft
https://play.teod.eu/product-for-developers/ Article draft
https://play.teod.eu/journal/ Rambling
https://play.teod.eu/unix-signals-crash-course/ Article draft
https://play.teod.eu/knowledge-worker/ Article draft

How do we tag?

By form:

:form :rambling
:form :article
:form :explore
:form :unknown

By readiness:

:readiness :in-progress
:readiness :published

By language:

:lang :no
:lang :en

Batch editing tags?

Dump -

distributed ${ARTICLE}/play.edn files are “near” to the article (nice), but tedious to batch edit

batch editing is nice in a table

A normalized model is EAV.

EAV example:

:id "emacs" :title "(Doom) Emacs learning journal"
:id "emacs" :form :rambling
:id "emacs" :readiness :in-progress

What are nice ways of batch editing?

One big text file Excel table SQLite?

How should lines be deleted?

In Dired, simply d the line, then x to apply with confirmation In Magit, c c to commit, C-c C-c to apply

I could simply try dumping all the data into SQLite and see how that works out.

I have:

Metadata per page some pages

So - simple, flat model.

I could build

Files -> SQLite SQLite -> files

Do I want “apply everything” or “apply partial”?

I could implement “apply everything” in terms of “apply partial”

First delete all the play.edn files Then apply partial And confirm changes in Git.

    play.edn is a module declaration
    it declares dependencies
       And binds them to targets
        Maps to root.
    Challenge - link resolution.
        Do I need a redirect “service”?
            Yeah, perhaps I can use redirects.
        I thought I needed magic to rewrite links to target
            But I can generate a companion link site
                    ?link=LINK_ID that redirects

./play API draft

$ ./play2.clj relations :from :files :to :lines
:id "emacs" :title "(Doom) Emacs learning journal" :form :rambling :readiness :in-progress
:id "feedback-design-impl" :title "Feedback loops, API design and how stuff works"


Created the lines mode - and more.

Mode Read? Write? Purpose
:files y y play.edn files is the main storage
:lines y y lines give a concise overview
:table y y table is great for batch editing
:pretty n y :to :pretty is great when devleoping a reader

This almost looks like an hourglass architecture :)

relations interface in the middle.

I’m happy with the design.


Possible talk - dynamic programming

idea - dynaimc programming isn’t lack of types. Dynamic programming is options to do flexible stuff.

Option - work on data structures rather than types. XML - static types for everything, or a dynamic tree? JSON - types for everything? Alternative formulation - serialization for free

Option - dynamic runtime. Dynamic languages often support interpretation / dynamic recompilation

Option - extend language when required. Embedded DSLs are just data (Or macros, but macros can be complex)

Option - use schemas directly for validation rather than types

Examples? Hmm Python? Clojure? Javascript?


Braindump / thought stack

I want to give Ole Jacob a big JSON file he can build UIs on top of


rich entity semantics - “url” “title”

& filter on tags

I want to get more info when I generate pages. This should be possible:

./play.clj page compuational-engineering :title “Computational Engineering”

And it should also write :author-url and :created-at.

I’d like to avoid the watchbuild files

Are they even required any more?

I haven’t used any of them in a long time

Makefile works well

Action: delete em.

I deleted the watchbuild files


Docs fanout factor

For me:

Written for myself 10
Shared with others 1


Working on my own ideas / perception / intent is something I’d like to do with an internal feedback loop.


Hmm, good question.

Well, easy answer. Because I don’t get anywhere as fast ahead through conversation with others.


No, actually, that’s not it.

And amount of written text is the wrong metric

Effort is the right metric

For effort, it’s perhaps 50/50

50 % internal

50 % external.

I prefer writing to thinking when iterating internally.

I prefer speaking to writing in conversation

I think? I’m not quite sure.

I dislike IDEs because in IDEs, plain text and prose is second class

That basically means they are missing Org-mode.

So perhaps “I dislike anything that doesn’t have Org-mode” is better.


Observation: sometimes vague and general is required

I feel shackled by pressure for specificity. Always be specific. Specific is better. Don’t be vague.

This feels like a statement that sometimes a bad abstraction is required to get to a good abstraction. Also, I hate being forced to do stuff.

Emacs is a tool for research that happens to work for code too


refers to https://www.ingentaconnect.com/content/matthey/jmtr/2022/00000066/00000002/art00002;jsessionid=2tqj0na4wh7rw.x-ic-live-01

and https://pubs.acs.org/doi/10.1021/acscatal.5b00538

and https://www.technology.matthey.com/article/66/2/122-129/

And “which is the first subset of Org-mode that should be supported?” https://gitlab.com/publicvoit/orgdown/-/tree/master


Actionable - SQLite as a file system

Should be worthy of a page on its own.

Also paves the way for what I can do with play.teod.eu.

Also perhaps worthy of publishing to the Clojurians Slack? Hmm.


Driving in Troms, with Tjerand and Torstein.

Problem - npx doesn’t work offline

npx seems to look for new versions on each invocation. I can’t use the following offline:

$ cat preview.sh
#!/usr/bin/env bash

npx live-server --no-browser --port=3000

So … what do I want? Just having the dependencies available offline would be nice, really.


  1. Something NPM based. Probably means I need node_modules, package.json and package-lock.json.
  2. Something Clojure-JVM-based.
  3. Something Babashka-based.
  4. Just serving raw HTML in firefox, and triggering a hook to refresh on a keystroke - like I’m doing with Clerk


Discussing note taking systems on the Clojurians Slack

ag is using Org-Roam quite heavily. He separates between:

And says that there’s no semantic difference between those three categories and:

Hmm, I think I’ve actually landed on that same structure myself in Roam. Fleeting notes go on the Daily Notes page. Permanent notes are entities. Project notes are one big hierarchy.

How does that map to play.teod.eu?

Fleeting notes go into the journal. No new entities. Permanent notes get their own page. It should be possible to link to permanent notes! Project notes get a page per project. That page is deleteable or “removable from index”.

Question: “in what context do I want to re-discover this piece later”. Then – establish links to all those contexts.


I really don’t like UPS.

Better editing of play.edn files

I could create some simple Emacs lisp commands for that which shell out to babashka.

How do I split my Emacs lisp code into packages?


How do others do it? I tried looking at the Doom Emacs creator dotfiles, but I didn’t find any Emacs config. https://github.com/hlissner/dotfiles/tree/0df9027010b424410a4622eba54b979c256f0efb/./ I guess his Emacs config is just Doom. What about https://github.com/tecosaur/? He just has a big org-mode file. What about https://github.com/org-roam/? Good. Toplevel org-roam.el. Then (require 'org-id') and others.

I want to learn how to create an Emacs minor mode.


It’s the next step, I think. I know how to do basic stuff, I don’t know how to do interactive stuff. I love how magit works. How dired works. Dired’s view over the file system, the ease of moving around.

So … I probably want a major mode too. Haha.



How can git tell me when a folder was last changed?

Git knows this.

I would like: input folder path, output last changed timestamp.

Purpose: sort, enrich :relations.

Treat :changed the same way as I’m treating :id now. It’s a special tag, and should not be written down. When writing lines back to files, dissoc the :changed property.

Note categories

named ideas have a deeper meaning. They have an URL, and can be linked to.

project journal is temporally indexed. Date up top, topic below. Project-scope rambling.

project problems is a mutable approach to attention design. It does not function as a ledger. Rather, it is meant to be changed. Problem-scope attention design.

journal is the temporally indexed global catch-all thing. Put things here when in doubt. Global-scope rambling.

problems is a global list of things that want attention. Global scope attention design.


How to get nice-to-copy terminal output with GRML ZSH config

teodorlu@teod-t490s ~/tmp/temp-2022-07-15/prompt % prompt off
% PROMPT="$ "
$ echo hello there
hello there


I’m afraid to stop learning

I don’t want to end up stuck. In a context where there’s no novelty. Where there’s nothing I can learn.

What does such a context look like?

Is it closer to a research lab than a product company? Can there be both?

Reading Elements of Clojure

ec Nextcloud/store/elementsofclojure.pdf

“Indirection is abstraction”

Indirection, also sometimes called abstraction, is the foundation of the software we write. Layers of indirection can be peeled away incrementally, allowing us to work withint a codebase without understanding its entrirety. Without indrection, we’d be unable to write software longer than a few hundred lines.

Huh, this names something I’ve seen. Python scripts written by civil engineers. One big for loop, with some clauses.

Advantage: straightforward. Disadvantage: Inflexible. Lesson: use indirection / abstraction to make code flexible.

SJ train Wifi allows SSH traffic




Participating in public

I wrote something I thought was of value:

  1. Make sure your note taking system supports your goals. My goals: (A) assist my learning, (B) easily share content and get feedback from others.
  1. When you produce content, consider (A) what you want to achieve by producing the content, and (B) how you want to find the content later.

  2. Use one global namespace for named concepts. Category / taxonomy / tags belongs in metadata.

Why the goals? If your system supports your goals, you will continue to use it and get value from it. If your system doesn’t support your goals, it becomes tedious to use, and you’ll abandon your notes.

I encourage you to put your notes publicly on the web. Public notes have URLs, and there’s no easier way to read content. You’re going to remember notes.yourname.com/THING, or just go via notes.yourname.com to list / search.

My information architecture consists of named concepts, journals and metadata.

Named concepts is the top level. Wikipedia uses this structure. There’s one global namespace with sufficiently qualified names. You are going to remember your note by this name. Disambiguate in your global names.

Journals are organized by date. The advantage of journals is that you don’t have to name anything. In general, it’s nice to start with a journal, and collect named concepts on demand. Journals don’t have to be discoverable.

Metadata helps you discover and index your notes. Categories and tags go here. But don’t go nuts on categorization, think about what those categories should achieve. Remember the fact boxes on Wikipedia? Those are driven by concept metadata. Sometimes it’s better to embed a table or a nested list on a concept page than introduce metadata. “Is this helpful to understand the concept?” - put it on the page. “Is this helpful to find/index your content?” - it’s metadata.

Let’s say you want to learn FUSE (https://en.wikipedia.org/wiki/Filesystem_in_Userspace). Create a journal page for learning FUSE, and tag it as “open problem”. Make sure you can list open problems. Each time you’ve got some time, open your FUSE journal, and work to understand something. Read the man page. Read wikipedia. Read the source. But annotate! Take notes in your journal as you go. When you revisit your FUSE journal, you can easily rediscover where you were last time, and decide where you want to go next.

Didn’t get any comments.

Am I dissatisfied? Doesn’t feel that way. Am I surprised? Yes, perhaps. That’s imprecise. Yes, I’m surprised. This is something I believe strongly in.

Am I disappointed? No.

Got the dots working

  1. create dots
  2. realize build system wasn’t yet configurable
  3. try to go for perfect
  4. ditch perfect, go for achievable instead
  5. Be happy.




bimodal strategies

deep work, tactical initiatives, strategic initiatives

Applying bimodal strategies to the design of the daily effort.

produce documents

Squirreltime – topic of stuff :)

Burnout, meaning and deep work. Reflecting on the last half year.

A perfect day

How is a perfect day structured?

copied from Roam notes

How do I want to work?

Principles to prevent burnout, mess and loss of the strategic picture.

copied from Roam notes



I want Live.js to work on this site

Live.js: https://livejs.com/

I’m hosting with Cloudflare.

Problem: there’s no live-reload.

Diagnosis: Cloudlfare sends the same headers on each request. That’s meant to disallow caching. But in my case, it causes cache invalidation to never happen – opposite of the intended effect. I could fork live.js if I want, it’s small.

To get Live.js working with Cloudflare, I need Cloudflare to produce correct etag headers. That means I need to disable some Cloudflare stuff.

General Cloudflare Etag docs: https://support.cloudflare.com/hc/en-us/articles/218505467-Using-ETag-Headers-with-Cloudflare

Cloudflare pages dog, mentions etags: https://developers.cloudflare.com/pages/platform/serving-pages/

Do I have live reload in production now?

Pretty pleeeease


Still no etag header on the responses.

Another fix

is it automatic or not?????

It’s actually automatic. It works!!!




How do contribute when you don’t know what you’re doing

Also, “how to help”.

From Aphorisms:

10 - When in doubt, do that which builds trust.

11 - When still in doubt, do that which reifies and distributes intent.

12 - When still in doubt, reduce WIP.

13 - When STILL in doubt, improve your specific & general feedback loops.


Target audience: xxx

When taking notes, why not just copy-paste from wikipedia?

Because taking notes is 90 % for the process, and only 10 % for the resulting artifact.

Because simply copying does not help you reify your taste. It does not help you to cultivate your taste.


wanna code



se Terminalen: Hvordan løpe med motorsag. Jeg har lyst til å bruke dingsen til Magnus. Det hadde vært fint. Men … hvor var det vi gjorde det? Vi flyttet det til et Iterate-repo, vi.


On the bitter aftertaste of cultivated aesthetics

“Cultivate your aesthetic”, visa said. I wanted answers. I wanted a preference. So I figured, Okay, let’s try that. Let’s dig in.

I had no idea what that little germ of an idea would do to me.

Now, it has changed how I think. I find more joy, meaning, purpose and connection in each day. I enjoy doing my work. I’m not doing it for somebody else, I’m doing it for me.

Yet — at times, the devil in me shows his face. His strikes are more powerful. His tongue sharper, his arguments bear more conviction than before.

I realize that I cannot be only kind.

As I started to write today, I expected to want to explore bitterness. Bitterness at lack of quality. Bitterness when those around me don’t care.

I had a lang walk+talk with Sindre today. We talked about things I’m frustrated with. (and we bumped into Ida, which was fun)

I have this model of human relationships. Your relationship with someone has three attributes:

  1. Trust
  2. Shared sense of quality
  3. Shared intent

I find that trust and shared intent can be built. And building those are sort of … easy. Well, it’s not exactly easy. But it’s soluable, in the words of David Deutsch. It’s work. It’s something you can do. Improve trust and shared intent every day, and you’ll succeed. (or figure out that this is someone you don’t want to work with)

I digress.

I don’t think shared sense of quality can be built. I think shared sense of quality is discovered. You figure out what someone likes, explore their kneejerk reactions. What do they deliver, when given freedom? Is it any good?

Making an explicit effort to cultivate my own aesthetic has sharpened my inner critic. I see clearly what I like and what I despise. And my reaction to content without substance is bitterness and disgust.

Is this my new normal? Is disgust the price to pay for joy? Does a tree with branches reaching to heaven necessarily need roots anchored to hell?

We’ll see.

Oh, we’re not quite done. The eagles have come, saved the day, and we wake up in Rivendell.

I actually feel good now. Being dead serious, actually honest about that sense of quality. I felt like an ass when I was in the heat of the moment. Now I feel … relief. I feel good. I didn’t expect that.

I bet there’s a lesson in here somewhere.

Until next time,


we wake up again, this time in the Shire. Yet Another End That’s Not An End.

I read this:

feeling proud of your work is critical for any ambitious/high-achieving person. for this type, it’s not about the hours put in, it’s about the *feeling they get out* of doing the work. and if they’re not interested in the work, it’s hard to make it phenomenal and be proud of it.

— Isabel⚡️ (@isabelunraveled) November 7, 2022

The War of Art introduces “territorial orientation”. Here’s a quote from the chapter The Definition of a Hack:

In other words, the hack writes hierarchically. He writes what he imagines will play well in the eyes of others. He does not ask himself, “What do I want to write? What do I think is important?” Instead, he asks “What’s hot, what can I make a deal for?”

The hack is like a politician who consults the polls before he takes a position. He’s a demagogue. He panders.

“so what?”

Yeah, that’s the feeling of quality and lack thereof. The joy of doing something worthwhile, and the disgust of wading through swamps.

“so what, you feel like complaining?”

No. I like where I am. I like where I’m going.

And I like that I don’t like everything I see.

bitter can be good. Ginger. A good beer. Grapefruit.

👋 talk to you later,


We had some snow! Now we have some gray stuff.

cond-> confusion

I expect the following to evaluate without crashing.

(cond-> (list 1 2 3)
  number? inc)

Do you know what? It crashes! Who would have thought.

1. Unhandled java.lang.ClassCastException
   class clojure.lang.PersistentList cannot be cast to class java.lang.Number
   (clojure.lang.PersistentList is in unnamed module of loader 'app';
   java.lang.Number is in module java.base of loader 'bootstrap')

I thought

(cond-> form condition transform)

was equivalent to

(if (condition form)
  (transform form)

, but it appears I was wrong. Not sure why.



How to ask for stuff: in public or in private?

Proposed principle: don’t ask people for stuff in public.

Why? I was in an E-mail chain with five other people. I don’t like those E-mail chains.

Proposed principle:

  1. Provide information and invitations in public
  2. Ask for stuff in private.

Why? It decouples “is this disrespectful” from other questions as “what is good?” and “what is practical?”.

trailing whitespace