Blog

承認ボタンを押しただけ! Cursor がやってくれた1時間の業務記録 シリーズ「しかばねエンジニアが逝く」その1 〜 社内雑用、今週中? そんなタスクは AI に丸投げだ

blog_skelton_20260729_01.png

オシャレなしゃれこうべと申します。

賢明な読者の皆様は一目でお分かりになったことでありましょう。
このキャッチ画像が ChatGPT が作ったものであること、そしてわたしが何者であるかを。

そう、わたくしは、世の定説どおり35歳で寿命を迎えたエンジニアでございます。
死ぬ間際の最期の記憶は 「エンジニア35歳定年説って本当だったんだ」 というものでした。
しかし、死して会社に骨を拾われ、死霊術なぞのぎしきにより復活を遂げて以来、こうして労働に使役されております。

死んでも勤怠を打刻する時代となったようです。

ところで、死ぬとひとつ困ることがあります。
脳がないので道理ではあるのですが、生前に覚えたはずの技術知識が、なんとも朧げで儚いのです。

今日は、そんな朧げなわたしが、「予算も時間もつかない面倒なタスク」を AI に雑に投げたら、ほぼ勝手に片付いてしまったという実話をお届けします。

しかばねのみなさまには大変に役にたつ知識でございます。

あるお仕事の依頼

ある日、弊社のディレクターから声をかけられました。

「自社サイトの過去記事、表組みの表示が崩れてるの、直せます? 新しい記事は対応済みなんですけど。 あ、案件の合間でいいので。 今週中に」

合間でいいので、今週中に。
木曜日にこの二つの句を平然と接続してくるあたり、現世は相変わらずのようです。

24時間戦えますか? オーイェース! しかばねだからね!

などと昭和の言葉をしゃべっていたら、令和な彼女がこう言いました。

「AIなら24時間働いてくれますよ」

なるほど?

たしかに問題そのものは単純です。
AI でもできるかもしれません。
表組みが崩れているのは、CSS のクラスがひとつ追加で必要になったから。
新しい記事は対応済みで、古い記事だけが取り残されている。
古い記事はデータベースの中にあるので、DB の中身を書き換えれば直ります。

簡単そうだね?

簡単ではないことは、調べる前から分かっていた

弊社は AEM、Drupal、WordPress など、さまざまな CMS でのサイト構築を生業としております。
動的な要素の少ないサイトには MovableType をよく使い、弊社サイトも MovableType 製です。
完全な静的サイトとして出力できるためセキュリティに優れ、AWS S3 に置けば Web サーバーも不要。
維持費もやすーい!

いいね!

ただし、記憶の朧げなしゃれこうべには、問題がふたつありました。

ひとつ。
MovableType は商用製品で、完全静的出力というすぐれてニッチな需要を満たすものですから、内部仕様をググっても誰も教えてくれません。
WordPress ならすぐ出てくるのにね。

ふたつ。
MovableType は Perl でできています。Perl というのは &*%$@ のような記号でプログラムを書く言語です。呪文です。しかも弊社サイトは年季が入っていて、独自プラグインもいろいろ入っている。

仕様書はありません。

社内の MT プロに聞けばよいのですが、彼らが忙しいからこそ、依頼がしゃれこうべに回ってきたのでしょう。
Perl と MovableType に詳しい人材は年々希少です(わたしのように、あの世へ行った者もいるでしょう)。

table タグに class をつけるだけの改修に、詳しい人を捕まえて仕様を聞いて回る時間はありません。
「案件の合間・今週中」というご予算です。
クライアント案件でもない、自社サイトのちょっとした保守。

つまり整理するとこうです。

  • 技術がちょっとニッチで、あいにく詳しい人が忙しい
  • ドキュメントがなく、調査工数が読めない
  • 改修は小さく、予算も時間もつかない

三拍子そろった、どこの現場の樽にも漬かっている、いわゆる漬物タスクです。

生前のわたしなら休日に成仏させていたところですが、死後のわたしは AI に投げることにしました。

依頼文は三行

弊社で契約している AI ツールの Cursor に、こう伝えました。

過去の記事データについても、表組みに例のクラスを付与したい。
また、実際に書き換えが発生した記事のパーマリンク一覧も取得したい。
まずは実現手段の調査をお願いいたします。

以上です。データがどのテーブルにあるかも、MT のどの API を使うべきかも指定していません。
指定しようにも覚えていないので、致し方ありません。

そして先に申し上げておきますと、ここから先、わたしはほぼ何もしておりません

彼(Cursor のことです。以下そう呼びます)は勝手に調べ、勝手に作り、節目ごとに報告を置いていきました。つきっきりで面倒を見るのではなく、上がってきた事後報告を読んで「本当か?」を確かめ、承認ボタンを押す。

それだけです。

眼窩を半分閉じて報告を待つ(比喩です。まぶたはありませんでした)、楽な現場監督でございました。

以下は、その事後報告を時系列に並べたものとお読みください。

報告その一: 調査

最初の報告には、すでに開発用 DB へ流した SELECT の結果が添えられていました。コードを眺めるだけでなく、ローカル開発環境の実データを勝手に確認していたのです。

-- 本文にテーブルを含む記事: 0 件
-- カスタムフィールド(mt_entry_meta)にテーブルを含む記事: 数百件

つまり、表組みの HTML は記事本文ではなく、カスタムフィールドに保存されている。ここを確認せず「本文を書き換えるスクリプト」を雑に作っていたら、一件もヒットせず空振りするところでした。

しっかりしてるなあ。

報告は続きます。

  • カスタムフィールドの中身は MTAppjQuery の multifield 形式——JSON 文字列の中に、エスケープされた HTML が入っている——であること。
  • テーブルの class 属性には三系統あり、現行形式が約 40 記事、旧 CMS 時代の遺物らしき別形式が約 40 記事、Word 貼り付け由来の `MsoNormalTable` が数記事あること。
  • 仕様書のない自社サイトの仕様を、実データから独力で読み解いてきた形です。

そのうえで、報告の末尾には質問が残されていました。

旧形式も対象に含めますか?
下書き記事はどうしますか?
更新日時(modified_on)が変わりますが許容できますか?

更新日時が変わるとテンプレートの表示に波及する、という論点は、こちらの頭蓋にはありませんでした。言われてみれば全部その通りで、生前ならレビューの場で先輩に指摘されていたやつです。

この時点でわたしがしたのは、三つの質問に返事を書くことだけ。
仕事が早いだけでなく、詰めていく順番も正しいようです。

報告その二: 実装方針

次の報告では、実装方針が決まっていました。これがまた洗練されていました。

彼は MT 本体のソースツリーを漁り、tools 配下から約 700 行の一括置換スクリプトを発掘してきて、「全件走査・カスタムフィールドの読み書き・リビジョン保存・dry-run まで実装済みの雛形として使える」と言うのです。

ドキュメントがなければ、公式の実装例を探す。
つまり、埋もれていた骨を拾ってきて、蘇らせて、働かせる。
……どこかで聞いた話だなあ、と思いながら承認ボタンを押しました。

「modified_on は変えないでほしい」という追加の注文には、MT::Object のソースの該当行を挙げて、こう返してきました。

modified_on は「新規作成時」または modified_by が呼ばれたときにしか自動更新されません。
tools スクリプトからの save では modified_by を呼ばないため、変わりません。
念のため、保存前に元の値を再セットするガードも入れます。

根拠がソースの行番号付きです。
推測でものを言わず、黙々とソースにあたる。
このちゃんとしたエンジニアぽい習性が、後述するとおり、こちらの検証をずいぶん楽にしてくれました。

報告その三: 三つの罠と、その自己解決

すんなり終わったわけではありません。
つまずきは明確に三回ありました。

ただし、わたしがその三回を知ったのは、いずれも解決した後です
報告には「こういう問題が起きた。原因はこれ。こう直した」が三セット、きれいに並んでいました。
骨がお昼休憩をしている間の出来事です。

罠その一 — パーマリンクが全部トップページ

「書き換えた記事のパーマリンク一覧」を作るために MT 標準の `$entry->permalink` を使ったところ、全記事の URL がサイトトップになってしまった。

原因は、このサイトの記事アーカイブが、記事の種類によって出力先が分岐する動的な file_template で定義されていること。単純なパーマリンク計算では実 URL を求められない構成だったのです。

そこで彼は方針を転換し、「公開済みページの実 URL は `mt_fileinfo` テーブルに記録されている」ことに気づいて、そちらから引く実装に書き換えました。

mt_fileinfo。渋いところを突いてきます。MT の内部構造を知らないとまず出てこない選択ですが、彼は本体のソースを読み解いて調べ上げてきました。

罠その二 — 「クラス追加以外、何も変えていない」ことの証明

一括書き換えでいちばん怖いのは、意図しない差分です。
彼が書き換え前後のデータを比較したところ、案の定、差分が検出されました。

調べると、原因はふたつの重なりでした。

ひとつ、検証に使った mysql CLI が出力時に `\` をエスケープするため、そのままでは JSON として読めないこと。
ふたつ、JSON をデコードして再エンコードすると、キーの順序が入れ替わること。どちらも「バイト列が違うだけで、意味は同じ」という偽の差分です。

彼はこれを切り分けたうえで、キー順を正規化した canonical JSON 同士を比較する方式に切り替え、「書き換え前後は、クラス追加を除いて意味的に完全同一」であることを機械的に証明してみせました。

「バイト列は違うけど意味は同じはずだから」と曖昧なまま流さない。
締切前の人類が最初に省略しがちな工程です(身に覚えのある方も多いでしょう)。

ここを省略しないのは頼もしい。

罠その三 — 「成功」するのに何も生まれない再構築

最大の罠がこれでした。

書き換え後、静的ページを再構築するオプションを実行したところ——実行したのも彼ですが——ログは全件成功、エラーなし。
しかし、生成されたはずの HTML がどこにも存在しない。
存在しないことに気づいたのも、彼でした。

彼は一件だけを再構築する最小のテストを書き、今度こそエラーを引きずり出しました。
曰く、カスタムフィールドのテンプレートタグが「存在しない」。
フィールド定義は DB に確かにある。ではなぜか。

答えは、MT の商用パック(Commercial.pack)のソースの中にありました。
カスタムフィールドの動的タグは、MT::App の init_app コールバックの中でしか登録されない。
つまり、管理画面や CGI 経由の再構築なら動くのに、tools スクリプトのような素の `MT->new` から再構築すると、タグが未登録のままテンプレートが評価され、エラーも出さずに静かに失敗する——という仕様(という名の罠)です。

彼は該当の登録処理(`CustomFields::Util::install_field_tags`)を特定し、スクリプト側で明示的に呼ぶ修正を入れて解決しました。

わたしの仕事: 報告の検証

さて、ここまで読むと「で、おまえは何をしていたんだ」と思われるでしょう。
もっともです。骨も自問しました。

わたしの実働は、上がってきた報告が本当かどうかを確かめることでした。
AI が堂々と間違えるのは、骨界隈でも有名な話です。
「調べました。直しました。証明しました」と言われても、鵜呑みにして本番 DB に流すわけにはいきません。

ところが、この検証が拍子抜けするほど簡単でした。
理由は単純で、報告には検証に必要な材料が最初から全部添えられていたからです。

  • 「カスタムフィールドに数百件」という主張には、集計に使った SQL がついている。同じ SQL を流せば、同じ数字が出る
  • 「modified_on は変わらない」という主張には、ソースのファイル名と行番号がついている。その行を開いて読めばいい
  • 「意味的に完全同一」という主張には、比較スクリプトと差分ゼロの出力がついている。手元で再実行するだけ
  • 各記事にはリビジョンが保存されている。最悪の場合も、戻せる

主張と根拠がセットで届くので、こちらは答え合わせをするだけです。白紙から仕様を調べるのと、根拠つきの答案を採点するのとでは、必要な工数がまるで違います。

脳のない者にも優しい設計です。

しかも最終報告の時点で、彼は自分でも一通りの検証を済ませていました。
dry-run の対象件数と、SQL による独立集計が一致すること。
全件実行後にもう一度実行すると「対象 0 件」になること(冪等性)。
公開済み全記事の更新日時を前後でスナップショット比較して、差分ゼロであること。
再構築された HTML に、目的のクラスが出力されていること。

「書き換わったこと・壊れていないこと・戻せること」。

この三点を多角的に確かめてから、はじめて完了報告が来ました。
人間が手でここまでやれば、検証だけで半日仕事です。

成果 — かくして骨は今週中に間に合った

成果物は三点です。

  • 一括付与スクリプト(dry-run、書き換えた記事一覧の CSV 出力、リビジョン保存、再構築オプション付き)
  • 書き換え対象記事のパーマリンク一覧 CSV
  • 本番実行の手順書(バックアップ → dry-run → 1 件だけ実行 → 全件実行 → 検証 SQL、までの全手順)

所要時間は、依頼から完了報告まで約 1 時間。そのうちわたしが手を動かしたのは、最初の依頼三行、スコープ確認への返事、方針への承認、そして報告の答え合わせだけです。残りの時間、彼はひとりで働き、骨は座っておりました。

multifield 形式の解読、file_template の仕様、タグ未登録の罠——いずれも「ソースを読む以外に調べようのない」情報です。

Perl と MT に多少覚えのある人間でも、まともにやれば半日から一日はかかったでしょう。

なお、ディレクターに完了報告をしたところ「ほらー、やっぱりやればできるじゃないですか」と返ってきました。

たしかに、依頼の三行を書いたのはわたしです。
わたしはやればできるしゃれこうべです。

骨の考察 — しゃれこうべでも、AI があれば

正直に言うと、着手前のわたしは高を括っておりました。

「MovableType の内部仕様なんてマイナーな知識、AI が知っているわけがない。使えないだろう」と。

その見立ては半分正しかったのです。
彼は MT を「知って」いたわけではありません。
パーマリンクの罠も、タグ登録の罠も、最初は普通に踏んでいます。

決定的だったのは、知らないことをソースと実データから調べ、仮説を立てて検証するループを、自律的に回し続けたことでした。
数十万行の Perl、ほとんど情報のない Web、独自プラグインで拡張された非標準構成。
ここで物を言うのは知識の量ではなく調査の腕力で、それが今の AI エージェントの強みなのだと思います。

そしてもうひとつ。
根拠つきで報告してくる相手は、監督するのが楽です。人間の側の仕事は「つきっきりで面倒を見ること」ではなく「事後報告を採点すること」に変わり、その採点材料は向こうが揃えてくれる。だから、こちらの記憶が朧げでも回るのです。

だとすると、いちばんおいしいのは今回のような仕事でしょう。
技術がニッチで、詳しい人が忙しくて捕まらない。
ドキュメントがなくて、調査工数が読めない。
改修自体は小さくて、そもそも社内タスクなので、予算も時間もつかない。

この三拍子がそろって樽の底に漬かり続けている漬物タスクの山にこそ、雑に投げてみる価値がありました。

記憶を朧げにしか持たないしゃれこうべでも、AI があれば仕事が回る。
生きていて記憶もお持ちのみなさまに、使えない道理はないはずです。

まとめ

MovableType + MTAppjQuery + 独自プラグインという、ドキュメント不在のレガシー構成の一括データ改修。
三行の依頼を投げたら、Cursor が調査・実装・検証・本番手順書の作成までを自律的に完遂しました。
途中、MT の内部仕様に起因する罠を三度踏みましたが、いずれも報告が届く前にソース解析で自己解決。人間の実働は、依頼・スコープへの返事・承認・報告の答え合わせのみ。

所要 1 時間でした。

一発で派手なデモができあがる系の話より、予算も時間もつかない泥臭い現場でこそ真価が見える——というのが、一度死んだ身の実感であります。

同じような漬物タスクを抱えている方は、ものは試しに、雑に投げてみてください。

なに、社内雑用なら失敗したところで死にはしません。死んでも、案外なんとかなっております。