the-ready

kanto-movie-collector

"Collect Kanto-area movie and screening data for the next 6 months and produce data/movies.csv for the 「イベントボード」dashboard's 映画 tab. Use when the user asks to update, refresh, or regenerate the movie dashboard data, run the weekly movie collection, or rebuild movies.csv. Also use when they invoke this by name or reference the weekly movie-gathering routine. Searches a fixed roster of Kanto cinemas (シネコンチェーン、名画座・ミニシアター、野外上映・ドライブインシアター会場)、finds each theater's current new releases, revival/retrospective programs, outdoor and special screenings, and film festivals, re-verifies every previously collected screening against the previous CSV (still showing? held over? pulled? pre-sale window closed?), records official and booking links (no images — the CSV has no image columns), and grows the theater roster in data/theaters.csv itself as cinemas open and close."

the-ready 1 Updated 3w ago
GitHub

Install

npx skillscat add the-ready/player-one/kanto-movie-collector

Install via the SkillsCat registry.

SKILL.md

関東映画収集タスク(週次)

このタスクの目的

data/movies.csv を更新し、ダッシュボード「イベントボード」の「映画」タブに今週から6ヶ月以内に観に行ける上映を並べること。

新作ロードショーだけではない。名画座・ミニシアターのリバイバル上映、夏季の野外上映やドライブインシアター、爆音上映・オールナイトなどの特別上映、映画祭まで含めて「新作映画や、復活上映の映画の公開日や期間」を利用者が一目で把握できるようにすることがゴールである。

/kanto-event-collector と同じ思想(実際に確認できた情報だけを書く。不明なら空欄にする)をそのまま踏襲する。特に料金・上映期間については、確認していないものを推測で埋めることを固く禁止する。


取得してよいもの・いけないもの(着手前に読むこと)

このタスクは GitHub Actions の self-hosted runner から週3回(水木金)自動実行される。「人が調べているのと同じ」ではなく機械的なアクセスである。

  1. robots.txt が Allow している範囲だけを取得する。 サイト単位ではなくURL単位で見る
  2. 収集後に拒否の意思が示されたと分かったサイトは、由来する行を消し、data/sources.json からも外す
  3. 利用規約で自動収集・データの二次利用を明示的に禁じているサイトは使わない
  4. 会員登録・ログインが要る領域には立ち入らない(無料会員でも同じ)
  5. 同じホストへ続けて当たらない(待つのは fetch_gate.py の仕事で、自分で数える必要はない)
  6. 1サイトあたりの取得は、その週の調査に要る分だけ

判断に迷ったら取らない。 1と2が礼儀ではなく著作権法47条の5の政令基準に対応すること、
3を別項にしてあるのは30条の4が契約を上書きしないためであること、判定に
Claude-User と Claude-SearchBot の厳しいほうを採る理由——これらは
docs/COLLECTION-PROTOCOL.md 第6.5節に全部書いてある。判断に迷ったらそこを読む。

着手前:使えないサイトを一望する(検索予算を消費しない)

python3 tools/check_robots.py                  # data/sources.json の調査元サイト
python3 tools/fetch_gate.py --roster theaters   # 名簿のURL(この工程の主な取得先)

どちらも robots.txt だけを見る軽い確認なので、検索予算を消費しない。拒否と出たURLは今回の収集から外す。

取得のたび:URLごとに判定し、間隔を空ける(自動で走る)

サイトのトップが「OK」でも、これから開くページが Allow されているとは限らない。
tools/fetch_gate.py が PreToolUse フックに登録してあり、WebFetch のたびに自動で走る。

  • 拒否されたURLはブロックされる。 別ページを当たるか、その行を落とす。回避しようとしないこと
  • 許可されたURLは、必要な待ち時間を消化してから通る。 自分で数えて待つ必要はない
  • 「次の取得まで N 秒必要」と断られたら、先に別のホストを回る(Crawl-delay: 120 のサイトが実在する)
python3 tools/fetch_gate.py --check <URL>   # 判定だけ(待たない・記録しない)

curl でページ本体を取りに行かないこと。 ゲートを迂回することになる。
ページの内容が要るときは python3 tools/fetch_page.py <URL> --text を使う——
ゲートを通したうえで本文だけを取る正規の手段である(下記「検索を使わずに済ませる」)。

拒否されている先でも、許可された経路があることがある。 connpass は
Disallow: / だが Allow: /*.ics を併記しており、
https://connpass.com/event/{id}.ics は取得してよい(fetch_page.py --ics で読める)。
「このサイトは拒否」で止めず、robots.txt が何を開けているかを見ること
(docs/COLLECTION-PROTOCOL.md 第6.5.8節)。

実行時の出力について

このタスクはユーザーとの対話ではなく、システムが定期的に自動実行するものである。あいさつ・前置き・確認や提案といった会話的な文章は書かず、タスクの遂行に必要な情報(進捗のログ、最終的な集計結果など)だけを出力するようにしよう。


完走条件 —— このタスクをどう終えるか(着手前に読むこと)

このタスクは「調べ終わったとき」ではなく、終了工程を通したときに終わる。

検索の予算が尽きた、コンテキストが逼迫したなどで調査を途中で止めざるを得なくなったときの正しい振る舞いは、「そこで終わる」ことではなく、探索をやめて下の撤退の手順に入り、終了工程まで通し切ることである。

終了工程(省略不可)

2〜7 は python3 tools/close_run.py movies の1回でまとめて通す(中止・改名の処分 prev_rows.py movies --dispose だけは判断が要るので、先に済ませておく)。1コマンドずつ打たないこと——終了工程は親の文脈がいちばん膨らんだ時点で走るので、1回打つごとに200〜300kを送り直す(2026-09-25 の lives は最後の波の後に72ターン回り、その回の消費の53%を終了工程に使った)。落ちたら、まとめに出たものだけを直してもう一度回す。調べたうえで少ない都県・薄い列は --allow-short / --allow-thin をこのコマンドに付けて承知する。承知はこの回のあいだ記録され、終了前フックにも効く。

# やること コマンド
1 手元に残っている調査済みの行を書き切る python3 tools/append_rows.py movies
2 終了日を過ぎた行を機械的に取り除く(通常0件) python3 tools/purge_ended.py movies
3 中止・改名した行に理由を付けて取り除く python3 tools/prev_rows.py movies --dispose
4 名簿を整理する python3 tools/roster.py theaters --gc
5 差分を確定する(終了コード0) python3 tools/diff_data.py movies
6 検証する(ERROR 0) python3 tools/validate_data.py
7 充足率と分布を出し、下限を判定する python3 tools/report_stats.py movies --check
8 報告を出力する ——

この8つはすべてローカルのコマンドで、Web検索を1回も使わない。 つまり検索の予算が尽きていても、コンテキストが逼迫していても、終了工程は必ず実行できる。「予算が尽きたので終わります」という終わり方に、正当な理由は存在しない。

2 はステップ0の --carry-rest --apply が既に同じ処分をしているので、通常は0件で通る。 残してあるのは、調査中に書いた行が終了日を過ぎている場合の保険としてである。以下は、この工程がもともと何のために置かれたかの説明である。

2 は「まだ確認できていない行」の保険であって、1・3の代わりではない。 映画は終映日が告知されないまま静かに終わることが多く、tier A の再確認が検索予算切れで間に合わなかった行は、上映が終わった後もそのまま持ち越されがちである——表示側は上映終了日から毎回「上映終了」を計算して既定の一覧から隠す(設計書 第5.4.1節)ので利用者の目には触れないが、CSVの中には残り続ける。purge_ended.py は「終了日(end_date)が今日より前か」という一点だけを機械的に見て、そういう行をここで刈り取る。判断が要らないぶん --dispose より先に置いてあり、消した行には expired という処分が自動で付くので、3 で個別に扱う必要はない。確認できた行を ended として --dispose するほうが、この自動処理より常に望ましい。 expired は「確認できないまま終了日だけが過ぎた」という、確認不足のしるしだからである。新作ロードショー(end_date が未定のまま上映中の行)はこの機械判定の対象外——上映終了は日付ではなく劇場の判断で決まるため、収集タスクが公式情報で確認するしかない。

やってはいけない終わり方

  • ❌ --init した後、追記が途中のまま終える(ダッシュボードに残るのは書けた分だけになる)
  • ❌ 調査結果をコンテキストに溜めたまま終える(追記していない行は、最初から無かったのと同じ)
  • ❌ diff_data.py / validate_data.py を通さずに終える
  • ❌ 「予算が尽きたのでここまでにします」と述べて、終了工程を飛ばす
  • ❌ 報告を出さずに終える

--init は data/movies.csv を空にする。 ただしステップ0の --carry-rest --apply を通した時点で、CSVは先週の完全な状態に戻っている——そこから先の打ち切りで失うのは「今週増やせたはずの分」だけである。逆に言えば、ステップ0を通す前に終えると、その週の収集はまるごと失われる。

自動実行のスクリプト(.claude/scripts/claude-routine.sh)は、diff_data.py と validate_data.py が通らなかった回をコミットせず、data/ を実行前の状態に戻す。途中で終えた週は、その週の収集がまるごと失われる。

撤退の手順 —— 打ち切りは「中断」ではなく「終了工程へ進むこと」

調査は打ち切ってよい。次のどれかに当たったら、その場で探索をやめて下の手順に入る。

  • 工程ごとの枠(「調査予算」)を使い切った
  • 検索の合計が150回に達した(枠の合計170回のうち、20回を余裕として残す)
  • コンテキストが逼迫してきた
  • 情報源に到達できない状態が続く(取得の失敗が連続する)

ステップ0を通してあれば、撤退は「中断」ではなく「今週の上積みをここで止めること」である。
CSVは既に先週の完全な状態にあり、そこに今週わかったぶんが乗っている。前回行を
慌てて書き戻す工程はもう無い——だから撤退の手順は短い。

  1. 調査済みで未追記の行を append_rows.py で書き切る
  2. 子が書いた temp/dispose-movies-*.jsonl(今回の波のぶんだけ)を python3 tools/prev_rows.py movies --dispose に通す(cat temp/dispose-movies-*.jsonl | python3 tools/prev_rows.py movies --dispose。エラーで止まったら、その行を直すか外してやり直す)。これを飛ばすと、公開延期・中止になった作品が載り続ける
  3. python3 tools/diff_data.py movies --allow-unexplained --allow-noop で差分を一覧する(出力を切らない)
  4. [表記が変わった可能性] に挙がった組だけは片付ける(ここから先は検索を使わない)
    • 同じ作品だと分かる組 → --dispose に renamed として記録する。放っておくと
      旧uidの持ち越し行と新uidの新規行が両方とも公開される(ステップ5の説明を参照)
    • 判断が付かない組 → そのままでよい。来週 tier A で見直す
  5. 終了工程の 2〜8 を通す
  6. 報告に「どの工程を、どの理由で打ち切ったか」「今週あらたに書いた行数」「埋められなかった列」「上映劇場を特定できずに落とした作品の件数」を書く

--carry-rest --apply を撤退時に打ち直す必要はない。 ステップ0で既に通っており、
もう一度走らせても「今回書かれていない前回行」は残っていないので何も起きない。

進捗はツールが出す(自分で書かなくてよい)

append_rows.py が追記のたびに、次の1行を標準エラーへ出す。自動実行のログに残り、
途中で止まったときに「どこまで進んだか」を人間が判別できる唯一の手がかりになる。

[進捗] 追記8件(累計34件)/ 検索 63/170(残り107) / 取得 154(うち拒否2) / 待機 12分 / 経過 96分/360分(残り264分)

工程名を添えたいときは、工程の切り替わりで python3 tools/budget.py --phase "<工程名>" を打っておく。


出力仕様

出力先

  • data/movies.csv のみ(全件を毎回書き直す。追記ではなく完全再生成)。index.html はこのタスクでは編集しない
  • ダッシュボードは data/movies.csv を Web サーバー経由で取得する前提で作られており、file:// で直接開く運用は想定していない

書き出し先に注意: ダッシュボードは data/ 配下しか読まず、別パスへのフォールバックを意図的に持たない。リポジトリのルート直下に movies.csv を書いても反映されず、古い表示が残る。

  • data/theaters.csv(映画館の名簿)も tools/roster.py 経由で更新する。SKILL.md(この文書)の目標件数・品質基準・禁止事項は自分で書き換えない。 名簿(データ)は自動更新、それらは提案止まりという線を守り、変更提案は docs/skill-feedback.md に書く。それに当たらない小さなバグ(誤字脱字、壊れたコマンド例、古いパス・URLの参照、tools/*.py の些末な不具合など)は、この SKILL.md・tools/・対応する設計文書の記述を自分で直してよい。直した内容は docs/skill-feedback.md の「自己修正ログ」に記録すること。線引きは docs/COLLECTION-PROTOCOL.md 第7.1節

初期化(省略不可・親のみ実行)

調査開始前に、次を実行して data/movies.csv をヘッダーだけの状態にする。このコマンドは親だけが実行する。サブエージェントは実行しない——サブエージェントに実行させると、他の波が書いた分もろとも消える。

python3 tools/append_rows.py movies --init

このとき前回の内容は data/.prev/ に自動退避される(差分検知の材料になる)。

出力方法(CSV書き出しの手順)

全件を最後にまとめてCSVに書くのではなく、調査の区切りごとにバッチで追記する。全件を調査結果としてコンテキストに溜め込んでから最後に一括生成する、という進め方はしないこと。

  1. 5〜10件の調査が完了するごとに、結果をまとめて以下の形式でCSVに追記する

    python3 tools/append_rows.py movies <<'EOF'
    {"title": "...", "genre": "...", "theater": "...", ...}
    {"title": "...", "genre": "...", "theater": "...", ...}
    EOF
  2. コンテキストに全件のデータを保持する必要はない。追記が終わったバッチの内容は忘れてよい(次の調査のためにコンテキストを空ける)

  3. id はスクリプトが自動採番する。JSONに含める必要はない

  4. JSONに含めない列は空文字で補完される。分かっている列だけを渡せばよい

  5. 全件の調査・追記が終わったら python3 tools/diff_data.py movies と python3 tools/validate_data.py を実行する

自然なバッチの区切りの例:

  • 公開予定カレンダーで1ヶ月分の新作を拾ったらまとめて書く
  • 名画座・ミニシアターの定点観測リストを調べたらまとめて書く
  • 調査ステップが変わる節目(新作調査→名画座調査→野外上映補完)でまとめて書く

前回から変わっていない列は書き直さない(持ち越し)

劇場の座標・読み・劇場URLに加え、desc area official_url genre series_id も前回値から自動で補完される。空欄のまま渡してよい。(screening_type は作品の識別に使う列なので、毎回書くこと)

内容にも変更がないことを確認できた行は、"_carry": "*" を添えると note なども前回値で埋まる。

python3 tools/append_rows.py movies <<'EOF'
{"title": "...", "release_date": "2026-07-31", "screening_type": "new", "genre": "anime",
 "theater": "TOHOシネマズ|109シネマズ", "area": "東京都・千代田区", "pref": "tokyo",
 "price": "一般2,000円",
 "source": "eiga.com", "url": "https://...", "_carry": "*"}
EOF

公開日・料金・前売り期間は持ち越せない。 onsale_end や price_best を _carry に指定するとスクリプトがエラーで落ちる。ムビチケの販売終了日は公開日に向かって迫ってくるので、前回値の流用は「終わった前売りを発売中に見せる」ことになる。

CSV列(この順序・この列名を厳守)

id,title,kana,genre,screening_type,area,theater,theater_url,pref,release_date,end_date,date,dates,open_time,start_time,end_time,date_note,backup_date,status,rank,series_id,announced_date,is_additional,onsale_label,onsale_start,onsale_start_time,onsale_end,onsale_end_time,limited_sale,price,price_official,price_best,discount_pct,best_source,coupon_note,price_checked,price_condition,source,url,official_url,lat,lng,desc,note

全44列。列名・順序ともに厳守すること。空欄でよい列は多いが、列そのものを省いてはいけない(ヘッダーが1列でもずれると検証で落ちる)。

書き終えたら python3 tools/validate_data.py を実行し、ERROR が0件であることを確認すること。

列 規則
id 1から連番
title 正式名称。邦題・原題の別名やサイト独自の煽り文句は含めない
genre 下記ジャンルキーから該当するもの。複数は | 区切り
screening_type 下記上映形態キーから該当するもの。複数は | 区切り(例:revival|special)
area 表示用の地名(例:東京都・豊島区)
theater 下記「theater列の書き方」を厳守すること。不明なら -
pref tokyo kanagawa saitama chiba ibaraki tochigi gunma のいずれか
release_date / end_date YYYY-MM-DD。不明なら必ず空欄。推測で埋めない。新作ロードショーは終了日未定のことが多く、その場合 end_date は空欄でよい
日程のその他の列 dates open_time start_time end_time date_note backup_date date status rank は下記「日程の書き方」を参照。表示用の日付文字列は書かない
price 実際の料金(例:一般2,000円/大学生1,500円/高校生以下1,000円)。不明なら 要問合せ
source 情報の出典サイト名。URLではなく、人間が読める短いサイト名を書くこと(例:eiga.com シネマトゥデイ 公式サイト)。ダッシュボードはこの値をボタンのラベル「{source}で上映情報を見る」にそのまま使うため、URLを書くと「https://...で上映情報を見る」と表示されてしまう
url 上映スケジュール・座席予約ページなど、ユーザーが次に見るべきページ
official_url 作品公式サイト。見つからなければ空欄(推測URLを書かない)
theater_url 劇場・上映会場の公式サイト
lat / lng 劇場・会場の代表座標(小数4桁程度)。theaters.csv に載っている劇場は空欄で渡す(名簿と前回値から自動補完されるので調べない)。調べるのは、名簿にも前回にも無い会場——野外上映・ドライブインシアターの臨時会場など——だけで、そこは地図検索・現在地検索のためにできる限り埋める。不明なら空欄
desc 100〜150字程度。何が目玉かを具体的に書く(下記「descの書き方」参照)
note 日時指定制、整理券制、雨天中止、字幕/吹替の別など、料金以外の実務的な注意事項。なければ空欄

日程の書き方(3つの収集スキルで共通)

画面に出る日付の文字列も、開催中 まもなく開催 といった開催状況のバッジも、
ダッシュボードが表示のたびに組み立てる。
ここで書くのは、機械が読める事実だけである。

列 規則
dates 飛び日程(連続していない複数日)の実開催日を | 区切りで全部並べる。例:2026-08-10|2026-08-11|2026-08-14。連続した会期では空欄(開始日〜終了日で足りる)。書くときは先頭を開始日と、末尾を end_date と一致させること。この列があると、開催しない日は「本日は休み」と表示され、日程の絞り込みからも外れる
open_time 開場時刻 H:MM。無ければ空欄
start_time 上映開始時刻 H:MM。無ければ空欄
end_time 終了時刻 H:MM。無ければ空欄
date_note 上のどれにも収まらない但し書き(例:オールナイト 土日祝のみ 1週間限定 毎朝10時開始)。日付や時刻そのものをここに書き写さない——構造化列に入れれば画面が組み立てる
backup_date 予備日(雨天・荒天で順延したときの日)を | 区切りで。会期には含めない——本開催で終わればこの日は使われないので、end_date を1日延ばすと「まだやっている」と嘘をつくことになる。カードの日付欄ではなく「くわしく」の中に出る。予備日の当日は「本日予備日」バッジが出るので、順延が実際に起きたら開始日・終了日を新しい日に書き換えてこの列を空にすること
date 原則として空欄。 ISOの日付に落とすと持っていない精度を騙ることになる日程(2026年10月中旬〜下旬(見頃予想) 8月の毎週土日 など)だけ、ここに自由記述で書く。書いた行はこの文字列がそのまま表示され、上の構造化列は使われない。ただし月・季節までは読み取れる場合、release_date/end_date にはその月・季節をまるごと設定すること(下記「幅のある予測日程」参照)——空欄のままだと日程の絞り込みが「無期限」として扱い、無関係な日で絞っても表示されてしまう
status / rank 原則として空欄。 日付から毎回計算される。開始日も end_date も持たない行——つまり date に自由記述を書いた行——でだけ、下の表から選んで書く

なぜ表示用の文字列を書かないのか。 以前は date に表示用の文字列を、status に
収集した日の判定を書いていた。書き手ごとに表記がそろわず(同じ会期が 2026.8.1〜9.30 にも
2026年8月1日〜9月30日 にもなる)、しかも収集した日で時間が止まる——週の後半に見た人には、
始まっているものが「まもなく開催」のまま残る。どちらも「計算できる値を、計算せずに書き置いた」
ことが原因である。書いた値が画面に出ないのは気持ちが悪いかもしれないが、書かないほうが正しい
(設計書 第3.9節)。

幅のある予測日程(「10月中旬〜下旬」「2026年夏」など)は、date だけでなく release_date/end_date も埋める

date に自由記述を書く行のうち、月・季節までは読み取れるものは、release_date/end_date を
空欄のままにしてはいけない。空欄は「本当に何も分からない」の意味になり、日程の絞り込みが
一切効かなくなる
——10月開催のイベントが8月の日付で絞っても一覧に出てしまう。
読み取れた月・季節をまるごと設定する(精度は落ちるが、誤って除外するより実害が小さい。第3.9.6節)。

読み取れた期間 release_date end_date
10月中旬〜下旬 2026-10-01 2026-10-31
2026年夏 2026-06-01 2026-08-31

date 列の表示はそのままでよい。埋めた行は status/rank を空欄に戻すこと。
月すら読み取れない行(「会期は公式サイト参照」)は両方空欄のままにする——
読み取れる情報が無いところに月を割り当てるのは、それこそ憶測になる。

python3 tools/validate_data.py は、date に「◯月」「春」「夏」「秋」「冬」を含む自由記述があるのに
release_date/end_date が両方空の行を WARNING で挙げる。出たら必ずこの手順に従うこと。

新しく追加された列(以前の版には無かったもの)

列 規則
kana 作品名の読み(ひらがな or カタカナ)。漢字の作品名を「かな」で検索できるようにするための列。表示には使わない。読みが自明でない作品だけでよく、全件必須ではない
series_id 特集上映・映画祭のプログラムをまとめるキー(例:s-ozu2026)。同じ特集の各作品/同じ作品の複数会場に同じ値を入れる。単発上映は空欄
announced_date 公開日・上映が発表された日 YYYY-MM-DD。7日以内なら画面に NEW バッジが出る。分からなければ必ず空欄
is_additional 上映延長・アンコール上映が決まった行は 1、それ以外は空欄
onsale_* / limited_sale 前売り券(ムビチケ等)の販売期間(下記「前売り券」参照)
price_official 〜 price_condition 料金の比較レイヤー(下記「料金」参照)。条件付き割引は price_condition が必須

前売り券(onsale_*)の書き方

映画の「受付期間」は前売り券の販売期間にあたる。書くと、カード表面に「前売り発売中/前売り発売前/前売り終了」と締切カウントダウンが出て、「前売りが買えるものだけ」で絞り込めるようになる。

列 規則
onsale_label 券種(例:ムビチケ前売券(カード) オンライン前売券)。売り切れ・終了なら 販売終了 SOLD OUT
onsale_start / onsale_start_time 販売開始日 YYYY-MM-DD と時刻 HH:MM
onsale_end / onsale_end_time 販売終了日と時刻。多くのムビチケは公開日の前日で販売を終えるが、作品ごとに違うので必ず販売ページで確認する
limited_sale 特典付き前売り・数量限定券など(例:特典付き 限定5,000枚)。無ければ空欄

時刻が書かれていないときは空欄にする。 画面側は「開始時刻が空なら0:00、締切時刻が空なら23:59」として扱う。勝手に補うと、その時刻まで「発売前」と嘘をつくことになる。

料金(price_official 〜 price_condition)—— 条件を書けない割引は載せない

映画料金は「同じ席を安く買う」経路が多く、イベントと同じ形で比較できる。ただし映画の割引はほとんどが条件付きである点がイベントと決定的に違う。

列 規則
price_official 通常料金(一般1名・円・数値のみ)。例:2000
price_best 実際に払える最安額(一般1名・円・数値のみ)。例:1300
discount_pct 割引率(整数%)。両方揃った時のみ。値引き額から計算した値と2%以上ずれると検証で落ちる
best_source 最安を提供している経路名。例:ムビチケ前売券 auマンデー イオンシネマ ハッピーマンデー
coupon_note 配布中クーポンの説明
price_checked 価格を確認した日 YYYY-MM-DD。価格を1つでも書いたら必須
price_condition 適用条件。条件付きの割引を書いたら必須

price_condition の絶対規則。

  • au/UQ mobile会員・月曜のみ / 毎月1日 / 20:00以降の上映回 / 会員登録が必要 / 60歳以上 のように、誰が・いつ・どうすればその額になるのかを書く
  • 条件が特定できない割引は、書かずに落とす。条件を伏せた安い価格は、値段を出さないより有害になる(「1,300円だと思って行ったら2,000円だった」を作る)
  • 誰でも無条件に買える前売り券(ムビチケ一般1,500円など)は、条件が「公開前に買うこと」だけなので 公開日前日までの購入 と書く
  • レディースデイ等、特定の性別・属性を条件とする割引は載せない。 適用できない人には無関係な情報で、そのために表示スペースを割く意味がない

ジャンルキー

キー 対象
anime アニメ映画
jp-live 邦画(実写)
foreign 洋画(実写)
doc ドキュメンタリー
fam ファミリー・キッズ向け
tokusatsu 特撮・ヒーロー
music 音楽・ライブ映画

上映形態キー

キー 対象
new 新作公開(シネコン等での通常ロードショー)
revival 名画座・ミニシアターでのリバイバル上映・特集上映
outdoor 野外上映・ドライブインシアター
special 特別上映(爆音上映・オールナイト・舞台挨拶付き・応援上映等)
festival 映画祭

theater 列の書き方(重要)

新作ロードショーで特定の1館だけを書くと、「そこでしか観られない」という誤解を与える。上映形態によって書き方を変えること。ダッシュボード側は、新作(チェーン名)の行は場所を表示せずチェーン名のみ表示し、名画座・野外上映・特別上映・映画祭の行は具体的な会場名を表示する仕様になっている。この表示仕様と矛盾しないように、以下の書き方を厳守する。

screening_type theater に書く内容
new(新作・全国/広域公開) 上映しているシネコンチェーン名を書く(例:TOHOシネマズ 109シネマズ イオンシネマ ユナイテッド・シネマ MOVIX T・ジョイ USシネマ ヒューマントラストシネマ)。特定の1館名(例:TOHOシネマズ日比谷)は書かない。複数チェーンで同時公開されている場合は | 区切りで複数書いてよい(例:TOHOシネマズ|109シネマズ|イオンシネマ)。チェーン名の表記は下記「チェーン名の表記統一」のリストの表記に厳密に合わせること(ダッシュボードが theaters.csv の店舗ディレクトリと突き合わせて店舗地図を表示する際、表記が一致していないと機能しないため)
new(単館・limited公開) その作品を上映しているのが本当に1館だけ(単館系ミニシアター限定公開など)と確認できた場合に限り、その具体的な劇場名を書く
revival(名画座・リバイバル) 具体的な劇場名を書く(例:新文芸坐 早稲田松竹)。その劇場でしか観られない一回性の企画のため
outdoor(野外上映・ドライブイン) 具体的な会場名・臨時会場名を書く
special(特別上映) 具体的な劇場名を書く(爆音上映・オールナイト・舞台挨拶付きは特定の劇場・日時に紐づく一回性の企画のため)
festival(映画祭) 具体的な会場名を書く

area・lat/lng は theater の書き方に関わらず、代表的な上映劇場(新作なら関東での主要上映館の1つ。複数チェーンの場合はいずれか1つでよい)の座標を入れてよい。座標は地図検索・現在地検索のために必要なので、チェーン名を書く場合でも省略しない(ダッシュボードは新作行のareaをカード上のテキストとしては表示しないが、地図検索・県フィルタの内部処理では引き続き使うため、空欄にしないこと)。

チェーン名の表記統一(theaters.csv と一致させる)

映画チェーンの店舗ディレクトリは data/theaters.csv に別途まとめてある(chain,name,pref,area,lat,lng,url に加えて、名簿の保守用に status,first_seen,last_hit,hit_count,note を持つ)。新作行の theater に書くチェーン名は、必ず theaters.csv の chain 列に実在する値と完全に一致させること(表記ゆれがあると、チェーン名をクリックしたときの店舗地図が空になる)。対象チェーンは概ね以下の通り:

TOHOシネマズ / イオンシネマ / 109シネマズ / MOVIX / ユナイテッド・シネマ / T・ジョイ / USシネマ / ヒューマントラストシネマ

theaters.csv に載っていない新しいチェーン名を使う場合は、python3 tools/roster.py theaters --add で名簿に追加したうえで、収集結果の報告にもその旨を書くこと(チェーン名の表記が名簿と一致していないと、店舗地図が空になる)。


収集ルール

1. 対象期間は「直近6ヶ月のみ」

実行日から6ヶ月後までに公開・上映されている、または公開・上映が始まる作品のみを対象とする。

  • ✅ 実行日時点で上映中のもの(新作ロードショーは終了日未定でも可)
  • ✅ 6ヶ月以内に公開・上映が始まるもの
  • ❌ 6ヶ月より先にしか公開・上映されないもの → 含めない
  • ❌ すでに上映が終了したもの → 含めない

2. 対象地域は関東1都6県

東京都・神奈川県・埼玉県・千葉県・茨城県・栃木県・群馬県。

3. 目標件数

revival・outdoor・special・festival は合計で20〜40件を目安とする(5つの上映形態のうち new 以外の4種すべてから最低3件は拾うこと。ジャンルも8割が邦画実写・アニメに偏らないよう、洋画・ドキュメンタリー・特撮なども探すこと)。

new(新作)には上限を設けない。 「ステップ1:新作ロードショーの網羅調査」で拾えた対象期間内の新作は、目標件数を理由に間引かず全件載せる。結果として全体の総件数が70件を超えても構わない(件数より網羅性を優先する)。

各都県から最低2〜3件は拾い、東京に偏りすぎないようにすること(東京は全体の半分程度まで)。


調査予算 —— 検索は有限である(着手前に読むこと)

Web検索の呼び出しは、1セッション200回で打ち切られる。

このスキルは特に上限に当たりやすい。 公開予定の一覧サイトには対象月だけで120件超が載ることがあり、それを「新作は全件拾う」と解釈して1件ずつ裏取りすると、上限に達して、公開日だけ分かって上映劇場・公式URLを確認できなかった作品を丸ごと落とすことになる。

つまりこのスキルでは、予算の配り方がそのまま収録件数を決める。「たくさん調べる」ことは品質ではない。

工程ごとの検索の枠

この表を守れば、セッション全体の回数を数える必要はない。工程の中だけで数え、枠を使い切ったらその工程を終える。

工程 検索の枠 備考
ステップ0〜0.5 土台・棚卸し 0 ローカルのコマンドだけ
ステップ1 新作の網羅調査(カレンダー) 20 月ごとの一覧+主要配給会社。1ページから何十件も取れる工程なので単価が安い
ステップ2 名画座・ミニシアター 5 名簿のURLに直行する(全件URLあり)
ステップ3 野外・特別上映・映画祭 20
ステップ4 各作品の深掘り 80 新規行のみ。前回から続く行は前回の official_url に直行
ステップ4.5 いま載っている行の欠けを埋める 0 前回行の url/official_url に直行する。検索が要るときは予備から使う
予備 45
合計 170 上限200に対して30回の余裕を残す

消費は自分で数えない —— budget.py が実測を持つ

python3 tools/budget.py --phase "ステップ2 エリア軸の探索"   # 工程の切り替わりで打つ
python3 tools/budget.py --report --verbose                   # 工程別の内訳(報告にそのまま貼る)
[予算] 検索 63/170(残り107・上限200) / 取得 154(うち拒否2) / 待機 12分 / 追記 34行 / 経過 96分/360分(残り264分)

枠を超えたかどうかは、この出力で判断する。記憶で判断しない。 数えるのはフックの仕事なので、
自分で数える必要はない(理由は docs/COLLECTION-PROTOCOL.md 第8.7節)。

  • 上の枠表は検索の枠だが、実際に多いのは取得のほうで、時間の律速もそちらにある
    (fetch_gate.py が Crawl-delay を消化するため)。「次の取得まで N 秒必要」と断られたら、
    待たずに先に別のホストを回る
  • 時間で「まだ余っている」と判断しない。 枠は6時間あるが、実際に実行を止めるのは
    トークンのほうである。2026-08-26 の回は経過39分・検索14/170という表示の9分後に、
    アカウントの利用上限で強制終了された(docs/COLLECTION-PROTOCOL.md 第8.7.1節)。
    唯一の完走例も27分で終わっている(かつてここに書いていた「5時間27分」は、
    失敗した起動から数えた誤りだった)
  • 見るのは budget.py --report の [利用率] の行。 サブスクリプションの実際の利用率(5時間枠・7日枠)で、
    claude-routine.sh が数分おきに測り直している。2段階の線がある。
    • 5時間枠80%・または7日枠が「後の回の分を残す線」「新しい波を投げないでください」→ 動いている波は受け取って書き切り、終了工程へ
    • 5時間枠92%・または7日枠96%「撤退の手順に入ってください」→ 新しい調査はやめ、終了工程だけを通す
      撤退が出たら終了工程を通し切る余力を残して「撤退の手順」に入ること
    • 利用率が読めないときだけ、代わりに文脈再送の線(45M/60M)で同じ判定をする。
      文脈再送は2026-09-30まで応答1回をブロックの数だけ重ねて数えており(実際の約2倍)、
      線に届いたように見えても実際の利用率には大きな余りがあった

検索を使わずに済ませる(最も効く節約)

1回の取得から複数の行が取れる経路を優先する。 どれも WebSearch を消費しない。

経路 何が取れるか
roster.py --list --urls / prev_rows.py --uid 調査先のURL・前回の official_url(知っているURLを検索し直さない)
fetch_page.py <URL> そのページから何が取れるかの要約。次の手はこれを見てから選ぶ
fetch_page.py <URL> --schedule 日付を含む行と、その日にぶら下がる行だけ。会場・劇場のスケジュールページはまずこれ(本文が3〜4割に減る)。欲しい興行が出ていなければ --text に戻す
fetch_page.py <URL> --text 本文だけ。表はタブ区切りで残る(WebFetch の markdown 化で崩れる一覧の構造が保たれる)
fetch_page.py <URL> --links 「文字列+絶対URL」の一覧。一覧ページから詳細ページへ辿るとき
fetch_page.py <URL> --sitemap --since <日付> その施設の新着ページだけ
fetch_page.py <URL> --events / --ics 日付・会場・中止/延期が構造化された形で

会場スケジュールのように「行と列の対応が意味を持つ」ページでも --text でよい。
セルはタブ区切りで残る。JSON-LD は期待するほど普及していないので総当たりしないこと(第6.5.9節)。

--raw(生HTML)は --out が無いと動かない。 実測した信号率は3%——216ページ・
3,190万文字を通したところ、本文は76万文字(42分の1)だった。残る97%はタグ・スクリプト・
トラッキングで、文脈を埋めながらモデルの想起精度を下げる。生HTMLが本当に要る場面
(__NEXT_DATA__ の取り出しなど)は --raw --out temp/page.html で落として grep する。

data/theaters.csv は全件にURLが入っている。知っているURLを検索し直さないこと。

python3 tools/roster.py theaters --list --urls              # 4列目にURL
python3 tools/roster.py theaters --list --urls | awk -F'\t' '$2==""'   # 名画座・単館だけ

2列目が chain(空欄なら名画座・単館)。ステップ2で回るのは名画座・単館だけでよい——シネコン店舗は新作の上映館であって、独自の特集上映を組む先ではないためである。名画座・単館だけなら取得はその件数で済み、検索はステップ2の枠5回——名簿に足したばかりでURLが無い劇場のぶんだけである。

前回から続いている作品も同じで、前回の official_url を引けば検索は要らない。

同じことを二度調べない —— 打ち切りの基準

必須列が揃った行は、そこで調査を終える。

区分 列 扱い
これが揃ったら終わり title genre screening_type area theater pref release_date/end_date price source url desc 揃った時点で次の行へ移る
ついでに拾うだけ kana note theater_url series_id announced_date is_additional 開いたページに書いてあれば書く。このために追加でページを開かない
調べない(臨時会場を除く) lat / lng theaters.csv と前回値から自動で補完される。空欄で渡す。ただし名簿にも前回にも無い臨時会場(野外上映・ドライブイン)は、その会場のページを開いたついでに埋める

具体的にやってはいけないこと

  • ❌ シネコンチェーンの店舗を1館ずつ検索して、同じ新作の上映情報を何度も確認する(theater にはチェーン名を書くので、店舗ごとの確認は不要)
  • ❌ 新作の公開日を、カレンダーサイトで確認した後に配給会社サイトでも確認し直す(食い違いに気づいたときだけ当たり直す)
  • ❌ ポスターやキービジュアルの画像URLを探す(CSVに画像の列が無い。探す工程そのものが存在しない)
  • ❌ kana や series_id が分からないので検索する(分からなければ空欄が正しい)
  • ❌ 料金の割引条件を特定するために検索を重ねる(条件を書けない割引は価格ごと落とすのがこのスキルの規則である)

「新作は全件」の現実的な線引き

「新作には上限を設けない」という方針は維持する。ただし予算の中で全件を拾うために、深掘りの対象を絞る。

  • ✅ シネコンチェーンで公開される作品、または上映劇場が具体的に確認できる作品 → 全件拾う。これが「全件」の対象である
  • ⚠️ 単館系ドキュメンタリー・自主制作・海外インディーズで、上映劇場を特定できない作品 → カレンダーで公開日を確認した段階で、1回調べて劇場が分からなければ落とす。url は必須列なので、どのみち収録できない
  • ❌ 1作品のために3回以上検索する → その予算で、確認できる作品を3件収録するほうが利用者に資する

予算を薄く広く配ると、「日付だけ確認できて劇場を確認しきれない作品」が多数残り、どの作品も収録できないまま終わる。

予算が尽きたら、何を捨てるか

上から守り、下から捨てる。

  1. tier A の再確認(映画は終映日が告知されずに静かに終わる。落とすと終わった作品が載り続ける)
  2. 新作の網羅(カレンダーの確認。落とすと話題作を落とす)
  3. 公開前の新作の前売り(onsale_*。下記「今週かならず産む成果」)
  4. 新規行の深掘り
  5. 名画座・野外・特別上映の探索
  6. 補助列(kana note series_id)の穴埋め ← 最初に捨てるのはここ

onsale_* を 6 から 3 に移してある。 補助列と同じ扱いにしていた結果、
実測では130件すべてが空欄(0%)だった。前売りは「あれば書く」ものではなく、
利用者が公開前に行動できる唯一の情報である(price_* と違い、
公開日が来れば取り返しがつかない)。

目標件数に届かないことより、未検証の行を足すことのほうが重い失敗である。 打ち切ったら、その事実と工程名を報告に書く。

今週かならず産む成果(下限)

捨てる順を決めるだけでは、何も産まない週を止められない。 下限を決めておく。

成果 下限 確認のしかた
公開前の新作の前売り release_date が実行日より後の new 行について、前売りの有無を確認する report_stats.py の onsale_label 充足率
新作の網羅 対象期間の全月のカレンダーを確認する 報告に確認した月を書く
上映終了日 名画座・特集上映(revival special festival)は end_date 必須 report_stats.py の end_date 充足率

前売りは、作品公式サイトを開いた同じ取得の中で分かる。 「チケット」「前売券」の
記載を見て、あれば券種と販売終了日を、無ければ無いことを確認したという意味で
onsale_label を空欄のまま次へ進む。追加でページを開く必要はないので、
「予算が無いからできなかった」は成り立たない。

ムビチケの販売終了日は多くが公開日前日だが、作品ごとに違うので必ず販売ページで確認する
(推測で公開日前日と書かない。従来どおり、確認できないなら空欄)。

再確認まで手が回らなかった tier C の行は、消さずに前回値のまま書き直してよい(prev_rows.py --uid で全列を引ける)。ただし onsale_* は空欄にする(ムビチケの販売終了日は公開日に向かって迫ってくるので、前回値の流用は禁じられている)。詳細は docs/COLLECTION-PROTOCOL.md 第8.5節。使った行数は報告に含めること。



並行して調べてよい —— ただし親が待ち切ること

都県や名簿を分割して同時に調べるのは、予算配分として正しい。縛るのは起動の形と、1回に背負う範囲である。

  • 背景で起動しない。 run_in_background: false を明示する
    (書かないと既定で背景起動になり、agent-guard.sh が拒否する)
  • 親は、全ての結果を append_rows.py で書き終えるまでターンを終えない
  • サブエージェントは調査のみ。data/ には書かない。結果は temp/rows-<波の名前>.jsonl に自分で書き、返答にはそのパスと件数だけを書かせる
    (行そのものを載せた返答は SubagentStop フック(reply_gate.py)が拒否し、書き直させる)
    (rows- で始まる名前にすること。この名前だけを下のゲートが見る。作業用のファイルは別の名前にする。理由は docs/COLLECTION-PROTOCOL.md 第11.4節)
  • 親はページを取得しない。 親の仕事は棚卸し・分割・起動・追記・検証だけで、fetch_page.py も WebSearch も子に任せる(第11.5節)
  • worktree 隔離を使わない(書き込まないので隔離する対象が無く、名簿の更新まで破棄される)
  • 分けるのは取得先が重ならない軸で(都県・名簿の種別)。「日程係」「料金係」に分けると同じページを2回開く
  • 新作カレンダーの確認(枠20回)・横断サイトでの野外上映等の補完(枠20回)は、名画座・単館の個別確認の波に混ぜない。 それぞれ独立した波として起動し、割り当てられた枠を使い切ってから次に進む。姉妹スキル(events)は2026-08-29 15:30の実行で、施設の個別再確認と名簿外発掘(エリア軸探索・横断サイト補完)を1つの波に混ぜ、名簿外発掘の枠を実質使い切れずに終えた
  • 1回の呼び出しに、調査軸1つの全体を背負わせない。 名簿の一部・対象期間の一部などに区切り、波——同時に起動して同時に受け取る1組——に分けて呼ぶ
  • 1つの波の結果を全部 append_rows.py で書き切ってから、次の波を投げる。
    python3 tools/append_rows.py movies < temp/rows-<波の名前>.jsonl
    書いていないと次の Agent 起動が拒否される(agent-guard.sh → wave_gate.py)。調べた行がCSVに入っているかを数えているので、追記の書き方(< でも cat | でも)は問わない。2026-08-27 の実行は、2つの波が90行を調べ終えてファイルに書きながら一度も追記せず、新規0件で差し戻された(あとで追記して流し直すと通る=調査は成功していた)
  • 波は subagent_type: "kanto-collector-worker" で起動する。 事前定義したエージェント(.claude/agents/kanto-collector-worker.md)で、maxTurns: 60 が入っている。general-purpose で投げると——subagent_type を書かないと既定でそうなる——この上限は一切効かない。指定の無い波は agent-guard.sh が拒否する
  • 1体が回るターン数は60でハーネスが打ち切る。 取得回数ではなくターン数で数えること——2026-08-27 の実測は取得202回に対しターン426回で、--help の読み直しや作業用スクリプトがその差である。担当範囲を小さく割れば下がる。budget.py --report が「最長の子◯ターン」を出す(第11.6節)。打ち切られるのは応答であってファイルではないので、子に「1件書けるたびに temp/rows-*.jsonl へ追記する」と守らせておけば、そこまでの成果は必ず残る(下の項目1)。2026-09-05 の events は1体が188ターン、2026-09-17 の movies は1体が167ターン回り、どちらも文脈再送を使い切って残りの工程を一度も実行できずに撤退した——目安として書いてあるだけでは守られなかったので、上限そのものを起動の形に移してある
  • 波を投げる前に python3 tools/budget.py --report を見る。 投げてしまうと、動いている子に割り込む手段が無い。「新しい波を投げないでください」が出ていたら、そこで探索を終えて終了工程へ進む
  • 1波の体数は自分で数えなくてよい。 CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS が上限を与える(このスキルは2体。3スキルで最も少ない)。超えた起動は Concurrent subagent limit reached で失敗する

このスキルだけ2体なのは、作品数ではなくホスト数の都合である。 theaters.csv は85館あるが、
チェーンの全店舗が同じホストに載っているのでホストは25しかない。さらにステップ1の新作カレンダーは
eiga.com 等の集約サイト数件に取得が集中する。体数を増やしても fetch_gate.py の待ち行列が伸びるだけで、
トークンだけが体数ぶん増える。このスキルで並列化が効くのは、16ホストに分かれている名画座・単館(ステップ2)である。

そのステップ2を、16会場まとめて1体に渡さない。 2体しか使えないぶん、ここを1体に寄せたくなるが、
2026-09-17 の実行はそれで167ターン・文脈再送33.6Mを1体に使い、2波目(ステップ3の野外・特別上映補完と
tier B の個別再確認)を一度も起動できないまま94件を機械的に持ち越して撤退している。
official_url の充足率は74%→57%に落ち、screening_type=outdoor は2件(下限3件)で終わった。

割り方は会場数ではなく、前回の tier A/B の件数で決める。 会場ごとの特集数のばらつきが大きく、
新文芸坐・シネマート新宿は特集の入れ替わりが多くて件数が膨らみやすい一方、
特集を年に数回しか組まない館もある。会場数で等分すると、膨らむ側の1体だけが構造的に長時間化する。

python3 tools/prev_rows.py movies --worklist --summary   # tier A/B の件数を見てから割る

1波の1体には、前回の tier A/B が合計15件を超えない範囲を渡す(超えるなら波を増やす)。
16会場ならおおむね1体4会場、4波になる。

この15という数字は maxTurns: 60 から逆算している。2026-09-17 の実測は
16会場・60行で167ターン=1行あたり2.78ターンと記録した。ただしこの167は、応答1回を
ブロックの数だけ重ねて数えていた budget.py の表示で、実際はおよそ半分である
(tools/budget.py
の _scan())。重複を除いた 2026-09-24 の実測では、13件前後を渡した波が47〜60ターンで
終わっており、15件はやはり60ターンに収まるぎりぎりの位置にある。記録どおりの係数を当てると

1波の tier A/B 推定ターン数 60ターンに対して
15件 42 余裕18ターン
20件 56 余裕4ターン
25件 70 超過10ターン

となり、25件では担当範囲を終える前に打ち切られる。20件でも余裕が4ターンしかなく、
会場の通信障害が1つあれば足りなくなる(2026-09-04 の lives は、DNS不通・SSL証明書不一致・
タイムアウトの連鎖だけで1体が141ターンまで膨らんでいる)。余裕を持って15件に置く。

打ち切られても成果は失われない(逐次追記のため)が、担当範囲の尾が次の波に回るぶん、
同じ仕事に余分な波が要る。最初から入る大きさに割っておくほうが安い。

events と単価が1桁違う。 events は正常に完走した回で1施設あたり1.0〜1.5ターン
(施設ページ1枚で複数行が確定する)だが、movies のステップ2は1会場あたり10.4ターン
——名画座は会場ページから特集ごとに深掘りが要るためである。同じ「1波の上限」を
両者で共有しない。

背景起動を禁じている理由——4体ぶんの調査結果が誰にも受け取られないまま破棄された実例——と、
前景でも1回の範囲が大きすぎると同じことが起きた実例(2026-08-20、movies)は
docs/COLLECTION-PROTOCOL.md 第11章にある。体数をホスト数から決めてある理由は第11.2節。
結果をファイルで受け渡す理由は第11.4節、親が取得しない理由は第11.5節、1体が背負う取得回数を区切る理由は第11.6節にある——3つとも、2026-08-26 に開始50分でアカウントの利用上限に当たった実行の内訳から来ている。

サブエージェントへの指示に必ず含めること(省略禁止)

2026-08-21 に姉妹スキル(events)をHaikuで実行した実測では、指示にこれらが無かったために15体中3体がJSONLを1件も返さず(うち1体は再要請しても直らず8件が回復不能のまま失われた)、会期が残っている前回行の大半を再発見できず、補助列の充足率が0%近くまで落ちた。次の5項目は、都度書くのではなく指示のテンプレートに固定で含めること。

  1. SKILL.md を読ませない。抜粋を「貼る」のではなく、ファイルで渡す。 波を投げる前に1回だけ python3 tools/skill_brief.py movies --out temp/brief-movies.md を実行し、指示には**「まず temp/brief-movies.md を Read し、そこに書かれた規則に従うこと」の1文だけ**を書く。抜粋のパスへの参照が無い波は .claude/hooks/agent-guard.sh が拒否する。 貼る形は親に「抜粋ぶんの出力トークン × 体数」を払わせ、払えないと判断した親は自作の要約に逃げる——2026-09-02 の events はそれで料金の列が指示から丸ごと落ち、新規90件の公式料金が0件になった(docs/COLLECTION-PROTOCOL.md 第11.8.1節)。読んだ瞬間に子の文脈が +23k トークン跳ねる(2026-08-27 の実測。3体とも同じ)。平均文脈 115k の2割で、196ターン回った子ではこの1ファイルだけで約4.5M になる。しかも文脈は毎ターン送り直されるので、体を分けてもこの分は減らない。抜粋は終了工程・撤退の手順・工程の割り振り・初期化(--init)・消えた行の説明だけを外し、行の書き方と取得の作法は丸ごと残す(docs/COLLECTION-PROTOCOL.md 第11.8節)。抜粋の先頭には「data/ に書かない・--init を実行しない・結果は temp/rows-*.jsonl に書く」という固定の注意書きが常に付く——下の1と同じ内容だが、指示にこれを書き忘れても子には届く二重の安全策である

  2. 「結果は temp/rows-<波の名前>.jsonl に1行1件のJSONLで書き、返答にはファイルのパスと件数だけを書け。行そのものを返答に含めるな」と明記する。あわせて「1件書けるたびに追記せよ(>>)。最後にまとめて書くな」も必ず書く——子は60ターンで打ち切られることがあり(maxTurns)、打ち切られるのは応答であってファイルではない。まとめ書きの設計だと、打ち切られた瞬間にその波の調査がまるごと失われる。逐次追記なら、そこまでが成果として残って親が追記できる。 2つのことを同時に防いでいる。ひとつは「調査した内容の要約」を返して機械可読な行が1件も戻らない失敗(実測で15体中3体がこれになった)。もうひとつは同じ行を3回払うこと——子が返答に書き(出力)、親の文脈に入り(入力)、親がヒアドキュメントで書き直す(出力)。2026-08-26 の lives 収集は88行でこれをやり、03:07:48 → 03:11:21 の3分半がほぼ純粋な出力生成に消えた。ファイル経由なら1回で済む

  3. 担当範囲の前回行を「ファイルで」渡す。 波を投げる前に担当範囲ごとに1回だけ実行する。

    python3 tools/prev_rows.py movies --worklist --pref <担当都県> > temp/worklist-movies-<担当都県>.md
    wc -l temp/worklist-movies-<担当都県>.md   # 件数だけ確認する(中身は開かない)

    指示には「temp/worklist-movies-<担当都県>.md を Read し、この一覧の行を再確認しながら新規を探すこと」と書く。出力を指示に貼らない——抜粋を貼らないのと同じ理由で、貼ると親が「その字数 × 体数」を出力トークンで払い、しかも同じ内容が親の文脈にも載る(実測: events の全県ぶんで28,922字)。渡すこと自体は省略しない。前回分の照合をサブエージェント任せにすると、実行時には素通りされる

  4. このスキルの中核工程を名指しする——前売り(onsale_*)の有無確認。「今週かならず産む成果」章にあるとおり、公開前の新作は前売りの有無を確認するのが下限の成果である

  5. 「開いたページに書いてあれば拾う」補助列を列挙する——kana note series_id announced_date is_additional。書かなければ0%のまま埋まらない

  6. 有効なキーの一覧と対象期間を明記する。 genre は anime jp-live foreign doc fam tokusatsu music の7キー、screening_type は new revival outdoor special festival の5キーのみ(それ以外は append_rows.py が拒否する)。対象地域は関東1都6県、対象期間は実行日から6ヶ月以内に公開・上映が始まるもの・または実行日時点で上映中のもの

  7. 中止・対象外になっていた前回行の書き先を渡す。 指示に「前回行が公開延期・中止・対象外だと確認できたら、rows には書かず temp/dispose-movies-<波の名前>.jsonl に {"uid", "status": "cancelled|out-of-scope", "note"} を1行ずつ追記すること」と書く。前回行はステップ0で既にCSVへ戻っているので、子が書かないだけでは消えない——この経路が無いと、公開延期になった作品が載り続ける。書き方の規則は子の定義(.claude/agents/kanto-collector-worker.md)にある

親の残りトークンが少ないときの追加指示(サブエージェントには意識させない)

タスク着手時点で、Claude Codeが示す残りトークン数(コンテキストの残量表示)を控えておく。これは親(このタスクを実行している自分自身)だけが気にすればよく、サブエージェントに「自分の残りトークンを確認しながら進めて」と指示してはならない——サブエージェント自身に残量を意識させると、調査そのものの遂行能力が落ちる。

以後、残りが着手時の10%を切ったら、その時点から新しく投げるサブエージェントの指示に次の一文を追加する。

担当範囲を最後までやり切れなくてよい。区切りのよいところ(1件・1都県などの単位)まで進めたら、そこまでの結果をJSONLで返してターンを終えてよい。

強制的に打ち切らせるものではない。 すでに動いているサブエージェントに割り込むのではなく、次に投げる指示にあらかじめ「早めに畳んでよい許可」を含めておくことで、キリの悪いところで打ち切られる事故を防ぐための措置である。

調査手順

差分検知・持ち越し・名簿更新の考え方は docs/COLLECTION-PROTOCOL.md に詳しい。手を動かすのに必要なコマンドはこのSKILL.mdに全部ある。

ステップ0:土台を作る(省略禁止・検索を1回も使わない)

調査を始める前に、CSVを「先週の完全な状態」に戻す。 --init はCSVを空にするので、
ここを通さないと、調査が届かなかった行はそのまま消える。

python3 tools/prev_rows.py movies --carry-rest --apply

終了日を過ぎた行は expired として処分され、残りは前回値のまま書き戻る(前売り欄は
空になる)。この1コマンドが「期間切れの削除」と「前回分の引き継ぎ」をまとめて行う。

なぜ最初に置くのか。 以前はこれを終了工程の側に置き、調べ終えてから残りを
書き戻していた。その順序では、予算が尽きた回に大量の行が未確認のまま最後に
なだれ込む——2026-09-17 の回は1波目だけで予算を使い切り、94件を機械的に持ち越して
撤退した。先に書き戻しておけば、調査はどこで打ち切っても「今週増やせたはずの分」
しか失わない。
同じ作品を再収集しても append_rows.py が uid で突き合わせて
更新するので、行が二重になることはない。

次に、重複が残っていないかを確かめる。

python3 tools/validate_data.py --duplicates-only

完全重複(同じ uid の行が複数)は、通常ここで0件のはずである。 直前の --carry-rest
が前回CSVの全行を1回のバッチで処理するとき、同じuidの行が複数あれば自動的に1行へ
畳んでいる
。畳まれた際に列が失われていれば、直前の出力に WARNING: ...は同じ回の中で 前の行(同じuid)を更新し、[...] が空になりました が出る。出ていたら、失われた側の値は
data/.prev/movies.csv を直接あたって拾い直す。

重複の候補(uid は違うが同じ劇場・同じ公開日・似たタイトル)は別企画のこともあるので、
中身を見て判断する——同じ上映だと分かったら片方を --dispose の merged で処分する。

ステップ0.5:前回分の棚卸し(省略禁止)

土台を作ったら、次に前回載せた上映の一覧を取る。

python3 tools/prev_rows.py movies --worklist

前回分の全行が、CSV全文より1桁小さい圧縮一覧で出る(CSV全文をそのまま読んではいけない)。全列が必要になった行だけ、あとから引く。

python3 tools/prev_rows.py movies --uid 3f2a1b9c
python3 tools/prev_rows.py movies --venue 新文芸坐
python3 tools/prev_rows.py movies --worklist --tier A   # 今回必ず再確認する行だけに絞る

一覧の先頭に「終了日を過ぎたまま未確認の行」が出る。今週いちばん最初に確かめるのはここである。

印 意味 やること
notfound 先週、調べたが分からなかった行 まだ開催中なら「静かな欠落」になっている。 会場ページで確かめ直す
expired 終了日を過ぎて機械的に消えた行(未確認) 会期が延長されていた可能性がある。 その会場を開くついでに見る

これらの行は前回のCSVにはもう残っていない。この一覧に出さない限り、二度と視界に入らない。
拾い直せたら普通に行として書けばよく、本当に終わっていたなら何もしなくてよい。

映画は tier A(今回必ず再確認)の比率が高い。新作は公開日が動き、前売りは公開日で締まり、名画座の特集は1〜2週で入れ替わるためである。

tier の判定は3スキル共通で、tools/prev_rows.py が機械的に付ける。

tier 意味
A 今回必ず再確認する(公開日・終了日が10日以内/前売りの締切・発売が21日以内/onsale_label があるのに締切が空 等)
B 劇場ページを開いたついでに確認する(価格の確認日が45日より古い行もここ)
C 低頻度でよい(日程を持たない行)

tier は「手を抜いてよい行」を決めるためのものではなく、限られたページ取得の回数を
変わりやすい行から先に使うための優先順位である
(docs/COLLECTION-PROTOCOL.md 第3.1節)。

この工程で確認するのは、次の4つ。

確認すること 見落とすとどうなるか
まだ上映しているか 終わった作品が「上映中」として残る。映画は終映日が告知されないことが多く、静かに終わる
上映が延長されたか(is_additional) アンコール上映・ロングラン継続を拾えない
前売りが終わっていないか 買えない前売りを「発売中」として見せる
上映館が増減していないか 拡大公開・縮小をカードに反映できない

新作行の theater(チェーン名)が変わっても、同じ作品として扱われる。 差分の同定に新作の会場は使っていないため、「翌週に109シネマズが加わった」場合は [消滅]+[新規] ではなく [変更] として出る。

ステップ1:新作ロードショーの網羅調査(最重要・省略禁止)

新作(screening_type=new)は「取りこぼしなく全件拾う」ことを最優先とする。 個別のシネコンを1館ずつ検索して見つかったものだけを載せる、というやり方は取りこぼしの原因になるため行わない。必ず公開予定を一覧できるカレンダー・リストのページを起点にすること。

  1. 公開予定カレンダーを月ごとに確認する。 実行日を含む月から6ヶ月後までの各月について、以下のようなページを開き、掲載されている作品を全部書き出す(大作・話題作に限らず、小規模公開の作品も含めて拾う)。
    {実行年}年{実行月}月 公開映画 一覧
    {実行年}年{実行月+1}月 公開映画 一覧
    {実行年}年{実行月+2}月 公開映画 一覧
    {実行年}年{実行月+3}月 公開映画 一覧
    eiga.com・映画.com・シネマトゥデイ・Yahoo!映画などの「公開予定」「新作映画」一覧ページ、Wikipediaの「{年}年の映画」記事なども横断的に使う。
  2. 主要配給会社ごとの公開予定ページも確認する。 カレンダー系サイトは掲載漏れや配給会社発表前の情報を拾えないことがあるため、東宝・東映・松竹・ワーナー ブラザース ジャパン・ディズニー(日本語)・ソニー・ピクチャーズ・ユニバーサル・ギャガ・KADOKAWA・アスミック・エース・ロングライド・ショウゲート・松竹ODS等の「公開予定作品」ページを確認する。
  3. 1で拾った作品ごとに、公開日・上映しているシネコンチェーン(theater列の書き方は前章の表に従う)・料金を個別に確認する。1作品あたりの検索は原則1回とし、3回以上使わない(「『新作は全件』の現実的な線引き」参照)。上映劇場を特定できない単館作品は、その1回で分からなければ落とす。
  4. 話題作・大作を取りこぼしていないか最後に必ずセルフチェックする。 実行時点で世間的に注目されている大作・続編・話題のアニメ映画等が対象期間内にあるにもかかわらず一覧に入っていない、という状態が起きていないかを確認すること。「目標件数に達したから打ち切る」という理由で新作の掘り起こしを止めない。 新作は目標件数の上限にかかわらず、対象期間内に公開されるとわかっているものは可能な限り全件載せる。

この工程は単価が安い。 1ページのカレンダーから何十件も取れるので、枠20回のうち大半は1・2(一覧ページと配給会社ページ)に使い、3の個別確認はステップ4の枠で行う。逆に、シネコンの店舗を1館ずつ当たるのは最も単価が高く、しかも theater にはチェーン名しか書かないので得るものがない。

参考:主要シネコンチェーン(関東主要館の例):
TOHOシネマズ(日比谷・新宿・六本木ヒルズ・池袋等) / イオンシネマ(市原・幕張新都心等) / ユナイテッド・シネマ(としまえん・豊洲等) / 109シネマズ(プレミアム新宿・グランベリーパーク等) / MOVIX(さいたま等) / T・ジョイ(PRINCE品川等) / 川崎チネチッタ / 横浜ブルク13 / 新宿ピカデリー / 丸の内ピカデリー / グランドシネマサンシャイン池袋 / 立川シネマシティ

ステップ2:名画座・ミニシアターの特集上映調査

定点観測する劇場は data/theaters.csv にある。

python3 tools/roster.py theaters --list --pref tokyo --urls
python3 tools/roster.py theaters --list --urls | awk -F'\t' '$2==""'   # 名画座・単館だけ

4列目に劇場の公式サイトURLが出る(全件埋まっている)。この工程では、そのURLを直接開く(検索しない)。検索の枠は5回。

回るのは名画座・単館(2列目の chain が空欄の16件)だけでよい。 シネコン店舗は新作の上映館であって、独自の特集上映を組む先ではない。新作は theater にチェーン名を書く仕様なので、店舗ごとに調べても書ける情報が増えない。

1館につき1回、上映スケジュールを調べる。そのとき「前回分の照合」と「新規の発見」を同時に済ませる。 名画座の特集は1〜2週で入れ替わるので、前回の行はほぼ入れ替わっているはずだが、入れ替わったことを確認せずに落としてはいけない(延長されている場合がある)。

{劇場名} 上映スケジュール {実行年}年{実行月}月
{劇場名} 特集上映

収穫があった劇場、閉館した劇場、新しく見つけた劇場は、その場で名簿に反映する。ミニシアターは閉館・開館が多い。

python3 tools/roster.py theaters --retire ○○シネマ --reason "2026年5月閉館"
python3 tools/roster.py theaters --block ○○シネマ --reason "robots.txt が拒否"  # 閉館ではない
python3 tools/roster.py theaters --add <<'EOF'
{"chain": "", "name": "○○座", "pref": "tokyo", "area": "東京都・中野区",
 "lat": "35.70", "lng": "139.66", "url": "https://..."}
EOF

chain はシネコンチェーンの店舗なら親チェーン名、名画座・単館なら空欄にする(theaters.csv はチェーンの店舗ディレクトリと名画座の名簿を兼ねている)。収集元のページの文言を note にそのまま持ち込まないこと(roster.py が指示文めいた文字列を拒否するが、そもそも入れない)。

ステップ3:野外上映・ドライブインシアター・特別上映の補完調査

施設リストに載らない単発の上映イベントを、季節キーワードと横断サイトで補完する。

関東 野外上映 {実行年}年{実行月}月
ドライブインシアター 関東 {実行年}
{映画タイトル} 爆音上映
{映画タイトル} オールナイト上映
映画祭 関東 {実行年}

補完用の横断サイト:eiga.com / 映画.com / Filmarks / シネマトゥデイ / ぴあ映画生活 / Yahoo!映画

ステップ4:各作品・上映の深掘り

この工程の枠は80回(「調査予算」参照)。枠を使ってよいのは新規行だけである。前回から続く行は、前回の official_url を直接開けば検索は要らない。

拾った作品・上映1件ごとに、公式サイトや劇場公式サイトまで辿って以下を確定させる。まとめサイトの記載だけで済ませない(日付・料金が古いことがある)。

  1. 正確な公開日・上映期間
  2. 何が目玉か — あらすじではなく、特集の切り口・上映形態の特徴(二本立て、爆音、日時指定制など)
  3. 料金 — 券種ごとの正確な金額
  4. 公式サイトURL・劇場URL

1〜4は、原則として作品公式サイト1回の取得で揃う。 項目ごとにページを開き直さないこと。

深掘りを打ち切る基準

必須列(title genre screening_type area theater pref release_date/end_date price source url desc)が揃った時点で、その行の調査を終える。

  • 料金は「一般◯◯円」が確認できれば十分である。割引の適用条件を特定するために検索を重ねない——条件を書けない割引は価格ごと落とすのがこのスキルの規則なので、掘っても書けない
  • 新作の theater はチェーン名でよい。どの店舗で何時から上映しているかは、このCSVに書く列が無い

前回から続いている上映については、変わりうる項目(上映継続の有無・上映館・前売りの締切)だけを確認すればよい。目玉・座標を毎回調べ直す必要はない(_carry で持ち越す)。

前回行は着手の時点で既にCSVへ書き戻されている。 書かないだけでは消えないので、確認の結果は次のように返す。

  • 上映が終わっていた(新作で終映が分かった) → 分かった最終上映日を end_date に入れて、その行を書く。日付が過ぎていれば終了工程が機械的に片付ける。ended は使えない——end_date が空の行や先の行の ended は --dispose が拒否する
  • 公開延期・中止・対象外になっていた → rows には書かず、指示にある temp/dispose-movies-<波の名前>.jsonl に {"uid", "status": "cancelled|out-of-scope", "note"} を書く
  • 確認できなかった → 何も書かない(親が判断する)

ステップ4.5:いま載っている行の欠けを埋める

新規の発見が一巡したら、残りの予算はCSV全体の欠けに回す。 ステップ4の深掘りは
新規行を対象にしているので、前回から続く行の空欄はそのままでは埋まらない。

python3 tools/report_stats.py movies --json

中核列(price official_url onsale_* desc 等)の充足率が出る。低い列から順に、
劇場・作品の公式ページ単位で埋めていく——名画座の特集ページや作品の公式サイトを
1枚開けば、同じ劇場・同じ作品の行がまとめて埋まる。

python3 tools/prev_rows.py movies --venue 新文芸坐   # その劇場の行をまとめて引く

この工程は予算が尽きたらそこで畳んでよい。埋まらなかった列は報告に件数を書く。
ステップ0で土台が出来ているので、ここを飛ばしてもCSVが壊れることはない。

ステップ5:消えた行の説明と、差分の確定

まず、終了日を過ぎた行を機械的に片付ける。ステップ0の --carry-rest --apply が既に同じ
処分をしているので通常は0件で通るが、調査中に書いた行(ステップ4で最終上映日を入れた
新作など)が終了日を過ぎている場合のために残してある。

python3 tools/purge_ended.py movies

検索を使わない機械的な処理で、消した行には自動で expired の処分が付く(end_date が未定のまま上映中の新作は対象外——上映終了は劇場の判断で決まるので、機械には判定できない)。

次に、子が「公開延期・中止になっていた」「対象外になっていた」と報告した行を取り除く。

cat temp/dispose-movies-*.jsonl | python3 tools/prev_rows.py movies --dispose

前回行はステップ0で全部CSVへ書き戻してあるので、子がその行を書かなかっただけでは
消えない——下の [消滅] にも出てこない(CSVに残っているため)。子の報告を通さない限り、
公開延期になった作品が載り続ける。
通すのは今回の波が書いたファイルだけ。1行でも規則に
合わない行(前回データに無い uid・ended)があると全体がエラーで止まるので、その行を
直すか外してからやり直す。

次に、差分を一覧して表記が変わっただけの二重掲載が無いかを確かめる。

この工程は、工程順を変えたことでむしろ重要になった。 ステップ0で前回行を全部
書き戻しているので、邦題や副題の表記が変わると「旧uidの持ち越し行」と「新uidの新規行」が
両方ともCSVに残る。放っておくと同じ作品が2つ並んで公開される。renamed として
処分すれば、--dispose が旧uidの行をCSVから取り除く。

python3 tools/diff_data.py movies --allow-unexplained --allow-noop

この出力を head / tail / grep で切ってはいけない。 [新規] の一覧が長いと見出しだけで数十行を使うため、その直後に出る [表記が変わった可能性](下記)が画面外に落ちる。姉妹スキル(events)の2026-08-29 15:30の実行はこの確認を| head -20に通し、[表記が変わった可能性]を一度も見ないまま--carry-rest --applyを全件に適用して、会場名や副題の表記が変わっただけの作品を旧行・新行の両方として二重掲載した。

処分が要る行は、必ず次のどれかである。

  1. 公開が延期・中止になった → cancelled として記録する。ended は end_date を過ぎた行にしか使えない——新作で上映終了が分かったなら、ステップ4のとおり最終上映日を end_date に入れて書き直す
  2. 今回は確認できなかった → notfound として記録する。行は削除されない——--dispose が前回値のまま自動で書き戻し、来週 tier A で最優先の再確認対象になる。確認できていない以上、上映終了の根拠にはならない
  3. 表記が変わっただけで、実は同じ作品が [新規] 側にも出ている → renamed として記録する(二重掲載事故。[表記が変わった可能性] に挙がる)

--dispose は記録するだけでなく、notfound 以外の行をCSVから取り除く——ステップ0で
全行を書き戻している以上、記録だけでは延期になった作品が公開され続けるためである。

映画は終映日が告知されないまま静かに終わることが多いので、「見当たらないから終わったのだろう」という推測が特に通りやすい。だからこそ確認できたものだけを処分し、確認できなかったものは notfound にとどめる。

python3 tools/prev_rows.py movies --dispose <<'EOF'
{"uid": "8d7e6f50", "status": "cancelled",    "note": "公開延期(配給発表)"}
{"uid": "a1b2c3d4", "status": "out-of-scope", "note": "公開が6ヶ月より先に延びた"}
{"uid": "c1d2e3f4", "status": "renamed",      "to": "e5f6a7b8", "note": "邦題の表記が変わった"}
{"uid": "b2c3d4e5", "status": "notfound",     "note": "上映館を特定できず"}
EOF

「表記が変わった可能性」として挙がったペアは、同じ作品なら renamed を記録する。放置すると、同じ作品が旧行・新行の2つとして公開され、NEWバッジも誤って付く。

最後に名簿を整理し、差分と検証を確定させる。

python3 tools/roster.py theaters --gc
python3 tools/diff_data.py movies       # 説明漏れがあると落ちる
python3 tools/validate_data.py          # ERROR 0 を確認

diff_data.py の出力が、そのまま報告の「前回からの変化」になる。記憶や印象で変化を書かないこと。


画像は扱わない(このタスクで特に注意すること)

data/movies.csv に画像の列は存在しない。ポスター・スチール・キービジュアルのURLを、どの列にも書いてはならない。

3タブとも画像を扱わない。権利者が出しているのは自社サイトへ掲載するための許諾であって、第三者サイトへの再掲載や画像への直リンクの許諾ではないためである。画像ファイルを複製せずURL参照するだけでも、サイトの利用規約違反と著作者人格権(氏名表示権)の問題は残る。判断の詳細は docs/DESIGN.md 第7.1節。

このルールは tools/validate_data.py が検証する。 poster_url / poster_source の列を復活させた時点で ERROR になり、週次ルーチンのコミットが止まる。作品情報を読みに開いたページのOGP画像を拾うのは常に最も安い経路であり、散文のお願いは安い経路の前では負けるので、仕組みで担保してある(第7.1節)。

画像を探す工程そのものが無いので、そのぶんの調査予算は他に使ってよい。 ステップ4で作品公式サイトを開いたときも、OGP画像やキービジュアルを見に行かないこと。


desc の書き方

100〜150字程度で、その上映の何が特別なのかを具体的に書く。

悪い例(一般論・中身なし)

❌ 話題の新作映画です。ぜひご覧ください。
❌ 名作を大きなスクリーンで楽しめます。

良い例(目玉が具体的)

✅ 一週間限定の二本立て特集で、監督デビュー作と最新作を続けて上映。トークイベントは初日のみ、要事前予約。
✅ 屋外駐車場を使ったドライブインシアター企画。車内から鑑賞するため冷房いらずで、荒天時は中止・順延の判断が当日17時に告知される。

含めるべき要素

  • 目玉(特集の切り口、上映形態の特徴、規模感)
  • 注意事項(日時指定制、事前予約必須、雨天中止など)

含めないもの

  • 配給会社の宣伝文句をそのまま転記したもの
  • 「必見」「感動」といった評価語
  • 出典サイトの文章のコピー(必ず自分の言葉で書き直す)

品質チェック(提出前に必ず実施)

完走

  • 「完走条件」の終了工程8つを、すべて実行したか
  • 調査済みなのに append_rows.py で書いていない行を残していないか
  • 打ち切った場合、前回行を「持ち越し」または notfound として片付けたか(黙って消していないか)
  • 工程の区切りごとに [進捗] の1行を出したか
  • data/ の該当CSVが、ヘッダーだけの状態で終わっていないか
  • python3 tools/diff_data.py movies が「収穫0件」で落ちていないか(新規・変更・消滅がすべて0の週は、調査が実質的に走っていない)
  • サブエージェントを使ったなら、背景で起動せず、全ての結果を append_rows.py で書き終えてからターンを終えたか
  • サブエージェント1回の呼び出しに、調査軸1つの全体を背負わせていないか。1つの波の結果を全部書き切ってから、次の波を投げたか(波をまたいで投げ続けていないか)
  • サブエージェントの結果を temp/*.jsonl で受け取ったか(行そのものを返答に含めさせていないか。第11.4節)
  • 親が自分でページを取得していないか(budget.py --report の文脈再送が親>子になっていたら逸脱。第11.5節)
  • (親だけが行う工程) 着手の最初に python3 tools/prev_rows.py movies --carry-rest --apply を通したか(ステップ0。これを飛ばすと、調査が届かなかった行がまるごと消える)
  • (親だけが行う工程) 子が書いた temp/dispose-movies-*.jsonl を --dispose に通したか(通さないと、公開延期・中止になった作品が載り続ける)
  • 先週の notfound / expired の行(棚卸しの先頭に出る)を確認したか

予算

  • 名画座・単館を --urls のURLで直接開いたか(URLが分かっている劇場を検索し直していないか)
  • シネコンの店舗を1館ずつ調べていないか(theater はチェーン名なので、店舗ごとの確認は書ける情報を増やさない)
  • 前回から続く行を、前回の official_url に直行して確認したか
  • lat/lng を空欄で渡したか(theaters.csv にある劇場を調べ直していないか)
  • 上映劇場を特定できない単館作品に、3回以上の検索を使っていないか
  • 割引の適用条件を特定するために検索を重ねていないか(条件を書けない割引は価格ごと落とす)
  • 補助列(kana series_id 等)を埋めるために追加の検索をしていないか
  • python3 tools/budget.py --report --verbose で工程別の消費を確認し、報告に含めたか(記憶で書いていないか)
  • 撤退を判断する前に、残り時間を確認したか(上限6時間。まだ余っているのに早く畳んでいないか)
  • 一覧の構造が意味を持つページを、fetch_page.py --text ではなく WebFetch で読んで崩していないか
  • 予算切れで工程を打ち切った場合、その事実を報告に書いたか(件数を埋めるために未検証の行を足していないか)
  • 再確認できずに前回値で持ち越した tier C の行について、onsale_* を空欄にしたか

差分・名簿

  • python3 tools/purge_ended.py movies を、--dispose より先に実行したか
  • python3 tools/diff_data.py movies が終了コード0で通るか(説明のない消滅が0件か)
  • 「消滅」の各行に付けた理由は、実際に劇場のスケジュールを確認した結果か(「消えたから終わったのだろう」で ended にしていないか。expired は purge_ended.py が自動で付けるものであり、モデルが手で選ぶ理由ではない)
  • 「表記が変わった可能性」に挙がったペアを確認し、同じ作品なら renamed を記録したか
  • 上映延長・アンコール上映を is_additional=1 として拾えているか(差分の [変更] で end_date が伸びた行)
  • _carry: "*" を使った行は、内容に変更がないことを実際に確認した行だけか
  • append_rows.py が挙げた「名簿に無い会場」を確認し、常設の劇場を --add したか(収穫の記録そのものは自動)
  • roster.py theaters --gc を実行したか
  • 閉館したミニシアター・新しく見つけた劇場を名簿に反映したか
  • python3 tools/report_stats.py movies を実行し、中核列の充足率が前回より落ちていないか確認したか(落ちていれば理由を報告に書く。原因が目標件数・品質基準・手順の側にあるなら、報告に書くだけで終えず docs/skill-feedback.md にも提案を残したか——書かないと、同じ理由で毎週落ち続けても誰も気づけない)
  • 「今週かならず産む成果」の下限に届いたか。届かなかった項目を報告に書いたか
  • 目標件数・品質基準・禁止事項に触れるルール変更に気づいたら、自分で書き換えず docs/skill-feedback.md に書いたか(それ以外の小さなバグ修正は自分で直してよい。出力先の章を参照)

形式・内容

  • 全行の列数が揃っているか(python3 tools/validate_data.py で確認。ERRORは0件必須。列数をこのチェックリストに書き写さないこと——列は増えるので、書き写した数字は必ず古くなる)
  • price_best / discount_pct を書いた行すべてに price_condition と best_source と price_checked があるか
  • 条件を特定できない割引を書いていないか(書けないなら価格ごと落とす)
  • onsale_* を書いた行で、時刻を推測で補っていないか
  • series_id を付けた行が2件以上あるか(1件だけのシリーズは付けない)
  • id が1から連番になっているか
  • release_date / end_date が YYYY-MM-DD 形式か、または空欄か
  • 上映が実行日より前に終了している作品が混ざっていないか
  • 公開・上映開始が実行日の6ヶ月後より後の作品が混ざっていないか
  • pref が7県のいずれかになっているか
  • genre が定義済み7キーのみか
  • screening_type が定義済み5キーのみか
  • status / rank が空欄か(日付を持つ行では表示側が毎回計算するので、書いても無視される。validate_data.py が「無視される値が入っている」を WARNING で挙げる)
  • url が実在するページか(リンク切れがないか)
  • 画像の列(poster_url / poster_source)を復活させていないか(validate_data.py が ERROR で落とす)
  • 確認できていない料金・日付を推測で埋めた行が1件もないか
  • lat/lng を、名簿にも前回にも無い会場(野外上映・ドライブインシアターの臨時会場など)についてできる限り埋めたか(theaters.csv にある劇場は自動補完されるので、調べ直さないのが正しい)
  • desc が一般論で終わっていないか
  • revival・outdoor・special・festival すべてから3件以上あるか(new には下限を設けない)
  • 7都県すべてから2件以上あるか
  • 東京の件数が全体の半分以下か
  • theater 列が上映形態ごとの書き方に従っているか(new はチェーン名、単館限定公開の場合を除いて特定の1館名を書いていないか。revival/outdoor/special/festival は具体的な会場名になっているか)
  • new 行のチェーン名が theaters.csv の chain 列の表記と完全に一致しているか(表記ゆれ・省略形がないか。複数チェーンは \| 区切りになっているか)
  • new 行でも area/lat/lng が空欄になっていないか(表示はされないが地図検索に必要)
  • 公開予定カレンダーを対象期間の全月について確認し、話題作・大作を取りこぼしていないか最終セルフチェックしたか(目標件数に達したことを理由に新作の掘り起こしを打ち切っていないか)

実行手順まとめ

手順11〜16は終了工程である(終了工程表の1「調査済みの行を書き切る」は、手順9の追記をやり切ることに当たる)。調査を途中で打ち切る場合も、ここは必ず全部通すこと(「完走条件」参照)。終了工程はローカルのコマンドだけで完結するので、検索の予算が尽きていても実行できる。

手順2〜4は検索を1回も使わない土台作りである。ここを通した時点でCSVは先週の完全な状態に戻り、以降の調査はすべて上積みになる。

  1. python3 tools/check_robots.py と python3 tools/fetch_gate.py --roster theaters を実行し、調査元サイトと名簿URLの robots.txt を確認する(検索予算は消費しない。拒否されたURLは対象から外す。個々のページの可否は、取得のたびにフックが自動で判定する)。あわせて「調査予算」の工程別の枠を確認し、python3 tools/budget.py --report で今回の消費と**残り時間(上限6時間)**を見る(このスキルは特に上限に当たりやすい)
  2. 実行日を確認し、対象期間(実行日 〜 6ヶ月後)を確定する。サブエージェントを使うなら、ここで python3 tools/skill_brief.py movies --out temp/brief-movies.md を1回だけ実行しておく(波の指示にはこのパスを Read させる1文だけを書く。抜粋を貼らない)
  3. python3 tools/append_rows.py movies --init(前回分は data/.prev/ に自動退避される。先に --init してから棚卸しを取ること——先週の carried.jsonl が今回分に更新されるのは --init の中でなので、順序を逆にすると「終了日を過ぎたまま未確認の行」が1週遅れて表示される)
  4. python3 tools/prev_rows.py movies --carry-rest --apply で土台を作る(省略禁止)。 終了日を過ぎた行は expired として処分され、残りは前回値のまま書き戻る(前売り欄は空になる)。この1コマンドが「期間切れの削除」と「前回分の引き継ぎ」をまとめて行う。 先に通しておけば、調査はどこで打ち切っても「今週増やせたはずの分」しか失わない
  5. python3 tools/validate_data.py --duplicates-only で重複を確かめ、解消する。 完全重複(同じ uid の行が複数)は手順3のバッチが自動で1行に畳むので通常0件。重複の候補(uid は違う)は中身を見て判断し、同じ上映なら片方を --dispose の merged で処分する
  6. python3 tools/prev_rows.py movies --worklist --summary で前回分の棚卸しを取る(省略禁止)。--summary を付けること——最優先の行(終了日を過ぎたまま未確認)と都県ごとの件数だけが出て、数百行の一覧そのものは出ない。一覧は親の文脈に置くと毎ターン再送されるので(events で28,922字)、担当分は「並行して調べてよい」章のとおり --pref <都県> > temp/worklist-movies-<都県>.md でファイルに書いて子に渡す。個別の行が要るときは --uid <uid> で引く。先頭に出る「終了日を過ぎたまま未確認の行」を最初に片付ける。終了日を過ぎた行はこの一覧に出ない——日付を比べるだけで確定するので、調査の枠も文脈もそこに使わない(末尾に除いた件数が出る。この除外が、今週の調査対象から終了済みの行を実質的に外す工程にあたる。実際の削除は手順3の --carry-rest --apply が済ませている)
  7. 公開予定カレンダー・配給会社の公開予定ページを対象期間の全月について確認し、新作ロードショーを取りこぼしなく拾う(枠20回・ステップ1参照。個別のシネコン検索だけに頼らない/シネコン店舗を1館ずつ当たらない)
  8. python3 tools/roster.py theaters --list --urls で名画座・単館(2列目が空欄の16件)を引き、4列目のURLを直接開いて「既存行の再確認」と「新規の発見」を同じ1回の調査で済ませる(検索の枠5回)。上映スケジュールは python3 tools/fetch_page.py <URL> --schedule で取る——日付と作品名の対応を保ったまま、本文が3〜4割に減る(欲しい作品が出ていなければ --text に戻す)。収穫の記録は append_rows.py が自動で行う
  9. 季節キーワードと横断サイトで、野外上映・ドライブインシアター・特別上映・映画祭を補完する(枠20回)
  10. 各作品・上映について公式情報まで辿り、期間・目玉・料金を確定する(枠80回・新規行のみ。前回から続く行は前回の official_url に直行し、変わりうる項目だけを確認して残りは _carry で持ち越す)。theater 列は上映形態ごとの書き方(新作はチェーン名、それ以外は具体的な会場名)に従う。必須列が揃った行はそこで打ち切る。上映劇場を特定できない単館作品は、1回調べて分からなければ落とす 前回行は手順3で既にCSVに戻っているので、新作で上映終了が分かった行は最終上映日を end_date に入れて書き直し、公開延期・中止・対象外になっていた行は子に temp/dispose-movies-<波の名前>.jsonl へ書かせる(「サブエージェントへの指示に必ず含めること」6)
  11. python3 tools/report_stats.py movies --json で中核列の充足率を見て、低い列を劇場・作品の公式ページ単位で埋める(残りの予算を全部ここに使ってよい)。予算が尽きたらそこで畳んでよい——土台は既に出来ているので、飛ばしてもCSVは壊れない
  12. python3 tools/purge_ended.py movies(手順3で同じ処分が済んでいるので通常は0件。調査中に最終上映日を入れた行などの保険。end_date が未定の新作は対象外)
  13. 子が書いた temp/dispose-movies-*.jsonl を python3 tools/prev_rows.py movies --dispose に通し(公開延期・中止・対象外の行を取り除く)、python3 tools/diff_data.py movies --allow-unexplained --allow-noop で差分を一覧して [表記が変わった可能性] を確かめる(出力を切らない)。同じ作品だと分かった組は renamed として記録する。手順3で前回行を全部書き戻しているので、表記が変わった作品は「旧uidの持ち越し行」と「新uidの新規行」の両方がCSVに残る——renamed を記録すると --dispose が旧uidの行を取り除く(notfound だけは行を残す)
  14. python3 tools/roster.py theaters --gc で名簿を整理する
  15. python3 tools/diff_data.py movies と python3 tools/validate_data.py を実行し、どちらも終了コード0であることを確認する(diff_data.py は収穫が0件でも落ちる)
  16. python3 tools/report_stats.py movies --check で充足率と分布を確認する(とくに今週あらたに書いた行の price と desc の下限。持ち越した行の値は前回値であって、今週確認した値ではない。落ちたらまず実際に調べ直し、どうしても確認できない行ばかりだったなら --allow-thin <列名> で承知したことにして理由を報告に書く)
  17. 最後に、以下の要点だけを簡潔に出力する(あいさつや前置き、次の提案などの会話的な文章は付けない)
  • 件数・都県別内訳・上映形態別内訳・ジャンル別内訳(report_stats.py の出力をそのまま使う)
  • 中核列の充足率と前回比(とくに前売りの onsale_label / onsale_end。落ちた列があれば理由も)
  • 前回からの変化(diff_data.py の出力をそのまま使う。記憶で書かない)
    • 新規公開/上映延長・アンコール上映/上映終了・公開延期
    • --carry-rest(手順3)と purge_ended.py が expired として処分した件数(多いなら、tier A の再確認が追いついていない兆候として書く)
    • 子の報告で取り除いた件数(temp/dispose-movies-*.jsonl。cancelled / out-of-scope の別)
    • notfound として前回値のまま持ち越した件数(0でないなら、その一覧も。行は削除しない。来週 tier A で再確認する)
  • 名簿の変化(追加した劇場/retired にした劇場/--gc の結果)
  • 工程別の消費と、予算切れで打ち切った工程(budget.py --report --verbose の出力をそのまま貼る。打ち切りが無ければ「なし」と書く)
  • 「今週かならず産む成果」の下限に届いたか(届かなかった項目は件数と理由)
  • 上映劇場を特定できずに落とした作品の件数(「新作は全件」の線引きで落ちた分)
  • 再確認できず前回値のまま残った行数(手順3で書き戻したまま、どの工程でも触らなかった行)
  • 手順10で埋めた欠けの件数と、埋められなかった列
  • docs/skill-feedback.md に書いた提案があれば、その要旨