2026年6月の素振り

これは何

2026年6月の単発の記事にするほどでもない雑多な話題をまとめている。

CodexでPRの作成とコードレビューをしてもらう

Codex では、GitHub 連携したリポジトリをチェックアウトし、任意のセットアップを済ませたコンテナ上で Issue 対応やコードレビューをしてもらえる。

何も設定していないコンテナでは、一般的に開発でよく使われるツールチェーンは入っている。
ただし、Scala の実行環境や細かいコマンドラインツールは存在しないケースが多い。

プロジェクトで必要なツールを明示しておく手段として mise を使う選択肢がある。
人間にとってもエージェントにとっても同じ環境を使う前提ができるので扱いやすい。

また、Codex の環境設定ではカスタムセットアップスクリプトを指定できる。
Web 上で長いセットアップスクリプトを直接管理するより、リポジトリ側にセットアップスクリプトを配置し、Codex 側では用意したスクリプトを呼ぶ作りにしておいたほうが運用しやすい。

加えて、開発中によく使うコマンドは just のレシピにしておくと便利。
testlintfmtbuild のようなレシピを揃えておくと、こちらも同様に人間もエージェントも同じ入口で作業を進められる。

実際にはリポジトリを見てもらうのが早いのだが、プライベートリポジトリのため今回は詳細を割愛する。
後日のブログで公開できたら...と考えている。

初めてのMCP開発

foldory というものを作った。
今さらながらに「MCP を作るというのはこんな感じか〜」という体験ができたのは良かった。

任意のディレクトリにテキストファイルを書いたり、階層を作ったりできる単純な MCP。
エージェントが必要なタイミングで記憶として取り出せるようにしている。
複数の文脈を横断する場合に今のところは便利に使っている。

n8nを止めた

2月の素振り」で導入した n8n だったのだが、結局使い道が思いつかない上に、ちょっとしたツールを書くにしても手で書いたほうが早いことが多かった。

AI エージェントとの接続がシームレスであるという点については、それはそうなのだが AI を使わなくてもできることのほうが私の場合は多かった。

承認駆動ワークフロー を消した

そもそもこのスキルを作成した背景として、初期のCodexにplanモードがなかったためである。
これからは初手で Shift + Tab を押して、積極的にplanモードを使っていきましょう。

コンテナのハードニング

大きく分けて4つの設定がある。

security_opt:
  - no-new-privileges:true
cap_drop:
  - ALL
read_only: true
tmpfs:
  - /tmp

Docker Compose を扱う場合に、侵害されたときの被害をできるだけ最小限に収めたい。
よって、上述のような設定を逐次反映している。

no-new-privileges はコンテナ内のプロセスが実行後に新しい権限を得られないようにする設定。
cap_drop: ALL は Linux capabilities をまず全部落とす設定で、必要になったものだけ cap_add で戻すことができる。

それぞれの capabilities でできることは上述を参考にしてもらうとして、デフォルトで有効になる capability も Docker のドキュメントにまとまっている。

read_only: true は、コンテナのルートファイルシステムを読み取り専用にできる。
(もちろん、指定されたボリュームには書き込み可能)

ただし、アプリケーションはログ、キャッシュ、一時ファイル、PID ファイルなどを書き込むことが往々にしてある。
今回は最低限の逃がし場所として /tmp だけ tmpfs で許可するといったことができる。

Windows と 1Password と SSHエージェントのフォワード

普段使用している Windows マシンから別の開発マシンへ入り、その先で GitHub などに SSH 接続したいことがある。
そのために、Windows 上の 1Password の SSH エージェントを接続先から参照できるように ForwardAgent を使った。

毎回の接続で ForwardAgent を有効にする場合、Windows 側の SSH config に書いておく。

Host dummy1
  HostName 192.168.10.10
  User user
  ForwardAgent yes

Host dummy2
  HostName 192.168.10.11
  User user
  ForwardAgent yes

秘密鍵を各マシンへ配らずに済むため、鍵管理の見通しがよくなる。
一方で、接続先からエージェント経由の認証が使えるようになるため、どの接続で ForwardAgent を有効にするかは雑に広げないほうがよさそう。

Tailscale を入れた

ローカルネットワーク内で利用していた開発マシンに、外からも内部の環境へアクセスしたくなった。
その候補として Tailscale を試した。

JetBrains のリモート開発を組み合わせると、手元のマシンのリソースをほとんど使うことがなく開発できるのが便利。

読んだ

ゆとりの法則

忙しい現場や機能不全がどのように負の連鎖を生むのかを扱っている本に読めた。
プロジェクト管理の本ではあるが、単にスケジュールや見積もりの話だけではなく、人間がどのように疲弊していくのか、組織の余裕がどのように失われていくのかが書かれていて興味深かった。
実際にそれ起きていたり、予兆があると現場としては黄色信号と見てもよさそう。

人間は時間的なプレッシャーをいくらかけられても、早くは考えられない

あと「この人がいなくなったらヤバい」とか「この人の後継となる人物がいるか?」っていう人的資源が、いろいろな面で深刻なリスクに見えた。 書籍では次のような簡易な式が。

人的資本 = 普通の速さで仕事ができるまでの期間 x (給与 + 間接コスト) x 50%

「属人化をなくす」は普通の速さで仕事ができるようになるまでにかかる時間もコストを圧縮するための投資だったりリスクヘッジとも思える。

エンジニア育成現場の「失敗」集めてみた。

なんだかんだ後輩や新入社員のオンボーディングに関わる機会が増えていることもあり読んだ。
失敗事例を読むこと自体も有用だが、それ以上に自分が誠実に振る舞えているかが気になっていた。
以下の項が興味深かった。

「ここに自分の人生を託せるか」

1日に大体は8時間前後働いているという前提で、仕事は人生のなかでかなりの時間を占めている。
というわけで、伸び代を伸ばしてもらえるか、人生において実のある機会を与えることができるか。
うっすらとこちらとしても責任を感じるわけで、明るい未来だとか見せられるかって大事だよねって思った。

「期待されているのにその期待がわからない」 / 「業務の目的と価値を書き下す」

期待されるタスクの質だったり内容をすり合わせておくのは大事だけど、いわゆる「ちゃんと」ではなく期待値を具体的に合わせることができている状態。
クオリアじゃないけどお互いに近しいものを見ている状態が好ましい。
言ってることはわかるんだけど、実践することは難しいし往々にしておかしな方向に転がっていってしまいがちなのが現実だと思う。

「企業にとって成果以外はすべて負債」

個人的には和気あいあいとメンバーがお互いに気持ちよく仕事できる状態が好ましいよねって思っている。当然だ。

ただ、その話を友人としていたら「結局はアウトプットがないと意味ないよね」って話をされて、魂の形が違うと思った。
が、言ってることは正しい。色々な気持ちが交錯している今日このごろ。

「応募者は自分自身をも騙すことがある」

これは私が転職活動しているときにも多分起きた気がする。 いろいろな会社を受けていると、発言に対するインプレッションをなんとなく察して、別の会社では言い方を変えている節があった。 面談を行う上でも、そのような「ウソ」といったら言い過ぎだし失礼だけど、見抜く術とか捲る技術って大事そう。 ただ、私自身は採用に関する業務を行っていないから適当言ってます。

セキュリティ周りで読んだもの

感想は割愛するが「セキュリティエンジニアの教科書」を読んでから参考文献として挙げられているものと周辺の資料を読んだ。

いい加減にMITRE ATT&CKを読むか〜って思ったり、ここから派生して読みたい資料が増えた。

おわり

今月はめまい、立ちくらみ、真っ直ぐに歩けない、呂律が回らないなど散々な目に遭った。
長らく精神病と付き合っているが、これはこれで初体験。
脳梗塞なのではと心配になって、#7119に電話をかけて相談をしたら救急車が病院に運んでくれた。
結局のところ私生活に問題があり、脳に原因があるわけではないということがわかってよかった。
7月も7月で休養を意識して過ごしたい。

2026年5月の素振り

これは何

2026年5月の単発の記事にするほどでもない雑多な話題をまとめている。

shokushitsu のアップデート

「shokushitsu」についてはこちらの記事を参照されたし。

リスト表示する内容が見切れてしまう問題に気がついたため、ウィンドウの高さを考慮してページングする修正を加えた。

読んだ

ユニコーン企業のひみつ

組織、チーム、プロジェクトといった、複数人で仕事をしていくことへの関心が個人として強くなっている。
全体としては、テック企業における組織文化、ロールの定義、立ち振る舞いについて書かれている。

Spotifyの事例が取り上げられているが、当然ながら全く同じ文化や仕組みをそのまま持ち込めるわけではない。
どのようにやるかというより、なぜそういう仕組みが必要だったのか、何を守るための文化なのかという部分が抽出されている。

以下、引用。

文化や価値観を守るためには、気が進まない局面であっても行動をとる。なぜなら、文化は流れていくに任せてしまうと、取り戻すのが難しくなってしまう。だからこそSpotifyはその文化を強めることに力を入れているのであり、職場に文化が息づくことに多大な労力を費やしている。

気持ちよく仕事ができるのがイチバンですね。次に倒産しないことです。

グラフィックデザインにおける秩序と構築

以前から引き続き、「小手先のデザインの引き出しを増やしたいな〜」ってくらいの気持ちで読んだ。
実際にはデザインによって人の情念をどのように扱うか、そのために社会学や心理学、時勢のようなものまで使っていく発想をもたらしてくれる本だった。
受け取り手に対してどう心情を仕向けるか、という観点で興味深かった。

上述の理由もあり、参考文献として社会学、経済学、心理学に関するものも多く含まれている。
最近の関心領域に被る分野でもあるためお得感がある。

重ねて、重ねていうと...Webデザインについて期待して読むと、少し齟齬があるかもしれない。
汎用的に使えるテクニックはあるものの、雑誌、新聞やフライヤーのような印刷物を例として挙げる内容が多かった印象。

セキュリティエンジニアの教科書

時勢柄、実務でもセキュリティに本腰を入れる機運が高まっている。
セキュリティエンジニアに転向するわけではないものの、バックエンドエンジニアとしてセキュリティ意識を引き締めたく読んだ次第。

情報処理技術者試験の対策で教科書的には「機密性」、「完全性」、「可用性」の3要素が頭に叩きこまれることが多いのだが、本書ではそれに「真正性」、「責任追従性」、「否認防止」、「信頼性」を加えた7要素が紹介されていた。
説明されてみれば、「確かにそうだよね」といったところだが具体と照らし合わせると整理しやすい。

以下は引用。

特性 セキュリティ対策の例
機密性
(Confidentiality)
・高度なセキュリティ対策が導入済みのデータセンターへサーバー機器などを保管
・強固なパスワードポリシーを適用して、ブルートフォース攻撃による不正ログインを防止
・アクセス制限機能を実装して、権限のないユーザーによる情報の閲覧を防止
完全性
(Integrity)
・アクセス制限機能を実装して、権限のないユーザーによる情報の変更を防止
・暗号技術を用いて情報を暗号化して改ざんを防止
・バックアップの取得(完全性が失われた場合の復旧手段)
可用性
(Availability)
・情報システムの冗長化
・予期せぬ停電など電源障害が発生したときに一定時間電力を供給する無停電電源装置(UPS:Uninterruptible Power System)の設置や信頼できるデータセンターの利用
真正性
(Authenticity)
・多要素認証、生体認証の利用
・公開鍵暗号基盤(PKI)、セキュアチャネル(TLS)の利用
責任追従性
(Accountability)
・アクセスログ、操作ログなどのログの保存
・情報システムの正確な時刻情報の維持
・アカウントの使いまわしを防止するための適切なアカウント管理
否認防止
(Non-repudiation)
・アクセスログ、操作ログなどのログの保存
・情報システムの正確な時刻情報の維持
・公開鍵暗号基盤(PKI)の利用
信頼性
(Reliability)
・情報システムの誤操作や誤動作が発生したときに、安全な状態にする考え方であるフェイルセーフを前提とした設計
・情報システムの十分なテスト

私が考えていたよりも「セキュリティエンジニアとしての役職」について説明されており、セキュリティマネジメントも確かに実務として存在するもののここまで細分化して考えたことがなかったな〜と思った。
なんとなく私のような未熟な開発者としては、脆弱性やセキュア開発といったテクノロジー寄りの話題に意識が向きがちなものの、組織としてリスクの捉え方と管理はかなり重要そう。

そうかもしれない。

ネガティブ・ケイパビリティ 答えの出ない事態に耐える力

この言葉になじみがなければないほど、読んでみてほしいと思った。

ネガティブ・ケイパビリティの概念について雑に説明すると、間に合わせの解決策で帳尻を合わせず、あえて中ぶらりんの状態を保つ能力というものと理解している。
その観点で日常に収まるような規模から、もう少し大きく広げて社会的な現象まで、答えの出ない場面に直面することは多い。
(そんなこと言ってられない話題と場面もなんだかんだあるけど...)

寛容さについて述べられているが、今の世の中にそれはあるだろうか、むしろ昔にはあっただろうか。
そして、私にはあるだろうか。
概念を理解していても、それを実践するのはすごく難しい。

それはそうと、概念を理解するために具体の話題で構成されている。
その中でも興味深かったのは、詩人であるジョン・キーツ、文学者である渡辺 一夫、人間の脳のわかりたがる性質について。
関連書籍をdigって行きたい所存。

おわり

5月はゴールデン・ウィーク明けにガッツリ風邪を引いてしまいダウンしていたため、技術的な素振りがあまりできなくて読書と映画鑑賞をして過ごしていた。できるだけ休むことを意識して暮らしていたけど、思うようには本調子ではない。
最終週に徐々に回復の兆しがありつつも、6月も力を抜きながら自分のペースを取り戻していきたい。
そろそろ梅雨入りっぽい天気予報になっていて、外出は最低限にしたいところ。休むにはちょうどいいかも?

2026年4月の素振り

これは何

2026年4月の単発の記事にするほどでもない雑多な話題をまとめている。

shokushitsu のアップデート

「shokushitsu」についてはこちらの記事を参照されたし。
私には過集中のクセがあって、50分おきに休憩を促す機能を追加した。
その他には、大きな機能追加というよりも、コーディングエージェントが生成した冗長な実装を整理するなどした。

コーディングエージェントのスキルを公開した

この記事を読んでいる方々にとっては釈迦に説法かもしれないが、頻出する作業のプロンプト入力をショートカットするための機能と認識している。
あくまでプロンプトのためエージェントが命令に従うかというとそうでもないというのが実情。
LLMの出力を私の価値観に仕向ける機能として扱っている。
よって、これらのスキルを見せることは自分自身の思想と価値観を開示することと同じで抵抗があったのだが、信頼できる身内同士で公開することでありがたがられることが多々あって、メリットのほうが大きいと判断して公開することにした。

承認駆動ワークフロー

1つ実験的に試しているスキル。

生成・評価・改善・蓄積のフィードバックループによって、AIを自律的に働かせる仕組みが重要という話はよく耳にする。
カーゴカルト感が否めないのだが、百聞は一見に如かずということで個人でPoCレベルで実践したいと考えて作成したスキル。

再考!令和の情報ジャンキーの生存戦略

インターネットはかつて真実に近づくための手段と考えられていた。
現況としては、嘘や誤った情報を拡散する構造に変化している。
生成AIの到来でさらに見分けのつかない情報が増えており、結果として人々は何を信じてよいかわからない状態に置かれている。
すべての情報がすべての人に開かれていて、世界で起こったことがログに残って、アクセスできるものがインターネットだと解釈している。
ただある程度にそうなった結果としては、情報の陳腐化が加速した虚無感がある。

以前に「令和の情報ジャンキーの生存戦略」について書いた。
当時はレコメンドシステムによって、時系列の情報を追いかけることが難しくなってしまったことに課題感を持っていた。
その対策として、知り合いと知り合いの知り合いくらいの間柄の人にクローズドなコミュニティへ集まってもらい、情報を共有する場を運営(?)していた。
これはある程度には成功したのだが、ブログやニュースのように単方向な情報ではなく、双方向のコミュニケーションが発生するため人間関係の摩擦が生じるようになってしまった。
今後もそういった出来事と向き合っていかなければならないのかと考えた瞬間に気分がすごく落ち込んだ。と、同時に自身の未熟さもなんとなく痛感。コミュニティは解体した。
今のところは付かず離れず...友人たちとは必要に応じて個別に連絡を取るスタイルに着地した。

少し話を戻す。
情報を疑う防御姿勢みたいなものを求められる頻度とそのコストが明らかに上がっている。比喩的に言えば、何かに出くわす度に出会い頭で殴るくらいの疑い方をしている。
情報を得る経路、媒体としては編集さんが入っていて品質がある程度に担保されているハズ!と思われるもの。具体的には雑誌や書籍は、現在のところ相対的に心理的なコストが低い。

クオーターライフクライシス

26歳になった。25歳と26歳は20代の後半という点では1年しか変わらない。
誕生日の前後、その1日で特に大きな変化があったわけでもないのだが、鏡の前に立った私が突然に老け込んでしまったように思えた。
若さの象徴が失われていくことに対して、焦燥感を覚えるばかりであった。今のうちにやっておいたほうがいい失敗がもう少しあるんじゃないかと思える。

自分自身に対して理想を持ちすぎている自覚があって、それが現状と乖離していることから発生している感情と解釈している。
そこにかなりのフラストレーションはあるものの、考え方を捻じ曲げたら貪欲さが失われていないことが確認できたのは良かった。

読んだ

systemdの思想と機能

上述の記事を読んでから個人のサーバーでは、cronからsystemd timerに移行を進めている。
なんとなく深掘りの余地があるというか、深掘りの余地しかないため読んだ。
直近で込み入ったunitを書く予定があるわけではないのだが、知らないディレクティブとオプションを山ほど知ることができて楽しめた。
とにかく何かとログを残してくれるのはとても便利。

エンジニアのためのマネジメントキャリアパス

ハーバード流交渉術 必ず「望む結果」を引き出せる!」に続いて、人間に関する本を読んでいる。直近のお仕事をにおいて処世術をもっと増やしていく必要に駆られているため。
自分自身のキャリアの選択肢として2つのルートがあると認識している。技術者として一点突破するか、マネジメントや調整も含めてオールラウンダープレイヤーとしてよしなに立ち回るか。
プログラマとして働いている。若輩ながらも。
後者は積極的には選択しないと考えていた。とはいえ、実際に経験してみないと向いている役割であるかを判断しかねる部分がある。なんだかんだ前者のルートであっても、意思決定にはそれなりの視座の高さを求められていると思っている。

それはそれとして、これまでの上司の行動や判断や組織中で求められていた役割を振り返ってみると、説明のつくものが増えて面白かった。

パスキーのすべて

色々あってパスキーに関わり合いになる今日このごろ。
なんだかんだ実装自体は、IDaaSに依存していて手を動かす部分の少なさに物足りなさを感じていて読んだ。
ユーザー体験に重きを置きながら網羅的にまとめられていて、自前でパスキーの認証を構築するには良い書籍なのではないか。

おわり

4月は自分のペースで生活することに失敗してしまって、現在地点や先行きを見失って視野狭窄気味になっていて色々なバランスが大きく乱れた。
5月は壊れたものを直すというか、ペースを取り戻すことを意識したいと考えている。
よって、手始めにゴールデンウィークは家族や友人に会うために福岡や大阪に何日か滞在してコンピューターからは離れて過ごしていた。一緒に時間を過ごしてくれた人たちにとても感謝している。

皆さまはいかがお過ごしだったでしょうか。

2026年3月の素振り

これは何

2026年3月の単発の記事にするほどでもない雑多な話題をまとめている。

shokushitsu を作った

ポモドーロとToggl Trackを組み合わせたようなツール。
実物が動いているものを見てもらったほうがわかりやすいのだが、動画を作るのが面倒で作っていない。
README.mdにツールの概要を記載しています。興味がある方はそちらを参照ください。

1日のうちに何をやったか、というのはそれとなく記憶している。
が、「何にどれだけ時間を使ったか?」と問われると答えに詰まってしまう。
日記を書くのが好きな私にとって、あったら嬉しいな〜と思って作った。

n8n

2月に立ち上げたn8nを、3月はもう少し実運用に寄せていた。
取引先ごとに定期的に何らかの締め切りタスクが発生するため、RedmineのAPIを叩いてチケットを自動生成するワークフローを作った。

Code Nodeを使うとJavaScriptを書けて便利。
ローコード/ノーコードのはずが本末転倒だが、思いつきで書いた便利ツールが飼っているサーバーでシュッと動かせるというのは気分がいい。

YubiKeyを買った

今までセキュリティキーを持っていたことがなかったな〜と単純なガジェット欲から買った。

PAMの設定を変更するにあたって、上述の記事が参考になった。
さすがにPCを開けられるようにするのは怖いので、詳細は省くが別の用途で使用している。

sufficientrequired (コントロールフラグ)の扱いは慎重になったほうがいい。
ガードを固めるどころか逆に穴を開けたり、固め過ぎてロックアウトされる可能性がある。
便利、不便、リスクの兼ね合いで設定を熟考すべし。

just

よく使う処理はシェルスクリプトにする運用で現況に不満はないのだが、このツールを導入しているプロジェクトでそれなりに便利に使わせてもらった。
新しく作る個人のプロジェクトにも積極的に導入している。

Cockpit

やんごとなき理由により遭遇したが、何かと便利なツールでもっと早く知りたかった。
ただ、これを入れるとでかい穴が1つ増えるので良い子はSSHトンネルを掘って使いましょう。

読んだ

14歳からの社会学

初版が2013年でちょうど私が13歳の頃に出た本ということになる。
今になって読んでみると何のためにやらされていて、何のためにやっていたことなのかが辻褄が合う説明が多かった。
もっと早く読んでおくべきだったかもしれない。
が、14歳だったころの私が読んだところで右から左に流してたかもしれない。

ハーバード流交渉術 必ず「望む結果」を引き出せる!

タイトルは若干あやしげに見えるが、信頼する人のお墨付きの書籍ゆえ読んでみた。

全体として、当然だが「人と問題を切り分けて話しましょう」という話に収束している。
ただ、それができない人が多いのか、自分自身も無意識にできていないのか、ハッとさせられる。
なぜ今になってこのテの本を読んでいるのかというのは、そういう引き出しが欲しいお年頃になってきたということです。

はじめてのデジタルアイデンティティ

認証・認可・ID管理について、シーン全体がここ数年でガラッと変わっていると感じていて、初心にかえるつもりで読んだ。
何らかのサービスを作っていると往々にして認証・認可もしくは本人確認を作ることになるわけだけど、システム設計という点でも良い内容だったと思う。

レタースペーシング - タイポグラフィにおける文字間調整の考え方

大雑把に、ひとくくりにして、カーニングという作業を完全に雰囲気でやっている。
ガチャガチャいじった結果、結局破綻してベタ組みでいいか...みたいに折れたりすることがある。
そもそも文字を取り扱うこと自体を避けて生きている。

やはり文字を創作に活かしたい今日このごろ、重い腰を上げて本を開いてみた次第。
観点を押さえて、雰囲気から意識に持ってくることができた感じがする。
とはいえ、本職ではないし忘れるから辞書的に開いて使っていきたい。

グラフィックとしての文脈でフォントが与える印象と目的にあまり関心がなかったことに気が付かされた。
知らないことが増えて嬉しい。

おわり

創作活動がマンネリ化してきていて、別の切り口からアイデアを得ようとしている。
藁にも縋る思いで楽しくなりたい。

2026年2月の素振り

これは何

2026年2月の単発の記事にするほどでもない雑多な話題をまとめている。

相変わらず飼っているサーバーのお手入れをしていた

全体的な話としてスクラップアンドビルドがやりやすくなった環境が整った。

n8n

単純にツールとして話題になっていた&気になっていたため導入した。
観測範囲だとAIエージェントとの繋ぎこみがアツいというような話が多い印象だったけど、入れてみた感想としては単純にメチャクチャに賢いIFTTTとかZapierが欲しかったんだなと思った。

付随して「yokonagashid」を作った

当然だけど、n8nはDiscordのサーバーとして振る舞ってくれる機能はないが、Webhookの受け口は用意してくれている。
というわけで、Discordの特定のサーバーを監視し、n8nのWebhookに投げるBotを書いた。 具体的に何に活用しているか、というものは今のところは無いのだけど。

個人で管理しているRedmineにDiscord経由(n8n経由)からチケットを作ったり、n8n側のスケジューリング機能からチケットの情報をfetchして期限アラートを飛ばしたりできるだろうか。

ロギング・メトリクス

さすがにそろそろロギングとメトリクスを取るかぁ、ということで以下を導入した。

Grafana自体に何かアラートを設定する段階には来ていない。
が、気になっているデータについてはダッシュボードにまとめている状態。
何を目的としてアラートが取りたいか?は追々考えるつもり。

Codexのお手入れ

affaan-m/everything-claude-code: The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Cowork, and beyond.が話題になっていて、 agents をいくつかパクった。
オリジナル内容はフロントエンドエンジニア寄りの内容が多くて、バックエンドエンジニアや自身の価値観的に色々とカスタマイズして設定した。

話題は逸れるけど、こういうプロンプトって個人の価値観と哲学の開示に近いものを感じていて他人に共有するのが何となく恥ずかしい気持ちになる。
本来であれば他人と共有することで新しい視点を得られる可能性もあるのだけど...。

Playwright

業務委託でWebサービスの開発している。
当然だけど、「動作確認をお願いね」というのは依頼される。
が、現場によってエビデンスを残す基盤自体はなかったりする。
口先だけでは「やりました」は言える上に、何度も手で確認するのも面倒くさい。
「他の箇所も壊れていまいか?」とリグレッションテストがやりたいため、素振りをしていた。

こういうことは以前にはSeleniumでやっていた。
比較をしてしまうとPlaywrightはかなり便利。
なんで今まで使って来なかったんだろう、というレベルで良い。

便利ツール整備

webm2mp4

背景としては開発機としてUbuntuを使用しているのだが、画面録画をするとWebM形式で保存される。
しかし、WebM形式はあまり汎用的な形式ではなくて少なくともSlackではプレビューで再生できなかった。
というわけで、mp4に変換するシェルを書いてパスを通した。

codex-play-notify.sh

Codexの作業が完了した際に通知音を鳴らすツール。

読んだ

SAML入門 技術の泉シリーズ (技術の泉シリーズ)

認証認可の仕事を持っている以上はこういうことも知っておくべきだよな、という気持ちで読んだ程度のもの。
座学的に読んだだけであって、実際に手を動かしてみないとわからないことが多いんじゃないかと思っている。

個人事業主1年目の強化書

背景としては、去年の9月くらいから(副業として)個人事業主を始めた。
やっぱり個人の名刺を作ったほうが良いよね、実務的な身の振り方、ベストプラクティスがまとまっている。
「始めるなら一読の価値があるか??」というと、そうでもないという印象。

というのも、本を読む以前に先輩に実務的なことを教えてもらっていて、そういう意味では本の内容は既に知っていることが多かった。

カメラは、撮る人を写しているんだ。

写真やカメラのそのものには興味はないけど、先輩に勧められたから読んだ。
いつもSNSで人の写真を見たときに、言外の意図や「その画角のためにわざわざ手間をかけたんだろうな〜」を勝手に感じていることがある。
それが「撮る人を写しているんだ」はピンとくるものがあった。

「ホテルのナイトプールや、流行りの店のパンケーキを撮影して、シャンパンの泡とともにエモちらかして誰かに見せたい。それまで写真など撮ったこともなかった人々が、自分にも撮れるじゃん、ソーシャルメディアで褒められたじゃん、俺、いけるじゃん、マネタイズできるじゃん、と勘違いさせられるフェーズまで一気に駆け上がるんだ」 「僕のこと...


「写ってほしくないもの、忌み嫌うものが背景に存在しているから仕方なくボカす。つまり消極的選択として画面から排除しているんだよ。もし日本の風景がヴェネチアみたいだったら、カズトだっておそらく今のように背景をボカして、そこに存在するものに目をつぶるような撮り方はしないはずだ」 「ガチャガチャした風景への劣等感ってことか。絵にならないものをフォーカスを外して無意識に消そうとしていると」

「FOLIO Meetup #1 リアルワールドScala - 金融を支えるシステムの実装ノウハウ」

重ね重ねになりますが、貴重な華金にご参加いただいた皆さまには御礼申し上げます。
皆さまにとっても楽しく、有意義な時間となっていましたら嬉しく思います。

「Scalaが支える4RAPの認証・認可基盤」という内容でお話させていただきましたが、少々機微な情報も含まれているため、発表資料については非公開とさせてください。

発表資料を公開しても良い、という判断をもらったため公開しました~。
Scalaが支える4RAPの認証認可基盤 - Speaker Deck

バックヤード的な話題

普段は外部・内部に問わず、LT(5分)で話すことには慣れている。今回は20分という枠をアサインされて話の組み立て方にものすごく悩んだ。
かなり緊張しており、聞き苦しい点もあったかと思いますが、最後までお付き合いいただきありがとうございました...。

今後ともどうぞよろしくお願いいたします。

おわり

素振りは「そぶり」とも読むし、「すぶり」とも読む。
2月も短い割には色々とあったものの、忘れてしまった話題は色々あるかも。

2026年1月の素振り

これは何

2026年1月の単発の記事にするほどでもない雑多な話題をまとめている。

GitHub ActionsでIaCツールをガチャガチャした

飼っているサーバーのお手入れ

手動で管理していたサーバーを構築しなおした。
上述の検証はこのためにあった。
それと以前にミスってローカルのtfstateファイルを飛ばしたことが数回あるため、流石にそろそろCIで運用するか...となった次第。

検証用リポジトリGithub ActionsでIaCツールを動かすことだけを目的としているため中身があまりにもお粗末な状態になっている。
実際に運用しているリポジトリを公開したいところだが、見られたくない情報もギュウギュウに詰まっているため割愛。

サーバーはLinodeを使っている。個人で使うには「お、ねだん以上。」であり、手頃にエンジニアフレンドリー。
Ansibleのユーザーを作るといったゼロイチの構築も手作業で行いたくなかったため、以下のようなStackScriptを書くなどした。

#!/bin/bash
# <UDF name="ansible_public_key" label="ansible user authorized public key" example="ssh-ed25519 AAA..." default="">
apt-get -q update && apt-get -q -y upgrade

ANSIBLE_USER="ansible"
SUDOERS_FILE="/etc/sudoers.d/${ANSIBLE_USER}"

useradd -m -s /bin/bash "${ANSIBLE_USER}"

mkdir -p "/home/${ANSIBLE_USER}/.ssh"
chmod 700 "/home/${ANSIBLE_USER}/.ssh"
echo "${ANSIBLE_PUBLIC_KEY}" | tee "/home/${ANSIBLE_USER}/.ssh/authorized_keys"
chmod 600 "/home/${ANSIBLE_USER}/.ssh/authorized_keys"
chown -R "${ANSIBLE_USER}:${ANSIBLE_USER}" "/home/${ANSIBLE_USER}/.ssh"

echo "${ANSIBLE_USER} ALL=(ALL) NOPASSWD: ALL" | tee "${SUDOERS_FILE}"
chmod 440 "${SUDOERS_FILE}"

これさえやってしまえばソッコーAnsibleで構築を始めることができてハッピー。

Caddyを試した

証明書の取得と更新を自動でやってくれるWebサーバー。
リバースプロキシとして使っている。

先述したサーバーでは、GrafanaやらチョットしたWebツールが走っている。
これまではNginx & Let's Encryptsのよくある構成を組んでいたわけだけど、新しく組むに当たってちゃんと設定するのが面倒に感じていてChatGPTに愚痴っていたら教えてもらった。

これもAnsibleでよしなに設定。ロールをチラ見せ。

roles/caddy/tasks/main.yml

---
- name: Install prerequisites
  ansible.builtin.apt:
    pkg:
      - debian-keyring
      - debian-archive-keyring
      - apt-transport-https
      - curl
      - gnupg
    update_cache: true
  become: true

- name: Add Caddy GPG key
  ansible.builtin.get_url:
    url: https://dl.cloudsmith.io/public/caddy/stable/gpg.key
    dest: /etc/apt/keyrings/caddy.asc
    mode: "0644"
  become: true

- name: Add Caddy repository
  ansible.builtin.apt_repository:
    repo: >-
      deb
      [signed-by=/etc/apt/keyrings/caddy.asc]
      https://dl.cloudsmith.io/public/caddy/stable/deb/ubuntu
      {{ ansible_facts['distribution_release'] }}
      main
    filename: caddy-stable
    state: present
  become: true

- name: Update apt cache after adding Caddy repo
  ansible.builtin.apt:
    update_cache: true
  become: true

- name: Install Caddy
  ansible.builtin.apt:
    name: "{{ caddy_package }}"
    state: present
  become: true

- name: Ensure Caddy log directory exists
  ansible.builtin.file:
    path: "{{ caddy_log_dir }}"
    state: directory
    owner: "{{ caddy_user | default('caddy') }}"
    group: "{{ caddy_user | default('caddy') }}"
    mode: "0755"
  become: true

- name: Render Caddyfile
  ansible.builtin.template:
    src: Caddyfile.j2
    dest: "{{ caddy_config_path }}"
    owner: root
    group: root
    mode: "0644"
  notify: Restart caddy
  become: true

- name: Ensure caddy service is enabled and started
  ansible.builtin.systemd:
    name: "{{ caddy_service }}"
    enabled: true
    state: started
  become: true

Caddyの設定自体はCaddyfileというものを書くわけだが、これが手頃に簡潔に記述できるのが非常に良い。

roles/caddy/templates/Caddyfile.j2

{% for site in caddy_sites %}
{{ site.domain }} {
  reverse_proxy {{ site.upstream }}

  log {
    output file {{ site.log_path | default(caddy_log_dir ~ '/' ~ site.domain ~ '.log') }} {
      roll_size 50MB
      roll_keep 5
      roll_keep_for 720h
    }
    format json
  }
}

{% endfor %}

Software Designの購読を始めた

SNSで技術ネタを掘るのにちょっと疲れてしまった今日このごろ。
編集さんが入っていて情報の質が、ある程度に担保された雑誌という権威(?)に回帰しようかなぁと考えた次第。

インターネットで熱量がある人を見つけるのが大変になった...ってその話は面倒臭いのでしません。

読んだ

SREの知識地図

常日頃として意識しておくとソフトウェアエンジニアとしての視座が少し高くなるハナシが詰まっていると思う。

愛するということ

家柄、家庭、幼少、ティーンエイジの環境ってその後の人生に相当の影響があるんだなって思った。悪く言えば呪いに近い。

昇降デスクを昇降できるようにした

ケーブルが干渉し合って上げ下げができなくなっていた。
1年くらい放ったらかしていたら座り過ぎで前立腺炎になった。
これは看過できない問題のため気合を入れて整理した。
ひたすらに長いケーブルを買い、上から下に垂らして、また上げると収まりがいい。

おわり

素振りは「そぶり」とも読むし、「すぶり」とも読む。

「Max for Live」のデバイスを開発する上でユーティリティクラスを作った

こんにちは!こんにちは!
FOLIO Advent Calendar 2025 6日目を担当する石川です。

厳しい寒さが続き、冬の陽だまりがことのほか暖かく感じる歳末の候、皆さまいかがお過ごしでしょうか。

これは何

Max for Liveでオリジナルの制作ワークフローを構築する楽しさを感じるここ数ヶ月です。
開発をする上で作ったユーティリティクラスを2つ紹介しています。

なんとなく対象読者

上記に該当せずとも、興味関心を持っていただけると幸いです。
よって「Ableton Live」と「Max for Live」を知らない方もいらっしゃると思いますので、簡単な説明も挟んでいます。
なお、パッチングやオブジェクトの細かい説明は割愛させていただきます。

Ableton Live(Live)」とは何か

端的に言えば音楽制作ソフトウェアです。
世の中にはCubase、Studio One、Logic Pro...といった選択肢もあるワケですが...
「Live」は名は体を表すようにライブ演奏やリアルタイム操作に強みがあります。

「Max for Live(M4L)」とは何か

Live内の機能を自作のプログラムで拡張できる仕組み、と言ったら言うべきか...。
専用のヴィジュアルプログラミング環境(Max)を用いて、エフェクターやオートメーションツールなどを開発できます。
他の開発者が配布しているプログラム(デバイス)も自身の環境にインストールして使用することもできます。

開発スタイル

基本的にはヴィジュアルプログラミングのような形式で開発を進めていきます。
画面上で「オブジェクト」と呼ばれる部品を配置・配線する形になります。
これを「パッチング」と呼ばれていたり、呼ばれていなかったりします。(曖昧)

Maxの独特のクセに このパッチングに慣れてしまえば多くの処理は視覚的かつ直感的に組めますし、コードを書くよりも手っ取り早く済む面もあるでしょう。
が、ありがたいことにJavaScript(V8エンジン)も搭載されているようです。
これによってテキストベースのプログラミングに馴染み深い私の土俵に引っ張り出すことができるワケです。

パッチ上のダイアルやメニューの値をJavaScript側で受け取り、Liveに命令を送るといったことが可能です。
パッチで完結するケースと、JavaScriptで書いたほうがラクできるケースを適宜使い分けていきましょう。

このあたりの棲み分けというか、設計方針に関しても論ずる点が割とあるのですが話が長くなりそうなので省略します。

live.thisdevice について(補足)

live.thisdevice reports three pieces of information about your Max Device. A bang message is automatically sent from the left outlet when the Max Device is opened and completely initialized, or when the containing patcher is part of another file that is opened. Additionally, a bang will be reported every time a new preset is loaded or the device is saved (and thus reloaded within the Live application). A 1 or 0 will be sent from the middle outlet when the Device is enabled or disabled, respectively. A 1 or 0 will be sent from the right outlet when preview mode for the Device is enabled or disabled, respectively. Used within Max, live.thisdevice functions essentially like the loadbang object. The middle and right outlets are inactive in this case.

https://docs.cycling74.com/reference/live.thisdevice より引用。

ひとまずこのようにパッチングをしておきましょう。
ちなみに左側はパッチング画面、右側はコンソール(ログ)画面です。

main.js の内容は以下のとおりです。

function bang() {
  post("Device initialization complete! \n");
}

このオブジェクトを使用することでデバイスが確実に初期化(読み込まれた)ことを担保することができます。
完全に初期化されていない状態で、LiveAPIやその他の処理を行うとエラーが起きがちです。
live.thisdevice からの通知を受け取ってから処理を実行することがセオリーと思います。

post関数

上述のソースコードを読んでみると不思議な post 関数が出てきました。

Prints a representation of the arguments in the Max window.

If post() has no arguments, it prints starting on the next line. Otherwise it prints the input on the current line separated by spaces. Arrays are unrolled to one level as with jsthis.outlet().

post - Max JS API | Cycling '74 Documentation より引用。

JavaScriptでは見慣れた console.log() を使いたくなるところですが...Maxでは機能しません!
Maxでは post 関数を使うことでコンソールへ文字列を出力することができます。

出力する毎に改行を挟みたいので、なんとなくさっくりと以下の関数を用意してしまうのも手でしょう。

function lnPost(obj) {
  if (obj) {
    post(obj + "\n");
  }
}

ロガー

...という話を踏まえた上で今回は人間に優しい形でログを出力するユーティリティクラスを実装しました。
形としてはよく見かける実装ではないでしょうか。

ログの出力例

v8: 2025-11-29T14:56:26.144Z [INFO] main: Device initialization complete!  

開発中のデバッグに何かと便利と思います。 JSON形式での出力やログレベルの設定といった味変はおまかせします。

「Live API」について

M4Lには、Liveの内部状態の取得および操作するためのクラスとして Live API が提供されています。
これによってトラックやエフェクターのパラメーターなどにアクセスし、プログラマブルに操作することが可能です。

LiveAPI - Max JS API | Cycling '74 Documentation

Liveの内部データ構造は階層構造となっており、これを「Live Object Model(LOM)」と定義されています。

Live Object Model | Cycling '74 Documentation より引用

プロジェクト全体(live_set)の中にトラック(tracks)があり、その子供としてデバイスエフェクターなど)、デバイスのパラメーター...などとぶら下がっている具合です。

各オブジェクトにアクセスする際は、文字列でパスを指定するような形となっています。
たとえば、プロジェクトの任意のトラックにアクセスする場合は、live_set tracks 0のようなパス指定します。
(初めてドキュメントを見たときにAPIの作りとしてかなり違和感がありましたが一旦は飲み込むこととしました。)

const track = new LiveAPI("live_set tracks 0");

コンストラクタにはパス以外にもコールバックを設定することができます。
基本的には子オブジェクトに変化(トラックの追加やパラメーターの変更)を監視することを目的としています。

property string The observed property, child or child-list of the object at the current path, if desired

For instance, if the LiveAPI object refers to "live_set tracks 1", setting the property to "mute" would cause changes to the "mute" property of the 2nd track to be reported to the callback function defined in the LiveAPI Constructor.

LiveAPI - Max JS API | Cycling '74 Documentation より引用

const liveSet = new LiveAPI(() => {
  logger.info("callback invoked!");
}, "live_set");

liveSet.property = "tracks"

このコールバックは Live APIインスタンス化された場合と property に設定したトラック(tracks)に変化があった場合に呼び出されます。

「Live API」のコールバックの挙動が不思議だった

さて、先述したようにこのコールバックについては以下の通りであるわけですが...

a function to be called when the LiveAPI object refers to a new object in Live (if the LiveAPI object's path changes, for instance) or when an observed property changes

LiveAPI - Max JS API | Cycling '74 Documentation より引用

なんとなく、監視対象のオブジェクトに変化があった場合にのみ処理を行いたい気持ちが出てくるわけです。
順当に初期化済みフラグを立てて条件分岐で早期returnすると良さそうに思います。

const logger = new Logger("init");
let initialized = false;

const liveApi = new LiveAPI(() => {
  logger.info("LiveAPI callback invoked");

  // 初回呼び出しではこの条件にマッチする
  if (!initialized) {
    // フラグを立てる
    initialized = true;
    logger.info("Initialization complete");

    // 監視対象のオブジェクトを設定する
    liveApi.property = "tracks";
    logger.info("Setting property to tracks");

    return;
  }

  logger.info("Detected change in live_set tracks");
}, "live_set tracks");

これでトラックが追加・削除といった変化があった場合にのみ、処理を継続することが期待できそうです。
が、実際に出力されたログは以下のとおりです。

v8: 2025-11-06T20:50:22.704Z [INFO] init: LiveAPI callback invoked  
v8: 2025-11-06T20:50:22.704Z [INFO] init: Initialization complete <- 初期化フラグが立った(期待した動作)
v8: 2025-11-06T20:50:22.704Z [INFO] init: LiveAPI callback invoked  

v8: 2025-11-06T20:50:22.704Z [INFO] init: Detected change in live_set tracks  <- トラックの変更が行われていないもかかわらず、変更が検知された(期待しない動作)
v8: 2025-11-06T20:50:22.705Z [INFO] init: LiveAPI callback invoked  

v8: 2025-11-06T20:50:22.705Z [INFO] init: Detected change in live_set tracks  <- 再びトラックの変更が行われていないもかかわらず、変更が検知された(期待しない動作)

v8: 2025-11-06T20:50:22.705Z [INFO] init: Setting property to tracks  <- 監視対象のオブジェクトを設定された

--- 手動でトラックを追加した場合にのみハンドラが呼び出されている ----

v8: 2025-11-06T20:50:37.048Z [INFO] init: LiveAPI callback invoked
v8: 2025-11-06T20:50:37.048Z [INFO] init: Detected change in live_set tracks

監視対象のオブジェクトの設定が設定される前に、トラックの操作をしていないにも関わらず変更が検知される不思議な挙動に遭遇しました。
プロパティが更新されたあともコールバックが呼ばれ、プロパティを設定した時点では initialized フラグが true となり変更が検知されたかのような挙動になっている気がします。
というわけで初期化フラグに加えて、プロパティ設定済みフラグも用意してみることにします。

const logger = new Logger("init");

let initialized = false;
let detectedPropertySet = false;

const liveApi = new LiveAPI(() => {
  logger.info("LiveAPI callback invoked");

  // 初回呼び出しではこの条件にマッチする
  if (!initialized) {
    // フラグを立てる
    initialized = true;
    liveApi.property = "tracks";

    logger.info("Initialization complete & Property set to tracks");
    return;
  }

  // プロパティが設定されたタイミングではこの条件にマッチする
  if (!detectedPropertySet) {
    // フラグを立てる
    detectedPropertySet = true;
    logger.info("Detected property set to tracks");

    return;
  }

  logger.info("Detected change in live_set tracks");
}, "live_set");

出力されたログは以下のとおりです。

v8: 2025-11-09T04:12:21.709Z [INFO] init: LiveAPI callback invoked  
v8: 2025-11-09T04:12:21.709Z [INFO] init: LiveAPI callback invoked  
v8: 2025-11-09T04:12:21.709Z [INFO] init: Detected property set to tracks
v8: 2025-11-09T04:12:21.709Z [INFO] init: LiveAPI callback invoked  
v8: 2025-11-09T04:12:21.709Z [INFO] init: tracks changed
v8: 2025-11-09T04:12:21.709Z [INFO] init: Initialization complete & Property set to tracks 

またしても、監視対象のオブジェクトを触っていないにもかかわらず変更が検知されているように見えます。
プロパティが設定されたあともコールバックが呼ばれるのでしょうか?
さらにフラグを足してみます。

const logger = new Logger("init");

let initialized = false;
let detectedPropertySet = false;
let detectedFirst = false;

const liveApi = new LiveAPI(() => {
  logger.info("LiveAPI callback invoked");

  // 初回呼び出しではこの条件にマッチする
  if (!initialized) {
    // フラグを立てる
    initialized = true;
    liveApi.property = "tracks";

    logger.info("Initialization complete & Property set to tracks");
    return;
  }

  // プロパティが設定されたタイミングではこの条件にマッチする
  if (!detectedPropertySet) {
    // フラグを立てる
    detectedPropertySet = true;
    logger.info("Detected property set to tracks");

    return;
  }

  // とりあえず一発(?)実行されたタイミングではこの条件にマッチする
  if (!detectedFirst) {
    // フラグを立てる
    detectedFirst = true;
    logger.info("Detected first callback after property set");

    return;
  }

  logger.info("Detected change in live_set tracks");
}, "live_set");

ログの出力は以下の通り

v8: 2025-11-09T04:22:47.803Z [INFO] init: LiveAPI callback invoked  
v8: 2025-11-09T04:22:47.803Z [INFO] init: LiveAPI callback invoked  
v8: 2025-11-09T04:22:47.803Z [INFO] init: Detected property set to tracks  
v8: 2025-11-09T04:22:47.804Z [INFO] init: LiveAPI callback invoked  
v8: 2025-11-09T04:22:47.804Z [INFO] init: Detected first callback after property set
v8: 2025-11-09T04:22:47.804Z [INFO] init: Initialization complete & Property set to tracks

--- 手動でトラックを追加した場合にのみハンドラが呼び出されている ----

v8: 2025-11-09T04:25:46.433Z [INFO] init: LiveAPI callback invoked  
v8: 2025-11-09T04:25:46.433Z [INFO] init: Detected change in live_set tracks

それとなく当初の目的は達成できたような気がします。
これをうまくラップしたクラスを実装してみました。

ログ出力は以下の通り

v8: 2025-11-29T18:02:08.331Z [INFO] main: Device initialization complete!  
v8: 2025-11-29T18:02:08.332Z [INFO] LiveObjectObserver: LiveAPI initialized  
v8: 2025-11-29T18:02:08.332Z [INFO] LiveObjectObserver: Detected property set  
v8: 2025-11-29T18:02:08.332Z [INFO] LiveObjectObserver: Detected first change notification  
v8: 2025-11-29T18:02:08.332Z [INFO] LiveObjectObserver: Set property  [property=tracks] 

--- 手動でトラックを追加した場合にのみハンドラが呼び出されている ---

v8: 2025-11-29T18:02:13.937Z [INFO] main: Tracks changed  
v8: 2025-11-29T18:02:15.321Z [INFO] main: Tracks changed  

変更自体は検知できますが、状態の差分はLiveAPIでは提供されていません。
必要に応じて状態と差分を管理するロジックを実装すると良いでしょう。

しかし...このLiveAPIのコールバックの挙動に半月ほど悩まされました~...

おわりに

何かと想定と異なる挙動に何かと遭遇する場面が多々ありつつ、良くも悪くもなんとか慣れてきた今日この頃です。
こじんまりとしたプラクティスでしたが、同じ轍を踏まないことを祈り記事にした次第です。

それでは良いお年を。

追伸

株式会社FOLIOでは現在、メンバーを募集中です!
会社の雰囲気や仕事の様子など、カジュアル面談でぜひお話ししましょう。

カジュアル面談(社員紹介専用フォーム/社員紹介の方のみこちらからご応募下さい) - 株式会社FOLIO

追追伸

楽器の経験がある方や現在進行形で音楽活動をしている方など、音楽に親しみがあるメンバーが何気に多い会社に思います。
音楽性は違えど 互いを尊重し、各々の専門領域を活かしながら、妥協ない仕事ができる環境です。

会社の雰囲気や仕事の様子など、カジュアル面談でぜひお話ししましょう。(大事なことは2回言うタイプ)

カジュアル面談(社員紹介専用フォーム/社員紹介の方のみこちらからご応募下さい) - 株式会社FOLIO