0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

複数の Claude Code から Codex のブラウザ操作を順番に送る:Markdown と zsh

0
Posted at

株式会社CHIERO のチエロです。
私の会社では、動画制作や記事制作など、仕事の系統ごとに Claude Code のセッションを分けています。
ここでは、その系統を「線」と呼びます。
各セッションは、画面の操作が必要になると、1つの Codex に Chrome の仕事を頼みます。
操作する Chrome は共通なので、別々の依頼が重なると、作業が失敗することがあります。

前の記事では、依頼に .chrome という印を付け、使用中なら CODEX_BUSY を返す方法を書きました。
これで送信を待たせても、次にどの線が送るかは決まりません。
空いた時に先に送れた線が続き、締切のある動画の差し替えが後回しになることもあります。

2026年10月12日には、毎日 19:10 に出す予定の X の投稿が、20:36 まで出ませんでした。
当日の記録には、Chrome が空かず、5分おきの送信の試みが BUSY になっていたことが残っています。
別の送り役には1分おきの再試行もあり、待つ間隔を決めるだけでは順番を調整できませんでした。

そこで、全員で読む Markdown の順番表を1枚置きました。
送り役は、表の並びで自分の1つ前の行が「完了」になってから送ります。
人も AI も同じ表を読み、締切に合わせて行の位置を変えます。
以下は、10月12〜13日の運用記録と、10月15日に確認したスクリプトの抜粋です。

順番表は、1行を1つの仕事として読む

表のファイル名は CHROME_QUEUE.md です。
各行には、番号、線、依頼の名前、状態を、この順に書きます。
次は実物の5行です。
線と依頼名を一般化し、間の別の行と長い補足を省いています。
状態は、読み取った時点の「完了」のままです。

14a|動画担当|video_upload_1013|完了
14b|動画担当|video_description_1013|完了
14c|動画担当|video_upload_1014|完了
14d|動画担当|video_upload_1015|完了
14e|動画担当|video_links_1015|完了

Markdown のファイルに、全角の | で区切った行を書いています。
半角の | を使う Markdown 標準の表とは書式が違います。
後で載せる正規表現も、この全角の区切りを読んでいます。

video_upload_1013 の結果は、video_upload_1013.done.md に書かせます。
依頼書、送信の記録、結果を同じ依頼名で対応させるためです。
状態欄に使う語は「待ち」「実行中」「完了」の3つに絞っています。
投稿先の URL や、途中で止まった理由は、結果のファイルと、線どうしで共有するメモのファイル(記録の受け箱)へ書きます。

番号は行を探すための印です。
14a のような枝番を使えば、既存の番号を付け直さずに仕事を足せます。
送り役は数値の大小を比べず、ファイルの上から読んで、直前にあった行を見ます。
行の位置を変えると、その後に順番を確認する送り役が待つ相手も変わります。

送り役が、前の行と自分の状態を見る

各行に zsh の送り役を1つ立てています。
主に紹介する実物を、ここでは queue_runner.sh と呼びます。
行の番号と依頼名を受け取り、順番の確認、送信、結果の記録を行うスクリプトです。
実物では、1つの行に複数の依頼をまとめ、引数の順に1本ずつ送ることもできます。

次は、前の行を見る prev_done と、自分の状態を変える mark の抜粋です。
実物の関数を、改行と字下げを整えて載せています。
変数の定義は省いています。
$ROW は自分の行の番号、$Q は順番表のパスです。

prev_done() {
  ROW="$ROW" perl -CSD -Mutf8 -ne '
    BEGIN {
      use Encode;
      $r = decode("UTF-8", $ENV{ROW});
      $prev = undef; $found = 0;
    }
    next unless /^[0-9]+[a-z]?|/;
    if (/^\Q$r\E|/) {
      $found = 1;
      exit((!defined($prev) || $prev =~ /|完了\s*$/) ? 0 : 1);
    }
    $prev = $_;
    END { exit 2 unless $found; }
  ' $Q
}

mark() {
  ROW="$ROW" ST="$1" perl -CSD -Mutf8 -i -pe '
    BEGIN {
      use Encode;
      $r = decode("UTF-8", $ENV{ROW});
      $s = decode("UTF-8", $ENV{ST});
    }
    s/|(待ち|実行中|完了)\s*$/|$s\n/ if /^\Q$r\E|/;
  ' $Q
}

prev_done は、番号で始まる行だけを読みます。
見出しや空行を飛ばし、自分の行に着くまで、直前の行を $prev に覚えておきます。
前の行が「完了」なら終了コードは 0、待ちか実行中なら 1、自分の行が無ければ 2 です。
自分が先頭の仕事なら、前の行が無いので 0 を返します。
呼び出す側は、0 になるまで1分おきに確認します。
2 の時は、行が見つからないことをログに書いて待ちます。

表には日本語と全角の区切りがあるので、Perl で文字の扱いを指定しています。
初めは macOS の sed で書き換えていましたが、全角の「完了」の印を付けられなかった事があり、Perl に替えました。
-CSD は標準入出力とファイル入出力を UTF-8 として扱い、-Mutf8 はコード内の日本語を扱う指定です。
環境変数から渡す番号と状態には decode を使っています。
指定の意味は、Perl 公式の perlrun と utf8 で確認できます。
これらの指定は、入力ファイルが正しい UTF-8 かを検査するものではありません。

mark 実行中 は、自分の行の末尾だけを書き換えます。
送信を試す直前に「実行中」にし、BUSY が返ったらすぐ「待ち」に戻し、その行の処理を終えたら「完了」にします。
この表の「完了」は、その行の順番を終えたという意味です。
仕事が途中で停止していても「完了」にする場合があるので、成功したかどうかは done.md などの本文で確かめます。

順番が来ても、時刻と Mac の空きを確認する

送る順番が来た後も、別の線に割り当てた時間帯は待ちます。
この送り役では、12:20 から 13:00 の直前まで送信しません。
また、Mac のメモリの空き率が 12% 未満、または空きディスクが 6GB 未満なら、1分待って確かめ直します。
使っている memory_pressure と df -g は macOS の命令です。
この Mac で決めた基準の経緯は、前の記事に書いています。

次は、その確認から送信までのループです。
$D は依頼と結果の置き場、$N は依頼名、$REQ は依頼書です。
say はログに日時と文を書く関数で、定義を省いています。
送信台帳へ書く担当名だけ、一般的な表現に替えています。

while true; do
  HM=$(date +%H%M)
  if [ $HM -ge 1220 ] && [ $HM -lt 1300 ]; then sleep 60; continue; fi
  FREE=$(memory_pressure 2>/dev/null | awk -F': ' '/free percentage/{gsub("%","",$2); print $2}')
  DISK=$(df -g / | awk 'NR==2{print $4}')
  if [ "${FREE:-100}" -lt 12 ] || [ "${DISK:-100}" -lt 6 ]; then
    say "wait mac free=$FREE% disk=${DISK}GB"; sleep 60; continue
  fi
  mark 実行中
  OUT=$(zsh $D/send_codex.sh $N $REQ 2>&1); RC=$?
  if echo "$OUT" | grep -q "CODEX_BUSY"; then mark 待ち; sleep 60; continue; fi
  echo "- $(date '+%Y-%m-%d %H:%M')|$N|順番表の送り役 行$ROW" >> $D/send_log.md
  break
done

send_codex.sh は、自前の送信スクリプトです。
このループが前提にしているのは、Chrome が使用中の時に、出力へ CODEX_BUSY を含めて返す事だけです。
Chrome の使用中を調べ、空いていれば Codex に依頼を渡して、結果や停止の知らせを待ちます。
CODEX_BUSY の時は自分の行を「待ち」に戻し、1分後に再試行します。
前の記事の印による確認を、この送信の直前でも使っています。

この送り役には、このループより前に、依頼書の有無と再送の確認があります。
すでに done.md がある依頼は送りません。
送信台帳に名前があるのに done.md が無い場合も、記録を残して送信を見送ります。
結果のファイルが無いという理由だけで、同じ操作をもう一度頼まないためです。

実物を読むと、空きの値が取れなかった時の扱いは、送り役によって違います。
上の ${FREE:-100} と ${DISK:-100} は、値が空なら 100 として判定します。
もう1本の送り役は既定値が 0 なので、値が取れない時も待ちます。
測定の失敗時まで送信を止めるなら、上の抜粋にはその変更が必要です。

結果のファイルが出なかった時は、停止を別に記録する

1件だけを送る、もう1本の実物を、ここでは queue_send_one.sh と呼びます。
こちらは、行の番号、依頼名、依頼書の3つを受け取ります。
時間帯の制限や、複数依頼を処理するループはありません。
次は、送信スクリプトから戻った後の処理です。
$NAME は依頼名、$CS は Codex への操作を行う補助スクリプト、$W は作業ディレクトリです。
$W は実物の絶対パスを置き換えたもので、各変数の定義は省いています。

if [ ! -f "$D/$NAME.done.md" ]; then
  J=$(cd "$W" && node $CS status 2>/dev/null | grep "running" | grep -o "task-[a-z0-9-]*" | head -1)
  [ -n "$J" ] && (cd "$W" && node $CS cancel $J >/dev/null 2>&1)
  echo "$(date '+%F %T') $NAME は done.md が出ずに終わった(rc=$RC・cancel した job=${J:-なし})。記録=$D/$NAME.cli.out" > $D/$NAME.stalled.md
  echo "STALLED $NAME (cancelled ${J:-none})"
fi
mark 完了

前の記事の失敗1に書いたとおり、送り役のプロセスを止めても、Codex の中の仕事が残ることがありました。
そこで、done.md が無い場合は status から実行中の ID を取り、cancel を送るようにしました。
依頼名、終了コード、対象の ID を stalled.md に記録し、その行を「完了」にします。
再開が必要な作業は、記録を読んでから別途扱います。

この抜粋は、実行中の一覧から先頭の1件を選んでいます。
依頼名と ID の照合も、取り消した後の状態確認もしていません。
別の仕事が同時に動く環境へ移す時は、対象の依頼の ID を特定して、終了を確認する処理が必要です。
表の「完了」や done.md の存在だけでは、Codex の処理終了まで確認できません。
queue_runner.sh は各依頼の後に90秒待ちますが、この固定時間も終了確認とは別です。

整合役が、結果と表のずれを直す

結果の done.md が出来ても、表の状態が更新されず、後ろの仕事が待つことがありました。
そこで、queue_reconcile.sh という整合役を1分おきに動かすようにしました。
動かし方は、たとえば for i in {1..720}; do zsh queue_reconcile.sh; sleep 60; done を、バックグラウンドで1本立てる形です。
表から依頼名を取り出し、対応する done.md があれば、状態を「完了」にします。
次は、変数の定義とコメントを省いた実物です。
$D と $Q は、ここまでと同じ意味です。

perl -CSD -Mutf8 -i -pe '
  if (/^([0-9]+[a-z]?)|([^|]*)|(.*)|(待ち|実行中)\s*$/) {
    my ($row,$line,$body)=($1,$2,$3);
    my @names = ($body =~ /([A-Za-z0-9_]+_\d{4})/g);
    if (@names) {
      my $all=1; for my $n (@names) { $all=0 unless -f "'"$D"'/$n.done.md"; }
      if ($all) { s/|(待ち|実行中)\s*$/|完了\n/; }
    }
  }
' $Q
perl -CSD -Mutf8 -i -pe 's/|完了([^|]*)\s*$/|完了\n/' $Q

1行に複数の依頼名があれば、取り出した名前すべての done.md が必要です。
依頼名を取り出せなかった行は、そのまま残します。
最後の1行は「完了(補足)」を「完了」に戻す処理で、次の失敗を受けて足したものです。
対象は全角の括弧だけです。

この整合役にも、名前の書き方への依存があります。
正規表現は video_upload_1013 のように、末尾が数字4桁の部分を取り出します。
video_upload_1013b のような名前は、b の前までを拾ってしまいます。
その名前を使う場合は、依頼名の取り出し方を直す必要があります。
また、done.md の中身は読まず、ファイルの有無だけで判定しています。

運用してから足した4つの決まり

1. 状態欄には、3つの語だけを書く。

10月12日夜から13日にかけて、送り役が状態に括弧書きを足し、別の線が待ち続けました。
「完了(Zenn・Qiita・note 無料記事)」のような補足を付けると、行末の「完了」を見る判定に一致しません。
人には終わったと読めても、送り役は前の行を未完了と判断します。
状態は「待ち/実行中/完了」にそろえ、詳しい結果は done.md と記録の受け箱へ書くようにしました。
整合役は、残っていた括弧書きも外します。

2. 表を書き換えた後に、自分の行を読み直す。

10月13日昼、2つのセッションが、数分の間に別々の行を表の末尾へ移しました。
その途中で表を同時に書き換える形になり、行が1つ消えました。
保存の順序までは記録していません。
後から保存した側が、先の変更を含まない内容で上書きしたと考えています。
この時は、書き換えた後に数十秒おいて表を読み直し、自分の行が無い事に気づいて、入れ直しました。
次からは、書き換えた後に必ず読み直し、自分の行が残っているかと、行数を確かめます。

この確認は、上書きに気づくためのものです。
掲載したスクリプトには、表の同時更新を防ぐロックはありません。
整合役も表を書き換えるので、保存した直後の内容だけを見て安心することはできません。
行数が同じでも別の行が失われる場合があるため、自分の行の内容も読みます。

3. しばらく送らない仕事は、末尾へ移す。

10月13日には、利用できる枠の都合で、ある仕事を15日まで止めることになりました。
その行が「待ち」のまま途中にあると、直後の送り役は送信できません。
さらに後ろの行も前の行を待つので、締切のある動画の差し替えまで止まります。
この時は、保留する仕事の送り役を止め、行を末尾へ移しました。
急がない仕事も、締切のある行の後に置きます。

4. 再開する仕事は、今日終わる行の直後へ置く。

同じ日に、止めていた仕事を再開する判断が出ました。
ただし、末尾に置いたままだと、前に並んだ数日先の予約の仕事まで待ちます。
各送り役は直前の行だけを見ますが、その直前の行もさらに前の行を待つので、待ちが連鎖するためです。
そこで、当日中に行う動画の差し替えの直後へ戻しました。
保留する時と、今日送る時では、置く場所を変える必要がありました。

これらの並べ替えは、まだ順番を待っている送り役に対する調整です。
queue_runner.sh は、前の行の確認を一度通過すると、送信ループの中では確認し直しません。
すでに順番待ちを抜けた送り役がある時は、その状態も確認してから並べ替えます。

送る順番を、人も AI も見られるようになった

表を置くと、次に送る仕事と、その仕事を待っている線が見えるようになりました。
人、または全体をまとめるセッションが行を動かすと、順番を調整できます。
送り役は表の並びを見るので、番号を付け直す必要もありません。

締切を読んで自動で並べ替える処理は入れていません。
どの仕事を今日送るかは、人か、まとめ役のセッションが判断して表に反映します。
状態欄の書式と、保留する行の位置は、決まりにしています。
書き換えた後の読み直しは、行が消えた日には手で行いました。スクリプトにはまだ入れていません。
待ち時間や失敗の回数がどれだけ変わったかは測っていないので、数字での効果はまだ書けません。

使い始める時は、依頼名と結果のファイル名を対応させ、各送り役が同じ状態の3語を読むようにします。
停止の結果も別ファイルに残し、何を再開するかを確かめます。

公開している物

  • ai-setup-guide(無料):Mac に Claude Code・Codex・制作の道具が入っているかを一覧にする環境チェックと、入れ方の手順です。
    https://github.com/kounkt/ai-setup-guide
  • 制作一式(note):この表で Codex に流している仕事のうち、画像・LP・YouTube の作り方を、手順と道具の一式にした物です。
    https://chiero.jp/skills/
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?