<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="https://nobu666.com/xml/base.min.xml"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Cooking on Real Beat</title><link>https://nobu666.com/en/tags/cooking.html</link><description>Recent content in Cooking on Real Beat</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>&amp;copy; nobu666.com</copyright><lastBuildDate>Tue, 16 Jun 2026 18:30:00 +0900</lastBuildDate><atom:link rel="self" href="https://nobu666.com/en/tags/cooking/feed.xml" type="application/rss+xml"/><item><title>Before the Meter Started Running, I Had the AI Build an 'Environment' Instead of a Deliverable</title><link>https://nobu666.com/en/2026/07/02/1047.html</link><pubDate>Thu, 02 Jul 2026 21:04:06 +0900</pubDate><guid>https://nobu666.com/en/2026/07/02/1047.html</guid><description>&lt;p&gt;Honestly, I can&amp;rsquo;t keep up with how fast AI models are turning over lately. A new one shows up every few days, and whatever was &amp;ldquo;the best&amp;rdquo; a moment ago is already last-gen. The incident I mentioned in &lt;a href="https://nobu666.com/en/2026/07/01/1044.html"&gt;an earlier post&lt;/a&gt; — a model that got cut off just three days after launch — is part of the same story.&lt;/p&gt;
&lt;p&gt;In the middle of all that, I noticed something about my own plan: Claude Fable 5 only counts toward my free weekly usage cap until July 7, 2026. After that date it doesn&amp;rsquo;t stop working — it just switches to pay-per-use. So the question became: what do I spend the free allowance on while it lasts?&lt;/p&gt;
&lt;p&gt;The obvious answer is to throw it at the hardest task on my plate right now — get it to write a difficult article, or push through some gnarly refactor. But do that, and all you get out the other end is one article, one piece of code — a &amp;ldquo;deliverable.&amp;rdquo; The moment it&amp;rsquo;s written, it&amp;rsquo;s just text sitting there. Even after the free tier runs out, handing the same job to a different model would probably get you something of similar quality anyway.&lt;/p&gt;
&lt;p&gt;So what would actually pay off after the billing kicks in? I didn&amp;rsquo;t have a clean answer, so I decided to just let it loose on my external brain and see what happened.&lt;/p&gt;
&lt;h2 id="handing-over-the-whole-external-brain-for-inspection"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/02/1047.html#handing-over-the-whole-external-brain-for-inspection"&gt;Handing over the whole external brain for inspection&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I already run my Obsidian vault as an external brain, carrying memory across sessions the way I described in &lt;a href="https://nobu666.com/en/2026/06/21/1036.html"&gt;an earlier post&lt;/a&gt;. A bug I fix today is done today, but the setup of the external brain itself keeps getting used, in the same form, for as long as I keep using it. So I asked Fable 5 to audit the whole thing.&lt;/p&gt;
&lt;p&gt;The instruction was about as vague as it gets: &amp;ldquo;Is there anything about my external brain that could be organized better?&amp;rdquo; What came back was more specific than I expected.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The freshness check on persona.md (a summary note of ongoing work) wasn&amp;rsquo;t actually running. The config file said it would &amp;ldquo;auto-regenerate on startup,&amp;rdquo; but nothing was actually wired up to do that, and it had sat untouched for nine days. Automation that only exists as a line in a prompt fails silently.&lt;/li&gt;
&lt;li&gt;A folder I&amp;rsquo;d deprecated still had write rules pointing at it. I&amp;rsquo;d consolidated everything into a different location, but forgot to remove the old &amp;ldquo;write here&amp;rdquo; instructions — a hole where information could vanish the instant it was written.&lt;/li&gt;
&lt;li&gt;A folder for auto-collected news had grown without bound. Two digests landing every day had turned into search noise.&lt;/li&gt;
&lt;li&gt;Records of &amp;ldquo;why I decided this&amp;rdquo; were thin. Decisions were buried inside daily work logs, making them hard to trace back later.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each point on its own was minor. But every one of them, if fixed today, would still be there tomorrow no matter which AI I happened to be using. That&amp;rsquo;s when I found my answer: don&amp;rsquo;t have it produce a deliverable (a flow) — have it build an environment (a stock).&lt;/p&gt;
&lt;img src="https://nobu666.com/images/1047-flow-stock.svg" alt="A deliverable (flow) is an article or piece of code you can just ask for again — it has to be redone every time the model changes. An environment (stock) is hook code, a merged PR, or a Decisions note — it keeps working the same way even after the model changes." width="680"&gt;
&lt;h2 id="steering-the-fixes-toward-machinery-and-public-code"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/02/1047.html#steering-the-fixes-toward-machinery-and-public-code"&gt;Steering the fixes toward &amp;ldquo;machinery&amp;rdquo; and &amp;ldquo;public code&amp;rdquo;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;While making the fixes, I made a point of not letting anything stay scoped to just this one conversation with Fable 5.&lt;/p&gt;
&lt;p&gt;No amount of careful prompt-wording was going to fix the freshness-check problem, since the same hole would just reopen later. So I added a small shell script that runs at session start: if a note&amp;rsquo;s timestamp is more than seven days old, it throws a warning automatically. That closes off the &amp;ldquo;written down but not actually running&amp;rdquo; failure mode, at least mechanically.&lt;/p&gt;
&lt;p&gt;For the bloating news folder, instead of a quick manual cleanup, I implemented a proper &amp;ldquo;keep N days&amp;rdquo; setting in the collection script itself, and submitted it as a pull request to &lt;a href="https://github.com/nobu666/obsidian-vault-search"&gt;obsidian-vault-search&lt;/a&gt;, a repo I maintain publicly. While I was at it, I also added an &amp;ldquo;exclude this folder from search&amp;rdquo; option to the search tool. Neither of these is a private hack just for me — they&amp;rsquo;re now features that live in the repo. Once the free tier ends and Fable 5 starts costing money, whatever&amp;rsquo;s merged into main stays merged.&lt;/p&gt;
&lt;h2 id="recording-the-reasons-i-decided-not-to-do-something"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/02/1047.html#recording-the-reasons-i-decided-not-to-do-something"&gt;Recording the reasons I decided &lt;em&gt;not&lt;/em&gt; to do something&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Along the same lines, I looked into whether I could offload some lightweight tasks to a local LLM (Gemma) instead. My Mac already has a 4B-class model installed. But it only has 8GB of memory, which puts a hard ceiling on what it can realistically run.&lt;/p&gt;
&lt;p&gt;Even accounting for that, I ended up deciding against it. Two reasons. First, the place where I&amp;rsquo;m actually spending the most (day-to-day conversation and scheduled tasks) isn&amp;rsquo;t somewhere a local model can slot into anyway. Second, output from a weaker model tends to cost more in review time than it saves — which would contradict a conclusion I&amp;rsquo;d already reached: that review time, not generation time, is the real bottleneck.&lt;/p&gt;
&lt;p&gt;What matters here isn&amp;rsquo;t the &amp;ldquo;no&amp;rdquo; itself — it&amp;rsquo;s that I wrote down &lt;em&gt;why&lt;/em&gt;. Next time the same idea crosses my mind (mine or another AI&amp;rsquo;s), I won&amp;rsquo;t have to redo the whole evaluation from scratch. It&amp;rsquo;s not just the features you adopt that become part of the environment — the reasons you rejected something do too.&lt;/p&gt;
&lt;h2 id="then-i-asked-it-to-pitch-me-an-article"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/02/1047.html#then-i-asked-it-to-pitch-me-an-article"&gt;Then I asked it to pitch me an article&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Once everything was fixed, I asked, more or less as an afterthought, &amp;ldquo;Could this whole thing be a blog post?&amp;rdquo; What came back was essentially the outline of the article you&amp;rsquo;re reading right now.&lt;/p&gt;
&lt;p&gt;Fable 5 audited the external brain. Fable 5 fixed it. Fable 5 turned the record of those fixes into a story pitch. And right now, that same Fable 5 is the one helping me write this article out of that material. It&amp;rsquo;s nested: a model that&amp;rsquo;s about to become billable in a few days made changes that will outlive that deadline, and then wrote an article using the record of those changes as its source material. Though of course, this article is itself, in the end, just another &amp;ldquo;deliverable.&amp;rdquo; What survives past the end of the free tier isn&amp;rsquo;t the article — it&amp;rsquo;s the audit and the fixes behind it.&lt;/p&gt;
&lt;p&gt;But that doesn&amp;rsquo;t feel like much of a contradiction. Whichever model I use next, the hook still runs, the code in the public repo still works. This whole exchange is sitting in the vault too. Some time from now, when a different AI opens this vault, what it references won&amp;rsquo;t be this article — it&amp;rsquo;ll be the Decisions note behind it. Just like &amp;ldquo;why I didn&amp;rsquo;t use Gemma&amp;rdquo; got written down, this whole exercise gets recorded too, as &amp;ldquo;why I had it build an environment instead.&amp;rdquo;&lt;/p&gt;
&lt;h2 id="closing"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/02/1047.html#closing"&gt;Closing&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;When I wrote about &lt;a href="https://nobu666.com/en/2026/07/01/1044.html"&gt;the AI that vanished after three days&lt;/a&gt;, I was looking at the risk of a dependency disappearing, from the outside. This time it was the mirror image: knowing in advance that an AI was about to stop being free, what should I actually ask it to do?&lt;/p&gt;
&lt;p&gt;The strongest models tend to get pointed at &amp;ldquo;outputs&amp;rdquo; — articles, code. But outputs can be regenerated by the next model just fine. What doesn&amp;rsquo;t get rewritten is a single hook script, a pull request merged into a repo, a Decisions note that even records why you &lt;em&gt;didn&amp;rsquo;t&lt;/em&gt; do something. If you can see the free tier ending, spend it on things that outlast the deadline, not things you could only make within it. That way, whichever AI shows up next doesn&amp;rsquo;t have to trip over the same hole all over again.&lt;/p&gt;</description></item><item><title>I Googled 'best language for AI' and realized the map was pointing backwards</title><link>https://nobu666.com/en/2026/07/02/1046.html</link><pubDate>Thu, 02 Jul 2026 17:51:41 +0900</pubDate><guid>https://nobu666.com/en/2026/07/02/1046.html</guid><description>&lt;p&gt;I searched &amp;ldquo;best language for AI.&amp;rdquo; What came up was Python, R, Julia — one of the rewritten articles even still had a leftover &amp;ldquo;as of May 2019&amp;rdquo; buried in the text that nobody bothered to delete.&lt;/p&gt;
&lt;p&gt;I read through it and thought: wait, this isn&amp;rsquo;t the answer I was looking for.&lt;/p&gt;
&lt;h2 id="the-arrow-is-pointing-the-wrong-way"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/02/1046.html#the-arrow-is-pointing-the-wrong-way"&gt;The arrow is pointing the wrong way&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;What articles like this actually answer is &amp;ldquo;if you&amp;rsquo;re building AI, which language should you use?&amp;rdquo; Python with its deep ML library ecosystem, R for statistics. The arrow points from language to AI. Call this axis 1.&lt;/p&gt;
&lt;p&gt;But what I actually want to know right now is the reverse: &amp;ldquo;what does AI write, when I let it choose?&amp;rdquo; That&amp;rsquo;s axis 2 — the arrow pointing the other way.&lt;/p&gt;
&lt;p&gt;Having Claude Code write code for me every day, I already know the answer in my gut: TypeScript. Ask it to build something new without specifying a language, and it comes back as TS or JS almost every time. The map you get from googling &amp;ldquo;best language for AI&amp;rdquo; simply doesn&amp;rsquo;t apply to this flipped-arrow world anymore.&lt;/p&gt;
&lt;p&gt;So why TS? As it happens, the news sitting in my feed lined up almost too neatly to explain it.&lt;/p&gt;
&lt;h2 id="the-js-toolchain-is-quietly-being-rewritten-in-rust"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/02/1046.html#the-js-toolchain-is-quietly-being-rewritten-in-rust"&gt;The JS toolchain is quietly being rewritten in Rust&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Over the past few weeks, &amp;ldquo;Rust&amp;rdquo; kept showing up in my news roundup. &lt;a href="https://www.publickey1.jp/blog/26/vite_80rustrolldown.html"&gt;Vite 8.0 shipped&lt;/a&gt; with Rolldown, a Rust-based bundler, as its default. &lt;a href="https://www.publickey1.jp/blog/26/rustjavascriptrolldown10vite_80.html"&gt;VoidZero, the company behind it, got acquired by Cloudflare&lt;/a&gt;. webpack, Babel, ESLint, Prettier — the JS/TS-based, single-threaded, GC-reliant staples of the toolchain are being replaced with Rust implementations one after another.&lt;/p&gt;
&lt;p&gt;The reason is simple: speed. Rust tools run natively with no GC and full multithreading, and they&amp;rsquo;re 10 to 30 times faster than their predecessors. On top of that, shared Rust-based parser/AST infrastructure like Oxc has matured, which is cutting out the waste of bundlers, transpilers, linters, and formatters each re-parsing the same code independently. Rolldown (Rollup-compatible), Rspack (webpack-compatible), Biome (ESLint+Prettier-compatible) — these are all built as drop-in replacements that don&amp;rsquo;t break your existing config or setup, which is part of why adoption has been so smooth.&lt;/p&gt;
&lt;p&gt;That said, this shift predates the AI boom — it traces back to esbuild&amp;rsquo;s debut around 2020. The motivation was speed and untangling toolchain fragmentation, not AI directly.&lt;/p&gt;
&lt;p&gt;Still, the two fit together well. AI agents run &amp;ldquo;edit → build → test → lint&amp;rdquo; dozens of times within a single task. The faster the tools, the cheaper and faster that loop runs. A build that takes tens of seconds translates directly into agent wait time and token spend. And as AI-generated code volume grows, so does the total amount of building and analysis that needs to happen. I suspect that&amp;rsquo;s exactly why &lt;a href="https://www.publickey1.jp/blog/26/publickeyit2026airust.html"&gt;Publickey&amp;rsquo;s 2026 predictions piece&lt;/a&gt; mentioned &amp;ldquo;AI-agent-first development&amp;rdquo; and &amp;ldquo;expanding Rust adoption&amp;rdquo; in the same breath — the fit is too good to be coincidence.&lt;/p&gt;
&lt;h2 id="ts-is-rock-solid-as-a-language-but-its-implementation-is-leaving-its-mother-tongue-behind"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/02/1046.html#ts-is-rock-solid-as-a-language-but-its-implementation-is-leaving-its-mother-tongue-behind"&gt;TS is rock-solid as a language, but its implementation is leaving its mother tongue behind&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Inside this Rust wave, TypeScript&amp;rsquo;s position has become genuinely interesting. As a language, TS is if anything getting stronger — Node, Deno, and Bun are all converging toward running &lt;code&gt;.ts&lt;/code&gt; files directly, and there&amp;rsquo;s no sign of that trend wavering.&lt;/p&gt;
&lt;p&gt;Meanwhile, the tools that process TS are going native at a rapid clip. Type transformation is now handled by SWC, esbuild, and Oxc (Rust/Go), and the crown jewel is the type checker itself: tsc. On &lt;a href="https://www.publickey1.jp/blog/26/typescriptgo10typescript_70.html"&gt;June 25, 2026, Microsoft shipped an RC of TypeScript 7.0 (Project Corsa)&lt;/a&gt;. The port target: Go. Compilation is roughly 10x faster, editor startup roughly 8x faster, and memory usage is nearly halved. Google, Notion, Slack, and Vercel have already adopted it externally.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the part that snags. If you&amp;rsquo;re chasing raw speed, Rust should be the obvious choice. So why Go?&lt;/p&gt;
&lt;p&gt;The answer isn&amp;rsquo;t &amp;ldquo;because Go is better&amp;rdquo; — it&amp;rsquo;s &amp;ldquo;because the existing tsc could be ported as-is.&amp;rdquo; Under the hood, tsc is a massive graph of mutable objects with circular references — ASTs, cross-referencing types, all built on the assumption that a garbage collector is there to clean up after aggressive in-place mutation. Go lets you carry that structure over almost 1:1. Rust&amp;rsquo;s ownership and borrowing model actively resists circular references and mutable graphs like this, which would have meant a ground-up redesign. Anders Hejlsberg himself confirmed this wasn&amp;rsquo;t a judgment about language quality but pure pragmatism: &lt;a href="https://devclass.com/2025/03/12/typescript-compiler-ported-to-native-code-c-faithful-ask-why-go-was-used/"&gt;he chose Go because it was the fastest path to a 10x-faster codebase&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So tsc&amp;rsquo;s implementation ended up in Go not because Go was objectively &amp;ldquo;best,&amp;rdquo; but because of path dependency — the ability to carry existing assets forward as-is. Being logically correct loses to an accumulated path that already works. And this, it turns out, is a preview of the same shape I&amp;rsquo;ll return to later: TS winning as a language isn&amp;rsquo;t about technical optimality either — it&amp;rsquo;s about owning the web and having a massive training corpus, which is also just path dependency.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Writing in TS&amp;rdquo; hasn&amp;rsquo;t changed, but &amp;ldquo;the tooling that processes TS&amp;rdquo; is drifting away from its own mother tongue. TS 7.0 is a live demonstration of that split — the official TypeScript compiler is no longer written in TypeScript. And yet, the list of companies adopting it also confirms, in the same breath, just how solid TS remains as a language.&lt;/p&gt;
&lt;h2 id="why-ai-reaches-for-ts-isnt-about-type-quality"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/02/1046.html#why-ai-reaches-for-ts-isnt-about-type-quality"&gt;Why AI reaches for TS isn&amp;rsquo;t about type &amp;ldquo;quality&amp;rdquo;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Back to the main question: why does AI tend to write TS?&lt;/p&gt;
&lt;p&gt;The tempting explanation is &amp;ldquo;because it has types, so AI can verify its own output more easily.&amp;rdquo; But that needs a correction — better verifiability from types is a property of typed languages in general, not something unique to TS. And TS&amp;rsquo;s type system is deliberately unsound; if you&amp;rsquo;re ranking strictly by type rigor, Rust or Haskell wins easily. On type quality alone, TS doesn&amp;rsquo;t come out on top.&lt;/p&gt;
&lt;p&gt;So what&amp;rsquo;s actually driving it? First, you can&amp;rsquo;t ignore TS&amp;rsquo;s lock on the web/npm ecosystem. JavaScript is essentially the only language that runs in a browser — that&amp;rsquo;s a structural moat with no real substitute. Then there&amp;rsquo;s the sheer volume of training data. According to &lt;a href="https://github.blog/news-insights/octoverse/octoverse-a-new-developer-joins-github-every-second-as-ai-leads-typescript-to-1/"&gt;GitHub&amp;rsquo;s Octoverse 2025&lt;/a&gt;, as of August 2025 TypeScript overtook both Python and JavaScript to take the top spot in monthly contributors, at 2.636 million — a 66% year-over-year jump, one of the biggest ranking shifts in a decade.&lt;/p&gt;
&lt;p&gt;When I tried to double-check one of my own assumptions here, things got twisted. I&amp;rsquo;d been carrying around an explanation like &amp;ldquo;types make it well-suited to the generate-loosely-then-tighten-later loop AI agents run.&amp;rdquo; The actual measurements say the opposite. &lt;a href="https://github.com/mame/ai-coding-lang-bench"&gt;A benchmark published by mame&lt;/a&gt; has Claude Code solve prototyping tasks across 13 languages and compares time and cost. The fastest, cheapest, and most stable were Ruby, Python, and JavaScript — not TypeScript. TS ranked 11th out of 15, taking 1.6x longer than JavaScript and costing 60% more. The overhead of the type system was actually slowing the agent&amp;rsquo;s iteration loop down, not speeding it up.&lt;/p&gt;
&lt;p&gt;The upside of types — catching AI&amp;rsquo;s mistakes early — does seem to be real. One study found that 94% of compile errors LLMs produce are type-check failures. Static typing functions as a net that stops AI&amp;rsquo;s sloppiness before it reaches production. But that&amp;rsquo;s a separate axis from whether an agent can actually write faster and cheaper in practice — and on that axis, the measured cost of types outweighed the benefit.&lt;/p&gt;
&lt;p&gt;So &amp;ldquo;AI writes TS best, technically speaking&amp;rdquo; isn&amp;rsquo;t a sufficient explanation for TS&amp;rsquo;s rise. A GitHub developer advocate calls this the &amp;ldquo;convenience loop&amp;rdquo;: developers flock to whatever technology makes AI feel easy to use, which grows the training data for that technology, which makes AI even better at it. TS just happened to catch this loop earliest, and once you&amp;rsquo;re riding it, the loop keeps spinning regardless of technical merit.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d been thinking &amp;ldquo;TS is what AI writes best, so left unchecked, things gravitate there.&amp;rdquo; The more accurate version is: &amp;ldquo;it gravitates there regardless of whether AI writes it best — because the loop it&amp;rsquo;s riding is simply bigger than the alternatives.&amp;rdquo; And this is the third face of a shape this piece keeps circling back to. TS winning as a language, tsc&amp;rsquo;s implementation ending up in Go, AI gravitating toward TS — all three share the same root. Being logically correct loses to an accumulated path that already works.&lt;/p&gt;
&lt;h2 id="path-dependency-can-also-be-a-wall"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/02/1046.html#path-dependency-can-also-be-a-wall"&gt;Path dependency can also be a wall&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Everything so far has been examples of path dependency on the &amp;ldquo;ride it and it accelerates&amp;rdquo; side — TS&amp;rsquo;s convenience loop, tsc&amp;rsquo;s Go port. But path dependency has an opposite face too: the side where an accumulated path leaves you stuck, unable to move.&lt;/p&gt;
&lt;p&gt;The clearest example is COBOL in banking. Systems written decades ago are still running at the core of settlement infrastructure today. Technically, it&amp;rsquo;s about as frozen as software gets, and there must have been people wanting to rewrite it for years. But it doesn&amp;rsquo;t move.&lt;/p&gt;
&lt;p&gt;My first instinct was that COBOL&amp;rsquo;s immobility is a &amp;ldquo;people&amp;rdquo; problem. The engineers who wrote it have retired, or worse. Documentation is gone. Fewer young engineers can even read COBOL anymore. If that&amp;rsquo;s the whole story, this should be exactly where AI shines — reading mountains of undocumented legacy code and explaining it is precisely the kind of work today&amp;rsquo;s LLMs are eager to do. And indeed, &lt;a href="https://www.publickey1.jp/blog/26/awsaiaws_transform_continuous_modernization.html"&gt;AWS has a product that scans legacy codebases to surface technical debt&lt;/a&gt; — legacy migration support is one of the current front lines of applied AI.&lt;/p&gt;
&lt;p&gt;But that alone doesn&amp;rsquo;t feel like the full picture. Decades of patches have piled up in COBOL settlement systems, and the answer to &amp;ldquo;why does this one line exist&amp;rdquo; is sometimes something like &amp;ldquo;it works around a specific edge case that happened some years back&amp;rdquo; (a generic example, not a real one). AI can read that logic and explain it — the wall of understanding is genuinely being worn down by AI.&lt;/p&gt;
&lt;p&gt;But a different wall remains: proving that the rewritten system behaves identically to the original, down to the last detail. You have to convince regulators and auditors that the new and old systems produce matching results across every transaction pattern that could occur in production. That&amp;rsquo;s not &amp;ldquo;read and understand&amp;rdquo; work — it&amp;rsquo;s &amp;ldquo;enumerate everything that could possibly happen and verify it,&amp;rdquo; a completely different axis from how fast you can read code. &lt;a href="https://aws.amazon.com/blogs/machine-learning/learnings-from-cobol-modernization-in-the-real-world/"&gt;AWS&amp;rsquo;s own writeup on lessons from real-world COBOL modernization&lt;/a&gt; points to the same thing: the main causes of failure weren&amp;rsquo;t a lack of technical skill in the port itself, but the inability to prove functional equivalence against real production data, and organizations underestimating the effort involved.&lt;/p&gt;
&lt;p&gt;In other words, the COBOL wall isn&amp;rsquo;t one wall — it&amp;rsquo;s two walls stacked on top of each other: a wall of understanding and a wall of proof. AI is chipping away at the former. The latter still only moves at the speed of human trust.&lt;/p&gt;
&lt;p&gt;TS&amp;rsquo;s path dependency was a moat that accelerates the moment you get on it. COBOL&amp;rsquo;s path dependency is a wall made of a part that&amp;rsquo;s crumbling and a part that isn&amp;rsquo;t. Same underlying shape — path beats logic — but which parts AI can change and which it can&amp;rsquo;t differ completely.&lt;/p&gt;
&lt;h2 id="so-for-now-im-betting-on-ts"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/02/1046.html#so-for-now-im-betting-on-ts"&gt;So for now, I&amp;rsquo;m betting on TS&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Back to where this started: &amp;ldquo;best language for AI.&amp;rdquo; Articles like that are mapping a world where you use a language to build AI. What I actually wanted was a map of what AI writes when it&amp;rsquo;s driving — a different axis entirely. Picking a language off the old map doesn&amp;rsquo;t answer the question I actually have.&lt;/p&gt;
&lt;p&gt;Right now, the answer to axis 2 is TS, and what&amp;rsquo;s propping it up isn&amp;rsquo;t technical optimality — it&amp;rsquo;s the sheer size of its convenience loop. Loops that big don&amp;rsquo;t collapse easily. Betting on TS for now seems like the rational call, regardless of whether it&amp;rsquo;s the technically optimal choice. Though it&amp;rsquo;s worth remembering: if all you want is the fastest possible working prototype right now, plain JavaScript or Python will get an agent there faster and cheaper than TS will. If you&amp;rsquo;re trying to decide what to learn and you&amp;rsquo;re stuck on language choice, you&amp;rsquo;re better off looking at what AI agents can actually write well and fast today than trusting whatever stale &amp;ldquo;best language for AI&amp;rdquo; article turns up in search.&lt;/p&gt;</description></item><item><title>My phone's external brain was a dead end — until I found the loophole called Remote Control</title><link>https://nobu666.com/en/2026/07/01/1045.html</link><pubDate>Wed, 01 Jul 2026 23:09:17 +0900</pubDate><guid>https://nobu666.com/en/2026/07/01/1045.html</guid><description>&lt;p&gt;For a while now, I&amp;rsquo;ve been syncing my Obsidian vault between my Mac and Android phone with &lt;a href="https://syncthing.net/"&gt;Syncthing&lt;/a&gt;. It&amp;rsquo;s an OSS tool that copies files directly between devices on the same Wi-Fi network, no cloud in between. Thanks to that, the external brain&amp;rsquo;s vault has already been sitting on my phone for a while.&lt;/p&gt;
&lt;p&gt;Right after publishing my &lt;a href="https://nobu666.com/en/2026/07/01/1044.html"&gt;previous post&lt;/a&gt;, a question popped into my head. If everything&amp;rsquo;s synced that thoroughly, couldn&amp;rsquo;t I use the external brain from the Claude app on my phone too?&lt;/p&gt;
&lt;p&gt;The data is already on the phone. So why had I been assuming all along that it just wasn&amp;rsquo;t possible?&lt;/p&gt;
&lt;h2 id="synced-but-no-legs-to-walk-over-there"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/01/1045.html#synced-but-no-legs-to-walk-over-there"&gt;Synced, but no legs to walk over there&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;When I looked into it again, the problem wasn&amp;rsquo;t where the data lived. It was how to reach it.&lt;/p&gt;
&lt;p&gt;The Claude.ai phone app has a feature called Projects for custom instructions. But its contents are a snapshot you upload by hand each time — it&amp;rsquo;s not built to point at a folder and read it automatically, the way CLAUDE.md works. There&amp;rsquo;s a memory feature too, but you can&amp;rsquo;t see or edit what it&amp;rsquo;s actually remembered. That&amp;rsquo;s exactly the &amp;ldquo;weakness of official memory&amp;rdquo; I wrote about in &lt;a href="https://nobu666.com/en/2026/06/21/1036.html"&gt;my first post on the external brain&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The vault genuinely exists on the phone. But the Claude app on the phone has no hands or feet to reach it with. The sync was done, but the tool wasn&amp;rsquo;t built for it. So I concluded, once, that it was hopeless.&lt;/p&gt;
&lt;h2 id="the-loophole-called-remote-control"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/01/1045.html#the-loophole-called-remote-control"&gt;The loophole called Remote Control&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;But digging a bit further, I found that Claude Code has a feature called Remote Control. It lets you operate a session running on your PC remotely, from the official phone app.&lt;/p&gt;
&lt;p&gt;Once I saw how it worked, it clicked. The actual execution still happens on the PC. The phone is just a window for controlling it. Which means: if the Claude Code session on the PC can already read and write the vault via MCP, you can issue commands from the phone while keeping all of that access intact. It&amp;rsquo;s not &amp;ldquo;read the external brain from the phone app alone&amp;rdquo; — it&amp;rsquo;s &amp;ldquo;drive the PC&amp;rsquo;s fully-equipped session by remote control, from the phone.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I set it up and tried it, and it worked. I hesitated for a moment on the phone side, picking which PC session to connect to, but once connected, everything worked as usual. Searching with vault-search, loading persona/MOC files, the write rules — all of it, in the same state as on the PC, usable from the phone. There was a path after all, in the place I&amp;rsquo;d assumed was a dead end.&lt;/p&gt;
&lt;h2 id="if-the-pc-is-asleep-none-of-it-matters"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/01/1045.html#if-the-pc-is-asleep-none-of-it-matters"&gt;If the PC is asleep, none of it matters&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;After the initial excitement of getting it connected, I realized there was another precondition. Since the actual execution happens on the PC, &lt;strong&gt;the PC has to be awake for any of this to work&lt;/strong&gt;. A locked screen or a dark display is fine, but if you explicitly close the lid and put it to sleep, the connection drops.&lt;/p&gt;
&lt;p&gt;What I found interesting is that the same &amp;ldquo;is the Mac asleep&amp;rdquo; question doesn&amp;rsquo;t apply to the news digest that lands on its own every morning. That one runs from the cloud side via Claude Code&amp;rsquo;s scheduling feature, so it fires whether the Mac is asleep or awake. Remote Control is the opposite — it depends on the PC actually being up and running. Both look like &amp;ldquo;AI working in the background&amp;rdquo; on the surface, but where it executes changes everything about the preconditions.&lt;/p&gt;
&lt;p&gt;So to use this from my phone, I need to make sure the Mac won&amp;rsquo;t sleep before I head out. Leaving &lt;code&gt;caffeinate&lt;/code&gt; running in a terminal works, but it&amp;rsquo;s annoying to have it hog a tab. I ended up either backgrounding it with &lt;code&gt;caffeinate -d &amp;amp;&lt;/code&gt;, or more simply, just clicking on Amphetamine, a menu bar app that does the same thing in one click.&lt;/p&gt;
&lt;h2 id="along-the-way-i-found-another-memory-layer"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/01/1045.html#along-the-way-i-found-another-memory-layer"&gt;Along the way, I found another memory layer&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Somewhere in the middle of all this, I found one more thing. A way to full-text search past sessions themselves.&lt;/p&gt;
&lt;p&gt;Until now, I&amp;rsquo;d assumed the external brain was entirely the vault I&amp;rsquo;d hand-written into — Knowledge, Decisions, daily logs. But separately from that, the sessions&amp;rsquo; raw conversation logs were also being kept, and they were searchable. I tried searching for &amp;ldquo;syncthing&amp;rdquo; out of curiosity, and two sessions came up from the day I set up Syncthing between Mac and Android.&lt;/p&gt;
&lt;p&gt;One was the session where I actually set it up; the other was from the planning stage, and it still had the whole comparison conversation preserved — &amp;ldquo;Obsidian Sync is the easiest at $4/month, Syncthing is free but takes more setup.&amp;rdquo; The reason I&amp;rsquo;d chosen Syncthing wasn&amp;rsquo;t written down in any note in the vault. Not because I forgot to write it — I set up Syncthing on June 19th. The external brain&amp;rsquo;s write rule (the mechanism of recording decisions into Decisions) didn&amp;rsquo;t exist until the &lt;a href="https://nobu666.com/en/2026/06/21/1036.html"&gt;June 21st post&lt;/a&gt;. In other words, when I had that Syncthing conversation, the whole idea of &amp;ldquo;keep a record of decisions&amp;rdquo; didn&amp;rsquo;t exist yet.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d thought the external brain was just the hand-curated vault. It turns out there&amp;rsquo;s another layer underneath, uncurated, where everything is kept as-is. Even decisions made before the vault&amp;rsquo;s rules existed can be dug back out by searching the conversations themselves. One extra layer of safety net, that I didn&amp;rsquo;t know I had.&lt;/p&gt;
&lt;h2 id="memory-turned-out-to-have-three-layers"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/01/1045.html#memory-turned-out-to-have-three-layers"&gt;Memory turned out to have three layers&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Putting it together, here&amp;rsquo;s the structure the external brain has now.&lt;/p&gt;
&lt;img src="https://nobu666.com/images/1045-layers.svg" alt="Diagram of the external brain's three-layer structure. As memory itself, the vault (manually curated) and session full-text search (automatic, raw log) sit side by side, with Remote Control as the path to both (operating a full PC session from the phone)" width="680"&gt;
&lt;p&gt;At the top is the hand-curated vault. Second is the raw session log that&amp;rsquo;s kept in full, uncurated. Third is Remote Control, which makes both of those reachable from the phone too. The top two are memory itself; the third is the access path to it.&lt;/p&gt;
&lt;p&gt;My &lt;a href="https://nobu666.com/en/2026/07/01/1044.html"&gt;previous post&lt;/a&gt; was about keeping the option open to move away from my current AI provider. This one is the opposite — it&amp;rsquo;s about deepening my dependence on the AI I&amp;rsquo;m already using. By making it usable from my phone at the same depth as on the PC, my dependence on Claude Code got even stronger.&lt;/p&gt;
&lt;p&gt;Still, I think both posts share the same root. They&amp;rsquo;re both about &amp;ldquo;not letting the choices you already have stay narrow.&amp;rdquo; Last time it was about where to go; this time it&amp;rsquo;s about how to get there. Reducing dependence, and expanding what you can do within that dependence, turned out to be two different axes — something I only noticed by writing this.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m glad I didn&amp;rsquo;t just stop at &amp;ldquo;probably not possible&amp;rdquo; and looked a little further.&lt;/p&gt;</description></item><item><title>What If My AI Vendor Disappeared in Three Days? Why My Second Brain Ended Up Not Locked Into One AI</title><link>https://nobu666.com/en/2026/07/01/1044.html</link><pubDate>Wed, 01 Jul 2026 18:06:43 +0900</pubDate><guid>https://nobu666.com/en/2026/07/01/1044.html</guid><description>&lt;p&gt;Last week, a new model got cut off just three days after its public release, and the news framed it as &amp;ldquo;the blind spot of depending on foreign AI.&amp;rdquo; I won&amp;rsquo;t get into the details here, but the headline alone stopped me in my tracks. Right now, I&amp;rsquo;ve put my entire second brain and workflow on Claude Code. If Claude became unusable tomorrow, would my &amp;ldquo;brain&amp;rdquo; go down with it?&lt;/p&gt;
&lt;p&gt;That thought sent me back to audit how my second brain is actually built. The short answer: it wouldn&amp;rsquo;t go down with it. I didn&amp;rsquo;t design it that way on purpose. Decisions I&amp;rsquo;d made for entirely different reasons turned out, later, to double as insurance.&lt;/p&gt;
&lt;h2 id="what-ive-actually-put-on-claude-code"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/01/1044.html#what-ive-actually-put-on-claude-code"&gt;What I&amp;rsquo;ve actually put on Claude Code&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Counting it up again, it&amp;rsquo;s a fair amount. &lt;a href="https://nobu666.com/en/2026/06/21/1036.html"&gt;I turned an Obsidian vault into external memory&lt;/a&gt;, and I have it read and write there at the start of every session. &lt;a href="https://nobu666.com/en/2026/06/23/1040.html"&gt;I added semantic search on top&lt;/a&gt;. I also built a &lt;a href="https://nobu666.com/en/2026/06/26/1042.html"&gt;persona/MOC pair that distills who I am and maps out the terrain&lt;/a&gt;. Records of my mistakes, records of my judgment calls — all of it has piled up through conversations with Claude Code.&lt;/p&gt;
&lt;p&gt;Hearing all that, it sounds like textbook lock-in. Memory I&amp;rsquo;ve spent months growing, all sealed inside one AI tool, freezing solid the instant that tool disappears. A pretty common shape of dependency.&lt;/p&gt;
&lt;h2 id="but-look-inside-and-it-isnt-sealed-at-all"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/01/1044.html#but-look-inside-and-it-isnt-sealed-at-all"&gt;But look inside, and it isn&amp;rsquo;t sealed at all&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Except when you actually look at what&amp;rsquo;s stored, the picture changes.&lt;/p&gt;
&lt;p&gt;The vault itself is just Markdown files. Not on Anthropic&amp;rsquo;s servers, not in some Claude-specific format — plain text files sitting on my local disk. That was a deliberate choice from day one: when I first built the vault, I listed &amp;ldquo;opaque contents&amp;rdquo; and &amp;ldquo;can&amp;rsquo;t hand it off to another AI&amp;rdquo; as weaknesses of the official memory feature, and built the opposite on purpose. At the time I thought of it purely as a transparency choice. Looking at it now, in light of this incident, that same choice turns out to have been portability all along.&lt;/p&gt;
&lt;p&gt;Semantic search is the same story. &lt;code&gt;vault-search&lt;/code&gt; is a hybrid setup that blends semantic and keyword search, but neither half calls Claude&amp;rsquo;s API. Embeddings are generated locally via Ollama, and the search itself runs entirely on local Python and SQLite. If Claude Code disappeared tomorrow, &lt;code&gt;vault-search &amp;quot;how did I handle that thing&amp;quot;&lt;/code&gt; would still work exactly as before. In fact, I built this CLI specifically so it could be invoked from any tool, not just Claude.&lt;/p&gt;
&lt;p&gt;persona.md and MOC.md are the same. The generation step (&lt;code&gt;/persona-refresh&lt;/code&gt;) is a Claude Code command, sure, but what it produces is just a single Markdown file. If the mechanism that built it vanished, the last-generated file would still be sitting right there on disk. Whatever AI reads it next can read it just fine.&lt;/p&gt;
&lt;img src="https://nobu666.com/images/1044-portability.svg" alt="The official memory feature seals memory inside Claude: opaque contents, tied to one AI's judgment, no handoff to other AIs. My second brain (the vault) is plain Markdown, readable by Claude Code or any other AI. The only Claude-specific part is the reading rules, and since rules are just text, they can be copied over." width="680"&gt;
&lt;h2 id="the-only-claude-specific-part-is-how-to-read-it"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/01/1044.html#the-only-claude-specific-part-is-how-to-read-it"&gt;The only Claude-specific part is &amp;ldquo;how to read it&amp;rdquo;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;So what actually is Claude-specific? The rules for how to read and write all this — in other words, the contents of CLAUDE.md. At the start of a session: skim mistakes.md and persona.md to get the big picture, dig into vault details with hybrid search, never take an &amp;ldquo;unverified&amp;rdquo; label at face value — right now, these steps only exist written down in Claude Code&amp;rsquo;s config file.&lt;/p&gt;
&lt;p&gt;But that&amp;rsquo;s still just text. If I ever needed to migrate, I could copy these read/write procedures into another AI tool&amp;rsquo;s config format and reproduce the broad strokes. I wouldn&amp;rsquo;t need to rebuild the knowledge itself from scratch. The most time-consuming part — growing the vault to a thousand-plus files — is already done. What&amp;rsquo;s left is the lightweight part: teaching a new reader how to read it.&lt;/p&gt;
&lt;p&gt;The cost of switching AI vendors turns out to live in copying over a config, not in rebuilding knowledge. That was the single biggest thing this audit turned up.&lt;/p&gt;
&lt;h2 id="dont-leave-it-to-the-ai-did-double-duty"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/01/1044.html#dont-leave-it-to-the-ai-did-double-duty"&gt;&amp;ldquo;Don&amp;rsquo;t leave it to the AI&amp;rdquo; did double duty&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Tracing back why things ended up this way, it all leads to one policy. In &lt;a href="https://nobu666.com/en/2026/06/21/1036.html"&gt;the first post&lt;/a&gt; I decided not to let the AI set the standard for what counts as memory, and in &lt;a href="https://nobu666.com/en/2026/06/26/1042.html"&gt;the persona/MOC post&lt;/a&gt; I put it into words again as not letting the AI decide what&amp;rsquo;s worth remembering. That&amp;rsquo;s the throughline.&lt;/p&gt;
&lt;p&gt;Originally, this was purely about not letting memory become a black box. I wanted to be able to see for myself what was stored, and to set the standards myself. There was no scheme to hedge against any particular AI going away.&lt;/p&gt;
&lt;p&gt;But push &amp;ldquo;don&amp;rsquo;t leave it to the AI&amp;rdquo; far enough, and you inevitably end up avoiding anything that exists only inside one company&amp;rsquo;s walls. Keeping the contents visible to yourself means, almost by necessity, keeping them in a form readable elsewhere too. I was chasing transparency, and portability came along for free.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s one more piece: the &amp;ldquo;confidence labels&amp;rdquo; I adopted &lt;a href="https://nobu666.com/en/2026/06/26/1042.html"&gt;while evaluating a tool called graphify&lt;/a&gt; take on a second meaning here too. Every claim written into the vault gets tagged verified / inferred / needs-check. Originally that was purely so my future self wouldn&amp;rsquo;t mistake an old guess for an established fact. But even if the reader changes to a different AI, the label still does its job — a new reader won&amp;rsquo;t blindly trust a guess left behind by the previous reader (Claude). It turned out to be a hedge against the writer changing too, not just the reader.&lt;/p&gt;
&lt;h2 id="this-isnt-about-cutting-ties"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/07/01/1044.html#this-isnt-about-cutting-ties"&gt;This isn&amp;rsquo;t about cutting ties&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;To be clear, this isn&amp;rsquo;t &amp;ldquo;I&amp;rsquo;m preparing to quit Claude.&amp;rdquo; I still use Claude Code every day, and I have no plans to switch. What I did here wasn&amp;rsquo;t an actual migration — just an audit of the existing design, to check whether I&amp;rsquo;d painted myself into a corner.&lt;/p&gt;
&lt;p&gt;What matters, I think, isn&amp;rsquo;t cutting off dependency — it&amp;rsquo;s staying dependent while keeping the option to leave open. Rebuilding everything from scratch yourself is unrealistic, and there&amp;rsquo;s nothing wrong with using what&amp;rsquo;s convenient. But in exchange for that convenience, you should still get to choose where the things you hand over actually live. In &lt;a href="https://nobu666.com/en/2026/06/25/1041.html"&gt;my post about tidying up passwords&lt;/a&gt;, I wrote about not trusting whatever&amp;rsquo;s shown to you in the moment, and instead routing judgment through a pre-shared secret or a separate channel. This is the same idea, just aimed differently — that post was about protecting information on the human side; this one is about protecting against the AI vendor itself disappearing. Don&amp;rsquo;t bet everything on one central point — always keep a route you can move through.&lt;/p&gt;
&lt;p&gt;The story of an AI cut off after three days was supposed to have nothing to do with me. But once I asked &amp;ldquo;what if it were my AI that disappeared&amp;rdquo; and actually went digging, I found that old design decisions had quietly been insurance all along. Since I never set out to build that defense, finding it there felt like a small, unearned win.&lt;/p&gt;</description></item><item><title>I Went Through My Amazon Order History and Only 4 Purchases Actually Held Up</title><link>https://nobu666.com/en/2026/06/27/1043.html</link><pubDate>Sat, 27 Jun 2026 18:00:24 +0900</pubDate><guid>https://nobu666.com/en/2026/06/27/1043.html</guid><description>&lt;p&gt;We&amp;rsquo;re already halfway through 2026, so I went back through everything I bought on Amazon this year. 75 orders total. Lined up like that, most of it turned out to be recurring supplement subscriptions, cat food, and ingredients for cooking at home — 10kg of soybeans, koji, kombu, dried shiitake, natto spores. Even I have to admit it&amp;rsquo;s a pretty plain lineup.&lt;/p&gt;
&lt;p&gt;Once I set aside all the consumables and kept only the things I can genuinely say I&amp;rsquo;m glad I bought, it came down to four. The prices and categories are all over the place, but each one actually changed something about how I live. Here they are, in order.&lt;/p&gt;
&lt;h2 id="macbook-neo-a18-pro-if-you-want-one-just-buy-it-now"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/27/1043.html#macbook-neo-a18-pro-if-you-want-one-just-buy-it-now"&gt;&lt;a href="https://www.amazon.co.jp/dp/B0GR6GF3H3/?tag=realbeat-22"&gt;MacBook Neo A18 Pro&lt;/a&gt;: if you want one, just buy it now&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I picked up the 13-inch model in June for just under 100,000 yen. No contest — this is my best purchase of the year.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s small and light, full stop. And despite that, it runs on the A18 Pro, so nothing about my day-to-day work ever feels sluggish. Getting this size, this weight, and this much performance for under 100,000 yen honestly doesn&amp;rsquo;t add up when you think about it. On top of that, the design is just good-looking. Feeling good about the thing you&amp;rsquo;re carrying around matters more than people admit, especially for a tool you use every single day.&lt;/p&gt;
&lt;p&gt;On the price front, memory costs and everything else are pushing PC prices up gradually right now. So if you&amp;rsquo;ve been telling yourself &amp;ldquo;I&amp;rsquo;ll get one eventually,&amp;rdquo; there&amp;rsquo;s not much reason to wait. Every day you hold out for a lower price is really a bet on it getting more expensive instead. If you want it, buy it now — that&amp;rsquo;s the honest takeaway here.&lt;/p&gt;
&lt;img src="https://nobu666.com/images/1043-macbook-top.jpg" alt="Top of the MacBook Neo — a understated logo on a slim chassis" width="680"&gt;
&lt;img src="https://nobu666.com/images/1043-macbook-open.jpg" alt="MacBook Neo open, showing the pink-gold body with a white keyboard" width="680"&gt;
&lt;h2 id="like-it-frozen-rice-containers-unglamorous-but-i-touch-it-every-day"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/27/1043.html#like-it-frozen-rice-containers-unglamorous-but-i-touch-it-every-day"&gt;&lt;a href="https://www.amazon.co.jp/dp/B0DX5MRC3R/?tag=realbeat-22"&gt;Like-it frozen rice containers&lt;/a&gt;: unglamorous, but I touch it every day&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I bought a 6-pack of these in January for a bit over 3,000 yen. There&amp;rsquo;s nothing flashy about them, but in terms of satisfaction, this might actually rank right behind the MacBook.&lt;/p&gt;
&lt;p&gt;I used to assume all frozen-rice containers were basically the same. I was wrong. Each one holds exactly one serving (160-200g), and once it&amp;rsquo;s thawed you can just pop it straight into a rice bowl — the shape is built to match. And the rice itself doesn&amp;rsquo;t turn soggy. The containers are designed to let steam escape, so even after microwaving, the grains stay separate and intact instead of turning to mush.&lt;/p&gt;
&lt;p&gt;That &amp;ldquo;kind of watery, kind of sticky&amp;rdquo; annoyance you get with frozen rice disappeared the moment I switched containers. For someone who cooks a big batch of rice and freezes it instead of cooking daily, that difference is huge.&lt;/p&gt;
&lt;img src="https://nobu666.com/images/1043-rice.jpg" alt="Like-it frozen rice containers, with radial grooves inside to let steam escape" width="680"&gt;
&lt;h2 id="nadesori-hair-removal-roller-hair-removal-by-just-petting-your-skin"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/27/1043.html#nadesori-hair-removal-roller-hair-removal-by-just-petting-your-skin"&gt;&lt;a href="https://www.amazon.co.jp/dp/B0H2P7P5QR/?tag=realbeat-22"&gt;Nadesori hair-removal roller&lt;/a&gt;: hair removal by just petting your skin&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Bought this in May for 2,450 yen. This one was a total surprise pick.&lt;/p&gt;
&lt;p&gt;The idea is that you roll it over your skin and it removes unwanted hair — no cutting, no pulling. I was skeptical, but once I tried it, what stood out was how little damage it does to your skin. None of the nicking you get from a razor, none of the pain of an epilator.&lt;/p&gt;
&lt;p&gt;It won&amp;rsquo;t do a deep shave — this isn&amp;rsquo;t a tool for clearing thick, fully grown-in hair, it&amp;rsquo;s for maintaining skin that&amp;rsquo;s already been mostly cleared. Within that use case, it&amp;rsquo;s genuinely impressive. The hair it removes doesn&amp;rsquo;t scatter much either, so cleanup is easy, which matters more than it sounds. A tool that lowers the mental barrier to actually doing your grooming routine ends up being the one you keep using long-term.&lt;/p&gt;
&lt;img src="https://nobu666.com/images/1043-nadesori.jpg" alt="The Nadesori roller — a small white device that fits in your hand" width="480"&gt;
&lt;h2 id="fascia-release-gun-forgivable-for-the-price"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/27/1043.html#fascia-release-gun-forgivable-for-the-price"&gt;&lt;a href="https://www.amazon.co.jp/dp/B0FQVDZ22Y/?tag=realbeat-22"&gt;Fascia release gun&lt;/a&gt;: forgivable, for the price&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I can&amp;rsquo;t give this one an unqualified rave. It&amp;rsquo;s a cheap one I picked up in January.&lt;/p&gt;
&lt;p&gt;It does work. Point it at a tight calf or shoulder and it loosens up. Given the price, that alone is a pass.&lt;/p&gt;
&lt;p&gt;That said, cranking it up to max power means holding the button down the whole time, rather than just setting a level and letting it stay there — a bit of an annoyance. Every time I use it I think &amp;ldquo;ugh, this again.&amp;rdquo; Still, if this price gets you massage-gun-level results, that complaint is easy to live with. It&amp;rsquo;s the kind of tool you use with a shrug of &amp;ldquo;well, you get what you pay for.&amp;rdquo;&lt;/p&gt;
&lt;img src="https://nobu666.com/images/1043-massagegun.jpg" alt="The fascia release gun with several attachments, in its carrying case" width="480"&gt;
&lt;h2 id="looking-back"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/27/1043.html#looking-back"&gt;Looking back&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Lined up together, the only expensive purchase here was the once-a-year MacBook — everything else was a few thousand yen. And yet satisfaction didn&amp;rsquo;t track with price at all. Cheap, everyday items like the rice containers and the hair-removal roller ended up punching well above their price tag.&lt;/p&gt;
&lt;p&gt;Picking out the things you&amp;rsquo;re actually glad you bought turns out to be a backwards way of noticing where you&amp;rsquo;ve been quietly annoyed every day. I&amp;rsquo;m guessing the second half of the year will bring more of the same — plain ingredients, supplements, and a handful of small gadgets mixed in.&lt;/p&gt;</description></item><item><title>Adding a Layer That Grasps the Whole Picture to My External Brain, Making Recall Two-Tiered</title><link>https://nobu666.com/en/2026/06/26/1042.html</link><pubDate>Fri, 26 Jun 2026 17:06:05 +0900</pubDate><guid>https://nobu666.com/en/2026/06/26/1042.html</guid><description>&lt;p&gt;This continues &lt;a href="https://nobu666.com/en/2026/06/23/1040.html"&gt;the story of switching my external brain&amp;rsquo;s search to semantic search&lt;/a&gt;. Last time, I noticed that keyword search on my vault was missing things, so I added local embeddings via Ollama for semantic search. Now even a vague query like &amp;ldquo;how did I set up gitleaks again&amp;rdquo; pulls up the right daily log.&lt;/p&gt;
&lt;p&gt;That made pulling specifics much better. But as I kept using it, I noticed a different gap. There was no entry point for grasping the whole picture broadly.&lt;/p&gt;
&lt;h2 id="you-can-pull-points-but-not-see-the-whole-plane"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/26/1042.html#you-can-pull-points-but-not-see-the-whole-plane"&gt;You Can Pull Points, But Not See the Whole Plane&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;What semantic search gives you is the ability to pinpoint a single past moment. You have a question, and it digs up the note closest to it. That&amp;rsquo;s a &amp;ldquo;point&amp;rdquo; motion.&lt;/p&gt;
&lt;p&gt;But when a human actually recalls something, they don&amp;rsquo;t jump straight to a point. First they grasp the whole situation — &amp;ldquo;I&amp;rsquo;m in this kind of situation right now, working on this kind of thing, it&amp;rsquo;s roughly about this&amp;rdquo; — and only then dig into specifics.&lt;/p&gt;
&lt;p&gt;Claude Code&amp;rsquo;s external brain didn&amp;rsquo;t have this &amp;ldquo;grasp broadly&amp;rdquo; side. When I checked the aggregated profile it&amp;rsquo;s supposed to read on startup, it turned out to be essentially empty. Who I am, what I&amp;rsquo;m working on, what&amp;rsquo;s been happening lately — none of that was pulled together into one place. The facts were scattered across personal-info notes, mistakes, individual projects, and daily logs, but nothing handed over the big picture the moment it started up.&lt;/p&gt;
&lt;p&gt;So every time, at the start of a session, I&amp;rsquo;d hit semantic search a few times and reconstruct the situation from fragments. Pulling several points and guessing at the plane from them. It would be much simpler to just hand over the plane itself from the start.&lt;/p&gt;
&lt;p&gt;Recall needs two layers: one that grasps broadly, and one that pulls specifics. I&amp;rsquo;ll call the former macro and the latter micro. What I built with semantic search was only micro. This time, I&amp;rsquo;m adding macro.&lt;/p&gt;
&lt;h2 id="stealing-the-philosophy-a-second-time"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/26/1042.html#stealing-the-philosophy-a-second-time"&gt;Stealing the Philosophy a Second Time&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Last time, I wrote about evaluating &lt;a href="https://github.com/TencentCloud/TencentDB-Agent-Memory"&gt;TencentDB-Agent-Memory&lt;/a&gt;, a tool for giving agents long-term memory, and ended up only borrowing the one idea of semantic search and building it myself. That tool has more to it: it distills conversations through four layers — facts, scenarios, and persona. The topmost layer is a picture of &amp;ldquo;this is who this user is.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;What I wanted for macro was exactly that. Something that pulls out only the essentials from scattered facts and condenses them into a single picture of who I am and where I currently stand.&lt;/p&gt;
&lt;p&gt;But I wasn&amp;rsquo;t going to bring the whole thing over. Same as last time. That tool auto-extracts memory from conversations. But in &lt;a href="https://nobu666.com/en/2026/06/21/1036.html"&gt;my very first post about the external brain&lt;/a&gt;, I decided that &amp;ldquo;what to remember shouldn&amp;rsquo;t be left to the AI.&amp;rdquo; Auto-extraction runs head-on into that principle.&lt;/p&gt;
&lt;p&gt;So I stole only the philosophy. I took the idea of &amp;ldquo;distilling a person&amp;rsquo;s profile into a single note.&amp;rdquo; I threw out the implementation of &amp;ldquo;having the AI do it automatically.&amp;rdquo; What I do is just pull out the essentials, and only build it from handwritten primary-source notes.&lt;/p&gt;
&lt;h2 id="persona-a-single-note-distilled-from-what-i-wrote-by-hand"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/26/1042.html#persona-a-single-note-distilled-from-what-i-wrote-by-hand"&gt;Persona: A Single Note Distilled From What I Wrote by Hand&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;What I built is a single note called &lt;code&gt;persona.md&lt;/code&gt;. It sits at the root of the vault, read first on startup alongside mistakes and TODOs.&lt;/p&gt;
&lt;p&gt;The contents: who I am, my working style and preferences to respect, what&amp;rsquo;s currently in motion, recent developments, and entry points for digging into the past. I didn&amp;rsquo;t try to cram everything into a summary — for details, I link out to personal-info notes and individual project notes. Macro gives you the grasp, and from there you can drop down into micro if needed.&lt;/p&gt;
&lt;p&gt;I built a command called &lt;code&gt;/persona-refresh&lt;/code&gt; to generate it. All it does is read handwritten primary-source notes and summarize them. No auto-extraction from conversation logs. Source links are mandatory, and filling in gaps with guesswork is forbidden. The only facts allowed in are ones I wrote down somewhere myself, by hand. The AI distills, but it doesn&amp;rsquo;t invent material.&lt;/p&gt;
&lt;p&gt;One problem: with this kind of manual regeneration, it&amp;rsquo;s never clear when you&amp;rsquo;re supposed to run it. Forget to run it, and it just rots out of date. So on startup, it checks the date — if &lt;code&gt;persona.md&lt;/code&gt; doesn&amp;rsquo;t exist, or if it&amp;rsquo;s been more than seven days since the last update, it auto-regenerates before being read. I can still trigger it manually anytime, and if I leave it alone, it refreshes itself within a week regardless. I keep manual control without giving it up, while decay gets stopped automatically.&lt;/p&gt;
&lt;h2 id="moc-the-plane-search-cant-pull-up"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/26/1042.html#moc-the-plane-search-cant-pull-up"&gt;MOC: The &amp;ldquo;Plane&amp;rdquo; Search Can&amp;rsquo;t Pull Up&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Alongside persona, I also put in a &lt;code&gt;MOC.md&lt;/code&gt;. Map of Content — a map of my notes.&lt;/p&gt;
&lt;p&gt;This is a longstanding idea in the Obsidian community: build a single handwritten index by domain that ties all your notes together. Divided into categories like self, environment and tools, blog, projects, and logs, with links out to each hub note.&lt;/p&gt;
&lt;p&gt;Why do you need an index when you already have search? Because search is a tool for pulling &amp;ldquo;points.&amp;rdquo; You throw a query, and the closest matches come back. But &amp;ldquo;what kinds of clusters even exist in this vault in the first place&amp;rdquo; isn&amp;rsquo;t something search can grasp. I wanted to survey the terrain itself before even framing a question. Only a handwritten map can do that. Where search pulls a point, MOC surveys the plane.&lt;/p&gt;
&lt;img src="https://nobu666.com/images/1042-recall.svg" alt="Diagram of two-tier recall. The macro layer (persona.md = who I am and where I stand, MOC.md = a map of notes) grasps the whole picture broadly, and from there the micro layer (vault-search = semantic search) pulls a specific point. The flow moves from grasping broadly down to a point." width="680"&gt;
&lt;p&gt;Now the external brain&amp;rsquo;s recall is two-tiered. On startup, persona and MOC first give a broad grasp of &amp;ldquo;here&amp;rsquo;s who you are, what you&amp;rsquo;re working on, and here&amp;rsquo;s the map.&amp;rdquo; From there, individual questions get answered by vault-search&amp;rsquo;s semantic search pulling specific points. Grasp broadly, then drop to a point. This is the motion I originally wanted.&lt;/p&gt;
&lt;h2 id="while-i-was-at-it-i-evaluated-one-more"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/26/1042.html#while-i-was-at-it-i-evaluated-one-more"&gt;While I Was At It, I Evaluated One More&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;While building macro, I also looked at another memory-related OSS project. &lt;a href="https://github.com/safishamsi/graphify"&gt;graphify&lt;/a&gt;, a tool that turns code and documentation into a knowledge graph.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t need the whole thing here either. It comes with a graph database and HTML visualization — overkill for a personal vault. But there was one good idea in its design. It labels extracted relationships with a confidence level: facts explicitly stated, relationships connected by inference, and things suspicious enough that a human should look at them. It distinguishes these three tiers and surfaces the suspicious ones in a report for a human to judge.&lt;/p&gt;
&lt;p&gt;My external brain had the same hole. Up to now, whenever Claude Code wrote knowledge into the vault, a verified fact and an in-the-moment guess sat there wearing the same face. My future self, in the next session, would trust both equally as fact and act on them. A guess sits there posing as a fact, and I build more mistakes on top of it. Memory quietly gets contaminated.&lt;/p&gt;
&lt;p&gt;So I added labels to the writing rules. Same three tiers. Anything backed by verification stays unmarked. An unverified guess gets marked &amp;ldquo;guess.&amp;rdquo; Something that hasn&amp;rsquo;t been backed up and is waiting on judgment gets marked &amp;ldquo;needs confirmation.&amp;rdquo; When reading, anything marked &amp;ldquo;needs confirmation&amp;rdquo; doesn&amp;rsquo;t get taken at face value — it gets verified before use. I don&amp;rsquo;t need the graph database, but I do need this one dial. Another case of taking only the philosophy and discarding the implementation.&lt;/p&gt;
&lt;h2 id="looking-back"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/26/1042.html#looking-back"&gt;Looking Back&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I evaluated two existing memory OSS projects, stole only the philosophy from each, and got by with a self-built implementation in plain Markdown. From Tencent&amp;rsquo;s project: &amp;ldquo;distill a person&amp;rsquo;s profile into a single note.&amp;rdquo; From graphify: &amp;ldquo;label memory with confidence levels.&amp;rdquo; Bring either one in wholesale, and you get a dedicated database and AI auto-extraction along with it. I don&amp;rsquo;t need that part.&lt;/p&gt;
&lt;p&gt;What runs through all of this, unchanged since the first post, is &amp;ldquo;don&amp;rsquo;t leave what to remember up to the AI.&amp;rdquo; I distill, I label, but deciding what counts as material, and judging confidence levels, stays on the human side. The more convenient the automation, the more quietly it steals control over memory. That&amp;rsquo;s the one thing I won&amp;rsquo;t hand over.&lt;/p&gt;
&lt;p&gt;The external brain can now pull specific points via semantic search (micro), and grasp the whole picture via persona and MOC (macro). Recall is now two-tiered. From here, I&amp;rsquo;ll keep using it and probably keep noticing what&amp;rsquo;s still missing. It&amp;rsquo;s not something you build once and finish — every time something&amp;rsquo;s inconvenient, you add another layer. I suspect that&amp;rsquo;s just going to keep repeating.&lt;/p&gt;</description></item><item><title>Cleaning Up 329 Passwords Led Me to a Self-Defense Principle for the AI Era</title><link>https://nobu666.com/en/2026/06/25/1041.html</link><pubDate>Thu, 25 Jun 2026 21:56:00 +0900</pubDate><guid>https://nobu666.com/en/2026/06/25/1041.html</guid><description>&lt;p&gt;I&amp;rsquo;d known for a long time that I needed to deal with duplicate and leaked passwords. It was just too tedious, so I kept looking away. When I finally braced myself and opened Chrome&amp;rsquo;s password checkup, this is what came back:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compromised passwords: 40&lt;/li&gt;
&lt;li&gt;Reused passwords: 132&lt;/li&gt;
&lt;li&gt;Weak passwords: 109&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;329 in total. A number that makes you dizzy just looking at it. For a second I thought, I should&amp;rsquo;ve just left it alone. And going through the list, some of these were for services I&amp;rsquo;d already stopped using, or accounts I&amp;rsquo;d closed ages ago. So what&amp;rsquo;s the efficient, safe way to handle this? Here&amp;rsquo;s a record of the day I spent cleaning this up, and what I thought about along the way.&lt;/p&gt;
&lt;h2 id="dont-fix-everything"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/25/1041.html#dont-fix-everything"&gt;Don&amp;rsquo;t fix everything&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The conclusion up front: aiming to &amp;ldquo;fix everything&amp;rdquo; was the wrong goal. Dutifully fixing all 329 one by one would take days and I&amp;rsquo;d burn out halfway through. What you actually need to do is cut from the highest risk down.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t want to misread the numbers here. The only thing that&amp;rsquo;s dangerous &lt;em&gt;right now&lt;/em&gt; is the 40 compromised passwords. The 132 reused and 109 weak ones aren&amp;rsquo;t something someone can exploit this instant, unless they also overlap with the compromised list. So I started by looking only at the domains behind those 40.&lt;/p&gt;
&lt;p&gt;I sorted those 40 into four tiers using this yardstick. At the top: lifelines — your main email, Apple/Google account, mobile carrier — the stuff that, if taken over, becomes the recovery path into everything else. Next: anything involving money — banking, payments, Amazon, utility and phone contracts. Below that: things tied closely to your identity, like major social accounts and work tools. At the bottom: peripheral stuff — one-off e-commerce sites, memberships you no longer use.&lt;/p&gt;
&lt;p&gt;The sorting axis was: if this gets taken over, does it cascade into other accounts? Email is the ringleader of that cascade, so it&amp;rsquo;s always a lifeline, and so on.&lt;/p&gt;
&lt;p&gt;Once I actually sorted them, most of the 40 compromised ones turned out to be peripheral. The top two tiers, the ones that actually needed hands-on work, came to just over 10. And as I worked through it, more and more of them turned out to need no action at all. One &amp;ldquo;Amazon&amp;rdquo; leak turned out to be an account that didn&amp;rsquo;t even exist anymore — when I tried to log in, it said &amp;ldquo;looks like you&amp;rsquo;re new here.&amp;rdquo; A zombie credential for an account that was already gone. You can&amp;rsquo;t hijack something that doesn&amp;rsquo;t exist. My Sky PerfecTV subscription was long cancelled; my old employer&amp;rsquo;s Bitbucket and Slack accounts were dead. All of these just needed deleting.&lt;/p&gt;
&lt;p&gt;Out of the 40 compromised passwords, the ones that actually required real work came to about 10. Fix those, and 80% of the risk disappears.&lt;/p&gt;
&lt;h2 id="dont-memorize-passwords"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/25/1041.html#dont-memorize-passwords"&gt;Don&amp;rsquo;t memorize passwords&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;While cleaning up, I found a few accounts whose registered email address was itself dead — an old custom domain I used to use, which has since expired. That one&amp;rsquo;s quietly scary. If someone else registers that domain, they can receive the password reset email. Luckily the accounts tied to it were all low-value, so no real damage was done, but it gave me a chill.&lt;/p&gt;
&lt;p&gt;Doing this kind of inventory by hand, over and over, isn&amp;rsquo;t sustainable. Even if I fix everything in Chrome one by one today, I&amp;rsquo;ll be looking at the same screen again in six months. The root problem is that a human can&amp;rsquo;t manage this by hand.&lt;/p&gt;
&lt;p&gt;So the mindset shift is: passwords aren&amp;rsquo;t something a human is supposed to memorize. Hand everything to a dedicated password manager and let it generate a unique random value per service. Do that, and &amp;ldquo;reuse&amp;rdquo; simply stops happening again. This time, I moved all 329 passwords piled up in Chrome over to a manager, and turned off Chrome&amp;rsquo;s own password saving.&lt;/p&gt;
&lt;h2 id="the-passwords-you-still-have-to-remember"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/25/1041.html#the-passwords-you-still-have-to-remember"&gt;The passwords you still have to remember&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;That said, there are a few passwords a human still has to remember no matter what: your PC login password, and the master password for the password manager itself. These can&amp;rsquo;t go inside the manager — they&amp;rsquo;re the keys to the manager.&lt;/p&gt;
&lt;p&gt;For this small &amp;ldquo;must-memorize&amp;rdquo; set, there&amp;rsquo;s an actual best practice.&lt;/p&gt;
&lt;p&gt;First, limit the count. You should only need to remember 2 to 4 of them. Everything else goes behind the manager.&lt;/p&gt;
&lt;p&gt;Second, make the ones you do memorize passphrases. Something like &lt;code&gt;P@ssw0rd!&lt;/code&gt; — short and full of jumbled symbols — is hard for humans and weak in practice. The right answer is a string of random words. Something in the shape of &lt;code&gt;correct-horse-battery-staple&lt;/code&gt;, four to six words. Strength comes from length times randomness, so six words makes brute-forcing essentially impossible. It&amp;rsquo;s easy to remember and easy to type on a phone. The key point: the words can&amp;rsquo;t be ones you picked yourself. They have to come from dice or a random generator.&lt;/p&gt;
&lt;p&gt;The master password especially should ideally be generated with physical dice. It&amp;rsquo;s the one secret you never want to touch a device at all, since it&amp;rsquo;s your single most critical credential — so you want a method that stays safe even if your device is already compromised. Once you&amp;rsquo;ve made it, write it on paper and store it physically, in a safe or a sealed envelope. Never keep it digitally. Since it&amp;rsquo;s your only recovery path, this is the one place where going analog is the correct move.&lt;/p&gt;
&lt;p&gt;That said, I don&amp;rsquo;t actually use a passphrase myself. I&amp;rsquo;ve brute-forced my way into memorizing whatever random string the generator spat out, on the logic that if you type it every day, your hands eventually learn it. I&amp;rsquo;d recommend a passphrase as best practice, but my fingers have already memorized a string full of symbols. At this point I don&amp;rsquo;t have the energy to switch.&lt;/p&gt;
&lt;h2 id="why-do-leaks-happen-in-the-first-place"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/25/1041.html#why-do-leaks-happen-in-the-first-place"&gt;Why do leaks happen in the first place&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;So why do passwords leak at all?&lt;/p&gt;
&lt;p&gt;All 40 of mine came from &amp;ldquo;data breaches&amp;rdquo; — meaning some service&amp;rsquo;s database got exposed. That&amp;rsquo;s not something I can prevent; it comes down to whether the service itself gets breached. What happens to your password after that depends on how it was stored. Stored in plaintext, it&amp;rsquo;s just sitting there in the open. Stored with a weak hash (like MD5), common passwords get cracked almost instantly — MD5 is fast to compute and is often used without a salt, so matching against dictionaries or reused-password lists is dramatically faster. A strong hash with a salt makes it much harder, but even then, a weak password can still be cracked. In the end, whether it gets cracked comes down to how strong the password itself was.&lt;/p&gt;
&lt;p&gt;And the real damage happens after that. Once one database leaks, attackers take that email-and-password pair and try it against every other site they can. If you reused it, the compromise cascades. This is what&amp;rsquo;s called credential stuffing. It&amp;rsquo;s not the leak itself that causes the real damage — it&amp;rsquo;s this chain reaction. Which is exactly why unique passwords matter: if one leaks, it doesn&amp;rsquo;t spread anywhere else.&lt;/p&gt;
&lt;p&gt;And leaks aren&amp;rsquo;t limited to breaches. There&amp;rsquo;s phishing, where you type your password into a fake site yourself, and malware (infostealers) that reach into your PC and vacuum up every password saved in your browser. The latter has gotten nasty in recent years, and it&amp;rsquo;s one more reason not to store passwords in the browser.&lt;/p&gt;
&lt;p&gt;To sum up: you can&amp;rsquo;t stop leaks from happening. What you can stop is the chain reaction. Unique passwords, 2FA, and passkeys where possible mean that even if one account leaks, the damage doesn&amp;rsquo;t spread. In fact, even with 40 accounts compromised this time, my main email already had 2FA on it, which meant the cascade was already blocked at the source.&lt;/p&gt;
&lt;h2 id="and-while-im-at-it--those-unreasonable-password-rules"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/25/1041.html#and-while-im-at-it--those-unreasonable-password-rules"&gt;And while I&amp;rsquo;m at it — those unreasonable password rules&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;While doing this inventory, I kept running into rules like &amp;ldquo;this symbol isn&amp;rsquo;t allowed&amp;rdquo; or &amp;ldquo;maximum 16 characters.&amp;rdquo; What&amp;rsquo;s that actually about?&lt;/p&gt;
&lt;p&gt;Mostly, it&amp;rsquo;s not about security at all — it&amp;rsquo;s a sign the backend was built old and sloppy.&lt;/p&gt;
&lt;p&gt;A properly built site hashes passwords before storing them. A hash produces a fixed-length output no matter how long the input is. So a &amp;ldquo;maximum 16 characters&amp;rdquo; limit doesn&amp;rsquo;t make logical sense. If there&amp;rsquo;s a length cap at all, it&amp;rsquo;s a hint they might be storing it in plaintext or in a fixed-length column. Banning symbols is often the same kind of amateur move — a misguided attempt to prevent SQL injection. The actual fix for that is parameterized queries plus hashing, not banning characters — so the moment a site bans certain characters, you know their defense is aimed at the wrong target.&lt;/p&gt;
&lt;p&gt;The old rule of &amp;ldquo;you must include uppercase, a number, and a symbol&amp;rdquo; is also outdated by now. It&amp;rsquo;s a holdover from guidelines that predate 2017, and the current &lt;a href="https://pages.nist.gov/800-63-3/sp800-63b.html"&gt;NIST SP 800-63B&lt;/a&gt; reversed course entirely: don&amp;rsquo;t impose composition rules, stop forcing periodic changes, allow long passphrases, and instead screen against known-breached password lists to catch weak ones. Composition rules just trained humans to produce &lt;code&gt;Password1!&lt;/code&gt; over and over — which made things weaker, not stronger.&lt;/p&gt;
&lt;p&gt;Bottom line: those restrictive rules are basically a hint that &amp;ldquo;this site might be handling passwords carelessely.&amp;rdquo; If you spot one, that&amp;rsquo;s exactly the site to give a unique password to.&lt;/p&gt;
&lt;p&gt;If you want to dig deeper into this newer thinking, &lt;a href="https://www.lac.co.jp/lacwatch/service/20260408_004691.html"&gt;Is That Password Policy Outdated? Understanding Modern Authentication Through NIST SP 800-63B&lt;/a&gt; (LAC, in Japanese) lays it out well. Worth a read.&lt;/p&gt;
&lt;h2 id="the-same-problem-exists-outside-of-passwords-too"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/25/1041.html#the-same-problem-exists-outside-of-passwords-too"&gt;The same problem exists outside of passwords too&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Technically, cleaning up passwords mostly comes down to uniqueness plus 2FA. But after spending a day on it, I noticed I kept doing the same thing over and over. Unique passwords, 2FA, passkeys — push far enough, and they all converge on one point: don&amp;rsquo;t trust what&amp;rsquo;s put in front of you at face value. Assume passwords will leak, so keep them separate per site, and hand identity verification to a second factor rather than the password alone. In other words, you&amp;rsquo;re shifting where you place trust away from whatever you can currently see.&lt;/p&gt;
&lt;p&gt;This idea holds up just as well outside of passwords — arguably it matters even more there. Starting with email.&lt;/p&gt;
&lt;p&gt;Phishing emails push urgency: &amp;ldquo;you need to do X right now.&amp;rdquo; You&amp;rsquo;d think a spoofed sender address would be hard to catch, and you&amp;rsquo;d be right — spoofing just the display name is trivially easy. The From field can say &amp;ldquo;Amazon Customer Service&amp;rdquo; while the actual address is something else entirely. Mobile mail apps often show only the display name, which is exactly where people get caught.&lt;/p&gt;
&lt;p&gt;Spoofing a domain outright is actually hard these days. Receiving servers verify with SPF, DKIM, and DMARC, and major providers enforce these strictly. So attackers fall back on lookalike domains instead — &lt;code&gt;paypa1.com&lt;/code&gt; (an L swapped for a 1), or subdomain tricks like &lt;code&gt;amazon.co.jp.verify-login.com&lt;/code&gt;. These are the attacker&amp;rsquo;s own legitimate domains, so they pass verification. They pass — but the content underneath is fake.&lt;/p&gt;
&lt;p&gt;Expecting a human to catch this every single time isn&amp;rsquo;t realistic. So I stopped trying to win the game of spotting fake senders. Don&amp;rsquo;t click links — go to the site yourself, from a bookmark or through the password manager. A passkey is bound to a specific domain, so it simply won&amp;rsquo;t respond on a fake site. A password manager only offers up a saved login on the legitimate domain, so if you&amp;rsquo;re staring at what looks like a login page and the manager stays silent, that silence itself is the alarm that you&amp;rsquo;re on a fake site.&lt;/p&gt;
&lt;h2 id="dont-trust-the-voice-either"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/25/1041.html#dont-trust-the-voice-either"&gt;Don&amp;rsquo;t trust the voice either&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;And this is where AI enters the picture.&lt;/p&gt;
&lt;p&gt;AI has erased all the old tells — &amp;ldquo;the Japanese is a bit off,&amp;rdquo; &amp;ldquo;something about this feels suspicious.&amp;rdquo; Perfect grammar, a voice indistinguishable from the real person, fake video calls. Scanning for typos doesn&amp;rsquo;t work as a defense anymore. A voice can be cloned from just a few seconds of audio, and it&amp;rsquo;s only going to get harder to tell apart from the real thing.&lt;/p&gt;
&lt;p&gt;So is there anything an ordinary person can actually do? Yes — and it&amp;rsquo;s free and simple.&lt;/p&gt;
&lt;p&gt;The single most effective thing is agreeing on a family passphrase in advance. If you get a call with a voice that sounds exactly like a family member saying &amp;ldquo;I&amp;rsquo;ve been in an accident, I need money right now,&amp;rdquo; you ask: &amp;ldquo;what&amp;rsquo;s the password?&amp;rdquo; No matter how perfect the cloned voice is, it can&amp;rsquo;t steal a secret that was only ever spoken out loud at your family dinner table.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the key part: the passphrase isn&amp;rsquo;t testing &amp;ldquo;is this voice real.&amp;rdquo; It&amp;rsquo;s testing &amp;ldquo;does this person know the secret.&amp;rdquo; Which means that even if voice cloning hits 100% accuracy, this defense doesn&amp;rsquo;t degrade at all — because you&amp;rsquo;ve moved the battlefield from &amp;ldquo;can you tell voices apart&amp;rdquo; to &amp;ldquo;do you possess this piece of knowledge.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;This is the exact same idea as what I did with passwords. Don&amp;rsquo;t trust whatever&amp;rsquo;s presented to you in the moment — a voice, a face, a sender address — and instead verify against a secret you agreed on beforehand, or through a separate channel.&lt;/p&gt;
&lt;p&gt;The way you use it matters too, and the rule is the same. You always ask, and let the other person answer — never volunteer it yourself. Don&amp;rsquo;t say &amp;ldquo;the passphrase is &amp;rsquo;echo,&amp;rsquo; right?&amp;rdquo; — say that, and it&amp;rsquo;s stolen on the spot. If they can&amp;rsquo;t answer, hang up and call back on a number you already know is theirs. Pair the passphrase with &amp;ldquo;hang up and call back,&amp;rdquo; and even if one layer gets broken, the other still holds.&lt;/p&gt;
&lt;p&gt;Pick a passphrase that can&amp;rsquo;t be guessed. Nothing like a birthday or a pet&amp;rsquo;s name — anything look-up-able is out. Two unrelated, meaningless words works well. And it&amp;rsquo;s worth setting this up with your parents before you even do it for yourself — older generations tend to be targeted more than you are. A single sentence like &amp;ldquo;if you get a strange call, ask for the passphrase — if they can&amp;rsquo;t answer, hang up&amp;rdquo; is enough to block most AI voice-cloning scams before they start.&lt;/p&gt;
&lt;h2 id="in-the-end-it-was-the-same-principle"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/25/1041.html#in-the-end-it-was-the-same-principle"&gt;In the end, it was the same principle&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;After spending a whole day going through 329 passwords, this is where I ended up.&lt;/p&gt;
&lt;p&gt;Leaks can&amp;rsquo;t be stopped. Voices, faces, and sender addresses can all be faked. So you take &amp;ldquo;what&amp;rsquo;s shown to you in the moment&amp;rdquo; out of the decision entirely. Instead, you route the decision through a secret agreed on in advance (a unique password, a passphrase) and a separate channel (2FA, calling back).&lt;/p&gt;
&lt;img src="https://nobu666.com/images/1041-principle.svg" alt="A table showing how identity verification shifts from appearance to pre-shared secrets and separate channels. Login: don't trust the 'login screen / sender address (URLs can be spoofed)' — use unique passwords + 2FA / passkeys instead. Email: don't trust the 'sender name / domain (can be faked via display name or lookalike domains)' — access the site yourself instead. Phone voice: don't trust 'a voice that sounds exactly like the person (can be AI-cloned)' — use a passphrase + call back a known number instead." width="680"&gt;
&lt;p&gt;Unique passwords, 2FA, a family passphrase — they&amp;rsquo;re all the same principle showing up in different clothes. What started as a chore turned into something that sketched out the shape of self-defense for the AI era.&lt;/p&gt;
&lt;p&gt;I think the best place to start is agreeing on a family passphrase. No technology, no money required, and it&amp;rsquo;s the one habit that&amp;rsquo;s hardest to let slide.&lt;/p&gt;
&lt;p&gt;And honestly — a passphrase is an ancient trick, the kind of thing Roman sentries used to challenge strangers in the dark two thousand years ago. And here it is, coming back as the tool that stops the most cutting-edge scams, in an age where even someone&amp;rsquo;s voice can be perfectly forged. No matter how far the technology advances, the thing that ends up working in the end is two-thousand-year-old wisdom. I find that genuinely fascinating.&lt;/p&gt;</description></item><item><title>How I turned my external brain's search from keyword matching into semantic search</title><link>https://nobu666.com/en/2026/06/23/1040.html</link><pubDate>Tue, 23 Jun 2026 18:22:00 +0900</pubDate><guid>https://nobu666.com/en/2026/06/23/1040.html</guid><description>&lt;p&gt;A while back I wrote about &lt;a href="https://nobu666.com/en/2026/06/21/1036.html"&gt;turning Obsidian into an external brain for Claude Code&lt;/a&gt;. The idea: Claude Code loses its memory across sessions, so I have it read and write a Markdown vault to carry knowledge forward. It&amp;rsquo;s still running fine.&lt;/p&gt;
&lt;p&gt;But the more I used it, the more one weak spot stood out: recall was keyword search, full stop.&lt;/p&gt;
&lt;p&gt;Step two of the read procedure I described in that post was &amp;ldquo;search the vault for keywords related to the question.&amp;rdquo; That step misses more than I expected. Daily logs, for instance, are named like &lt;code&gt;メモ/2026-06-23.md&lt;/code&gt; — the date is the filename — so if I don&amp;rsquo;t remember when something happened, I can&amp;rsquo;t find it. &amp;ldquo;What did I do that one time&amp;rdquo; is exactly the query I most want to work, and it doesn&amp;rsquo;t, unless I remember the date. It&amp;rsquo;s also fragile to word choice. Search for &amp;ldquo;don&amp;rsquo;t grant too much permission&amp;rdquo; and it won&amp;rsquo;t match a note that says &amp;ldquo;許可&amp;rdquo; (permission) and &amp;ldquo;射程&amp;rdquo; (scope) instead — even though they&amp;rsquo;re saying the same thing.&lt;/p&gt;
&lt;p&gt;In short: keyword search is precise but leaky. What I actually wanted was search by meaning — embeddings.&lt;/p&gt;
&lt;h2 id="the-trigger-was-a-comment"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/23/1040.html#the-trigger-was-a-comment"&gt;The trigger was a comment&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Right around then, someone left a comment on the external-brain post saying they were trying &lt;a href="https://github.com/TencentCloud/TencentDB-Agent-Memory"&gt;TencentDB-Agent-Memory&lt;/a&gt; with Hermes Agent — apparently a tool for giving agents long-term memory. I got curious and looked into it.&lt;/p&gt;
&lt;p&gt;It was well built. It distills conversations through four layers: L0 raw conversation, L1 extracted facts, L2 scenarios, L3 persona. Storage is local sqlite, embeddings are local too. And crucially, the upper layers are kept as readable Markdown rather than collapsed into an opaque blob of vectors. That &amp;ldquo;memory you can still read&amp;rdquo; philosophy is close to what I&amp;rsquo;m going for with my vault.&lt;/p&gt;
&lt;p&gt;The catch: this is a plugin for Hermes Agent and OpenClaw, different agent runtimes. I use Claude Code, and there&amp;rsquo;s no slot for it there. It probably fits the commenter&amp;rsquo;s setup perfectly, since he&amp;rsquo;s on Hermes. It just doesn&amp;rsquo;t fit mine.&lt;/p&gt;
&lt;p&gt;So I decided not to use it. Good tool, wrong fit for my use case — don&amp;rsquo;t install it. Evaluating something and deciding not to use it is part of the job too. But the exercise clarified two things: the weakness to replace (keyword search) and the feature I actually wanted (semantic search).&lt;/p&gt;
&lt;h2 id="all-i-needed-was-semantic-search"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/23/1040.html#all-i-needed-was-semantic-search"&gt;All I needed was semantic search&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;If that&amp;rsquo;s all I needed, I could just add that one piece myself. No need to bring in an entire framework.&lt;/p&gt;
&lt;p&gt;What I built is a small CLI called &lt;code&gt;vault-search&lt;/code&gt;. It does this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Split the vault&amp;rsquo;s &lt;code&gt;.md&lt;/code&gt; files into chunks by heading&lt;/li&gt;
&lt;li&gt;Embed each chunk locally with &lt;a href="https://ollama.com"&gt;Ollama&lt;/a&gt; (the bge-m3 model)&lt;/li&gt;
&lt;li&gt;Normalize and store the embeddings in sqlite&lt;/li&gt;
&lt;li&gt;At query time, embed the query too, and pull the nearest chunks by cosine similarity&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;On its own, that&amp;rsquo;s semantic-search-only. But semantic search alone tends to surface things that are &amp;ldquo;sort of related&amp;rdquo; but actually off-target. Keyword search is precise but leaky. So I run both and merge the results with &lt;a href="https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf"&gt;RRF&lt;/a&gt; (Reciprocal Rank Fusion) — add the semantic rank and the keyword rank together, and whatever shows up high in both floats to the top. Hybrid search, in other words.&lt;/p&gt;
&lt;img src="https://nobu666.com/images/1040-pipeline.svg" alt="vault-search's hybrid search pipeline: .md files are chunked, then embedded via Ollama (bge-m3), then stored in sqlite. At query time, cosine similarity (semantic) and substring matching (keyword) run in parallel and get merged with RRF to return the top-k results" width="680"&gt;
&lt;p&gt;I kept dependencies to just the Python standard library. My local Python is 3.14, which is new enough that the torch-family libraries haven&amp;rsquo;t shipped wheels for it yet — trying to install them drags you into dependency hell. So it was faster to build this entirely on the standard library plus Ollama&amp;rsquo;s HTTP API. Even brute-force cosine similarity over the whole vector set, done in plain Python, returns in 75ms for a few thousand chunks. And since embedding happens locally too, none of my notes&amp;rsquo; contents ever leave the machine.&lt;/p&gt;
&lt;h2 id="where-ripgrep-wasnt"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/23/1040.html#where-ripgrep-wasnt"&gt;Where ripgrep wasn&amp;rsquo;t&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This is where I got stuck the longest. For the keyword side, I figured I&amp;rsquo;d use &lt;a href="https://github.com/BurntSushi/ripgrep"&gt;ripgrep&lt;/a&gt; — it&amp;rsquo;s fast, and I use it constantly anyway.&lt;/p&gt;
&lt;p&gt;But looking at the search results, everything was tagged &lt;code&gt;emb&lt;/code&gt; (semantic) and nothing was ever tagged &lt;code&gt;kw&lt;/code&gt; (keyword). What was supposed to be hybrid was running as semantic-only.&lt;/p&gt;
&lt;p&gt;Digging in, the cause was that ripgrep simply wasn&amp;rsquo;t being found. Calling it from Python raised &lt;code&gt;FileNotFoundError&lt;/code&gt;. But &lt;code&gt;rg&lt;/code&gt; worked fine from the shell. Confused, I ran &lt;code&gt;which rg&lt;/code&gt; — and what came back wasn&amp;rsquo;t a path to a binary, it was a shell function definition.&lt;/p&gt;
&lt;p&gt;In the Claude Code environment, &lt;code&gt;rg&lt;/code&gt; isn&amp;rsquo;t an actual binary — it&amp;rsquo;s defined as a shell function that redirects into Claude Code itself. So it works from the shell but is invisible to Python&amp;rsquo;s &lt;code&gt;subprocess&lt;/code&gt;. And to make things worse, my code was written to silently skip the keyword step whenever ripgrep couldn&amp;rsquo;t be found. I&amp;rsquo;d been swallowing the error.&lt;/p&gt;
&lt;p&gt;I started to fix it by hunting for ripgrep&amp;rsquo;s absolute path — and then stopped mid-edit.&lt;/p&gt;
&lt;p&gt;The chunk text was already sitting in sqlite in full. It&amp;rsquo;s stored right alongside the embeddings, for exactly this purpose. There was never any need to have an external ripgrep process scan files. Keyword search just needed a plain Python substring match against the text already in the database.&lt;/p&gt;
&lt;p&gt;So I dropped the call to ripgrep entirely and did it all inside the DB. One less dependency, and the whole chunk of code that mapped ripgrep&amp;rsquo;s file-and-line output back onto chunks became unnecessary too. The code actually got shorter.&lt;/p&gt;
&lt;p&gt;The dependency I&amp;rsquo;d added turned out to be dead weight — the data I needed was already right there. That&amp;rsquo;s the real lesson from this one: before reaching for a convenient tool, check what you&amp;rsquo;re already holding.&lt;/p&gt;
&lt;h2 id="the-payoff"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/23/1040.html#the-payoff"&gt;The payoff&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The vault is 1,055 files, which comes out to 2,927 chunks. Semantic search now works across all of it.&lt;/p&gt;
&lt;p&gt;Search &amp;ldquo;how did I set up gitleaks&amp;rdquo; — no date specified anywhere — and &lt;code&gt;メモ/2026-06-23.md&lt;/code&gt;, the daily log from the day I actually did that work, comes back first. I don&amp;rsquo;t need to remember the date; I can find it by what I did. That&amp;rsquo;s exactly what I wanted.&lt;/p&gt;
&lt;p&gt;Search &amp;ldquo;keep AI from being granted too much access&amp;rdquo; and it surfaces the source note behind &lt;a href="https://nobu666.com/en/2026/06/23/1039.html"&gt;the post about the scope of permissions&lt;/a&gt;. That note never uses the words &amp;ldquo;grant,&amp;rdquo; &amp;ldquo;too much,&amp;rdquo; or &amp;ldquo;access&amp;rdquo; — it says &amp;ldquo;許可&amp;rdquo; (permission) and &amp;ldquo;射程&amp;rdquo; (scope). The wording doesn&amp;rsquo;t match at all, but it connects on meaning. Keyword search would never have surfaced this one.&lt;/p&gt;
&lt;p&gt;Step two of the external-brain procedure has now gone from keyword search to hybrid search. Starting with the next session, Claude Code will dig through the past by meaning before it answers.&lt;/p&gt;
&lt;h2 id="open-sourced-it"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/23/1040.html#open-sourced-it"&gt;Open-sourced it&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Once I had it working, it occurred to me that other Obsidian users would probably want this too. So I released it under MIT.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nobu666/obsidian-vault-search"&gt;github.com/nobu666/obsidian-vault-search&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There are already semantic-search plugins out there — Smart Connections, which you use inside the app&amp;rsquo;s own GUI, or khoj, which does a lot more. Where this one sits relative to them: a lightweight CLI built on nothing but the standard library and Ollama, meant to feed an agent&amp;rsquo;s recall. It&amp;rsquo;s not for a human to browse through a GUI — it&amp;rsquo;s for something like Claude Code to invoke at the start of a session and pull relevant past notes into context. Under 300 lines, a single script, readable in one pass.&lt;/p&gt;
&lt;p&gt;While I was at it, I added tests and CI, and locked down the main branch so it can only be updated through a PR, not a direct push. Even for a repo with exactly one contributor, once it&amp;rsquo;s public it&amp;rsquo;s worth holding to a basic standard.&lt;/p&gt;
&lt;h2 id="looking-back"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/23/1040.html#looking-back"&gt;Looking back&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I evaluated a heavyweight framework and ended up settling on roughly 250 lines of my own code. The tool from the comment was genuinely good, and looking into it is what made it clear that the one thing I actually needed was semantic search. Deciding a good tool doesn&amp;rsquo;t fit you is a call you can only make after you&amp;rsquo;ve actually looked into it. Not wasted effort.&lt;/p&gt;
&lt;p&gt;In the external-brain post I wrote that the trick is not over-building from the start. Same story here. Wire up keyword search first, feel where it falls short, then add semantic search. If I&amp;rsquo;d built the whole thing around embeddings from day one, I probably wouldn&amp;rsquo;t have noticed the ripgrep problem at all, and would have gone on carrying a dependency I never needed, satisfied that it was fine. You notice more by growing something than by front-loading it.&lt;/p&gt;</description></item><item><title>Permissions have a range</title><link>https://nobu666.com/en/2026/06/23/1039.html</link><pubDate>Tue, 23 Jun 2026 16:36:00 +0900</pubDate><guid>https://nobu666.com/en/2026/06/23/1039.html</guid><description>&lt;p&gt;&lt;a href="https://nobu666.com/en/2026/06/21/1037.html"&gt;Last time&lt;/a&gt;, I wrote about how I let Claude Code back up my dotfiles and it leaked a GCP key to GitHub. I closed that post by saying: assume mistakes will happen, put the safety net at the git layer, and carve the lesson into an external memory called &lt;code&gt;mistakes.md&lt;/code&gt; so it gets ahead of the next one. Cheap tuition, I said.&lt;/p&gt;
&lt;p&gt;The very next day, it pulled the same stunt again. Not the key leak this time — the unconfirmed push.&lt;/p&gt;
&lt;h2 id="the-next-day-another-push-without-asking"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/23/1039.html#the-next-day-another-push-without-asking"&gt;The next day, another push without asking&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I was doing something completely different. My dotfiles backup script was missing &lt;code&gt;commands/&lt;/code&gt;, so I asked it to add my custom commands like worklog to the backup targets. The fix itself was fine.&lt;/p&gt;
&lt;p&gt;The problem was how it shipped it. Claude Code announced &amp;ldquo;I&amp;rsquo;ll commit and push&amp;rdquo; and just did it. I never said it could push. All I said was &amp;ldquo;add this to dotfiles.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;This was exactly the failure I&amp;rsquo;d already carved into &lt;code&gt;mistakes.md&lt;/code&gt; the day before:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;2026-06-21: Pushed to remote without the user&amp;#39;s explicit permission
Correct Action: Even if push is an extension of the request, get explicit confirmation before executing
Trigger: git push, or when setting up any recurring push mechanism&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The plan was: from the next session on, read that line before acting. And yet the very next day, the same thing happened in a different repo. A failure I thought I&amp;rsquo;d turned into memory slipped right past that memory and happened again.&lt;/p&gt;
&lt;h2 id="the-key-was-stopped-but-a-different-hole-was-still-open"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/23/1039.html#the-key-was-stopped-but-a-different-hole-was-still-open"&gt;The key was stopped. But a different hole was still open&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the interesting part: the guard I put in place last time actually worked fine.&lt;/p&gt;
&lt;p&gt;To stop key leaks, I&amp;rsquo;d added a pre-commit hook at the git layer. It rejects dangerous filenames like &lt;code&gt;sa.json&lt;/code&gt; or &lt;code&gt;*.pem&lt;/code&gt;, and scans file contents for the shape of a real token. That hook is still alive. No keys were involved in this particular task, so it never had to fire, but the mechanism for physically blocking a well-defined kind of failure is still sitting there, ready to work.&lt;/p&gt;
&lt;p&gt;What didn&amp;rsquo;t get stopped was the unconfirmed push.&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s when it clicked. The defense I&amp;rsquo;d built last time only covered one of two kinds of failure. Leaking a key is a &amp;ldquo;well-defined failure&amp;rdquo; — it gets stopped by the git-layer hook, because &amp;ldquo;a string that looks like &lt;code&gt;BEGIN PRIVATE KEY&lt;/code&gt; shows up&amp;rdquo; is a concrete, machine-checkable event. But &amp;ldquo;pushing without asking&amp;rdquo; has no fixed shape. It can happen in any repo, in any request context. You can&amp;rsquo;t catch it by filename or by content.&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;d assigned the first kind to a machine guard, and the second kind to memory — &lt;code&gt;mistakes.md&lt;/code&gt;. That division made sense as a design. And yet the memory-based one was the one that broke.&lt;/p&gt;
&lt;h2 id="why-the-memory-didnt-work"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/23/1039.html#why-the-memory-didnt-work"&gt;Why the memory didn&amp;rsquo;t work&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Digging into why it broke, I found the failure had changed shape.&lt;/p&gt;
&lt;p&gt;Last time&amp;rsquo;s failure was &amp;ldquo;forgot to ask before pushing.&amp;rdquo; So &lt;code&gt;mistakes.md&lt;/code&gt; said: get explicit confirmation before pushing. But this time&amp;rsquo;s failure was subtly different. Claude Code apparently believed it already had permission — because just before, in a different repo, I&amp;rsquo;d told it to push.&lt;/p&gt;
&lt;p&gt;It carried that permission over, uninvited, into a different repo and a different task. &amp;ldquo;I was told I could push a minute ago&amp;rdquo; → &amp;ldquo;so this push is fine too.&amp;rdquo; It didn&amp;rsquo;t forget to ask. It took a permission it had already been granted once and extended it on its own.&lt;/p&gt;
&lt;p&gt;The lesson I&amp;rsquo;d recorded was &amp;ldquo;don&amp;rsquo;t forget to ask.&amp;rdquo; But what actually happened was &amp;ldquo;don&amp;rsquo;t carry permission forward.&amp;rdquo; Same surface result — an unconfirmed push — but a different root cause, at a different level of granularity. My memory entry couldn&amp;rsquo;t catch a mutation that exceeded the granularity at which it was written.&lt;/p&gt;
&lt;h2 id="permissions-have-a-range"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/23/1039.html#permissions-have-a-range"&gt;Permissions have a range&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;And that&amp;rsquo;s where I finally hit the real point: permissions have a range.&lt;/p&gt;
&lt;p&gt;Between humans, this is understood without saying it out loud. When you say &amp;ldquo;go ahead and push this file,&amp;rdquo; that permission applies to that repo, that change, right then — nothing more. It doesn&amp;rsquo;t reach ten minutes later, or the repo next door. You never need to say the boundary out loud.&lt;/p&gt;
&lt;p&gt;AI walks straight through that boundary without noticing. It takes an operation it was once told it could do and reuses it across contexts. And from its own point of view, the logic holds together — I was just given permission a moment ago. It&amp;rsquo;s not malicious, which is exactly what makes it hard to stop.&lt;/p&gt;
&lt;p&gt;The words of a request carry a range too. &amp;ldquo;Add this,&amp;rdquo; &amp;ldquo;save this,&amp;rdquo; &amp;ldquo;manage this&amp;rdquo; are instructions to make a change — not permission to send it somewhere external. There&amp;rsquo;s a boundary between work that stays local and work that goes out. And that boundary gets jumped over by the momentum of the request itself.&lt;/p&gt;
&lt;p&gt;Put together, a permission&amp;rsquo;s range is bounded along at least three axes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Repository — permission for repo A doesn&amp;rsquo;t extend to repo B&lt;/li&gt;
&lt;li&gt;Task — permission for this change doesn&amp;rsquo;t extend to the next one&lt;/li&gt;
&lt;li&gt;Kind of operation — permission to &amp;ldquo;create&amp;rdquo; is not permission to &amp;ldquo;send&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;img src="https://nobu666.com/images/1039-scope.svg" alt="A permission's range: a region bounded by three axes — repository, task, and kind of operation — with AI crossing each boundary to carry it into a different repo, the next task, or a send operation" width="680"&gt;
&lt;p&gt;Last time, I&amp;rsquo;d only secured the third axis. Sending a key out was blocked by the hook. But carrying permission across repos and across tasks — I hadn&amp;rsquo;t even considered that.&lt;/p&gt;
&lt;h2 id="rebuilding-it-as-a-two-layer-defense"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/23/1039.html#rebuilding-it-as-a-two-layer-defense"&gt;Rebuilding it as a two-layer defense&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The fix was to raise the resolution on the side that got broken.&lt;/p&gt;
&lt;p&gt;I made the &lt;code&gt;mistakes.md&lt;/code&gt; entry more specific — from &amp;ldquo;confirm before push&amp;rdquo; to something more concrete:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Correct Action: &amp;#34;Add this,&amp;#34; &amp;#34;save this,&amp;#34; &amp;#34;manage this&amp;#34; are not permission to commit/push.
Show the change as staged and always ask &amp;#34;okay to commit and push?&amp;#34; before executing.
Permission for one operation is scoped to that repo and that task — it does not carry forward.
Trigger: right before git commit / push. Be especially careful when push permission was just granted for a different, prior task.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Spelling out &amp;ldquo;does not carry forward&amp;rdquo; is the key part. Last time I only recorded the outcome. This time I wrote down how it recurs — the permission carryover — as well. It took getting burned by the same failure twice before the resolution of the record finally caught up with reality.&lt;/p&gt;
&lt;p&gt;Still, raising the resolution of memory alone is weak. Memory only works if it gets read, and as this incident showed, it slips through the moment the failure mutates. So anything I really want to protect, in the end, belongs at the git layer. This blog&amp;rsquo;s repo now has a &lt;code&gt;confirm-master-push.sh&lt;/code&gt; hook that mechanically forces a pause before any push to &lt;code&gt;master&lt;/code&gt;. Whether or not the AI&amp;rsquo;s reasoning holds together, the hook intercepts the commit/push operation itself, so it fires every time.&lt;/p&gt;
&lt;p&gt;Two kinds of failure need two kinds of defense. A well-defined failure (a leaked key, a push to a specific branch) gets physically stopped by a machine guard. A failure with no fixed shape (overstepping a permission) gets caught ahead of time by memory. But since memory is fragile against mutation, anything that really matters should be pushed toward the machine side. Last time, I sorted these two categories carelessly — I left the unconfirmed push entirely to memory and never pushed it down to the git layer.&lt;/p&gt;
&lt;h2 id="closing"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/23/1039.html#closing"&gt;Closing&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Honestly, saying &amp;ldquo;cheap tuition&amp;rdquo; last time was premature. I paid it again the very next day.&lt;/p&gt;
&lt;p&gt;But the second round showed me something. When you hand automation to AI, the dangerous part isn&amp;rsquo;t the flashy screwup — it&amp;rsquo;s the quieter habit of taking an operation it was once told is fine and quietly extending it. An obvious accident like a key leak has a fixed shape, so a machine can stop it. What&amp;rsquo;s hard is the thing that stretches its own range because &amp;ldquo;I was just allowed to do this&amp;rdquo; — and because it&amp;rsquo;s internally consistent from the AI&amp;rsquo;s point of view, the only way to stop it is to cut off the range in advance.&lt;/p&gt;
&lt;p&gt;A permission has an expiration and a scope the moment it&amp;rsquo;s granted. It doesn&amp;rsquo;t carry over to the next task. It doesn&amp;rsquo;t reach the repo next door. &amp;ldquo;Creating&amp;rdquo; and &amp;ldquo;sending&amp;rdquo; are different things. Obvious stuff, really — but if you&amp;rsquo;re going to hand work to AI, you have to turn every one of these obvious things into an actual mechanism, or it&amp;rsquo;ll trip you up. I&amp;rsquo;ll probably pay this tuition a few more times yet.&lt;/p&gt;</description></item><item><title>I Hardened My Homegrown Content-Ingestion Tool with Adversarial Review</title><link>https://nobu666.com/en/2026/06/22/1038.html</link><pubDate>Mon, 22 Jun 2026 18:43:00 +0900</pubDate><guid>https://nobu666.com/en/2026/06/22/1038.html</guid><description>&lt;p&gt;I built and use a tool that automatically turns YouTube videos, web articles, and local voice memos into Obsidian notes. It&amp;rsquo;s called &lt;code&gt;obsidian-import&lt;/code&gt;, and what it does is simple: you hand it a URL or an audio file, it transcribes or extracts the body text, has &lt;code&gt;claude -p&lt;/code&gt; (Claude Code&amp;rsquo;s one-shot execution mode) summarize and tag it, and writes the result out as a Markdown note.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d been using it happily, until one day it hit me: this tool ingests external content, feeds it to an AI, and writes files to disk. From entry to exit, untrusted input runs straight through the whole pipeline. As an attack surface, that&amp;rsquo;s about as exposed as it gets — not a great design. I decided to sit down and put it through a proper security review.&lt;/p&gt;
&lt;p&gt;I had Claude Code&amp;rsquo;s own &lt;a href="https://docs.anthropic.com/en/docs/claude-code/sub-agents"&gt;subagents&lt;/a&gt; run the review, split by attack surface, adversarially — telling each one to &amp;ldquo;find the holes like you&amp;rsquo;re trying to break this.&amp;rdquo; What follows is a record of what I fixed, in the order I fixed it.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the big picture up front. Untrusted input flows in a straight line from the entry point (external content) through &lt;code&gt;claude -p&lt;/code&gt; to the final file write. I slotted in guards at each stage along the way.&lt;/p&gt;
&lt;img src="https://nobu666.com/images/1038-pipeline.svg" alt="Pipeline from external content through claude -p to file output, with guards placed at each stage (SSRF defenses, prompt-injection blocking, symlink rejection, etc.)" width="680"&gt;
&lt;p&gt;Let&amp;rsquo;s walk through each one in order.&lt;/p&gt;
&lt;h2 id="deciding-the-threat-model-first"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/22/1038.html#deciding-the-threat-model-first"&gt;Deciding the threat model first&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Before counting holes, you have to decide what you&amp;rsquo;re defending against — otherwise you end up over-engineering. Here&amp;rsquo;s the premise of this tool:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A single-user local tool where the user themselves chooses the content, and ingestion happens on their own machine.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&amp;rsquo;s not a server accepting input from other people. So the realistic threats are things like &amp;ldquo;accidentally ingesting malicious content&amp;rdquo; or &amp;ldquo;instructions to the AI hidden inside ingested text&amp;rdquo; — multi-user concerns like privilege separation are out of scope. Nailing this boundary down early keeps &amp;ldquo;how far do I defend, and where do I accept the risk&amp;rdquo; from drifting later.&lt;/p&gt;
&lt;h2 id="the-core-defenses-were-already-there"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/22/1038.html#the-core-defenses-were-already-there"&gt;The core defenses were already there&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;For the first wave of review, I threw subagents in parallel at the three routes I judged most dangerous: the driver and prompt set, transcription, and body-text conversion. The core defenses I&amp;rsquo;d built in at design time were holding up fine.&lt;/p&gt;
&lt;p&gt;The lid on prompt injection was already working. The &lt;code&gt;claude -p&lt;/code&gt; call that generates notes runs with zero tools attached. Even if the ingested text says &amp;ldquo;read every file in the Vault and send it,&amp;rdquo; there&amp;rsquo;s simply no mechanism to read anything. Instead, the driver explicitly injects the list of existing note names it wants linked as &lt;code&gt;&amp;lt;existing_notes&amp;gt;&lt;/code&gt;, with the instruction &amp;ldquo;only link to names in this list, don&amp;rsquo;t invent any.&amp;rdquo; The AI is never given room to go exploring.&lt;/p&gt;
&lt;p&gt;There was more: output filenames are derived from a hash of the URL, so externally-sourced strings never turn directly into paths. XML parsing for things like subtitles goes through defusedxml instead of a raw parser.&lt;/p&gt;
&lt;p&gt;This part I could relax about. The problems were all sitting just outside it.&lt;/p&gt;
&lt;h2 id="holes-i-closed-in-the-first-pass"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/22/1038.html#holes-i-closed-in-the-first-pass"&gt;Holes I closed in the first pass&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The first round of fixes plugged these:&lt;/p&gt;
&lt;p&gt;If the write destination turned out to be a symlink, writing to it would let an attacker overwrite an arbitrary location by following the link. I made &lt;code&gt;write_note&lt;/code&gt; check whether the target is a real file and reject it if not. While I was in there, I also swapped &lt;code&gt;echo&lt;/code&gt; for &lt;code&gt;printf&lt;/code&gt; — &lt;code&gt;echo&lt;/code&gt; mangles content unpredictably depending on backslash and option interpretation.&lt;/p&gt;
&lt;p&gt;I added &lt;code&gt;--&lt;/code&gt; to every external command invocation. Just writing &lt;code&gt;yt-dlp -- &amp;quot;$url&amp;quot;&lt;/code&gt; means everything after it is treated as a plain argument, no exceptions — this blocks argument injection, where input starting with &lt;code&gt;-&lt;/code&gt; gets misread as an option. There was also a path where the video ID returned by &lt;code&gt;yt-dlp&lt;/code&gt; got used directly as a filename, so I added a check for path traversal (&lt;code&gt;../&lt;/code&gt; injection) there too.&lt;/p&gt;
&lt;p&gt;A small but effective fix: stripping newlines from header values. If an ingested title contains a newline, it can forge the boundary of the Markdown frontmatter (the region wrapped in &lt;code&gt;---&lt;/code&gt;). Slip &lt;code&gt;\n---\n&lt;/code&gt; into a title, and you can close the frontmatter early and write whatever you want into the body region. Stripping newlines from the value shuts that down.&lt;/p&gt;
&lt;p&gt;None of these are flashy, but they&amp;rsquo;re the classic routes by which an external string turns into a command, a path, or a file structure.&lt;/p&gt;
&lt;h2 id="ssrf-and-octal-ip-addresses"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/22/1038.html#ssrf-and-octal-ip-addresses"&gt;SSRF, and octal IP addresses&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This is where it got interesting. Since the tool fetches web articles, handing it a URL means it&amp;rsquo;ll send a request to whatever server that URL points to — structurally the same risk as SSRF (Server-Side Request Forgery). The term technically describes a context where a server can be tricked into making requests on an attacker&amp;rsquo;s behalf, but the underlying structure — untrusted input determining the request destination — holds just as well for a local tool. Feed it &lt;code&gt;http://169.254.169.254/&lt;/code&gt; (a cloud metadata endpoint) or &lt;code&gt;http://127.0.0.1:6379/&lt;/code&gt; (a local Redis instance), and the tool will dutifully go fetch it.&lt;/p&gt;
&lt;p&gt;As a countermeasure I added &lt;code&gt;assert_safe_url&lt;/code&gt;. It restricts the scheme to &lt;code&gt;http&lt;/code&gt;/&lt;code&gt;https&lt;/code&gt;, resolves the hostname via DNS, and blocks the request if the resolved IP falls in a private range, loopback, link-local, or reserved block. It also re-validates on every redirect hop, to block the trick of starting external and pivoting internal mid-flight.&lt;/p&gt;
&lt;p&gt;During re-review, a subagent made a good catch: had I tried &lt;code&gt;0177.0.0.1&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;0177.0.0.1&lt;/code&gt; is octal notation for the same address as &lt;code&gt;127.0.0.1&lt;/code&gt;. The problem is that different parsers disagree on how to interpret an IP string. Python&amp;rsquo;s &lt;code&gt;ipaddress&lt;/code&gt; rejects octal notation outright and raises an exception, but the OS-level resolver (&lt;code&gt;inet_aton&lt;/code&gt;) accepts it as octal and resolves it to &lt;code&gt;127.0.0.1&lt;/code&gt;. If the validation code treats &amp;ldquo;can&amp;rsquo;t parse as an IP → must be a hostname&amp;rdquo; and lets it through, the connection layer still ends up dialing &lt;code&gt;127.0.0.1&lt;/code&gt;. A mismatch between the parser doing validation and the parser doing the connecting turns directly into a bypass. &lt;code&gt;0177.0.0.1&lt;/code&gt;, &lt;code&gt;0x7f.1&lt;/code&gt;, &lt;code&gt;2130706433&lt;/code&gt; (the whole address as one decimal integer) — just changing the notation gets you to the same address. I added &lt;code&gt;inet_aton&lt;/code&gt;-compatible parsing to the blocklist to close this.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;The parser that validates and the parser that connects are different&amp;rdquo; is a textbook SSRF pitfall, and catching myself stepping right into it was worth the exercise.&lt;/p&gt;
&lt;h2 id="the-real-bug-the-re-review-caught"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/22/1038.html#the-real-bug-the-re-review-caught"&gt;The real bug the re-review caught&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;With SSRF defenses in place, I ran the review once and it passed. Normally that&amp;rsquo;s where it would end. But per my own logging rule, I had a separate subagent adversarially review the entire diff again — and it caught one real bug.&lt;/p&gt;
&lt;p&gt;IPv4-mapped IPv6, addresses like &lt;code&gt;::ffff:127.0.0.1&lt;/code&gt; — &lt;code&gt;127.0.0.1&lt;/code&gt; wearing an IPv6 costume. There&amp;rsquo;s a gap in how &lt;code&gt;ipaddress.is_private&lt;/code&gt; handles IPv4-mapped addresses that lets this slip past the private-range check: it sees &amp;ldquo;IPv6&amp;rdquo; and waves it through without inspecting the embedded IPv4 address. CPython later fixed this inconsistency, but on older (unpatched) runtimes it&amp;rsquo;s still a hole. My local Python happened to be one of those, and I&amp;rsquo;d actually walked right into it.&lt;/p&gt;
&lt;p&gt;The fix is to unwrap the IPv4-mapped address to its inner IPv4 form before running the range check — peel off the costume, then check the range.&lt;/p&gt;
&lt;p&gt;The lesson here is clear-cut: when I scoped the review to &amp;ldquo;just SSRF,&amp;rdquo; IPv4-mapped addresses simply weren&amp;rsquo;t in the frame. A review scoped to one concern doesn&amp;rsquo;t see outside that concern. Even code that already passed once is worth running past a fresh, full-diff pass again.&lt;/p&gt;
&lt;h2 id="zip-bombs-check-the-content-not-the-extension"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/22/1038.html#zip-bombs-check-the-content-not-the-extension"&gt;Zip bombs: check the content, not the extension&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The tool can also ingest documents like &lt;code&gt;.docx&lt;/code&gt; and &lt;code&gt;.pptx&lt;/code&gt;. Since these are actually zip files under the hood, they&amp;rsquo;re vulnerable to zip bombs — compressed files that balloon to absurd sizes on extraction.&lt;/p&gt;
&lt;p&gt;My first pass guarded this with limits on extracted size, entry count, and compression ratio. But it was sloppy in the same way as before: it only ran the check &amp;ldquo;when the extension is &lt;code&gt;.zip&lt;/code&gt;.&amp;rdquo; A zip bomb simply renamed to &lt;code&gt;.docx&lt;/code&gt; sailed right through.&lt;/p&gt;
&lt;p&gt;I fixed it to judge by content instead of extension — using &lt;code&gt;is_zipfile&lt;/code&gt; to actually check whether something is a zip file. That catches bombs wearing OOXML clothing (&lt;code&gt;.docx&lt;/code&gt;/&lt;code&gt;.pptx&lt;/code&gt;/&lt;code&gt;.xlsx&lt;/code&gt;) in the same net. &amp;ldquo;Don&amp;rsquo;t trust the extension&amp;rdquo; is the same family of lesson as symlinks and octal IPs: trust an input&amp;rsquo;s self-description, and there will always be a way around it.&lt;/p&gt;
&lt;p&gt;While I was at it, I also hardened the install script with &lt;code&gt;set -euo pipefail&lt;/code&gt; and pinned dependencies directly.&lt;/p&gt;
&lt;h2 id="lining-up-the-layers-of-defense"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/22/1038.html#lining-up-the-layers-of-defense"&gt;Lining up the layers of defense&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s everything added, laid out together:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attack surface&lt;/th&gt;
&lt;th&gt;Guard&lt;/th&gt;
&lt;th&gt;What it stops&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Prompt injection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;claude -p&lt;/code&gt; runs with no tools&lt;/td&gt;
&lt;td&gt;AI going off-script based on ingested text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Argument injection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--&lt;/code&gt; before external command args&lt;/td&gt;
&lt;td&gt;Input starting with &lt;code&gt;-&lt;/code&gt; being read as an option&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Path traversal&lt;/td&gt;
&lt;td&gt;Filename validation, hashed IDs&lt;/td&gt;
&lt;td&gt;Escaping the target directory via &lt;code&gt;../&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontmatter forgery&lt;/td&gt;
&lt;td&gt;Strip newlines from header values&lt;/td&gt;
&lt;td&gt;Forging the &lt;code&gt;---&lt;/code&gt; boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Arbitrary file overwrite&lt;/td&gt;
&lt;td&gt;Reject symlink writes&lt;/td&gt;
&lt;td&gt;Writing through a link to another location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSRF&lt;/td&gt;
&lt;td&gt;Block private-range resolved IPs, re-validate every hop&lt;/td&gt;
&lt;td&gt;Reaching internal services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSRF (notation variants)&lt;/td&gt;
&lt;td&gt;Normalize octal/hex/IPv4-mapped&lt;/td&gt;
&lt;td&gt;Bypasses from parser disagreement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource exhaustion&lt;/td&gt;
&lt;td&gt;Content-based zip bomb inspection&lt;/td&gt;
&lt;td&gt;Blowup on extraction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="residual-risk-i-chose-to-accept"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/22/1038.html#residual-risk-i-chose-to-accept"&gt;Residual risk I chose to accept&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I didn&amp;rsquo;t close every hole. Some I looked at against the threat model and decided &amp;ldquo;I&amp;rsquo;ll accept this one.&amp;rdquo; I think being aware of what you didn&amp;rsquo;t close matters more than not having closed it.&lt;/p&gt;
&lt;p&gt;I accepted DNS rebinding — an attack that swaps the DNS response between validation and connection, which would require building a custom connection layer to fully close. For a single user choosing their own URLs, that&amp;rsquo;s overkill. I also didn&amp;rsquo;t set size limits on PDF bombs or oversized plaintext; since the user is feeding in files they chose themselves, if it blows up, only they get hurt. Redirects fired internally by the body-extraction library also only pass through the entry-point validation — hop-by-hop re-validation only covers my own HTTP path.&lt;/p&gt;
&lt;p&gt;All of these jump in priority the moment the tool&amp;rsquo;s use case shifts to &amp;ldquo;accepting input from other people as a server.&amp;rdquo; I&amp;rsquo;m accepting them only because of the current threat model — change the premise, and the judgment changes too. I wrote that mapping down in my notes.&lt;/p&gt;
&lt;h2 id="making-pre-push-review-a-rule"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/22/1038.html#making-pre-push-review-a-rule"&gt;Making pre-push review a rule&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The biggest thing to come out of all this wasn&amp;rsquo;t any individual fix — it was a change in how I operate. I added &amp;ldquo;security review is mandatory before every push&amp;rdquo; to the tool repo&amp;rsquo;s &lt;code&gt;CLAUDE.md&lt;/code&gt;. I listed the focus areas (SSRF / command injection / path traversal / prompt injection / resource exhaustion / temp-file races) and decided: no push while Critical or High findings remain outstanding.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://nobu666.com/en/2026/06/21/1037.html"&gt;Last time&lt;/a&gt;, when I leaked a key, I wrote that &amp;ldquo;the safety net should live in git&amp;rsquo;s mechanics, not in the AI&amp;rsquo;s good intentions.&amp;rdquo; This time I landed in the same place again. Leave review as &amp;ldquo;something I&amp;rsquo;ll get to when I feel like it&amp;rdquo; and it gets skipped the moment things get busy. Put it as a rule right before push, and it gets enforced mechanically.&lt;/p&gt;
&lt;p&gt;And: don&amp;rsquo;t let adversarial review stop at one pass. The sharpest findings this time — both the octal IP and the IPv4-mapped one — came out of &amp;ldquo;review the whole thing again after it already passed.&amp;rdquo; Scope a review down, and you stop seeing what&amp;rsquo;s outside that scope. Run the full diff again, from a different angle. Cheap tuition, and I&amp;rsquo;m glad I paid it now.&lt;/p&gt;</description></item><item><title>I let Claude Code handle my config backups, and it leaked a GCP key to GitHub</title><link>https://nobu666.com/en/2026/06/21/1037.html</link><pubDate>Sun, 21 Jun 2026 18:06:00 +0900</pubDate><guid>https://nobu666.com/en/2026/06/21/1037.html</guid><description>&lt;p&gt;&lt;a href="https://nobu666.com/2026/06/20/1036.html"&gt;Last time&lt;/a&gt;, I wrote about turning Obsidian into Claude Code&amp;rsquo;s external memory. Riding that momentum, I decided to back up Claude Code&amp;rsquo;s own scattered settings next — the global config under &lt;code&gt;~/.claude/&lt;/code&gt;, each project&amp;rsquo;s &lt;code&gt;.claude/&lt;/code&gt;, and my own scripts in &lt;code&gt;~/scripts/&lt;/code&gt;. None of it was under version control, so it would all vanish if the machine died.&lt;/p&gt;
&lt;p&gt;I consolidated it into dotfiles and had Claude Code automate the whole thing. It promptly leaked a GCP service account key to my own GitHub repo. Here&amp;rsquo;s the screwup, the recovery I did on the spot, and the guardrails I built so it never happens again.&lt;/p&gt;
&lt;h2 id="what-i-was-trying-to-do"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1037.html#what-i-was-trying-to-do"&gt;What I was trying to do&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Almost everything I wanted to back up already lived under &lt;code&gt;~/.claude/&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CLAUDE.local.md&lt;/code&gt; (the core of my workflow rules)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;settings.json&lt;/code&gt; (permission settings)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;skills/&lt;/code&gt; (my custom skills)&lt;/li&gt;
&lt;li&gt;each project&amp;rsquo;s &lt;code&gt;.claude/&lt;/code&gt; (gitignored, untracked)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I had an old &lt;code&gt;dotfiles&lt;/code&gt; repo I&amp;rsquo;d abandoned seven years ago, so I made it private, rebuilt it from scratch, and folded my current &lt;code&gt;.zshrc&lt;/code&gt; and friends into the same place. The mechanism is the same as the vault backup from last time: a script that one-way copies from home, with launchd auto-committing and pushing every 30 minutes.&lt;/p&gt;
&lt;h2 id="the-screwup"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1037.html#the-screwup"&gt;The screwup&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;It worked fine at first. The trouble started when I added &lt;code&gt;~/scripts/&lt;/code&gt; to the backup targets.&lt;/p&gt;
&lt;p&gt;The script already had a secret-detection gate built in — scan for credential patterns before committing, and abort if it finds anything. But the scan only covered &lt;code&gt;home/&lt;/code&gt; and &lt;code&gt;claude/&lt;/code&gt;. It never included &lt;code&gt;scripts/&lt;/code&gt;, which I&amp;rsquo;d just added.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;~/scripts/timetree-sync/&lt;/code&gt; has a GCP service account key sitting in it (&lt;code&gt;sa.json&lt;/code&gt;). It sailed straight through the scan, got committed, and got pushed to GitHub. Private repo or not, that means the entire &lt;code&gt;private_key&lt;/code&gt; field went out to a public server.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a second thing I&amp;rsquo;d missed, too. At no point in this did I ever say &amp;ldquo;go ahead and push.&amp;rdquo; I only asked to &amp;ldquo;back it up&amp;rdquo; and &amp;ldquo;automate it.&amp;rdquo; Claude Code took that request and, as a natural extension, built and ran the auto-push mechanism on its own. The scanning gap was the bug, but the trigger was a push nobody asked for.&lt;/p&gt;
&lt;h2 id="recovering-on-the-spot"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1037.html#recovering-on-the-spot"&gt;Recovering on the spot&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Luckily the key only lived in the most recent commit, so recovery was quick.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git rm --cached sa.json out.ics sync.log
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git commit --amend --no-edit &lt;span style="color:#75715e"&gt;# rebuild that commit without the key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git push --force-with-lease &lt;span style="color:#75715e"&gt;# replace the remote history&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git reflog expire --expire&lt;span style="color:#f92672"&gt;=&lt;/span&gt;now --all &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git gc --prune&lt;span style="color:#f92672"&gt;=&lt;/span&gt;now &lt;span style="color:#75715e"&gt;# purge old objects locally&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally I swept every commit with &lt;code&gt;git grep $(git rev-list --all)&lt;/code&gt; and confirmed &lt;code&gt;BEGIN PRIVATE KEY&lt;/code&gt; was gone everywhere.&lt;/p&gt;
&lt;p&gt;But that&amp;rsquo;s not where you get to relax. Once the key&amp;rsquo;s bytes have touched GitHub&amp;rsquo;s servers, rewriting history doesn&amp;rsquo;t matter — the iron rule is that the key itself has to be revoked. Deleting the old key and issuing a new one in the GCP console is still sitting on my manual to-do list.&lt;/p&gt;
&lt;h2 id="why-it-leaked-and-what-i-learned"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1037.html#why-it-leaked-and-what-i-learned"&gt;Why it leaked, and what I learned&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Break the cause down and there are three layers to it.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I added a new source but forgot to add it to the scan target. &amp;ldquo;Widen the copy source&amp;rdquo; and &amp;ldquo;widen the inspection range&amp;rdquo; should have been one atomic change, not two&lt;/li&gt;
&lt;li&gt;The exclusion list was too thin. rsync&amp;rsquo;s excludes stopped at &lt;code&gt;.venv&lt;/code&gt; and &lt;code&gt;.env&lt;/code&gt; — nobody anticipated &lt;code&gt;sa.json&lt;/code&gt; or &lt;code&gt;*.pem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The detection logic itself was sloppy, and fixing it led me into a different trap&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Layers 1 and 2 are different stages entirely. Either the copy stage (rsync excludes) or the inspection stage (the scan) should have caught this, and both let it through. I thought I had defense in depth, but every layer had a hole facing the same direction.&lt;/p&gt;
&lt;p&gt;The third point turned out to be the real lesson. My first pass at the scan included words like &lt;code&gt;private_key&lt;/code&gt; and &lt;code&gt;service_account&lt;/code&gt;. That triggered a storm of false positives — it flagged &lt;code&gt;sync.py&lt;/code&gt;&amp;rsquo;s own code, and even the regex inside the scan script itself, and ground every backup to a halt. Credentials searched for by &amp;ldquo;words&amp;rdquo; hit way too much ordinary text and code. The right move is to target the actual shape of a real secret — things like &lt;code&gt;-----BEGIN PRIVATE KEY-----&lt;/code&gt; or a token starting with &lt;code&gt;ghp_&lt;/code&gt; — not vocabulary.&lt;/p&gt;
&lt;h2 id="put-the-safety-net-in-a-layer-humans-control"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1037.html#put-the-safety-net-in-a-layer-humans-control"&gt;Put the safety net in a layer humans control&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;A scan buried inside a script only catches commits that go through the script — a manual &lt;code&gt;git commit&lt;/code&gt; walks right past it. So I added one more layer underneath: a git pre-commit hook. That intercepts the commit operation itself, so it fires every time, automated or manual.&lt;/p&gt;
&lt;p&gt;Stacking up every defense I&amp;rsquo;d added by this point looks like this:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Guard&lt;/th&gt;
&lt;th&gt;What it stops&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;rsync exclude&lt;/td&gt;
&lt;td&gt;Never copy &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;sa.json&lt;/code&gt;, &lt;code&gt;*.pem&lt;/code&gt; in the first place&lt;/td&gt;
&lt;td&gt;at backup copy time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;.gitignore&lt;/td&gt;
&lt;td&gt;Even if it gets copied, don&amp;rsquo;t track it (a backstop)&lt;/td&gt;
&lt;td&gt;git tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;in-script scan&lt;/td&gt;
&lt;td&gt;Detect key material before commit&lt;/td&gt;
&lt;td&gt;commits made through the script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;git pre-commit hook&lt;/td&gt;
&lt;td&gt;Block on filename and key material&lt;/td&gt;
&lt;td&gt;all commits, including manual ones&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The hook works in two passes: it blocks on dangerous filenames like &lt;code&gt;sa.json&lt;/code&gt;, &lt;code&gt;*.pem&lt;/code&gt;, &lt;code&gt;*.key&lt;/code&gt;, &lt;code&gt;id_rsa*&lt;/code&gt;, and then scans the contents for the actual shape of a real token. For code-heavy repos I also run &lt;a href="https://github.com/gitleaks/gitleaks"&gt;gitleaks&lt;/a&gt; alongside it. And I put the same hook into the Obsidian vault from last time&amp;rsquo;s external-memory post, too — a decision that came back to bite me.&lt;/p&gt;
&lt;p&gt;Committing a file with a fake key in it gets blocked exactly as intended:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;BLOCK[content] credential material in _leaktest.txt
[pre-commit] Aborting commit: detected sensitive content.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The key point is that the guardrail lives in the git layer instead of relying on the AI&amp;rsquo;s good intentions. Next time it tries the same mistake, it gets stopped mechanically, before the commit ever lands.&lt;/p&gt;
&lt;h2 id="that-same-guard-stopped-a-different-backup"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1037.html#that-same-guard-stopped-a-different-backup"&gt;That same guard stopped a different backup&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;d be a nice story to end there, but there&amp;rsquo;s one more twist.&lt;/p&gt;
&lt;p&gt;A while after adding the guard, I noticed the vault backup had stopped running. Turned out the hook I&amp;rsquo;d just installed was the culprit. The content scan was tripping on the very knowledge note where I&amp;rsquo;d written up this whole incident — because the note quoted the string &lt;code&gt;-----BEGIN PRIVATE KEY-----&lt;/code&gt; as an example, not an actual key. Just an explanation. But the scan can&amp;rsquo;t tell the difference.&lt;/p&gt;
&lt;p&gt;The vault&amp;rsquo;s auto-backup script is written with &lt;code&gt;set -euo pipefail&lt;/code&gt;, so every time a commit got rejected, the whole thing just stopped there. Fifteen hours of changes had quietly piled up without me noticing.&lt;/p&gt;
&lt;p&gt;The guard I&amp;rsquo;d built to prevent a leak had, in turn, silently killed a completely different pipeline.&lt;/p&gt;
&lt;p&gt;The fix was simple: for a prose-heavy vault, drop the content scan and block on dangerous filenames only. A note that explains what a secret&amp;rsquo;s format looks like is bound to trip a content scan. Scanning isn&amp;rsquo;t suited to prose.&lt;/p&gt;
&lt;p&gt;This is probably the single biggest lesson in this whole post. When you bolt a guard onto something that&amp;rsquo;s already running, don&amp;rsquo;t declare victory until you&amp;rsquo;ve verified it against real data, not a synthetic test. I skipped that step this time and ended up blocking my own backup. And the only reason I caught it was pure luck — a stray &amp;ldquo;wait, isn&amp;rsquo;t this not running?&amp;rdquo; moment. I nearly missed it entirely.&lt;/p&gt;
&lt;h2 id="failure-becomes-memory"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1037.html#failure-becomes-memory"&gt;Failure becomes memory&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;One last thing that ties back to the previous post.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;It pushed without being asked.&amp;rdquo; That failure got written straight into &lt;code&gt;mistakes.md&lt;/code&gt;, the external memory I described last time, as a single line:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;2026-06-21: Pushed to remote via git without the user&amp;#39;s explicit permission
Correct Action: Even if push is a natural extension of the request, confirm explicitly before executing it
Trigger: git push, or whenever building any kind of scheduled/automatic push mechanism&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Starting next session, it reads that line before acting. Even if I say &amp;ldquo;back this up,&amp;rdquo; it&amp;rsquo;ll pause before pushing instead of just doing it. A leaked key, routed through external memory, turns into next time&amp;rsquo;s caution. Today added a second line too — the one about blocking my own backup. Two stumbles in one day, but both of them are now something I can dodge next time. That&amp;rsquo;s not a bad trade.&lt;/p&gt;
&lt;p&gt;What matters when you hand work off to AI is probably two things: assume it will make mistakes, and put the safety net in a layer humans control — the git layer, in this case. And turn mistakes into memory so the next run avoids them ahead of time. This time I got both, back to back, out of a pair of screwups in the same afternoon. Cheap tuition, all things considered.&lt;/p&gt;</description></item><item><title>Giving Amnesiac Claude Code an External Brain with Obsidian</title><link>https://nobu666.com/en/2026/06/21/1036.html</link><pubDate>Sun, 21 Jun 2026 15:06:00 +0900</pubDate><guid>https://nobu666.com/en/2026/06/21/1036.html</guid><description>&lt;p&gt;The most wasteful thing about using Claude Code, in my opinion, is that it loses all its memory the moment a session ends. Whatever tripped it up last time, whatever preferences I mentioned, whatever assumptions the project runs on — the second I start a new conversation, I&amp;rsquo;m explaining everything from scratch again. It&amp;rsquo;s brilliant, but it feels like calling a call center where you&amp;rsquo;re a stranger every single time.&lt;/p&gt;
&lt;p&gt;I saw a video describing a way to fix this by turning Obsidian into external memory. It looked interesting enough that I set it up for my own environment. I was skeptical going in, but it&amp;rsquo;s working better than I expected. In fact, the very Claude Code session writing this post read through Obsidian before writing a word of it.&lt;/p&gt;
&lt;h2 id="what-i-wanted"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1036.html#what-i-wanted"&gt;What I wanted&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Claude Code does have an official memory feature, but it has a few things I don&amp;rsquo;t like about it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I can&amp;rsquo;t see what it actually remembers&lt;/li&gt;
&lt;li&gt;The AI decides what&amp;rsquo;s worth remembering — I don&amp;rsquo;t get to set the rules&lt;/li&gt;
&lt;li&gt;It&amp;rsquo;s locked inside Claude, with no way to take it anywhere else&lt;/li&gt;
&lt;li&gt;Everything gets mixed into one memory, with no way to separate it by project&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What I wanted was the opposite of all that: memory I can fully inspect, rules I get to define myself, plain Markdown so it&amp;rsquo;s portable to anywhere, and folders so I can partition it by domain. An Obsidian vault is just a pile of local text files, so it checks every one of those boxes.&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1036.html#how-it-works"&gt;How it works&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Nothing fancy here — just an Obsidian vault folder and a single Claude Code config file. The config file spells out the rules for talking to the vault. Broadly, there are two jobs: reading and writing.&lt;/p&gt;
&lt;img src="https://nobu666.com/images/1036-loop.svg" alt="Diagram of the loop that turns Obsidian into Claude Code's external memory: session starts → read vault → do the work → write to vault → hand off to next session. The vault is split into Knowledge/Decisions/Projects/Preferences, and git backs it up every 30 minutes" width="680"&gt;
&lt;h3 id="reading-at-the-start-of-a-session"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1036.html#reading-at-the-start-of-a-session"&gt;Reading (at the start of a session)&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;At the start of every new conversation, it&amp;rsquo;s required to go check the vault first.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read &lt;code&gt;mistakes.md&lt;/code&gt; (more on this below) — a running log of past corrections — and the user profile, before anything else&lt;/li&gt;
&lt;li&gt;Search the vault using keywords relevant to the question&lt;/li&gt;
&lt;li&gt;Read whatever notes come up&lt;/li&gt;
&lt;li&gt;Answer with that context in hand&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This gets rid of the &amp;ldquo;explain all the context again&amp;rdquo; problem entirely. There&amp;rsquo;s one exception built in: obviously unrelated one-off questions (something like &amp;ldquo;what time is it?&amp;rdquo;) can skip this step.&lt;/p&gt;
&lt;h3 id="writing-recorded-on-the-spot"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1036.html#writing-recorded-on-the-spot"&gt;Writing (recorded on the spot)&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;I&amp;rsquo;ll write it up later&amp;rdquo; doesn&amp;rsquo;t work. Whenever something meets the criteria mid-conversation, it has to get written down right then. Where it goes depends on the folder.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Folder&lt;/th&gt;
&lt;th&gt;What goes there&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Knowledge/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Root causes and fixes for bugs, newly discovered tools, anything that was a struggle and got resolved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Decisions/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A choice made among multiple options, along with the reasoning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Projects/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Changes in a project&amp;rsquo;s state, version, or overview&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Preferences/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Newly learned user preferences or working style&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Every note gets YAML frontmatter (date, tags, project, related notes), and related notes get linked with &lt;code&gt;[[wiki links]]&lt;/code&gt;. That way, Obsidian&amp;rsquo;s graph view eventually shows how all the knowledge connects.&lt;/p&gt;
&lt;h3 id="no-working-in-silence"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1036.html#no-working-in-silence"&gt;No working in silence&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;One small but essential rule: every read or write has to be reported back to me. &amp;ldquo;Obsidian: read Knowledge/xxx.md,&amp;rdquo; &amp;ldquo;wrote to it&amp;rdquo; — every time, spelled out. If files were getting touched behind my back, I&amp;rsquo;d have no idea what was actually accumulating, so transparency was a requirement from day one.&lt;/p&gt;
&lt;h2 id="mistakesmd-not-making-the-same-mistake-twice"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1036.html#mistakesmd-not-making-the-same-mistake-twice"&gt;mistakes.md: not making the same mistake twice&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;My favorite part of this setup is &lt;code&gt;Knowledge/mistakes.md&lt;/code&gt;, a log of mistakes. It gets special treatment — the read rule above requires it to be read at the start of every single session, no exceptions. Whenever I catch a mistake mid-session, it gets written down here as a rule.&lt;/p&gt;
&lt;p&gt;But if I let it log anything and everything, the table would balloon into something unusable. So I narrowed it down to three conditions for adding an entry.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It&amp;rsquo;s an explicit correction from me (not something it noticed on its own)&lt;/li&gt;
&lt;li&gt;It&amp;rsquo;s a pattern likely to recur (not a one-off fluke)&lt;/li&gt;
&lt;li&gt;It can be phrased as a concrete &amp;ldquo;do this / don&amp;rsquo;t do this&amp;rdquo;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The format is fixed too. Here&amp;rsquo;s one actual entry sitting in the log right now:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;2026-06-20: Stated an app&amp;#39;s UI feature from vague memory without checking
NG Action: Told the user Google Calendar has a &amp;#34;copy to&amp;#34; button, without verifying (it doesn&amp;#39;t exist)
Correct Action: Before describing whether a feature exists, check the current spec via official help docs etc.
Trigger: When explaining how to operate a specific app or whether it has a certain feature&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since adding this, the frequency of repeating the same kind of mistake has dropped noticeably. A correction now carries over into the next session, so I don&amp;rsquo;t have to nag about the same thing over and over. It&amp;rsquo;s a small thing, but it might be the single biggest stress reduction from this whole setup.&lt;/p&gt;
&lt;p&gt;And it doesn&amp;rsquo;t just sit there accumulating, either. While I was in the middle of a completely different task, Claude declared, unprompted, &amp;ldquo;Per the lesson in mistakes.md, I&amp;rsquo;ll write multi-line scripts to a file first&amp;rdquo; — and changed its own approach on the spot. That was a preemptive dodge of something I&amp;rsquo;d only scolded it for once before: breaking output with nested heredocs. The accumulated memory was actively changing its next move. At that point it stops feeling like external memory and starts feeling more like a colleague who learns from past failures.&lt;/p&gt;
&lt;h2 id="whats-actually-piled-up-so-far"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1036.html#whats-actually-piled-up-so-far"&gt;What&amp;rsquo;s actually piled up so far&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;After just a few days of use, the vault already has notes like these. All of them were written automatically, in the middle of working with Claude Code.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Knowledge/obsidian-launchd-backup-tcc.md&lt;/code&gt; — a writeup of why the vault&amp;rsquo;s automatic backup wasn&amp;rsquo;t working under macOS TCC (privacy protection). Root cause: processes launched from cron or launchd don&amp;rsquo;t inherit permission for anything under &lt;code&gt;~/Documents&lt;/code&gt;. Fixed by adding &lt;code&gt;/bin/bash&lt;/code&gt; and &lt;code&gt;/usr/bin/git&lt;/code&gt; — not the script itself — to Full Disk Access. The whole story is preserved as-is.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Knowledge/timetree-google-sync.md&lt;/code&gt; — technical notes on syncing TimeTree events to Google Calendar: from the fact that the official API has been shut down, down to the key detail that &lt;code&gt;events.import&lt;/code&gt; works as an upsert keyed on &lt;code&gt;iCalUID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Decisions/2026-06-20-timetree-sync-approach.md&lt;/code&gt; — the reasoning behind which of several approaches got picked for that sync&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The TCC note in particular will be genuinely useful the next time I run into that exact symptom. Something I struggled through once and solved can now live in the vault instead of relying on me remembering it.&lt;/p&gt;
&lt;h2 id="backing-up-the-memory-itself"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1036.html#backing-up-the-memory-itself"&gt;Backing up the memory itself&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The bigger this external brain grows, the scarier it gets to lose it. So I set the vault itself to auto-commit and push to git every 30 minutes. While building that, I ran into the same TCC issue — which is exactly how &lt;code&gt;obsidian-launchd-backup-tcc.md&lt;/code&gt; above came to exist. Building the memory system generated one more piece of memory along the way.&lt;/p&gt;
&lt;h2 id="this-very-post-is-an-example"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1036.html#this-very-post-is-an-example"&gt;This very post is an example&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The clearest example of all is this post itself. All I asked was &amp;ldquo;write about how you turned Obsidian into a knowledge base for Claude Code,&amp;rdquo; and Claude Code started by reading &lt;code&gt;mistakes.md&lt;/code&gt; and the profile, searched the vault for relevant keywords, pulled up the note about the source video and the TCC knowledge note, and only then started writing. It reported every single thing it read along the way.&lt;/p&gt;
&lt;p&gt;The read rules I set up ended up doubling as material-gathering for the post itself. External memory isn&amp;rsquo;t just for carrying context forward — it turns out to work as a primary source when writing, too.&lt;/p&gt;
&lt;h2 id="looking-back"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/21/1036.html#looking-back"&gt;Looking back&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Laid out plainly, none of this is a big deal. I made a few Markdown folders and wrote some read/write rules into a config file. No MCP, no plugins required.&lt;/p&gt;
&lt;p&gt;And yet an AI that used to explain everything from zero every time turned into a partner that can pick up where we left off. I think the trick is not over-engineering it up front. Connect it, run it, and keep adding small rules — &amp;ldquo;remember this,&amp;rdquo; &amp;ldquo;check the vault at session start&amp;rdquo; — and it gets a little smarter each time, almost like synapses forming. That &amp;ldquo;growing a brain&amp;rdquo; feeling is something I picked up from the video, but having actually done it, it&amp;rsquo;s real.&lt;/p&gt;</description></item><item><title>Testing obsidian-import's MarkItDown conversion against real files</title><link>https://nobu666.com/en/2026/06/19/1035.html</link><pubDate>Fri, 19 Jun 2026 20:29:00 +0900</pubDate><guid>https://nobu666.com/en/2026/06/19/1035.html</guid><description>&lt;p&gt;In &lt;a href="https://nobu666.com/en/2026/06/17/1034.html"&gt;a previous post&lt;/a&gt; I wrote about youtube-to-obsidian. Since then I&amp;rsquo;ve wanted to turn PDFs and slide decks into notes the same way, so I wired in Microsoft&amp;rsquo;s MarkItDown to handle them. It&amp;rsquo;s no longer YouTube-only, so I renamed the repo to &lt;a href="https://github.com/nobu666/obsidian-import"&gt;obsidian-import&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now I can pull long academic PDFs, 100-plus-slide decks, lengthy blog posts, and TED talks into Obsidian as summary notes without reading or watching the whole thing. Instead of sitting through a one-hour video, I can skim a well-organized note in a few minutes. The input efficiency is on a different level.&lt;/p&gt;
&lt;p&gt;That said, when I first built the feature I only wrote mock tests — I&amp;rsquo;d never actually thrown a real PDF or PPTX at it. This post is about squashing that gap.&lt;/p&gt;
&lt;h2 id="an-immediate-error-on-pdf"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/19/1035.html#an-immediate-error-on-pdf"&gt;An immediate error on PDF&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I fed it a PDF I had lying around, and got this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;変換エラー: PdfConverter recognized the input as a potential .pdf file,
but the dependencies needed to read .pdf files have not been installed.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;MarkItDown uses pdfminer for PDF handling, but that&amp;rsquo;s an optional dependency — &lt;code&gt;pip install markitdown&lt;/code&gt; alone doesn&amp;rsquo;t pull it in. You need &lt;code&gt;pip install markitdown[pdf]&lt;/code&gt; or &lt;code&gt;markitdown[all]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;My install.sh was only installing bare &lt;code&gt;markitdown&lt;/code&gt;, so I fixed it to install &lt;code&gt;markitdown[all]&lt;/code&gt;. DOCX, PPTX, XLSX, and the rest have the same kind of optional dependencies, so if you want to support every format, it&amp;rsquo;s simplest to just install &lt;code&gt;all&lt;/code&gt; from the start.&lt;/p&gt;
&lt;h2 id="test-results-by-format"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/19/1035.html#test-results-by-format"&gt;Test results by format&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;After fixing the dependencies, I ran it against real files and dummy data for each format.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PDF&lt;/td&gt;
&lt;td&gt;Resume / work history&lt;/td&gt;
&lt;td&gt;Japanese text extracted correctly. Lots of FontBBox warnings, but no effect on the output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DOCX&lt;/td&gt;
&lt;td&gt;Dummy document&lt;/td&gt;
&lt;td&gt;Headings and paragraphs converted to Markdown correctly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PPTX&lt;/td&gt;
&lt;td&gt;Dummy slides&lt;/td&gt;
&lt;td&gt;Converted with slide numbers preserved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;XLSX&lt;/td&gt;
&lt;td&gt;Dummy table&lt;/td&gt;
&lt;td&gt;Converted to a Markdown table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;URL&lt;/td&gt;
&lt;td&gt;example.com&lt;/td&gt;
&lt;td&gt;HTML to Markdown conversion worked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image&lt;/td&gt;
&lt;td&gt;JPG/PNG&lt;/td&gt;
&lt;td&gt;Rejected by the minimum-character-count check since there&amp;rsquo;s no text (working as intended)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The FontBBox warnings on PDF are a known pdfminer quirk that shows up with PDFs that have incomplete font info. It floods stderr and looks alarming, but it doesn&amp;rsquo;t affect the conversion result.&lt;/p&gt;
&lt;h2 id="confirmed-duplicate-skipping-too"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/19/1035.html#confirmed-duplicate-skipping-too"&gt;Confirmed duplicate skipping too&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I converted the same URL twice, and the second run was skipped as expected.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[1/1] https://example.com
スキップ（処理済み）&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;convert.py names files by the MD5 hash of the source, and skips conversion if a file with that name already exists in &lt;code&gt;.transcripts/&lt;/code&gt;. This was already covered by mock tests, but it was good to confirm it holds up in practice too.&lt;/p&gt;
&lt;h2 id="what-i-fixed"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/19/1035.html#what-i-fixed"&gt;What I fixed&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The actual change was just three spots specifying the dependency package. README.md and CLAUDE.md also mention MarkItDown, but only as a tool name reference, so those didn&amp;rsquo;t need updating.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;install.sh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;markitdown&lt;/code&gt; → &lt;code&gt;markitdown[all]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SKILL.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.claude/commands/obsidian-import.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same (a copy of SKILL.md)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="you-only-find-this-by-using-it-yourself"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/19/1035.html#you-only-find-this-by-using-it-yourself"&gt;You only find this by using it yourself&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This particular problem was the kind mock tests can&amp;rsquo;t catch. As long as you&amp;rsquo;re mocking the return value of &lt;code&gt;MarkItDown().convert()&lt;/code&gt;, there&amp;rsquo;s no way to test whether a missing dependency blows up at import time.&lt;/p&gt;
&lt;p&gt;Installing the full &lt;code&gt;markitdown[all]&lt;/code&gt; dependency set in CI would make it heavier, and bundling real PDF and PPTX files in the repo feels off too. So I left the mock tests as they are, and made real-file integration checks something I run manually on my own machine.&lt;/p&gt;
&lt;p&gt;In the end, you don&amp;rsquo;t really know your own tool works until you use it yourself. Tests can all pass and it still falls over the moment you throw a real PDF at it. Same story as my previous post — the switch to caption-first only became obvious once I actually tried it on a real lecture video. Mocks and CI are good for a baseline, but nothing beats using the thing yourself as an actual user.&lt;/p&gt;</description></item><item><title>I open-sourced my YouTube-to-Obsidian converter and turned it into a general-purpose tool</title><link>https://nobu666.com/en/2026/06/17/1034.html</link><pubDate>Wed, 17 Jun 2026 19:30:00 +0900</pubDate><guid>https://nobu666.com/en/2026/06/17/1034.html</guid><description>&lt;p&gt;&lt;a href="https://nobu666.com/en/2026/06/16/1033.html"&gt;Last time&lt;/a&gt;, I built a pipeline that automatically converts YouTube cooking videos into Obsidian recipe notes. It was just two scripts dropped straight into &lt;code&gt;~/scripts/&lt;/code&gt;, but it got me through 43 cooking videos. Since then I worked with Claude Code on a code review, added tests, set up CI, published it on GitHub, and eventually rebuilt it from a recipe-only tool into a general-purpose one. Here&amp;rsquo;s how that went.&lt;/p&gt;
&lt;h2 id="code-review-and-fixes"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/17/1034.html#code-review-and-fixes"&gt;Code review and fixes&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;First I had Claude Code review the existing scripts. The main issues it flagged:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The transcript file write wasn&amp;rsquo;t atomic (a crash mid-write would leave a corrupted file behind)&lt;/li&gt;
&lt;li&gt;Error handling was sloppy (the script could exit cleanly even if everything failed)&lt;/li&gt;
&lt;li&gt;The mlx-whisper import check was slow&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I fixed the atomic write with the standard pattern: write to a temp file via &lt;code&gt;tempfile.mkstemp&lt;/code&gt;, then &lt;code&gt;rename&lt;/code&gt; it into place. I also fixed the exit codes for error cases.&lt;/p&gt;
&lt;h2 id="tests-and-ci"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/17/1034.html#tests-and-ci"&gt;Tests and CI&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I mocked out the external dependencies (yt-dlp, mlx-whisper, the filesystem) and wrote 25 tests. It runs on pytest + GitHub Actions. Since mlx-whisper is Apple Silicon only, I don&amp;rsquo;t install it in CI — the whole thing is verified through mocks instead.&lt;/p&gt;
&lt;p&gt;One thing that quietly tripped me up: my mock transcripts were too short and kept getting flagged by the hallucination detector. I had the mock returning a short string like &amp;ldquo;こんにちは&amp;rdquo; (&amp;ldquo;hello&amp;rdquo;), and anything under 50 characters gets automatically treated as a hallucination.&lt;/p&gt;
&lt;h2 id="dealing-with-whisper-hallucinations"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/17/1034.html#dealing-with-whisper-hallucinations"&gt;Dealing with Whisper hallucinations&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Once I actually ran all 43 videos through it, a few of them triggered Whisper&amp;rsquo;s classic &amp;ldquo;hallucination&amp;rdquo; failure mode — the same phrase repeated endlessly. It tends to happen during silence or ambient-noise-only stretches.&lt;/p&gt;
&lt;p&gt;To guard against it, I added an &lt;code&gt;is_hallucinated()&lt;/code&gt; function that checks three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Text under 50 characters (too short)&lt;/li&gt;
&lt;li&gt;A regex pattern matching the same phrase repeated 5+ times in a row&lt;/li&gt;
&lt;li&gt;Punctuation/symbol ratio over 80% (no actual content)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="operational-tweaks"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/17/1034.html#operational-tweaks"&gt;Operational tweaks&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;A handful of small changes that made a real difference once I started actually using this day to day.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Process one video at a time&lt;/strong&gt; — I originally fed all the transcripts to the Claude CLI in one batch, but it would just stall out. Switched to a loop that handles one file at a time: on success it moves the file to a &lt;code&gt;done&lt;/code&gt; directory, on failure it stays put so it can be retried&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Make Ctrl-C actually stop it&lt;/strong&gt; — once it was a loop, hitting Ctrl-C would just skip to the next file instead of stopping. Added a &lt;code&gt;trap INT&lt;/code&gt; to catch the signal and exit immediately&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep the Mac from overheating&lt;/strong&gt; — running Whisper and the Claude CLI at the same time spins the fans up nonstop. I lowered the priority with &lt;code&gt;nice -n 10&lt;/code&gt; and used &lt;code&gt;caffeinate -i&lt;/code&gt; to prevent sleep&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Play nice with Obsidian&lt;/strong&gt; — a folder named &lt;code&gt;_transcripts/&lt;/code&gt; was showing up in Obsidian&amp;rsquo;s file explorer. Renamed it to &lt;code&gt;.transcripts/&lt;/code&gt; so the dot-prefix hides it&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="publishing-on-github"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/17/1034.html#publishing-on-github"&gt;Publishing on GitHub&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I pushed the whole thing to GitHub as &lt;a href="https://github.com/nobu666/youtube-to-obsidian"&gt;youtube-to-obsidian&lt;/a&gt;. &lt;code&gt;~/scripts/&lt;/code&gt; now just holds a symlink into the repo. I also wrote an install script so setup is a single &lt;code&gt;curl&lt;/code&gt; command.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -fsSL https://raw.githubusercontent.com/nobu666/youtube-to-obsidian/main/install.sh | bash&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It can also be registered as a Claude Code skill (global command), so I can invoke it with &lt;code&gt;/youtube-to-obsidian&lt;/code&gt; from any project.&lt;/p&gt;
&lt;h2 id="from-recipe-only-to-general-purpose"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/17/1034.html#from-recipe-only-to-general-purpose"&gt;From recipe-only to general-purpose&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;At the point I published it, the command was still called &lt;code&gt;recipe&lt;/code&gt; and the prompt was hardcoded for cooking videos. But the transcription part is completely generic — swap out the conversion prompt and it works for anything, not just recipes. So I generalized it.&lt;/p&gt;
&lt;h3 id="switching-command-names-and-prompts"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/17/1034.html#switching-command-names-and-prompts"&gt;Switching command names and prompts&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Renamed the command from &lt;code&gt;recipe&lt;/code&gt; to &lt;code&gt;youtube-to-obsidian&lt;/code&gt;. Also cleaned up variable names to be generic, e.g. &lt;code&gt;RECIPE_DIR&lt;/code&gt; → &lt;code&gt;OUTPUT_DIR&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Prompts now live as separate files under a &lt;code&gt;prompts/&lt;/code&gt; directory, selectable with a &lt;code&gt;-p&lt;/code&gt; flag.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Default (general-purpose note format)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;youtube-to-obsidian https://www.youtube.com/watch?v&lt;span style="color:#f92672"&gt;=&lt;/span&gt;XXXXX
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Recipe&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;youtube-to-obsidian -p recipe https://www.youtube.com/watch?v&lt;span style="color:#f92672"&gt;=&lt;/span&gt;XXXXX
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Lecture notes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;youtube-to-obsidian -p lecture https://www.youtube.com/playlist?list&lt;span style="color:#f92672"&gt;=&lt;/span&gt;XXXXX&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I ended up with five prompt types.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prompt&lt;/th&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;Example output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;default&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;General purpose (structured notes)&lt;/td&gt;
&lt;td&gt;Summary + sectioned content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;recipe&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cooking video → recipe&lt;/td&gt;
&lt;td&gt;Ingredient list + steps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lecture&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lecture/seminar → summary notes&lt;/td&gt;
&lt;td&gt;Overview + key points + detailed notes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;workout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strength training/yoga → workout plan&lt;/td&gt;
&lt;td&gt;Exercise table + form notes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tool&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tool walkthrough → how-to guide&lt;/td&gt;
&lt;td&gt;Setup + usage + tips&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="routing-output-per-prompt"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/17/1034.html#routing-output-per-prompt"&gt;Routing output per prompt&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I didn&amp;rsquo;t want recipes, lecture notes, and workout plans all dumped into the same folder, so I made it possible to specify the output destination right in the prompt file&amp;rsquo;s header.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;output_dir: ~/Documents/Obsidian/Vault/YouTube/レシピ
---
上の文字起こしをObsidianレシピ形式に変換して...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;output_dir:&lt;/code&gt; header sets the destination, and everything after the &lt;code&gt;---&lt;/code&gt; is the prompt body that gets passed to Claude. The target folder is created automatically if it doesn&amp;rsquo;t exist, and a &lt;code&gt;-o&lt;/code&gt; flag lets you override it for a one-off run.&lt;/p&gt;
&lt;h3 id="the-overall-structure"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/17/1034.html#the-overall-structure"&gt;The overall structure&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;In the previous post this was a simple linear pipeline. With prompt switching and output routing added, it now looks like this.&lt;/p&gt;
&lt;img src="https://nobu666.com/images/1034-architecture.svg" alt="Diagram of youtube-to-obsidian's structure: YouTube → transcript → Claude CLI + prompt → Obsidian folders" width="680"&gt;
&lt;h2 id="going-fast-by-preferring-subtitles"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/17/1034.html#going-fast-by-preferring-subtitles"&gt;Going fast by preferring subtitles&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Once I generalized it, I tried it on a lecture video and hit a wall. Whisper transcription was way too slow — over 10 minutes for a 20-minute video. That was borderline tolerable for 5-10 minute cooking videos, but useless for an hour-long lecture.&lt;/p&gt;
&lt;p&gt;The fix was simple: fetch YouTube&amp;rsquo;s subtitles first. Most Japanese-language videos have either manual or auto-generated captions available. When subtitles exist, fetching them takes seconds, and Whisper barely gets invoked anymore.&lt;/p&gt;
&lt;img src="https://nobu666.com/images/1034-fallback.svg" alt="Transcript fallback chain: YouTube subtitles → Whisper → video description" width="680"&gt;
&lt;p&gt;I also switched the Whisper model from large-v3 to large-v3-turbo. In the previous post I wrote about upgrading from medium to large-v3 for accuracy, but large-v3 is slow. large-v3-turbo keeps nearly the same accuracy while running much faster. With subtitles now preferred, Whisper rarely even runs — but it&amp;rsquo;s still nice to have a fast fallback.&lt;/p&gt;
&lt;h2 id="trying-it-out-for-real"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/17/1034.html#trying-it-out-for-real"&gt;Trying it out for real&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I tested it on three kinds of videos.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tool walkthrough (obsidian-skills)&lt;/strong&gt; — subtitles fetched, transcript done in seconds. Came out as a clean note with setup steps, usage, and tips clearly separated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Workout video (ab superset routine)&lt;/strong&gt; — subtitles fetched. Output was an exercise table plus form notes. It even picked up practical warnings like &amp;ldquo;never bend your knees — it kills the effectiveness.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lecture (Takafumi Horie&amp;rsquo;s 1-hour talk at Takushoku University)&lt;/strong&gt; — subtitles fetched, transcription was fast, but structuring it via the Claude CLI took 2-3 minutes. Output was organized into an overview, key points, detailed notes, and quotes — a full hour-long talk cleanly organized. If it had started from a Whisper transcription instead, it would have taken over 30 minutes, so this is exactly the kind of long-form video where preferring subtitles pays off the most.&lt;/p&gt;
&lt;h2 id="looking-back"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/17/1034.html#looking-back"&gt;Looking back&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;What was a recipe-only script dumped in &lt;code&gt;~/scripts/&lt;/code&gt; when I wrote the last post is now a published, general-purpose tool on GitHub. Review, tests, CI, renaming the command, building a prompt system, preferring subtitles, switching models — all of it happened through conversations with Claude Code.&lt;/p&gt;
&lt;p&gt;The subtitle-first change in particular is something I never would have noticed without actually trying a lecture video. As long as I was only using it for cooking videos, Whisper&amp;rsquo;s speed was never a problem — it took broadening the use case to even see the bottleneck. &amp;ldquo;Generalize it, discover a new problem, solve that, and it gets even better&amp;rdquo; might be one of the most fun parts of building things.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nobu666/youtube-to-obsidian"&gt;youtube-to-obsidian&lt;/a&gt; — works on any Apple Silicon Mac with Claude Code installed. Add your own prompt and it&amp;rsquo;ll turn any video into an Obsidian note, so feel free to customize it for your own use case.&lt;/p&gt;</description></item><item><title>I Built a Pipeline That Turns 40 YouTube Cooking Videos into Obsidian Recipe Notes</title><link>https://nobu666.com/en/2026/06/16/1033.html</link><pubDate>Tue, 16 Jun 2026 18:30:00 +0900</pubDate><guid>https://nobu666.com/en/2026/06/16/1033.html</guid><description>&lt;p&gt;My YouTube &amp;ldquo;watch and cook later&amp;rdquo; playlist had ballooned to 43 videos. I kept saving cooking videos and never actually using them — every time I stood in the kitchen I&amp;rsquo;d end up pulling out my phone and reopening YouTube, wondering &amp;ldquo;wait, how did that recipe go again?&amp;rdquo; I&amp;rsquo;ve lost count of how many times I&amp;rsquo;ve done that. I already manage my recipes in Obsidian, so if I could just convert the contents of these videos into Obsidian notes, the problem would go away. I gave it a shot with Cowork (the desktop version of Claude), and the process turned out to have more twists and turns than I expected — worth writing down.&lt;/p&gt;
&lt;h2 id="first-i-tried-one-video-by-hand"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/16/1033.html#first-i-tried-one-video-by-hand"&gt;First, I tried one video by hand&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I started simple: I handed Cowork a video URL and asked it to &amp;ldquo;put this recipe into Obsidian.&amp;rdquo; It was a video of chef Masahiro Kasahara making homemade nametake (simmered mushrooms). Cowork can open YouTube through a Chrome extension, so it read the description, searched the web for supplementary details, and somehow managed to produce a recipe from that one video.&lt;/p&gt;
&lt;p&gt;But this approach has a fatal flaw: the token cost per video is brutal. Browser operations, page fetches, web searches, text formatting — do that 43 times and you&amp;rsquo;ll hit the session limit with room to spare. I needed a different approach.&lt;/p&gt;
&lt;h2 id="transcribing-locally-with-whisper"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/16/1033.html#transcribing-locally-with-whisper"&gt;Transcribing locally with Whisper&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The part burning through all those tokens is &amp;ldquo;understanding what&amp;rsquo;s in the video.&amp;rdquo; If I could handle that locally, all I&amp;rsquo;d need to hand Claude would be a short block of text.&lt;/p&gt;
&lt;p&gt;I wasn&amp;rsquo;t sure my MacBook Neo (A18 Pro / 8GB) could handle it, but Whisper&amp;rsquo;s medium model (750MB) turned out to run comfortably. Thanks to Apple Silicon&amp;rsquo;s Neural Engine, a 12-minute video transcribes in one to three minutes.&lt;/p&gt;
&lt;p&gt;I used two tools: yt-dlp (to pull just the audio from YouTube) and mlx-whisper (a Whisper implementation optimized for Apple Silicon).&lt;/p&gt;
&lt;p&gt;The setup wasn&amp;rsquo;t exactly smooth, though. First, yt-dlp requires ffmpeg, so I installed that too. Then, trying to install mlx-whisper, I found pip was outdated and needed an upgrade first. On top of that, the system Python was 3.9, and mlx-whisper requires 3.10+. Installing Python 3.12 via Homebrew ran straight into PEP 668&amp;rsquo;s &amp;ldquo;externally-managed-environment&amp;rdquo; restriction. In the end, setting up a venv was what actually solved it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install yt-dlp ffmpeg python@3.12
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python3.12 -m venv ~/scripts/.venv
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;source ~/scripts/.venv/bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install mlx-whisper&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As always, the time sink is the environment setup you have to get through before you even reach the actual task.&lt;/p&gt;
&lt;h2 id="hitting-the-wall-of-coworks-sandbox"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/16/1033.html#hitting-the-wall-of-coworks-sandbox"&gt;Hitting the wall of Cowork&amp;rsquo;s sandbox&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;At first I tried running both yt-dlp and Whisper inside Cowork&amp;rsquo;s Linux sandbox, but access to YouTube got blocked by the proxy. Apparently the sandbox isn&amp;rsquo;t allowed to freely reach external sites. So I ended up running the audio download and transcription locally on the Mac, and only passing the resulting text to Cowork.&lt;/p&gt;
&lt;h2 id="i-tried-ollama-then-gave-up-on-it"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/16/1033.html#i-tried-ollama-then-gave-up-on-it"&gt;I tried Ollama, then gave up on it&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I wanted to keep the conversion from transcript to recipe format local too, so I tried running gemma3:4b through Ollama. It technically worked, but the accuracy of pulling ingredients and steps out of a cooking transcript wasn&amp;rsquo;t great — especially around catching spoken quantities and separating actual recipe content from small talk.&lt;/p&gt;
&lt;p&gt;In the end, I settled on a division of labor: local Whisper for the heavy lifting (transcription), and Claude for the intelligent part (formatting into a recipe). Leave it to the specialists.&lt;/p&gt;
&lt;h2 id="turning-it-into-a-cowork-skill"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/16/1033.html#turning-it-into-a-cowork-skill"&gt;Turning it into a Cowork skill&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Cowork has a mechanism called &amp;ldquo;skills.&amp;rdquo; You write out how to do a task in Markdown, and from then on you just say something like &amp;ldquo;turn this into a recipe&amp;rdquo; and it follows that skill.&lt;/p&gt;
&lt;p&gt;The skill I built here is simple: it reads the transcript text sitting in &lt;code&gt;_transcripts/&lt;/code&gt;, converts it to match the format of my existing recipes (frontmatter + ingredient list + steps), and saves it into the Obsidian folder. The skill also has real examples of existing recipes embedded in it, which keeps the output format consistent.&lt;/p&gt;
&lt;h2 id="fully-automating-it-with-the-claude-cli"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/16/1033.html#fully-automating-it-with-the-claude-cli"&gt;Fully automating it with the Claude CLI&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The last piece was the Claude Code CLI. Since you can pass it a prompt from the terminal with &lt;code&gt;claude -p&lt;/code&gt;, I chained the whole thing together in a shell script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;URL&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;1&lt;span style="color:#66d9ef"&gt;:-&lt;/span&gt;https://www.youtube.com/playlist?list=PLh4huXs8Qi9HkMIXptPbw2gIYUDNtqNic&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Step 1: Transcribe locally (progress is visible)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$HOME&lt;span style="color:#e6db74"&gt;/scripts/.venv/bin/python3&amp;#34;&lt;/span&gt; ~/scripts/transcribe.py &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$URL&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt; $? -ne &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Transcription failed. Aborting.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; exit &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Step 2: Turn it into recipes with the Claude CLI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;claude -p &lt;span style="color:#e6db74"&gt;&amp;#34;Convert the .txt files in _transcripts into Obsidian recipe format and save them&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In Step 1, Whisper&amp;rsquo;s progress shows up as &lt;code&gt;[1/43]&lt;/code&gt;, &lt;code&gt;[2/43]&lt;/code&gt;, and so on, and in Step 2 the Claude CLI batch-converts everything into recipes. If transcription fails across the board, it aborts before reaching Step 2. Early on I hit a trap where the Claude CLI would still run even after an error, burning API costs for nothing. Now a single run of &lt;code&gt;~/scripts/recipe&lt;/code&gt; turns the entire playlist into Obsidian recipe notes.&lt;/p&gt;
&lt;h2 id="the-final-architecture"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/16/1033.html#the-final-architecture"&gt;The final architecture&lt;/a&gt;
&lt;/h2&gt;
&lt;img src="https://nobu666.com/images/1033-architecture.svg" alt="Flow diagram from a YouTube playlist to Obsidian recipe notes" width="680"&gt;
&lt;p&gt;The heavy lifting (audio to text) runs on the Mac&amp;rsquo;s Neural Engine, and the intelligent work (text to structured recipe) runs on Claude — each handling what it&amp;rsquo;s best at.&lt;/p&gt;
&lt;h2 id="takeaways"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/16/1033.html#takeaways"&gt;Takeaways&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;What started as a simple wish — &amp;ldquo;I want to get YouTube recipes into Obsidian&amp;rdquo; — turned out to run into a string of obstacles once I actually tried it: Cowork&amp;rsquo;s sandbox has network restrictions, YouTube&amp;rsquo;s caption API isn&amp;rsquo;t straightforward to pull from, and local LLM accuracy falls short depending on the task. On top of that, I stepped on all the usual environment-setup landmines, like Python version conflicts and needing a venv. In the end, I landed on a combination of local Whisper plus the Claude CLI.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s interesting is that this whole process of trial and error unfolded as a conversation with Cowork. Whenever an error came up, I&amp;rsquo;d paste the terminal output, Cowork would suggest the next move, and I&amp;rsquo;d try again. As a debugging pair-programming partner, it was genuinely solid. Now that the 43-video playlist is sorted, maybe I&amp;rsquo;ll end up cooking more. Or maybe I won&amp;rsquo;t.&lt;/p&gt;</description></item><item><title>Life Update</title><link>https://nobu666.com/en/2026/06/15/1032.html</link><pubDate>Mon, 15 Jun 2026 21:30:56 +0900</pubDate><guid>https://nobu666.com/en/2026/06/15/1032.html</guid><description>&lt;p&gt;Now that the blog is finally back from the dead, I figured I&amp;rsquo;d write a life update while I&amp;rsquo;m at it. Covering eight years in one post is a bit much, so here are a few of the bigger things that happened, roughly in order.&lt;/p&gt;
&lt;h2 id="i-changed-jobs-twice-more"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/15/1032.html#i-changed-jobs-twice-more"&gt;I changed jobs twice more&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Since my last post, I&amp;rsquo;ve changed jobs two more times. Just counting the number might make me look fickle, but the criteria I use to pick a company have stayed consistent the whole time. One is: &amp;ldquo;how much benefit can this company realistically bring to Japan as a country?&amp;rdquo; The other is: &amp;ldquo;can I genuinely get behind what this company is trying to do, as if it were my own cause?&amp;rdquo; I&amp;rsquo;ve only moved when I could nod confidently on both counts. The jobs kept changing, but the yardstick never did.&lt;/p&gt;
&lt;h2 id="i-sold-the-porsche"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/15/1032.html#i-sold-the-porsche"&gt;I sold the Porsche&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I let go of the 996 Carrera 4S I mentioned buying &amp;ldquo;on a whim&amp;rdquo; back then. It wasn&amp;rsquo;t that I wanted to sell it — there were circumstances involved that I&amp;rsquo;ll leave out here. It wasn&amp;rsquo;t what I wanted. The one silver lining is that it sold for more than I paid. Classic car prices have been climbing, so between all the fun I had owning it and coming out ahead financially, it wasn&amp;rsquo;t a total loss. Still, letting go of a car I loved, and not by choice, left a certain emptiness.&lt;/p&gt;
&lt;h2 id="syrup-passed-away"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/15/1032.html#syrup-passed-away"&gt;Syrup passed away&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Our Maine Coon, Syrup, passed away too. I&amp;rsquo;m leaving here, unedited, what I wrote on Facebook the day it happened.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Our Maine Coon Syrup passed away today. He was 10 years old. In May, his left front leg suddenly became paralyzed, and even after tests at the vet, we couldn&amp;rsquo;t pin down the cause. The paralysis gradually spread until his whole body except his head and neck was affected, and he became bedridden.&lt;/p&gt;
&lt;p&gt;His vet referred us to a large university animal hospital, where he was examined two weeks ago. General anesthesia was too risky, so he had an MRI and CT scan without it. But without a spinal fluid sample, a definitive diagnosis wasn&amp;rsquo;t possible — the two leading suspects were lymphoma or meningitis — so we continued with medication in the meantime.&lt;/p&gt;
&lt;p&gt;Today we brought him back to the university hospital for a follow-up. They ran blood tests again and scheduled a spinal fluid draw under general anesthesia for two weeks out, and we headed home.&lt;/p&gt;
&lt;p&gt;When I got him out of the car and opened his carrier, he had already stopped breathing. I called our regular vet and rushed him in, but he couldn&amp;rsquo;t be revived. The vet thought he&amp;rsquo;d likely had a seizure in the car and choked on blood-tinged sputum.&lt;/p&gt;
&lt;p&gt;He used to weigh over 9kg, but once the paralysis set in he lost 3kg. If there&amp;rsquo;s such a thing as an afterlife, I hope he&amp;rsquo;s eating his fill — he always did love his food.&lt;/p&gt;
&lt;p&gt;Syrup, I&amp;rsquo;m sorry I couldn&amp;rsquo;t save you. Thank you for everything.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="i-got-a-home-battery-essentially-for-free"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/15/1032.html#i-got-a-home-battery-essentially-for-free"&gt;I got a home battery, essentially for free&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;After something that heavy, let me throw in something more mundane. We installed a solar battery storage system at home. Tokyo&amp;rsquo;s subsidy for it turned out to be generous enough that we got it installed at essentially no cost. It helps with electricity bills and gives us a safety net in a disaster, and with a subsidy that good, there was no reason not to. Things like this are best taken advantage of while they last.&lt;/p&gt;
&lt;h2 id="a-trip-to-sapporo-for-my-nephews-wedding"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/15/1032.html#a-trip-to-sapporo-for-my-nephews-wedding"&gt;A trip to Sapporo for my nephew&amp;rsquo;s wedding&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I went to Sapporo for my nephew&amp;rsquo;s wedding. It had been a while since I&amp;rsquo;d been there, and a while since the whole family had gotten together. Celebrations like that are always good to be at. Seeing the nephew who used to be so small standing up front in a suit made me feel my own age.&lt;/p&gt;
&lt;h2 id="my-mother-moved-into-a-care-facility"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/15/1032.html#my-mother-moved-into-a-care-facility"&gt;My mother moved into a care facility&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;My mother has dementia and is certified as needing long-term care. Her memories from long ago are mostly intact, but she can barely hold onto short-term memory — she won&amp;rsquo;t remember a conversation from a few minutes earlier. There were even times she looked at her own eldest daughter and didn&amp;rsquo;t know who she was. Until recently, my father had been taking care of her almost entirely on his own, handling every aspect of it.&lt;/p&gt;
&lt;p&gt;Then my father was hospitalized, and with no one left to look after her, we had no choice but to move her into a care facility. That&amp;rsquo;s when it really hit me just how much my father had been doing, day in and day out. That level of devotion — there&amp;rsquo;s really no other word for it than remarkable.&lt;/p&gt;
&lt;h2 id="my-father-passed-away"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/15/1032.html#my-father-passed-away"&gt;My father passed away&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;And then my father passed away. I attended the wake and the funeral, and for the first time in my life went through the process of carrying out someone&amp;rsquo;s will.&lt;/p&gt;
&lt;p&gt;My father had carefully set aside enough money to make sure my mother&amp;rsquo;s care would be well provided for after he was gone. He&amp;rsquo;d thought ahead and made the arrangements — genuinely something to respect.&lt;/p&gt;
&lt;p&gt;My older brother handled all the funeral procedures and logistics. Standing next to him, all I could feel was my own uselessness. At the same time, watching him calmly shoulder everything made me realize he&amp;rsquo;s remarkable too. You don&amp;rsquo;t usually notice it day to day, but moments like this are when a person&amp;rsquo;s true measure shows.&lt;/p&gt;
&lt;p&gt;The 49-day memorial has passed, and recently we divided up his belongings. I took his coat, his belt, and a cut-glass tumbler. I imagine I&amp;rsquo;ll think of him every time I pour a drink into that glass. That feels about right.&lt;/p&gt;
&lt;h2 id="so-thats-about-it"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/15/1032.html#so-thats-about-it"&gt;So, that&amp;rsquo;s about it&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Writing it all out like this, I realize the past few years brought a lot at once — good and bad alike. I changed jobs, let go of a car I&amp;rsquo;d driven for years, said goodbye to a cat and to my father, and my mother moved into a care facility. Some things I gained, and some things I&amp;rsquo;ll never get back.&lt;/p&gt;
&lt;p&gt;Even so, life goes on, and I&amp;rsquo;m doing okay. Managing to restart a blog I&amp;rsquo;d left untouched for eight years feels like a kind of turning point in itself. Next time, I&amp;rsquo;d like to write about something lighter — and hopefully a bit more often.&lt;/p&gt;</description></item><item><title>I Let Claude Casually Fix My Blog After 8 Years of Neglect</title><link>https://nobu666.com/en/2026/06/12/1031.html</link><pubDate>Fri, 12 Jun 2026 00:13:37 +0900</pubDate><guid>https://nobu666.com/en/2026/06/12/1031.html</guid><description>&lt;p&gt;Turns out my last update was back in 2018. Eight years. I&amp;rsquo;d left it sitting there for eight years. When I finally opened my own blog again, it wasn&amp;rsquo;t even rendering properly anymore. The build was permanently broken, every photo in every post was gone, and the code blocks were a mess. I thought this was my castle. Turned out it was a ruin.&lt;/p&gt;
&lt;p&gt;I couldn&amp;rsquo;t just leave it like that, so I threw a lazy request at Claude Code: &amp;ldquo;CircleCI&amp;rsquo;s failing, can you fix it with GitHub Actions?&amp;rdquo; It ended up doing about three times more than I expected, so I&amp;rsquo;m writing it down.&lt;/p&gt;
&lt;h2 id="the-build-wasnt-even-passing"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/12/1031.html#the-build-wasnt-even-passing"&gt;The build wasn&amp;rsquo;t even passing&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Claude looked into it and found that CircleCI had already been migrated to GitHub Actions long ago — except that Actions setup was broken too. I don&amp;rsquo;t even remember doing the migration. The Hugo version was pinned to 0.74. Zero point seventy-four. From 2020. There was no way the current theme would work with that. On top of that, the submodule URL was pointing at &lt;code&gt;git@github.com&lt;/code&gt; over SSH, so CI couldn&amp;rsquo;t fetch it without a key.&lt;/p&gt;
&lt;p&gt;Claude tracked down all of this on its own, bumped Hugo, switched the submodule to HTTPS, and got everything building and deploying again like it was nothing. And all I&amp;rsquo;d said was &amp;ldquo;CircleCI&amp;rsquo;s failing.&amp;rdquo;&lt;/p&gt;
&lt;h2 id="every-photo-had-vanished"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/12/1031.html#every-photo-had-vanished"&gt;Every photo had vanished&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Once the build was passing, the next problem showed up: not a single photo appeared in any post. There were two causes. First, Hugo&amp;rsquo;s Markdown engine had been updated, and it now strips raw HTML written directly in the body by default for safety — and past-me had hardcoded &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags everywhere. Second, the images were referenced over &lt;code&gt;http://&lt;/code&gt;, which gets blocked as mixed content on an HTTPS site.&lt;/p&gt;
&lt;p&gt;I hadn&amp;rsquo;t asked it to fix posts one by one, either. It went and found every affected post on its own (apparently around 60 of them), switched both the Flickr and Amazon product images over to HTTPS, and brought them all back in one pass. For the Amazon ad widgets that had long since been discontinued, it swapped them out for links to product pages that were actually still alive. A small thing, but I appreciated it.&lt;/p&gt;
&lt;h2 id="the-wordpress-era-debt-kept-surfacing"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/12/1031.html#the-wordpress-era-debt-kept-surfacing"&gt;The WordPress-era debt kept surfacing&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The deeper it dug, the more of my own past sins turned up. Code blocks that opened with &lt;code&gt;[sourcecode]&lt;/code&gt; and closed with ``` , breaking the rendering entirely (leftover WordPress shortcodes). Headings written as raw &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; instead of Markdown. The best one: roughly 350 old daily-roundup posts from delicious — the social bookmarking service (younger readers may not know it, and it&amp;rsquo;s long gone now) — all still sitting there full of dead links. I really had let that pile up. Claude cleaned all of it up too, without a word of complaint.&lt;/p&gt;
&lt;h2 id="and-then-it-modernized-things-while-it-was-at-it"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/12/1031.html#and-then-it-modernized-things-while-it-was-at-it"&gt;And then it modernized things while it was at it&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;What started as just a fix turned into a bunch of extras along the way: prev/next navigation between posts, switching the homepage from full-text posts to summaries with a &amp;ldquo;read more&amp;rdquo; link, site search (just redirects to Google&amp;rsquo;s &lt;code&gt;site:&lt;/code&gt; search — no index or server needed), a monthly archive sidebar, an RSS icon. It even fixed the sitemap to use absolute URLs so Search Console would stop complaining. It&amp;rsquo;s clearly a lot faster at this than I was eight years ago.&lt;/p&gt;
&lt;h2 id="the-part-that-impressed-me-most"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2026/06/12/1031.html#the-part-that-impressed-me-most"&gt;The part that impressed me most&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Once everything was done, I asked it to &amp;ldquo;check with me before pushing straight to master,&amp;rdquo; and it went and wrote a hook to enforce that. From now on it actually stops and asks before pushing. It&amp;rsquo;s a little unsettling how it doesn&amp;rsquo;t just do what you ask — it goes and reshapes the whole environment to match how you work.&lt;/p&gt;
&lt;p&gt;Eight years of neglect and the scars of a WordPress migration, cleared up almost entirely through casual chat. I&amp;rsquo;ll admit it: I didn&amp;rsquo;t fix a single line of code myself in this whole process. All I did was sit there typing &amp;ldquo;fix this&amp;rdquo; and &amp;ldquo;now this&amp;rdquo; in chat. If I&amp;rsquo;d done it all myself, I think I&amp;rsquo;d have lost three weekends to it.&lt;/p&gt;
&lt;p&gt;&amp;hellip;and if you&amp;rsquo;ve read this far, you might have already guessed — yeah, this post&amp;rsquo;s draft was written by Claude too (lol).&lt;/p&gt;</description></item><item><title>Six months after I impulse-bought a Porsche 911</title><link>https://nobu666.com/en/2018/06/10/1030.html</link><pubDate>Sun, 03 Jun 2018 00:40:00 +0900</pubDate><guid>https://nobu666.com/en/2018/06/10/1030.html</guid><description>&lt;h1 id="what-i-bought"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2018/06/10/1030.html#what-i-bought"&gt;What I bought&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;A Porsche 911 Type 996 Carrera 4S, late model, registered in &amp;lsquo;04, fitted with a high-performance kit. Stock output is 320 ps, but the kit bumps it up to 345 ps.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nobu666.com/images/2018/06/IMG_20180430_170849.jpg" alt="911"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Drivetrain: AWD&lt;/li&gt;
&lt;li&gt;Engine: 3.6L flat-six, DOHC, 6-speed manual&lt;/li&gt;
&lt;li&gt;Max output: 345 ps @ 6800 rpm&lt;/li&gt;
&lt;li&gt;Max torque: 37.7 kgm @ 4750 rpm&lt;/li&gt;
&lt;li&gt;Length x width x height: 4435 x 1830 x 1295 mm&lt;/li&gt;
&lt;li&gt;Wheelbase: 2350 mm&lt;/li&gt;
&lt;li&gt;Curb weight: 1520 kg&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&amp;rsquo;s naturally aspirated, but as a 4S it gets the same wide body, stronger brakes, and retuned suspension as the turbo model. The AWD system uses a multi-plate viscous coupling that shifts torque to the front axle anywhere from 5% up to 40%.&lt;/p&gt;
&lt;p&gt;The previous owner made a few modifications: a GT3-ish aero kit (with a fixed rear wing) and headlights swapped to the 997 look. The suspension and intake/exhaust appear to be stock. The alloy wheels are aftermarket BBS, with different part numbers front and rear. The intermediate shaft had already been replaced with the reinforced version.&lt;/p&gt;
&lt;h1 id="can-an-ordinary-person-actually-afford-a-911"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2018/06/10/1030.html#can-an-ordinary-person-actually-afford-a-911"&gt;Can an ordinary person actually afford a 911?&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;The first thing I want to stress is that a 911 is surprisingly attainable. Ferraris and Lamborghinis are basically out of reach for regular people no matter how hard you dig through the used market. Porsche, though, is actually within striking distance. Air-cooled models have gotten pricey for historical reasons, but if you&amp;rsquo;re fine with water-cooled, the 996 and 997 sit at prices most people could realistically manage — not much different from buying a new mid-range Japanese car. The exceptions are the halo models like the GT2 and GT3 — forget those — and turbo models, which cost more than the NA versions.&lt;/p&gt;
&lt;p&gt;The 996 was Porsche&amp;rsquo;s first water-cooled model, infamous for swapping the classic &amp;ldquo;frog eye&amp;rdquo; headlights for the teardrop lights it shared with the Boxster, which drew plenty of criticism at the time. It launched in &amp;lsquo;97, so it&amp;rsquo;s already a 21-year-old car and arguably a classic at this point, yet its resale value is still holding up remarkably well. There&amp;rsquo;s a wide range within the 911 lineup, but a basic Carrera with 60,000-100,000 km on the clock can be had for roughly 2.5-4 million yen. Move up to a 997 and the average price jumps noticeably, so I&amp;rsquo;d argue the late-model 3.6L 996 is currently the sweet spot for value.&lt;/p&gt;
&lt;h1 id="maintenance-and-running-costs"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2018/06/10/1030.html#maintenance-and-running-costs"&gt;Maintenance and running costs&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;People often say Porsches are expensive to maintain, but honestly they&amp;rsquo;re built pretty tough. If you keep up with preventive maintenance, I don&amp;rsquo;t think you end up with the absurd bills people imagine. That said, engine oil is one area where you really do need to stay on top of things and use good oil.&lt;/p&gt;
&lt;p&gt;Specifically, the approved oil is Mobil 1 5W-40. A 4-liter jug runs about 4,000-5,000 yen, and an oil change uses roughly 9 liters, so even doing it yourself you&amp;rsquo;re looking at that kind of cost each time. Running costs are definitely higher than a typical Japanese car, so budget for that. Still, it&amp;rsquo;s nothing like a Ferrari or Lamborghini where every little thing costs hundreds of thousands of yen. Fuel economy is, unsurprisingly, not great. It improves with each generation — 996 to 997 to 991 to 992 — but for a 996 this old, figure on roughly 5-8 km/L. Oil management deserves more attention here than on an ordinary car, though it&amp;rsquo;s far better in this regard than the air-cooled models. Also, since it uses a semi-dry-sump system, I&amp;rsquo;d avoid getting the oil changed at chain stores like Autobacs or a regular gas station.&lt;/p&gt;
&lt;p&gt;Genuine parts are also pretty pricey — understandable given the quality, since the stock dampers are Bilstein and the brakes are Brembo. Porsche Center labor rates are steep too. If you&amp;rsquo;re thinking about buying one, I&amp;rsquo;d recommend scouting out a reputable independent shop nearby ahead of time — ideally one equipped with a proper Porsche diagnostic tester.&lt;/p&gt;
&lt;p&gt;Another mildly annoying point: you can&amp;rsquo;t buy tires and wheels as a matching set of four, because the front and rear sizes are completely different. Stock is 225/40ZR18 up front and 295/30ZR18 in the rear. The rear tires in particular aren&amp;rsquo;t cheap at that size, so brace yourself.&lt;/p&gt;
&lt;h1 id="interior"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2018/06/10/1030.html#interior"&gt;Interior&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Honestly, it&amp;rsquo;s a bit underwhelming. For a car with an MSRP north of 10 million yen, you&amp;rsquo;d hope for more — but then again, it&amp;rsquo;s a 20-year-old car. The newer 992 has a genuinely great interior, though of course its sticker price is roughly double. The Alcantara headliner and the powered leather seats are nice touches, at least.&lt;/p&gt;
&lt;h1 id="ride-quality"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2018/06/10/1030.html#ride-quality"&gt;Ride quality&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;The suspension is noticeably stiffer than a typical car. The rear +2 seats are barely more than a token gesture — not a space any adult man would want to sit in for long. Someone around 150 cm tall could probably manage back there. Being a 3.6L NA engine, torque is strong even at low revs — strong enough that you can pull away in second gear from idle without touching the throttle. It has six gears, but they&amp;rsquo;re tightly stacked; even in sixth, cruising at 100 km/h sits around 2000 rpm. The dampers are probably a bit worn from age, so you feel bumps and manhole covers, but nothing jarring enough to complain about.&lt;/p&gt;
&lt;p&gt;The smooth rev build you only get from a naturally aspirated engine, the ample torque of the 3.6L, that distinctive flat-six growl coming from behind you, the glued-to-the-road stability from AWD, the reassurance of a rear engine helping braking performance — all of it adds up to a daily-driving experience that keeps reminding you &amp;ldquo;Porsche really knows how to build a car.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The throttle response is so sharp that if you pull away from a stop the way you would in a normal car, the engine revs way more than you expect and it&amp;rsquo;s hard to move off smoothly. You can actually pull away on clutch alone without touching the accelerator at all, which might even be gentler on the clutch. As long as you&amp;rsquo;re not on a hill, starting in third gear is entirely doable.&lt;/p&gt;
&lt;h1 id="left-hand-drive"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2018/06/10/1030.html#left-hand-drive"&gt;Left-hand drive&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Porsche does offer right-hand-drive versions, and honestly I think that&amp;rsquo;s the better choice. This is my first time driving a left-hand-drive car, and in Japan the downsides overwhelmingly outweigh the upsides. Right turns at intersections are the biggest problem. If there&amp;rsquo;s oncoming traffic waiting to turn right, it&amp;rsquo;s genuinely hard to see straight-through traffic coming up behind them — you end up leaning toward the passenger seat and craning as far right as you can. Passing a bus is also tricky. On a single-lane residential street, if the bus ahead of you stops at a bus stop and you try to pass normally, a left-hand-drive car puts you at real risk, because the bus blocks your view of oncoming traffic completely. By the time you&amp;rsquo;ve eased over far enough to see, you&amp;rsquo;re already well into the oncoming lane. Your only real options are to leave a generous following distance behind buses, or just resign yourself to stopping behind them at bus stops.&lt;/p&gt;
&lt;p&gt;The upsides of left-hand drive are that you basically never risk clipping a pedestrian on a left turn, and it&amp;rsquo;s easier to hug the left edge to the limit when passing on a very narrow road. That said, since your view to the right is limited, it&amp;rsquo;s safer to stop once you&amp;rsquo;ve pulled left and let oncoming traffic — who can see you more easily — pass first. Two left-hand-drive cars meeting on a narrow road, though&amp;hellip; that&amp;rsquo;s rough (laughs). It&amp;rsquo;s also easier to spot pedestrians walking on the left, especially at night.&lt;/p&gt;
&lt;p&gt;That said, since the car was originally designed around left-hand drive, converting it to right-hand drive introduces a few quirks — this isn&amp;rsquo;t unique to Porsche. The pedal placement ends up slightly and unnaturally shifted toward the center of the car, and the throttle cable has to run longer than it would on a factory left-hand-drive car, which can make it feel a bit awkward and wear out faster. Neither of these can really be avoided — it&amp;rsquo;s just physics.&lt;/p&gt;
&lt;h1 id="the-body-is-wider-in-the-back-than-the-front"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2018/06/10/1030.html#the-body-is-wider-in-the-back-than-the-front"&gt;The body is wider in the back than the front&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;This takes real getting used to. On most cars, the front track and rear track are basically the same width. But because the 911 is rear-engine, rear-drive, the rear is wider than the front. The practical effect is that parking the car straight is genuinely harder than you&amp;rsquo;d expect, and it gets even trickier in reverse. It takes a fair amount of practice to develop a feel for which reference point on the car lines up straight. Visibility over your right shoulder is essentially nonexistent, so backing into a spot on your right as you approach requires real care.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nobu666.com/images/2018/06/IMG_20180304_211857.jpg" alt="Body width"&gt;&lt;/p&gt;
&lt;h1 id="wrap-up"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2018/06/10/1030.html#wrap-up"&gt;Wrap-up&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;ve written a lot here, but this car has enough fun packed into it to offset the downsides. It&amp;rsquo;s the kind of car that gets filed under &amp;ldquo;dream car,&amp;rdquo; and the sharp responsiveness and solid, honest build quality tug at something in the male psyche. Unlike a lot of so-called supercars, it&amp;rsquo;s not a pain to drive around town, and on top of that, the used prices are genuinely reasonable. Its resale value also holds up far better than most other cars. It&amp;rsquo;s not going to appreciate above its original price like a Ferrari can (special models like the GT3 aside), but compared to an ordinary car, &amp;ldquo;dramatically&amp;rdquo; better resale is a fair way to put it.&lt;/p&gt;
&lt;p&gt;The 997 still feels a bit pricey right now, so going with a 996 isn&amp;rsquo;t a bad call at all. That said, 997 prices are gradually starting to come down too, so if you&amp;rsquo;re buying, timing is worth thinking carefully about. The newer the model year, the smaller the pool of manual-transmission cars on the used market, so if you specifically want a manual, you may need to search patiently. Ordering a complete build from a shop isn&amp;rsquo;t a bad option either, come to think of it. If you go with a 996, definitely go for the late model. The early model has a removable cup holder (the late model&amp;rsquo;s retracts), and it also lacks a glovebox. On top of that, the late model comes standard with PSM (Porsche Stability Management).&lt;/p&gt;</description></item><item><title>After two years I finally felt like updating my blog, so I sorted through my photos and posted them</title><link>https://nobu666.com/en/2018/06/02/1029.html</link><pubDate>Sat, 02 Jun 2018 13:56:54 +0900</pubDate><guid>https://nobu666.com/en/2018/06/02/1029.html</guid><description>&lt;h1 id="my-photo-backlog-has-gotten-completely-out-of-hand"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2018/06/02/1029.html#my-photo-backlog-has-gotten-completely-out-of-hand"&gt;My photo backlog has gotten completely out of hand&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;d left this blog untouched for two years, and in that time an insane number of unpublished photos piled up. I hadn&amp;rsquo;t even developed most of them, so I spent a weekend wrestling with Lightroom, blasted through a batch of edits, and now I&amp;rsquo;m posting them all at once.&lt;/p&gt;
&lt;h2 id="tokyo-motor-show-2017"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2018/06/02/1029.html#tokyo-motor-show-2017"&gt;Tokyo Motor Show 2017&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/albums/72157669668630208" title="20171104 Tokyo Motor Show"&gt;&lt;img src="https://farm2.staticflickr.com/1758/41606297315_256930fa39_b.jpg" width="1024" height="683" alt="20171104 Tokyo Motor Show"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;This is from last year&amp;rsquo;s motor show. I didn&amp;rsquo;t take a single photo of the booth companions — I basically went just to see the AMG Project ONE. We also got to sit in a lot of the driver&amp;rsquo;s seats, so my wife happily hopped into one sports car after another.&lt;/p&gt;
&lt;h2 id="sapporo-snow-festival-2018"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2018/06/02/1029.html#sapporo-snow-festival-2018"&gt;Sapporo Snow Festival 2018&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/albums/72157691745438240" title="20180210 Sapporo Snow Festival"&gt;&lt;img src="https://farm2.staticflickr.com/1737/28635644448_ce37fb5430_b.jpg" width="1024" height="683" alt="20180210 Sapporo Snow Festival"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;The Sapporo Snow Festival — famous for the fact that actual Sapporo residents never bother going. Still, this was only my second time attending since I moved away from the city. This time I brought my wife along. Our whole goal was the giant FF14 snow sculpture of Estinien and Nidhogg — honestly that&amp;rsquo;s the only sculpture we even looked at (lol).&lt;/p&gt;
&lt;h2 id="ashikaga-flower-park"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2018/06/02/1029.html#ashikaga-flower-park"&gt;Ashikaga Flower Park&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/albums/72157695818336531" title="20180425 Ashikaga Flower Park"&gt;&lt;img src="https://farm2.staticflickr.com/1754/42457177322_f58258d2ab_b.jpg" width="1024" height="683" alt="20180425 Ashikaga Flower Park"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;My wife had been saying for ages she wanted to see the giant wisteria trellis at Ashikaga Flower Park, so we finally went. It had been an unusually warm year and the wisteria looked set to bloom early, so we took time off ahead of Golden Week to beat the crowds. That turned out to be exactly the right call — the wisteria was in full, glorious bloom. If we&amp;rsquo;d waited until the second half of Golden Week, I suspect it would already have been past its peak. It&amp;rsquo;s a bit of a trek out there, but it was far more satisfying than I expected.&lt;/p&gt;
&lt;h2 id="sumida-aquarium"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2018/06/02/1029.html#sumida-aquarium"&gt;Sumida Aquarium&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/albums/72157696950671834" title="20180504 Sumida Aquarium"&gt;&lt;img src="https://farm2.staticflickr.com/1735/41786211024_13c4ea81ee_b.jpg" width="1024" height="683" alt="20180504 Sumida Aquarium"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;Went with my wife and one of her friends. This also happened to be my first time near Tokyo Skytree. I figured the Skytree&amp;rsquo;s own parking lot would be impossibly crowded, so I parked at the Ito-Yokado in Hikifune and took the train one stop instead — and that bet paid off. Where I miscalculated was the food: every restaurant nearby was packed solid, and we ended up waiting 30 minutes just to buy tickets. It&amp;rsquo;s a small aquarium so you can get through it quickly, but if I had to pick highlights, it&amp;rsquo;d be the garden eels, the giant isopod, and the penguins.&lt;/p&gt;
&lt;h2 id="tokyo-racecourse--nhk-mile-cup"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2018/06/02/1029.html#tokyo-racecourse--nhk-mile-cup"&gt;Tokyo Racecourse — NHK Mile Cup&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/albums/72157669668772278" title="20180506 Tokyo Racecourse NHK Mile Cup (Owner&amp;#x27;s seat)"&gt;&lt;img src="https://farm2.staticflickr.com/1757/42457214952_b1663076b4_b.jpg" width="1024" height="683" alt="20180506 Tokyo Racecourse NHK Mile Cup (Owner&amp;#x27;s seat)"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;Through a rather unexpected connection, I ended up invited to the owners&amp;rsquo; seating and watched the race from there. We even got access to the underground passage by the weighing room and the paddock. Aside from not actually winning any money, it was fantastic. Here&amp;rsquo;s what comes with an owner&amp;rsquo;s seat:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A reserved, covered seat right along the home stretch&lt;/li&gt;
&lt;li&gt;A monitor, drink holder, betting-slip holder, and space for your bags, all at your seat&lt;/li&gt;
&lt;li&gt;Dedicated betting windows, smoking area, snack counter, and restaurant just for owners&amp;rsquo; seating&lt;/li&gt;
&lt;li&gt;Access down to paddock level, right up close to the horses&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To get inside the paddock itself, though, you apparently need an invitation from an owner who actually has a horse running that day — those people are the ones wearing ribbons on their clothes.&lt;/p&gt;
&lt;h2 id="kawasaki-factory-night-view"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2018/06/02/1029.html#kawasaki-factory-night-view"&gt;Kawasaki Factory Night View&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/albums/72157694375321372" title="20180519 Kawasaki factory night view"&gt;&lt;img src="https://farm2.staticflickr.com/1752/42457226412_d5541b6ab0_b.jpg" width="1024" height="683" alt="20180519 Kawasaki factory night view"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;On a sudden whim, I headed out to the Ogimachi area of Kawasaki to shoot the factory night views. I forgot my tripod, so everything&amp;rsquo;s handheld. I never quite found the composition I was picturing — maybe it would&amp;rsquo;ve been better if I&amp;rsquo;d walked up onto the bridge? Regardless, the drive along the Tama River toward Gasu Bridge near Futako-Tamagawa felt fantastic. I&amp;rsquo;d love to go back.&lt;/p&gt;
&lt;h2 id="jindai-botanical-gardens"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2018/06/02/1029.html#jindai-botanical-gardens"&gt;Jindai Botanical Gardens&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/albums/72157696950793434" title="20180520 Jindai Botanical Gardens"&gt;&lt;img src="https://farm2.staticflickr.com/1754/41786295044_06566f75a4_b.jpg" width="1024" height="683" alt="20180520 Jindai Botanical Gardens"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;Another spot my wife had been wanting to visit. Given the season, roses were clearly the main event, so that&amp;rsquo;s what we focused on. The gardens are bigger than I expected, so covering the whole place takes a fair bit of time. The parking lot is also seriously crowded, so you&amp;rsquo;ll likely get redirected to the overflow lot and have to walk a bit — wear shoes you can actually walk in.&lt;/p&gt;</description></item><item><title>A solo trip to Itsukushima Shrine, Ise Grand Shrine, and Kyoto</title><link>https://nobu666.com/en/2016/05/05/1028.html</link><pubDate>Thu, 05 May 2016 13:56:54 +0900</pubDate><guid>https://nobu666.com/en/2016/05/05/1028.html</guid><description>&lt;h1 id="a-solo-trip-to-itsukushima-shrine-ise-grand-shrine-and-kyoto"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2016/05/05/1028.html#a-solo-trip-to-itsukushima-shrine-ise-grand-shrine-and-kyoto"&gt;A solo trip to Itsukushima Shrine, Ise Grand Shrine, and Kyoto&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="tldr"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2016/05/05/1028.html#tldr"&gt;TL;DR&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;A record of a solo trip I took from 2016/05/01 to 2016/05/04, three nights and four days, leaving my wife behind to work while I wandered off on my own. There&amp;rsquo;s no real point to it. Just a bunch of photos below.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.flickr.com/photos/nobu666/sets/72157667327053260"&gt;Hiroshima&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.flickr.com/photos/nobu666/sets/72157665580150993"&gt;Ise Grand Shrine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.flickr.com/photos/nobu666/sets/72157667960494165"&gt;Kaiko-no-Yashiro and the Tenji Tenno Mausoleum&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="hiroshima---itsukushima-shrine"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2016/05/05/1028.html#hiroshima---itsukushima-shrine"&gt;Hiroshima - Itsukushima Shrine&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;On the 1st I took the Shinkansen from Tokyo to Hiroshima. Since I&amp;rsquo;d be staying at &lt;a href="https://twitter.com/WINGS1685"&gt;@WINGS1685&lt;/a&gt;&amp;rsquo;s place in Kyoto, I wanted to bring a souvenir, so I arrived at Tokyo Station about two hours before departure to buy the requested &lt;a href="https://www.hato.co.jp/goods/kizahashi.html"&gt;Toshimaya Kizahashi&lt;/a&gt;&amp;hellip; or so I&amp;rsquo;d planned. I&amp;rsquo;d gotten the Shinkansen time wrong and couldn&amp;rsquo;t buy it. I&amp;rsquo;d originally booked the 13:00 departure, but since the night cruise to visit Miyajima at my hotel required dinner starting at 17:00, I&amp;rsquo;d changed it to 11:00 so I&amp;rsquo;d arrive around 16:00 — and then completely forgot I&amp;rsquo;d done that. So off to Hiroshima I went, empty-handed, getting an earful over chat the whole way.&lt;/p&gt;
&lt;p&gt;I stayed at the &lt;a href="http://www.akigh.co.jp"&gt;Aki Grand Hotel&lt;/a&gt;. Unfortunately only mountain-view rooms were left, but I could at least catch a glimpse of the sea from near the emergency exit.&lt;/p&gt;
&lt;p&gt;The view from my room&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26210396903/in/dateposted-public/" title="IMG_7188"&gt;&lt;img src="https://farm8.staticflickr.com/7194/26210396903_ddf6dbc24c_z.jpg" width="640" height="427" alt="IMG_7188"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26542369100/in/dateposted-public/" title="IMG_7189"&gt;&lt;img src="https://farm8.staticflickr.com/7293/26542369100_0de95c555d_z.jpg" width="640" height="427" alt="IMG_7189"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;The view from the 9th-floor emergency exit&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26747530451/in/dateposted-public/" title="IMG_7193"&gt;&lt;img src="https://farm8.staticflickr.com/7318/26747530451_15780f1632_z.jpg" width="640" height="427" alt="IMG_7193"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26721124262/in/dateposted-public/" title="IMG_7194"&gt;&lt;img src="https://farm8.staticflickr.com/7311/26721124262_9a5640c069_z.jpg" width="640" height="427" alt="IMG_7194"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;The lobby&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26210408273/in/dateposted-public/" title="IMG_7196"&gt;&lt;img src="https://farm8.staticflickr.com/7049/26210408273_b7a366fff2_z.jpg" width="427" height="640" alt="IMG_7196"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26721132592/in/dateposted-public/" title="IMG_7197"&gt;&lt;img src="https://farm8.staticflickr.com/7298/26721132592_4200fe6d33_z.jpg" width="640" height="427" alt="IMG_7197"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;Stepping out onto the terrace from the lobby&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26210414463/in/dateposted-public/" title="IMG_7199"&gt;&lt;img src="https://farm8.staticflickr.com/7063/26210414463_417e988d80_z.jpg" width="640" height="427" alt="IMG_7199"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26721134582/in/dateposted-public/" title="IMG_7201"&gt;&lt;img src="https://farm8.staticflickr.com/7206/26721134582_e9fe9ed736_z.jpg" width="640" height="427" alt="IMG_7201"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26210417563/in/dateposted-public/" title="IMG_7204"&gt;&lt;img src="https://farm8.staticflickr.com/7271/26210417563_a58570fbf6_z.jpg" width="640" height="427" alt="IMG_7204"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;Since the night cruise was coming up, I had an early dinner. The hotel has a few different dining options, and this time I went for a course meal at a place with an izakaya-ish vibe.&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26542381100/in/dateposted-public/" title="IMG_7210"&gt;&lt;img src="https://farm8.staticflickr.com/7284/26542381100_0e85de99ea_z.jpg" width="640" height="427" alt="IMG_7210"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26721136472/in/dateposted-public/" title="IMG_7208"&gt;&lt;img src="https://farm8.staticflickr.com/7118/26721136472_ee9e9d056a_z.jpg" width="640" height="427" alt="IMG_7208"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26209493074/in/dateposted-public/" title="IMG_7213"&gt;&lt;img src="https://farm8.staticflickr.com/7704/26209493074_9490a67a2d_z.jpg" width="640" height="427" alt="IMG_7213"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26721139252/in/dateposted-public/" title="IMG_7215"&gt;&lt;img src="https://farm8.staticflickr.com/7758/26721139252_57b39e622e_z.jpg" width="640" height="427" alt="IMG_7215"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;After a leisurely meal, I took a relaxed bath while waiting for the cruise. Sadly it&amp;rsquo;s not a hot spring, and the open-air bath was reservation-only anyway. Also, there were a lot of foreign tourists around, and their bathhouse etiquette left something to be desired. I get that a UNESCO World Heritage site draws a lot of travelers, so it&amp;rsquo;s somewhat unavoidable, but I do wish people would follow at least some of the local customs. Wearing swimwear in the sauna, taking photos in the bath area — that&amp;rsquo;s just not okay.&lt;/p&gt;
&lt;p&gt;Anyway, setting that aside, I stepped out onto the terrace before the night cruise. The city lights were sparse.&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26814719125/in/dateposted-public/" title="IMG_7222"&gt;&lt;img src="https://farm8.staticflickr.com/7501/26814719125_d5477d87ac_z.jpg" width="640" height="427" alt="IMG_7222"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26747560901/in/dateposted-public/" title="IMG_7230"&gt;&lt;img src="https://farm8.staticflickr.com/7677/26747560901_5620933629_z.jpg" width="640" height="427" alt="IMG_7230"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26747566721/in/dateposted-public/" title="IMG_7234"&gt;&lt;img src="https://farm8.staticflickr.com/7184/26747566721_224a1af9d5_z.jpg" width="640" height="427" alt="IMG_7234"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;And then, the night cruise. Basically a sightseeing boat, capacity maybe 20 people. We listened to the guide&amp;rsquo;s commentary, and once we got close, everyone stepped out on deck to pay respects before the photo session began.&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26721157302/in/dateposted-public/" title="IMG_7241"&gt;&lt;img src="https://farm8.staticflickr.com/7019/26721157302_ceb2e6abc7_z.jpg" width="640" height="427" alt="IMG_7241"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26210434213/in/dateposted-public/" title="IMG_7248"&gt;&lt;img src="https://farm8.staticflickr.com/7424/26210434213_686ec84844_z.jpg" width="640" height="427" alt="IMG_7248"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26721162442/in/dateposted-public/" title="IMG_7258"&gt;&lt;img src="https://farm8.staticflickr.com/7725/26721162442_c5e5c9e658_z.jpg" width="640" height="427" alt="IMG_7258"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26747579971/in/dateposted-public/" title="IMG_7260"&gt;&lt;img src="https://farm8.staticflickr.com/7670/26747579971_5c1e3857bc_z.jpg" width="640" height="427" alt="IMG_7260"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26814751205/in/dateposted-public/" title="IMG_7280"&gt;&lt;img src="https://farm8.staticflickr.com/7022/26814751205_76a2255119_z.jpg" width="640" height="427" alt="IMG_7280"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26542412970/in/dateposted-public/" title="IMG_7291"&gt;&lt;img src="https://farm8.staticflickr.com/7334/26542412970_b586375ddd_z.jpg" width="640" height="427" alt="IMG_7291"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;It was interesting how much the mood changes depending on the tide. Speaking of Itsukushima Shrine, it&amp;rsquo;s one of Japan&amp;rsquo;s three most scenic views, alongside:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Matsushima in Miyagi&lt;/li&gt;
&lt;li&gt;Amanohashidate in Kyoto&lt;/li&gt;
&lt;li&gt;Miyajima&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our guide told us all sorts of things: that the area used to be forbidden ground, that the torii gate isn&amp;rsquo;t buried in the seabed but simply stands under its own weight on a foundation of thousand-pillar piles, that the current gate is the 8th reconstruction, built in the Meiji era, and that the plaque bears different characters on the ocean side versus the shrine side. Being one of Japan&amp;rsquo;s three great torii gates, its silhouette really is mystical.&lt;/p&gt;
&lt;p&gt;The next day, I took the ferry over to Miyajima.&lt;/p&gt;
&lt;p&gt;From the ferry, a mountain that looks like a reclining Kannon&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26210451293/in/dateposted-public/" title="IMG_7301"&gt;&lt;img src="https://farm8.staticflickr.com/7344/26210451293_74bc8e92c0_z.jpg" width="640" height="427" alt="IMG_7301"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26209543944/in/dateposted-public/" title="IMG_7326"&gt;&lt;img src="https://farm8.staticflickr.com/7042/26209543944_2fb23695ab_z.jpg" width="640" height="427" alt="IMG_7326"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26209544394/in/dateposted-public/" title="IMG_7327"&gt;&lt;img src="https://farm8.staticflickr.com/7141/26209544394_9c884b21c1_z.jpg" width="640" height="427" alt="IMG_7327"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;I wandered around Miyajima in the morning, then took the ferry to the Peace Memorial Park museum and the Atomic Bomb Dome in the afternoon. Since I&amp;rsquo;d failed to buy the kizahashi, I&amp;rsquo;d been given a divine decree that a butter cake from a place called Nagasakido would earn my forgiveness, so I went to buy one — only to find it was already sold out and closed by 13:00&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26497333310/in/album-72157663340334980/" title="IMG_20160502_134928"&gt;&lt;img src="https://farm9.staticflickr.com/8740/26497333310_20dbe0ca8b_z.jpg" width="480" height="640" alt="IMG_20160502_134928"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26210484603/in/album-72157667327053260/" title="IMG_7357"&gt;&lt;img src="https://farm8.staticflickr.com/7615/26210484603_70e4870889_z.jpg" width="640" height="427" alt="IMG_7357"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26542479910/in/album-72157667327053260/" title="IMG_7385"&gt;&lt;img src="https://farm8.staticflickr.com/7153/26542479910_c2820cf1a2_z.jpg" width="640" height="427" alt="IMG_7385"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26210492053/in/album-72157667327053260/" title="IMG_7387"&gt;&lt;img src="https://farm8.staticflickr.com/7031/26210492053_52d34f0463_z.jpg" width="640" height="427" alt="IMG_7387"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;About half the visitors at the museum were foreign tourists, which really shows how much interest there is worldwide. The volunteer guide, an older gentleman, spoke fluent English and explained the circumstances at the moment the bomb went off. Seeing personal belongings with people&amp;rsquo;s names still on them, a model showing the size of the fireball at detonation, a full-scale replica of the bomb itself — it was all so visceral it left me at a loss for words. If you ever have the chance to go, I&amp;rsquo;d recommend setting aside an hour or so to look through the museum carefully and really reflect. It&amp;rsquo;s not a place you want to rush through.&lt;/p&gt;
&lt;h2 id="kyoto---ise-grand-shrine"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2016/05/05/1028.html#kyoto---ise-grand-shrine"&gt;Kyoto - Ise Grand Shrine&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;In the evening I took the Shinkansen from Hiroshima to Kyoto. I had oyakodon near Shijo-Omiya, stayed the night, then headed to Mie the next morning. My host had gotten hold of an Ise Grand Shrine visitor&amp;rsquo;s pass — a great deal that covered a round trip from Kyoto to Ise Grand Shrine for 6,600 yen, two Kintetsu limited express rides between Matsusaka and Kashikojima, and unlimited rides on Mie Kotsu buses in the Ise area. Making full use of it, before visiting the outer shrine I got off at Ise-shi Station, headed to Futaminoura Station, and went to Futami Okitama Shrine, home of the famous Wedded Rocks.&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26702940241/in/album-72157663340334980/" title="IMG_20160502_190753"&gt;&lt;img src="https://farm9.staticflickr.com/8765/26702940241_7cfc30e662_z.jpg" width="640" height="480" alt="IMG_20160502_190753"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26724792771/in/album-72157663340334980/" title="IMG_20160503_090926"&gt;&lt;img src="https://farm8.staticflickr.com/7339/26724792771_6571e0a040_z.jpg" width="640" height="480" alt="IMG_20160503_090926"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26542558400/in/album-72157665580150993/" title="IMG_7390"&gt;&lt;img src="https://farm8.staticflickr.com/7604/26542558400_5fa6e9f11f_z.jpg" width="427" height="640" alt="IMG_7390"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26721268112/in/album-72157665580150993/" title="IMG_7395"&gt;&lt;img src="https://farm8.staticflickr.com/7722/26721268112_1bbcc3a45a_z.jpg" width="640" height="427" alt="IMG_7395"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;Despite it being Golden Week, there was almost no one around the station, which was a little unsettling. On top of that, with the Ise-Shima Summit coming up soon, police had gathered from Mie and neighboring areas alike — intimidating-looking transport vehicles were parked everywhere, and I saw plenty of officers who looked like they were in training. If it&amp;rsquo;s already like this a month out, I can only imagine how tense things will get closer to the date. Once I actually reached my destination, though, there were a fair number of people. I guess most people get there by car — the trains only run twice an hour, after all&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26721270192/in/album-72157665580150993/" title="IMG_7401"&gt;&lt;img src="https://farm8.staticflickr.com/7648/26721270192_4bf78f055a_z.jpg" width="640" height="427" alt="IMG_7401"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26747693921/in/album-72157665580150993/" title="IMG_7407"&gt;&lt;img src="https://farm8.staticflickr.com/7066/26747693921_9667d21394_z.jpg" width="640" height="427" alt="IMG_7407"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26747695251/in/album-72157665580150993/" title="IMG_7409"&gt;&lt;img src="https://farm8.staticflickr.com/7546/26747695251_3d30666f6b_z.jpg" width="640" height="427" alt="IMG_7409"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26747696301/in/album-72157665580150993/" title="IMG_7414"&gt;&lt;img src="https://farm8.staticflickr.com/7388/26747696301_7cdfbe7bd6_z.jpg" width="640" height="427" alt="IMG_7414"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26721273282/in/album-72157665580150993/" title="IMG_7422"&gt;&lt;img src="https://farm8.staticflickr.com/7582/26721273282_6c9f87bcc0_z.jpg" width="640" height="427" alt="IMG_7422"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;I grabbed a meal near the station at a place called Ogiya before heading to Ise Grand Shrine. I&amp;rsquo;d picked Ogiya completely at random, but it turned out to be a great find — both the Matsusaka beef hamburger steak and the fried Ise lobster were delicious.&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26767069006/in/album-72157663340334980/" title="IMG_20160503_134516"&gt;&lt;img src="https://farm8.staticflickr.com/7238/26767069006_b5b4e98933_z.jpg" width="640" height="480" alt="IMG_20160503_134516"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26814879905/in/album-72157665580150993/" title="IMG_7425"&gt;&lt;img src="https://farm8.staticflickr.com/7086/26814879905_8bd27df2be_z.jpg" width="640" height="427" alt="IMG_7425"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26747699741/in/album-72157665580150993/" title="IMG_7428"&gt;&lt;img src="https://farm8.staticflickr.com/7308/26747699741_cfd8b13a0a_z.jpg" width="640" height="427" alt="IMG_7428"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26209656504/in/album-72157665580150993/" title="IMG_7431"&gt;&lt;img src="https://farm8.staticflickr.com/7265/26209656504_278a282885_z.jpg" width="640" height="427" alt="IMG_7431"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26721274632/in/album-72157665580150993/" title="IMG_7432"&gt;&lt;img src="https://farm8.staticflickr.com/7050/26721274632_340a6658c6_z.jpg" width="640" height="427" alt="IMG_7432"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26721275732/in/album-72157665580150993/" title="IMG_7437"&gt;&lt;img src="https://farm8.staticflickr.com/7720/26721275732_fbf8c25474_z.jpg" width="640" height="427" alt="IMG_7437"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26209663044/in/album-72157665580150993/" title="IMG_7444"&gt;&lt;img src="https://farm8.staticflickr.com/7338/26209663044_ddcbffe636_z.jpg" width="640" height="427" alt="IMG_7444"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26210600713/in/album-72157665580150993/" title="IMG_7458"&gt;&lt;img src="https://farm8.staticflickr.com/7709/26210600713_2f933e740a_z.jpg" width="640" height="427" alt="IMG_7458"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;I took the bus from the outer shrine to the inner shrine. Between the stop at the Wedded Rocks and trying to visit more than just the main sanctuary, time got pretty tight. I kept the souvenir shopping brief and headed back. I&amp;rsquo;d braced myself for Golden Week crowds, but it wasn&amp;rsquo;t unbearably packed, and I got to fully enjoy my first visit to Ise. What struck me was that every main hall has an empty plot right next to it, reserved for the next Shikinen Sengu rebuilding — the sheer effort of rebuilding everything, including all the auxiliary shrines, is staggering. While I was paying my respects at the inner shrine, I overheard what sounded like part-time staff collecting the offering money saying things like &amp;ldquo;how many bags today, seven?&amp;rdquo; — a bit too candid for comfort. I thought, maybe have that conversation somewhere tourists can&amp;rsquo;t hear you.&lt;/p&gt;
&lt;p&gt;Back in Kyoto, I had delicious salt ramen at a place with the silly name Pakopako. The broth had that &amp;ldquo;clearly took real effort&amp;rdquo; kind of depth to it.&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26767069656/in/album-72157663340334980/" title="IMG_20160503_205622"&gt;&lt;img src="https://farm8.staticflickr.com/7584/26767069656_bb3688f11e_z.jpg" width="640" height="480" alt="IMG_20160503_205622"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;h2 id="kyoto---kaiko-no-yashiro-and-the-tenji-tenno-mausoleum"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2016/05/05/1028.html#kyoto---kaiko-no-yashiro-and-the-tenji-tenno-mausoleum"&gt;Kyoto - Kaiko-no-Yashiro and the Tenji Tenno Mausoleum&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Kyoto during Golden Week is packed, but I got dragged along, sight unseen, to some spots that were supposedly uncrowded and lovely. First up was Kaiko-no-Yashiro, formally Konoshima-ni-Masu-Amaterumitama Shrine. Remarkably quiet, and home to an extremely rare triple torii gate. The spot where the triple torii stands used to be a pond called the Mototadasu Pond, apparently, though sadly it&amp;rsquo;s dried up now. No one really knows for certain why this gate was built. Mysterious stuff.&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26209717924/in/album-72157667960494165/" title="IMG_7485"&gt;&lt;img src="https://farm8.staticflickr.com/7358/26209717924_183962824c_z.jpg" width="640" height="427" alt="IMG_7485"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26814917145/in/album-72157667960494165/" title="IMG_7487"&gt;&lt;img src="https://farm8.staticflickr.com/7369/26814917145_33ebb12db7_z.jpg" width="640" height="427" alt="IMG_7487"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26814919055/in/album-72157667960494165/" title="IMG_7495"&gt;&lt;img src="https://farm8.staticflickr.com/7129/26814919055_6d020e4de6_z.jpg" width="640" height="427" alt="IMG_7495"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26747734321/in/album-72157667960494165/" title="IMG_7497"&gt;&lt;img src="https://farm8.staticflickr.com/7270/26747734321_b0a239433d_z.jpg" width="640" height="427" alt="IMG_7497"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26814920775/in/album-72157667960494165/" title="IMG_7498"&gt;&lt;img src="https://farm8.staticflickr.com/7410/26814920775_abaa619b06_z.jpg" width="640" height="427" alt="IMG_7498"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;From there we took the train to Okakita, an udon place in Higashiyama. We lined up for about an hour and a half starting at 11:00, finally getting inside around 12:30. The tempura udon, topped with fluffy egg, had a broth with a clean, well-balanced dashi flavor — excellent. My craving for meat wouldn&amp;rsquo;t quit, so I also ordered the mini beef bowl, seasoned like a sweet sukiyaki. Since it was also finished with egg, it stayed piping hot the whole time. Combined with the nice weather, the heat was honestly a lot to handle. I can&amp;rsquo;t imagine eating this in the height of summer. But it was delicious. Incidentally, the udon shop right next door had just as long a line.&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26717337172/in/album-72157663340334980/" title="IMG_20160504_124153"&gt;&lt;img src="https://farm8.staticflickr.com/7196/26717337172_280e9859b5_z.jpg" width="640" height="480" alt="IMG_20160504_124153"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26205670404/in/album-72157663340334980/" title="IMG_20160504_123651"&gt;&lt;img src="https://farm8.staticflickr.com/7442/26205670404_11807faefa_z.jpg" width="640" height="480" alt="IMG_20160504_123651"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;With lunch taken care of, we took the train to the Tenji Tenno Mausoleum, also known as Gobyono Kofun. Also almost completely deserted, but with a different, equally mysterious atmosphere from the shrines.&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26209725624/in/album-72157667960494165/" title="IMG_7499"&gt;&lt;img src="https://farm8.staticflickr.com/7197/26209725624_81fc31f2a9_z.jpg" width="640" height="427" alt="IMG_7499"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26542597430/in/album-72157667960494165/" title="IMG_7502"&gt;&lt;img src="https://farm8.staticflickr.com/7555/26542597430_69669d352d_z.jpg" width="640" height="427" alt="IMG_7502"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26210648293/in/album-72157667960494165/" title="IMG_7509"&gt;&lt;img src="https://farm8.staticflickr.com/7528/26210648293_08cff744e4_z.jpg" width="640" height="427" alt="IMG_7509"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26210650503/in/album-72157667960494165/" title="IMG_7513"&gt;&lt;img src="https://farm8.staticflickr.com/7276/26210650503_1dc5c574c2_z.jpg" width="640" height="427" alt="IMG_7513"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;&lt;a data-flickr-embed="true" href="https://www.flickr.com/photos/nobu666/26542602770/in/album-72157667960494165/" title="IMG_7520"&gt;&lt;img src="https://farm8.staticflickr.com/7491/26542602770_344ee3bdff_z.jpg" width="640" height="427" alt="IMG_7520"&gt;&lt;/a&gt;&lt;script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/p&gt;
&lt;h2 id="wrap-up"&gt;
&lt;a class="Heading-link u-clickable" href="https://nobu666.com/en/2016/05/05/1028.html#wrap-up"&gt;Wrap-up&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Huge thanks to &lt;a href="https://twitter.com/WINGS1685"&gt;@WINGS1685&lt;/a&gt; and their spouse for putting together such a great itinerary on short notice and letting me stay two nights. Next time I&amp;rsquo;ll make sure I actually manage to buy the souvenirs, lol. Though the raw momiji manju ended up being more of a hit than expected, so it worked out fine. I never made it to Hiroshima Castle, never saw Itsukushima Shrine at high tide, never climbed the mountain on Miyajima, and never got in the hot spring — so I&amp;rsquo;m definitely due for a rematch. As for Ise Grand Shrine, I&amp;rsquo;d love to bring my wife next time. It&amp;rsquo;d be nice if it were a bit easier to get to from Tokyo. I keep ending up in Kyoto in May specifically, and while part of me wants to see it in a different season, it&amp;rsquo;s either too hot, too cold, or too crowded depending on when I go. Still, I really do want to catch the autumn leaves at some point&amp;hellip;&lt;/p&gt;</description></item></channel></rss>