2026年 01月 05日

システムあたらしくしたのでどんどん使っていくぞ〜

ポケモンZAはスタッフロールみるところまでやりました。

Chemr に mcp を実装してみて、はじめて自動的に役に立った。具体的には Gemini CLI が golang の MarshalXML でクソみたいな推測でモノを言うから検索しろと言ったらちゃんと最速で必要なドキュメントに辿りついた。「CDATA にはするには独自実装するしかない」と言い張っていた (そんなことはなく構造体タグにオプション書くだけ)。

というか別に Google Search がちゃんとしてればこんなMCPいらないはずなんだよな。誰だか知らねークソエントリをトップに出して公式サイトの順位を下げる(あるいは一切表示しない)というクソサーチエンジンが悪い。

LLM に我々(誰)が必要な図を描かせる場合、今のところは「matplotlib で描け」と指示するのが良さそう。特に↑のように2つのグラフの関係が正確であってほしいときは強力。

こういうのとかも: https://github.com/cho45/Hanrangon/blob/main/docs/frontend_arch_diagram.png

ただデフォルトのままよりは、ちゃんと Noto Sans JP を入れて使ってもらったほうが数段上に見える。

Mermaid で描かせてたけど、それほど表現力が高くないので最初からコードで書かせたほうがいい。まず人間が伝えるためのプロンプトとして Mermaid はいいかもだけど、自然言語でも伝わるしなあ

1月1日にインターネットが数度不通になってやきもきした。

Gemini CLI が急に無応答になったと思いきや、ネット全般に繋がらない。今までよくあった DS-Lite の NAT 枯渇のような症状ではなく、IPv6 も全滅。一応 VDSL モデムとかルーターの再起動をしてみたけど、関係なし。まぁずっと安定してるしLEDも正常だった。

ということで、これはまたかつての固定回線異常を彷彿とさせるなと新年早々に気分が重く。

問合せ方法とかを調べていたら自動的に復旧したので良かった、と思いきやまた数分切断とかとなって嫌な感じだった。結局 NTT 側の障害だった (結構この障害情報も遅れて表示されるので、当日の夜にようやく原因がわかった)

とりあえず2日〜4日は大丈夫だった。

Makefile で複数のプロセスを起動させたい(けど同時に終了してほしい)とき以下のようなイディオムがあるのを知った。foreman とかないと面倒だと思ってたわ

run-with-fe-dev:
        trap 'kill 0' EXIT; \
        (cd admin-frontend && npm run dev) & \
        HANRANGON_FE_DEV=true go run -tags "$(GO_TAGS)" .

Makefile というかシェルの機能だけど、EXIT をトラップして kill 0 (プロセスグループにシグナルを送る = 起動したシェル以下のプロセス全部が死ぬ)

node あるなら concurrently が一番楽なのかな? 名前が長いよな…… あとは高級なやつだと mprocsがある? ほかにもあるんだろうか

2026年 01月 04日

日記システムPerlからgolangに書き換え

この日記システムを golang で書きなおした。元日から4日ぐらいかけた。なんかおかしかったら教えてください。

前々から日記システムをgolangにしたいなという気持ちを抱きつつxatena-goを書いたりしていた。しかし実際全体を移行するという気がなかなか起きなかった。というのも Hatena記法だけじゃなくてMarkdownだったり素のHTMLだったり、tDiary記法だったりをそのままDBに入れていたり、類似エントリ機能とか、類似画像機能とか、それらを実行するジョブキューとか、なんか細けえ最適化 (linkヘッダとか) が入ってたりとか……

最初のとっかかりが重要、ということで、既存のDBそのままで閲覧側だけまず作り (表示するだけだから特にフォーマッタとかは関係ない)、一旦安心しつつ書きこみ系を実装していった。大きな変更は主に Claude Code で計画させて割と自動で実装してもらい、Claude Code の Limit がきたら、細かいレビューしたり、小さい変更は Gemini CLI で補完した。

無駄に元のスキーマで DATE や DATETIME をつかっていたせいで (SQLite ではTEXTと一緒)、これらを go-sqlite3 が「いい感じ」に time.Time に変換するので、若干ややこしいクエリに書きかえたり、最小限のマイグレーション (created_at などを TZ 付きのフルのISO 8601に) をかけた。

サーバサイドで主につかったもの

  • sqlc backtick のエスケープに対応してない?のが罠なぐらい?
  • echo 特に気になるところない。HideBanner = true ぐらい

管理画面

自分しか使わない管理画面をリッチに作る気がさらさら起きなかったので、今までは1つの「編集」画面しかなかった。十分といえば十分だけど予約投稿機能が実はあるので、それを管理するのが面倒なのと、ジョブキューの見える化をしたかったので、新しく作った。

編集画面は、元々は Polymer による Web Components 実装だった。なので、最初は Lit で Web Components を書き直したけど、どうしても HTML in JS が許せないので、やっぱやめて使ったことない Svelte にしてみた。

Svelteも最初は Web Components モード (SvelteはSPAを念頭にしてるが、そういうモードがある) として使ってたけど最終的には管理画面全体で SPA として再構築した。

といってもほとんどこれらの「あっちこっち」の書き換えは Gemini CLI でやったので特にまだ Svelte の特徴を感じてはいない……

Gemini CLI はなんか Lit は Google が推してるから安心ですよみたいなこと言ってきたので「PolymerやAngularJSのこと考えると Google が推してるからといって安心ということはない」と言ったら Svelte がこの手の小さいやつだと最適とか本当に適当なことをいう。

PSS (Proportional Set Size) = 実質的な消費量

smem というツールを使うと実質的な消費量が簡単に見れるらしい。知らんかった。preforkモデルだと copy on writeを考慮した実質的なメモリ消費量が top や ps だと簡単にわからないので便利だ〜

smem -P "/srv/www/backend.psgi" -t -k
  PID User     Command                         Swap      USS      PSS      RSS 
2438110 cho45    /srv/www/backend.psgi (mast        0     6.0M    10.9M    50.0M 
2438111 cho45    /srv/www/backend.psgi (work        0     6.8M    10.9M    49.4M 
2438114 cho45    /srv/www/backend.psgi (work        0     7.5M    11.2M    49.1M 
2438205 cho45    /usr/bin/python3 /usr/bin/s        0    11.0M    11.3M    13.7M 
2438118 cho45    /srv/www/backend.psgi (work        0     7.8M    11.4M    49.4M 
2438119 cho45    /srv/www/backend.psgi (work        0     8.5M    12.2M    50.2M 
2438120 cho45    /srv/www/backend.psgi (work        0     8.7M    12.3M    49.6M 
2438117 cho45    /srv/www/backend.psgi (work        0     8.7M    12.3M    49.7M 
2438112 cho45    /srv/www/backend.psgi (work        0     8.9M    12.4M    49.7M 
2438113 cho45    /srv/www/backend.psgi (work        0     8.8M    12.6M    50.5M 
2438115 cho45    /srv/www/backend.psgi (work        0     9.3M    13.0M    51.1M 
2438116 cho45    /srv/www/backend.psgi (work        0    17.9M    21.6M    57.8M 
-------------------------------------------------------------------------------
   12 1                                           0   109.8M   152.0M   570.1M 

PSSの合計が重要で、この場合152MB使っている。

  PID User     Command                         Swap      USS      PSS      RSS 
2498750 cho45    /usr/bin/python3 /usr/bin/s        0     9.8M    10.1M    12.2M 
2498688 cho45    /srv/www/lowreal.net/Hanran        0    17.8M    17.9M    19.6M 
-------------------------------------------------------------------------------
    2 1                                           0    27.6M    28.0M    31.8M 

移行後はそもそも1プロセスにした。やたらメモリ使用量減ってくれた。

bye daemontools

日記のバックエンド系のコンポーネントだけ daemontools を使い続けていたけど、移行したことによって完全に無になった。

あんまりアクセスログとか見てなくて気付かなかったけど、GPTBot/1.3 が 5req/sec ぐらいでずっとアクセスしてきていて謎い。そんなにずっと辿るほどのエントリ数はないだろ……

2026年 01月 03日

『JavaScriptから現実世界に干渉する7の方法: ブラウザでハードウェアをコントロールする技術』という本を書いた

[JavaScriptから現実世界に干渉する7の方法: ブラウザでハードウェアをコントロールする技術]

JavaScriptから現実世界に干渉する7の方法: ブラウザでハードウェアをコントロールする技術という本を書いてみた。前々からこのテーマでまとめた一冊というのを作りたかったので勢いで書いた。サンプルコードもほぼ各章ごとに、そこそこすぐ試せる形で用意したりして工夫してみた。

JSから直接ハードウェアをコントロールするのが好きなのと (特に WebSerial/WebUSB)、もともと WebAudio が好きだったのもあるので、これらを包括的に「現実的に干渉する」というまとめにすることにしてみた。そうなると「ディスプレイ」(光)」も無視できないので、光→振動→音→(HID→MIDI)→Serial→USB とより低レベルになるような構成にしてみた。

直前にいろいろ一気に書いたけど、だんだんやりかたをアップデートした結果、これが一番図とサンプルコードに気合が入っている。

1月3日17時〜7日の17時までは無料に設定してみたので眺めてみてください。

2025年 12月 31日

2025年

2024年も2023年も、、、と辿っていったら最後に年末にまとめ書いたのが2016年だった。いつだよ

今年はコーディングエージェントが急激に実用的になって、実装にかける時間が少なくなり企画・要件定義・設計・レビューの重要さと伝えかたの重要度をしみじみ感じた。コーディング面だと書くよりも読むことの重要度がさらに高まりを感じる。ただそれでも現時点で人間を代替できるほどまったく賢くなくイライラさせられるので、さっさともっと性能あがってくれと願っている。

昔書いて飽きたコードを再発掘・再実装するモチベになったのがよかった。

あとはひたすら金の計算をしてたなぁ…… 夫婦間のお金の使い道の見える化をすすめた (いままでやってなかったのかよと言われそう)。コスト最適化。

XSLTという関数型言語

ブラウザの安全性を高めるための XSLT の削除とかいうことで、つまりブラウザネイティブでの XSLT のサポートは消えるので、ちょっと哀しいですねという気持ち。

一時期(20年前……)は XML を手書きで書いて、XSLT で変換してHTMLにするという日記システムを作って使っていた (さすがにブラウザ直ではなくPHPでやらせてたけど)。そこから20年、クライアントサイド XSLT の本来の目的である、データをレンダリング可能な HTML に変換する機能は、より安全で、より人間工学に基づき、より適切にメンテナンスされた JavaScript API に取って代わられています。とか書いてあるのを見ると、まぁ事実なんだけどただかなしいなという。

XSLT (特に 1.0) は、副作用がなく、変数は不変(一度定義したら変更不可)、ループは再帰で実現するという、割と強めの関数型言語で、自分が初めて触った関数型だったと思う。XSLT のおかげで他の関数型のパラダイムも割とすんなり入った気もする。

Gemini に XSLT で関数型っぽさがわかるサンプルを書かせてみた

出力そのまま。SVG を出力するというサンプルを書いてくれた。しかもアニメーションする。これが素のブラウザで見れるのも今だけです。

XML はただの起点

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="fractal.xsl"?>
<root></root>

XSLT 側では4つの四角を call-template で生成するところから開始して、再帰でフラクタル状に四角をつくっていく。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/2000/svg">
  <xsl:output method="xml" indent="yes" media-type="image/svg+xml"/>

  <xsl:template match="/">
    <svg width="100%" height="100%" viewBox="-500 -500 1000 1000" style="background-color: #1a1a1a;">
      <title>XSLT Recursive Fractal</title>
      
      <xsl:call-template name="draw-square">
        <xsl:with-param name="x" select="0"/>
        <xsl:with-param name="y" select="0"/>
        <xsl:with-param name="size" select="300"/>
        <xsl:with-param name="depth" select="5"/> </xsl:call-template>
    </svg>
  </xsl:template>

  <xsl:template name="draw-square">
    <xsl:param name="x"/>
    <xsl:param name="y"/>
    <xsl:param name="size"/>
    <xsl:param name="depth"/>

    <xsl:if test="$depth > 0">
      
      <xsl:variable name="color">
        <xsl:text>hsl(</xsl:text>
        <xsl:value-of select="$depth * 40"/>
        <xsl:text>, 70%, 60%)</xsl:text>
      </xsl:variable>

      <rect 
        x="{$x - $size div 2}" 
        y="{$y - $size div 2}" 
        width="{$size}" 
        height="{$size}" 
        fill="{$color}" 
        opacity="0.9"
        stroke="#fff"
        stroke-width="1"
      >
        <animateTransform 
            attributeName="transform" 
            type="rotate" 
            from="0 {$x} {$y}" 
            to="360 {$x} {$y}" 
            dur="{10 + $depth * 5}s" 
            repeatCount="indefinite" />
      </rect>

      <xsl:variable name="nextSize" select="$size * 0.5"/>
      <xsl:variable name="offset" select="$size * 0.75"/> <xsl:variable name="nextDepth" select="$depth - 1"/>

      <xsl:call-template name="draw-square">
        <xsl:with-param name="x" select="$x"/>
        <xsl:with-param name="y" select="$y - $offset"/>
        <xsl:with-param name="size" select="$nextSize"/>
        <xsl:with-param name="depth" select="$nextDepth"/>
      </xsl:call-template>

      <xsl:call-template name="draw-square">
        <xsl:with-param name="x" select="$x"/>
        <xsl:with-param name="y" select="$y + $offset"/>
        <xsl:with-param name="size" select="$nextSize"/>
        <xsl:with-param name="depth" select="$nextDepth"/>
      </xsl:call-template>

      <xsl:call-template name="draw-square">
        <xsl:with-param name="x" select="$x - $offset"/>
        <xsl:with-param name="y" select="$y"/>
        <xsl:with-param name="size" select="$nextSize"/>
        <xsl:with-param name="depth" select="$nextDepth"/>
      </xsl:call-template>

      <xsl:call-template name="draw-square">
        <xsl:with-param name="x" select="$x + $offset"/>
        <xsl:with-param name="y" select="$y"/>
        <xsl:with-param name="size" select="$nextSize"/>
        <xsl:with-param name="depth" select="$nextDepth"/>
      </xsl:call-template>

    </xsl:if>
  </xsl:template>

</xsl:stylesheet>