<rss xmlns:source="http://source.scripting.com/" version="2.0">
  <channel>
    <title>Carlo Zottmann</title>
    <link>https://zottmann.org/</link>
    <description></description>
    
    <language>en</language>
    
    <lastBuildDate>Sat, 27 Jun 2026 19:56:52 +0200</lastBuildDate>
    <item>
      <title>pi-automode: Bringing Claude&#39;s Auto Mode to Pi</title>
      <link>https://zottmann.org/2026/06/27/piautomode-bringing-claudes-auto-mode.html</link>
      <pubDate>Sat, 27 Jun 2026 19:56:52 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2026/06/27/piautomode-bringing-claudes-auto-mode.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m having a good time with &lt;a href=&#34;https://pi.dev/&#34;&gt;pi&lt;/a&gt;, a lovely minimal agent harness made by thoughtful Austrians. It&amp;rsquo;s a super-solid, very extensible foundation to work with, and writing your own extensions is more than encouraged. I like that.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;One of the new features in Claude Code that I ended up digging a lot more than I had anticipated is its &lt;a href=&#34;https://claude.com/blog/auto-mode&#34;&gt;Auto Mode&lt;/a&gt;. It is not sandboxing; it&amp;rsquo;s a set of guardrails. The basic idea of Auto Mode is that there&amp;rsquo;s a separate classifier model looking at everything the clanker is trying to do (reads, writes, edits, any tool calls, etc.) and judging it against a list of rules, rules like &lt;em&gt;&amp;ldquo;Do not exfiltrate secrets.&amp;quot;&lt;/em&gt; The classifier model is independent from the main model, they do not cross their streams, and it has the power to stop an agent&amp;rsquo;s action before it is executed. Again: this is &amp;ldquo;only&amp;rdquo; surprisingly effective guardrails.&lt;/p&gt;
&lt;p&gt;I was sceptical at first, but it works exceptionally well for me. For some work, I still like to put the clanker in a VM, but most of the time, I&amp;rsquo;m quite happy with auto mode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pi doesn&amp;rsquo;t have an auto mode.&lt;/strong&gt; There were a couple of extensions, but none did it for me; either I ran into issues or they weren&amp;rsquo;t working as expected.&lt;/p&gt;
&lt;p&gt;My &lt;a href=&#34;https://github.com/czottmann/pi-automode&#34;&gt;pi-automode&lt;/a&gt; guardrail extension brings Auto Mode to Pi (who would&amp;rsquo;ve guessed) and is modelled after Claude Code&amp;rsquo;s behavior. From the readme:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It intercepts agent tool calls before execution and blocks actions that match permission deny rules, deterministic hard-deny checks, or the auto-mode classifier&amp;rsquo;s block decision. […]&lt;/p&gt;
&lt;p&gt;Extensions run in the Pi process, and a determined malicious extension can do anything your user account can do. It also does not guard user &lt;code&gt;!&lt;/code&gt;/&lt;code&gt;!!&lt;/code&gt; shell commands; by design, it guards agent tool calls only. Use this to reduce unsafe autonomous tool use, not as an OS security boundary.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&amp;rsquo;s customizable, comes with a sane set of default rules, supports extra rules, shows you whether it&amp;rsquo;s running or not in the footer, outputs clear deny messages, and tries to be a good pi plugin in general.&lt;/p&gt;
&lt;p&gt;After installing it, pick a classifier model via &lt;code&gt;/automode model&lt;/code&gt;. Personally, I&amp;rsquo;m quite fond of &lt;code&gt;gemma-4-26b-a4b-it&lt;/code&gt; for that task, served by French inference provider Scaleway: capable AF, cheap as chips, and plenty fast (usually &amp;gt;500 tps). I access Scaleway through the EU-based inference router Cortecs &lt;a href=&#34;https://zottmann.org/2026/05/20/the-european-llm-routers-are.html&#34;&gt;which I&amp;rsquo;ve written about here a few weeks ago&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After picking a model, you&amp;rsquo;re good to go. Give it a spin! I use it a lot, I like it a lot, and maybe it&amp;rsquo;s something you&amp;rsquo;ll find useful, too.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GitHub repo: &lt;a href=&#34;https://github.com/czottmann/pi-automode&#34;&gt;https://github.com/czottmann/pi-automode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;pi.dev: &lt;a href=&#34;https://pi.dev/packages/@czottmann/pi-automode&#34;&gt;https://pi.dev/packages/@czottmann/pi-automode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;npmx: &lt;a href=&#34;https://npmx.dev/package/@czottmann/pi-automode&#34;&gt;https://npmx.dev/package/@czottmann/pi-automode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/@czottmann/pi-automode&#34;&gt;https://www.npmjs.com/package/@czottmann/pi-automode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <source:markdown>I&#39;m having a good time with [pi](https://pi.dev/), a lovely minimal agent harness made by thoughtful Austrians. It&#39;s a super-solid, very extensible foundation to work with, and writing your own extensions is more than encouraged. I like that.

---

One of the new features in Claude Code that I ended up digging a lot more than I had anticipated is its [Auto Mode](https://claude.com/blog/auto-mode). It is not sandboxing; it&#39;s a set of guardrails. The basic idea of Auto Mode is that there&#39;s a separate classifier model looking at everything the clanker is trying to do (reads, writes, edits, any tool calls, etc.) and judging it against a list of rules, rules like _&#34;Do not exfiltrate secrets.&#34;_ The classifier model is independent from the main model, they do not cross their streams, and it has the power to stop an agent&#39;s action before it is executed. Again: this is &#34;only&#34; surprisingly effective guardrails.

I was sceptical at first, but it works exceptionally well for me. For some work, I still like to put the clanker in a VM, but most of the time, I&#39;m quite happy with auto mode.

**Pi doesn&#39;t have an auto mode.** There were a couple of extensions, but none did it for me; either I ran into issues or they weren&#39;t working as expected.

My [pi-automode](https://github.com/czottmann/pi-automode) guardrail extension brings Auto Mode to Pi (who would&#39;ve guessed) and is modelled after Claude Code&#39;s behavior. From the readme:

&gt; It intercepts agent tool calls before execution and blocks actions that match permission deny rules, deterministic hard-deny checks, or the auto-mode classifier&#39;s block decision. […]
&gt;
&gt; Extensions run in the Pi process, and a determined malicious extension can do anything your user account can do. It also does not guard user `!`/`!!` shell commands; by design, it guards agent tool calls only. Use this to reduce unsafe autonomous tool use, not as an OS security boundary.

It&#39;s customizable, comes with a sane set of default rules, supports extra rules, shows you whether it&#39;s running or not in the footer, outputs clear deny messages, and tries to be a good pi plugin in general.

After installing it, pick a classifier model via `/automode model`. Personally, I&#39;m quite fond of `gemma-4-26b-a4b-it` for that task, served by French inference provider Scaleway: capable AF, cheap as chips, and plenty fast (usually &gt;500 tps). I access Scaleway through the EU-based inference router Cortecs [which I&#39;ve written about here a few weeks ago](https://zottmann.org/2026/05/20/the-european-llm-routers-are.html).

After picking a model, you&#39;re good to go. Give it a spin! I use it a lot, I like it a lot, and maybe it&#39;s something you&#39;ll find useful, too.

- GitHub repo: &lt;https://github.com/czottmann/pi-automode&gt;
- pi.dev: &lt;https://pi.dev/packages/@czottmann/pi-automode&gt;
- npmx: &lt;https://npmx.dev/package/@czottmann/pi-automode&gt;
- npm: &lt;https://www.npmjs.com/package/@czottmann/pi-automode&gt;
</source:markdown>
    </item>
    
    <item>
      <title>On software developers mourning &#34;The loss of our craft&#34; due to AI, part 67354</title>
      <link>https://zottmann.org/2026/06/25/on-software-developers-mourning-the.html</link>
      <pubDate>Thu, 25 Jun 2026 13:38:32 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2026/06/25/on-software-developers-mourning-the.html</guid>
      <description>&lt;p&gt;My man &lt;a href=&#34;https://bsky.app/profile/hmans.dev/post/3mox5pk33ok2z&#34;&gt;Hendrik over on Bluesky&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How can we reach people like this and show them that their understanding of AI coding is fundamentally wrong, and that the reason they&amp;rsquo;re not finding a job is not that AI has killed the job market, but that they’re demonstrating a profound unwillingness to adapt to a changing technology landscape?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;It&amp;rsquo;s not like the job market was that much better before AI infested every single corner of the market, but it supercharged all of the worst aspects of everything. I&amp;rsquo;ve seen people supposedly smarter than I advocate for just giving in, conceding to AI coding as it&amp;rsquo;s the future. But doing so means tossing out my friends who make art or the people who work their asses off to properly test and review code or the writers pouring all of their energy into even mundane dialogue. It means throwing out my dignity as a software engineer, as someone that truly gives a shit about security and code.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;I answered on Bluesky but I feel like re-iterating it here, too.&lt;/p&gt;
&lt;p&gt;To me, &lt;strong&gt;the framing those folks use is all wrong.&lt;/strong&gt; At least in our craft (software development), you&amp;rsquo;ll have to understand AI as a &lt;em&gt;tool&lt;/em&gt; to do your job, not as a means to an end.&lt;/p&gt;
&lt;p&gt;But that requires curiosity and a willingness to step out of your comfort zone, not just once but repeatedly, not least because the field is evolving at an insanely fast clip. &lt;em&gt;&amp;ldquo;I played around with ChatGPT once for half an hour, about 2 years ago, it was crap&amp;rdquo;&lt;/em&gt; doesn&amp;rsquo;t cut it anymore. Because your knowledge is outdated by ~2 years, friend.&lt;/p&gt;
&lt;p&gt;Me, personally? I love watching LLMs understand my intent and code, writing new functionality, helping me brainstorm, spitball, iterate, develop. And truth be told: my apps are much better for it. More meaningful tests, less overlooked edge cases, greater stability. It&amp;rsquo;s ace! But then again, I started programming when I was 12, and work in this field in a professional fashion for ~30 years by now. That experience helps, it helps a lot. I don&amp;rsquo;t just lean back and let &amp;lsquo;er rip, I understand &lt;em&gt;my&lt;/em&gt; job and the job of my &lt;em&gt;tools.&lt;/em&gt; Those are quite different!&lt;/p&gt;
&lt;p&gt;But back to the framing: CEOs telling everyone that AI will totally fuck us over but it&amp;rsquo;ll be great and desirable, that doesn&amp;rsquo;t help either. Theirs is often a zero-sum framing, and frankly, it&amp;rsquo;s shit.&lt;/p&gt;
&lt;p&gt;I get why folks are against that. That framing plays hard into existing fears (job loss, loss of status, etc. — not unfounded), and that wave of fear and rejection &lt;em&gt;can be&lt;/em&gt; another thing to hide behind for people who are unwilling to adapt, out of fear, or uncertainty, or sheer laziness of mind.&lt;/p&gt;
&lt;p&gt;So, I think &lt;strong&gt;we will be unable to reach some folks because they simply do not want to be reached.&lt;/strong&gt; For some, it&amp;rsquo;s easier to just conflate any and all AI use, condemn it flat-out, and frame it as an act of solidarity with creators. For some, it&amp;rsquo;s a willful rejection of nuance, I think.&lt;/p&gt;
&lt;p&gt;On the risk of repeating myself: it&amp;rsquo;s exhausting, all of it. All the while agentic development continues to blow my mind, in a very good way.&lt;/p&gt;
</description>
      <source:markdown>My man [Hendrik over on Bluesky](https://bsky.app/profile/hmans.dev/post/3mox5pk33ok2z):

&gt; How can we reach people like this and show them that their understanding of AI coding is fundamentally wrong, and that the reason they&#39;re not finding a job is not that AI has killed the job market, but that they’re demonstrating a profound unwillingness to adapt to a changing technology landscape?
&gt;
&gt; &gt; &#34;It&#39;s not like the job market was that much better before AI infested every single corner of the market, but it supercharged all of the worst aspects of everything. I&#39;ve seen people supposedly smarter than I advocate for just giving in, conceding to AI coding as it&#39;s the future. But doing so means tossing out my friends who make art or the people who work their asses off to properly test and review code or the writers pouring all of their energy into even mundane dialogue. It means throwing out my dignity as a software engineer, as someone that truly gives a shit about security and code.&#34;

I answered on Bluesky but I feel like re-iterating it here, too.

To me, **the framing those folks use is all wrong.** At least in our craft (software development), you&#39;ll have to understand AI as a *tool* to do your job, not as a means to an end.

But that requires curiosity and a willingness to step out of your comfort zone, not just once but repeatedly, not least because the field is evolving at an insanely fast clip. _&#34;I played around with ChatGPT once for half an hour, about 2 years ago, it was crap&#34;_ doesn&#39;t cut it anymore. Because your knowledge is outdated by ~2 years, friend.

Me, personally? I love watching LLMs understand my intent and code, writing new functionality, helping me brainstorm, spitball, iterate, develop. And truth be told: my apps are much better for it. More meaningful tests, less overlooked edge cases, greater stability. It&#39;s ace! But then again, I started programming when I was 12, and work in this field in a professional fashion for ~30 years by now. That experience helps, it helps a lot. I don&#39;t just lean back and let &#39;er rip, I understand _my_ job and the job of my _tools._ Those are quite different!

But back to the framing: CEOs telling everyone that AI will totally fuck us over but it&#39;ll be great and desirable, that doesn&#39;t help either. Theirs is often a zero-sum framing, and frankly, it&#39;s shit.

I get why folks are against that. That framing plays hard into existing fears (job loss, loss of status, etc. — not unfounded), and that wave of fear and rejection *can be* another thing to hide behind for people who are unwilling to adapt, out of fear, or uncertainty, or sheer laziness of mind.

So, I think **we will be unable to reach some folks because they simply do not want to be reached.** For some, it&#39;s easier to just conflate any and all AI use, condemn it flat-out, and frame it as an act of solidarity with creators. For some, it&#39;s a willful rejection of nuance, I think.

On the risk of repeating myself: it&#39;s exhausting, all of it. All the while agentic development continues to blow my mind, in a very good way.
</source:markdown>
    </item>
    
    <item>
      <title>I&#39;ve changed the pricing for Actions For Obsidian</title>
      <link>https://zottmann.org/2026/06/22/ive-changed-the-pricing-for.html</link>
      <pubDate>Mon, 22 Jun 2026 17:20:02 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2026/06/22/ive-changed-the-pricing-for.html</guid>
      <description>&lt;p&gt;TL;DR: &lt;strong&gt;Most in-app purchases got more affordable, biggest price drops outside the Euro zone.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It was brought to my attention that, internationally, the pricing for my macOS/iOS app &lt;a href=&#34;https://actions.work/actions-for-obsidian&#34;&gt;Actions For Obsidian&lt;/a&gt; was a bit odd. Apple isn&amp;rsquo;t without blame here, but ultimately that&amp;rsquo;s fully on me. I simply didn&amp;rsquo;t check all the local prices after setting the base Euro price tags, and I apologize.&lt;/p&gt;
&lt;h3 id=&#34;why-it-matters&#34;&gt;Why it matters&lt;/h3&gt;
&lt;p&gt;The below changes do not look like much if you&amp;rsquo;re the Euro zone but &lt;strong&gt;for calculated local prices the difference can be staggering&lt;/strong&gt;. Read below for more.&lt;/p&gt;
&lt;h3 id=&#34;the-new-pricing&#34;&gt;The new pricing&lt;/h3&gt;
&lt;p&gt;… went into effect over the weekend:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;text-align:left&#34;&gt;License&lt;/th&gt;
&lt;th style=&#34;text-align:center&#34;&gt;&amp;ldquo;Size&amp;rdquo;&lt;/th&gt;
&lt;th style=&#34;text-align:right&#34;&gt;Old price&lt;/th&gt;
&lt;th style=&#34;text-align:right&#34;&gt;New price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;macOS &lt;strong&gt;or&lt;/strong&gt; iOS&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;S&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€11&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€9.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;M&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€14&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€12.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;L&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€17&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€17.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Upgrade to both platforms&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;S&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€8&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€7.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;M&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€10&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€9.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;L&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€12&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€12.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Combo license for both&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;S&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€19&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€17.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;M&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€24&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€20.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;L&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€29&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;€25.99&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;You can access them from inside the app.&lt;/p&gt;
&lt;h3 id=&#34;where-i-went-wrong&#34;&gt;Where I went wrong&lt;/h3&gt;
&lt;p&gt;I looked a bit deeper into how Apple calculates local prices, and …it&amp;rsquo;s surprisingly lazy? Totally bonkers? Both, actually. I had no idea!&lt;/p&gt;
&lt;p&gt;So, when a developer sets the price for an app or &lt;abbr title=&#34;In App Purchases&#34;&gt;IAPs&lt;/abbr&gt; in the App Store, they can either pick from a pre-defined list of prices, or they can enter an arbitrary price. Since I like nice round numbers, I set the price to €19 instead of €18.99 (using one of the IAPs as example).&lt;/p&gt;
&lt;p&gt;Here, Apple&amp;rsquo;s price calculation kicked in. I then spot-checked a few of those prices, they all looked fine, and I considered that task completed.&lt;/p&gt;
&lt;p&gt;So, in the process, for those €19, Apple&amp;rsquo;s &amp;ldquo;calculation&amp;rdquo; set the local Australian price to AUD 29.00.&lt;/p&gt;
&lt;p&gt;Only last week I learned that when I decrease that nice round €19 to €18.99, the Aussie price becomes AUD 29.99. (What.) Changing it to €18.00, it becomes AUD 29.00 again. Going down to €17.99, the local price drops to AUD 22.99. 🤯&lt;/p&gt;
&lt;p&gt;Absolutely wild.&lt;/p&gt;
&lt;h3 id=&#34;but-why-carlo&#34;&gt;But why, Carlo‽&lt;/h3&gt;
&lt;p&gt;My current theory is that Apple really, &lt;strong&gt;really&lt;/strong&gt; wants us developers to use their *.99 pricing scheme matrix (…, €7.99, €8.99, €9.99, €12.99, €14.99, €17.99, €19.99, €24.99, etc.). For those prices, there are properly calculated local counterparts.&lt;/p&gt;
&lt;p&gt;But for those custom prices, Apple doesn&amp;rsquo;t actually calculate anything, it seems; they just use the nearest &amp;ldquo;standard&amp;rdquo; price. That&amp;rsquo;s why €17.99 comes out as AUD 22.99 correctly (because €17.99 exists in Apple&amp;rsquo;s matrix), but €18 is AUD 29.00 because their pricing matrix doesn&amp;rsquo;t contain that.&lt;/p&gt;
&lt;p&gt;And it&amp;rsquo;s not just about Australia — my apps are available in 150+ countries which don&amp;rsquo;t use the EUR. 🤦🏻‍♂️&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;To my (potential) customers, I&amp;rsquo;m sorry for the oversight. If you were still on the fence whether or not to spend money on Actions For Obsidian, I do hope you take another friendly look.&lt;/p&gt;
&lt;p&gt;To my fellow developers, spot-check your local prices often. 😅&lt;/p&gt;
&lt;p&gt;PS: &lt;a href=&#34;https://actions.work/&#34;&gt;My other useful, nice, non-AppStore apps are unaffected by Apple&amp;rsquo;s pricing, and still very affordable.&lt;/a&gt;  Wink wink, nudge nudge. 🤙🏼&lt;/p&gt;
</description>
      <source:markdown>TL;DR: **Most in-app purchases got more affordable, biggest price drops outside the Euro zone.**

It was brought to my attention that, internationally, the pricing for my macOS/iOS app [Actions For Obsidian](https://actions.work/actions-for-obsidian) was a bit odd. Apple isn&#39;t without blame here, but ultimately that&#39;s fully on me. I simply didn&#39;t check all the local prices after setting the base Euro price tags, and I apologize.

### Why it matters

The below changes do not look like much if you&#39;re the Euro zone but **for calculated local prices the difference can be staggering**. Read below for more.

### The new pricing

… went into effect over the weekend:

| License                   | &#34;Size&#34; | Old price | New price |
|:--------------------------|:------:|----------:|----------:|
| macOS **or** iOS          | S      | €11       | €9.99     |
|                           | M      | €14       | €12.99    |
|                           | L      | €17       | €17.99    |
| Upgrade to both platforms | S      | €8        | €7.99     |
|                           | M      | €10       | €9.99     |
|                           | L      | €12       | €12.99    |
| Combo license for both    | S      | €19       | €17.99    |
|                           | M      | €24       | €20.99    |
|                           | L      | €29       | €25.99    |

You can access them from inside the app.

### Where I went wrong

I looked a bit deeper into how Apple calculates local prices, and …it&#39;s surprisingly lazy? Totally bonkers? Both, actually. I had no idea!

So, when a developer sets the price for an app or &lt;abbr title=&#34;In App Purchases&#34;&gt;IAPs&lt;/abbr&gt; in the App Store, they can either pick from a pre-defined list of prices, or they can enter an arbitrary price. Since I like nice round numbers, I set the price to €19 instead of €18.99 (using one of the IAPs as example).

Here, Apple&#39;s price calculation kicked in. I then spot-checked a few of those prices, they all looked fine, and I considered that task completed.

So, in the process, for those €19, Apple&#39;s &#34;calculation&#34; set the local Australian price to AUD 29.00.

Only last week I learned that when I decrease that nice round €19 to €18.99, the Aussie price becomes AUD&amp;nbsp;29.99. (What.) Changing it to €18.00, it becomes AUD&amp;nbsp;29.00 again. Going down to €17.99, the local price drops to AUD&amp;nbsp;22.99. 🤯

Absolutely wild.

### But why, Carlo‽

My current theory is that Apple really, **really** wants us developers to use their *.99 pricing scheme matrix (…, €7.99, €8.99, €9.99, €12.99, €14.99, €17.99, €19.99, €24.99, etc.). For those prices, there are properly calculated local counterparts.

But for those custom prices, Apple doesn&#39;t actually calculate anything, it seems; they just use the nearest &#34;standard&#34; price. That&#39;s why €17.99 comes out as AUD&amp;nbsp;22.99 correctly (because €17.99 exists in Apple&#39;s matrix), but €18 is AUD&amp;nbsp;29.00 because their pricing matrix doesn&#39;t contain that.

And it&#39;s not just about Australia — my apps are available in 150+ countries which don&#39;t use the EUR. 🤦🏻‍♂️

### Summary

To my (potential) customers, I&#39;m sorry for the oversight. If you were still on the fence whether or not to spend money on Actions For Obsidian, I do hope you take another friendly look.

To my fellow developers, spot-check your local prices often. 😅

PS: [My other useful, nice, non-AppStore apps are unaffected by Apple&#39;s pricing, and still very affordable.](https://actions.work/)  Wink wink, nudge nudge. 🤙🏼
</source:markdown>
    </item>
    
    <item>
      <title>The European LLM Routers Are Here</title>
      <link>https://zottmann.org/2026/05/20/the-european-llm-routers-are.html</link>
      <pubDate>Wed, 20 May 2026 16:36:39 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2026/05/20/the-european-llm-routers-are.html</guid>
      <description>&lt;p&gt;If you&amp;rsquo;re EU based and need a GDPR compliant LLM router for legal (or ethical) reasons, don&amp;rsquo;t sleep on &lt;a href=&#34;https://cortecs.ai/&#34;&gt;Cortecs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(What is an LLM router? It&amp;rsquo;s a software layer positioned between an application and multiple large language model (LLM) providers that directs incoming prompts to the most suitable model for a specific task. So you get one API URL and specify the model in your calls, whether that model is from Google, DeepSeek, or Mistral. Behind the scenes, the LLM router connects your request with a suitable provider.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t get me wrong, I really like the OG &lt;a href=&#34;https://openrouter.ai/&#34;&gt;OpenRouter&lt;/a&gt; — IMHO it&amp;rsquo;s still without equal when it comes to both the number of supported providers and configurability. But as a European business that wants to reduce US-based risks in its supply chain, it might not be a good-enough option: even if you use an EU-based inference provider through OpenRouter, OR itself remains a venture in US jurisdiction.&lt;/p&gt;
&lt;p&gt;Based in Vienna (Austria), Cortecs offers &lt;a href=&#34;https://cortecs.ai/serverlessModels&#34;&gt;a good (small) selection of models&lt;/a&gt;, zero data retention (ZDR), and &amp;ldquo;sovereign cloud&amp;rdquo; toggles. It&amp;rsquo;s clearly early days, but good stuff already.&lt;/p&gt;
&lt;p&gt;My current go-to example: I like DeepSeek V4 Flash, a very fine offering from the eponymous Chinese lab. With Cortecs, I can do that: with ZDR, fully EU-based, in a 100% GDPR compliant way. One of their supported providers is &lt;a href=&#34;https://tensorix.ai/&#34;&gt;Tensorix&lt;/a&gt; from Ireland, and they offer that model. These days, &lt;a href=&#34;https://cortecs.ai/detailedServerlessView/deepseek-v4-flash&#34;&gt;I get 350 tps throughput with ~1.5s latency&lt;/a&gt;, which isn&amp;rsquo;t bad at all.&lt;/p&gt;
&lt;p&gt;BTW, a very nice side effect of using LLM routers: I only have to deal with one party and only get one invoice, even if I use all of their supported providers. But that doesn&amp;rsquo;t mean lock-in! Cortecs&#39; API is OpenAI-compatible, so if I become unhappy with them at some point, I can just up and leave. Because that API is basically a standard by now, changing providers usually just means changing the base URL and model ID in my tool&amp;rsquo;s config.&lt;/p&gt;
&lt;p&gt;And since I use &lt;a href=&#34;https://pi.dev/&#34;&gt;pi&lt;/a&gt; quite a lot, I released &lt;a href=&#34;https://github.com/czottmann/pi-cortecs&#34;&gt;npm:pi-cortecs&lt;/a&gt; a few days ago. Nothing special — it just adds Cortecs provider support for &lt;code&gt;/login&lt;/code&gt;, a new &lt;code&gt;/cortecs-models&lt;/code&gt; command, and auto-fetching of their available models. Useful! Here&amp;rsquo;s the &lt;a href=&#34;https://npmx.dev/package/pi-cortecs&#34;&gt;npm package&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So, Cortecs is good. But it&amp;rsquo;s not the only kid on the block, mind. There&amp;rsquo;s also &lt;a href=&#34;https://tokenfactory.nebius.com/&#34;&gt;Nebius Token Factory&lt;/a&gt;, from the Netherlands, which is also good — they offer another set of models, and also support prepaid API billing. Don&amp;rsquo;t sleep on them, either. 😉&lt;/p&gt;
&lt;p&gt;#DataSovereignty #EU #LLM&lt;/p&gt;
</description>
      <source:markdown>If you&#39;re EU based and need a GDPR compliant LLM router for legal (or ethical) reasons, don&#39;t sleep on [Cortecs](https://cortecs.ai/).

_(What is an LLM router? It&#39;s a software layer positioned between an application and multiple large language model (LLM) providers that directs incoming prompts to the most suitable model for a specific task. So you get one API URL and specify the model in your calls, whether that model is from Google, DeepSeek, or Mistral. Behind the scenes, the LLM router connects your request with a suitable provider.)_

Don&#39;t get me wrong, I really like the OG [OpenRouter](https://openrouter.ai/) — IMHO it&#39;s still without equal when it comes to both the number of supported providers and configurability. But as a European business that wants to reduce US-based risks in its supply chain, it might not be a good-enough option: even if you use an EU-based inference provider through OpenRouter, OR itself remains a venture in US jurisdiction.

Based in Vienna (Austria), Cortecs offers [a good (small) selection of models](https://cortecs.ai/serverlessModels), zero data retention (ZDR), and &#34;sovereign cloud&#34; toggles. It&#39;s clearly early days, but good stuff already.

My current go-to example: I like DeepSeek V4 Flash, a very fine offering from the eponymous Chinese lab. With Cortecs, I can do that: with ZDR, fully EU-based, in a 100% GDPR compliant way. One of their supported providers is [Tensorix](https://tensorix.ai/) from Ireland, and they offer that model. These days, [I get 350 tps throughput with ~1.5s latency](https://cortecs.ai/detailedServerlessView/deepseek-v4-flash), which isn&#39;t bad at all.

BTW, a very nice side effect of using LLM routers: I only have to deal with one party and only get one invoice, even if I use all of their supported providers. But that doesn&#39;t mean lock-in! Cortecs&#39; API is OpenAI-compatible, so if I become unhappy with them at some point, I can just up and leave. Because that API is basically a standard by now, changing providers usually just means changing the base URL and model ID in my tool&#39;s config.

And since I use [pi](https://pi.dev/) quite a lot, I released [npm:pi-cortecs](https://github.com/czottmann/pi-cortecs) a few days ago. Nothing special — it just adds Cortecs provider support for `/login`, a new `/cortecs-models` command, and auto-fetching of their available models. Useful! Here&#39;s the [npm package](https://npmx.dev/package/pi-cortecs).

So, Cortecs is good. But it&#39;s not the only kid on the block, mind. There&#39;s also [Nebius Token Factory](https://tokenfactory.nebius.com/), from the Netherlands, which is also good — they offer another set of models, and also support prepaid API billing. Don&#39;t sleep on them, either. 😉

#DataSovereignty #EU #LLM
</source:markdown>
    </item>
    
    <item>
      <title>Nuance in the current AI Yay/Nay &#34;debate&#34;</title>
      <link>https://zottmann.org/2026/04/29/nuance-in-the-current-ai.html</link>
      <pubDate>Wed, 29 Apr 2026 17:39:21 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2026/04/29/nuance-in-the-current-ai.html</guid>
      <description>&lt;p&gt;I gotta say, as a place for getting undifferentiated and breathless &lt;em&gt;&amp;ldquo;ALL AI BAD&amp;rdquo;&lt;/em&gt; takes, Mastodon as a whole remains peerless. Bluesky is a close second.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s fucking exhausting, because any semblance of nuance is gone.&lt;/p&gt;
&lt;p&gt;On one side, there are the hyperactive &lt;em&gt;&amp;ldquo;AI RULES EVERYTHING AROUND ME&amp;rdquo;&lt;/em&gt; motherfuckers who want to disrupt/steamroll all the things, and who, for some reason that escapes me, assume they&amp;rsquo;re immune to the ongoing and coming shifts in both society and markets. (They&amp;rsquo;re very likely wrong.) You know the type: the dudes&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; who still think that tech will solve every problem, contrary to empirical evidence from the last 50-500 years.&lt;/p&gt;
&lt;p&gt;TBF, I guess I was one of them, 20-30 years ago, but I&amp;rsquo;ve evolved as a person.&lt;/p&gt;
&lt;p&gt;On the other side, there&amp;rsquo;s the &lt;em&gt;&amp;ldquo;never AI&amp;rdquo;&lt;/em&gt; &amp;amp; &lt;em&gt;&amp;ldquo;all AI is fascist&amp;rdquo;&lt;/em&gt;&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt; crowd, whose majority flat-out refuses to entertain the notion that the tech in question might be excessively useful to others and &lt;strong&gt;is already&lt;/strong&gt; enabling many people all over the globe to participate more fully in life. I&amp;rsquo;m not talking just about devs like me but about people like my own mother (who suffers from clinically deteriorating eyesight) and less fluent folks who have to deal with fields they&amp;rsquo;re not trained for, like, say, any type of bureaucracy. For example, having tech translate German gov&amp;rsquo;t documents into your native tongue in the blink of an eye and for free, that&amp;rsquo;s helpful. Having an infinitely patient &lt;em&gt;something&lt;/em&gt; help you understand what&amp;rsquo;s asked of you in those documents, now that&amp;rsquo;s a goddamn game changer.&lt;/p&gt;
&lt;p&gt;I know AI is neither a panacea for all our ills nor without flaws (and often questionable training), but at the same time I see it as very useful. Both those things can be true at the same time. You know, because &lt;em&gt;nuance.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;But the FUD and vitriol from &lt;strong&gt;both&lt;/strong&gt; sides is exhausting AF, and not helpful in any way.&lt;/p&gt;
&lt;section class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;And it&amp;rsquo;s usually dudes, let&amp;rsquo;s be honest here.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;It&amp;rsquo;s not, and just because you don&amp;rsquo;t know any words doesn&amp;rsquo;t mean the one argumentative sledgehammer you &lt;em&gt;do&lt;/em&gt; know fits every nail.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <source:markdown>I gotta say, as a place for getting undifferentiated and breathless _&#34;ALL AI BAD&#34;_ takes, Mastodon as a whole remains peerless. Bluesky is a close second.

It&#39;s fucking exhausting, because any semblance of nuance is gone.

On one side, there are the hyperactive _&#34;AI RULES EVERYTHING AROUND ME&#34;_ motherfuckers who want to disrupt/steamroll all the things, and who, for some reason that escapes me, assume they&#39;re immune to the ongoing and coming shifts in both society and markets. (They&#39;re very likely wrong.) You know the type: the dudes[^1] who still think that tech will solve every problem, contrary to empirical evidence from the last 50-500 years.

[^1]: And it&#39;s usually dudes, let&#39;s be honest here.

TBF, I guess I was one of them, 20-30 years ago, but I&#39;ve evolved as a person.

On the other side, there&#39;s the _&#34;never AI&#34;_ &amp; _&#34;all AI is fascist&#34;_[^2] crowd, whose majority flat-out refuses to entertain the notion that the tech in question might be excessively useful to others and **is already** enabling many people all over the globe to participate more fully in life. I&#39;m not talking just about devs like me but about people like my own mother (who suffers from clinically deteriorating eyesight) and less fluent folks who have to deal with fields they&#39;re not trained for, like, say, any type of bureaucracy. For example, having tech translate German gov&#39;t documents into your native tongue in the blink of an eye and for free, that&#39;s helpful. Having an infinitely patient _something_ help you understand what&#39;s asked of you in those documents, now that&#39;s a goddamn game changer.

[^2]: It&#39;s not, and just because you don&#39;t know any words doesn&#39;t mean the one argumentative sledgehammer you _do_ know fits every nail.

I know AI is neither a panacea for all our ills nor without flaws (and often questionable training), but at the same time I see it as very useful. Both those things can be true at the same time. You know, because _nuance._

But the FUD and vitriol from **both** sides is exhausting AF, and not helpful in any way.
</source:markdown>
    </item>
    
    <item>
      <title>TIL how to auto-move Xcode DEBUG builds to Applications</title>
      <link>https://zottmann.org/2026/01/24/til-how-to-automove-xcode.html</link>
      <pubDate>Sat, 24 Jan 2026 02:12:20 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2026/01/24/til-how-to-automove-xcode.html</guid>
      <description>&lt;p&gt;TIL a neat Xcode trick!&lt;/p&gt;
&lt;p&gt;So my apps contain app intents. Those won&amp;rsquo;t show up in Shortcuts unless the app is anywhere in &lt;code&gt;/Applications&lt;/code&gt;. For months now I&amp;rsquo;ve been using a post-build scheme script to move the DEBUG build there, w/ a symlink to the app in its original place in derived data.&lt;/p&gt;
&lt;p&gt;This worked well but was a whole thing because I had to make sure cleaning the build would wipe the moved app etc.&lt;/p&gt;
&lt;p&gt;Turns out I was overthinking it! All I needed to do was to add this in the right &lt;code&gt;.xcconfig&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;DEPLOYMENT_LOCATION = YES
DSTROOT = /Applications/Dev Builds
INSTALL_PATH = /
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now Xcode takes care of all the things, I don&amp;rsquo;t need those scripts anymore, and Xcode even correctly cleans the built when necessary.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s that. The more you know!&lt;/p&gt;
</description>
      <source:markdown>TIL a neat Xcode trick! 

So my apps contain app intents. Those won&#39;t show up in Shortcuts unless the app is anywhere in `/Applications`. For months now I&#39;ve been using a post-build scheme script to move the DEBUG build there, w/ a symlink to the app in its original place in derived data.

This worked well but was a whole thing because I had to make sure cleaning the build would wipe the moved app etc.

Turns out I was overthinking it! All I needed to do was to add this in the right `.xcconfig`:

```
DEPLOYMENT_LOCATION = YES
DSTROOT = /Applications/Dev Builds
INSTALL_PATH = /
```

Now Xcode takes care of all the things, I don&#39;t need those scripts anymore, and Xcode even correctly cleans the built when necessary.

So that&#39;s that. The more you know!
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://zottmann.org/2026/01/22/aller-deutschsprachigen-kommentare-zu-jedwedem.html</link>
      <pubDate>Thu, 22 Jan 2026 19:18:06 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2026/01/22/aller-deutschsprachigen-kommentare-zu-jedwedem.html</guid>
      <description>&lt;p&gt;83% aller deutschsprachigen Kommentare zu jedwedem Thema sind &lt;em&gt;&amp;ldquo;Das haben wir noch nie so gemacht&amp;rdquo;&lt;/em&gt;, &lt;em&gt;&amp;ldquo;Sie können hier nicht parken&amp;rdquo;&lt;/em&gt; und &lt;em&gt;&amp;ldquo;Hier wird nicht gerannt&amp;rdquo;&lt;/em&gt;, nur halt mit Hut und Trenchcoat und aufgeklebtem Schnauzbart.&lt;/p&gt;
</description>
      <source:markdown>83% aller deutschsprachigen Kommentare zu jedwedem Thema sind _&#34;Das haben wir noch nie so gemacht&#34;_, _&#34;Sie können hier nicht parken&#34;_ und _&#34;Hier wird nicht gerannt&#34;_, nur halt mit Hut und Trenchcoat und aufgeklebtem Schnauzbart.
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://zottmann.org/2026/01/01/quoting-craig-mod-on-the.html</link>
      <pubDate>Thu, 01 Jan 2026 22:40:41 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2026/01/01/quoting-craig-mod-on-the.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://craigmod.com/ridgeline/203/&#34;&gt;Quoting Craig Mod&lt;/a&gt;, on the correct way of handling the garbage we create:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A funny thing happens when a Snickers bar goes from whole to eaten — the wrapper transmogrifies from useful to toxic. Suddenly, this thing that was keeping germs and dirt off your chocolate sugar log is now &amp;ldquo;useless&amp;rdquo; and with this comes the heaviest burden a modern person unencumbered by genocide or famine can hold: garbage responsibility. […]&lt;/p&gt;
&lt;p&gt;There are no garbage cans in Kamakura, and, indeed, if you are buying a coffee to go, you will be responsible for that receptacle for, potentially, a very long time. This is your grandé-sized hair shirt to bear. […]&lt;/p&gt;
&lt;p&gt;This obsession with the immediate &amp;ldquo;unburdening&amp;rdquo; of a thing &lt;em&gt;you&lt;/em&gt; created is common in non-Japanese contexts, but I posit: The Japanese way is the correct way. Be an adult. Own your garbage. Garbage responsibility is something we’ve long since abdicated not only to faceless cans on street corners (or just all over the street, as seems to be the case in Manhattan or Paris), but also faceless developing countries around the world. Our oceans teem with the waste from generations of averted eyes. And I believe the two — local pathologies and attendant global pathologies — are &lt;em&gt;not&lt;/em&gt; not connected.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;m slowly starting to believe I&amp;rsquo;m secretly Japanese.&lt;/p&gt;
</description>
      <source:markdown>[Quoting Craig Mod](https://craigmod.com/ridgeline/203/), on the correct way of handling the garbage we create:

&gt; A funny thing happens when a Snickers bar goes from whole to eaten — the wrapper transmogrifies from useful to toxic. Suddenly, this thing that was keeping germs and dirt off your chocolate sugar log is now &#34;useless&#34; and with this comes the heaviest burden a modern person unencumbered by genocide or famine can hold: garbage responsibility. […]
&gt;
&gt; There are no garbage cans in Kamakura, and, indeed, if you are buying a coffee to go, you will be responsible for that receptacle for, potentially, a very long time. This is your grandé-sized hair shirt to bear. […]
&gt;
&gt; This obsession with the immediate &#34;unburdening&#34; of a thing *you* created is common in non-Japanese contexts, but I posit: The Japanese way is the correct way. Be an adult. Own your garbage. Garbage responsibility is something we’ve long since abdicated not only to faceless cans on street corners (or just all over the street, as seems to be the case in Manhattan or Paris), but also faceless developing countries around the world. Our oceans teem with the waste from generations of averted eyes. And I believe the two — local pathologies and attendant global pathologies — are *not* not connected.

I&#39;m slowly starting to believe I&#39;m secretly Japanese.
</source:markdown>
    </item>
    
    <item>
      <title>Toleranz ist kein moralisches Gebot, sondern ein Friedensvertrag</title>
      <link>https://zottmann.org/2026/01/01/toleranz-ist-kein-moralisches-gebot.html</link>
      <pubDate>Thu, 01 Jan 2026 15:36:01 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2026/01/01/toleranz-ist-kein-moralisches-gebot.html</guid>
      <description>&lt;p&gt;Das Essay &lt;a href=&#34;https://medium.com/extra-extra/tolerance-is-not-a-moral-precept-1af7007d6376&#34;&gt;Tolerance is not a moral precept&lt;/a&gt; von Yonatan Zunger ist eines, das ich regelmäßig auf Mastodon teile, denn es ist sehr gut. Yonatan löst das scheinbare &lt;a href=&#34;https://de.wikipedia.org/wiki/Toleranz-Paradoxon&#34;&gt;Paradoxon der Toleranz&lt;/a&gt; sehr elegant auf, indem er das &lt;em&gt;Framing&lt;/em&gt; zurechtrückt, und das Konzept &amp;ldquo;Toleranz&amp;rdquo; schärfer definiert.&lt;/p&gt;
&lt;p&gt;Da ich es als sehr klug empfinde, aber keine Möglichkeit gefunden habe, den Autor zu kontaktieren&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, um ihn zu fragen, ob ich es liebevoll ins Deutsche übersetzen darf, möchte ich hier auf seinen frei zugänglichen Post in der (sehr lesbaren) Auto-Übersetzung hinweisen.&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;→ &lt;a href=&#34;https://freedium--mirror-cfd.translate.goog/https://medium.com/extra-extra/tolerance-is-not-a-moral-precept-1af7007d6376?_x_tr_sl=auto&amp;amp;_x_tr_tl=de&amp;amp;_x_tr_hl=de&#34;&gt;Toleranz ist kein moralisches Gebot | von Yonatan Zunger&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Toleranz ist kein absoluter moralischer Wert, sondern ein Friedensvertrag.&lt;/strong&gt; Toleranz ist eine soziale Norm, weil sie es verschiedenen Menschen ermöglicht, friedlich zusammenzuleben. Sie bedeutet, dass wir akzeptieren, dass Menschen anders sein können als wir – in ihren Bräuchen, ihrem Verhalten, ihrer Kleidung, ihrem Sexualleben – und dass uns das nichts angeht, solange es unser Leben nicht direkt betrifft. Doch das Modell eines Friedensvertrags unterscheidet sich von einem moralischen Gebot in einem einfachen Punkt: Der Schutz eines Friedensvertrags gilt nur für diejenigen, die bereit sind, sich an seine Bedingungen zu halten. Es ist eine Vereinbarung, in Frieden zu leben, nicht eine Vereinbarung, unabhängig vom Verhalten anderer friedlich zu sein. Ein Friedensvertrag ist kein Selbstmordpakt.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ich wünsche Euch ein gutes neues Jahr. Bleibt tolerant, aber nicht passiv.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#FCKNZS&lt;/code&gt; &lt;code&gt;#FCKAFD&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://medium.com/extra-extra/tolerance-is-not-a-moral-precept-1af7007d6376&#34;&gt;Tolerance is not a moral precept. | by Yonatan Zunger&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://archive.is/20250325114235/https://medium.com/extra-extra/tolerance-is-not-a-moral-precept-1af7007d6376&#34;&gt;archive.is&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://web.archive.org/web/20251112164151/https://medium.com/extra-extra/tolerance-is-not-a-moral-precept-1af7007d6376&#34;&gt;web.archive.org&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;section class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;Ausserhalb LinkedIn, und meinen LI-Account habe ich vor Jahren schon gelöscht. Tja.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;Mit Umweg über den freedium-Mirror des originalen Medium-Posts.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <source:markdown>Das Essay [Tolerance is not a moral precept](https://medium.com/extra-extra/tolerance-is-not-a-moral-precept-1af7007d6376) von Yonatan Zunger ist eines, das ich regelmäßig auf Mastodon teile, denn es ist sehr gut. Yonatan löst das scheinbare [Paradoxon der Toleranz](https://de.wikipedia.org/wiki/Toleranz-Paradoxon) sehr elegant auf, indem er das _Framing_ zurechtrückt, und das Konzept &#34;Toleranz&#34; schärfer definiert.

Da ich es als sehr klug empfinde, aber keine Möglichkeit gefunden habe, den Autor zu kontaktieren[^1], um ihn zu fragen, ob ich es liebevoll ins Deutsche übersetzen darf, möchte ich hier auf seinen frei zugänglichen Post in der (sehr lesbaren) Auto-Übersetzung hinweisen.[^2]

[^1]: Ausserhalb LinkedIn, und meinen LI-Account habe ich vor Jahren schon gelöscht. Tja.
[^2]: Mit Umweg über den freedium-Mirror des originalen Medium-Posts.

→ [Toleranz ist kein moralisches Gebot | von Yonatan Zunger](https://freedium--mirror-cfd.translate.goog/https://medium.com/extra-extra/tolerance-is-not-a-moral-precept-1af7007d6376?_x_tr_sl=auto&amp;_x_tr_tl=de&amp;_x_tr_hl=de):

&gt; **Toleranz ist kein absoluter moralischer Wert, sondern ein Friedensvertrag.** Toleranz ist eine soziale Norm, weil sie es verschiedenen Menschen ermöglicht, friedlich zusammenzuleben. Sie bedeutet, dass wir akzeptieren, dass Menschen anders sein können als wir – in ihren Bräuchen, ihrem Verhalten, ihrer Kleidung, ihrem Sexualleben – und dass uns das nichts angeht, solange es unser Leben nicht direkt betrifft. Doch das Modell eines Friedensvertrags unterscheidet sich von einem moralischen Gebot in einem einfachen Punkt: Der Schutz eines Friedensvertrags gilt nur für diejenigen, die bereit sind, sich an seine Bedingungen zu halten. Es ist eine Vereinbarung, in Frieden zu leben, nicht eine Vereinbarung, unabhängig vom Verhalten anderer friedlich zu sein. Ein Friedensvertrag ist kein Selbstmordpakt.

Ich wünsche Euch ein gutes neues Jahr. Bleibt tolerant, aber nicht passiv.

`#FCKNZS` `#FCKAFD` 

Links:

- [Tolerance is not a moral precept. | by Yonatan Zunger](https://medium.com/extra-extra/tolerance-is-not-a-moral-precept-1af7007d6376)
	- [archive.is](https://archive.is/20250325114235/https://medium.com/extra-extra/tolerance-is-not-a-moral-precept-1af7007d6376)
	- [web.archive.org](https://web.archive.org/web/20251112164151/https://medium.com/extra-extra/tolerance-is-not-a-moral-precept-1af7007d6376)
</source:markdown>
    </item>
    
    <item>
      <title>Cities are organisms</title>
      <link>https://zottmann.org/2025/12/25/cities-are-organisms.html</link>
      <pubDate>Thu, 25 Dec 2025 01:21:33 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2025/12/25/cities-are-organisms.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://www.samholden.jp/p/is-a-city-alive&#34;&gt;An interesting question posed by Sam Holden:&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://www.theguardian.com/books/2025/apr/28/is-a-river-alive-by-robert-macfarlane-review-streams-of-consciousness&#34;&gt;Robert Macfarlane’s Is A River Alive?&lt;/a&gt; asks what would it mean for our understanding of the natural world and our relationship to it if we saw rivers not just as ecosystems containing life, but as truly alive. In beautifully animistic prose, Macfarlane ventures to the frontier of the rights of nature movement, where rivers are treated as subjective entities that think, feel and are endowed with legal protections. The book also inspires a related thought experiment: is a city alive?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;My personal view, one that I held for years, is that a city is an organism – so yes, it is alive. Its citizens are its cells, its genome, its flesh, its live blood. Remove those, and the organism will die and wither. Remove the stones, however, the steel and glass, the asphalt, and you&amp;rsquo;re left with half an organism quickly regrowing the missing parts of its body.&lt;/p&gt;
</description>
      <source:markdown>[An interesting question posed by Sam Holden:](https://www.samholden.jp/p/is-a-city-alive)

&gt; [Robert Macfarlane’s Is A River Alive?](https://www.theguardian.com/books/2025/apr/28/is-a-river-alive-by-robert-macfarlane-review-streams-of-consciousness) asks what would it mean for our understanding of the natural world and our relationship to it if we saw rivers not just as ecosystems containing life, but as truly alive. In beautifully animistic prose, Macfarlane ventures to the frontier of the rights of nature movement, where rivers are treated as subjective entities that think, feel and are endowed with legal protections. The book also inspires a related thought experiment: is a city alive?

My personal view, one that I held for years, is that a city is an organism – so yes, it is alive. Its citizens are its cells, its genome, its flesh, its live blood. Remove those, and the organism will die and wither. Remove the stones, however, the steel and glass, the asphalt, and you&#39;re left with half an organism quickly regrowing the missing parts of its body.
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://zottmann.org/2025/12/07/quoting-jon-worth-from-a.html</link>
      <pubDate>Sun, 07 Dec 2025 23:51:59 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2025/12/07/quoting-jon-worth-from-a.html</guid>
      <description>&lt;p&gt;Quoting &lt;a href=&#34;https://bsky.app/profile/jonworth.eu/post/3m7gdnt775s26&#34;&gt;Jon Worth&lt;/a&gt; from a Bluesky thread:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;After […] Musk&amp;rsquo;s statement to abolish the EU (after the Commission fined X), let&amp;rsquo;s ask ourselves simply:&lt;/p&gt;
&lt;p&gt;HOW ARE EUROPEAN COMMISSIONERS USING X NOW? […]&lt;/p&gt;
&lt;p&gt;If European Digital Sovereignty is to mean anything, it ought to mean NOT posting on a platform owned and run by a fascist intent on undermining the EU!&lt;/p&gt;
&lt;p&gt;And don&amp;rsquo;t give me the bullshit you have to be there for &amp;ldquo;balance&amp;rdquo; or &amp;ldquo;reach&amp;rdquo; - the rules are stacked against you from the outset&lt;/p&gt;
&lt;/blockquote&gt;
</description>
      <source:markdown>Quoting [Jon Worth](https://bsky.app/profile/jonworth.eu/post/3m7gdnt775s26) from a Bluesky thread:

&gt; After \[…] Musk&#39;s statement to abolish the EU (after the Commission fined X), let&#39;s ask ourselves simply:
&gt; 
&gt; HOW ARE EUROPEAN COMMISSIONERS USING X NOW? \[…]
&gt;
&gt; If European Digital Sovereignty is to mean anything, it ought to mean NOT posting on a platform owned and run by a fascist intent on undermining the EU!
&gt; 
&gt; And don&#39;t give me the bullshit you have to be there for &#34;balance&#34; or &#34;reach&#34; - the rules are stacked against you from the outset
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://zottmann.org/2025/12/02/quoting-louis-rosenberg-in-other.html</link>
      <pubDate>Tue, 02 Dec 2025 14:22:34 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2025/12/02/quoting-louis-rosenberg-in-other.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://bigthink.com/the-present/the-rise-of-ai-denialism/&#34;&gt;Quoting Louis Rosenberg&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In other words, we are not watching a bubble expand with blustery vapors. We are watching a planet form from churning magma, and it will solidify into a new framework for society. Denial will only make us unprepared. This is not an AI bubble. This is real.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I agree with several of his points, but I also take the whole post with a grain of salt, mind. The guy is a computer scientist but also the &lt;a href=&#34;https://bigthink.com/people/louis-rosenberg/&#34;&gt;CEO of a AI company&lt;/a&gt; which I suspect colors/ informs his POV and predictions.&lt;/p&gt;
</description>
      <source:markdown>[Quoting Louis Rosenberg](https://bigthink.com/the-present/the-rise-of-ai-denialism/):

&gt; In other words, we are not watching a bubble expand with blustery vapors. We are watching a planet form from churning magma, and it will solidify into a new framework for society. Denial will only make us unprepared. This is not an AI bubble. This is real.

I agree with several of his points, but I also take the whole post with a grain of salt, mind. The guy is a computer scientist but also the [CEO of a AI company](https://bigthink.com/people/louis-rosenberg/) which I suspect colors/ informs his POV and predictions.
</source:markdown>
    </item>
    
    <item>
      <title>November Sale! 25% off of BarCuts and Browser Actions</title>
      <link>https://zottmann.org/2025/11/27/november-sale-off-of-barcuts.html</link>
      <pubDate>Thu, 27 Nov 2025 15:07:10 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2025/11/27/november-sale-off-of-barcuts.html</guid>
      <description>&lt;p&gt;I recently released updates to both my contextual macOS Shortcuts launcher, &lt;a href=&#34;https://actions.work/barcuts&#34;&gt;BarCuts&lt;/a&gt;, and &lt;a href=&#34;https://actions.work/browser-actions&#34;&gt;Browser Actions&lt;/a&gt;, which let you automate Google Chrome, Vivaldi, Microsoft Edge, and other browsers. 🚀&lt;/p&gt;
&lt;p&gt;And since it&amp;rsquo;s November, I&amp;rsquo;ve also decided to join the fray and run a sale for them:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;✨They&amp;rsquo;re 25% off with code &lt;code&gt;FCKNZS2025&lt;/code&gt; until end of the month!✨&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;(People asked me whether &lt;a href=&#34;https://actions.work/actions-for-obsidian&#34;&gt;Actions For Obsidian&lt;/a&gt; will go on sale, but given it&amp;rsquo;s been &lt;a href=&#34;https://docs.actions.work/actions-for-obsidian/faqs/licensing/#pay-what-you-think-is-fair&#34;&gt;&amp;ldquo;Pay What You Think Is Fair&amp;rdquo;&lt;/a&gt; since the very beginning, I feel like it&amp;rsquo;s kind of always been on sale.😉)&lt;/p&gt;
&lt;p&gt;🖖🏼&lt;/p&gt;
</description>
      <source:markdown>I recently released updates to both my contextual macOS Shortcuts launcher, [BarCuts](https://actions.work/barcuts), and [Browser Actions](https://actions.work/browser-actions), which let you automate Google Chrome, Vivaldi, Microsoft Edge, and other browsers. 🚀 

And since it&#39;s November, I&#39;ve also decided to join the fray and run a sale for them:

 **✨They&#39;re 25% off with code `FCKNZS2025` until end of the month!✨**

(People asked me whether [Actions For Obsidian](https://actions.work/actions-for-obsidian) will go on sale, but given it&#39;s been [&#34;Pay What You Think Is Fair&#34;](https://docs.actions.work/actions-for-obsidian/faqs/licensing/#pay-what-you-think-is-fair) since the very beginning, I feel like it&#39;s kind of always been on sale.😉)

🖖🏼
</source:markdown>
    </item>
    
    <item>
      <title>A quick story about how a LLM saved me €100&#43; today</title>
      <link>https://zottmann.org/2025/11/19/a-quick-story-about-how.html</link>
      <pubDate>Wed, 19 Nov 2025 20:18:05 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2025/11/19/a-quick-story-about-how.html</guid>
      <description>&lt;p&gt;I have this setup: XLR mic → ext. USB audio interface → Mac. The name of the audio interface (UMC22) has a trailing space, which macOS gets confused by every other day. It sanitizes the stored name, can’t find the device anymore, removes it, then finds a &amp;ldquo;new&amp;rdquo; device it doesn’t know and re-adds it. The mic works fine, but setting the audio interface as the default for anything doesn’t, because macOS keeps forgetting it.&lt;/p&gt;
&lt;p&gt;This has been going on forever, but it has gotten progressively worse with every major macOS update. (Don&amp;rsquo;t at me; I’ll keep using macOS because I write software for it-this is how I make my money.) Since macOS 26 rolled around, it happens every two days — basically every time I dock the MBP.&lt;/p&gt;
&lt;p&gt;I finally got fed up and started looking into a new USB mic — €100+ for quality products, naturally — which pissed me off because I like my existing mic; it&amp;rsquo;s good, and it works. It&amp;rsquo;s just that shitty interface issue + macOS, damn it.&lt;/p&gt;
&lt;p&gt;In the past, I&amp;rsquo;d tried setting up an aggregate (virtual) audio device that contained the audio interface, but that didn&amp;rsquo;t change anything. The interface would vanish and reappear as a new device, and the aggregate device wouldn&amp;rsquo;t know about it. I had to manually add the new device to the aggregate — no good.&lt;/p&gt;
&lt;p&gt;Anyway. Today I asked Claude to research that problem for me. It did and came back with a lot of technical detail, which boiled down to &lt;em&gt;&amp;ldquo;Carlo, you were right about what&amp;rsquo;s going on.&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;But all my describing the issue, plus the research, gave Claude a metric shit-ton of insight, so I then asked it to write me a Swift CLI tool that would let me add a named device to an existing aggregate device. And it did! When it was almost done, the 5-hour limit window struck, so I handed the prototype script over to Kimi K2 Thinking&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, and it hammered out the rest for me.&lt;/p&gt;
&lt;p&gt;Frens, I was tickled fancy, let me tell you.&lt;/p&gt;
&lt;p&gt;Now I have a script that runs automatically in the background every time the USB device is connected. And since the aggregate doesn&amp;rsquo;t change (it&amp;rsquo;s just updated!), I can use that as the default device.&lt;/p&gt;
&lt;p&gt;Sure, without Claude and K2, I could&amp;rsquo;ve written that script myself, but it probably would have taken several days of my free time (since I have a day job) for research, trial, and error. This way, it took less than an hour to fix this specific issue.&lt;/p&gt;
&lt;p&gt;And I can keep using my old stuff. Win!&lt;/p&gt;
&lt;section class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;It&amp;rsquo;s a monster! A slow, slow monster.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <source:markdown>I have this setup: XLR mic → ext. USB audio interface → Mac. The name of the audio interface (UMC22) has a trailing space, which macOS gets confused by every other day. It sanitizes the stored name, can’t find the device anymore, removes it, then finds a &#34;new&#34; device it doesn’t know and re-adds it. The mic works fine, but setting the audio interface as the default for anything doesn’t, because macOS keeps forgetting it.

This has been going on forever, but it has gotten progressively worse with every major macOS update. (Don&#39;t at me; I’ll keep using macOS because I write software for it-this is how I make my money.) Since macOS 26 rolled around, it happens every two days — basically every time I dock the MBP.

I finally got fed up and started looking into a new USB mic — €100+ for quality products, naturally — which pissed me off because I like my existing mic; it&#39;s good, and it works. It&#39;s just that shitty interface issue + macOS, damn it.

In the past, I&#39;d tried setting up an aggregate (virtual) audio device that contained the audio interface, but that didn&#39;t change anything. The interface would vanish and reappear as a new device, and the aggregate device wouldn&#39;t know about it. I had to manually add the new device to the aggregate — no good.

Anyway. Today I asked Claude to research that problem for me. It did and came back with a lot of technical detail, which boiled down to _&#34;Carlo, you were right about what&#39;s going on.&#34;_

But all my describing the issue, plus the research, gave Claude a metric shit-ton of insight, so I then asked it to write me a Swift CLI tool that would let me add a named device to an existing aggregate device. And it did! When it was almost done, the 5-hour limit window struck, so I handed the prototype script over to Kimi K2 Thinking[^1], and it hammered out the rest for me.

[^1]: It&#39;s a monster! A slow, slow monster.

Frens, I was tickled fancy, let me tell you.

Now I have a script that runs automatically in the background every time the USB device is connected. And since the aggregate doesn&#39;t change (it&#39;s just updated!), I can use that as the default device.

Sure, without Claude and K2, I could&#39;ve written that script myself, but it probably would have taken several days of my free time (since I have a day job) for research, trial, and error. This way, it took less than an hour to fix this specific issue.

And I can keep using my old stuff. Win!
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://zottmann.org/2025/11/16/why-solarpunk-is-already-happening.html</link>
      <pubDate>Sun, 16 Nov 2025 21:16:58 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2025/11/16/why-solarpunk-is-already-happening.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://climatedrift.substack.com/p/why-solarpunk-is-already-happening&#34;&gt;Why Solarpunk is already happening in Africa&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What’s happening across Sub-Saharan Africa right now is the most ambitious infrastructure project in human history, except it’s not being built by governments or utilities or World Bank consortiums. It’s being built by startups selling solar panels to farmers on payment plans. And it’s working.&lt;/p&gt;
&lt;p&gt;Over 30 million solar products sold in 2024. 400,000 new solar installations every month across Africa. 50% market share captured by companies that didn’t exist 15 years ago. Carbon credits subsidizing the cost. IoT chips in every device. 90%+ repayment rates on loans to people earning $2/day.&lt;/p&gt;
&lt;p&gt;And if you understand what’s happening in Africa, you understand the template for how infrastructure will get built everywhere else for the next 50 years.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Really, really cool.&lt;/p&gt;
</description>
      <source:markdown>[Why Solarpunk is already happening in Africa](https://climatedrift.substack.com/p/why-solarpunk-is-already-happening):

&gt; What’s happening across Sub-Saharan Africa right now is the most ambitious infrastructure project in human history, except it’s not being built by governments or utilities or World Bank consortiums. It’s being built by startups selling solar panels to farmers on payment plans. And it’s working.
&gt; 
&gt; Over 30 million solar products sold in 2024. 400,000 new solar installations every month across Africa. 50% market share captured by companies that didn’t exist 15 years ago. Carbon credits subsidizing the cost. IoT chips in every device. 90%+ repayment rates on loans to people earning $2/day.
&gt; 
&gt; And if you understand what’s happening in Africa, you understand the template for how infrastructure will get built everywhere else for the next 50 years.

Really, really cool.
</source:markdown>
    </item>
    
    <item>
      <title>Restarting macOS/iOS Continuity &amp; Handoff</title>
      <link>https://zottmann.org/2025/11/15/restarting-macosios-continuity-handoff.html</link>
      <pubDate>Sat, 15 Nov 2025 13:19:23 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2025/11/15/restarting-macosios-continuity-handoff.html</guid>
      <description>&lt;p&gt;After updating both my Mac and iPhone to the latest OS 26 versions, Continuity/Handoff wasn&amp;rsquo;t working anymore, meaning no more cross-device copy-n-paste etc. Rebooting either device didn&amp;rsquo;t change anything.&lt;/p&gt;
&lt;p&gt;15 mins of searching brought me to &lt;a href=&#34;https://old.reddit.com/r/mac/comments/1fo2976/solution_to_handoff_not_working_between_mac_and/&#34;&gt;a Reddit post by /u/shesabadone&lt;/a&gt; which suggested resetting macOS&#39; &lt;code&gt;useractivityd&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;defaults delete com.apple.coreservices.useractivityd
killall useractivityd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It did the trick!&lt;/p&gt;
</description>
      <source:markdown>After updating both my Mac and iPhone to the latest OS 26 versions, Continuity/Handoff wasn&#39;t working anymore, meaning no more cross-device copy-n-paste etc. Rebooting either device didn&#39;t change anything.

15 mins of searching brought me to [a Reddit post by /u/shesabadone](https://old.reddit.com/r/mac/comments/1fo2976/solution_to_handoff_not_working_between_mac_and/) which suggested resetting macOS&#39; `useractivityd`:

```bash
defaults delete com.apple.coreservices.useractivityd
killall useractivityd
```

It did the trick!
</source:markdown>
    </item>
    
    <item>
      <title>Linearis v2025.11.2</title>
      <link>https://zottmann.org/2025/11/12/linearis-v.html</link>
      <pubDate>Wed, 12 Nov 2025 15:38:25 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2025/11/12/linearis-v.html</guid>
      <description>&lt;p&gt;Yesterday, I released &lt;a href=&#34;https://github.com/czottmann/linearis&#34;&gt;Linearis v2025.11.2&lt;/a&gt;, a new version of my CLI tool&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; that lets you (and your LLM) work with the very good project management and ticketing system, Linear&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. The headliner features for this update are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;support for cycles&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;support for project milestones&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a href=&#34;#fn:4&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;issues now specify their sub-issues and parents&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Shout-out to &lt;a href=&#34;https://github.com/ryanrozich&#34;&gt;@ryanrozich&lt;/a&gt; for the patch that added both cycles and project milestones. 🤘🏼&lt;/p&gt;
&lt;p&gt;Also, I&amp;rsquo;m happy to announce that the tool is now &lt;a href=&#34;https://www.npmjs.com/package/linearis&#34;&gt;a standard npm package&lt;/a&gt;, meaning that it&amp;rsquo;s installable with a simple &lt;code&gt;npm install -g linearis&lt;/code&gt; call. (Up until now, you could only install it directly from the GitHub repo, but that brought some issues with updating.)&lt;/p&gt;
&lt;p&gt;And ICYMI: the previous update, &lt;strong&gt;v2025.11.1, added support for downloading screenshots and documents&lt;/strong&gt; that are embedded in a ticket description or a comment. So now your agent of choice gets to look everything! For example, to read one of my test tickets, it&amp;rsquo;d do this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;❯ linearis issues read ZCO-1569
&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;de732438-8ae2-4f9e-95a9-85a614b04816&amp;#34;&lt;/span&gt;,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;identifier&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ZCO-1569&amp;#34;&lt;/span&gt;,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;File embed testing grounds&amp;#34;&lt;/span&gt;,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;The Earth was small, light blue, and so touchingly alone, our home that must be defended like a holy relic. The Earth was absolutely round. I believe I never knew what the word round meant until I saw Earth from space.\n\nThe dreams of yesterday are the hopes of today and the reality of tomorrow.\n\n![Confident Man and Futuristic Car.png](https://uploads.linear.app/7df2165d-5920-4338-950f-f2b7152a3c02/8a8adc83-0609-4bb7-9c2d-6450bd2e20a5/db4145e5-6499-45cd-b35c-9aa621bfde51?signature=&amp;lt;signature_omitted&amp;gt;)\n\nIt suddenly struck me that that tiny pea, pretty and blue, was the Earth. I put up my thumb and shut one eye, and my thumb blotted out the planet Earth. I didn&amp;#39;t feel like a giant. I felt very, very small.\n\n![i-swear-to-god-connor.gif](https://uploads.linear.app/7df2165d-5920-4338-950f-f2b7152a3c02/571bf0bf-3586-4f92-9427-a88c13d3c7e4/00d4a028-2053-452c-8da4-a55930943868?signature=&amp;lt;signature_omitted&amp;gt;)&amp;#34;&lt;/span&gt;,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;embeds&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;label&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Confident Man and Futuristic Car.png&amp;#34;&lt;/span&gt;,
      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://uploads.linear.app/7df2165d-5920-4338-950f-f2b7152a3c02/8a8adc83-0609-4bb7-9c2d-6450bd2e20a5/db4145e5-6499-45cd-b35c-9aa621bfde51?signature=&amp;lt;signature_omitted&amp;gt;&amp;#34;&lt;/span&gt;,
      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;expiresAt&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-11-12T14:19:59.963Z&amp;#34;&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;,
    &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;label&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;i-swear-to-god-connor.gif&amp;#34;&lt;/span&gt;,
      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://uploads.linear.app/7df2165d-5920-4338-950f-f2b7152a3c02/571bf0bf-3586-4f92-9427-a88c13d3c7e4/00d4a028-2053-452c-8da4-a55930943868?signature=&amp;lt;signature_omitted&amp;gt;&amp;#34;&lt;/span&gt;,
      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;expiresAt&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-11-12T14:19:59.963Z&amp;#34;&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
  &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;state&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0d744059-bc97-4dc2-82d0-5bac53673c26&amp;#34;&lt;/span&gt;,
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Done&amp;#34;&lt;/span&gt;
  &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;assignee&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cef0517b-cd50-48d9-80d6-8ad34007458a&amp;#34;&lt;/span&gt;,
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Carlo Zottmann&amp;#34;&lt;/span&gt;
  &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;team&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;413e044c-d008-4bcb-b48d-1ef45ada89f4&amp;#34;&lt;/span&gt;,
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;key&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ZCO&amp;#34;&lt;/span&gt;,
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ZCo&amp;#34;&lt;/span&gt;
  &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;project&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;904984cc-eb35-4e4d-9caf-aa21f9f7daa7&amp;#34;&lt;/span&gt;,
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Linearis&amp;#34;&lt;/span&gt;
  &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cycle&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bbde6b5b-c5fe-4319-9bbc-9081746e6660&amp;#34;&lt;/span&gt;,
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: null,
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;number&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;110&lt;/span&gt;
  &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;projectMilestone&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;38cb29af-af9e-493c-b935-205b31396a4b&amp;#34;&lt;/span&gt;,
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025.11.1&amp;#34;&lt;/span&gt;,
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;targetDate&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-11-06&amp;#34;&lt;/span&gt;
  &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;priority&amp;#34;&lt;/span&gt;: 0,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;labels&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt;,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;parentIssue&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;d2d0ad4d-8ad6-4e6d-8ab0-cb9b748d21e8&amp;#34;&lt;/span&gt;,
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;identifier&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ZCO-1441&amp;#34;&lt;/span&gt;,
    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Implement Linear API File Download Capabilities&amp;#34;&lt;/span&gt;
  &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;subIssues&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt;,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;comments&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt;,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;createdAt&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-11-06T16:09:46.447Z&amp;#34;&lt;/span&gt;,
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;updatedAt&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-11-09T18:45:35.964Z&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What&amp;rsquo;s great about that is that e.g. Claude is clever enough to understand that result without any further explanations. For me, that means that I had to teach Claude four things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;My ticket IDs sport the prefix &amp;ldquo;ZCO-&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;linearis&lt;/code&gt; to work with tickets.&lt;/li&gt;
&lt;li&gt;Before doing that, run &lt;code&gt;linearis usage&lt;/code&gt; once to learn how to use it.&lt;/li&gt;
&lt;li&gt;If you come across embeds, fetch and read them as well.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And that&amp;rsquo;s it. The output is structured enough, and Claude knows how to extract what it needs and how to look up anything else related to that ticket using &lt;code&gt;linearis&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;No MCP server eating 16k tokens at rest, just a CLI tool. (I recommend that approach in general.)&lt;/p&gt;
&lt;section class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://zottmann.org/2025/09/03/linearis-my-linear-cli-built.html&#34;&gt;Earlier announcement blog post here.&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://linear.app/&#34;&gt;Linear – Plan and build products&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://linear.app/docs/use-cycles&#34;&gt;Cycles – Linear Docs&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:4&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://linear.app/docs/project-milestones&#34;&gt;Project milestones – Linear Docs&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:4&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      <source:markdown>Yesterday, I released [Linearis v2025.11.2](https://github.com/czottmann/linearis), a new version of my CLI tool[^announcement] that lets you (and your LLM) work with the very good project management and ticketing system, Linear[^linear]. The headliner features for this update are:

- support for cycles[^cycles]
- support for project milestones[^milestones]
- issues now specify their sub-issues and parents

[^linear]: [Linear – Plan and build products](https://linear.app/)
[^announcement]: [Earlier announcement blog post here.](/2025/09/03/linearis-my-linear-cli-built.html)
[^cycles]: [Cycles – Linear Docs](https://linear.app/docs/use-cycles)
[^milestones]: [Project milestones – Linear Docs](https://linear.app/docs/project-milestones)

Shout-out to [@ryanrozich](https://github.com/ryanrozich) for the patch that added both cycles and project milestones. 🤘🏼

Also, I&#39;m happy to announce that the tool is now [a standard npm package](https://www.npmjs.com/package/linearis), meaning that it&#39;s installable with a simple `npm install -g linearis` call. (Up until now, you could only install it directly from the GitHub repo, but that brought some issues with updating.)

And ICYMI: the previous update, **v2025.11.1, added support for downloading screenshots and documents** that are embedded in a ticket description or a comment. So now your agent of choice gets to look everything! For example, to read one of my test tickets, it&#39;d do this:

```bash
❯ linearis issues read ZCO-1569
{
  &#34;id&#34;: &#34;de732438-8ae2-4f9e-95a9-85a614b04816&#34;,
  &#34;identifier&#34;: &#34;ZCO-1569&#34;,
  &#34;title&#34;: &#34;File embed testing grounds&#34;,
  &#34;description&#34;: &#34;The Earth was small, light blue, and so touchingly alone, our home that must be defended like a holy relic. The Earth was absolutely round. I believe I never knew what the word round meant until I saw Earth from space.\n\nThe dreams of yesterday are the hopes of today and the reality of tomorrow.\n\n![Confident Man and Futuristic Car.png](https://uploads.linear.app/7df2165d-5920-4338-950f-f2b7152a3c02/8a8adc83-0609-4bb7-9c2d-6450bd2e20a5/db4145e5-6499-45cd-b35c-9aa621bfde51?signature=&lt;signature_omitted&gt;)\n\nIt suddenly struck me that that tiny pea, pretty and blue, was the Earth. I put up my thumb and shut one eye, and my thumb blotted out the planet Earth. I didn&#39;t feel like a giant. I felt very, very small.\n\n![i-swear-to-god-connor.gif](https://uploads.linear.app/7df2165d-5920-4338-950f-f2b7152a3c02/571bf0bf-3586-4f92-9427-a88c13d3c7e4/00d4a028-2053-452c-8da4-a55930943868?signature=&lt;signature_omitted&gt;)&#34;,
  &#34;embeds&#34;: [
    {
      &#34;label&#34;: &#34;Confident Man and Futuristic Car.png&#34;,
      &#34;url&#34;: &#34;https://uploads.linear.app/7df2165d-5920-4338-950f-f2b7152a3c02/8a8adc83-0609-4bb7-9c2d-6450bd2e20a5/db4145e5-6499-45cd-b35c-9aa621bfde51?signature=&lt;signature_omitted&gt;&#34;,
      &#34;expiresAt&#34;: &#34;2025-11-12T14:19:59.963Z&#34;
    },
    {
      &#34;label&#34;: &#34;i-swear-to-god-connor.gif&#34;,
      &#34;url&#34;: &#34;https://uploads.linear.app/7df2165d-5920-4338-950f-f2b7152a3c02/571bf0bf-3586-4f92-9427-a88c13d3c7e4/00d4a028-2053-452c-8da4-a55930943868?signature=&lt;signature_omitted&gt;&#34;,
      &#34;expiresAt&#34;: &#34;2025-11-12T14:19:59.963Z&#34;
    }
  ],
  &#34;state&#34;: {
    &#34;id&#34;: &#34;0d744059-bc97-4dc2-82d0-5bac53673c26&#34;,
    &#34;name&#34;: &#34;Done&#34;
  },
  &#34;assignee&#34;: {
    &#34;id&#34;: &#34;cef0517b-cd50-48d9-80d6-8ad34007458a&#34;,
    &#34;name&#34;: &#34;Carlo Zottmann&#34;
  },
  &#34;team&#34;: {
    &#34;id&#34;: &#34;413e044c-d008-4bcb-b48d-1ef45ada89f4&#34;,
    &#34;key&#34;: &#34;ZCO&#34;,
    &#34;name&#34;: &#34;ZCo&#34;
  },
  &#34;project&#34;: {
    &#34;id&#34;: &#34;904984cc-eb35-4e4d-9caf-aa21f9f7daa7&#34;,
    &#34;name&#34;: &#34;Linearis&#34;
  },
  &#34;cycle&#34;: {
    &#34;id&#34;: &#34;bbde6b5b-c5fe-4319-9bbc-9081746e6660&#34;,
    &#34;name&#34;: null,
    &#34;number&#34;: 110
  },
  &#34;projectMilestone&#34;: {
    &#34;id&#34;: &#34;38cb29af-af9e-493c-b935-205b31396a4b&#34;,
    &#34;name&#34;: &#34;2025.11.1&#34;,
    &#34;targetDate&#34;: &#34;2025-11-06&#34;
  },
  &#34;priority&#34;: 0,
  &#34;labels&#34;: [],
  &#34;parentIssue&#34;: {
    &#34;id&#34;: &#34;d2d0ad4d-8ad6-4e6d-8ab0-cb9b748d21e8&#34;,
    &#34;identifier&#34;: &#34;ZCO-1441&#34;,
    &#34;title&#34;: &#34;Implement Linear API File Download Capabilities&#34;
  },
  &#34;subIssues&#34;: [],
  &#34;comments&#34;: [],
  &#34;createdAt&#34;: &#34;2025-11-06T16:09:46.447Z&#34;,
  &#34;updatedAt&#34;: &#34;2025-11-09T18:45:35.964Z&#34;
}
```

What&#39;s great about that is that e.g. Claude is clever enough to understand that result without any further explanations. For me, that means that I had to teach Claude four things:

1. My ticket IDs sport the prefix &#34;ZCO-&#34;.
2. Use `linearis` to work with tickets.
3. Before doing that, run `linearis usage` once to learn how to use it.
4. If you come across embeds, fetch and read them as well.

And that&#39;s it. The output is structured enough, and Claude knows how to extract what it needs and how to look up anything else related to that ticket using `linearis`!

No MCP server eating 16k tokens at rest, just a CLI tool. (I recommend that approach in general.)

</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://zottmann.org/2025/11/07/hn-user-yanhangyhy-on-a.html</link>
      <pubDate>Fri, 07 Nov 2025 14:19:26 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2025/11/07/hn-user-yanhangyhy-on-a.html</guid>
      <description>&lt;p&gt;Quoting &lt;a href=&#34;https://news.ycombinator.com/user?id=yanhangyhy&#34;&gt;HN user yanhangyhy&lt;/a&gt;&amp;rsquo;s &lt;a href=&#34;https://news.ycombinator.com/item?id=45843056&#34;&gt;comment about Kimi K2 Thinking&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[…] China&amp;rsquo;s open-source strategy has many significant effects—not only because it aligns with the spirit of open source. For domestic Chinese companies, it also prevents startups from making reckless investments to develop mediocre models. Instead, everyone is pushed to start from a relatively high baseline. Of course, many small companies in the U.S., Japan, and Europe are also building on Qwen. Kimi is similar: before DeepSeek and others emerged, their model quality was pretty bad. Once the open-source strategy was set, these companies had no choice but to adjust their product lines and development approaches to improve their models.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Moreover, the ultimate competition between models will eventually become a competition over energy.&lt;/strong&gt; China’s open-source models have major advantages in energy consumption, and China itself has a huge advantage in energy resources. They may not necessarily outperform the U.S., but they probably won’t fall too far behind either.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(Emphasis mine.)&lt;/p&gt;
</description>
      <source:markdown>Quoting [HN user yanhangyhy](https://news.ycombinator.com/user?id=yanhangyhy)&#39;s [comment about Kimi K2 Thinking](https://news.ycombinator.com/item?id=45843056):

&gt; […] China&#39;s open-source strategy has many significant effects—not only because it aligns with the spirit of open source. For domestic Chinese companies, it also prevents startups from making reckless investments to develop mediocre models. Instead, everyone is pushed to start from a relatively high baseline. Of course, many small companies in the U.S., Japan, and Europe are also building on Qwen. Kimi is similar: before DeepSeek and others emerged, their model quality was pretty bad. Once the open-source strategy was set, these companies had no choice but to adjust their product lines and development approaches to improve their models.
&gt;
&gt; **Moreover, the ultimate competition between models will eventually become a competition over energy.** China’s open-source models have major advantages in energy consumption, and China itself has a huge advantage in energy resources. They may not necessarily outperform the U.S., but they probably won’t fall too far behind either.

(Emphasis mine.)
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://zottmann.org/2025/11/05/wild-wild-space-hbo-original.html</link>
      <pubDate>Wed, 05 Nov 2025 19:15:00 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2025/11/05/wild-wild-space-hbo-original.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=Q50dAiKB4lI&#34;&gt;Wild Wild Space&lt;/a&gt;: &lt;em&gt;&amp;ldquo;HBO Original Documentary that chronicles the fierce competition to tackle humanity’s next great frontier and the dark side of capitalism’s insatiable appetite for profit&amp;rdquo;&lt;/em&gt;&lt;/p&gt;

&lt;style&gt;
:root {
   
  --yt-aspect-ratio-16-9: 56.25%;

   
  --yt-bg-black: #000;
  --yt-play-button-bg: rgba(255, 0, 0, 0.8);
  --yt-play-button-bg-hover: rgba(255, 0, 0, 1);
  --yt-play-button-icon: #fff;
  --yt-overlay-bg: rgba(0, 0, 0, 0.85);
  --yt-overlay-text: #fff;
  --yt-focus-outline: #e53935;

   
  --yt-play-button-width: 68px;
  --yt-play-button-height: 48px;
  --yt-play-button-border-radius: 12px;

   
  --yt-focus-outline-width: 3px;
  --yt-focus-outline-offset: 4px;

   
  --yt-transition-duration: 0.2s;
  --yt-transition-timing: ease-in-out;
}

.video-wrapper {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  padding-bottom: var(--yt-aspect-ratio-16-9);
  margin: 0;
  height: 0;
  max-width: 100%;
  overflow: hidden;
  background-color: var(--yt-bg-black);
  cursor: pointer;
  outline: none;
  border: none;
  color: inherit;
  font: inherit;
  text-align: inherit;
  appearance: none;
  -webkit-appearance: none;
}

.video-wrapper[data-is-loaded=&#34;true&#34;] {
  cursor: auto;
}

.video-wrapper:focus-visible {
  outline: var(--yt-focus-outline-width) solid var(--yt-focus-outline);
  outline-offset: var(--yt-focus-outline-offset);
}

.video-thumbnail {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  margin: auto;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  object-position: center;
  background-color: var(--yt-bg-black);
  z-index: 1;
  transition: opacity var(--yt-transition-duration) var(--yt-transition-timing);
}

.video-wrapper:hover .video-thumbnail {
  opacity: 0.9;
}

.video-overlay {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  background: var(--yt-overlay-bg);
  color: var(--yt-overlay-text);
  text-align: center;
  font-size: 0.85em;
  padding: 0.5em 1em;
  z-index: 2;
  pointer-events: auto;
}

.video-play-button {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: var(--yt-play-button-width);
  height: var(--yt-play-button-height);
  background-color: var(--yt-play-button-bg);
  border-radius: var(--yt-play-button-border-radius);
  z-index: 3;
  pointer-events: none;
  transition: all var(--yt-transition-duration) var(--yt-transition-timing);
}

.video-wrapper:hover .video-play-button {
  background-color: var(--yt-play-button-bg-hover);
  transform: translate(-50%, -50%) scale(1.1);
}

.video-wrapper:active .video-play-button {
  transform: translate(-50%, -50%) scale(1.05);
}

.video-play-button::before {
  content: &#39;&#39;;
  position: absolute;
  left: 26px;
  top: 14px;
  width: 0;
  height: 0;
  border-left: 18px solid var(--yt-play-button-icon);
  border-top: 12px solid transparent;
  border-bottom: 12px solid transparent;
}

.video-wrapper iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: none;
  z-index: 4;
}

.video-noscript {
  display: block;
  max-width: 100%;
  background-color: var(--yt-bg-black);
  color: inherit;
  text-align: center;
  padding: 0;
  margin-top: 0.5em;
}

.video-noscript img {
  display: block;
  width: 100%;
  height: auto;
}

.video-noscript a {
  display: inline-block;
  padding: 0.75em 1em;
  color: inherit;
  text-decoration: underline;
}

.yt-text-link {
  display: none;
}
&lt;/style&gt;

&lt;script&gt;
(function(){
  &#39;use strict&#39;;

  
  const CONSTANTS = {
    VIDEO_ID_LENGTH: 11,
    MAX_INPUT_LENGTH: 2048,
    VIDEO_ID_PATTERN: /^[a-zA-Z0-9_-]{11}$/,
    PLAYLIST_PREFIXES: /^(PL|OL|UU|LL|FL)/i,
    ALLOWED_URL_CHARS: /^[a-zA-Z0-9_\-?=&amp;#:/.]+$/,
    TIME_FORMATS: {
      HOURS: 3600,
      MINUTES: 60,
      SECONDS: 1
    },
    YOUTUBE_NOCOOKIE_DOMAIN: &#39;https://www.youtube-nocookie.com&#39;,
    YOUTUBE_IMG_DOMAIN: &#39;https://img.youtube.com&#39;,
    EMBED_PARAMS: &#39;autoplay=1&amp;mute=1&#39;
  };

  

  function validateInput(input, maxLength = CONSTANTS.MAX_INPUT_LENGTH) {
    if (!input || typeof input !== &#39;string&#39;) return &#39;&#39;;

    const trimmed = input.trim();

    if (trimmed.length &gt; maxLength) {
      console.warn(`Input exceeds maximum length of ${maxLength}`);
      return &#39;&#39;;
    }

    
    if (!CONSTANTS.ALLOWED_URL_CHARS.test(trimmed)) {
      console.warn(&#39;Input contains invalid characters&#39;);
      return &#39;&#39;;
    }

    return trimmed;
  }

  

  function parseStartValue(value) {
    if (!value) return 0;

    
    if (/^\d+$/.test(value)) {
      const parsed = parseInt(value, 10);
      return isNaN(parsed) ? 0 : Math.max(0, parsed);
    }

    
    let total = 0;
    let matched = false;
    const regex = /(\d+)(h|m|s)/gi;
    let match;

    while ((match = regex.exec(value)) !== null) {
      matched = true;
      const num = parseInt(match[1], 10);
      if (isNaN(num)) continue;

      const unit = match[2].toLowerCase();
      if (unit === &#39;h&#39;) total += num * CONSTANTS.TIME_FORMATS.HOURS;
      else if (unit === &#39;m&#39;) total += num * CONSTANTS.TIME_FORMATS.MINUTES;
      else if (unit === &#39;s&#39;) total += num * CONSTANTS.TIME_FORMATS.SECONDS;
    }

    return matched ? Math.max(0, total) : 0;
  }

  

  function extractStartTime(queryString) {
    if (!queryString) return 0;

    try {
      const cleanQuery = queryString.startsWith(&#39;?&#39;) || queryString.startsWith(&#39;#&#39;)
        ? queryString.slice(1)
        : queryString;

      const params = new URLSearchParams(cleanQuery);

      
      return parseStartValue(params.get(&#39;start&#39;)) ||
             parseStartValue(params.get(&#39;t&#39;)) ||
             parseStartValue(params.get(&#39;time_continue&#39;)) ||
             0;
    } catch (e) {
      console.warn(&#39;Failed to parse query parameters:&#39;, e);
      return 0;
    }
  }

  

  function parseId(raw) {
    const validated = validateInput(raw);
    if (!validated) {
      return { type: &#39;unknown&#39;, id: &#39;&#39;, start: 0 };
    }

    let startSeconds = 0;
    let normalized = validated;

    
    const queryIndex = normalized.indexOf(&#39;?&#39;);
    if (queryIndex !== -1) {
      startSeconds = extractStartTime(normalized.slice(queryIndex + 1));
      normalized = normalized.slice(0, queryIndex);
    }

    
    const hashIndex = normalized.indexOf(&#39;#&#39;);
    if (hashIndex !== -1) {
      startSeconds = startSeconds || extractStartTime(normalized.slice(hashIndex + 1));
      normalized = normalized.slice(0, hashIndex);
    }

    
    if (CONSTANTS.PLAYLIST_PREFIXES.test(normalized)) {
      return { type: &#39;playlist&#39;, id: normalized, start: startSeconds };
    }

    
    if (CONSTANTS.VIDEO_ID_PATTERN.test(normalized)) {
      return { type: &#39;video&#39;, id: normalized, start: startSeconds };
    }

    
    const listMatch = normalized.match(/list=([^&amp;]+)/);
    if (listMatch &amp;&amp; listMatch[1]) {
      return { type: &#39;playlist&#39;, id: listMatch[1], start: startSeconds };
    }

    console.warn(&#39;Unable to parse video/playlist ID:&#39;, raw);
    return { type: &#39;unknown&#39;, id: &#39;&#39;, start: startSeconds };
  }

  

  function validateThumbnailUrl(thumbUrl) {
    if (!thumbUrl) return &#39;&#39;;

    let cleaned = thumbUrl.trim();

    
    const markdownMatch = cleaned.match(/^\[[^\]]*\]\(([^)]+)\)$/);
    if (markdownMatch) {
      cleaned = markdownMatch[1].trim();
    }

    
    try {
      const url = new URL(cleaned, document.baseURI);

      
      if (url.protocol !== &#39;http:&#39; &amp;&amp; url.protocol !== &#39;https:&#39;) {
        console.warn(&#39;Thumbnail URL must use http or https protocol&#39;);
        return &#39;&#39;;
      }

      return url.href;
    } catch (e) {
      console.warn(&#39;Invalid thumbnail URL:&#39;, e);
      return &#39;&#39;;
    }
  }

  

  function getPlaylistPlaceholder() {
    const svg = `&lt;svg xmlns=&#39;http://www.w3.org/2000/svg&#39; viewBox=&#39;0 0 1280 720&#39;&gt;` +
                `&lt;rect width=&#39;100%&#39; height=&#39;100%&#39; fill=&#39;#000&#39;/&gt;` +
                `&lt;rect x=&#39;220&#39; y=&#39;260&#39; width=&#39;840&#39; height=&#39;60&#39; fill=&#39;#fff&#39;/&gt;` +
                `&lt;rect x=&#39;220&#39; y=&#39;340&#39; width=&#39;660&#39; height=&#39;60&#39; fill=&#39;#fff&#39;/&gt;` +
                `&lt;/svg&gt;`;
    return &#39;data:image/svg+xml;charset=utf-8,&#39; + encodeURIComponent(svg);
  }

  /**
   * Sets up thumbnail for video wrapper
   * @param {HTMLImageElement} thumbnail - Thumbnail element
   * @param {Object} kind - Parsed video/playlist info
   * @param {string} customThumb - Custom thumbnail URL
   */
  function setupThumbnail(thumbnail, kind, customThumb) {
    if (!thumbnail) return;

    if (customThumb) {
      // Use custom thumbnail
      thumbnail.onerror = null;
      thumbnail.src = customThumb;
    } else if (kind.type === &#39;playlist&#39;) {
      // Use playlist placeholder
      thumbnail.src = getPlaylistPlaceholder();
    } else if (kind.type === &#39;video&#39; &amp;&amp; kind.id) {
      // Use YouTube thumbnail with fallback
      thumbnail.src = `${CONSTANTS.YOUTUBE_IMG_DOMAIN}/vi/${kind.id}/maxresdefault.jpg`;
      thumbnail.onerror = () =&gt; {
        thumbnail.onerror = null;
        thumbnail.src = `${CONSTANTS.YOUTUBE_IMG_DOMAIN}/vi/${kind.id}/0.jpg`;
      };
    }
    // For unknown type, keep the default placeholder
  }

  /**
   * Creates embed URL for video or playlist
   * @param {Object} kind - Parsed video/playlist info
   * @returns {string} Embed URL or empty string if invalid
   */
  function createEmbedUrl(kind) {
    if (kind.type === &#39;playlist&#39; &amp;&amp; kind.id) {
      return `${CONSTANTS.YOUTUBE_NOCOOKIE_DOMAIN}/embed/videoseries?list=${encodeURIComponent(kind.id)}&amp;${CONSTANTS.EMBED_PARAMS}`;
    } else if (kind.type === &#39;video&#39; &amp;&amp; kind.id) {
      let url = `${CONSTANTS.YOUTUBE_NOCOOKIE_DOMAIN}/embed/${encodeURIComponent(kind.id)}?${CONSTANTS.EMBED_PARAMS}`;
      if (kind.start &gt; 0) {
        url += `&amp;start=${Math.floor(kind.start)}`;
      }
      return url;
    }
    return &#39;&#39;;
  }

  /**
   * Creates and loads iframe embed
   * @param {HTMLElement} wrapper - Video wrapper element
   * @param {Object} kind - Parsed video/playlist info
   * @param {string} title - Video title
   */
  function loadEmbed(wrapper, kind, title) {
    if (wrapper.dataset.isLoaded === &#39;true&#39;) return;

    const embedUrl = createEmbedUrl(kind);
    if (!embedUrl) {
      console.error(&#39;Unable to create embed URL - invalid video/playlist ID&#39;);
      // Show error message to user
      wrapper.innerHTML = &#39;&lt;div style=&#34;padding: 2em; text-align: center; color: #fff;&#34;&gt;Unable to load video. Please check the video ID.&lt;/div&gt;&#39;;
      return;
    }

    // Create iframe
    const iframe = document.createElement(&#39;iframe&#39;);
    iframe.setAttribute(&#39;allowfullscreen&#39;, &#39;&#39;);
    iframe.setAttribute(&#39;loading&#39;, &#39;lazy&#39;);
    iframe.setAttribute(&#39;allow&#39;, &#39;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&#39;);
    iframe.setAttribute(&#39;title&#39;, title ? `YouTube video player: ${title}` : &#39;YouTube video player&#39;);
    iframe.src = embedUrl;

    
    const replacement = document.createElement(&#39;div&#39;);
    replacement.className = wrapper.className;
    replacement.dataset.listenersAdded = &#39;true&#39;;
    replacement.dataset.isLoaded = &#39;true&#39;;
    replacement.appendChild(iframe);

    wrapper.replaceWith(replacement);
  }

  

  function enhanceEmbeds() {
    const wrappers = document.querySelectorAll(&#39;.video-wrapper&#39;);

    wrappers.forEach(wrapper =&gt; {
      
      if (wrapper.dataset.listenersAdded === &#39;true&#39;) return;
      wrapper.dataset.listenersAdded = &#39;true&#39;;

      
      const rawVideoId = wrapper.dataset.videoId || &#39;&#39;;
      const rawThumbUrl = wrapper.dataset.thumb || &#39;&#39;;
      const videoTitle = wrapper.dataset.videoTitle || &#39;&#39;;

      
      const kind = parseId(rawVideoId);
      const safeThumb = validateThumbnailUrl(rawThumbUrl);

      
      const thumbnail = wrapper.querySelector(&#39;.video-thumbnail&#39;);
      setupThumbnail(thumbnail, kind, safeThumb);

      
      const loadHandler = () =&gt; loadEmbed(wrapper, kind, videoTitle);

      wrapper.addEventListener(&#39;click&#39;, loadHandler);

      const overlay = wrapper.querySelector(&#39;.video-overlay&#39;);
      if (overlay) overlay.addEventListener(&#39;click&#39;, loadHandler);
      if (thumbnail) thumbnail.addEventListener(&#39;click&#39;, loadHandler);

      
      wrapper.addEventListener(&#39;keydown&#39;, (e) =&gt; {
        if (e.key === &#39;Enter&#39; || e.key === &#39; &#39;) {
          e.preventDefault();
          loadHandler();
        }
      });
    });
  }

  
  enhanceEmbeds();
  if (document.readyState === &#39;loading&#39;) {
    document.addEventListener(&#39;DOMContentLoaded&#39;, enhanceEmbeds);
  }
})();
&lt;/script&gt;&lt;p class=&#34;yt-text-link&#34;&gt;
      &lt;a href=&#34;https://www.youtube.com/watch?v=Q50dAiKB4lI&#34;&gt;YouTube Video&lt;/a&gt;
    &lt;/p&gt;&lt;button type=&#34;button&#34;
    class=&#34;video-wrapper&#34;
    aria-label=&#34;Play YouTube video&#34;
    data-video-id=&#34;Q50dAiKB4lI&#34;
    data-thumb=&#34;https://i.ytimg.com/vi_webp/Q50dAiKB4lI/sddefault.webp&#34;
    &gt;&lt;img
      class=&#34;video-thumbnail&#34;
      src=&#34;data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns=%27http://www.w3.org/2000/svg%27%20viewBox=%270%200%201280%20720%27%3E%3Crect%20width=%27100%25%25%27%20height=%27100%25%25%27%20fill=%27%23000%27/%3E%3C/svg%3E&#34;
      alt=&#34;YouTube Thumbnail&#34;
      loading=&#34;lazy&#34;
      decoding=&#34;async&#34;&gt;&lt;div class=&#34;video-play-button&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;&lt;noscript&gt;
    &lt;div class=&#34;video-noscript&#34;&gt;&lt;img src=&#34;https://i.ytimg.com/vi_webp/Q50dAiKB4lI/sddefault.webp&#34;
             alt=&#34;YouTube Thumbnail&#34;&gt;&lt;p&gt;
          &lt;a href=&#34;https://www.youtube.com/watch?v=Q50dAiKB4lI&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;YouTube Video&lt;/a&gt;
        &lt;/p&gt;&lt;/div&gt;
  &lt;/noscript&gt;
&lt;p&gt;A cool documentary to watch if you&amp;rsquo;re interested in the current commercial space race (like I am), showing some key players, and telling the story how it came to be. Based on Ashlee Vance&amp;rsquo;s book &lt;a href=&#34;https://www.harpercollins.com/products/when-the-heavens-went-on-sale-ashlee-vance?variant=41111416995874&#34;&gt;&amp;ldquo;When the Heavens Went on Sale&amp;rdquo;&lt;/a&gt; which I&amp;rsquo;ve yet to read.&lt;/p&gt;
</description>
      <source:markdown>[Wild Wild Space](https://www.youtube.com/watch?v=Q50dAiKB4lI): _&#34;HBO Original Documentary that chronicles the fierce competition to tackle humanity’s next great frontier and the dark side of capitalism’s insatiable appetite for profit&#34;_

{{&lt; yt &#34;Q50dAiKB4lI&#34; &#34;[i.ytimg.com/vi_webp/Q...](https://i.ytimg.com/vi_webp/Q50dAiKB4lI/sddefault.webp)&#34; &gt;}}

A cool documentary to watch if you&#39;re interested in the current commercial space race (like I am), showing some key players, and telling the story how it came to be. Based on Ashlee Vance&#39;s book [&#34;When the Heavens Went on Sale&#34;](https://www.harpercollins.com/products/when-the-heavens-went-on-sale-ashlee-vance?variant=41111416995874) which I&#39;ve yet to read.
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://zottmann.org/2025/11/05/bbc-radio-live-bugzy-malones.html</link>
      <pubDate>Wed, 05 Nov 2025 19:04:12 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2025/11/05/bbc-radio-live-bugzy-malones.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://www.bbc.co.uk/programmes/p0df1893&#34;&gt;BBC Radio 5 Live - Bugzy Malone’s Grandest Game&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A loving look at the GTA series by BBC&amp;rsquo;s Chris Warburton and UK rapper Bugzy Malone. &lt;em&gt;&amp;ldquo;It&amp;rsquo;s the game that shocked and thrilled the world: Grand Theft Auto. Its rise to domination is an incredible story.&amp;quot;&lt;/em&gt; They look at how GTA came to be, how it grew, its controversies, and what it meant to different people. They talked with ex-Rockstar devs, modders, games journalists, players. Seven episodes, nicely produced, ace little treats right before bed time.&lt;/p&gt;
</description>
      <source:markdown>[BBC Radio 5 Live - Bugzy Malone’s Grandest Game](https://www.bbc.co.uk/programmes/p0df1893)

A loving look at the GTA series by BBC&#39;s Chris Warburton and UK rapper Bugzy Malone. _&#34;It&#39;s the game that shocked and thrilled the world: Grand Theft Auto. Its rise to domination is an incredible story.&#34;_ They look at how GTA came to be, how it grew, its controversies, and what it meant to different people. They talked with ex-Rockstar devs, modders, games journalists, players. Seven episodes, nicely produced, ace little treats right before bed time.
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://zottmann.org/2025/11/03/peter-leyden-why-is-the.html</link>
      <pubDate>Mon, 03 Nov 2025 01:22:24 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2025/11/03/peter-leyden-why-is-the.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=w5k72A30kUc&#34;&gt;Peter Leyden: Why 2025 is the single most pivotal year in our lifetime&lt;/a&gt;&lt;/p&gt;

&lt;style&gt;
:root {
   
  --yt-aspect-ratio-16-9: 56.25%;

   
  --yt-bg-black: #000;
  --yt-play-button-bg: rgba(255, 0, 0, 0.8);
  --yt-play-button-bg-hover: rgba(255, 0, 0, 1);
  --yt-play-button-icon: #fff;
  --yt-overlay-bg: rgba(0, 0, 0, 0.85);
  --yt-overlay-text: #fff;
  --yt-focus-outline: #e53935;

   
  --yt-play-button-width: 68px;
  --yt-play-button-height: 48px;
  --yt-play-button-border-radius: 12px;

   
  --yt-focus-outline-width: 3px;
  --yt-focus-outline-offset: 4px;

   
  --yt-transition-duration: 0.2s;
  --yt-transition-timing: ease-in-out;
}

.video-wrapper {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  padding-bottom: var(--yt-aspect-ratio-16-9);
  margin: 0;
  height: 0;
  max-width: 100%;
  overflow: hidden;
  background-color: var(--yt-bg-black);
  cursor: pointer;
  outline: none;
  border: none;
  color: inherit;
  font: inherit;
  text-align: inherit;
  appearance: none;
  -webkit-appearance: none;
}

.video-wrapper[data-is-loaded=&#34;true&#34;] {
  cursor: auto;
}

.video-wrapper:focus-visible {
  outline: var(--yt-focus-outline-width) solid var(--yt-focus-outline);
  outline-offset: var(--yt-focus-outline-offset);
}

.video-thumbnail {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  margin: auto;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  object-position: center;
  background-color: var(--yt-bg-black);
  z-index: 1;
  transition: opacity var(--yt-transition-duration) var(--yt-transition-timing);
}

.video-wrapper:hover .video-thumbnail {
  opacity: 0.9;
}

.video-overlay {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  background: var(--yt-overlay-bg);
  color: var(--yt-overlay-text);
  text-align: center;
  font-size: 0.85em;
  padding: 0.5em 1em;
  z-index: 2;
  pointer-events: auto;
}

.video-play-button {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: var(--yt-play-button-width);
  height: var(--yt-play-button-height);
  background-color: var(--yt-play-button-bg);
  border-radius: var(--yt-play-button-border-radius);
  z-index: 3;
  pointer-events: none;
  transition: all var(--yt-transition-duration) var(--yt-transition-timing);
}

.video-wrapper:hover .video-play-button {
  background-color: var(--yt-play-button-bg-hover);
  transform: translate(-50%, -50%) scale(1.1);
}

.video-wrapper:active .video-play-button {
  transform: translate(-50%, -50%) scale(1.05);
}

.video-play-button::before {
  content: &#39;&#39;;
  position: absolute;
  left: 26px;
  top: 14px;
  width: 0;
  height: 0;
  border-left: 18px solid var(--yt-play-button-icon);
  border-top: 12px solid transparent;
  border-bottom: 12px solid transparent;
}

.video-wrapper iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: none;
  z-index: 4;
}

.video-noscript {
  display: block;
  max-width: 100%;
  background-color: var(--yt-bg-black);
  color: inherit;
  text-align: center;
  padding: 0;
  margin-top: 0.5em;
}

.video-noscript img {
  display: block;
  width: 100%;
  height: auto;
}

.video-noscript a {
  display: inline-block;
  padding: 0.75em 1em;
  color: inherit;
  text-decoration: underline;
}

.yt-text-link {
  display: none;
}
&lt;/style&gt;

&lt;script&gt;
(function(){
  &#39;use strict&#39;;

  
  const CONSTANTS = {
    VIDEO_ID_LENGTH: 11,
    MAX_INPUT_LENGTH: 2048,
    VIDEO_ID_PATTERN: /^[a-zA-Z0-9_-]{11}$/,
    PLAYLIST_PREFIXES: /^(PL|OL|UU|LL|FL)/i,
    ALLOWED_URL_CHARS: /^[a-zA-Z0-9_\-?=&amp;#:/.]+$/,
    TIME_FORMATS: {
      HOURS: 3600,
      MINUTES: 60,
      SECONDS: 1
    },
    YOUTUBE_NOCOOKIE_DOMAIN: &#39;https://www.youtube-nocookie.com&#39;,
    YOUTUBE_IMG_DOMAIN: &#39;https://img.youtube.com&#39;,
    EMBED_PARAMS: &#39;autoplay=1&amp;mute=1&#39;
  };

  

  function validateInput(input, maxLength = CONSTANTS.MAX_INPUT_LENGTH) {
    if (!input || typeof input !== &#39;string&#39;) return &#39;&#39;;

    const trimmed = input.trim();

    if (trimmed.length &gt; maxLength) {
      console.warn(`Input exceeds maximum length of ${maxLength}`);
      return &#39;&#39;;
    }

    
    if (!CONSTANTS.ALLOWED_URL_CHARS.test(trimmed)) {
      console.warn(&#39;Input contains invalid characters&#39;);
      return &#39;&#39;;
    }

    return trimmed;
  }

  

  function parseStartValue(value) {
    if (!value) return 0;

    
    if (/^\d+$/.test(value)) {
      const parsed = parseInt(value, 10);
      return isNaN(parsed) ? 0 : Math.max(0, parsed);
    }

    
    let total = 0;
    let matched = false;
    const regex = /(\d+)(h|m|s)/gi;
    let match;

    while ((match = regex.exec(value)) !== null) {
      matched = true;
      const num = parseInt(match[1], 10);
      if (isNaN(num)) continue;

      const unit = match[2].toLowerCase();
      if (unit === &#39;h&#39;) total += num * CONSTANTS.TIME_FORMATS.HOURS;
      else if (unit === &#39;m&#39;) total += num * CONSTANTS.TIME_FORMATS.MINUTES;
      else if (unit === &#39;s&#39;) total += num * CONSTANTS.TIME_FORMATS.SECONDS;
    }

    return matched ? Math.max(0, total) : 0;
  }

  

  function extractStartTime(queryString) {
    if (!queryString) return 0;

    try {
      const cleanQuery = queryString.startsWith(&#39;?&#39;) || queryString.startsWith(&#39;#&#39;)
        ? queryString.slice(1)
        : queryString;

      const params = new URLSearchParams(cleanQuery);

      
      return parseStartValue(params.get(&#39;start&#39;)) ||
             parseStartValue(params.get(&#39;t&#39;)) ||
             parseStartValue(params.get(&#39;time_continue&#39;)) ||
             0;
    } catch (e) {
      console.warn(&#39;Failed to parse query parameters:&#39;, e);
      return 0;
    }
  }

  

  function parseId(raw) {
    const validated = validateInput(raw);
    if (!validated) {
      return { type: &#39;unknown&#39;, id: &#39;&#39;, start: 0 };
    }

    let startSeconds = 0;
    let normalized = validated;

    
    const queryIndex = normalized.indexOf(&#39;?&#39;);
    if (queryIndex !== -1) {
      startSeconds = extractStartTime(normalized.slice(queryIndex + 1));
      normalized = normalized.slice(0, queryIndex);
    }

    
    const hashIndex = normalized.indexOf(&#39;#&#39;);
    if (hashIndex !== -1) {
      startSeconds = startSeconds || extractStartTime(normalized.slice(hashIndex + 1));
      normalized = normalized.slice(0, hashIndex);
    }

    
    if (CONSTANTS.PLAYLIST_PREFIXES.test(normalized)) {
      return { type: &#39;playlist&#39;, id: normalized, start: startSeconds };
    }

    
    if (CONSTANTS.VIDEO_ID_PATTERN.test(normalized)) {
      return { type: &#39;video&#39;, id: normalized, start: startSeconds };
    }

    
    const listMatch = normalized.match(/list=([^&amp;]+)/);
    if (listMatch &amp;&amp; listMatch[1]) {
      return { type: &#39;playlist&#39;, id: listMatch[1], start: startSeconds };
    }

    console.warn(&#39;Unable to parse video/playlist ID:&#39;, raw);
    return { type: &#39;unknown&#39;, id: &#39;&#39;, start: startSeconds };
  }

  

  function validateThumbnailUrl(thumbUrl) {
    if (!thumbUrl) return &#39;&#39;;

    let cleaned = thumbUrl.trim();

    
    const markdownMatch = cleaned.match(/^\[[^\]]*\]\(([^)]+)\)$/);
    if (markdownMatch) {
      cleaned = markdownMatch[1].trim();
    }

    
    try {
      const url = new URL(cleaned, document.baseURI);

      
      if (url.protocol !== &#39;http:&#39; &amp;&amp; url.protocol !== &#39;https:&#39;) {
        console.warn(&#39;Thumbnail URL must use http or https protocol&#39;);
        return &#39;&#39;;
      }

      return url.href;
    } catch (e) {
      console.warn(&#39;Invalid thumbnail URL:&#39;, e);
      return &#39;&#39;;
    }
  }

  

  function getPlaylistPlaceholder() {
    const svg = `&lt;svg xmlns=&#39;http://www.w3.org/2000/svg&#39; viewBox=&#39;0 0 1280 720&#39;&gt;` +
                `&lt;rect width=&#39;100%&#39; height=&#39;100%&#39; fill=&#39;#000&#39;/&gt;` +
                `&lt;rect x=&#39;220&#39; y=&#39;260&#39; width=&#39;840&#39; height=&#39;60&#39; fill=&#39;#fff&#39;/&gt;` +
                `&lt;rect x=&#39;220&#39; y=&#39;340&#39; width=&#39;660&#39; height=&#39;60&#39; fill=&#39;#fff&#39;/&gt;` +
                `&lt;/svg&gt;`;
    return &#39;data:image/svg+xml;charset=utf-8,&#39; + encodeURIComponent(svg);
  }

  /**
   * Sets up thumbnail for video wrapper
   * @param {HTMLImageElement} thumbnail - Thumbnail element
   * @param {Object} kind - Parsed video/playlist info
   * @param {string} customThumb - Custom thumbnail URL
   */
  function setupThumbnail(thumbnail, kind, customThumb) {
    if (!thumbnail) return;

    if (customThumb) {
      // Use custom thumbnail
      thumbnail.onerror = null;
      thumbnail.src = customThumb;
    } else if (kind.type === &#39;playlist&#39;) {
      // Use playlist placeholder
      thumbnail.src = getPlaylistPlaceholder();
    } else if (kind.type === &#39;video&#39; &amp;&amp; kind.id) {
      // Use YouTube thumbnail with fallback
      thumbnail.src = `${CONSTANTS.YOUTUBE_IMG_DOMAIN}/vi/${kind.id}/maxresdefault.jpg`;
      thumbnail.onerror = () =&gt; {
        thumbnail.onerror = null;
        thumbnail.src = `${CONSTANTS.YOUTUBE_IMG_DOMAIN}/vi/${kind.id}/0.jpg`;
      };
    }
    // For unknown type, keep the default placeholder
  }

  /**
   * Creates embed URL for video or playlist
   * @param {Object} kind - Parsed video/playlist info
   * @returns {string} Embed URL or empty string if invalid
   */
  function createEmbedUrl(kind) {
    if (kind.type === &#39;playlist&#39; &amp;&amp; kind.id) {
      return `${CONSTANTS.YOUTUBE_NOCOOKIE_DOMAIN}/embed/videoseries?list=${encodeURIComponent(kind.id)}&amp;${CONSTANTS.EMBED_PARAMS}`;
    } else if (kind.type === &#39;video&#39; &amp;&amp; kind.id) {
      let url = `${CONSTANTS.YOUTUBE_NOCOOKIE_DOMAIN}/embed/${encodeURIComponent(kind.id)}?${CONSTANTS.EMBED_PARAMS}`;
      if (kind.start &gt; 0) {
        url += `&amp;start=${Math.floor(kind.start)}`;
      }
      return url;
    }
    return &#39;&#39;;
  }

  /**
   * Creates and loads iframe embed
   * @param {HTMLElement} wrapper - Video wrapper element
   * @param {Object} kind - Parsed video/playlist info
   * @param {string} title - Video title
   */
  function loadEmbed(wrapper, kind, title) {
    if (wrapper.dataset.isLoaded === &#39;true&#39;) return;

    const embedUrl = createEmbedUrl(kind);
    if (!embedUrl) {
      console.error(&#39;Unable to create embed URL - invalid video/playlist ID&#39;);
      // Show error message to user
      wrapper.innerHTML = &#39;&lt;div style=&#34;padding: 2em; text-align: center; color: #fff;&#34;&gt;Unable to load video. Please check the video ID.&lt;/div&gt;&#39;;
      return;
    }

    // Create iframe
    const iframe = document.createElement(&#39;iframe&#39;);
    iframe.setAttribute(&#39;allowfullscreen&#39;, &#39;&#39;);
    iframe.setAttribute(&#39;loading&#39;, &#39;lazy&#39;);
    iframe.setAttribute(&#39;allow&#39;, &#39;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&#39;);
    iframe.setAttribute(&#39;title&#39;, title ? `YouTube video player: ${title}` : &#39;YouTube video player&#39;);
    iframe.src = embedUrl;

    
    const replacement = document.createElement(&#39;div&#39;);
    replacement.className = wrapper.className;
    replacement.dataset.listenersAdded = &#39;true&#39;;
    replacement.dataset.isLoaded = &#39;true&#39;;
    replacement.appendChild(iframe);

    wrapper.replaceWith(replacement);
  }

  

  function enhanceEmbeds() {
    const wrappers = document.querySelectorAll(&#39;.video-wrapper&#39;);

    wrappers.forEach(wrapper =&gt; {
      
      if (wrapper.dataset.listenersAdded === &#39;true&#39;) return;
      wrapper.dataset.listenersAdded = &#39;true&#39;;

      
      const rawVideoId = wrapper.dataset.videoId || &#39;&#39;;
      const rawThumbUrl = wrapper.dataset.thumb || &#39;&#39;;
      const videoTitle = wrapper.dataset.videoTitle || &#39;&#39;;

      
      const kind = parseId(rawVideoId);
      const safeThumb = validateThumbnailUrl(rawThumbUrl);

      
      const thumbnail = wrapper.querySelector(&#39;.video-thumbnail&#39;);
      setupThumbnail(thumbnail, kind, safeThumb);

      
      const loadHandler = () =&gt; loadEmbed(wrapper, kind, videoTitle);

      wrapper.addEventListener(&#39;click&#39;, loadHandler);

      const overlay = wrapper.querySelector(&#39;.video-overlay&#39;);
      if (overlay) overlay.addEventListener(&#39;click&#39;, loadHandler);
      if (thumbnail) thumbnail.addEventListener(&#39;click&#39;, loadHandler);

      
      wrapper.addEventListener(&#39;keydown&#39;, (e) =&gt; {
        if (e.key === &#39;Enter&#39; || e.key === &#39; &#39;) {
          e.preventDefault();
          loadHandler();
        }
      });
    });
  }

  
  enhanceEmbeds();
  if (document.readyState === &#39;loading&#39;) {
    document.addEventListener(&#39;DOMContentLoaded&#39;, enhanceEmbeds);
  }
})();
&lt;/script&gt;&lt;p class=&#34;yt-text-link&#34;&gt;
      &lt;a href=&#34;https://www.youtube.com/watch?v=w5k72A30kUc&#34;&gt;YouTube Video&lt;/a&gt;
    &lt;/p&gt;&lt;button type=&#34;button&#34;
    class=&#34;video-wrapper&#34;
    aria-label=&#34;Play YouTube video&#34;
    data-video-id=&#34;w5k72A30kUc&#34;
    data-thumb=&#34;&#34;
    &gt;&lt;img
      class=&#34;video-thumbnail&#34;
      src=&#34;data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns=%27http://www.w3.org/2000/svg%27%20viewBox=%270%200%201280%20720%27%3E%3Crect%20width=%27100%25%25%27%20height=%27100%25%25%27%20fill=%27%23000%27/%3E%3C/svg%3E&#34;
      alt=&#34;YouTube Thumbnail&#34;
      loading=&#34;lazy&#34;
      decoding=&#34;async&#34;&gt;&lt;div class=&#34;video-play-button&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;&lt;noscript&gt;
    &lt;div class=&#34;video-noscript&#34;&gt;&lt;img src=&#34;https://img.youtube.com/vi/w5k72A30kUc/maxresdefault.jpg&#34;
             alt=&#34;YouTube Thumbnail&#34;&gt;&lt;p&gt;
          &lt;a href=&#34;https://www.youtube.com/watch?v=w5k72A30kUc&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;YouTube Video&lt;/a&gt;
        &lt;/p&gt;&lt;/div&gt;
  &lt;/noscript&gt;
&lt;p&gt;That was an interesting 15 min watch. What I liked a lot was that it briefly touched on massive inequality, too, it didn&amp;rsquo;t just ignore it – in fact, I think inequality&amp;rsquo;s very much part of Mr Leyden&amp;rsquo;s prediction:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The economic system […] that has worked for essentially the top 10% for sure and certainly for the top 1% has &lt;em&gt;not&lt;/em&gt; been working for 80%. And it&amp;rsquo;s gotten to the point where they just have had it.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
      <source:markdown>[Peter Leyden: Why 2025 is the single most pivotal year in our lifetime](https://www.youtube.com/watch?v=w5k72A30kUc)

{{&lt; yt &#34;w5k72A30kUc&#34; &gt;}}

That was an interesting 15 min watch. What I liked a lot was that it briefly touched on massive inequality, too, it didn&#39;t just ignore it – in fact, I think inequality&#39;s very much part of Mr Leyden&#39;s prediction:

&gt; The economic system […] that has worked for essentially the top 10% for sure and certainly for the top 1% has *not* been working for 80%. And it&#39;s gotten to the point where they just have had it.
</source:markdown>
    </item>
    
    <item>
      <title>Adding some Vygotsky to my LLM system prompt </title>
      <link>https://zottmann.org/2025/10/25/adding-some-vygotsky-to-my.html</link>
      <pubDate>Sat, 25 Oct 2025 12:52:19 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2025/10/25/adding-some-vygotsky-to-my.html</guid>
      <description>&lt;p&gt;Anders Thoresson came up with a good addition to LLM system prompts (Claude, Codex, GLM et al). I&amp;rsquo;ve been using it for the last few weeks, and &lt;strong&gt;it definitely feels like I get better interactions out of Claude&lt;/strong&gt; when tasking it with research, helping me think things through, or have it explain new concepts to me.&lt;/p&gt;
&lt;p&gt;The prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Meet me where I am (Vygotsky&amp;rsquo;s theory of zone of proximal development): Gauge my understanding from what I write. If unclear, ask one question about my current knowledge level, then proceed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Since &lt;a href=&#34;https://anders.thoresson.se/post/2025/10/a-hundred-year-old-pedagogical-theory-that-elevates-ai-as-a-thinking-partner/&#34;&gt;Anders&#39; post is a good read&lt;/a&gt; which explains the theory and how he arrived at the prompt, I think you should just go there to learn more.&lt;/p&gt;
</description>
      <source:markdown>Anders Thoresson came up with a good addition to LLM system prompts (Claude, Codex, GLM et al). I&#39;ve been using it for the last few weeks, and **it definitely feels like I get better interactions out of Claude** when tasking it with research, helping me think things through, or have it explain new concepts to me.

The prompt:

&gt; Meet me where I am (Vygotsky&#39;s theory of zone of proximal development): Gauge my understanding from what I write. If unclear, ask one question about my current knowledge level, then proceed.

Since [Anders&#39; post is a good read](https://anders.thoresson.se/post/2025/10/a-hundred-year-old-pedagogical-theory-that-elevates-ai-as-a-thinking-partner/) which explains the theory and how he arrived at the prompt, I think you should just go there to learn more.
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://zottmann.org/2025/10/12/quoting-craig-mod-once-more.html</link>
      <pubDate>Sun, 12 Oct 2025 19:34:16 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2025/10/12/quoting-craig-mod-once-more.html</guid>
      <description>&lt;p&gt;Quoting &lt;a href=&#34;https://craigmod.com/&#34;&gt;Craig Mod&lt;/a&gt; once more:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Everyone was uniquely nuts. Each human contained their own special crazy. This was humanity&amp;rsquo;s gift to the universe. Humans were the &amp;ldquo;sensory organs&amp;rdquo; of the cosmos. The walker liked this framing — the purpose of humans was to meta-cognitize the universe itself. So to be, to observe, was to fulfill. If you thought about it like that, things were a lot less pressing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Another excerpt from his ongoing (and lovely) pop-up &lt;a href=&#34;https://craigmod.com/newsletters/&#34;&gt;newsletter&lt;/a&gt;, &lt;strong&gt;Between Two Mountains&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;BTW, I think the origin of that framing is &lt;a href=&#34;https://en.wikiquote.org/wiki/Carl_Sagan#The_Shores_of_the_Cosmic_Ocean_%5BEpisode_1%5D&#34;&gt;Carl Sagan&lt;/a&gt;: &lt;em&gt;&amp;ldquo;We are a way for the universe to know itself&amp;rdquo;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I am also reminded of a video short by Jason Silva that I watched about ten years ago (and which lived rent-free in my mind ever since for some reason), &lt;a href=&#34;https://www.youtube.com/watch?v=8D3NCSVIWUQ&#34;&gt;&amp;ldquo;Humans are the Sex Organs of Technology&amp;rdquo;&lt;/a&gt;. Different topic, sure, yet equally wild.&lt;/p&gt;
&lt;p&gt;The ideas of humanity as a witness, or a parent/ midwife for &lt;em&gt;something else&lt;/em&gt; to emerge are kind of mindbending.&lt;/p&gt;
</description>
      <source:markdown>Quoting [Craig Mod](https://craigmod.com/) once more:

&gt; Everyone was uniquely nuts. Each human contained their own special crazy. This was humanity&#39;s gift to the universe. Humans were the &#34;sensory organs&#34; of the cosmos. The walker liked this framing — the purpose of humans was to meta-cognitize the universe itself. So to be, to observe, was to fulfill. If you thought about it like that, things were a lot less pressing.  

Another excerpt from his ongoing (and lovely) pop-up [newsletter](https://craigmod.com/newsletters/), **Between Two Mountains**.

BTW, I think the origin of that framing is [Carl Sagan](https://en.wikiquote.org/wiki/Carl_Sagan#The_Shores_of_the_Cosmic_Ocean_[Episode_1]): _&#34;We are a way for the universe to know itself&#34;_.

I am also reminded of a video short by Jason Silva that I watched about ten years ago (and which lived rent-free in my mind ever since for some reason), [&#34;Humans are the Sex Organs of Technology&#34;](https://www.youtube.com/watch?v=8D3NCSVIWUQ). Different topic, sure, yet equally wild.

The ideas of humanity as a witness, or a parent/ midwife for _something else_ to emerge are kind of mindbending.
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://zottmann.org/2025/10/05/ein-wirklich-interessantes-interview-mit.html</link>
      <pubDate>Sun, 05 Oct 2025 19:56:58 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2025/10/05/ein-wirklich-interessantes-interview-mit.html</guid>
      <description>&lt;p&gt;Ein wirklich interessantes Interview mit dem Autoren Thomas Chatterton Williams beim ZEITmagazin: &lt;a href=&#34;https://archive.is/newest/https://www.zeit.de/zeit-magazin/2025/41/thomas-chatterton-williams-wokeness-politische-rechte-polarisierung/komplettansicht#selection-1283.62-1283.94&#34;&gt;&amp;ldquo;Ein Klima, in dem viele lieber schwiegen. Das machte Woke so wirksam&amp;rdquo;&lt;/a&gt;. Ich finde es in Gänze sehr lesenswert, und hätte die Hälfte des gesamten Artikels zitieren können, daher hier nur ein kurzer Auszug:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ZEITmagazin:&lt;/strong&gt; Was war das Gute an Wokeness?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Williams:&lt;/strong&gt; Wokeness benannte vieles richtig. Ein Beispiel ist MeToo: Als heterosexueller Mann hatte ich nie darüber nachgedacht, mit welchen subtilen Hürden Frauen am Arbeitsplatz konfrontiert sind, etwa beim Netzwerken oder Bewerben. Wokeness machte solche Unsichtbarkeiten sichtbar. […] Problematisch wurde Wokeness dort, wo sie über das Ziel hinausschoss: Wenn Unschuldige durch bloße Anschuldigungen ruiniert wurden und das als Preis fürs größere Ganze galt. Ein Kurator des San Francisco Museum of Modern Art wurde zum Rückzug gedrängt, nachdem er gesagt hatte, er wolle weiterhin Kunst von weißen Künstlern kaufen. Der Präsident und der Vorstandsvorsitzende der Poetry Foundation mussten zurücktreten, weil sie zwar Black Lives Matter unterstützten, aber nicht entschieden genug.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ZEITmagazin:&lt;/strong&gt; Black Lives Matter war der sichtbarste Ausdruck dieser neuen Sensibilität. Wo genau hat sich die Bewegung aus Ihrer Sicht verrannt?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Williams:&lt;/strong&gt; Der ursprüngliche Impuls von Black Lives Matter war berechtigt. Die Zahl der tödlichen Polizeieinsätze in den USA ist skandalös. Aber schnell verlagerte sich der Fokus von der Gewalt selbst auf die Gruppenzugehörigkeit der Opfer. Das hat viele Menschen entfremdet. Es gibt etwa den Fall von Tony Timpa, einem weißen Mann, der 2016 in Dallas fast auf dieselbe Weise starb wie George Floyd: Polizisten knieten auf ihm, während er um Atem rang, sie lachten – und er starb. Doch das Video ging nicht viral. Warum? Weil der identitätspolitische Fokus fehlte. Genau das war langfristig kontraproduktiv: Statt sich gemeinsam gegen Polizeigewalt zu wenden, wurde alles durch eine identitätspolitische Brille betrachtet.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
      <source:markdown>Ein wirklich interessantes Interview mit dem Autoren Thomas Chatterton Williams beim ZEITmagazin: [&#34;Ein Klima, in dem viele lieber schwiegen. Das machte Woke so wirksam&#34;](https://archive.is/newest/https://www.zeit.de/zeit-magazin/2025/41/thomas-chatterton-williams-wokeness-politische-rechte-polarisierung/komplettansicht#selection-1283.62-1283.94). Ich finde es in Gänze sehr lesenswert, und hätte die Hälfte des gesamten Artikels zitieren können, daher hier nur ein kurzer Auszug:

&gt; **ZEITmagazin:** Was war das Gute an Wokeness?
&gt;
&gt; **Williams:** Wokeness benannte vieles richtig. Ein Beispiel ist MeToo: Als heterosexueller Mann hatte ich nie darüber nachgedacht, mit welchen subtilen Hürden Frauen am Arbeitsplatz konfrontiert sind, etwa beim Netzwerken oder Bewerben. Wokeness machte solche Unsichtbarkeiten sichtbar. […] Problematisch wurde Wokeness dort, wo sie über das Ziel hinausschoss: Wenn Unschuldige durch bloße Anschuldigungen ruiniert wurden und das als Preis fürs größere Ganze galt. Ein Kurator des San Francisco Museum of Modern Art wurde zum Rückzug gedrängt, nachdem er gesagt hatte, er wolle weiterhin Kunst von weißen Künstlern kaufen. Der Präsident und der Vorstandsvorsitzende der Poetry Foundation mussten zurücktreten, weil sie zwar Black Lives Matter unterstützten, aber nicht entschieden genug.
&gt; 
&gt; **ZEITmagazin:** Black Lives Matter war der sichtbarste Ausdruck dieser neuen Sensibilität. Wo genau hat sich die Bewegung aus Ihrer Sicht verrannt?
&gt;
&gt; **Williams:** Der ursprüngliche Impuls von Black Lives Matter war berechtigt. Die Zahl der tödlichen Polizeieinsätze in den USA ist skandalös. Aber schnell verlagerte sich der Fokus von der Gewalt selbst auf die Gruppenzugehörigkeit der Opfer. Das hat viele Menschen entfremdet. Es gibt etwa den Fall von Tony Timpa, einem weißen Mann, der 2016 in Dallas fast auf dieselbe Weise starb wie George Floyd: Polizisten knieten auf ihm, während er um Atem rang, sie lachten – und er starb. Doch das Video ging nicht viral. Warum? Weil der identitätspolitische Fokus fehlte. Genau das war langfristig kontraproduktiv: Statt sich gemeinsam gegen Polizeigewalt zu wenden, wurde alles durch eine identitätspolitische Brille betrachtet.
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://zottmann.org/2025/10/03/quoting-craig-mod-tiny-men.html</link>
      <pubDate>Fri, 03 Oct 2025 19:02:49 +0200</pubDate>
      
      <guid>http://czottmann.micro.blog/2025/10/03/quoting-craig-mod-tiny-men.html</guid>
      <description>&lt;p&gt;Quoting &lt;a href=&#34;https://craigmod.com/&#34;&gt;Craig Mod&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tiny men with big sticks upend sanity the world ‘round and all you can do is try to find your footing and push back.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;From the first missive of his current pop-up &lt;a href=&#34;https://craigmod.com/newsletters/&#34;&gt;newsletter&lt;/a&gt;, &lt;strong&gt;Between Two Mountains&lt;/strong&gt;.&lt;/p&gt;
</description>
      <source:markdown>Quoting [Craig Mod](https://craigmod.com/):

&gt; Tiny men with big sticks upend sanity the world ‘round and all you can do is try to find your footing and push back.

From the first missive of his current pop-up [newsletter](https://craigmod.com/newsletters/), **Between Two Mountains**.
</source:markdown>
    </item>
    
  </channel>
</rss>
