the-ready

kanto-live-collector

"Collect live concert and music festival data for the Kanto region and its five adjacent prefectures (Yamanashi, Shizuoka, Nagano, Niigata, Fukushima) and produce data/lives.csv for the 「イベントボード」 dashboard's ライブ・フェス tab. Use when the user asks to update, refresh, or regenerate the live/festival dashboard data, run the weekly live collection, or rebuild lives.csv. Also use when they invoke this by name or reference the weekly live-gathering routine. Walks the venue roster in data/venues.csv to re-verify every previously collected performance (still scheduled? cancelled? on-sale window advanced? extra seats released?) while discovering new ones in the same pass, tracks festival lineup announcements from data/festivals.csv, detects newly announced and additional (追加) performances by machine-diffing against the previous CSV, records ticket on-sale windows and application deadlines alongside the official ticket link, and grows the venue and festival rosters itself as new places and events are found."

the-ready 1 Actualitzat fa 3 setm.
GitHub

Instal·la

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

Instal·la des del registre de SkillsCat.

SKILL.md

関東ライブ・フェス収集タスク(週次)

このタスクの目的

data/lives.csv を更新し、ダッシュボード「イベントボード」のライブ・フェスタブに、利用者がこれから実際に行ける公演を並べること。

このタスクが答えるべき問いは2つある。

  1. これから関東で何のライブ・フェスがあるのか(日時・会場・出演者)
  2. それは今からでも間に合うのか(チケットの受付状況と締切)

2つ目を落とさないことが、このタスクの成否を分ける。ライブは「公演日が先だから、まだ余裕がある」が成立しない。公演は11月でも、先行受付の締切は今週の日曜、ということが常態である。 公演日だけを集めた一覧は、利用者に「まだ間に合う」と誤解させる点で、無いよりも有害になりうる。

そしてもう1つ、このタスクに固有の任務がある。

  1. 今週あらたに発表された公演・追加公演を検知すること

追加公演は、一度売り切れた公演にもう一度席が出る機会であり、このダッシュボードで最も価値の高い情報である。発表された週に拾わなければ、次の週にはもう受付が終わっている。


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

このタスクは 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 venues   # 名簿のURL(この工程の主な取得先)

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

使えるサイトを見つけたときは、data/sources.json に足す

sources.json は減る一方になりやすい。 上の2(拒否の意思が示されたら外す)は明文化されているのに、足す側の手順がどこにも無かったため、運用が始まってから一度も更新されていない期間がある。一覧は画面の「調べたサイト一覧」にもなるので、実際の調査先とずれると利用者への説明が古くなる。

今週の調査で継続的に使えると判断したサイト(毎回見に行く価値があり、robots.txt が許可していて、利用規約が自動収集を禁じていないもの)は、live 配列に name / url / role を足したうえで check_robots.py を通し直す。1回だけ使った個別の公演ページや会場サイトは足さない——ここに載せるのは「毎回の起点」だけである。

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

# やること コマンド
1 手元に残っている調査済みの行を書き切る python3 tools/append_rows.py lives
2 終了日を過ぎた行を機械的に取り除く(通常0件) python3 tools/purge_ended.py lives
3 ラインナップの Apple Music リンクを埋める python3 tools/fill_apple_music.py
4 中止・改名した行に理由を付けて取り除く python3 tools/prev_rows.py lives --dispose
5 名簿を整理する python3 tools/roster.py venues --gc と python3 tools/roster.py festivals --gc
6 差分を確定する(終了コード0) python3 tools/diff_data.py lives
7 検証する(ERROR 0) python3 tools/validate_data.py
8 充足率・都県の網羅性・フェス名簿との整合を確認する python3 tools/report_stats.py lives --check と python3 tools/festival_gate.py
9 報告を出力する ——

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

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

2 は「まだ確認できていない行」の保険であって、1・4の代わりではない。 ライブは受付が日々進むため tier A の比率が高く、それだけ検索予算切れで再確認が間に合わない行も出やすい。公演日を過ぎたのに気づかれず持ち越された行は、表示側が公演日から毎回「終了」を計算して既定の一覧から隠す(設計書 第5.4.1節)ので利用者の目には触れないが、CSVの中には残り続ける。purge_ended.py は「終了日(end_date)が今日より前か」という一点だけを機械的に見て、そういう行をここで刈り取る。消した行には expired の処分が自動で付くので、4 で個別に扱う必要はない。確認できた行を ended として --dispose するほうが、この自動処理より常に望ましい。 また、lineup_id を持つ行を消した場合は data/lineups.csv 側の参照切れになる行も一緒に片付ける(他の行が同じ lineup_id を参照していない場合に限る)。

3 をここに置いてあるのは、これが「余裕があればやる工程」に見えるためである。 検索を使わないので予算とは無関係だが、調査の側に置くと予算切れの回で丸ごと飛ぶ——実際にそれで全358行が空欄のまま定着した。打ち切った週でも通す。

なお append_lineup.py(日割りそのものの書き出し)は調査を伴うので、この表には入らない。ただし --init で lineups.csv を空にしたまま終えると、lineup_id を持つ公演行の参照が切れて 7 の検証がERRORで落ちる。 書き出しに着手したら、そこも終わらせること。この検証に直面したとき、行そのものを消してはいけない——2026-08-29 の無人実行は、まさにこのエラーを行ごと lives.csv から削除する応急処置で押し切り、5件のフェスを本体行ごと失った。正しい対処はエラー文にも埋め込んである:ラインナップをまだ書けるなら書く、書けないなら python3 tools/prev_rows.py lives --uid <uid> で該当行を確認し lineup_id だけを空にして書き戻す。

8 の --check / festival_gate.py は、都県とフェス名簿の網羅性を機械的に確認するためのものである。 2026-08-29 の無人実行では、千葉・群馬の主要フェス会場(ROCK IN JAPAN・氣志團万博・New Acoustic Camp)が調査範囲から漏れたまま最後まで気づかれなかった。report_stats.py lives --check は「3件に満たない都県」「隣接5県が多すぎる」を終了コードで判定し、festival_gate.py は data/festivals.csv に登録されていて直近まで見つかっていたフェスが今回の lives.csv に見当たらないかを判定する。どちらも落ちたら、まず実際にその都県・フェスを調べ直すこと。 調べたうえで今回は本当に無い/少ないと確認できたなら、--allow-short <pref>(report_stats.py)・--allow "<フェス名>"(festival_gate.py)で明示的に承知したことにしてから終える。黙って無視しないこと——この2つは「気づかずに調査範囲から漏れる」という同じ事故を防ぐために追加した。

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

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

--init は data/lives.csv を空にする。 ただしステップ0の --carry-rest --apply を通した時点で、CSVは先週の完全な状態に戻っている——そこから先の打ち切りで失うのは「今週増やせたはずの分」だけである。逆に言えば、ステップ0を通す前に終えると、その週の収集はまるごと失われる。(日割り lineups.csv は戻らない。ステップ0の書き戻しでフェス行の lineup_id は外れ、今週あらためて書いたフェスだけが日割りを持つ——「日割りの書き込み手順」参照)

自動実行のスクリプト(.claude/scripts/claude-routine.sh)は、diff_data.py と validate_data.py が通らなかった回をコミットせず、data/ を実行前の状態に戻す。途中で終えた週は、その週の収集がまるごと失われる。 ライブでは、その週にしか拾えない追加公演・締切をまるごと落とすことを意味する。

「実行環境の前提」で取得ができないと判明した場合も、終了工程は通す。 その章の「最後まで実行してはならない」は、裏の取れない行を書くなという意味であって、CSVを未完成のまま放置してよいという意味ではない。裏の取れた行だけを書き、残りは下の撤退の手順で片付けてから、終了工程に進む。

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

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

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

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

  1. 調査済みで未追記の行を append_rows.py(フェスで日割りがあるものは append_lineup.py --rows)で書き切る
  2. 子が書いた temp/dispose-lives-*.jsonl(今回の波のぶんだけ)を python3 tools/prev_rows.py lives --dispose に通す(cat temp/dispose-lives-*.jsonl | python3 tools/prev_rows.py lives --dispose。エラーで止まったら、その行を直すか外してやり直す)。これを飛ばすと、中止になった公演が載り続ける——中止を知らない利用者が会場に行くことになる
  3. python3 tools/diff_data.py lives --allow-unexplained --allow-noop で差分を一覧する(出力を切らない)
  4. [表記が変わった可能性] に挙がった組だけは片付ける(ここから先は検索を使わない)
    • 同じ公演だと分かる組 → --dispose に renamed として記録する。放っておくと
      旧uidの持ち越し行と新uidの新規行が両方とも公開される(「消えた行の説明と、差分の確定」参照)
    • 判断が付かない組 → そのままでよい。来週 tier A で見直す
  5. 終了工程の 2〜9 を通す(3 の fill_apple_music.py を含む。検索を使わないので打ち切りの理由にならない)
  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 "<工程名>" を打っておく。


⚠️ 姉妹スキルとの重要な違い(最初に読むこと)

このスキルは /kanto-event-collector(イベント)・/kanto-movie-collector(映画)と同じダッシュボードのためのものだが、そのまま流用してはいけない箇所が5つある。

項目 イベント収集 ライブ収集(このタスク)
価格比較 中核工程。asoview!/Klook/KKday等を横断して最安を探す 行わない。 ライブチケットは主催者が定価を決めており、プレイガイドを横断しても価格は同じ(違うのは手数料程度)。「どこが安いか」という問いが成立しない
代わりに調べること — チケットの受付経路と受付期間を「時刻まで」。 いつ発売され、いつまでに申し込めるか
公演ごとの差 同じイベントは1行 ツアーは公演ごとに販売状況が違う。 一部の公演にだけ出る枠(見切れ席・追加席・公式リセール)を limited_sale に記録する
画像 収集しない 収集しない(理由は下記「画像を収集しない」章。3タブとも画像の列を持たない)
前回データとの突き合わせ 共通工程(3スキルとも実施する) 同じ仕組みを使うが、意味が重い。 追加公演・新規発表の検知そのものがこのタスクの主成果になる

価格比較の工数をまるごと「受付期間の確定」と「追加公演の検知」に振り替える、というのがこのスキルの設計である。


実行環境の前提(着手前に確認すること)

このタスクは公演ページを実際に開いて読むことを前提にしている。検索結果の要約だけでは、日付・時刻・受付期間を規則どおりに確定できない。

着手前に、次の2つができるかを確認する。

  1. アーティスト公式サイト・会場公式サイト・正規プレイガイドのページ本文を取得できるか
  2. 音楽メディア(音楽ナタリー等)の記事本文を取得できる

取得できない環境では、このタスクを最後まで実行してはならない。(これは「裏の取れない行を書くな」という意味であって、CSVを未完成のまま放置してよいという意味ではない。書ける行だけ書き、「完走条件」の撤退の手順で残りを片付けてから終了工程に進む。) 検索結果の要約は公演名と日付の対応を取り違えることがあり(下記ステップ1の警告を参照)、それを根拠に書くと、実在するアーティストの公演日を間違えて掲載することになる。

取得できない場合は、裏の取れた公演だけを書き、件数が目標に届かないことと理由をそのまま報告すること。目標件数に合わせるために未検証の行を足すのは、このタスクで最もやってはいけない失敗である。


出力仕様

出力先

  • data/lives.csv(全件を毎回書き直す。追記ではなく完全再生成)
  • data/lineups.csv(フェスの日割りラインナップ。同じく毎回書き直す。後述の「フェスの日割りラインナップ」章)
  • index.html はこのタスクでは編集しない
  • data/venues.csv(会場の名簿)も更新する。以前は「追加候補として報告し、追加は人間に委ねる」としていたが、名簿はスキーマ検証つきのデータなので tools/roster.py 経由で直接更新してよい(新会場は candidate として登録され、2回ヒットすると active に昇格する)。手順はステップ2(会場ベースの網羅調査)と docs/COLLECTION-PROTOCOL.md 第7章を参照
  • SKILL.md(この文書)の目標件数・品質基準・禁止事項は自分で書き換えない。 名簿(データ)は自動更新、それらは提案止まりという線を守ること。気づいたら docs/skill-feedback.md に追記する。それに当たらない小さなバグ(誤字脱字、壊れたコマンド例、古いパス・URLの参照、tools/*.py の些末な不具合など)は、この SKILL.md・tools/・対応する設計文書の記述を自分で直してよい。直した内容は docs/skill-feedback.md の「自己修正ログ」に記録すること。線引きは docs/COLLECTION-PROTOCOL.md 第7.1節

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

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

python3 tools/append_rows.py lives --init を実行し、data/lives.csv をヘッダーだけの状態にする。このコマンドは親だけが実行する。サブエージェントは実行しない——サブエージェントに実行させると、他の波が書いた分もろとも消える。前回の内容は data/.prev/ に自動退避されるので、差分検知の材料は失われない(以前は初期化の前に読む必要があったが、その制約は無くなった)。

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

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

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

    python3 tools/append_rows.py lives <<'EOF'
    {"title": "...", "artists": "...", "venue": "...", ...}
    {"title": "...", "artists": "...", "venue": "...", ...}
    EOF
  2. コンテキストに全件のデータを保持する必要はない。追記が終わったバッチの内容は忘れてよい(差分は diff_data.py が機械的に取るので、手元に抱える必要もない)

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

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

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

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

  • 会場リストを5〜10会場調べたらまとめて書く
  • 関東開催フェスの名簿を確認したらまとめて書く
  • 調査ステップが変わる節目(会場調査→フェス調査→プレイガイド補完)でまとめて書く

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

会場の座標・最寄り駅・駐車場・読みは、前回値から自動で補完される。**空欄のまま渡してよい。**同じ会場の別の公演からも引かれるので、ツアーで同じ箱が何度も出てくる場合でも書くのは1回でよい。desc area official_url genre live_type tour_id apple_music_url も、同じ公演(uid)の前回値から自動で埋まる。

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

python3 tools/append_rows.py lives <<'EOF'
{"title": "...", "venue": "Kアリーナ横浜", "start_date": "2026-09-05", "end_date": "2026-09-05",
 "open_time": "16:00", "start_time": "17:00",
 "genre": "rock", "live_type": "oneman", "area": "神奈川県・横浜市西区",
 "pref": "kanagawa", "price": "全席指定 9,800円", "source": "チケットぴあ", "url": "https://...",
 "onsale_label": "一般発売", "onsale_start": "2026-08-09", "onsale_start_time": "10:00", "_carry": "*"}
EOF

受付・日付・料金は持ち越せない。 onsale_end などを _carry に指定するとスクリプトがエラーで落ちる。これは「❌ 前回CSVの onsale_end をそのまま流用する」という後述の禁止事項を、指示文でのお願いではなく仕組みで担保したものである。受付は進む。 確認できないなら空欄にする。

"_carry": "*" を使ってよいのは、その行の内容に変更がないことを実際に確認できたときだけ。ライブでは出演者の追加・キャンセルが起きるので、artists の持ち越しには特に注意する。

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

id,tour_id,title,kana,artists,genre,live_type,area,venue,venue_url,pref,start_date,end_date,date,dates,open_time,start_time,end_time,date_note,backup_date,status,rank,announced_date,is_additional,onsale_label,onsale_start,onsale_start_time,onsale_end,onsale_end_time,limited_sale,price,source,url,official_url,lat,lng,desc,note,parking,nearest_station,apple_music_url,lineup_id

全42列。列名・順序ともに厳守すること(kana が増え、venue_url の位置が venue の隣に移り、末尾に parking nearest_station apple_music_url lineup_id が加わった)。

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

このタブは価格比較レイヤーを持たない。 price_official / price_best / discount_pct の列は存在せず、書いてはいけない。チケットは定価固定で、プレイガイドを横断しても値段が変わらないため(設計書 第12.8節)。安さではなく「受付経路と締切」を追うのがこのタスクの価値である。

列 必須 規則
id ✓ 1から連番
tour_id 同じツアー・同じフェスの行に同じ値を入れる(例:t-01)。単発公演は空欄でよい。ダッシュボードはこの値で「このツアーの他の公演」を引く
title ✓ 公演名・ツアー名・フェス名の正式名称。煽り文句は含めない
kana 公演名・アーティスト名の読み(ひらがな or カタカナ)。漢字表記のアーティストを「かな」で検索できるようにするための列。表示には使わない。例:米津玄師 → よねづけんし。読みが自明でないものだけでよく、全件必須ではない
artists ✓ 出演者。複数は ARTIST A|ARTIST B のように | 区切り。フェスはここに主要な出演者を最大8組まで(カードのタイトル直下に出る行の材料で、200組を1セルに入れると表示もCSVも壊れる)。残りの全出演者は data/lineups.csv に日ごとに書く——後述の「フェスの日割りラインナップ」章
genre ✓ 下記10キーから該当するもの。複数可(| 区切り)
live_type ✓ 下記5キーから1つ
area ✓ 表示用の地名(例:東京都・江東区)
venue ✓ 会場名。data/venues.csv の venue 列と完全一致させる(一致しないと会場検索・会場地図が機能しない)。マスターに無い会場はその会場の正式名称を書く
venue_url 会場の公式サイト。venues.csv に会場がある場合は不要(マスター側から引く)
pref ✓ tokyo kanagawa saitama chiba ibaraki tochigi gunma other
start_date ✓ 公演日 YYYY-MM-DD。不明なら空欄。推測で埋めない
end_date ✓ 最終日 YYYY-MM-DD。複数日開催(フェス・同一会場での連日公演)はその最終日、単日公演は start_date と同じ日。空欄は「終了日が未定」の意味になり、公演が終わっても「開催中」と表示され続ける
open_time 開場時刻 H:MM。公演の価値の半分はここにある——下記「日程の書き方」
start_time 開演時刻 H:MM
日程のその他の列 dates end_time date_note backup_date date status rank は下記「日程の書き方」を参照。表示用の日時文字列は書かない
announced_date 公演・追加公演が発表された日 YYYY-MM-DD。分かる範囲で。7日以内なら画面に「NEW」バッジが出る
is_additional 追加公演・追加席開放なら 1。それ以外は空欄
onsale_label 直近の受付の名称(例:オフィシャル先行/一般発売)。受付が無い/終了なら 受付終了 SOLD OUT 当日券あり 入場無料 などの状態語でもよい
onsale_start その受付の開始日 YYYY-MM-DD
onsale_start_time その受付の開始時刻 HH:MM(24時間表記)。先着順の販売では日付より重要(下記)
onsale_end その受付の締切日 YYYY-MM-DD。7日以内なら画面にカウントダウンが出る。この列の精度がこのタスクの価値を決める
onsale_end_time その受付の締切時刻 HH:MM。多くは 23:59 だが、18:00 締切なども実在するので確認して書く
limited_sale 一部の公演にだけ出る枠(見切れ席・注釈付指定席・追加席・当日券・公式リセール等)を、内容と日時を含めた1文で。下記「限定・追加販売」参照
price ✓ 券種別の実際の料金(例:全席指定 9,800円/立見 8,800円(ドリンク代別))。不明なら 要問合せ
source ✓ 出典サイト名。URLではなく、人間が読める短いサイト名を書くこと(例:チケットぴあ イープラス 公式サイト)。ダッシュボードはこの値をボタンのラベル「{source}でチケットを見る」にそのまま使うため、URLを書くと「https://...でチケットを見る」と表示されてしまう
url ✓ チケット販売ページ。正規流通のみ(下記「転売サイトの絶対禁止」)
official_url 公演・ツアー・フェスの公式サイト。見つからなければ空欄(推測URLを書かない)
lat / lng 会場の代表座標。venues.csv に載っている会場なら空欄でよい(マスターから解決される)。臨時会場のときだけ書く
desc ✓ 100〜150字。タイトルと日付の言い換えでは不可(その公演の固有性を1つ以上入れる)。下記「desc の書き方」参照
note 年齢制限・ドリンク代・全席指定/オールスタンディング・雨天決行・再入場可否・公式リセールの有無など
parking 会場の駐車場情報(下記「駐車場・最寄り駅の書き方」参照)
nearest_station 最寄り駅と徒歩時間(下記「駐車場・最寄り駅の書き方」参照)
apple_music_url メインアーティストの Apple Music アーティストページ(下記「Apple Music アーティストリンク」参照)
lineup_id フェスだけ。data/lineups.csv の日割りラインナップを引くキー。luckyfes-2026 のような自分で決めるスラッグ(英小文字・数字・ハイフン)。一度決めたら翌週以降も同じ値を使う(_carry で自動的に持ち越される)。ラインナップを1件も書けないフェスは空欄にする

日程の書き方(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月の毎週土日 など)だけ、ここに自由記述で書く。書いた行はこの文字列がそのまま表示され、上の構造化列は使われない。ただし月・季節までは読み取れる場合、start_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 だけでなく start_date/end_date も埋める

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

読み取れた期間 start_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 に「◯月」「春」「夏」「秋」「冬」を含む自由記述があるのに
start_date/end_date が両方空の行を WARNING で挙げる。出たら必ずこの手順に従うこと。

ジャンルキー(genre・複数可)

キー 対象
rock ロック・バンド・パンク・メタル
pop J-POP・ポップス・シンガーソングライター
idol アイドル(国内)
kpop K-POP・アジアのアーティスト
hiphop ヒップホップ・R&B・ラップ
dance ダンスミュージック・EDM・クラブ
jazz ジャズ・ソウル・ファンク・ブルース
classical クラシック・オーケストラ・オペラ
anime アニメ・ゲーム音楽・声優・ボーカロイド
other 上記に当てはまらないもの・多ジャンル

公演形態キー(live_type・1つだけ)

キー 対象
fes 音楽フェス・複数組が出演する大型野外/屋内イベント
oneman ワンマンライブ・ツアー公演
event 対バン・イベンター主催の音楽イベント
classic クラシック・オペラ・オーケストラ公演
free 入場無料・フリーライブ

収集ルール

1. 対象期間 —— 「公演日」または「締切」のどちらかが3ヶ月以内

イベント収集スキルの「直近3ヶ月」とは条件が違う。次のどちらかを満たすものを対象とする。

  • ✅ 公演日が実行日から3ヶ月以内
  • ✅ チケットの受付締切または一般発売日が実行日から3ヶ月以内(公演日が3ヶ月より先でもよい)

理由: 半年先の公演のチケットが今日売られている、というのがライブの通常の姿である。公演日だけで切ると、今まさに申し込むべき公演が一覧から消える。このダッシュボードは「行けるものを探す」ためのものであり、「行動すべきタイミング」を落としてはならない。

除外するもの:

  • ❌ すでに終了した公演
  • ❌ 公演日が3ヶ月より先で、かつチケットの受付予定も3ヶ月より先のもの(まだ何もできない)

2. 対象地域は関東1都6県+隣接5県

区分 都県 pref 列
本体(関東1都6県) 東京都・神奈川県・埼玉県・千葉県・茨城県・栃木県・群馬県 tokyo kanagawa saitama chiba ibaraki tochigi gunma
隣接県 山梨県・静岡県・長野県・新潟県・福島県 すべて other

隣接県を含めるのは、関東からその日のうちに行ける公演が実際にあり、名簿にもその会場が
意図的に入っている
ためである(data/venues.csv の静岡県18会場、data/festivals.csv の
FUJI ROCK FESTIVAL=新潟県)。「隣接5県」は関東のいずれかの都県と県境を接する県、という
定義で決めてある——恣意的に増やさないための線引きなので、この5県より外へは広げない。

pref は8キーしかない。 隣接県はすべて other(関東以外)になる。shizuoka の
ような県名を書くと roster.py と validate_data.py が拒否する。県名は area 列に人間向けの
表記で書く
(例:area = 静岡県・浜松市、pref = other)。画面では「関東以外」の
チップで絞り込める。

ツアーは、この12都県で行われる公演だけを拾う。同じツアーの大阪公演・名古屋公演は入れない
(tour_id は同じでよいが、行としてこの範囲の分のみ書く)。

調査順序:フェス(地域を問わず、名簿を一巡)→東京→神奈川→埼玉→他都道府県(千葉・茨城・栃木・群馬・隣接5県をまとめて「他都道府県」として扱う)の順に着手する。ステップ1(フェス)を先に済ませてから、ステップ2(会場ベース)をこの都県順で回る。サブエージェントに分けて調べる場合も、優先順位の高いものから先の波に割り当てる。時間や予算が尽きたときは、この順の末尾(他都道府県→埼玉→神奈川→東京)から間引く。

3. ツアーは「1公演=1行」(重要)

ツアー全体を1行にまとめてはならない。 venue に会場を1つしか書けず、このダッシュボードの主機能である場所での絞り込みが壊れる(「東京公演」と「横浜公演」が1行になると、横浜で検索しても出てこない、あるいは逆に東京で検索して横浜公演が出てくる)。

  • ✅ 同じツアーの東京公演・横浜公演・さいたま公演 → 3行に分け、tour_id を共通にする
  • ✅ 同一会場での連日公演(2〜3日)→ 1行にまとめ、start_date/end_date で表現してよい
  • ✅ 複数日開催のフェス → 1行(start_date/end_date)
  • ❌ venue に 東京ドーム|横浜アリーナ のように複数会場を書く

4. 目標件数

80〜150件を目安とする。

バランスの取り方:

  • 関東7都県すべてから最低3件。 東京は全体の半分まで
  • 隣接県(pref="other")は、この7都県の枠を埋める代わりにはならない。 関東が薄いまま隣接県で件数を稼ぐと、このダッシュボードが「関東のイベントボード」でなくなる。全体の1割程度を上限の目安とし、それを超えそうなら関東側の探索が足りていないと考えること
  • ジャンル10種のうち最低7種をカバーする
  • 会場規模を散らす。 ドーム・アリーナ級だけを集めると「有名アーティストの一覧」になり、発見の価値が落ちる。ホール級・ライブハウス級を全体の1/3以上入れること
  • live_type は oneman に偏りやすい。fes event classic free も意識的に探す

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

Web検索の呼び出しは、1セッション200回で打ち切られる。 超えた時点で、以降の調査は中途半端な場所で止まる。

このタスクは3系統の起点を回るうえに、公演ページの裏取りが必須(下記ステップ2・会場ベースの網羅調査の警告)なので、3スキルの中で最も予算が厳しい。「たくさん調べる」ことは品質ではない。予算をどこに使うかが品質である。

工程ごとの検索の枠

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

工程 検索の枠 備考
ステップ0〜0.5 土台・棚卸し 0 ローカルのコマンドだけ
ステップ1 フェスの年間カレンダー 20 名簿はURLに直行。枠は主に「名簿に無いフェス」の探索に使う
ステップ2 会場ベースの網羅調査 5 名簿のURLに直行する(現状すべての会場にURLあり)
ステップ3 プレイガイド・音楽メディア 20 招聘元5サイトはURLに直行する(この枠を使わない)
ステップ4 追加公演・新規発表の検知 20 このタスクの主成果。削らない
ステップ5 各公演の深掘り(裏取り) 75 新規行のみ。前回から続く行は前回の url/official_url に直行
ステップ5.5 いま載っている行の欠けを埋める 0 前回行の url/official_url に直行する。検索が要るときは予備から使う
Apple Music(lives.csv 側) 0 APIを直接叩く。検索は使わない
Apple Music(lineups.csv 側) 0 tools/fill_apple_music.py に任せる。モデルは1件も引かない
予備 30
合計 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/venues.csv はほぼ全件にURLが入っている(data/festivals.csv も同様)。知っているURLを検索し直さないこと。

python3 tools/roster.py venues --list --urls
python3 tools/roster.py festivals --list --urls

4列目にURLが出る。会場の公演カレンダーはこのURLを直接開く。標準エラーに出る
「うちURL不明 N件」が、その工程で検索を使ってよい件数である——件数をこの文書に
書き写さないこと(名簿は毎週増えるので、書き写した数字は必ず古くなる)。

前回から続いている公演も同じで、前回の url(チケットページ)と official_url を引けば検索は要らない。

python3 tools/prev_rows.py lives --venue Kアリーナ横浜   # 前回の url / official_url が入っている

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

必須列が揃った行は、そこで調査を終える。 すでに「行けるか・間に合うか」を判断できる情報が揃っているのに調べ続けるのは、品質ではなく消費である。

区分 列 扱い
これが揃ったら終わり title artists genre live_type area venue pref start_date/end_date price source url desc + onsale_*(tier A は onsale_end を確認するまで終えない。B/C は確認できた範囲) 揃った時点で次の行へ移る
ついでに拾うだけ kana note venue_url tour_id announced_date is_additional parking nearest_station 開いたページに書いてあれば書く。このために追加でページを開かない
調べない lat / lng venues.csv と前回値から自動で補完される。空欄で渡す
1回で見つからなければ空欄 apple_music_url(lives.csv 側) iTunes Search API を1回叩いて確信が持てなければ空欄。粘らない

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

  • ❌ ツアーの各公演について、同じ会場の駐車場・最寄り駅を毎回調べ直す(会場の属性として自動補完される。書くのは1公演ぶんでよい)
  • ❌ 会場ページで日時・料金・受付を確認した後、座標のためにもう一度地図を開く
  • ❌ kana や tour_id が分からないので検索する(分からなければ空欄が正しい)
  • ❌ すでに onsale_label が SOLD OUT と確認できた公演について、他の受付経路を探し続ける(完売は確定情報である。公式リセールの有無は会場・ツアーページで分かった範囲だけ書く)
  • ❌ 価格比較(このタブでは行わない。定価固定のため——「姉妹スキルとの重要な違い」参照)

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

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

  1. tier A の再確認(落とすと中止を伝えられない。ライブでは最も実害が大きい)
  2. 追加公演・新規発表の検知(このタスク固有の主成果。発表週に拾わないと次週には終わっている)
  3. 今週締切が来る受付の確定(onsale_end の時刻まで)
  4. 新規行の深掘り
  5. tier B の再確認
  6. 補助列の穴埋め(lives.csv の apple_music_url、parking、kana 等) ← 最初に捨てるのはここ

lineups.csv の apple_music_url は、この優先順位の外にある。検索を1回も使わない
ローカルのコマンド(tools/fill_apple_music.py)なので、予算切れを理由に飛ばさない。

目標件数(80〜150件)に届かないことより、未検証の行を足すことのほうが重い失敗である。 これは「実行環境の前提」章がすでに述べているとおりで、予算切れでも同じことが言える。打ち切ったら、その事実と工程名を報告に書く。

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

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

成果 下限 確認のしかた
tier A の行の onsale_end tier A の行は、締切を確認するまでその行を終えない report_stats.py の充足率
追加公演・新規発表の検知 ステップ4を必ず通す(枠20回は削らない) diff_data.py の [新規]
来日公演の定点観測 招聘元5サイト(ステップ3)を全て開く 報告に「開いた招聘元/拾った公演数」
フェスの日割り live_type: fes の全行で取得を試みる 報告に「書けたフェス/書けなかった理由」

onsale_end はこのタブの価値そのものである。 本文が「この列の精度がこのタスクの
価値を決める」と書いているにもかかわらず、実測は90件中2件(2%)だった。
「確認できた範囲の onsale_*」という打ち切り基準が、確認しないことを常に許していた
のが原因である。tier A の行についてはそれを許さない。

締切を確認できなかった tier A の行は、空欄のまま残したうえで報告に件数を書く
(推測で埋めるのは従来どおり禁止。空欄は正しく、黙って空欄にすることだけが問題である)。

決着している受付は対象外。 SOLD OUT 受付終了 完売 当日券あり 入場無料 と
確認できた行に締切は無い。それらは onsale_label を書いた時点で終わりでよく、
tier A にも上がらない。

再確認まで手が回らなかった 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回開く
  • 都県で分けるときは、優先順位(フェス→東京→神奈川→埼玉→他都道府県)の高いものから先の波に割り当てる。 フェス(ステップ1)は地域を問わず先に済ませ、会場ベース(ステップ2)を波に分けるときも東京→神奈川→埼玉→他都道府県の順にする
  • 1体に渡す会場は10件程度までにする。 東京(約57会場)は6波前後、神奈川・埼玉・千葉(各22〜24会場)もそれぞれ2〜3波に割り、3体ずつ同時に起動する。子は maxTurns: 60 で打ち切られ、60ターンで開けるのは実測で28〜40回・6〜17サイトである(2026-09-25〜30、重複を除いた数え方)。2026-09-25 の東京会場の波(15会場)は60ターンで打ち切られ、有明アリーナの受付確認を残して終わった。2026-09-04 の実行は東京56件を28件ずつ2体に割ったところ、片方が会場の通信障害(DNS不通・SSL証明書不一致・タイムアウト)の連鎖で141ターンまで膨らみ、アカウントの利用上限を招いた(下の「1体が回るターン数を60以下に収める」の実測を参照。docs/routine-postmortems.md 2026-09-04)
  • 小箱公演の補完(枠20回)・追加公演の定点観測(枠20回)は、都県ベースの会場再確認の波に混ぜない。 それぞれ独立した波として起動し、割り当てられた枠を使い切ってから次に進む。姉妹スキル(events)は2026-08-29 15:30の実行で、施設の個別再確認と名簿外発掘(エリア軸探索・横断サイト補完)を1つの波に混ぜ、名簿外発掘の枠を実質使い切れずに終えた
  • 1回の呼び出しに、調査軸1つの全体を背負わせない。 名簿の一部・対象期間の一部などに区切り、波——同時に起動して同時に受け取る1組——に分けて呼ぶ
  • 1つの波の結果を全部 append_rows.py で書き切ってから、次の波を投げる。
    python3 tools/append_rows.py lives < 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 を見る。 投げてしまうと、動いている子に割り込む手段が無い。「新しい波を投げないでください」が出ていたら、そこで探索を終えて終了工程へ進む
  • 波の途中でも、撤退の線(60M)を越えた取得は自動で止まる。 fetch_page.py と WebSearch は呼び出しのたびにこの線を見ており、越えていれば取得せずに「ここまでの行を temp/rows-*.jsonl に書いてターンを終えること」という指示が返る(.claude/hooks/fetch-budget-guard.sh → tools/budget.py --gate-fetch)。これは子自身の取得にも掛かるので、子が指示を読んでいなくても働く——最初の波そのものが線を越えて打ち切られた2026-09-04の実例を受けた仕組みである
  • 1波の体数は自分で数えなくてよい。 CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS が上限を与える(このスキルは3体)。超えた起動は Concurrent subagent limit reached で失敗する

venues.csv の152会場は133ホストに分散しているので、3体を都県で分けてもホストは競合しない。

背景起動を禁じている理由——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 lives --out temp/brief-lives.md を実行し、指示には**「まず temp/brief-lives.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 lives --worklist --pref <担当都県> > temp/worklist-lives-<担当都県>.md
    wc -l temp/worklist-lives-<担当都県>.md   # 件数だけ確認する(中身は開かない)

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

  4. このスキルの中核工程を名指しする——受付期間の確定(onsale_*)と追加公演・新規発表の検知。価格比較は行わない(姉妹スキルとの違いを参照)

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

  6. 有効なキーの一覧と対象期間を明記する。 genre は rock pop idol kpop hiphop dance jazz classical anime other の10キー、live_type は fes oneman event classic free の5キーのみ(それ以外は append_rows.py が拒否する)。対象地域は関東7都県+隣接5県(pref="other")、対象期間は公演日または受付締切・一般発売日のどちらかが実行日から3ヶ月以内

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

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

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

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

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

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

調査手順

起点が3系統ある。イベント収集の「施設リスト1本」とは違い、1つのリストでは必ず取りこぼす。 3つとも回すこと。

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

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

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

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

なぜ最初に置くのか。 以前はこれを終了工程の側に置き、調べ終えてから残りを
書き戻していた。その順序では、予算が尽きた回に大量の公演が未確認のまま最後に
なだれ込む——ライブは受付が日々進むので tier A が多く、2波だけで撤退の線に届いた回もある。
先に書き戻しておけば、調査はどこで打ち切っても「今週増やせたはずの分」しか失わない。
同じ公演を再収集しても append_rows.py が uid で突き合わせて更新するので、
行が二重になることはない。

フェス行の lineup_id はここで外れる。 直前の append_lineup.py --init で
lineups.csv が空になっているため、参照先の無い lineup_id を書き戻すと検証が
ERROR になる。日割りは、フェスを調べた波を受け取った時点で、本体行と一緒に書き直す
(「日割りの書き込み手順」)。

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

python3 tools/validate_data.py --duplicates-only

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

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

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

土台を作ったら、必ず前回分の一覧を取る。 これをしないと announced_date と is_additional が埋まらず、このタスクの目玉である「追加公演の検知」が機能しない。

python3 tools/prev_rows.py lives --worklist

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

python3 tools/prev_rows.py lives --uid 3f2a1b9c            # その行を全列で
python3 tools/prev_rows.py lives --venue Kアリーナ横浜      # その会場の前回分をまとめて
python3 tools/prev_rows.py lives --worklist --tier A   # 今回必ず再確認する行だけに絞る

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

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

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

一覧には tier が付く。ライブは受付が日々進むため tier A(今回必ず再確認)の比率が高い。

tier 意味
A 今回必ず再確認する(受付の締切・発売が21日以内/公演日が10日以内/追加公演/onsale_label があるのに締切が空 等。ただし SOLD OUT など決着済みの受付は A に上げない)
B 会場スケジュールを開いたついでに確認する(SOLD OUT など決着済みで当面動かない行もここ。価格の鮮度による tier B は、価格の列を持つイベント・映画だけの基準)
C 低頻度でよい

手元に前回データを抱え込む必要はない。 突き合わせは最後に diff_data.py が機械的にやる。この一覧は、ステップ2で会場を回るときの照合表として使うためのものである。

ステップ1:フェスの年間カレンダー調査(最優先)

会場リストでは拾えないのが野外フェスである。名簿にあるフェスは毎回確認する。(関東開催のものに加え、隣接5県で行われる関東からの動員が大きいフェス——FUJI ROCK FESTIVAL=新潟県苗場など——も対象。対象地域の節を参照)

python3 tools/roster.py festivals --list --urls

ほぼ全件にURLが入っているので、名簿のフェスは直接開く。この工程の枠(20回)は、主に「名簿に無いフェス」の探索に使う。

名簿は data/festivals.csv(フェス名・主な会場・開催時期の目安)。以前この文書に表として書いていたが、フェスは開催地が変わり、隔年開催になり、終了する——散文に書いた一覧は更新されないまま古くなるので、データに出した。

収穫があったフェス、開催がなくなったフェス、新しく見つけたフェスは、その場で名簿に反映する。

python3 tools/roster.py festivals --hit "ROCK IN JAPAN FESTIVAL"
python3 tools/roster.py festivals --retire "○○フェス" --reason "2026年をもって終了"
python3 tools/roster.py festivals --add <<'EOF'
{"name": "○○ROCK FES", "venue": "○○公園", "pref": "chiba", "month_hint": "10月", "url": "https://..."}
EOF

フェスは出演者の発表が段階的(第1弾・第2弾…)であることに注意する。前回から出演者が追加されていれば、announced_date を更新し、desc のラインナップも書き換える(artists を _carry で持ち越すと、この更新を取りこぼす)。

検索クエリ例:

{フェス名} {実行年} 出演者
{フェス名} {実行年} タイムテーブル 発表
関東 音楽フェス {実行年}年{実行月}月
野外フェス {実行年} 関東 チケット

名簿に無いフェスも探すこと。 上のクエリの下2つは、そのためのものである。新しい都市型フェス・小規模フェスは毎年生まれている。

ステップ2:会場ベースの網羅調査

調査対象の会場は data/venues.csv にある。この順に回すこと:東京→神奈川→埼玉→他都道府県(千葉・茨城・栃木・群馬・隣接5県をまとめて「他都道府県」として扱う。「対象地域」章の「調査順序」参照)。

python3 tools/roster.py venues --list --pref tokyo --urls     # 1. 東京
python3 tools/roster.py venues --list --pref kanagawa --urls  # 2. 神奈川
python3 tools/roster.py venues --list --pref saitama --urls   # 3. 埼玉
python3 tools/roster.py venues --list --urls                  # 4. 他都道府県はここから拾う(全件・隣接県も含む)
python3 tools/roster.py venues --list --pref other --urls     # 隣接県だけを絞りたいとき(静岡ほか)
python3 tools/roster.py venues --list --kind livehouse --urls

都県ごとに回すときは --pref other を忘れないこと。 隣接県の会場は
pref="other" で入っているので(対象地域の節を参照)、関東7都県を順に指定するだけでは
1件も出てこない。名簿を漏れなく一周したいなら --pref を付けずに全件出すのが確実である。

(retired の会場と休館中の会場は自動的に除かれる。以前このSKILL.mdに直接書いていた「さいたまスーパーアリーナは改装休館中」といった情報は、venues.csv の closed_until 列に移した。散文に書くと更新されないまま古くなるため)

4列目に会場の公式サイトURLが出る。この工程では、そのURLを直接開く(検索しない)。 名簿は全件にURLが入っているので、この工程の検索の枠は5回——名簿に新しく足した会場のぶんだけである。

1会場につき1回、その箱のスケジュールを調べる。そのとき次の3つを同時に済ませる。

  1. 前回分の照合 —— ステップ0.5の一覧(担当表)でその会場の行を確認し、まだ予定されているか/中止・延期になっていないか/受付が次の段階に進んでいないか/追加席が出ていないかを見る
  2. 新規の発見 —— 前回になかった公演を拾う
  3. 収穫の記録 —— 手で打つ必要はない。 append_rows.py が、書いた行の venue から
    名簿の収穫を自動で記録する(名簿に無い会場は「名簿に無い会場 N件」として挙がるので、
    継続的に公演がある箱なら roster.py venues --add で入れる)

行を1件ずつ再確認してはいけない。 会場の公演カレンダーを1回開けば、既存行の確認と新規の発見は同じ取得で終わる。

新しい会場・閉館した会場を見つけたら、その場で名簿に反映する(以前は「報告して人間の判断に委ねる」としていたが、名簿はスキーマ検証つきのデータなので直接更新してよい)。

python3 tools/roster.py venues --add <<'EOF'
{"venue": "○○アリーナ", "kind": "arena", "pref": "chiba", "area": "千葉県・千葉市",
 "capacity": "10000", "lat": "35.60", "lng": "140.10", "url": "https://..."}
EOF
python3 tools/roster.py venues --retire ○○ホール --reason "2026年6月閉館"
python3 tools/roster.py venues --block ○○ホール --reason "robots.txt が拒否"  # 閉館ではない
python3 tools/roster.py venues --close ○○アリーナ --until 2027-04-01 --reason "改修工事のため休館"

kind は dome arena hall livehouse outdoor から選ぶ。収集元のページの文言を note にそのまま持ち込まないこと(roster.py が指示文めいた文字列を拒否するが、そもそも入れない)。

会場公式サイトの公演カレンダーを最優先の情報源とする。 理由は、それが「その箱で何月何日に何があるか」の一次情報であり、このダッシュボードの2大絞り込み軸(日付と場所)に直結する情報が、最も正確な形で載っているためである。まとめサイトは日付が古く、プレイガイドは受付が始まった公演しか載らない。

⚠️ 会場スケジュールは「候補の発見」にだけ使い、そのまま書き写さないこと。
実際の収集で、会場単位のスケジュール要約は公演名と日付の対応がずれることが繰り返し確認された(同一会場で12日連続公演と別公演が同じ日に立つ、季節の合わない公演名が混ざる、など)。
会場スケジュールで拾った公演は、必ずステップ5で公演名を軸に検索し直して、日付・会場・時刻を裏取りしてから書く。裏が取れなければその行は落とす。

休館中の会場について: 長期休館・建て替え中の会場には公演が組まれないので、調査しても収穫がない。roster.py venues --list はこれらを自動的に除いて出すので、リストに出てこない会場は調べなくてよい。新たに長期休館を知ったときは roster.py venues --close {会場名} --until {再開予定日} で名簿に記録すること(このSKILL.mdには書かない。散文に書いた休館情報は、再開しても更新されないまま残る)。

検索クエリの組み立て方(名簿に url が無い会場に限る。URLが分かっている会場は直接開く):

{会場名} 公演スケジュール {実行年}年{実行月}月
{会場名} イベントスケジュール
{会場名} ライブ {実行年}年{実行月}月

例:

  • Kアリーナ横浜 公演スケジュール 2026年9月
  • Zepp DiverCity スケジュール 2026年8月
  • さいたまスーパーアリーナ イベントスケジュール

スポーツ・展示会・株主総会などの非音楽イベントは拾わない。 このタブは音楽公演のためのものである。ただし「アイドルの握手会・ファンミーティング」は、音楽公演を伴う場合のみ対象としてよい。

ステップ3:プレイガイド・音楽メディアでの補完

会場リスト・フェスリストから漏れる公演(小箱の公演、急に決まった公演、追加公演)を補完する。

サイト URL 得意分野
チケットぴあ https://t.pia.jp/ 網羅性が最も高い。ジャンル別・エリア別の一覧が使える
イープラス https://eplus.jp/ 先行受付の情報が早い
ローソンチケット https://l-tike.com/ コンビニ発券系
楽天チケット https://ticket.rakuten.co.jp/ —
ticket board https://ticket.tickebo.jp/ ファンクラブ先行が多い
LivePocket https://t.livepocket.jp/ 小規模・インディーズ
ZAIKO https://zaiko.io/ 小規模・クラブイベント
teket https://teket.jp/ クラシック・吹奏楽に強い
音楽ナタリー https://natalie.mu/music 追加公演・新規発表のニュース
Billboard JAPAN https://www.billboard-japan.com/ 同上
SPICE https://spice.eplus.jp/ 同上
Real Sound https://realsound.jp/ 同上
rockin'on.com https://rockinon.com/ フェス情報に強い

来日公演は、招聘元の公演一覧から拾う

海外アーティストの来日公演は、上の2工程(会場名簿・プレイガイド)のどちらからも落ちる。 会場名簿を回る工程は会場側のカレンダーを見るが、ドーム・アリーナ級の来日公演は興行主が会場を押さえた時点では会場のカレンダーに公演名が出ず「貸切」等の表記で止まることがある。プレイガイドは受付が始まった公演しか載らない。結果として、発表済みで受付前の大型来日公演が、どの工程にも引っかからない期間が生まれる。

招聘元(プロモーター)の公演一覧は、この期間を埋める唯一の一次情報である。発表と同時に載り、受付状況も同じ画面で分かる。毎回この5サイトを定点観測する。

サイト URL 担当する層
CREATIVEMAN PRODUCTIONS https://www.creativeman.co.jp/upcoming/ SUMMER SONIC 主催。ロック・オルタナ系の来日全般
ウドー音楽事務所 https://www.udo.jp/shows/western-music 洋楽の来日。年月・エリア・受付状況で絞り込める
LIVE NATION H.I.P. https://www.livenationhip.co.jp/ ドーム・アリーナ級の来日
キョードー東京 https://kyodotokyo.com/ 招聘と販売を兼ねる。先行受付の告知が同じ画面に出る
indienative https://www.indienative.com/ 海外インディー・オルタナ系(小箱の来日を拾える)

source には招聘元の会社名をそのまま書く(例:LIVE NATION H.I.P. ウドー音楽事務所)。カードのボタンが「{source}でチケットを見る」になるため、利用者から見て販売元として意味の通る名前である必要がある。

ステップ4:追加公演・新規発表の検知(このタスク固有の工程)

音楽メディアの「追加公演」記事を毎回定点観測する。

検索クエリ例:

追加公演 決定 {実行年}年{実行月}月
{アーティスト名} 追加公演 {実行年}
追加席 販売 決定 {実行年}{実行月}
見切れ席 販売 {実行年}
{フェス名} 出演者 第{n}弾

announced_date は、メディアの発表記事の日付が分かればその日付を使う。分からない場合に限り、差分で新規と判定された行に今日の日付を入れる。分からないまま推測で古い日付を書かない。

公演名そのものに「追加公演」と入っている場合(例:ano Hall Tour 2026 DUAL DINER 追加公演)は、差分を取るまでもなく主催者が追加公演だと言っているので、is_additional=1 にしてよい。初回実行時(前回データが存在しないとき)に追加公演を拾える唯一の手がかりでもある。

初回実行時の注意: 前回データが無い状態では、差分による追加公演の検知は原理的に成立しない(diff_data.py は「前回データなし」と表示する)。初回は is_additional が上の「タイトルに明記された分」だけになるのが正しく、それらしく見せるために推測で立ててはならない。2回目以降から本来の検知が働く。

消えた行の説明と、差分の確定

ここから先は親(棚卸し・差分の確定・検証を行う側)の仕事である。 サブエージェントは data/ に書かないので実行できない(第11.5節)。

公演日を過ぎた行を、まず機械的に片付ける。ステップ0の --carry-rest --apply が既に同じ処分をしているので通常は0件で通るが、調査中に書いた行が公演日を過ぎている場合のために残してある。

python3 tools/purge_ended.py lives

検索を使わない機械的な処理で、消した行には自動で expired の処分が付く(lineup_id を持つ行を消した場合、他の行が同じ lineup_id を参照していなければ data/lineups.csv 側の該当行も一緒に消える)。expired は未確認のしるしであり、中止(cancelled)と紛れさせてはならない。

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

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

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

そのうえで、機械的に取った差分と突き合わせる。

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

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

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

前回との突き合わせを目視でやってはいけない。前回の全行×全列を読み直すことになるうえ、見落としても誰も気づけない。突き合わせは機械の仕事で、この工程でのモデルの仕事は、機械が拾った差分の意味を判断することである。

diff_data.py の出力 やること
[新規] に出た行 announced_date を確定する(上記)
既存の tour_id に増えた公演 is_additional = 1
[変更] に onsale_* が出た行 受付が次の段階に進んだということ。値が正しく更新されているか確認する
[変更] に artists が出た行 フェスの出演者追加。desc のラインナップも書き換えたか確認する
[前回値のまま持ち越し・未確認] ステップ0で書き戻したまま、今回どの工程でも書き直していない公演の件数。受付欄は空にしてあるだけで、受付が進んだのではない
[表記が変わった可能性] 同じ公演なら --dispose に renamed を記録する(放置すると同じ公演が2つとして公開され、NEWバッジが誤って付く)
[消滅] の行 処分済みの行(理由が付いている)。説明の無い行が出たら、1件ずつ理由を記録する(下記)

処分が要る公演には必ず説明をつける。

ended は end_date(フェス・連日公演では dates の最後の日)が実行日より前の行にしか付けてはならない。 end_date が実行日と同じか、まだ来ていない行を ended にすると、開催中・開催前の公演がCSVから消える。実際に「山中湖の3日間フェスを初日の翌日(=会期中)に ended として消してしまった」事故が起きている——「最終日の1日券が完売」「一部日程の受付終了」は会期そのものの終了ではない。 会期の一部にだけ「終了」「完売」を示す情報源の文言があっても、それだけで行全体を ended にせず、end_date と実行日を必ず突き合わせること。判断に迷う行は notfound(消えた理由が分からない)を使い、ended は情報源で会期満了を確認できたときだけ使う。なお、終了日を過ぎた行はステップ0で既に片付いているので、この回に ended を付ける場面はほとんど無い。

python3 tools/prev_rows.py lives --dispose <<'EOF'
{"uid": "8d7e6f50", "status": "cancelled", "note": "アーティスト体調不良により公演中止(公式発表)"}
{"uid": "c1d2e3f4", "status": "renamed",   "to": "e5f6a7b8", "note": "フェス名の表記が変わった"}
{"uid": "a1b2c3d4", "status": "notfound",  "note": "会場スケジュールから消えており、中止か延期か確認できず"}
EOF

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

中止・延期はライブで最も起きやすく、最も実害の大きい変化である。 前回にあった公演が今回無いとき、それを黙って落とすと、中止を知らない利用者が会場に行くことになる。cancelled と notfound を区別して記録し、どちらも報告に含めること。notfound は行を削除しない——確認できていないことは中止・終了の根拠にならないため、--dispose が前回値のまま自動で書き戻す(受付欄は空にする)。来週 tier A で最優先の再確認対象になる。

ステップ5:各公演の深掘り

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

拾った公演1件ごとに、必ず公式情報またはプレイガイドの公演ページまで辿って以下を確定させる。まとめサイトの記載だけで済ませない。

  1. 正確な公演日と開場・開演時刻
  2. 会場の正式名称(venues.csv と突き合わせる。表記ゆれに注意:Zepp DiverCity と Zepp DiverCity(TOKYO) など)
  3. 出演者(フェスは主要8組まで)
  4. チケットの受付状況 — いま何次先行まで進んでいるか、直近の締切はいつか、一般発売はいつか
  5. 券種別の料金(ドリンク代別かどうかも確認する)
  6. 公式サイトURL
  7. 注意事項 — 全席指定/オールスタンディング、年齢制限、再入場、雨天決行
  8. 駐車場・最寄り駅 — 「駐車場・最寄り駅の書き方」参照(初出の会場のみ)
  9. メインアーティストの Apple Music アーティストページ — 「Apple Music アーティストリンク」参照(API1回)

1〜7は、原則としてプレイガイドの公演ページ1回の取得で揃う。 項目ごとにページを開き直さないこと。

深掘りを打ち切る基準

必須列(title artists genre live_type 場所 日付 price source url desc + onsale_*)が揃った時点で、その行の調査を終える。

tier A の行では onsale_end を「確認できた範囲」で済ませない。
締切を確認するか、確認できなかったことを報告に残すかの二択である
(「今週かならず産む成果」参照)。以前の「確認できた範囲の onsale_*」という
書き方は、確認しないことを常に許していた——実測で90件中2件(2%)まで痩せた。

とくに次の状態は確定情報なので、それ以上の経路を探さない。

  • onsale_label = SOLD OUT(完売)→ 公式リセールの有無は、開いたページに書いてあった範囲だけ note / limited_sale に書く。別経路を探しに行かない
  • 全受付が終わっており、次の受付が未定 → onsale_* を空欄にして終わり。過去の受付を掘り返さない(「過去の受付しか分からないときは空欄」という規則のとおり)
  • apple_music_url が iTunes Search API 1回で確信を持てない → 空欄で確定。別の綴りで再検索しない

ツアーの2公演目以降は、1公演目で確定した内容を使い回す。 会場・駐車場・最寄り駅・アーティスト・Apple Musicリンクは公演ごとに変わらない。公演ごとに確認し直すのは、受付状況(onsale_*)と限定枠(limited_sale)だけである——これはツアー内で公演ごとに違うので、確認する価値がある(「限定・追加販売」章)。

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

  • 中止・延期・対象外になっていた → rows には書かず、指示にある temp/dispose-lives-<波の名前>.jsonl に {"uid", "status": "cancelled|out-of-scope", "note"} を書く。ended は使わない(終了日を過ぎた公演は着手時に片付いており、残りに付けると拒否される)
  • 確認できなかった → 何も書かない(親が判断する)

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

新規の発見と深掘りが一巡したら、残りの予算はCSV全体の欠けに回す。 ステップ5の深掘りは
新規行を対象にしているので、前回から続く公演の空欄はそのままでは埋まらない——とくに
ステップ0で空になった受付欄(onsale_*)は、ここで埋めない限り来週まで空のままになる。

python3 tools/report_stats.py lives --json

中核列(onsale_end onsale_label start_time price desc 等)の充足率が出る。低い列から順に、
会場スケジュール・プレイガイドの公演ページ単位で埋めていく——ツアーの公演一覧を1枚開けば、
同じツアーの公演がまとめて埋まる。

python3 tools/prev_rows.py lives --venue Kアリーナ横浜   # その会場の公演をまとめて引く

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


チケット情報の記載規則

転売サイトの絶対禁止(最重要)

url に入れてよいのは、正規の販売経路のみである。二次流通・転売サイトを、価格に関係なく採用してはならない。

ライブチケットの検索では、転売サイトが検索結果の上位に来ることが頻繁にある。イベント収集より事故の確率が桁違いに高い。

url に入れてよいもの

  • 公演・ツアー・フェスの公式サイトのチケットページ
  • チケットぴあ/イープラス/ローソンチケット/楽天チケット/ticket board/LivePocket/ZAIKO/teket などの正規プレイガイド
  • アーティストのファンクラブ受付ページ(会員限定である旨を note に書く)

url に入れてはならないもの

  • 二次流通・リセールを謳う非公式サイト
  • オークション・フリマサービスの出品ページ
  • 「定価より安い」ことを訴求するチケット売買仲介サイト

公式リセール(チケット流通センターではなく、主催者・プレイガイドが公式に運営するリセール。チケトレ、ぴあ公式リセール、e+ リセール等)は、url にはせず note に「公式リセール受付中」と書く。あくまで補足情報として扱う。

受付期間(onsale_*)の書き方 —— 日付だけでなく時刻まで取る

直近の、いま利用者が申し込める受付を1つ選んで書く。複数の先行が並行している場合は、締切が最も近いものを選ぶ。

onsale_label      = オフィシャル先行
onsale_start      = 2026-08-01
onsale_start_time = 10:00
onsale_end        = 2026-08-10
onsale_end_time   = 23:59

なぜ時刻が必須級なのか

先着順の販売では、時刻こそが情報の本体である。「8月1日発売」とだけ書かれた一覧は、実質的に役に立たない。人気公演は発売時刻の数分で売り切れるので、10:00 を知らないと並べない。

ダッシュボードは時刻まで含めて「発売前/受付中/受付終了」を判定する。日付だけを書くと開始は 0:00、締切は 23:59 とみなされるため、

  • 開始時刻を落とすと → 発売当日の朝に見た人へ「もう発売中」と嘘をつく
  • 締切時刻を落とすと → 18:00 締切の受付を、その日いっぱい受付中に見せる

いずれも「間に合うと思って開いたら終わっていた」を生む。日付が分かって時刻が分からないときは、時刻列を空欄にする(推測で 10:00 と書かない)。空欄なら上の安全側の解釈が働く。

時刻はどこに載っているか

  • プレイガイドの公演ページ(受付ごとに「受付期間 8/1(土)10:00〜8/10(日)23:59」の形で載る)
  • アーティスト公式サイトのチケットページ・お知らせ記事
  • ファンクラブ先行の告知(会員向けページに時刻まで書かれることが多い)

「◯時頃」としか分からない場合は、その旨が確認できた時刻をそのまま書き、note に「発売時刻は公式で最終確認を」と添える。

すでに全受付が終わっている、または売り切れている場合は、状態を onsale_label に書き、日付は空欄にする。

onsale_label = SOLD OUT        (完売。公式リセールがあれば note に書く)
onsale_label = 受付終了        (受付期間が終わり、次の受付が未定)
onsale_label = 当日券あり
onsale_label = 入場無料

完売公演も一覧には載せる。 公式リセールや当日券の可能性があり、「そもそも何をやっているか」自体が情報だからである。ただし onsale_label を必ず SOLD OUT にして、取れるように見せてはならない。

過去の受付しか分からないときは、onsale_* を空欄にする。 「6月の一般先行が6/14に終了した」ことだけを確認して 受付終了 と書くと、その後に始まった受付を見落として「もう買えない」と誤って伝えることになる。いま有効な受付が確認できたときだけ書くのが原則で、確認できなければ空欄が正しい。


限定・追加販売(limited_sale)—— ツアーは公演ごとに販売状況が違う

ツアーでは、一部の公演にだけ枠が出ることが頻繁にある。 ステージ構成が固まって見切れ席が解放される、機材席が客席に転用される、完売公演にだけ公式リセールが立つ——いずれも特定の日程・特定の会場にだけ起きる。

これは利用者にとって最大級の価値がある情報である。「東京は完売だが、横浜公演だけ追加席が出た」を知らせられるかどうかが、このダッシュボードの存在意義に直結する。

limited_sale に書くもの

対象 例
見切れ席・注釈付指定席 見切れ席を2026.8.1(土)10:00より追加販売
追加席・機材開放席 機材開放席を2026.9.3(木)18:00より販売
公式リセール 公式リセール(抽選)2026.8.25(火)16:00〜9.1(火)16:00 出品・購入申込
当日券 当日券を開演1時間前より会場窓口で販売
特定公演限定の券種 この公演のみ着席指定席を用意

内容と日時をセットで1文に書く。 日時のない「追加席あり」は、いつ動けばいいのか分からず役に立たない。

limited_sale の書き方の規則

  1. 公演ごとに確認する。 ツアーの1公演で見つけたからといって、他の公演の行にコピーしてはならない
  2. 逆に、ツアー全体に共通する案内(ツアー公式リセールなど)は、該当する全公演の行に書く。1行にだけ書くと「この公演だけ特別」と誤読される
  3. 二次流通は書かない。公式リセール(チケトレ/ぴあ公式リセール/e+リセール/チケプラTrade等、主催者・プレイガイドが運営するもの)に限る
  4. 確認できないものは空欄。「たぶん当日券が出る」は書かない

limited_sale の調査手順

前回CSVとの突き合わせ(ステップ4)と同じタイミングで、tour_id 単位で全公演を横に並べて見る。

tour_id ごとに、関東の全公演について
  ├ 受付状況(onsale_label / onsale_start / onsale_end)
  └ 限定枠の有無(limited_sale)
を並べ、公演ごとに違いがあるかを確認する

違いがあること自体が発見なので、報告時に「ツアー内で販売状況が分かれている公演」を明示すること。

検索クエリ例:

{アーティスト名} 見切れ席 販売
{アーティスト名} {会場名} 追加席
{アーティスト名} 公式リセール {実行年}
{フェス名} リセール 受付
{ツアー名} 当日券

⚠️ 日付・時刻・料金の絶対規則

確認していない日付・時刻・締切・料金を絶対に書かないこと。空欄のままにすること。

ライブは単日・時刻指定であり、間違いがイベント以上に直接的な実害になる。「開演19:00と書いてあったので18:30に着いたら、実際は18:00開演だった」は取り返しがつかない。

禁止事項

  • ❌ 「同じツアーの他会場が19:00開演だから、ここも19:00だろう」という推定
  • ❌ 「先行はだいたい2週間前に締め切る」という相場からの推測
  • ❌ 前回CSVの onsale_end をそのまま流用する(受付は進む)
  • ❌ 券種を確認せずに「一般〇〇円」とだけ書く
  • ❌ 発表日が分からないのに announced_date を埋める

空欄が多いことは品質の問題ではない。誤った締切が1件混ざることのほうが、はるかに深刻な品質問題である。(ダッシュボード側は、どの列が空でも壊れないように作られている)


画像を収集しない

アーティスト写真(アー写)・ツアーキービジュアル・フェスのポスター/フライヤー画像のURLを、収集してはならない。

data/lives.csv に画像URLの列は存在しない。列を勝手に追加してもいけない。

理由:権利者が出しているのは自社サイトへ掲載するための許諾であって、第三者サイトへの再掲載や画像への直リンクの許諾ではない。芸能事務所・レーベルは公式サイトでアーティスト画像の無断転載を明示的に禁止しており、さらに直リンク(ホットリンク)を禁止しているケースが実在する。「ファイルを複製せずURL参照するだけ」という論理は、著作権侵害の局面では概ね通っても、利用規約違反と著作者人格権(氏名表示権)の問題を何も片付けない。この判断は3タブに共通で、映画のポスターも同じ理由で扱わない。

詳細な判断の根拠は docs/DESIGN.md 第7.1節に記載してある。方針を変更する場合は、必ずそこを先に読むこと。

画像の代わりに、ダッシュボードは複数出演(フェス・共演)の公演で artists 列の出演者名をタイトル直下にテキストで並べる。文字は事実情報なので権利上の制約がなく、フェスにおいては小さく表示された画像よりも情報量が多い(画像からは出演者を読み取れない)。したがって、artists 列を丁寧に埋めることが、カードの情報量に直接つながる。ここは手を抜かないこと。


駐車場・最寄り駅の書き方(parking / nearest_station)

「くわしく」を開いた利用者が、車で行くか電車で行くかをその場で判断できるようにするための情報。会場公式サイトの「アクセス」ページに載っている内容をそのまま書き写す(憶測で埋めない)。

列 書き方
nearest_station 最寄り駅名と徒歩時間(例:国際展示場駅(徒歩7分))。使える駅が複数あるときは / 区切りで併記してよい
parking 駐車場の有無・台数・料金(例:あり(有料・300台)、契約駐車場なし(近隣コインパーキングを利用))。専用駐車場が無い会場は「無い」という事実を書く

同じ会場について、二度調べない・二度書かない

駐車場・最寄り駅は**「行の属性」ではなく「会場の属性」**なので、append_rows.py が同じ会場の別の行から自動で補完する。前回の lives.csv にある会場も、今回このセッションで既に書いた会場も、どちらからも引かれる。

  • ツアーの2公演目以降 → 空欄で渡す。自動で埋まる
  • 前回も公演があった会場 → 同上。調べ直さない
  • 初出の会場 → 公演ページ/会場ページを開いているそのついでに1回だけ拾い、1行だけ書けばよい

同じ箱の情報を公演の数だけ調べ直すのは、このタスクで最も起きやすい浪費である(ツアーは同じ会場が何度も出てくる)。

「たぶんあるだろう」という推測では書かない。 公式サイトにアクセス情報が無い、または確認できなかった場合は、両方とも空欄のままにする。空欄の行は「くわしく」にこの2項目が出ないだけで、表示は壊れない(第3.5節の欠損耐性)。


フェスの日割りラインナップ(data/lineups.csv)

フェスは1公演=1行なので、artists 列には主要8組しか入らない。それだけだとカードは

出演:相川七瀬・新しい学校のリーダーズ・AKB48・きゃりーぱみゅぱみゅ ほか155組

にしかならず、利用者が知りたい「自分の好きなアーティストは何日目に出るのか」に
1文字も答えられない。そこで全出演者を日ごとに data/lineups.csv へ書き出す
(設計書 第12.12節)。ここに書いた名前は検索の索引にも入るので、
artists に入らなかったアーティストの名前でもそのフェスが見つかるようになる。

対象

live_type: fes の行のうち、公式サイトが出演者一覧を出しているもの。
共演(event)や単独公演には要らない(artists 列で足りる)。

毎回、全フェスで「取得を試みたか」を確認する

日割り取得は検証で強制されない任意工程のため、確認せず省略すると誰にも気づかれないまま
定着する。そのため live_type: fes の行を1件ずつ棚卸しし、lineup_id の有無に関わらず今週取得を
試みたかを確認する(「品質チェック」節の「フェスの日割りラインナップ」の必須項目。
ステップ1で名簿のフェスを開くついでに確認すると二度手間にならない)。

試みて読めなかった(画像/JS配信のみ、robots.txtが公式サイト全体を拒否、等)場合は
lineup_id を空欄のまま進めてよい——「読めないものは書かない」の原則は変わらない。
変わるのは、確認せず静かに省略することを許さない点だけである。前回「取得不可」と
判定したフェスも、公式サイトが刷新されて日割りページが新設されている場合があるため、
lineup_id が空欄の fes 行は毎回再確認する。

日割りの書き込み手順(親のみ実行)

ここから先は親の仕事である。 lineups.csv に書くのは親だけで、サブエージェントは実行しない(append_lineup.py --init を子に実行させると、他の波が書いた分もろとも消える)。

  1. その公演の lives.csv 側の lineup_id を決める(例:luckyfes-2026)。
    前回すでに付けた値があるならそれを使う——ステップ0の書き戻しで一旦外れているので、
    自動では戻らない。 python3 tools/prev_rows.py lives --uid <uid> で前回の値を引き、
    本体行に明示的に書く(毎週書き直すと綴りが揺れ、表示側の参照が静かに切れる)
  2. lineups.csv の --init は、着手時(実行手順まとめの手順2)で済んでいる。ここで打ち直さない
    ——それまでに書いたフェスの日割りがまとめて消える
  3. フェス1本ぶんずつ、公式の並び順のまま、本体行と一緒に --rows で流し込む(下の注記。
    ステップ0で書き戻したフェス行は lineup_id を持たないので、--rows 無しでは参照先が無いとして落ちる)
python3 tools/append_lineup.py <<'EOF'
{"lineup_id":"luckyfes-2026","date":"2026-08-08","stage":"RAINBOW STAGE","artist":"TUBE","is_headliner":"1"}
{"lineup_id":"luckyfes-2026","date":"2026-08-08","stage":"RAINBOW STAGE","artist":"影山ヒロノブ"}
{"lineup_id":"luckyfes-2026","date":"2026-08-08","stage":"WING STAGE","artist":"NEWS"}
EOF

lives.csv が参照していない lineup_id を渡すと、このツールはその場で落ちる。
表示されない行を静かに書き込むより、書く前に止めるほうがよいためである。

日割りは --rows で lives.csv 側の行と1回の呼び出しにまとめる(ステップ0で書き戻したフェス行も lineup_id が外れているので、今回はじめて付けるのと同じ扱いになる)。
上の手順は「その公演の行が既に lives.csv にある」前提で、append_rows.py lives → append_lineup.py の2回に分かれる。
その間でセッションが打ち切られると、lineup_id を書いた行だけが残ってラインナップが1件も無い状態になる
(validate_data.py がERRORで捕まえる。2026-09-04 に実際に起きた——docs/DESIGN.md 第12.12節)。
temp/rows-<波の名前>.jsonl にその公演の行がまだあるうちに、次のようにまとめて書く。

python3 tools/append_lineup.py --rows temp/rows-festivals.jsonl <<'EOF'
{"lineup_id":"luckyfes-2026","date":"2026-08-08","stage":"RAINBOW STAGE","artist":"TUBE","is_headliner":"1"}
EOF

両方の検証が通ってから両方を書く(片方だけ書けた状態にはならない)。その波の
temp/rows-<波の名前>.jsonl を書き切るのに使う呼び出しはこれ1回でよい
——他の行(lineup_id
を持たない公演)も一緒に渡してよく、append_rows.py lives < temp/rows-<波の名前>.jsonl を
別途もう一度呼ぶ必要はない(呼ぶと同じ行を二重に書く)。

列 規則
lineup_id lives.csv 側と同じ値
date YYYY-MM-DD。公演の会期の中の日付(外れると validate_data.py がERROR)。日割りが未発表なら空欄。空欄の行は「日割り未発表」としてまとめて表示される
stage ステージ名。ステージ割りが未発表なら空欄でよい(その日の直下に並ぶ)
artist 出演者名。公式の表記をそのまま使う
is_headliner その日のトリだけ 1。公式の並びから明らかに読み取れるときだけ(メインステージの最終枠、または最上位表記)。分からなければ空欄
apple_music_url 手で書かない。 書き終えたあとに tools/fill_apple_music.py が一括で埋める(後述)。絞り込めなかった名前は空欄のまま残るのが正しい
note Opening Act ROJack 深夜公演・別チケット など、その枠の性質を一言で

並び順を崩さない

CSVの行順が、そのまま画面での並び順になる。 公式が出している並び
(トリが先頭、以下出演順/主催者が決めた序列)を保つこと。
五十音順やアルファベット順に直すと、その序列の情報が消える。

公式ページからの取り出し方(実測)

WebFetch はHTMLをmarkdown化してから渡すため、一覧の構造が失われやすい。
ページ本文を取って機械的に抜くほうが確実で、しかも検索の予算を一切消費しない。

python3 tools/fetch_page.py <ラインナップページのURL> --text
python3 tools/fetch_page.py <ラインナップページのURL> --links   # 日別ページに分かれている場合

出演者一覧は表かリストで組まれていることが多く、--text はどちらも構造を保つ
(表はタブ区切りの行になる)。生HTMLは要らない。

curl は使わない。 フックを通らないので robots.txt の判定も Crawl-delay の
消化も行われず、この文書が冒頭で禁じている行為そのものになる。fetch_page.py は
ゲートを通したうえで同じものを返す。

実際に効いた形:

形 取り出し方 例
Next.js の __NEXT_DATA__ <script id="__NEXT_DATA__"> のJSONに公式タイムテーブルが丸ごと入っている(日・ステージ・出演者・時刻) LuckyFes
専用のJSON API ページのJSから fetch() 先を探す(/api/get/artist/ 等) ROCK IN JAPAN FESTIVAL
日ごとの静的ページ .../lineup/tokyo-day1/ のように日ごとにURLが分かれている SUMMER SONIC
一覧に日のクラスが付いている data-category="day1 day2" / .firstday .secondday New Acoustic Camp・氣志團万博

フェス公式サイトも robots.txt の判定を通る。 data/festivals.csv のURLは
python3 tools/fetch_gate.py --roster festivals でまとめて事前確認でき、
ラインナップページを個別に開くときは fetch_page.py が取得の直前に判定する
(WebFetch の場合はフックが自動で判定する)。

読めないものは書かない

ラインナップを画像とJavaScriptでしか出していない公式サイトがある
(2026年時点の ULTRA JAPAN がこれ)。この場合は行を1件も作らず、lineup_id も空欄にする。
他所のまとめ記事の要約で埋めると、出演しないアーティストを載せる事故に直結する。
カードは従来どおり artists 列の出演者行だけを出すので、表示は壊れない。


Apple Music アーティストリンク(apple_music_url)

「くわしく」の中に、その公演のメインアーティスト1組の Apple Music アーティストページへのリンクを出す。単独公演では出演者本人、フェス・共演ではartists列の最初に置いた(=最も大きく扱われる)アーティストを対象とする。全出演者ぶんのリンクは持たない。

URLの規則性(調査結果)

Apple Music のアーティストページは https://music.apple.com/{ストアフロント}/artist/{スラッグ}/{アーティストID} という決まった形を取る(例:https://music.apple.com/jp/artist/yonezu-kenshi/1051352583)。{アーティストID} は名前から直接は導出できないが、iTunes Search API(Apple公式・無認証で使える検索API)で名前から引けるため、機械的にリンクを組み立てられる。

https://itunes.apple.com/search?term={アーティスト名(URLエンコード)}&entity=musicArtist&country=jp&limit=5

レスポンス中の results[].artistLinkUrl が、そのまま正しい形のアーティストページURLになっている。country=jp を付けるのは、ダッシュボードの利用者が日本のストアフロント(music.apple.com/jp/...)を期待するため。

apple_music_url の書き方の規則

  1. results の artistName が調べたい名前と完全一致し、primaryGenreName がそのアーティストのジャンルと矛盾しない候補を選ぶ。同姓同名・類似名の別アーティストと取り違えないこと。確信が持てなければ書かない
  2. Apple Music にページが無いアーティスト(配信を行っていない等)は空欄にする。存在しないページへのリンクを作らない
  3. artistLinkUrl はそのまま使う。末尾に付くことがあるトラッキング用のクエリ文字列(?uo=4 など)は取り除いてよい
  4. apple_music_url は https://music.apple.com/ から始まり /artist/ を含むURLであること(validate_data.py がこの形を検証し、外れているとERRORになる)
  5. 前回から続く行では叩き直さない。 空欄で渡せば前回値が自動で埋まる(append_rows.py の持ち越し)。前回値が別人のページだと分かったときだけ、正しいURLを書くか、確信が持てなければ "_no_carry": "apple_music_url" を添えて空欄に戻す——空欄で渡すだけでは前回値が埋め直される

ラインナップ側(lineups.csv)は、モデルではなくツールが埋める

lineups.csv には数百組が並ぶ。この列を1件ずつ手で引いてはいけない。
名前からURLへの写像は判断を要さない決定論的な処理なので、専用のツールに任せる。

python3 tools/append_lineup.py ...   # 先にラインナップを書き終える
python3 tools/fill_apple_music.py    # そのうえで、この1コマンド
  • append_lineup.py を流し終えたあとに実行する(CSVに無い名前は引きようがない)
  • 結果は data/apple-music.json に残り、2週目以降は新しく出てきた名前だけを引く。
    数百件を毎週引き直すことにはならない
  • 候補が1件に絞れない名前(HANA yama のような同名の別アーティスト)は
    空欄のまま残る。これは正しい挙動で、埋めようとして手で選んではいけない
  • 検索URL(music.apple.com/jp/search?...)をCSVに書いてはいけない。
    validate_data.py が /artist/ を含まないURLをERRORで弾く。名前から機械的に作れるものを
    データに持つと、アーティスト名を直したときにURLだけ古い名前のまま残る

アーティスト名は、公式の表記どおりに写すこと。 公式サイトの見出しが大文字で
組まれていても、原名が小文字なら小文字で書く(keshi を KESHI と書かない)。
表示がそのまま変わるうえ、fill_apple_music.py の照合精度も落ちる。

lives.csv 側(各公演のメインアーティスト1組)は、これまでどおり収集の流れの中で
APIを1回叩いて確かめる。こちらはツールの対象外である。

実行環境の前提

itunes.apple.com に届かない場合(ネットワークポリシー、または fetch_gate.py の
拒否)は、この工程を実行できない。その場合は**apple_music_url を空欄のままにし、
報告にその旨を書く**こと。fill_apple_music.py は届かなければそこで止まり、
それまでの結果を保存して終了する(収集全体は止めない)。

空欄が多いことは品質の問題ではない——存在確認をしていないリンクを1件載せることの方が、はるかに深刻な品質問題である(「価格・クーポン記載の絶対規則」と同じ考え方)。

なぜ itunes.apple.com を開けるのか。 このホストの robots.txt は /search* を
Disallow しているが、fetch_gate.py は文書化された公開APIとして例外に
登録している。判断の根拠と、例外を広げるときの条件は
docs/COLLECTION-PROTOCOL.md 6.5.7 にある。ここを読まずに例外を増やさないこと。


desc の書き方

100〜150字で書く。この列だけが「行くかどうか」を決める材料である。

利用者はタイトル・会場・日付をカードの表面で読み終えたうえで「くわしく」を開く。そこに出るのが desc である。だから表面に出ている事実を言い換えた文には、書く価値が無い——title に「◯◯ツアー 追加公演」とあるのに desc が「◯◯のツアーの追加公演」で終わる行は、ボタンを開かせただけで何も渡していない。

書くべきこと —— 「その日にしかないもの」を1つ以上

事実を並べるのではなく、この公演を他の公演と取り違えられなくする一点を必ず入れる。

層 拾うもの
その公演の固有性 ツアーファイナル/初の単独公演/解散・活動休止前の最後/何周年の節目/その会場で初めて/再結成後初
音楽的な中身 どのアルバムを携えたツアーか、全曲演奏・弾き語り・オーケストラ編成など編成や演目の特殊性
その日だけの座組 ゲスト、対バンの相手、フェスのヘッドライナーと目玉の組み合わせ
体験としての手触り 会場の規模感との対比(アリーナ規模のアーティストが小箱で、など)、野外・レイトショー・オールナイト

アーティストを知らない人にも通じるように、1節だけ「何者か」を添える。 「7年ぶりの来日」と書いても、その7年の意味が分からなければ刺さらない。グラミー受賞の〜 〇〇の主題歌で知られる〜 のように、検索して確認できた事実で位置づける(推測で書かない。第7.2.3節と同じ規則がこの列にも効く)。

悪い例

❌ 人気アーティストのライブです。ファン必見の公演となっています。
❌ ツアー「透明」武道館公演の5日目。9月に組まれた4公演に続く、10月の2デイズの初日にあたる公演。
  → 日程表の言い換えしかない。何日目かは利用者の関心ではない。
❌ 英国の名オルガニスト、トーマス・トロッターによるリサイタル。パイプオルガンシリーズ企画の第26回。
  → タイトルに書いてあることの繰り返しで、この回に何を弾くのかが無い。

良い例

✅ デビュー20周年を締めくくる全国ツアーの東京ファイナル。初期のシングルを中心にした選曲で、ツアー中の公演では演奏していない曲を入れると本人が明言している。この日限りのゲスト出演も告知済み。全席指定。
✅ 7年ぶりとなる来日公演。グラミー賞4部門を受賞した前作を携えたワールドツアーの一環で、日本ではドーム1会場のみ。スペシャルゲストに国内のヒップホップユニットが付く。
✅ 3ステージ制の都市型フェス。ヘッドライナー2組を含む計28組が出演し、うち5組は今回が初来日。フードエリアは入場券なしでも使える。雨天決行・荒天中止。
✅ バッハの「フーガの技法」全曲を1晩で弾き切るプログラム。国内では演奏機会の少ない曲集で、演奏者自身が30年ぶりに全曲に取り組むと告知している。

含めないもの

  • 主催者の宣伝文句をそのまま転記したもの
  • 「必見」「感動」「圧巻」といった評価語(事実で語れば、評価語は要らない)
  • 出典サイトの文章のコピー(必ず自分の言葉で書き直す。著作権に配慮)
  • 表面に出ている情報の言い換え(会場名・日付・料金・受付状況)

埋まらないときは、無理に伸ばさない

情報源にその公演の中身が書かれていないことは実際にある。字数を満たすために推測や評価語で薄めるのは、短い desc より悪い。 確認できた事実だけで数十字にしかならなければ、それで書いて次の行へ移る。report_stats.py の下限は「新規行の半分が60字以上」なので、こうなる行が少数あること自体は通る(全部がそうなるなら、調べ方のほうが間違っている)。ただし空欄で通す前に、official_url を1回は開くこと——会場の一覧ページの一文だけで済ませると、この列は必ず薄くなる。

再確認の回で desc を上書きしない。 会場の「今月の公演一覧」のような一覧ページには薄い一文しか無く、それで前回の説明を潰す退行が実際に起きている(docs/DESIGN.md 第9.3.6節)。前回の desc より短くなるなら、書き換えずに "_carry": "desc" で持ち越す。


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

完走

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

予算

  • 名簿の会場・フェスを --urls のURLで直接開いたか(URLが分かっている先を検索し直していないか)
  • 前回から続く行を、前回の url / official_url に直行して確認したか
  • lat/lng を空欄で渡したか(venues.csv にある会場を調べ直していないか)
  • parking / nearest_station を、同じ会場について公演ごとに調べ直していないか(1行書けば残りは自動補完される)
  • SOLD OUT と確認できた公演について、別の受付経路を探し続けていないか
  • apple_music_url を、API1回で確信が持てないまま粘っていないか(空欄が正しい)
  • 補助列(kana tour_id 等)を埋めるために追加の検索をしていないか
  • 価格比較を行っていないか(このタブでは行わない)
  • python3 tools/budget.py --report --verbose で工程別の消費を確認し、報告に含めたか(記憶で書いていないか)
  • 撤退を判断する前に、残り時間を確認したか(上限6時間。まだ余っているのに早く畳んでいないか)
  • 一覧の構造が意味を持つページを、fetch_page.py --text ではなく WebFetch で読んで崩していないか
  • 予算切れで工程を打ち切った場合、その事実を報告に書いたか(件数を埋めるために未検証の行を足していないか)
  • 再確認できずに前回値で持ち越した tier C の行について、onsale_* を空欄にしたか

差分・名簿

  • python3 tools/purge_ended.py lives を、--dispose より先に実行したか
  • python3 tools/diff_data.py lives が終了コード0で通るか(説明のない消滅が0件か)
  • 中止・延期を cancelled として記録したか(ended や notfound で片付けていないか。expired は purge_ended.py が公演日超過を機械的に検出したときだけ付くもので、中止の代わりに使わない)
  • notfound は、会場ページとプレイガイドの両方に当たったうえで残したものか
  • 「表記が変わった可能性」に挙がったペアを確認し、同じ公演なら renamed を記録したか
  • is_additional=1 の行が、差分または公演名の明記に基づいているか(雰囲気で立てていないか)
  • announced_date を、発表記事の日付または差分に基づいて決めたか
  • _carry: "*" を使った行は、内容に変更がないことを実際に確認した行だけか(とくに artists)
  • append_rows.py が挙げた「名簿に無い会場」を確認し、継続的に公演がある箱を --add したか(会場の収穫の記録は自動。フェスは roster.py festivals --hit を手で打つ——フェス名は venue 列に出ないため)
  • roster.py venues --gc と roster.py festivals --gc を実行したか
  • 名簿に無い会場・フェスを見つけたら --add したか(報告だけで終わらせていないか)
  • python3 tools/report_stats.py lives を実行し、中核列の充足率が前回より落ちていないか確認したか(落ちていれば理由を報告に書く。原因が目標件数・品質基準・手順の側にあるなら、報告に書くだけで終えず docs/skill-feedback.md にも提案を残したか——書かないと、同じ理由で毎週落ち続けても誰も気づけない)
  • 「今週かならず産む成果」の下限に届いたか。届かなかった項目を報告に書いたか
  • 目標件数・品質基準・禁止事項に触れるルール変更に気づいたら、自分で書き換えず docs/skill-feedback.md に書いたか(それ以外の小さなバグ修正は自分で直してよい。出力先の章を参照)

形式

  • python3 tools/validate_data.py を実行し、ERROR が0件か

  • 全行の列数が揃っているか(validate_data.py がヘッダーの不一致を検出する。列数をこのチェックリストに書き写さないこと——列は増えるので、書き写した数字は必ず古くなる)

  • parking/nearest_station は会場公式サイトの「アクセス」ページ等で確認したものだけを書いているか(不明なら空欄)

  • apple_music_url は iTunes Search API の artistLinkUrl で裏取りしたものだけを書いているか(同姓同名の別アーティストと取り違えていないか)

  • id が1から連番になっているか

  • start_date / end_date / announced_date / onsale_start / onsale_end が YYYY-MM-DD 形式か、または空欄か

  • pref が8キーのいずれかか

  • genre が定義済み10キーのみか

  • live_type が定義済み5キーのみか、かつ1つだけか

  • date に自由記述を残した行が、本当に ISO の日付へ落とせない日程だけになっているか(validate_data.py が該当行を WARNING で挙げる)

ライブ固有

  • venue が data/venues.csv の venue 列と完全一致しているか(一致しない会場は roster.py venues --add で名簿に追加する。表記ゆれなら公演行の側を名簿の表記に合わせる)
  • 同じ「会場 × 公演日」の行が重複していないか
  • ツアーが1行にまとめられていないか(venue に | が入っていないか)
  • 同じツアーの複数公演に、同じ tour_id が振られているか
  • onsale_end が start_date(公演日)より後になっている矛盾行がないか
  • onsale_start_time / onsale_end_time が HH:MM(24時間表記)か、または空欄か
  • onsale_start を書いた行に、開始時刻を確認したか(先着順の販売では時刻が本体。分からなければ空欄)
  • onsale_end を書いた行の締切時刻を確認したか(23:59 と決めつけていないか)
  • 過去に終わった受付しか分からない行で、onsale_label に 受付終了 と書いていないか(空欄が正しい)
  • limited_sale に内容と日時の両方が入っているか
  • limited_sale を、確認していない公演の行にコピーしていないか
  • 逆に、ツアー共通の案内を1行にだけ書いて「この公演だけ特別」に見せていないか
  • limited_sale に二次流通が混ざっていないか(公式リセールのみ)
  • tour_id 単位で全公演の受付状況を並べて確認したか
  • end_date が start_date より前になっている行がないか
  • 公演日が実行日より前の行が混ざっていないか
  • is_additional=1 の行が、前回CSVとの突き合わせに基づいているか(雰囲気で立てていないか)
  • date 列に開場・開演時刻が入っているか(判明している公演について)

フェスの日割りラインナップ(lineups.csv)

  • lineup_id が空欄の fes 行を含め、名簿の全フェスについて今週ラインナップ取得を試みたか(前回「取得不可」と判定したフェスも、公式サイトが変わっていないか再確認したか。「フェスの日割りラインナップ」章の「毎回、全フェスで『取得を試みたか』を確認する」参照)
  • 公式が出演者一覧を出している fes の行に、lineup_id と対応するラインナップがあるか
  • lineup_id は前回と同じ値か(毎週振り直していないか。綴りが変わると参照が切れる)
  • 公式ページから機械的に取り出したものだけか(まとめ記事の要約で埋めていないか)
  • 並び順が公式のままか(五十音順に並べ替えていないか)
  • 日割り未発表のアーティストを、推測で日に割り振っていないか(date は空欄が正しい)
  • python3 tools/fill_apple_music.py を実行したか(ラインナップを書き終えたあと。検索の予算を使わないので、予算切れは飛ばす理由にならない)
  • apple_music_url を手で書いていないか(ツールの結果だけ。絞り込めなかった名前は空欄が正しい)
  • apple_music_url に検索URL(/search?)を書いていないか(アーティストページのみ)

安全性

  • url に二次流通・転売サイトが1件も混ざっていないか(全URLのドメインを目視確認する)
  • onsale_label が SOLD OUT の行の url が、正規の公演ページになっているか
  • 画像URLの列を勝手に追加していないか
  • 推測・相場・前回からの流用で埋めた日付・時刻・料金が1件もないか

バランス

  • 関東7都県すべてから3件以上あるか
  • 隣接県(pref="other")が全体の1割程度に収まっているか(超えていれば関東側の探索不足を疑う)
  • 東京の件数が全体の半分以下か
  • genre が7種以上カバーされているか
  • ホール級・ライブハウス級が全体の1/3以上あるか
  • desc が一般論で終わっていないか
  • desc がタイトル・会場・日付の言い換えで終わっている行を残していないか(「◯日目」「第N回」だけの行)
  • report_stats.py --check-fresh が desc の下限で落ちていないか(新規行の desc が60字以上あるかを見ている。散文の目標は100〜150字で、下限はその手前に置いてある)

実行手順まとめ

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

とくに手順16(fill_apple_music.py)を飛ばさないこと。 検索を1回も使わないので予算切れは理由にならない(「完走条件」の 3 の注記)。

手順2〜4は検索を1回も使わない土台作りである。ここを通した時点で lives.csv は先週の完全な状態に戻り、以降の調査はすべて上積みになる(日割り lineups.csv だけは戻らない。今週調べたフェスのぶんを手順6で書き直す)。

  1. python3 tools/check_robots.py と python3 tools/fetch_gate.py --roster venues を実行し、調査元サイトと名簿URLの robots.txt を確認する(検索予算は消費しない。拒否されたURLは対象から外す。個々のページの可否は、取得のたびにフックが自動で判定する)。あわせて「調査予算」の工程別の枠を確認し、python3 tools/budget.py --report で今回の消費と**残り時間(上限6時間)**を見る
  2. 実行日を確認し、対象期間(公演日3ヶ月以内 または 受付締切3ヶ月以内)を確定する。サブエージェントを使うなら、ここで python3 tools/skill_brief.py lives --out temp/brief-lives.md を1回だけ実行しておく(波の指示にはこのパスを Read させる1文だけを書く。抜粋を貼らない)
  3. python3 tools/append_rows.py lives --init と python3 tools/append_lineup.py --init(lives.csv の前回分は data/.prev/ に自動退避される。先に --init してから棚卸しを取ること——先週の carried.jsonl が今回分に更新されるのは --init の中でなので、順序を逆にすると「終了日を過ぎたまま未確認の行」が1週遅れて表示される)
  4. python3 tools/prev_rows.py lives --carry-rest --apply で土台を作る(省略禁止)。 終了日を過ぎた行は expired として処分され、残りは前回値のまま書き戻る(受付欄・限定枠は空になる。フェス行の lineup_id も外れる)。この1コマンドが「期間切れの削除」と「前回分の引き継ぎ」をまとめて行う。 先に通しておけば、調査はどこで打ち切っても「今週増やせたはずの分」しか失わない
  5. python3 tools/validate_data.py --duplicates-only で重複を確かめ、解消する。 完全重複(同じ uid の行が複数)は手順3のバッチが自動で1行に畳むので通常0件。重複の候補(uid は違う)は中身を見て判断し、同じ公演なら片方を --dispose の merged で処分する
  6. python3 tools/prev_rows.py lives --worklist --summary で前回分の棚卸しを取る(省略禁止)。--summary を付けること——最優先の行(終了日を過ぎたまま未確認)と都県ごとの件数だけが出て、数百行の一覧そのものは出ない。一覧は親の文脈に置くと毎ターン再送されるので(events で28,922字)、担当分は「並行して調べてよい」章のとおり --pref <都県> > temp/worklist-lives-<都県>.md でファイルに書いて子に渡す。個別の行が要るときは --uid <uid> で引く。先頭に出る「終了日を過ぎたまま未確認の行」を最初に片付ける。終了日を過ぎた行はこの一覧に出ない——日付を比べるだけで確定するので、調査の枠も文脈もそこに使わない(末尾に除いた件数が出る。この除外が、今週の調査対象から終了済みの行を実質的に外す工程にあたる。実際の削除は手順3の --carry-rest --apply が済ませている)
  7. python3 tools/roster.py festivals --list --urls でフェスを確認し、開催情報・出演者発表の更新を拾う。名簿に無いフェスも探す(枠20回。主にこの探索に使う) フェスを調べた波を受け取ったら、日割りをその場で python3 tools/append_lineup.py --rows temp/rows-<波の名前>.jsonl で本体行と一緒に書く(手順3でフェス行の lineup_id は外れているので、--rows 無しでは落ちる。lineup_id は prev_rows.py lives --uid <uid> で前回の値を引いて本体行に明示する。「日割りの書き込み手順」)
  8. python3 tools/roster.py venues --list --pref {都県} --urls で調査対象を引き、東京→神奈川→埼玉→他都道府県の順に、4列目のURLを直接開いて「既存行の再確認」と「新規の発見」を同じ1回の調査で済ませる(検索の枠5回)。会場スケジュールは python3 tools/fetch_page.py <URL> --schedule で取る——WebFetch の markdown 化は公演名と日付の対応を崩す。--schedule は日付行とその日にぶら下がる行だけを残すので、本文が3〜4割に減ったうえで公演名と日付の対応は保たれる(欲しい公演が出ていなければ --text に戻す)。収穫の記録は append_rows.py が自動で行う
  9. プレイガイド・音楽メディアで、小箱公演・急な公演を補完する(枠20回)
  10. 音楽メディアの「追加公演」記事を定点観測し、diff_data.py の差分と突き合わせて announced_date / is_additional を確定する(枠20回・このタスクの主成果なので削らない)
  11. 各公演について公式情報まで辿り、日時・会場・出演者・料金を確定する(枠75回・新規行のみ。前回から続く行は前回の url/official_url に直行し、変わりうる項目(受付・中止の有無・限定枠)だけを確認して残りは _carry で持ち越す)。必須列が揃った行はそこで打ち切る 前回行は手順3で既にCSVに戻っているので、中止・延期・対象外になっていた公演は子に temp/dispose-lives-<波の名前>.jsonl へ書かせる(「サブエージェントへの指示に必ず含めること」6)
  12. 受付の開始・締切を時刻まで確定する(onsale_start_time / onsale_end_time)。SOLD OUT・全受付終了と確認できた公演は、そこで確定として別経路を探さない
  13. tour_id 単位で全公演の受付状況を並べ、一部の公演にだけ出ている枠を limited_sale に記録する(ツアーで公演ごとに確認し直す価値があるのは、この受付状況と限定枠だけである)
  14. 転売サイトを除外し、正規経路の url を選定する
  15. 駐車場・最寄り駅は初出の会場について1行だけ書く(残りは自動補完される)。lat/lng は空欄で渡す。メインアーティストの Apple Music アーティストページは iTunes Search API を1回叩き、確信が持てなければ空欄にする(これは lives.csv 側の話で、日割り側は下の手順16でツールが埋める)
  16. python3 tools/report_stats.py lives --json で中核列の充足率を見て、低い列を会場スケジュール・プレイガイドの公演ページ単位で埋める(とくに手順3で空になった onsale_*。残りの予算を全部ここに使ってよい)。予算が尽きたらそこで畳んでよい——土台は既に出来ているので、飛ばしてもCSVは壊れない
  17. python3 tools/fill_apple_music.py で日割りの Apple Music リンクを埋める(手順6で書いた名前のぶんだけ埋まる。検索の予算を消費しないので、予算切れを理由に飛ばさない。「Apple Music アーティストリンク」章)
  18. python3 tools/purge_ended.py lives(手順3で同じ処分が済んでいるので通常は0件。調査中に書いた行の保険。消した行には expired の処分が自動で付き、参照していた lineups.csv の行も一緒に片付く)
  19. 子が書いた temp/dispose-lives-*.jsonl を python3 tools/prev_rows.py lives --dispose に通し(中止・延期・対象外の公演を取り除く)、python3 tools/diff_data.py lives --allow-unexplained --allow-noop で差分を一覧して [表記が変わった可能性] を確かめる(出力を切らない)。同じ公演だと分かった組は renamed として記録する。手順3で前回行を全部書き戻しているので、表記が変わった公演は「旧uidの持ち越し行」と「新uidの新規行」の両方がCSVに残る——renamed を記録すると --dispose が旧uidの行を取り除く(notfound だけは行を残す)。ended は end_date が実行日より前の行にしか使えず、1回の呼び出しで8件まで
  20. python3 tools/roster.py venues --gc と python3 tools/roster.py festivals --gc で名簿を整理する
  21. python3 tools/diff_data.py lives と python3 tools/validate_data.py を実行し、どちらも終了コード0であることを確認する(diff_data.py は収穫が0件でも落ちる)
  22. python3 tools/report_stats.py lives --check で充足率・都県の網羅性を確認し、python3 tools/festival_gate.py でフェス名簿との整合を確認する(どちらも落ちたら、まず実際に調べ直す。調べたうえで今回は無いと確認できたなら --allow-short / --allow で承知したことにする。「終了工程」表の注記を参照)
  23. 最後に、以下の要点だけを簡潔に出力する(あいさつや前置き、次の提案などの会話的な文章は付けない)
    • 件数・都県別内訳・ジャンル別内訳・会場規模別内訳(report_stats.py の出力をそのまま使う)
  • 中核列の充足率と前回比(とくに onsale_end。落ちた列があれば理由も)
  • 今週あらたに発表された公演/追加公演の一覧(このタスクの主成果。diff_data.py の [新規] を根拠にする)
  • 今週締切が来る受付の一覧(日付だけでなく時刻も添えること)
  • 今週から発売が始まる公演の一覧(発売日時つき)
  • 受付が次の段階に進んだ公演(diff_data.py の [変更] に onsale_* が出た行。[前回値のまま持ち越し・未確認] は受付が進んだのではないので含めない)
  • ツアー内で販売状況が分かれている公演(一部だけ完売/一部にだけ限定枠、など)
  • --carry-rest(手順3)と purge_ended.py が expired として処分した件数(多いなら、tier A の再確認が追いついていない兆候として書く)
  • 前回から消えた公演(ended / cancelled を区別して。中止は件数だけでなく一覧も出す。子の報告(temp/dispose-lives-*.jsonl)で取り除いた分を含む)
  • notfound として前回値のまま持ち越した公演(行は削除しない。件数と一覧を出す。来週 tier A で再確認する)
  • 日割りラインナップを書いたフェスと、その組数・日数(書けなかったフェスは今週は日割りが表示されない。理由も。「公式が画像でしか出していない」等)
  • 名簿の変化(追加した会場・フェス/retired にしたもの/休館の登録/--gc の結果)
  • 工程別の消費と、予算切れで打ち切った工程(budget.py --report --verbose の出力をそのまま貼る。打ち切りが無ければ「なし」と書く)
  • 「今週かならず産む成果」の下限に届いたか(とくに tier A の行で onsale_end を確認できなかった件数)
  • 再確認できず前回値のまま残った公演の数(diff_data.py の [前回値のまま持ち越し・未確認])
  • 手順15で埋めた欠けの件数と、埋められなかった列
  • docs/skill-feedback.md に書いた提案があれば、その要旨