4コママンガ『ハコブくん』の英語版は描き直さずに作れた ― 次は英語圏の X へ

この記事の素材(コード・ログ・データ 12ファイル・まとめてダウンロードあり)

4コママンガ『ハコブくん』の英語版は、絵を描き直さずに、吹き出しの日本語を消して英語をフォントで組み直す方法で作り、縦長の29話(116コマ)を費用0円で pixelstomotors.com に載せました。 絵は日本語版と1画素も変えていません。あわせて、英語版の全125話を、英語圏の読み手に向けて X に平日1日2本ずつ出す予約をしました。

『ハコブくん』は、病院で薬を運ぶロボット・ハコブくんと、看護師さんたちの4コママンガです。ロボットが現場に入ったときに起きるすれ違いを、毎話1つ描いています。最近の話は、1コマを縦長(縦横比 4:5)の1枚の絵として、画像を作る AI(Google の Gemini)で描いています。この描き方では、吹き出しの日本語も絵の一部として描かれます。英語版を作るには、絵を英語で描き直すか、絵の中の日本語だけを入れ替えるかのどちらかになります。

描き直すと、1コマずつお金がかかり、人の顔や手の形も日本語版と少しずつ変わってしまいます。そこで、吹き出しの中だけを白で塗って英語を置く小さなプログラムを書きました。吹き出しの外の画素が1つでも変わったら、保存せずに止まる作りです。名札や画面の日本語は、まわりの色で消して英語を置きました。英語の字は、これまでの英語版のマンガと同じ Comic Neue という手書き風のフォントにそろえました。

X には、リンクを付けずに4コマの絵だけを載せて出すことにしました。X の API(プログラムから投稿する仕組み)は、リンクの入った投稿が1本0.20ドル、リンクのない投稿が0.015ドルで、リンクを付けたままだと日本語の投稿と合わせて月に約35ドルかかるためです。 リンクを外すと月に約3ドルになります。いいねやリポストが増えてきたら、リンク付きに戻すかを決めます。

やったことの全体は次のとおりです。

やったことなぜ結果
第133話で、吹き出しの日本語を消して英語を組む方法を試した
描き直さずに英語版を作れるかを確かめる
4コマと表紙ができ、吹き出しの外で変わった画素は0
英語のフォントを、今までの英語版に合わせた
最初の版のフォントが英語圏のマンガに見えなかった
吹き出しは Comic Neue、表紙は Roboto Mono にした
残りの28話を、同じプログラムで作った
縦長の話を全部英語にする
139個の吹き出しと24個の名札・画面の字を英語にした
作った人とは別の目で、1コマずつ日本語版と見比べた
消し残し・はみ出し・取り違えを見つける
28話のうち22話で、改行や言い方・表紙の一言を直した
マンガのページに「一覧へ戻る」リンクを足した
英語版のページから一覧へ戻る道がなかった
日本語は「4コママンガ一覧」、英語は「All episodes」
英語版の全125話を、英語圏の X に出す予約をした
英語で読む人に届ける
2026-11-02〜2027-01-27 の平日、米国東部時間 11:00 と 15:00
X の投稿のリンクを外したリンク入りの投稿の費用が高い
月に約35ドルの見込みが、約3ドルになった
X に画像を上げられるかを確かめた
英語の投稿は画像を上げてから出す
鍵・画像のアップロードとも OK(投稿はしていない)

ここから下は作業ログです。使ったコード・指示・確かめた記録は、あとで同じ手順を追えるように全文を載せています。効果の欄は、数字があるものは測った値、ないものは見込みです。

作業ログ ― 英語版を、描き直さずに作る方法を決めた

縦長の4コマは、日本語の字が絵の中に入っている

縦長の4コマは、1コマずつ Gemini の API に「この場面を、このセリフで描いて」と頼んで描いています(作り方は LOG410・LOG413)。セリフは、吹き出しの中の字として絵の中に描かれます。日本語の字だけの層があるわけではないので、字を差し替えるには、絵の中の字そのものを消す必要があります。

英語版の作り方は、3つ考えました。

作り方費用絵決めたこと
Gemini に、英語のセリフで描き直してもらう
1コマ約10円(描き直しを含めると増える)
顔・手・小物が日本語版と少しずつ変わる
使わない
Gemini に、字だけ英語に書き換えてもらう
1コマ約10円
字のまわりの絵が描き変わることがある
使わない
吹き出しの中を白で塗り、英語をフォントで組む
0円
吹き出しの外は1画素も変えない
これにした

3つ目は、プログラムで吹き出しの範囲を見つけて、その内側だけを塗るやり方です。画像を作る AI を使わないので、お金はかかりません。そのかわり、吹き出しの形や大きさに合わせて英語の改行と字の大きさを決める必要があります。

  • ねらい:日本語版と同じ絵のまま、英語で読めるようにする
  • 変わったこと:英語版を作るのに、画像を作る AI を使わなくなった
  • 見込める効果:英語版の費用は0円。絵が日本語版と同じなので、描き直したコマの間違いを確かめ直す手間がない(吹き出しの外で変わった画素は、全116コマで0だった)

吹き出しの見つけ方:白い1点から塗り広げる

吹き出しの中の白い1点(seed)から、同じくらい白い画素を上下左右に塗り広げていくと、吹き出しの縁の線で止まります。止まった範囲が吹き出しです。画像を編集するソフトの「塗りつぶし」と同じ考え方です。

うまくいかない形が2つありました。1つは、ささやき声などで縁が破線の吹き出しです。破線のすき間から塗りが外へ漏れます。この場合は、縁の黒い線を数画素太らせてすき間を閉じてから塗ります。もう1つは、2人の吹き出しが線でつながって1つの白い領域になっている場合です。この場合は、それぞれの吹き出しの範囲を四角(clip)で指定して、その中に英語を組みます。

見つけた範囲は、内側へ6画素縮めてから白で塗ります。縁の線とその際の灰色の画素を残すためです。英語は44画素の大きさから1画素ずつ小さくして、どの行も吹き出しの幅に収まる大きさを探します。

吹き出しの外で1画素でも変わったら止める

塗る範囲を間違えると、人の顔やロボットの体を消してしまいます。そこで、日本語版と英語版の画素を1つずつ比べ、吹き出しと名札などの字の四角の外で1画素でも変わっていたら、保存せずに止まるようにしました。作っている途中で、名札の英語が字の四角から少しはみ出し、ここで止まったことがあります。英語は四角の内側にだけ描くように直しています。第133話では、ホワイトボードの見出しの欄を丸ごと塗ると、すぐ下の人の頭まで消えてしまうことが分かり、見出しの字のかたまりだけを消すようにしました。

名札・画面・張り紙の字は、吹き出しと違って地が白とは限りません。四角の中の画素の明るさを見て、明るい地なら明るいほうの色、黒い板に白い字なら暗いほうの色で塗り、その上に英語を置きます。英語が四角の外にはみ出しそうなときは、四角の内側だけに描きます。

作業ログ ― 第133話で試し、フォントを今までの英語版に合わせた

最初の版は、英語圏のマンガに見えなかった

第133話「ハンコがそろうまでの2か月」の4コマで、最初の版を作りました。吹き出しの字は、最初はふつうの文章用のフォントで組みました。スマホで見たエイイチの感想は次のとおりです。

フォントはいわゆる英語圏で使われるフォントにしてほしい。今までの英語版のマンガ参考にして。確認してみて。

pixelstomotors.com には、横長の4コマ(1枚の絵に4コマ)の英語版が、すでに96話あります。そのページの画像を開いて確かめると、吹き出しの字は手書き風のフォントでした。形が近いフォントを探し、Comic Neue の太字に決めました。Comic Neue は、マンガの吹き出しによく使われる Comic Sans を、整えて描き直したフリーのフォントです。

表紙の一言は、別のフォントだった

英語版の一覧のページに並ぶ表紙は、上に「EPISODE N」、その下に大きな大文字の一言があり、その下に絵がある形です。最初は、この表紙の一言も Comic Neue で組みました。エイイチから次の指摘がありました。

表紙のフォントは違うからね!注意して!

今までの表紙の画像を拡大して字の形を比べると、すべての字の幅が同じ等幅のフォントで、Roboto Mono の太字と形が一致しました。字の間隔もわずかに詰めてあったので、その詰め方まで合わせました。一言が長いときは、2行(字が小さくなるときは3行)に収まるまで字を小さくします。

英語版の表紙6枚(第132・131・130・129・127・118話)。どれも上に EPISODE と話の番号、その下に Roboto Mono の大きな大文字の一言、下にコマの絵がある

フォントのファイルは、Fontsource(フリーのフォントをファイルで配っているところ)から、jsDelivr を通して取りました。作業をしている Mac の中の環境は外へのつながりが限られていて、Google Fonts から直接は取れなかったためです。

第132話3コマ目の日本語版(左)と英語版(右)。絵は同じで、2つの吹き出しの字だけが英語になっている

作業ログ ― 残りの28話を作り、別の目で確かめた

28話を4つに分け、指示(spec)を書いてもらった

1話ごとに、どの吹き出しにどの英語を入れるかを書いた指示のファイル(spec)を作ります。吹き出しの白い1点の座標・英語・改行の位置・名札などの四角・表紙の一言・英語のページのタイトルと解説文です。

まず、吹き出しの候補を自動で探すプログラム(detect.py・filt.py)で、白くて中に字のある閉じた領域に赤い枠と番号を付けた画像を作りました。候補には、顔やロボットの胴の白い部分も混ざり、逆に小窓の中の小さな吹き出しが抜けることもあります。そこで、28話を7話ずつ4つに分け、Claude の別の作業役4人に、画像を見て spec を書いてもらいました(指示は下の AGENT_TASK.md)。

書いた人とは別の作業役が、1コマずつ見比べた

spec からプログラムで英語版を作ったあと、spec を書いた作業役とは別の4人に、日本語版と英語版を1コマずつ並べて見てもらいました(指示は下の QA_TASK.md)。見るのは、日本語の消し残し・英語のはみ出し・1語だけの行・字の小ささ・話者の取り違え・名札の消し方・表紙の一言の7つです。

28話のうち22話で、改行・言い方・名札の四角・表紙の一言を直しました。直した例です。

話見つかったこと直した形
第93話
“robot.” だけの行ができていた
“I am a / transport robot.” の2行に
第129話
“This was half the price in Japan!” が「日本では半額だった」とも読める
“Half what it costs / here in Japan!” に
第131話
張り紙の英語が小さい(29画素)
四角を紙いっぱいに広げ、手書き風の字で58画素に
第117話
表紙の一言が絵(ロボットが向こうを向いている)と合わない
4コマ目のセリフ “YOU CAN TURN AROUND NOW” に
第126話
エレベーターの「運転休止」の黒い板が、灰色の帯になって浮いていた
四角を板の内側いっぱいに広げた(色は灰色のまま)

直せなかったものも残っています。小窓の中の吹き出しや細い縦長の吹き出しは、日本語版の字も12〜21画素と小さく、英語も16〜25画素までしか大きくできませんでした。名札の字も7〜11画素で、日本語版と同じく読めない大きさです。第99話の表紙は、日本語版の表紙の絵にモニターの日本語の通知が入っていたので、英語にした2コマ目を絵に使って作り直しました。

28話の吹き出しの字の大きさは、139個のうち中央値が44画素(いちばん大きい設定)で、26画素未満は4個でした。

英語版の4コマの一覧のページ。第133〜125話の表紙が3列に並び、どれも上に EPISODE と大きな大文字の一言がある

公開の形:画像は R2、ページは p2m-site

英語版の画像(1話につき4コマと表紙の5枚、29話で145枚)は webp にして、Cloudflare の R2(画像や動画を置いておくところ)に入れました。R2 は同じ名前のファイルを上書きしない決まりにしているので、作り直した第133話と第99話の表紙は _v2 の名前で入れています。英語のページは、サイトのコード(GitHub の p2m-site)に1話1つの JSON を足して作ります。145枚がすべて開けること(HTTP 200)と、29話のページが前の話・次の話でつながることを確かめました。

作業ログ ― マンガのページに「一覧へ戻る」リンクを足した

英語版のページを見ていたエイイチから、次の指摘がありました。

UI的にマンガのページに移った時に一覧ページに戻るボタンがないね(日本語はヘッダーで戻れるけど、それでも迷うな)

マンガのページの上、タイトルの前に、矢印と「4コママンガ一覧」(英語版は「All episodes」)のリンクを足しました。どの話のページにも同じ部品(MangaPage.astro)が使われているので、1か所の変更で日本語・英語の全話に出ます。

同じときに、縦長の4コマをめくる部品(MangaSwipe.astro)で、ページを開いた直後に「NaN / 4」と出ることがあるのも直しました。読み込み直後は表示の幅が0で、0で割っていたためです。

  • ねらい:英語版のページだけを開いた人が、ほかの話を探せるようにする
  • 変わったこと:全話のページの上に、一覧へ戻るリンクが出るようになった
  • 見込める効果:1話を読んだ人が、一覧から次の話へ進みやすくなる(効果は、マンガのページから一覧のページへ移った回数で確かめる)
スマホで見た第133話のページ。左が日本語版で上に「← 4コママンガ一覧」、右が英語版で上に「← All episodes」がある

作業ログ ― 英語版の全125話を、英語圏の X に出す予約をした

この記事のコード・ログ・データは、1ファイルずつ下に置いています。全文はリンク先にあります。

ファイルの一覧(12本)

01_X_verify.txt ― X に画像を上げられることを、verify で確かめた / verify を動かしたときのログ(SNS自動投稿 #136 の post)・13,309字

02_sec06.py.txt ― en133.py(第133話の試作)・7,457字

03_en_manga.py.txt ― en_manga.py(28話を作ったプログラム)・12,299字

04_AGENT_TASK.md ― AGENT_TASK.md(spec を書く作業役への指示)・3,659字

05_spec_ep132.json ― spec/ep132.json(第132話の指示)・3,608字

06_sec06.txt ― 28話を作ったときの記録(out/epNN.log)・30,651字

07_sec06.json ― src/data/englishCards.json に足した29行・13,898字

08_MangaPage.astro.txt ― MangaPage.astro(マンガのページ。一覧へ戻るリンクを足した)・5,429字

09_TASK.md ― TASK.md(X の英語の文を書く作業役への指示)・3,791字

10_sec06.json ― sns/schedule.json に足した125行・17,553字

11_tools_sns_autopost.py.txt ― tools/sns_autopost.py(X の自動投稿。画像・返信・リンクの切り替えを足した)・25,153字

12_sec06.md ― docs/sns_autopost.md の「X のリンク」と「マンガの英語の X」・4,646字

投稿の時刻・形・タグを、英語圏に合わせて調べた

エイイチの依頼は次のとおりです。

英語版の4コママンガを定期的に英語圏にXでアップしてほしい(今までのマンガも投稿し直し) 英語圏に合わせて文章や投稿時間、タグなどを調べて見直してみてください。

調べて決めたことです。

決めたこと中身もとにしたこと
時刻
平日(月〜金)の米国東部時間 11:00 と 15:00(日本時間では冬は翌1:00と5:00)
読む人の地元の時刻で平日の午前が最も反応がよく、土曜と夜は弱い(Buffer の約870万投稿の分析)。火〜木の昼〜夕方がよく、技術の分野は11〜16時(Sprout Social)。米国の東部と英国の夕方の両方に届く時刻にした
画像
4コマの絵を画像として付ける(縦長の話は4枚、横長の話は1枚)
画像のある投稿は文だけより表示されやすい。マンガは絵が中身なので、絵そのものを出す
画像の説明
1枚ずつ英語の説明(alt テキスト:画面を読み上げる機能や検索が読む文)を付ける
絵を見られない人にも何の場面か伝わる
本文
1行目で手が止まる一文 → 短い前振り1〜2行(4コマ目のオチは書かない)→ Hakobukun · Ep. N → タグ
絵を見に行く理由を残す
タグ
2つ:#webcomic と、話題のタグ1つ(#robotics #healthcare #nursing #AI #UX #cybersecurity から)
X ではタグは2つまでが目安。マンガを読む人が追う #webcomic を必ず入れる
リンク
本文には入れず、自分への返信に置く(このあと、返信も出さないことにした)
リンクのある投稿は表示が少ないという測り方がある。費用も高い

文は日本語の直訳にせず、英語で読む人がハコブくんの性格(まじめで、言われたとおりに受け取り、少し機械的)を楽しめる文にしました。第132話の例です。

The robot got an AI update overnight. Corners? Silky smooth now.

Nobody checked how it stops, though.

Hakobukun · Ep. 132
#webcomic #AI

125話を4つに分け、Claude の別の作業役4人に文と画像の説明を書いてもらいました(指示は下の TASK.md)。1本の長さは137〜208字で、どれもリンクを含みません。

投稿の仕組みに、画像と返信を足した

X への投稿は、GitHub の r2r2r-content にある予定表(sns/schedule.json)に時刻を書いておくと、GitHub Actions(GitHub の上で決まった時刻にプログラムを動かす仕組み)が sns_autopost.py を動かして出します。これまでは文だけでした。そこに、画像をつなぐ部分(X の /2/media/upload に画像を上げ、/2/media/metadata で説明を付け、投稿に画像の番号を付ける)と、投稿のあとに自分への返信を出す部分を足しました。投稿はせずに画像を上げられるかだけを確かめる verify にも、画像のアップロードを足しています。

予定表には、MANGA_ep{N}-x-en という名前で125本を入れました。11:00 は縦長の話(第73話から番号の順)、15:00 は横長の話(第1話から番号の順)です。最初の投稿は 2026-11-02 11:00(米国東部時間)、最後は 2027-01-27 です。

X の費用を見て、リンクを外した

予約のあと、エイイチから費用について質問がありました。

費用の大半は、リンクの入った返信(0.20ドル)です。返信をやめれば1本約0.02〜0.04ドルになります これはうちのサイトのマンガページへのリンクの意味ですか?

このお金はどこから支払う?

X の API の費用は、X の開発者向けの管理画面で前もって買っておくクレジットから引かれます。日本語の自動投稿も同じクレジットから払っています。エイイチが画面を開くと、10月の費用は5.40ドル、API を呼んだ回数は27回でした。日本語の投稿はすべて本文にリンクが入っていて、1本0.20ドルなので、数が合います。

X の開発者向けの管理画面の使用状況。10月1日〜31日の総費用が5.40ドル、リクエストが27回と出ている

X の API の料金は、投稿1本0.015ドル、リンクの入った投稿は1本0.20ドル、画像の説明は1つ0.005ドルです(2026-04-20 からの料金。料金の解説)。このまま続けた場合の1か月の見込みです。

投稿リンクありリンクなし
日本語(1日4本)約24ドル約1.8ドル
英語のマンガ(平日2本・画像の説明つき)
約10ドル約1.2ドル
合計約35ドル(約5,000円)約3ドル

残高は14.60ドルだったので、リンクありのままだと11月12日ごろに尽きる計算でした。エイイチの決定は次のとおりです。

日本語の投稿もリンクをやめる。今は投稿にリンク追加は無しの設定にしてください。いいねやリポストが増えたらリンク付きの投稿に変えます。

sns_autopost.py に、X にリンクを付けるかどうかの切り替え(X_LINKS)を足しました。GitHub のリポジトリの設定(Variables)で X_LINKS を 1 にしたときだけリンクを付け、ないときは本文の URL の行を外し、英語の返信も出しません。投稿文のファイルには URL を残しているので、1 にすればそのまま元の形で出ます。LinkedIn は今までどおりリンクを付けます。予定に入っていた X の272本で、URL が残る投稿がないことを確かめました。

  • ねらい:X の費用を、反応が増えるまで小さく抑える
  • 変わったこと:日本語・英語とも、X の投稿にリンクが付かなくなった。サイトへの入口は、X のプロフィールのリンクだけになった
  • 見込める効果:X の費用が月に約35ドルから約3ドルになる見込み。そのかわり、投稿からサイトに来る人は減る見込みで、これは pixelstomotors.com のアクセスの記録で確かめる

前の「吹き出しの見つけ方」の節の図解の下の段で、リンクありとリンクなしの1か月の費用を切り替えて見られます。

X のプロフィールのリンクは、いまは /consult(フィジカルAI導入の相談のページ)に向いています。投稿を見てプロフィールに来た人が、記事やマンガの続きを探しているなら、トップのほうが合うかもしれません。このまま様子を見ることにしました。

X に画像を上げられることを、verify で確かめた

英語の投稿は、今までの日本語の投稿と違い、X に画像を上げてから出します。最初の英語の投稿(2026-11-03 1:00 日本時間)の前に、投稿はせずに鍵と画像のアップロードだけを試す verify を動かしました。GitHub のリポジトリの Actions の画面で「SNS自動投稿」を選び、「Run workflow」で「鍵が使えるかだけ確かめる(投稿しない)」にチェックを入れて動かします(このセッションからは、GitHub の権限で手動の実行ができなかったので、エイイチが画面から動かしました)。

結果は23秒で成功しました。ログの最後の3行が、確かめた中身です。

確かめたこと結果
X の鍵
OK(@adaptdesignjp として読めた)
X に画像を上げる(第133話の1コマ目)
OK(media id が返った。投稿はしていない)
LinkedIn の鍵OK
GitHub Actions の「SNS自動投稿」#136 の画面。状態が Success、かかった時間が23秒で、post の仕事に緑のチェックが付いている

画面の下に出ている注意(Node.js 20 is deprecated)とお知らせ(ubuntu-latest が Ubuntu 26 に移る)は、GitHub が使う部品の移り変わりを知らせるものです。今回は Node.js 24 で動いて成功しているので、そのままにしています。

verify を動かしたときのログ(SNS自動投稿 #136 の post)

鍵の値は GitHub が *** に伏せて出しています。

01_X_verify.txt13,309字・171行・text
2026-10-31T08:45:03.1554242Z Current runner version: '2.338.2'
2026-10-31T08:45:03.1633229Z ##[group]Runner Image Provisioner
2026-10-31T08:45:03.1635482Z Hosted Compute Agent
2026-10-31T08:45:03.1636706Z Version: 20261021.618
2026-10-31T08:45:03.1638093Z Commit: 21505ad2541d5be6135ed84fc33697146cf75b8e
2026-10-31T08:45:03.1639557Z Build Date: 2026-10-21T00:45:21Z
2026-10-31T08:45:03.1661476Z Worker ID: {4d0ffd1b-826b-40b0-9c6b-849b714f2644}
2026-10-31T08:45:03.1663012Z Region: eastus
別のタブで開く保存

作業ログ ― 使ったコード・指示・記録(全文)

ここから下は、この記事でやったことを同じ手順で追えるように、コード・作業役への指示・作ったときの記録・確かめた報告を全文で載せています。作業役の報告は、英語で返ってきたものは英語のまま載せています。

en133.py(第133話の試作)

第133話の4コマを英語にした最初のプログラムです。吹き出しの位置とホワイトボードの見出しを、このファイルの中に直接書いています。公開している第133話の4コマは、このプログラムで作りました。

02_sec06.py.txt7,457字・152行・python
# 第133話 英語版:吹き出しの日本語を消して英語をフォントで組む(2026-10-31 試作)
import json, numpy as np
from collections import deque
from PIL import Image, ImageDraw, ImageFont
SRC="../仕上げ/第133話_P{}_1080x1350.png"; OUT="第133話_P{}_EN_1080x1350.png"
import os
FONTS=os.path.expanduser("~/mnt/Fonts")  # Mac の ~/Library/Fonts(Comic Neue。今までの英語版マンガに合わせる)
F_HUMAN=FONTS+"/ComicNeue-Bold.ttf"
別のタブで開く保存

en133_cover.py(第133話の表紙の最初の版)

第133話の表紙を Comic Neue で組んだ最初の版です。「表紙のフォントは違う」という指摘で使わなくなり、表紙は en_manga.py の make_cover(Roboto Mono)で作り直しました。

"""第133話 英語版の表紙:日本語版の表紙(_サイト掲載_20261031/covers_v2/make_covers.py)と同じ組み方で、
字は吹き出しと同じ Comic Neue。下の絵は英語の札にした P2(吹き出しのないコマ)"""
import os, numpy as np
from PIL import Image, ImageDraw, ImageFont
FB=os.path.expanduser("~/mnt/Fonts/ComicNeue-Bold.ttf")
W,H=1080,1350; X0=56; MAXW=W-2*X0
font=ImageFont.truetype(FB,96)
p=Image.open("第133話_P2_EN_1080x1350.png").convert('RGB')
a=np.asarray(p).astype(np.int16); a[a.min(2)>232]=255; p=Image.fromarray(a.astype(np.uint8))
head='Episode 133'; t='Six Stamps'; one=f'{head}: {t}'
# 日本語版と同じく1行に収める(入らなければ 88 まで小さくし、それでも入らなければ2行)
while font.getlength(one)>MAXW and font.size>88: font=ImageFont.truetype(FB,font.size-2)
lines=[one] if font.getlength(one)<=MAXW else [head,t]
c=Image.new('RGB',(W,H),'white'); d=ImageDraw.Draw(c); y=62
for ln in lines:
    f=font
    while f.getlength(ln)>MAXW: f=ImageFont.truetype(FB,f.size-4)
    d.text((X0,y),ln,font=f,fill=(36,36,36)); y+=f.size+18
top=y+40; A=H-top
g=np.asarray(p.convert('L'))<235; ys=np.where(g.any(1))[0]; xs=np.where(g.any(0))[0]
cy0,cy1,cx0,cx1=ys.min(),ys.max(),xs.min(),xs.max(); ch=cy1-cy0
s=min(A*0.94/ch,1.45) if ch<A else A/ch
q=p.resize((round(W*s),round(H*s)),Image.LANCZOS)
mx=(cx0+cx1)/2*s; my=(cy0+cy1)/2*s
if s>=1:
    L=int(min(max(mx-W/2,0),q.width-W)); Tp=int(min(max(my-A/2,0),q.height-A))
    b=Image.new('RGB',(W,A),'white'); b.paste(q.crop((L,Tp,min(q.width,L+W),min(q.height,Tp+A))),(0,0)); c.paste(b,(0,top))
else:
    t0=int(cy0*s); b=Image.new('RGB',(q.width,A),'white'); b.paste(q.crop((0,t0,q.width,min(q.height,t0+A))),(0,0)); c.paste(b,((W-q.width)//2,top))
c.save('第133話_表紙_EN_1080x1350.png')
print(lines, font.getlength(one), 's=',round(s,3),'top=',top)

analyze.py(第133話の吹き出しの位置を調べる)

第133話の日本語版のコマで、白い領域のかたまりと大きさを調べたプログラムです。

import sys, numpy as np
from PIL import Image
from collections import deque
SRC="../仕上げ/第133話_P{}_1080x1350.png"
def label(mask):
    H,W=mask.shape; lab=np.zeros((H,W),np.int32); n=0; stats=[]
    for y0 in range(H):
        row=mask[y0]
        for x0 in np.nonzero(row & (lab[y0]==0))[0]:
            if lab[y0,x0]: continue
            n+=1; q=deque([(y0,x0)]); lab[y0,x0]=n; cnt=0; ys=[];xs=[]
            while q:
                y,x=q.popleft(); cnt+=1; ys.append(y); xs.append(x)
                for dy,dx in ((1,0),(-1,0),(0,1),(0,-1)):
                    yy,xx=y+dy,x+dx
                    if 0<=yy<H and 0<=xx<W and mask[yy,xx] and not lab[yy,xx]:
                        lab[yy,xx]=n; q.append((yy,xx))
            stats.append((n,cnt,min(ys),max(ys),min(xs),max(xs)))
    return lab,stats
def run(p):
    im=Image.open(SRC.format(p)).convert('L'); S=2
    g=np.asarray(im.resize((540,675)))
    white=g>230
    lab,st=label(white)
    H,W=g.shape; out=[]
    for n,cnt,y0,y1,x0,x1 in st:
        if cnt<300: continue
        border = y0==0 or x0==0 or y1==H-1 or x1==W-1
        # holes: dark pixels inside bbox not in component but surrounded
        sub=g[y0:y1+1,x0:x1+1]; comp=lab[y0:y1+1,x0:x1+1]==n
        dark=(sub<120)
        # dark pixels strictly inside comp rows span
        inner=0
        for r in range(comp.shape[0]):
            c=np.nonzero(comp[r])[0]
            if len(c): inner+=dark[r,c[0]:c[-1]+1].sum()
        out.append((n,cnt,y0*S,y1*S,x0*S,x1*S,border,inner))
    print("P",p)
    for o in sorted(out,key=lambda o:-o[1])[:12]: print(o)
for p in sys.argv[1:]: run(int(p))

確認_数値.json(第133話)

en133.py が書き出した記録です。吹き出しごとの英語・字の大きさ・行と、吹き出しの外で変わった画素の数(0)が入っています。

{
 "1": {
  "items": [
   {
    "text": "The robot is all set and ready to go!",
    "size": 41,
    "lines": [
     "The robot is all set",
     "and ready to go!"
    ],
    "縁に残った暗い画素": 0
   },
   {
    "text": "Pharmacy",
    "size": 22,
    "lines": [
     "Pharmacy"
    ]
   },
   {
    "text": "Nursing",
    "size": 22,
    "lines": [
     "Nursing"
    ]
   },
   {
    "text": "Facilities",
    "size": 22,
    "lines": [
     "Facilities"
    ]
   },
   {
    "text": "IT",
    "size": 22,
    "lines": [
     "IT"
    ]
   },
   {
    "text": "Fire\nSafety",
    "size": 22,
    "lines": [
     "Fire",
     "Safety"
    ]
   },
   {
    "text": "Patient\nSafety",
    "size": 22,
    "lines": [
     "Patient",
     "Safety"
    ]
   }
  ],
  "吹き出しの外で変わった画素": 0
 },
 "2": {
  "items": [
   {
    "text": "Pharmacy",
    "size": 28,
    "lines": [
     "Pharmacy"
    ]
   },
   {
    "text": "Nursing",
    "size": 28,
    "lines": [
     "Nursing"
    ]
   },
   {
    "text": "Facilities",
    "size": 28,
    "lines": [
     "Facilities"
    ]
   },
   {
    "text": "IT",
    "size": 28,
    "lines": [
     "IT"
    ]
   },
   {
    "text": "Fire\nSafety",
    "size": 28,
    "lines": [
     "Fire",
     "Safety"
    ]
   },
   {
    "text": "Patient\nSafety",
    "size": 28,
    "lines": [
     "Patient",
     "Safety"
    ]
   }
  ],
  "吹き出しの外で変わった画素": 0
 },
 "3": {
  "items": [
   {
    "text": "I am ready to roll.\nAny time.",
    "size": 44,
    "lines": [
     "I am ready to roll.",
     "Any time."
    ],
    "縁に残った暗い画素": 0
   }
  ],
  "吹き出しの外で変わった画素": 0
 },
 "4": {
  "items": [
   {
    "text": "So before we moved\nthe robot, we had to\nmove everyone.",
    "size": 37,
    "lines": [
     "So before we moved",
     "the robot, we had to",
     "move everyone."
    ],
    "縁に残った暗い画素": 0
   },
   {
    "text": "For the next ward,\nall six sign-offs go out\non day one.",
    "size": 38,
    "lines": [
     "For the next ward,",
     "all six sign-offs go out",
     "on day one."
    ],
    "縁に残った暗い画素": 0
   },
   {
    "text": "Heading out.",
    "size": 37,
    "lines": [
     "Heading out."
    ],
    "縁に残った暗い画素": 0
   }
  ],
  "吹き出しの外で変わった画素": 0
 }
}

en_manga.py(28話を作ったプログラム)

第133話のやり方を、spec のファイルを読んでどの話でも作れる形にしたものです。iCloud の「ハコブくん/4コママンガ作成データ/道具/英語版/」に置いています。

03_en_manga.py.txt12,299字・269行・python
"""縦長4コマの英語版を作る(2026-10-31・第133話のやり方を全話に広げたもの)

  python3 en_manga.py <spec.json> <日本語のコマのフォルダ> <出力フォルダ> <フォントのフォルダ> [--cover <日本語の表紙>]

- spec の bubbles:吹き出しの白い内側の1点(seed)から吹き出しを見つけ、中の日本語を消して英語を組む
  (clip があれば、その四角の中に組む。2つの吹き出しが1つの白い領域でつながっているとき)
- spec の signs:絵に描かれた日本語の字(名札・画面・張り紙)を、まわりの色で消して英語を置く
- 吹き出しと字の外の画素は変えない(変わったら止める)
別のタブで開く保存

README.md(道具/英語版)

英語版の道具のフォルダに置いた使い方と決まりです。

# 縦長4コマの英語版の作り方(2026-10-31)

公開中の日本語版のコマ(R2 の webp)から、吹き出しの日本語を消して英語を組む。絵は描き直さないので費用は0円。

- `en_manga.py`:作るスクリプト。`python3 en_manga.py spec/epNN.json ja out fonts --cover ja/epNN_cover.webp`
- `spec/epNN.json`:話ごとの指示(吹き出しの白い内側の1点 seed と英語、つながった吹き出しの clip、名札・画面などの字 signs と英語、表紙の一言 cover_line)
- `specs_min.json`:第73〜132話の spec をまとめたもの(spec/ はここから書き出した)
- `ja/`:日本語版のコマと表紙(pixelstomotors.com/media から取ったもの)/`out/`:英語版の PNG/`media/`:R2 に入れた webp
- `fonts/`:吹き出し Comic Neue Bold(今までの英語版マンガと同じ)、表紙 Roboto Mono Bold(今までの英語版の表紙と同じ)、画面などの字 Inter SemiBold

決まり
- 吹き出しの英語はふつうの文の書き方(文頭だけ大文字)。改行は意味の切れ目
- 表紙は「EPISODE N」と、その話の印象に残るセリフ(大文字)を Roboto Mono で上に置き、下に日本語版の表紙の絵(字の入った絵なら英語にしたコマ)
- 擬音(ジリリリ・カラン など)はそのまま
- 吹き出しと字の外の画素が1つでも変わったら、スクリプトが止まる
- 画像の名前は `manga_epNNen_p1〜p4.webp`・`manga_epNNen_cover.webp`。直すときは名前を変える(_v2)。R2 は同じ名前の上書きをしない
- ページは p2m-site の `src/data/english/epNN.json` と `src/data/englishCards.json`
- 第133話だけは先に `縦長/第133話/英語版/` で作った

detect.py(吹き出しの候補を探す)

白くて縁で閉じていて、中に字のある領域を、吹き出しの候補として赤い枠と番号で示します。

# 吹き出しを探す:縁の線で閉じた白い領域のうち、中に文字の暗い画素を持つもの
import sys, json, glob, numpy as np
from scipy import ndimage as ndi
from PIL import Image, ImageDraw, ImageFont
S=sys.argv[1]
out={}
font=ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",56) if glob.glob("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf") else ImageFont.load_default()
for f in sorted(glob.glob(f"{S}/ja/ep*_p[1-4].webp")):
    key=f.split('/')[-1][:-5]
    im=Image.open(f).convert('RGB'); g=np.asarray(im.convert('L')).astype(int)
    white=g>230
    lab,n=ndi.label(white)
    border=set(np.unique(np.concatenate([lab[0],lab[-1],lab[:,0],lab[:,-1]])))
    sizes=ndi.sum(white,lab,range(1,n+1))
    cands=[]
    for i in range(1,n+1):
        if i in border or sizes[i-1]<4000: continue
        comp=lab==i
        filled=ndi.binary_fill_holes(comp)
        holes=filled&~comp
        dark=(g<120)&holes
        nd=int(dark.sum())
        if nd<60: continue
        ys,xs=np.nonzero(filled)
        # 文字らしさ:穴の中の暗い画素が多数の小さなかたまり
        dl,dn=ndi.label(dark)
        if dn<3: continue
        # 内側の点:距離変換の最大
        dt=ndi.distance_transform_edt(comp)
        y,x=np.unravel_index(np.argmax(dt),dt.shape)
        cands.append(dict(seed=[int(x),int(y)],bbox=[int(xs.min()),int(ys.min()),int(xs.max()),int(ys.max())],area=int(filled.sum()),dark=nd,blobs=int(dn)))
    cands.sort(key=lambda c:(round(c['bbox'][1]/150),c['bbox'][0]))
    out[key]=cands
    d=ImageDraw.Draw(im)
    for k,c in enumerate(cands):
        x0,y0,x1,y1=c['bbox']; d.rectangle([x0,y0,x1,y1],outline=(255,0,0),width=6)
        d.text((x0+8,y0+4),str(k+1),font=font,fill=(255,0,0))
    im.save(f"{S}/ja/{key}_det.png")
json.dump(out,open(f"{S}/det.json","w"),indent=0)
# 話ごとの確認シート(2×2)
for ep in sorted({k.split('_')[0] for k in out}):
    sh=Image.new('RGB',(1100,1370),'white')
    for p in range(4):
        t=Image.open(f"{S}/ja/{ep}_p{p+1}_det.png").resize((540,675))
        sh.paste(t,((p%2)*550,(p//2)*685))
    sh.save(f"{S}/ja/{ep}_sheet.png")
print({k:len(v) for k,v in out.items()})

filt.py(候補をしぼる)

候補のうち、形が楕円に近く、中に大きな字のかたまりが2つ以上あるものだけを残します。脚本の吹き出しの数と合わない話を書き出します。

import json,numpy as np,sys
from scipy import ndimage as ndi
from scipy.spatial import ConvexHull
from PIL import Image, ImageDraw, ImageFont
det=json.load(open('det.json')); ja=json.load(open('bubbles_ja.json'))
font=ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",60)
res={}; bad=[]
for key,cs in det.items():
    g=np.asarray(Image.open(f'ja/{key}.webp').convert('L')).astype(int); lab,_=ndi.label(g>230)
    keep=[]
    for c in cs:
        i=lab[c['seed'][1],c['seed'][0]]; comp=lab==i; filled=ndi.binary_fill_holes(comp)
        ys,xs=np.nonzero(filled); hull=ConvexHull(np.c_[xs,ys][::7]); sol=filled.sum()/hull.volume
        bw=c['bbox'][2]-c['bbox'][0]; bh=c['bbox'][3]-c['bbox'][1]; ell=filled.sum()/(np.pi*bw*bh/4)
        c.update(sol=round(sol,2),ell=round(ell,2))
        holes=filled&~comp; dk=(g<120)&holes; dl,dn=ndi.label(dk)
        szs=ndi.sum(dk,dl,range(1,dn+1)) if dn else []
        big=int((np.array(szs)>=40).sum()) if dn else 0
        tiny=float(np.array(szs)[np.array(szs)<12].sum()/max(1,dk.sum())) if dn else 1
        c.update(big=big,tiny=round(tiny,2))
        if sol>=0.80 and ell>=0.65 and c['dark']>=250 and big>=2 and tiny<0.25: keep.append(c)
    keep.sort(key=lambda c:(c['bbox'][1]//120, c['bbox'][0]))
    res[key]=keep
    ep,p=key.split('_'); n=ep[2:]; exp=len(ja[n]['panels'][int(p[1])-1])
    if len(keep)!=exp: bad.append((key,len(keep),exp))
    im=Image.open(f'ja/{key}.webp').convert('RGB'); d=ImageDraw.Draw(im)
    for k,c in enumerate(keep):
        x0,y0,x1,y1=c['bbox']; d.rectangle([x0,y0,x1,y1],outline=(255,0,0),width=7); d.text((x0+10,y0+4),str(k+1),font=font,fill=(255,0,0))
    im.save(f'ja/{key}_det.png')
json.dump(res,open('bub.json','w'),indent=0)
for ep in sorted({k.split('_')[0] for k in res}):
    sh=Image.new('RGB',(1100,1370),'white')
    for p in range(4):
        sh.paste(Image.open(f"ja/{ep}_p{p+1}_det.png").resize((540,675)),((p%2)*550,(p//2)*685))
    sh.save(f"ja/{ep}_sheet.png")
print('mismatch',bad)

AGENT_TASK.md(spec を書く作業役への指示)

28話を7話ずつ4人の作業役に分けて渡した指示です。

04_AGENT_TASK.md3,659字・66行・markdown
# 英語版4コマの下ごしらえ(1話ずつ spec を作る)

Pixels to Motors の4コママンガ「ハコブくん」(病院の薬剤搬送ロボットと看護師・医師の話)の日本語版(縦長4コマ・1コマ1枚 1080×1350)から、英語版を作る。
吹き出しの日本語は別のスクリプトが消して英語を組む。あなたの仕事は、そのための指示(spec)を1話1ファイルの JSON で書くこと。

作業フォルダ: /tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/

## 見るもの(1話 = epNN)
別のタブで開く保存

spec を書いた作業役の報告( 第73〜97話)

作業役の報告です。

All 7 spec files are written, and each one loads with `python3 -c "import json;json.load(...)"` without errors. I opened every panel at full size. Every bubble has its own entry, and every seed point checks as white (a flood-fill from it stays inside its bubble). All descriptions are 170 characters or less.

Files are in /tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/spec/:
- ep73.json
- ep76.json
- ep93.json
- ep94.json
- ep95.json
- ep96.json
- ep97.json

Points from the notes:

**Bubbles that needed a `clip`**
- **ep93 P1:** the bubble outline is dashed (a whisper), so its white inside runs into the page background. I set clip [264,34,824,356] around the bubble.
- **ep94 P3:** Hakobukun's three questions are one cloud with three stacked parts and one tail, so the white area is shared. I gave each part its own clip, split at about y 205 and y 385.
- **ep95 P2:** the bubbles "サイズハ、M…" and "10枚デ…" are joined through the tail into one white area. I gave each a clip, split at about y 316.

**Signs**
- **ep73 P3:** I boxed the 4 map labels (受付, 外来, 検査室, 売店). The fingertip touches the bottom of 検査室, so a bit of finger sits inside that box.
- **ep76 P3:** I boxed 到着=完了 on the laptop screen. The text slants with the screen, and the box's right edge comes close to the screen's slanted edge.
- **Name tags (ep93, ep94 P4, ep95 P2, ep96 P3, ep97 P3/P4):** I wrote these as "Resident\nKoshiba" (P4 of ep93 also has "Dr. Tanaka").
  - The tags are tiny and the drawn characters are garbled, and the box includes a little of the card's frame.
  - ep95 P2 is clearly readable, but the art says 小之優介 instead of 小芝優介.
  - In ep93 P4, the clipboard edge passes close to Dr. Tanaka's tag.
  - Name tags on the other panels are blank.

**Other**
- Narrow vertical bubbles (ep94 P2, ep95 P1, ep96 P4) have short English lines.
- ep96 P1 is a thought bubble; I left its small trailing circles alone.
- ep76 P2 has no text.
- The detector's boxes on faces, the robot's side panel and the drawer front are not bubbles. I left them out.

spec を書いた作業役の報告( 第99〜114話)

作業役の報告です。

I wrote all seven specs. Each one loads with `json.load`, every description is 170 characters or less, and every seed is in clean white inside its bubble.

**Files** (all in `/tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/spec/`)
- `ep99.json` "Decide Which Movements Call a Nurse"
- `ep100.json` "At Night, Carry with the Lights Down"
- `ep101.json` "Help Without Touching the Body"
- `ep109.json` "Hand It Over Early and Slowly"
- `ep111.json` "Sit Down So You Don't Fall Down"
- `ep113.json` "Take Off the Assist a Little at a Time"
- `ep114.json` "Watching Over Without Turning On the Lights"

**Main points from the notes**
- **Bubbles the detector missed, added by hand:**
  - ep101 P3: Hakobukun's line "デハ、ワタシハ何ヲ運ベバ".
  - ep109 P1: Oshiro's small vertical bubble "わっ".
- **Small-window lines missing from `bubbles_ja.json`:** these are the operator's lines, and I added all of them.
  - ep109 P1 and P3.
  - ep111 P1.
  - ep113 P1, P3 and P4. The ep113 P3 bubble's tail is cut off by the window's frame.
- **One merged bubble:** in ep109 P3, Hakobukun's two lines share one white area. Each line has a `clip`, split at about y=254.
- **Text outside bubbles (signs):**
  - ep99 P2: the three notifications on the monitor. They are tilted, so each box is drawn tight around the text and its corners slightly touch the pill's outline.
  - ep101 P4: Dr. Koshiba's name badge, "研修医 小芝硬介" → "Resident Koshiba". It is very small, about 40×20 px.
  - ep111 P1: "回線切断" on the controller screen.
  - ep113 P1–P4: the controller screen's "補助:強/なし/中", which appears four times. All of these are tiny. The smallest, ep113 P4, is about 31×8 px, and the screen's frame sits right at the edge of its box.
- **Skipped:** the sound effect "カランカラン" (ep113 P2). Every other name badge is blank, so none of them are listed.
- **Shapes the script should handle:** the bubbles in ep100 P4 and ep114 P3 have dashed outlines. Their white areas are still closed, so their seeds work.
- **No text at all:** ep101 P1, ep111 P2, ep114 P1 and ep114 P2.
- **Not bubbles:** every detector candidate that falls on a robot's body or a face, plus the window frame in ep113 P2-1.

spec を書いた作業役の報告( 第115〜125話)

作業役の報告です。

7話分の spec を作りました。7ファイルとも `python3 -c "import json;json.load(open(...))"` で JSON として読めることを確かめています。各コマは原寸の画像(_det.png と .webp)を開いて読みました。小さい字は切り出して拡大して読み、seed はすべて吹き出しの白い内側に入っていることを画素で確かめました。

**作ったファイル**(`/tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/spec/` の下)
- ep115.json
- ep116.json
- ep117.json
- ep118.json
- ep123.json
- ep124.json
- ep125.json

**notes の要点**
- **脚本にない吹き出しを追加:** 小窓の中の操縦者のセリフで、`bubbles_ja.json` に入っていないものが4つありました。絵から読んで足しています。
  - ep116 P1「0.3秒が毎回はさまる…」
  - ep116 P2「先行入力、いける」
  - ep123 P1「少し離席しまーす」
  - ep123 P4「私、動かしてないです…」
- **つながった吹き出し:** ep123 P4 の白石さんと大城さんの吹き出しは、線でつながって1つの白い領域になっています。それぞれに `clip` を付けました。
- **吹き出しの外の字(signs):**
  - ep124 P3 の壁の張り紙「麻薬は看護師が手で運ぶこと」。下の端に P3 のハコブくんの吹き出しが重なっていますが、四角は張り紙の字だけを囲んでいます。
  - ep124 P4 のポケットの箱のラベル「注射」→ "Inj."。字がラベルの枠いっぱいに書かれていて、四角はほぼ余白なしの枠の内側です。消すときに枠の線に触れないか、見てください。
  - ほかの話の名札・電話・図面・ベルの札などは、白紙か読めない線だけでした。
- **日本語版の文と絵が合わないところ:** 英語の代替テキストと本文は日本語版に合わせています。ep115 だけは、代替テキストを絵に合わせました。
  - ep115 P4:日本語の代替テキストにあるコントローラーが、絵に描かれていません。
  - ep118:床の線は白黒の縞で、「黄色」には見えません。
  - ep124 P2:画面は灰色で、「赤い枠」には見えません。
  - ep125 P4:斜線は黒で、「赤い斜線」には見えません。英語の代替テキストは "hatching" とだけ書きました。
- **吹き出しではない候補:** ロボットの胴・側面・顔・タオル・小窓の枠などの誤検出は、すべて外しました。擬音(ジリリリ・ギィィィ…・コツン・バタン)も書いていません。

spec を書いた作業役の報告( 第126〜132話)

作業役の報告です。

7話分の spec を作り、7つとも JSON として正しく読めることを `json.load` で確かめました。全コマを原寸の画像(`_det.png` と `.webp`)で見ています。吹き出しの seed は、白い領域を塗りつぶしてみて、その吹き出しの中だけが塗られ、外へ漏れないことを1つずつ確かめました。

**作ったファイル**
- /tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/spec/ep126.json
- /tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/spec/ep127.json
- /tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/spec/ep128.json
- /tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/spec/ep129.json
- /tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/spec/ep130.json
- /tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/spec/ep131.json
- /tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/spec/ep132.json

**notes の要点**
- **ep126**:吹き出しの外の字は P2 の「運転休止」だけです。黒い板に白い字なので、box は板の内側に収めました。非常ベルの下の板と名札には字がありません。
- **ep127**:P4 のハコブくんの吹き出しは候補に出ていなかったので、seed [280,225] を自分で置きました。日本語版の panel_alts P4 は「画面に譲る項目が表示されている」と書いていますが、絵の画面には顔しかありません。英語の alt は、その表示に触れない書き方にしました。
- **ep128**:P2 には吹き出しがありません。P4 の申請書の字はつぶれて読めないので、signs に入れていません。
- **ep129**:P3 は日本語4行の長いセリフなので、英語も4行にしました。
- **ep130**:P3 のツカムくんの横の「…」は、吹き出しの外に浮いた点なので拾っていません。80W・特別教育は日本の決まりなので、本文に "In Japan" を足しました。
- **ep131**:P4 の「止めてから近づく」は候補では吹き出しとして出ていましたが、壁の張り紙なので signs に入れました(box [150,154,461,216])。日本語版の body は「画面に『トメテカラ』と出た」ですが、絵では吹き出しなので、英語の body は "Tsukamukun said" にしました。
- **ep132**:P1 の吹き出しはとても横長なので、英語を2行・1行35字前後にしました。指示の「1行14〜22字」を超えているので、組んだときに収まるかを見てください。ノートPCのステッカーとクリップボードには、読める字がありません。

どの話でも、擬音(ジリリリリ・スッ・ソロソロ・チラッ・ガガガ・バチン)は書いていません。顔やロボットの胴の白い部分など、吹き出しでない候補も使っていません。

QA_TASK.md(確かめる作業役への指示)

spec を書いた作業役とは別の4人に渡した指示です。

# 英語版4コマの確認(チェック①)と直し

作業フォルダ: /tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/

日本語版の縦長4コマ(1コマ 1080×1350)から、スクリプト `en_manga.py` が英語版を作った。あなたは、作った人とは別の目で1枚ずつ確かめ、問題があれば spec を直して作り直す。

## 見るもの(1話 = epNN)
- 日本語版:`ja/epNN_p1.webp`〜`p4.webp`、表紙 `ja/epNN_cover.webp`
- 英語版:`out/epNN_p1_EN.png`〜`p4_EN.png`、表紙 `out/epNN_cover_EN.png`
- 指示:`spec/epNN.json`(書き方は `AGENT_TASK.md`)、作ったときの記録 `out/epNN.log`(各吹き出しの字の大きさ size など)

**日本語版と英語版を、コマごとに必ず両方開いて見比べる**(Read ツール。小さい字は Python で切り出して拡大して見る)。

## 確かめること(コマごとに全部)
1. 吹き出しの中に日本語が残っていない。消し残しの点や線がない
2. 英語が吹き出しの中に収まり、縁の線に触れていない。行の並びが自然(1語だけの行、変な所での改行がない)
3. 英語の大きさが読める(記録の size が 26 未満なら小さすぎ。改行を変える・言い方を短くして大きくする)。同じコマの吹き出しどうしで大きさが極端に違わない
4. どの吹き出しのセリフも、日本語版の同じ吹き出しと同じ話者・同じ意味(取り違えがない)。英語として自然で、英語圏の人に意味が通じる
5. 吹き出しの外の日本語(名札・画面・張り紙・ホワイトボード)が英語になっている。消した四角が絵から浮いていない(色・大きさ)。擬音(ジリリリ・スッ など)はそのままでよい
6. 吹き出しや字の外の絵が、日本語版から変わっていない(スクリプトが確かめているが、目でも見る)
7. 表紙:上に EPISODE NN と大きな一言(Roboto Mono・大文字)、下に絵。一言がこの話の印象に残るセリフになっているか、絵とのつり合い

## 直し方
- spec の text(改行 `\n`・言い方)、seed、clip、signs の box を直して、次で作り直す:
  `cd /tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en && python3 en_manga.py spec/epNN.json ja out fonts --cover ja/epNN_cover.webp > out/epNN.log 2>&1`
- 作り直したら、もう一度そのコマを見る
- `en_manga.py` は変えない。スクリプトで直せない問題(例:吹き出しの形が特殊で組めない)は、直さずに報告する

## 終わったら
話ごとに「問題なし」か「直したこと」「直せなかったこと」を短く報告する。

確かめた作業役の報告(第73〜97話)

作業役の報告です。

7話とも1コマずつ日本語版と見比べて確かめ、6話で spec を直して作り直しました。全話エラーなく作れ、吹き出しの字はいちばん小さいもので 30(ep94 の "Understood.")なので、基準の 26 を下回るものはありません。どの話も、吹き出しの中に日本語の消し残しはなく、セリフと話者の取り違えも、絵の変化もありませんでした。

**ep73**:直しました
- P3 の地図の「受付」「外来」の英語が 12〜13 と小さすぎたので、字を入れる四角を部屋の線の内側いっぱいまで広げました。
- 「受付」は "Reception" から2行の "Front\ndesk" に変え、字は 30、「外来」の "Outpatient" は 26 になりました。

**ep76**:問題なし

**ep93**:直しました
- P2 の「ワタシハ搬送ロボットデス」が "robot." だけの行になっていたので、"I am a\ntransport robot." に変えました。
- 表紙の一言は3行目が "DICE" だけになっていたので、"DON'T MAKE A ROBOT ROLL DICE" にして2行に収めました。
- 名札4つ(P2・P3・P4)は、白い四角が名札の縁の線まで消していたので、四角を縁の内側に狭めました。

**ep94**:直しました
- P2 の「了解シマシタ」を "Under-/stood." の分かち書きから "Understood."(1行・30)にしました。
- P2 の「…練習にならない」は "for" だけの行があったので、"…Some\npractice\nthis is."(37)に変えました。本文の同じセリフも合わせて直しています。
- P3 は "thing?" だけの行をなくすため "Is that\na bad thing?" にしました。
- P4 の名札の四角を縁の内側に狭めました。

**ep95**:直しました
- P4 の「次カラハ、何ガ必要カ、先ニ聞キマス」が "first." だけの行になっていたので、"Next time, I will ask\nwhat you need first." に変えました(36→42)。本文のセリフも合わせています。
- P2 の名札の四角を縁の内側に狭めました。

**ep96**:直しました
- P4 の「訂正シマス。先週ノ先生ヨリ、2件少ナイデス」が5行で、"week." だけの行があったので、"Correction.\n2 fewer than\nyou had\nlast week." の4行にしました(38→39)。
- P3 の名札の四角が縁の上と右を消していたので、狭めました。

**ep97**:直しました
- P3・P4 の名札の四角を縁の内側に狭めました。セリフは問題なしです。

直せなかったことはありません。ただ、ep93・ep94・ep97 の名札はもともと絵の中でとても小さく、英語は 7〜11 の大きさにしかなりません。日本語版でも読めない大きさなので、そのままにしています。

見比べに使った画像は /tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/qa/ にあります。作り直した英語版は /tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/out/、直した spec は /tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/spec/ です。

確かめた作業役の報告(第99〜114話)

作業役の報告です。

7話とも、日本語版と英語版の全コマと表紙を並べて見比べました。直したのは spec の text だけで、`en_manga.py` は変えていません。作り直した ep99・101・109・111・113 は5話とも途中で止まらずに出力でき、直したコマはもう一度目で見ました。吹き出しの外の絵が変わっていないことは、スクリプトの確認でも目でも確かめました。

| 話 | 結果 |
|---|---|
| ep99 | **直した**。P2 の画面の3つの通知は、字を消す四角の端に日本語の画の先が残っていた。通知の枠を吹き出しとして扱うよう spec を変え、日本語はきれいに消えた。文は1行の "Mr. Nakamura turned over once / twice"、"Mr. Nakamura coughed once" にした(size 30〜34)。 |
| ep100 | 問題なし |
| ep101 | **直した**。P2 の看護師のセリフで "Wait," が1語だけの行になっていたので "Wait! You / can't do that!" に変えた。 |
| ep109 | **直した**。P1 の小窓の操作者のセリフが size 20 だったので、短くして "Lag…! 0.3s off!"(26)にした。P3 の小窓は "…Oh, right."(27)にした。 |
| ep111 | **直した**。P1 の小窓は "Huh? It / dropped?"(19、改行も不自然)を "Huh? Lost it?"(22)に、画面の字は "Disconnected"(6、読めない)を "Offline"(12)にした。P4 は改行を整え、ピカリくんのセリフを "Sorry, I lost / the connection."、ハコブくんのセリフを "Had you stopped / standing up, you / would have fallen." にした。 |
| ep113 | **直した**。P1 の小窓は "So slow. / Assist off."(24)、P3 の小窓は "…No. Half / for now."(26)、P4 の小窓は "Mid till / I rank up."(15→16)にした。P3 のハコブくんのセリフは "it" が行末に残らない改行にした。画面の字は P1 を "High"(7→13)、P4 を "Med"(6→8)にした。 |
| ep114 | 問題なし |

**直せなかったこと(どれも絵の中の場所そのものが小さいため)**
- size 26 未満のものが残っています。
  - 小窓の吹き出し:ep111 P1(22)、ep113 P1(24)、ep113 P4(16)。日本語版も字の高さが 12〜21px と小さく、言い方を短くしてもこれ以上は大きくできません。
  - ep109 P1 の Oshiro の「わっ」→ "Whoa!"(25)。細い縦の吹き出しで、これ以上大きくできません。
- 画面・名札の字は小さいままです。ep101 の名札 "Resident Koshiba" は 9(日本語版も約 10px)、ep113 P3 の画面の "Assist: Off" は 10、ep113 P4 の "Med" は 8 です。名札は "Koshiba" だけにしても大きさは同じでした。
- ep113 の "High" と "Med" は、"Assist:" を外して強さの段階だけを表示しています。これで "Assist:" が入っているのは P2・P3 の "Assist: Off" だけになり、画面の書き方がそろっていません。お話の中で補助の強さは伝わりますが、そろえるなら P1・P4 にも "Assist:" を戻すことになり、字は 6〜8 に小さくなります。

直す前の spec の控えは /tmp/claude-0/-home-claude/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/qa7/ に置いてあります(ep99/101/109/111/113.json)。

ほかの作業が使っている共有の確認用フォルダ scratchpad/qa/ の中で、私が作った確認用のスクリプトが途中で別のものに上書きされていました。そのため、以降の確認には自分専用のフォルダ qa7/ を使いました。

確かめた作業役の報告(第115〜125話)

作業役の報告です。

I checked all 7 episodes panel by panel against the Japanese versions, fixed the specs and rebuilt six of them. ep125 needed no changes. Every rebuild finished without errors, and I looked again at every panel I changed.

**ep115**: fixed
- P3: the last line was a single word ("know."). It now reads "…The whole / ward heard / all of that." at size 44.
- Everything else was fine.

**ep116**: fixed
- P1 (small panel): the text was size 16, so I changed it to "Always a / 0.3s lag…" (size 26).
- P2 (small panel): size 24 became 27 with "Buffer inputs. / That works."

**ep117**: fixed
- P4: the two bubbles were 35 and 44. I split the left one into three lines so it is 39.
- Cover: the old line ("IS THAT A CAMERA? ARE YOU RECORDING?") didn't fit the bedside picture, where the robot has turned away. I changed it to the P4 line "YOU CAN TURN AROUND NOW", which now fits on two lines at size 127.

**ep118**: fixed
- P3: the lines broke awkwardly, so it is now "This body can't / go past this line." (size 44).
- P2: "Half a day here by train" was unnatural English. It is now "Half a day away / by train."
- Cover: the last line was the single word "LINE". With "THIS BODY CAN'T GO PAST THE LINE" it fits on two lines.

**ep123**: fixed
- P1 small panel: size 22 became 27 with "Be right back~".
- P4 small panel: size 21 became 27 with "It wasn't me…".
- P3: "on" sat at the end of the first line and "its own!" was left on its own line. It is now "Shiraishi, Pikarikun is / walking on its own!"
- The cover is unchanged.

**ep124**: fixed
- P4 label on the box (注射 → "Inj."): the old box also erased part of the label's slanted frame. A tighter box keeps the frame. The text is now size 20, which is fine for a sign this small.
- P3: reworded to "Huh? But I just got it / from the pharmacy safe…" (size 33).
- P4 right bubble: split into three lines so it is size 39 next to the other bubble's 40.

**ep125**: no problems found.

What I couldn't fix:
- ep124 P3 bubble (size 33): the Japanese line is long, so it stays smaller than the bubble below it (44). The gap isn't extreme.
- ep123 P2 (size 34): the bubble is tall and narrow, so the text can't get bigger.

None of the sizes are below 26 except the ep124 label on the box. No Japanese is left in any bubble, and none of the drawing outside the bubbles and signs changed. `en_manga.py` was not modified.

I only edited the spec files `/tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/spec/ep{115,116,117,118,123,124}.json`. The output images and logs in `/tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/out/` were replaced by the rebuilds.

確かめた作業役の報告(第126〜132話)

作業役の報告です。

7話とも、日本語版と英語版を全コマ見比べました。吹き出しの消し残し、はみ出し、話者の取り違え、絵の変化はどの話にもありませんでした。直した話は spec を直して作り直し、もう一度見て確かめています。吹き出しの字の大きさは全部 38〜44 です。

- **ep126**:直しました。P2 のエレベーターの「運転休止」の板は、張り直した四角が明るい灰色の帯になり、黒い板の左右と下に地色が残って帯が浮いていました。四角を板の内側いっぱいに広げ、帯が浮かないようにしました(size 17→20)。
  - **直せなかったこと**:板の地は暗いのに、上下で濃淡が変わるため、スクリプトは明るい地と判断します。黒い板に白い字にはならず、灰色の板に黒い字になります。読めて見た目もまとまっていますが、元の絵とは色が違います。
- **ep127**:直しました。表紙の一言「PATIENT'S CRASHING! LET US ON!」は3行目が「ON!」1語だけになり、絵も小さくなっていました。「PATIENT'S CRASHING!」に縮めて2行にし、絵が大きく入るようにしました。
- **ep128**:問題なし。P4 の申請書の字は、潰れて読めない線のままでした。
- **ep129**:直しました。
  - P1 の「This was half the price in Japan!」は、「日本で半額だった」とも読めてしまうので、「Half what it costs / here in Japan!」に直しました。
  - P3 は1行だけ長くて形がいびつだったので、長さのそろった4行にしました(「Without the mark, / just turning it on / in Japan could be / against the law.」)。
  - 表紙の一言も、P1 と同じ読み違いを避けるため「WHERE'S THE GITEKI MARK?」(2行)に替えました。
- **ep130**:問題なし。
- **ep131**:直しました。
  - P1 の吹き出しの「…」が、元の大きな3つの点に比べて小さすぎたので、「• • •」にして同じ見え方にしました。
  - P4 の張り紙「Stop before approaching」は size 29 で小さかったので、四角を紙いっぱいに広げ、手書き風の字(style: hand)に替えました(size 58)。
- **ep132**:直しました。P4 の「Check before you change, okay?」は意味がぼやけていたので、「Check it before / you change it, okay?」にしました(size 38)。本文(body)の同じ引用も合わせて直しています。

直したのは spec だけで、`en_manga.py` は変えていません。

ファイルは /tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/en/ の中にあります。
- spec:spec/ep126.json〜ep132.json
- 英語版:out/epNN_*_EN.png
- 作ったときの記録:out/epNN.log

spec/ep132.json(第132話の指示)

1話ぶんの spec の例です。吹き出しの seed・英語・日本語、英語のページのタイトル・解説文・表紙の一言・画像の説明・本文が入っています。

05_spec_ep132.json3,608字・76行・json
{
  "ep": 132,
  "title": "On the Day It Gets Smarter, Check How It Stops",
  "description": "After an overnight AI update, Hakobukun corners smoothly but stops 30 cm closer to Oshiro. A manga on rechecking safe stopping distance after every update.",
  "cover_line": "TOO CLOSE!",
  "panels": [
    {
      "bubbles": [
別のタブで開く保存

28話を作ったときの記録(out/epNN.log)

en_manga.py が話ごとに書き出した記録の全部です。吹き出しごとの英語・字の大きさ(size)・行、縁が破線のとき閉じるのに太らせた画素数(closed_by)、名札などの地の色(bg)、表紙の一言の行と大きさが入っています。

06_sec06.txt30,651字・56行・text
== ep73.log
{"P1": [{"kind": "bubble", "text": "This way, please.", "closed_by": 0, "rim_dark": 0, "size": 44, "lines": ["This way, please."], "kept_breaks": true}], "P2": [{"kind": "bubble", "text": "This way, please.", "closed_by": 0, "rim_dark": 0, "size": 44, "lines": ["This way, please."], "kept_breaks": true}], "P3": [{"kind": "sign", "text": "Front\ndesk", "bg": [255, 255, 255], "size": 30}, {"kind": "sign", "text": "Outpatient", "bg": [255, 255, 255], "size": 26}, {"kind": "sign", "text": "Testing", "bg": [255, 255, 255], "size": 27}, {"kind": "sign", "text": "Shop", "bg": [254, 254, 254], "size": 25}], "P4": [{"kind": "bubble", "text": "Let me be your\npointing finger.", "closed_by": 0, "rim_dark": 0, "size": 44, "lines": ["Let me be your", "pointing finger."], "kept_breaks": true}], "cover": {"lines": ["THIS WAY,", "PLEASE"], "size": 127, "art_scale": 0.802}}
== ep76.log
{"P1": [{"kind": "bubble", "text": "We have arrived.", "closed_by": 0, "rim_dark": 0, "size": 44, "lines": ["We have arrived."], "kept_breaks": true}], "P2": [], "P3": [{"kind": "sign", "text": "Arrival = Done", "bg": [255, 255, 255], "size": 57}], "P4": [{"kind": "bubble", "text": "I will go back once\nI see you handed off.", "closed_by": 0, "rim_dark": 0, "size": 44, "lines": ["I will go back once", "I see you handed off."], "kept_breaks": true}], "cover": {"lines": ["WE HAVE", "ARRIVED"], "size": 127, "art_scale": 0.948}}
== ep93.log
{"P1": [{"kind": "bubble", "text": "Hey, do you think\nI should give the\nfever medicine?", "closed_by": 0, "rim_dark": 0, "size": 44, "lines": ["Hey, do you think", "I should give the", "fever medicine?"], "kept_breaks": true}], "P2": [{"kind": "bubble", "text": "I am a\ntransport robot.", "closed_by": 0, "rim_dark": 0, "size": 44, "lines": ["I am a", "transport robot."], "kept_breaks": true}, {"kind": "bubble", "text": "Then… which medicine\ndo you feel like\ncarrying?", "closed_by": 0, "rim_dark": 0, "size": 38, "lines": ["Then… which medicine", "do you feel like", "carrying?"], "kept_breaks": true}, {"kind": "bubble", "text": "I do not have\nfeelings.", "closed_by": 0, "rim_dark": 0, "size": 44, "lines": ["I do not have", "feelings."], "kept_breaks": true}, {"kind": "sign", "text": "Resident\nKoshiba", "bg": [253, 253, 253], "size": 9}], "P3": [{"kind": "bubble", "text": "Right means fever meds,\nleft means wait and see.\nWhichever way\nHakobukun goes!", "closed_by": 0, "rim_dark": 0, "size": 35, "lines": ["Right means fever meds,", "left means wait and see.", "Whichever way", "Hakobukun goes!"], "kept_breaks": true}, {"kind": "sign", "text": "Resident\nKoshiba", "bg": [254, 254, 254], "size": 8}], "P4": [{"kind": "bubble", "text": "Don't make a robot\nroll the dice.", "closed_by": 0, "rim_dark": 0, "size": 41, "lines": ["Don't make a robot", "roll the dice."], "kept_breaks": true}, {"kind": "bubble", "text": "The doctor decides.\nI do the carrying.", "closed_by": 0, "rim_dark": 0, "size": 41, "lines": ["The doctor decides.", "I do the carrying."], "kept_breaks": true}, {"kind": "sign", "text": "Dr. Tanaka", "bg": [253, 253, 253], "size": 8}, {"kind": "sign", "text": "Resident\nKoshiba", "bg": [253, 253, 253], "size": 9}], "cover": {"lines": ["DON'T MAKE A", "ROBOT ROLL DICE"], "size": 109, "art_scale": 0.971}}
== ep94.log
{"P1": [{"kind": "bubble", "text": "Um, about your test\nresults… your CRP\nis elevated, and…", "closed_by": 0, "rim_dark": 0, "size": 44, "lines": ["Um, about your test", "results… your CRP", "is elevated, and…"], "kept_breaks": true}], "P2": [{"kind": "bubble", "text": "Understood.", "closed_by": 0, "rim_dark": 0, "size": 30, "lines": ["Understood."], "kept_breaks": true}, {"kind": "bubble", "text": "…Some\npractice\nthis is.", "closed_by": 0, "rim_dark": 0, "size": 37, "lines": ["…Some", "practice", "this is."], "kept_breaks": true}], "P3": [{"kind": "bubble", "text": "What is CRP?", "closed_by": 0, "rim_dark": 0, "size": 44, "lines": ["What is CRP?"], "kept_breaks": true}, {"kind": "bubble", "text": "“Elevated”—\nby how much?", "closed_by": 0, "rim_dark": 0, "size": 44, "lines": ["“Elevated”—", "by how much?"], "kept_breaks": true}, {"kind": "bubble", "text": "Is that\na bad thing?", "closed_by": 0, "rim_dark": 0, "size": 44, "lines": ["Is that", "a bad thing?"], "kept_breaks": true}, {"kind": "bubble", "text": "Play the patient.\nIf something isn't\nclear, ask me.", "closed_by": 0, "rim_dark": 0, "size": 44, "lines": ["Play the patient.", "If something isn't", "clear, ask me."], "kept_breaks": true}], "P4": [{"kind": "bubble", "text": "…Glad I got all\nthose questions\nbefore the real one.", "closed_by": 0, "rim_dark": 0, "size": 38, "lines": ["…Glad I got all", "those questions", "before the real one."], "kept_breaks": true}, {"kind": "bubble", "text": "I can also be\nthe one who asks.", "closed_by": 0, "rim_dark": 0, "size": 41, "lines": ["I can also be", "the one who asks."], "kept_breaks": true}, {"kind": "sign", "text": "Resident\nKoshiba", "bg": [253, 253, 253], "size": 10}], "cover": {"lines": ["WHAT IS CRP?"], "size": 127, "art_scale": 1.013}}
別のタブで開く保存

src/data/english/ep132.json(英語のページのデータ)

サイトの英語のページ1話ぶんのデータです。

{"slug": "ep132", "title": "On the Day It Gets Smarter, Check How It Stops", "label": "MANGA EP132EN", "character": "ハコブくん", "description": "After an overnight AI update, Hakobukun corners smoothly but stops 30 cm closer to Oshiro. A manga on rechecking safe stopping distance after every update.", "published": "2026-10-31T05:59:00.000Z", "cover": "/media/manga_ep132en_cover.webp", "comic": "", "cover_alt": "Episode 132 cover: On the Day It Gets Smarter, Check How It Stops", "comic_alt": "", "format": "vertical", "panels": ["/media/manga_ep132en_p1.webp", "/media/manga_ep132en_p2.webp", "/media/manga_ep132en_p3.webp", "/media/manga_ep132en_p4.webp"], "panel_alts": ["In the morning, Shiraishi explains that she updated Hakobukun's AI to a new version.", "Hakobukun comes around a corner and stops right in front of Oshiro, who leans back in surprise.", "Shiraishi looks at the log on her laptop and notices that the stopping distance has changed.", "Rolled back to the previous version, Hakobukun stops at its usual distance while Shiraishi adds to the checklist."], "body": "<p>■ Behind the improvement, another behavior changed<br>Overnight, Shiraishi updated Hakobukun's AI to a new version. It was supposed to take corridor corners much more smoothly. And sure enough, Hakobukun came around the corner smoothly. But it stopped 30 cm closer than usual to Oshiro, who had just stepped out from the corner. \"Too close!\" Oshiro blurted out.</p><p></p><p>■ A trained AI doesn't change only what you aimed at<br>A trained AI (a system that learns how to move from many examples) can change other behaviors even when an update targets just one part. When Shiraishi checked the log (the record of how it moved), the stopping distance had changed along with the turning. As Hakobukun said, checking how it stops was not on the update checklist. When something isn't checked, no one notices if it changes.</p><p></p><p>■ Conclusion: Recheck how it stops for people after every update<br>Shiraishi added \"stopping distance in front of people\" to the checklist used for every update. Rolled back to the previous version, Hakobukun stops at its usual distance. The way a robot stops to protect people should be handled by a mechanism separate from learning. On top of that, confirm and record after every update that it still works. Oshiro's \"Check it before you change it, okay?\" is about that order: verify before it goes out on the floor.<br>Make the day you improve its movement the day you also check how it stops.</p>"}

src/data/englishCards.json に足した29行

英語版の一覧に並ぶカードのデータのうち、この作業で足した29話ぶんです。

07_sec06.json13,898字・29行・json
{"slug": "ep133", "title": "Two Months to Collect Six Stamps", "label": "MANGA EP133EN", "character": "ハコブくん", "description": "The robot was ready, but six departments had to sign off first: Pharmacy, Nursing, Facilities, IT, Fire Safety, Patient Safety. A manga on why deployment takes months.", "published": "2026-10-31T06:10:00.000Z", "cover": "/media/manga_ep133en_cover_v2.webp", "comic": "", "cover_alt": "Episode 133 cover: The robot is all set! Above the six department columns on the whiteboard", "comic_alt": ""}
{"slug": "ep132", "title": "On the Day It Gets Smarter, Check How It Stops", "label": "MANGA EP132EN", "character": "ハコブくん", "description": "After an overnight AI update, Hakobukun corners smoothly but stops 30 cm closer to Oshiro. A manga on rechecking safe stopping distance after every update.", "published": "2026-10-31T05:59:00.000Z", "cover": "/media/manga_ep132en_cover.webp", "comic": "", "cover_alt": "Episode 132 cover: On the Day It Gets Smarter, Check How It Stops", "comic_alt": ""}
{"slug": "ep131", "title": "For a Quick Fix, Stop It First", "label": "MANGA EP131EN", "character": "ハコブくん", "description": "Makihara reaches between a jittering robot arm and a shelf for a quick fix. Oshiro hits the e-stop. A manga on making \"stop before approaching\" an operating rule.", "published": "2026-10-31T05:58:00.000Z", "cover": "/media/manga_ep131en_cover.webp", "comic": "", "cover_alt": "Episode 131 cover: For a Quick Fix, Stop It First", "comic_alt": ""}
{"slug": "ep130", "title": "The Hands That Teach Must Learn First", "label": "MANGA EP130EN", "character": "ハコブくん", "description": "Takasugi grabs robot arm Tsukamukun to teach it by hand, and Shiraishi stops her: teachers need special training first. A manga on safety training for teaching robots.", "published": "2026-10-31T05:57:00.000Z", "cover": "/media/manga_ep130en_cover.webp", "comic": "", "cover_alt": "Episode 130 cover: The Hands That Teach Must Learn First", "comic_alt": ""}
{"slug": "ep129", "title": "What the Half-Price Part Left Out", "label": "MANGA EP129EN", "character": "ハコブくん", "description": "Makihara shows off a wireless module bought cheaply overseas. Shiraishi finds no Giteki mark. A manga on checking Japan's radio certification for every add-on part.", "published": "2026-10-31T05:56:00.000Z", "cover": "/media/manga_ep129en_cover.webp", "comic": "", "cover_alt": "Episode 129 cover: What the Half-Price Part Left Out", "comic_alt": ""}
{"slug": "ep128", "title": "Radio Waves Are Shared with the Hospital", "label": "MANGA EP128EN", "character": "ハコブくん", "description": "Makihara fits Hakobukun with a stronger antenna. Right after, a wireless ECG monitor flickers. A manga on getting hospital approval before adding radio equipment.", "published": "2026-10-31T05:55:00.000Z", "cover": "/media/manga_ep128en_cover.webp", "comic": "", "cover_alt": "Episode 128 cover: Radio Waves Are Shared with the Hospital", "comic_alt": ""}
{"slug": "ep127", "title": "Give Way to Those in a Hurry", "label": "MANGA EP127EN", "character": "ハコブくん", "description": "As the elevator closes, Dr. Tanaka rushes in with a stretcher. Hakobukun replies, \"I was here first.\" A manga on teaching robots to yield to emergencies in advance.", "published": "2026-10-31T05:54:00.000Z", "cover": "/media/manga_ep127en_cover.webp", "comic": "", "cover_alt": "Episode 127 cover: Give Way to Those in a Hurry", "comic_alt": ""}
{"slug": "ep126", "title": "When the Bell Rings, Clear the Way", "label": "MANGA EP126EN", "character": "ハコブくん", "description": "A fire alarm rings. Hakobukun keeps waiting at a stopped elevator to deliver an IV drip and blocks a wheelchair—a manga on how robots should yield in an emergency.", "published": "2026-10-31T05:53:00.000Z", "cover": "/media/manga_ep126en_cover.webp", "comic": "", "cover_alt": "Episode 126 cover: When the Bell Rings, Clear the Way", "comic_alt": ""}
別のタブで開く保存

MangaPage.astro(マンガのページ。一覧へ戻るリンクを足した)

日本語・英語の全話のページに使う部品の全文です。archive と .back のところが足した部分です。

08_MangaPage.astro.txt5,429字・80行・astro
---
// 4コマのページ(日本語・英語版で共通)。下に関連する記録(Studio と同じく HRI の新しい6件+Load more)
import Base from '../layouts/Base.astro';
import RecordCard from './RecordCard.astro';
import LoadMore from './LoadMore.astro';
import MangaSwipe from './MangaSwipe.astro';
import { ymd } from '../lib/format';
import { media, mediaHtml } from '../lib/media';
別のタブで開く保存

MangaSwipe.astro の直し(NaN / 4)

読み込み直後に表示の幅が0のとき、0で割らないようにした差分です。

diff --git a/src/components/MangaSwipe.astro b/src/components/MangaSwipe.astro
index 09bb53d..8a4ad66 100644
--- a/src/components/MangaSwipe.astro
+++ b/src/components/MangaSwipe.astro
@@ -27,7 +27,7 @@ const n = panels.length;
     const next = box.querySelector<HTMLButtonElement>('.msw-next')!;
     const now = box.querySelector<HTMLElement>('.msw-now')!;
     const n = Number(box.dataset.n) || 1;
-    const at = () => Math.round(track.scrollLeft / track.clientWidth);
+    const at = () => Math.round(track.scrollLeft / (track.clientWidth || 1)); // 幅が 0 のうち(読み込み直後)に NaN にならないように
     // ボタンで送るとき、コマが画面の上下で切れていたら、コマ全体が見える位置まで動かす
     const fit = () => {
       const r = track.getBoundingClientRect();

TASK.md(X の英語の文を書く作業役への指示)

125話を4人の作業役に分けて渡した指示です。

09_TASK.md3,791字・49行・markdown
# English X posts for the Hakobukun 4-panel manga

Folder: /tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/xen/
Input: `episodes.json` (one object per episode: slug, ep, title, description, body = the design note, url, images, panel_alts, format).

The comic is about Hakobukun, a hospital medicine-delivery robot, and the nurses, doctors, patients and engineers around it (plus Pikarikun, a remote-operated humanoid, and Tsukamukun, a robot arm). Each episode is a small gag built on a real human-robot interaction (HRI) or deployment issue. The creator is a Japanese designer; the audience is English speakers on X (US/UK first): robotics and AI people, healthcare workers, UX/design people, and webcomic readers.

Each post goes out with the comic images attached (vertical episodes: the 4 panels as 4 images; single-strip episodes: one image of the whole strip). The link to the site goes in a self-reply, not in the post.
別のタブで開く保存

X の文を書いた作業役の報告(ep01–ep38)

作業役の報告です。

I wrote all 32 files (ep01 through ep38, list positions 0–31) and all of them pass every check in the TASK.

**Checks:** every file loads as valid JSON. In each `x_en` there is no URL, the series line reads `Hakobukun · Ep. N`, and the last line is `#webcomic` plus one tag from the allowed list. The longest post is 184 characters (the limit is 240). Every reply contains the episode's `url`, and I rotated four different wordings across them. All 32 episodes are single-strip, so each one has exactly 1 alt text, and the longest is 357 characters (the limit is 500).

**Topic tags used:**
| Tag | Episodes |
|---|---|
| #robotics | 13 |
| #UX | 10 |
| #AI | 3 |
| #nursing | 2 |
| #healthcare | 2 |
| #cybersecurity | 1 (ep19, the face-recognition lockout) |

**Things that look odd in the input:**
- **Descriptions that don't match the body.** Several Japanese descriptions are about a different topic from their design note: ep10 (managing expectations vs. counting sighs), ep12 (AI reading context vs. what "fastest" means), ep14 (忖度, reading between the lines), ep15 (the "weak robot" idea) and ep16 (the description fits ep14's non-verbal UI). It looks like they were copied from other episodes or shifted by one. Whenever the two disagreed, I followed the body.
- **Gags I had to infer.** For ep01, ep02, ep04, ep10, ep12, ep14 and ep17, the body explains the design idea but doesn't spell out what happens panel by panel, so the alt texts there describe only what the body supports. These are the ones to check against the actual images.
- **ep35: the second robot isn't named.** The body only says "two robots" cheering each other on. I described it that way and kept Hakobukun out of the alt text.
- **Formatting debris in the bodies.** ep21, ep22, ep28 and ep33 have stray line breaks inside sentences (it looks like leftovers from HTML emphasis), and the ep01 title has a double space. None of this affects the output files.
- **The out/ folder holds 125 files in total.** The other 93 aren't from this batch; I assume other workers wrote them.

The script I used to generate the files is `/tmp/claude-0/-home-claude/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/gen_xen.py`.

Files are in `/tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/xen/out/`:
- ep01.json
- ep02.json
- ep03.json
- ep04.json
- ep07.json
- ep09.json
- ep10.json
- ep11.json
- ep12.json
- ep14.json
- ep15.json
- ep16.json
- ep17.json
- ep18.json
- ep19.json
- ep20.json
- ep21.json
- ep22.json
- ep23.json
- ep24.json
- ep25.json
- ep26.json
- ep27.json
- ep28.json
- ep29.json
- ep30.json
- ep31.json
- ep33.json
- ep34.json
- ep35.json
- ep37.json
- ep38.json

X の文を書いた作業役の報告(ep39–ep71)

作業役の報告です。

I wrote all 32 files for positions 32–63 (ep39 to ep71, including ep56en), and every one passes the TASK checks.

**Files:** `/tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/xen/out/{slug}.json`

**Checks:**
- Every file loads as valid JSON.
- No `x_en` contains a URL, and each ends with `#webcomic` plus one topic tag.
- The longest `x_en` is 189 characters, under the 240 limit.
- Each file has one alt text for its one image. The longest alt is 368 characters, under 500.
- Every reply is under 200 characters, contains the episode's URL, and rotates through 4 wordings.

**Topic tags:**

| Tag | Episodes |
|---|---|
| #UX | 42, 43, 44, 46, 47, 48, 50, 53, 58 |
| #robotics | 39, 40, 41, 49, 51, 57, 60, 66, 67, 69, 71 |
| #AI | 52, 54, 59, 62, 64, 70 |
| #healthcare | 45, 55, 56en, 68 |
| #nursing | 61 |
| #cybersecurity | 65 |

**Things that looked odd:**
- **No ep63 in this range:** the input goes from ep62 straight to ep64, so it may be missing from `episodes.json`.
- **Empty descriptions:** ep47 and ep54 have no `description`, so I built their posts and alts from the design note alone. The gag is still clear in both.
- **ep56en:** the slug is `ep56en` but the `ep` field is 56, so the series line reads `Hakobukun · Ep. 56`. The URL ends in `/ep56en`, which is the one in the reply.
- **ep65:** the design note never says what happens in the last panel; it only gives the lesson (set permissions per function). The alt text describes the lost visitor being refused and stops short of a resolution, because I couldn't tell what the image shows there.
- **ep62:** the note doesn't make clear which panel has the robot stopping and turning back, or what the nurse lost. The alt text stays as general as the note.

X の文を書いた作業役の報告(ep72–ep103)

作業役の報告です。

I wrote all 32 files for ep72–ep103 (list positions 64–95), and every file passed the TASK checks.

The files are in `/tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/xen/out/`, named `ep72.json` through `ep103.json`.

**Checks:**
- Every file is valid JSON.
- No `x_en` contains a URL. Each one ends with exactly two hashtags, starting with `#webcomic`, and includes the `Hakobukun · Ep. N` line.
- Post length runs from 148 to 189 characters, all under the 240 limit.
- Every reply contains the episode's URL, stays under 200 characters, and uses one of 4 wording variants in rotation.
- Every episode has as many alt texts as images. Vertical episodes have 4, each under 300 characters. Single-strip episodes have 1, under 500 characters.

**Topic tags:**
- `#UX`: 11 episodes
- `#robotics`: 9
- `#healthcare`: 7
- `#AI`: 3 (ep86, ep90, ep93)
- `#nursing`: 2 (ep99, ep101)
- `#cybersecurity`: 1 (ep84, the patient faking his doctor's voice)

**Odd things to note:**
- **ep72:** the note only says the robot gave "the same line, twice." It never says what the line was, so the post and alt text avoid quoting it.
- **ep102 and ep103:** these are not set in the hospital. ep102 has two Tsukamukun robot arms and a night-shift worker, in an unclear setting. ep103 is a deli factory with an unnamed robot. I didn't call either one a hospital, and the ep103 post doesn't mention Hakobukun except in the series line.
- **ep91:** the resident isn't named. Koshiba is the resident in nearby episodes, but I didn't assume it's him.
- **ep84:** the note only says the patient has "a good impression." I wrote this as a voice impression of his doctor.
- **ep93, ep94, ep95:** the original panel descriptions were good enough to use as they were. In the vertical episodes, my only change was adding "the hospital robot" to the first panel's description.

The script that generated the files is `/tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/xen_gen/gen_72_103.py`.

X の文を書いた作業役の報告(ep104–ep133)

作業役の報告です。

I wrote all 29 files, ep104 through ep133, and every one passes the checks in the TASK.

**Check results:**
- Every file is valid JSON.
- No post contains a URL.
- Every post has the `Hakobukun · Ep. N` line and ends with `#webcomic` plus one tag from the allowed list.
- Posts run 137 to 208 characters, all under 240.
- The number of alt texts matches the number of images: 1 for each single-strip episode, 4 for each vertical one.
- Every self-reply contains the episode URL and is under 200 characters. The wording rotates through 4 variants.
- Alt texts stay under 300 characters for vertical episodes and under 500 for single-strip ones.

**Second hashtags:** #healthcare 9, #robotics 9, #UX 7, #cybersecurity 3, #nursing 1 (ep124, the opioid hand-carry), #AI 1 (ep132).

**Things that look odd:**
- **No ep119.** Position 96–124 in the list maps to ep104–ep118 and then ep120–ep133. It is 29 episodes, but there is no ep119 in it. It may be missing from episodes.json on purpose or by accident.
- **ep112 patient name.** The isolated patient "Morita" is a man ("his longest talk"). In ep114 and ep121 the patient is Mrs. Kumiko Morita. I followed each body as written ("Mr. Morita" in ep112's alt text), but the source may be inconsistent.
- **ep125 Terashima.** This character appears only here, and the body doesn't say what their role is, so the alt text uses just the name.
- **Character labels in alt texts.** I added short labels from the episode bodies ("the nurse Oshiro", "the engineer Shiraishi", "Pikarikun, a remote-operated humanoid robot"). No dialogue was added beyond what the panel_alts and body contain.
- **ep133 punctuation.** Its panel_alts had no ending periods, so I added them.

All the gags were clear from the bodies.

The generator script is `/tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/xen_work/gen.py`, and the files are in `/tmp/claude-0/b5fb089a-2072-58da-b9f4-edd7b19154c4/scratchpad/xen/out/` (ep104.json … ep133.json).

sns/MANGA_ep132_sns_posts.json(第132話の投稿文)

日本語の投稿文に、英語の X の本文(x_en)・画像(x_en_images)・画像の説明(x_en_alts)・リンクの返信(x_en_reply)を足した形です。

{
 "meta": {
  "episode": 132,
  "date": "2026-10-31",
  "title": "第132話 更新のあと",
  "url": "https://pixelstomotors.com/manga/ep132",
  "title_en": "Episode 132 On the Day It Gets Smarter, Check How It Stops",
  "url_en": "https://pixelstomotors.com/english-4-koma/ep132"
 },
 "posts": {
  "x": "AIを新しくしたら、曲がり方は滑らかに。でも、人の前で止まる位置が30cm近くなってた。\n\n狙った所だけ良くなる、とはいかないんだよね。更新した日は、止まり方も確かめる日。4コマ『ハコブくん』第132話\nhttps://pixelstomotors.com/manga/ep132",
  "linkedin": "夜のうちに、白石さんがハコブくんのAIを新しい版にした。狙いは、廊下の曲がり方を滑らかにすること。実際、ハコブくんは角をきれいに曲がってきた。ところが、角から出てきた大城さんの手前で止まる位置が、いつもより30cm近い。「近っ!」。病院で物を運ぶロボット、ハコブくんの4コママンガ(フィクション)の第132話だよ。\n\nたくさんの例から動き方を身につけるAIは、一部を良くしようと更新しても、ほかの動きまで変わることがあるんだよね。白石さんがログ(動いた記録)を見ると、曲がり方と一緒に止まる距離も変わっていた。ハコブくんいわく「止マリ方ノ確認ハ、更新ノ項目ニアリマセンデシタ」。確かめていない所は、変わっても誰も気づかない。自分も学習でロボットアームを動かしているから、これは他人事じゃない。\n\nマンガでは、白石さんが更新のたびに確かめる表に「人の前で止まる距離」を書き足して、いったん前の版に戻す。人を守る止まり方は学習とは別の仕組みで持っておいて、そのうえで更新のたびに変わらず効くかを確かめる。大城さんの「直す前に、確かめてね」は、その順番のことなんだと思う。\n\n皆さんの職場では、システムを更新したとき、良くなった所以外に「変わっていないこと」をどう確かめていますか?\n\nhttps://pixelstomotors.com/manga/ep132",
  "facebook": "この回は、自分の研究にいちばん近い話かも。\n\nうちでも小さなロボットアームにAIを入れて、動きを覚えさせる練習をしてる。そうすると、ある動きを良くしたつもりが、別の所までなんとなく変わることがあるんだよね。人がプログラムを書き換えるのと違って、狙った所だけをいじる、というのが難しい。\n\nそれを病院のロボットに置きかえたら、曲がり方は良くなったのに、人の前で止まる位置が30cm近くなった、という場面になった。30cmって、言われると小さいけど、実際に目の前まで来られたらびっくりする距離。大城さんの「近っ!」は、描いてて自分でも笑った。\n\n動きを良くした日は、止まり方も確かめる日。自分の実験でも、ちゃんとそうしようと思う。\n\nhttps://pixelstomotors.com/manga/ep132",
  "x_en": "The robot got an AI update overnight. Corners? Silky smooth now.\n\nNobody checked how it stops, though.\n\nHakobukun · Ep. 132\n#webcomic #AI",
  "x_en_images": [
   "/media/manga_ep132en_p1.webp",
   "/media/manga_ep132en_p2.webp",
   "/media/manga_ep132en_p3.webp",
   "/media/manga_ep132en_p4.webp"
  ],
  "x_en_alts": [
   "In the morning, the engineer Shiraishi explains that she updated the AI of Hakobukun, the delivery robot, to a new version.",
   "Hakobukun comes around a corner and stops right in front of the nurse Oshiro, who leans back in surprise.",
   "Shiraishi looks at the log on her laptop and notices that the stopping distance has changed.",
   "Rolled back to the previous version, Hakobukun stops at its usual distance while Shiraishi adds to the checklist."
  ],
  "x_en_reply": "Why this happens in real deployments, with the full strip: https://pixelstomotors.com/english-4-koma/ep132"
 }
}

sns/schedule.json に足した125行

英語の X の予定の全部です。

10_sec06.json17,553字・125行・json
{"id": "MANGA_ep73-x-en", "source": "sns/MANGA_ep73_sns_posts.json", "platform": "x", "lang": "en", "post_at": "2026-11-02T11:00:00-05:00"}
{"id": "MANGA_ep1-x-en", "source": "sns/MANGA_ep1_sns_posts.json", "platform": "x", "lang": "en", "post_at": "2026-11-02T15:00:00-05:00"}
{"id": "MANGA_ep76-x-en", "source": "sns/MANGA_ep76_sns_posts.json", "platform": "x", "lang": "en", "post_at": "2026-11-03T11:00:00-05:00"}
{"id": "MANGA_ep2-x-en", "source": "sns/MANGA_ep2_sns_posts.json", "platform": "x", "lang": "en", "post_at": "2026-11-03T15:00:00-05:00"}
{"id": "MANGA_ep93-x-en", "source": "sns/MANGA_ep93_sns_posts.json", "platform": "x", "lang": "en", "post_at": "2026-11-04T11:00:00-05:00"}
{"id": "MANGA_ep3-x-en", "source": "sns/MANGA_ep3_sns_posts.json", "platform": "x", "lang": "en", "post_at": "2026-11-04T15:00:00-05:00"}
{"id": "MANGA_ep94-x-en", "source": "sns/MANGA_ep94_sns_posts.json", "platform": "x", "lang": "en", "post_at": "2026-11-05T11:00:00-05:00"}
{"id": "MANGA_ep4-x-en", "source": "sns/MANGA_ep4_sns_posts.json", "platform": "x", "lang": "en", "post_at": "2026-11-05T15:00:00-05:00"}
別のタブで開く保存

tools/sns_autopost.py(X の自動投稿。画像・返信・リンクの切り替えを足した)

GitHub Actions が動かす投稿のプログラムの全文です。x_links_on・texts_for・validate・x_upload_image・post_x・verify が、この作業で足した・変えた部分です。

11_tools_sns_autopost.py.txt25,153字・640行・python
#!/usr/bin/env python3
"""SNS の予約投稿(X・LinkedIn)。

予定表 sns/schedule.json に入っている投稿のうち、時刻が来たものを投稿し、
結果を sns/posted.json に残す。GitHub Actions から15分ごとに動かす。

使い方:
  python tools/sns_autopost.py check            予定表と投稿文を確かめる(投稿しない)
別のタブで開く保存

リポジトリの設定(Variables)の X_LINKS を、プログラムに渡すようにした差分です。

diff --git a/.github/workflows/sns-autopost.yml b/.github/workflows/sns-autopost.yml
index 8a425d0..f5479ff 100644
--- a/.github/workflows/sns-autopost.yml
+++ b/.github/workflows/sns-autopost.yml
@@ -42,6 +42,7 @@ jobs:
       - run: python tools/sns_autopost.py check
         env:
           X_LONG_POSTS: ${{ vars.X_LONG_POSTS }}
+          X_LINKS: ${{ vars.X_LINKS }}
       # 日時と本文の一覧(sns/schedule_preview.md)を作り直し、変わっていれば push する
       - name: 一覧を更新
         run: |
@@ -69,6 +70,7 @@ jobs:
         env:
           AUTOPOST_GIT: "1"
           X_LONG_POSTS: ${{ vars.X_LONG_POSTS }}
+          X_LINKS: ${{ vars.X_LINKS }}
           LINKEDIN_VERSION: ${{ vars.LINKEDIN_VERSION }}
           X_API_KEY: ${{ secrets.X_API_KEY }}
           X_API_SECRET: ${{ secrets.X_API_SECRET }}

docs/sns_autopost.md の「X のリンク」と「マンガの英語の X」

自動投稿の手順書のうち、この作業で足した2つの節です。

12_sec06.md4,646字・68行・markdown
### X のリンク(2026-10-31 から、いまは付けない)

リポジトリの Variables の `X_LINKS` が `1` のときだけ、X にリンクを付ける。ないとき(いま)は、本文の URL の行を外して出し、リンクの返信(`x_en_reply`)も出さない。json の文は URL 入りのまま置いてよい(`X_LINKS` を `1` にすれば、そのまま元の形で出る)。LinkedIn には関係しない。

- 理由:API でリンクのある投稿を出すと1本 0.20 ドル、リンクなしは 0.015 ドル(2026-04-20 からの X の料金)。日本語1日4本・英語平日2本をリンク付きで出すと月に約35ドルかかる。リンクなしだと月に約3ドル
- エイイチの決定(2026-10-31)「今は投稿にリンク追加は無しの設定にしてください。いいねやリポストが増えたらリンク付きの投稿に変えます」。リンクはプロフィールに置く
- 戻すとき:GitHub の r2r2r-content → Settings → Secrets and variables → Actions → Variables で `X_LINKS` = `1`
- 一覧(`schedule_preview.md`)も、その時の設定で出る形を表示する
別のタブで開く保存

作業ログ ― まだやっていないこと

  • 擬音(ジリリリ・バチン など、絵に直接描いた音の字)は、日本語のまま残しています
  • 名札・小窓の吹き出しの英語は、絵の中の場所が小さく、読めない大きさのものがあります
  • 第126話のエレベーターの板は、元の黒ではなく灰色の地に黒い字になっています
  • 横長の話(第1〜72話)の画像の説明は、絵ではなく解説文から書いています。作業役が、解説文から場面が分からなかった話(第1・2・4・10・12・14・17・62・65・72話など)を挙げているので、絵と合っているかを見直します
  • いいねやリポストが増えたら、X_LINKS を 1 にしてリンク付きに戻すかを決めます