← 一覧に戻る

footballnext-operation

GitHub ↗ HTML 最終push: 2026/8/23 21:24

WIP(現在進行中)

Work In Progress

このプロジェクトで現在進行中の作業と、過去のスナップショットを記録する。

現在の状況

テーマ1(ダイジェスト・第8次FB(項目39〜47・仕様v2.17=試合状態/スコア表示/代表経由)対応完了=serverデプロイ・ビルド16配布済み・返信文作成済み/残りは返信+比較画像のYou送付→トムソンのビルド16確認→設定戻し(トムソン側)→App Store提出)

  • 経緯: 07-22実装完走 → 07-23運用適用 → 08-01〜08-19 FB1〜7次(ビルド9〜15) → 08-23 第8次FB(シート項目39〜47・9件+仕様書v2.17。トムソンが進行中の実画面を初確認しての指摘)を全消化・5.6.0(16)配布・返信文作成(DEVLOG 08-23 21:21)。今回から各項目の「修正前|修正後」比較画像を作成fb-items39-47-shots/compare/ 9枚+#46パルス動画)。
  • 本日のコミット(第8次分): server 329b484=①#45根本修正: getPlayerMatches が代表戦にも海外組14大会フィルタを適用→代表5大会と交差ゼロで national_recent が常に空だった(経過日数条件ではない)。チーム別大会集合に切替。ビルド15でも直る ②#40: livesに lineup_announced 追加配信(旧ビルドは未知キーを無視=無影響。caption_id=4新設案は旧ビルド表示崩れのため撤回=DECISIONS 08-23)。iOS 926f17f=#39 ラベルを選手ブロックstateから導出しPRE_LINEUPは青#3c76d3ピル「スタメン発表」/#41 チップ共通固定幅/#42 ダイジェスト選手欄の状態テキスト削除/#43・#44 スコア所属クラブ基準(perspectiveScore・シェア画像も統一)/#46 状態ピル3色+LIVE赤●パルス(DigestFixtureStatePill)/#47 キックオフ前「vs」/#40 青「スタメン」切替(Realm schemaVersion 4→5)。
  • 検証: 本番データで#43(鎌田/冨安 0-2)・#44・#45(冨安の代表直近3試合表示)・#47 目視PASS。#40はAPI監視スクリプトで21:03のPSV戦スタメン発表を捕捉し「赤LIVE|青スタメン|黄時刻」3状態同居の実画面で確認。パルスは録画フレーム抽出で動作確認。ビルド16はASCで processingState=VALID 確認済み(採番はASC実数15→16)。
  • 次にやること: ①返信文 トムソン返信_項目39-47対応完了_20260823.md +比較画像9枚+ after/46_LIVEパルス動画.mp4 のYou送付 ②トムソンのビルド16確認待ち(項目4・5・9・10の「本番確認待ち」継続。#22ザルツブルク国旗も継続)③設定4項目戻し(90/30/30/65→3/1/7/1・トムソン側)④App Store提出(xcarchiveはscratchpad一時領域のため提出時は再アーカイブ)。
  • 注意: 本番 /api4/jpPlayer が間欠的に30秒超→タイムアウトする事象(リトライ即成功・未調査)。SSH用SGは回線IP変化のたびにport22へ現IP追加が必要。ビルド番号はWIPの記録を鵜呑みにせずASC APIで実数確認してから採番全ビルド共有のJSON(S3 lives等)へのenum新値追加は旧ビルドを壊しうる→追加フィールド方式が安全
  • フォロー項目: needs_review 22名の精査。004(説明文UPDATE)の本番適用確認。jpPlayer遅延の原因調査。MinimumOSVersion引き上げ(2027年春以降15.0未満アップロード不可・現在13.0)。
  • seed SQL正本: digest-jp-players-seed_20260723.sql。crontabバックアップ: ~/crontab_backup_20260723.txt。sim検証手順(ダミーJSON差し込み・シェア画像デバッグ書き出し)はDEVLOG 08-05/08-08参照。ダイジェスト系デバッグ起動引数の全一覧はDEVLOG 08-23参照(-DigestLiveFixturePath 等)。DB直結はWeb1号機(3.112.232.196)SSHトンネル・API-Footballキーはserverリポジトリ ApiFootball.php

テーマ2(起動時広告A/B v9・enrollment窓クローズ済み(8/17)・次は #37 9/15 本解析)

  • 実験は v9(min_build=50505・窓 7/6〜8/17・config_version=9)で走り、2026-08-17 00:00 JST に enrollment 窓が正常クローズ。#36(08-17実施・全PASS)で確認: S3稼働config=v9のままローカル正本と完全一致・GA4の enrollment_day は 08-16 が最終・08-17 の enroll 0件=canEnroll 窓ゲート機能。
  • クリーンコホート最終規模: 計3,141人(A=1,548/B=1,593=49.3/50.7%)・SRMなし(χ²≈0.65・p≈0.42)。実績約75人/日で見込み約4,000人よりやや小さい着地=9月本解析の検出力はこの実数が前提。
  • #36 残差率 中間再read PASS(ガードレール④維持)(cv_9・成熟 target_cohort_day 07-08〜08-14): A=5.51%(10,796/196,022)・B=3.84%(7,540/196,385)=絶対両方<10% ✓・群間差1.67pp<3pp ✓。日次10%超えゼロ。#31同範囲の再現一致(A5.37/B3.66)。
  • 要留意(本解析向け): 7/23以降は汚染コホートが D28+ ゲートで窓外に抜け母数急減(→600〜800件/日)。クリーン主体期(07-23〜08-14)は残差率 A=7.07%/B=5.81%(差1.26pp・基準内)とやや高め=この水準感が本解析の前提。
  • 参考(非確定・途中判定しない方針維持): 未到達離脱率 A=18.09%/B=14.76%(B 3.33pp低・仮説の向き・first-look/#31 と3回一貫)。クリーン主体期のみだと A=17.34%/B=15.49%(差1.85pp)=新規ユーザーでは効果量が縮む兆候。D7/D14・円/人はユーザー単位 n≈1,550/群で検出力小さめ=本解析は funnel系中心+円/人の点推定と幅で判断の見込み。
  • 次にやること: dev-timer #37(9/15)本解析(残差率確定・未到達離脱率本判定・円/人・D7/D14)。母集団 enrollment_day>=2026-07-06 かつ cv_9。
  • site-ad-scoring WIP は ios stash@{0} 温存(別レーン)。repoルートに codex-site-ad 系の未コミットファイルあり(別レーン・据え置き)。

テーマ3(実況板リーグ追加+海外組チーム紐づけ・2026-08-04 クローズ=残タスク全消化

  • リーグ追加ポータル化・3リーグ投入・Edit画面チーム検索UI(server 602de4c)は08-01完了。残っていた実操作は08-04に全消化: 7名紐づけ(You実施済みと判明)・J組3名紐づけ・コペンハーゲン is_publish=1・スーペルリーガ20試合取り込み。以後の紐づけ・確認はテーマ1側で管理。

別レーン・保留中(詳細は ROADMAP と過去アーカイブ)

  • サーバ側の要対応: RDS SG 3306/tcp 全開放・DBパスワード平文・cron EC2 AMI 2018.03 EOL(ROADMAP 今月/今四半期)。
  • インフラ継続: API GW 飛び地の原点直叩き移行・OshiraseViewController デッドコード整理・rss-crawler の cron 一元化。
  • A/B実装の経緯(レビューround1〜10・点火前チェックリスト・実機スモーク・凍結仕様・型ズレ修正)は下記 2026-07-08 スナップショットと SPEC.md / runbook を参照。

過去のWIPアーカイブ

(新しい「現在の状況」を書く前に、古いものをここに追記でアーカイブする。新しいものが上)

2026-08-23 21:21 時点: テーマ2・テーマ3は変更なし(アーカイブ省略)。テーマ1を書き換え(旧: 「第7次FB(項目25〜38・仕様v2.16)対応完了=ビルド15配布・返信送付済み/ビルド15確認待ち」→ 08-23にトムソンが進行中の実画面を確認して第8次FB(シート項目39〜47+仕様書v2.17)を受領し全消化: #45=サーバーの大会フィルタが代表戦を全滅させnational_recent常時空だった根本修正、#40=livesへのlineup_announced追加配信+青「スタメン」切替(caption_id=4案は旧ビルド互換で撤回)、#39/41/42/43/44/46/47=試合状態ピル・チップ幅・スコア所属クラブ基準・パルス等のiOS表示改修。今回から全項目のbefore/after比較画像を作成。server 329b484 デプロイ・iOS 926f17f・ビルド16配布(VALID)・返信文作成まで完了し「返信送付待ち→ビルド16確認待ち」へ。旧テーマ1本文の詳細はDEVLOG 08-19 21:41参照)。

2026-08-19 21:41 時点: テーマ2・テーマ3は変更なし(アーカイブ省略)。テーマ1を書き換え(旧: 「第6次FB(項目22・23)対応完了=ビルド13配布・返信送付済み/ビルド13確認待ち」→ 08-10に別セッションでバッジ#24修正・ビルド14配布があったと判明(DEVLOG未記録)。08-19に第7次FB(シート項目25〜38+仕様書v2.16)を受領し全消化: #25=API側スタッツ配信遅延×1回きりbackfillの穴を特定しデータ修復+72h窓リトライ化、#26〜31=試合一覧のチームロゴ・揃え・2名表示安定化・リーグロゴ縮小、#33〜38=ダイジェスト2セクションのロゴ+クラブ別グループ表示。server efdb9dc デプロイ・iOS ee88a7d・ビルド15配布・返信送付まで完了し「ビルド15確認待ち」へ。旧テーマ1本文の詳細はDEVLOG 08-08 19:47参照)。

2026-08-17 16:11 時点: テーマ1・テーマ3は変更なし(アーカイブ省略)。テーマ2を書き換え(旧: 「v9稼働監視フェーズ・次チェックは #36 8/17 窓クローズ」=#30/#31の結果詳細を含む版 → 08-17に #36 実施=窓クローズ正常・SRMなし(最終規模3,141人)・残差率中間再read PASS を確認し「窓クローズ済み・次は #37 9/15 本解析」へ。#30/#31 の詳細は DEVLOG 2026-07-11/07-22 参照)。

2026-08-08 19:47 時点: テーマ2・テーマ3は変更なし(アーカイブ省略)。テーマ1を書き換え(旧: 「第5次FB(仕様v2.14 項目19〜21)対応完了=ビルド12配布・返信送付済み」→ 同日中にトムソンのMVP順位再変更提案へ逆提案(欠損下位の共通並び)で合意し、項目22(ザルツブルク国旗=country_code全クラブnull配信の根本修正+NEC登録漏れ)・項目23(並び順・MVP共通化)+仕様書v2.15を全消化。デプロイ・ビルド13配布・返信送付まで完了し「ビルド13確認待ち」へ)。

2026-08-08 13:22 時点: テーマ2・テーマ3は変更なし(アーカイブ省略)。テーマ1を書き換え(旧: 「第4次FB(仕様v2.12 項目13〜18)対応完了=ビルド11配布・DM返信済み」→ 08-07にビルド11確認の過程で試合一覧の選手名非表示2件の指摘→調査回答、08-08に第5次FB(シート項目19〜21+仕様書v2.14)を受領し全消化。対象大会分割+MVP選定ルール+シェア画像右揃えを実装・デプロイ・ビルド12配布・返信送付まで完了し「ビルド12確認待ち」へ)。

2026-08-05 19:14 時点: テーマ2・テーマ3は変更なし(アーカイブ省略)。テーマ1を書き換え(旧: 「第3次FB(仕様v2.11)フル対応完了=ビルド10配布済み・DM送付待ち」→ 同日中にトムソンのビルド10確認OK+第4次FB(シート項目13〜18+仕様書v2.12)を受領し全消化。シェアレーン温存差分もビルド11へ統合して配布・DM返信済み。「トムソン確認待ち」へ)。

2026-08-05 16:49 時点: テーマ2・テーマ3は変更なし(アーカイブ省略)。テーマ1を書き換え(旧: 「第2次FB対応完了=ビルド9配布済み・トムソン最終確認待ち」→ 08-05に第3次FB(仕様v2.11 項目6〜12)全消化・試合タブ空表示バグ修正・リーグ国旗実装・クラブLINEUP/LIVE差異修正・ビルド10配布まで完了し「DM送付待ち」へ。ビルド9は試合タブバグ残存のため確認対象から外す)。

2026-08-04 18:27 時点: テーマ2は変更なし(アーカイブ省略)。テーマ1を書き換え(旧: 「FB1回目対応済み・トムソン再確認待ち」→ 08-03設定上限拡張+08-04第2次FB対応(仕様v2.7・1行表示)・ビルド9配布・選手データ整備・リーグ88/119追加まで完了し「最終確認待ち」へ。一時設定の戻しはトムソン側タスク化)。テーマ3をクローズ(残っていた紐づけ・コペンハーゲンpublish・Superliga取り込みを08-04に全消化)。

2026-08-01 19:56 時点: テーマ2・テーマ3は変更なし(アーカイブ省略)。テーマ1を書き換え(旧: 「トムソン確認フェーズ・フィードバック待ち」→ FBシートの指摘2件(日本人選手欄の空表示・日本代表期間の扱い)の実装対応・デプロイ・DM返信まで完了し「再確認待ち」へ進んだため)。

2026-08-01 15:26 時点: テーマ1・テーマ2は変更なし(アーカイブ省略)。テーマ3を書き換え(旧: 「実装済み・You のコミット/デプロイ待ち。デプロイ後に3リーグ投入」→ デプロイ・3リーグ投入・チーム紐づけUI改修まで完了したため)。

2026-08-01 13:36 時点: テーマ1・テーマ2は変更なしのため本文維持(アーカイブ省略)。テーマ3(実況板リーグ追加のポータル化)を新設。

2026-07-23 13:40 時点のスナップショット(ダイジェスト テーマ1のみ・テーマ2は継続中のため本文維持)

テーマ1(ダイジェスト新機能・サーバー側 本番稼働開始(運用適用①〜⑤完了 2026-07-23)・残りは管理画面目視+iOS提出)

  • 2026-07-22 実装完走(Codex M0〜M7全承認・DEVLOG 23:22)→ 2026-07-23 運用適用①〜⑤完了(DEVLOG 09:10)。DDL適用・server 8コミットデプロイ済み・選手マスター54名(active52/needs_review22)・チームname_ja 666件・代表スカッド26名+日本語名・crontab4行・S3毎分生成 まで本番稼働中
  • 本番E2Eで2バグ発見・修正済み: DDLの fnlive_teams.del 参照(bf9b38b)・DataProviderのRO datasource不一致でJOIN欠落(2eca081)。後方互換smoke・/api4/jpPlayer・S3公開200 全PASS。
  • ⑥管理画面目視=You確認済み(7/23)。⑦iOSは5.6.0設定・sim本番データ確認・GitHub push済み(ios 9コミット)次にやること: You の実機確認(Xcode直挿し)→アーカイブ→App Store提出(リリース時期トムソン調整)→トムソンへ実装完了報告。
  • フォロー項目: needs_review 22名の精査。overseas.json は欧州開幕(8月上旬)まで空構造が正常。任意で直近代表戦の updateFixture/{id} バックフィル。
  • seed SQL正本: digest-jp-players-seed_20260723.sql(operation repo)。cron EC2 crontabバックアップ: ~/crontab_backup_20260723.txt

2026-07-22 13:57 時点のスナップショット(ダイジェスト旧テーマ1のみ・テーマ2は継続中のため本文維持)

テーマ1(新レーン: トムソン提案「海外組・日本代表ダイジェスト」・API検証済み・DM返信→トムソン返答待ち)

  • 2026-07-22 トムソンから開発仕様書HTML受領(新規3画面+既存修正3画面+ダイジェストタブ/更新バッジ)。読解済み・仕様の練度は高い。
  • API検証完了=実装可能: RapidAPI API-Football 現行プラン7,500req/日内で運用可。全対象リーグ+代表戦で評価/lineups/G/A取得確認済み。v3 /fixtures?id= が全部同梱1req=既存 ovserveFixtures 拡張+選手stats保存で賄える。最繁忙日+2,700〜3,400req/日見込み。
  • 日本語名(APIはローマ字のみ): Claude(サブスク内)で名前解決+WebSearch裏取り+needs_reviewフラグ運用で初期リリースから対応する方針。クラブ名日本語表記も同パイプ。
  • 次にやること: DM送信→トムソン返答後に工数見積もり(→実際は同日に実装許可が出て見積もりを省略し実装へ直行した)。

2026-07-08 17:07 時点のスナップショット(v9再スタート完了直後・全文保存)

テーマ(2026-06-28〜07-08・起動時広告A/B コホートparam型ズレ修正〜5.5.5リリース〜v9実質再スタート 完了

点火(6/22 20:40・v8/active=true)後、dev-timer「maturation明け」通知を受け watermark=6/25 で GA4 first-look(property 211701453・experiment_id=app_open_interval_2026q3)を実施。 結論:

  • 機構は健全: SRM無し(A=4720/B=4712 installs・χ²≈0.007・p≈0.93)/applied_interval=A:50・B:1440 で群別policy反映済/kill_observed=true は0件/主要funnel(experiment_enrolled/target_launch/content_reached/launch_no_content/app_open_ad_decision/fn_ad_revenue)両群着弾。=実験は正しく走っている。
  • 🔴 重大欠陥: target_cohort_day/enrollment_day/days_since_enrollment全イベント100% (not set)/空experiment_id/assigned_group/applied_interval_minutes/kill_observed は正常着弾=永続enrollレコードから算出する日付3 param のみが死んでいる。→ ガードレール④(成熟後 残差率・target_cohort_day集計)と 円/人(days_since_enrollment)が算出不能。assigned_group は生きているので群レベル比較のみ可。
  • 方向性(参考・イベント日基準=未成熟混在・判定不可): 離脱率 launch_no_content/target は A=19.7%・B=15.8%(B が ~3.9pp 低く仮説の向き)。ただし未解決率 A=6.5%/B=4.0% の群間差2.5pp がガードレール④の保留条件に該当し、精緻化に要る maturation 法が欠陥で使えない=結論は出せない
  • 次アクション(最優先): footballnext-ios で日付3 param の送信箇所を特定し原因究明・修正。事後 param 追加は過去データを復元できないため早期修正が必須(毎日 cohort 解像度を失う)。記録は本ブロック+DECISIONS 2026-06-28+runbook 点火ログ注記。
  • 記録は完了(DECISIONS/WIP/runbook 反映済)。This セッションで ga4 MCP 接続・first-look 4クエリ実行・3ファイル記録まで実施。
  • 修正→レビュー→提出準備まで完了(2026-06-28): gods-talk ab-cohort-param-fix を round1〜3 実施(方針→実コード→最終確認)・release可判定。ios master に修正コミット(型ズレ解消の 28af0f1/窓外gate+validation 698cf5f/実在日チェック 83a4796バージョン5.5.4 f864eb7)+ operation main に記録(55e9e71 他)。いずれも push 済。sim build SUCCEEDED。
  • 修正内容: 数値/Bool param × text dimension の型ズレを emit境界 ABTest.ga4*Label で解消(target_cohort_day/enrollment_day→ISO・days_since_enrollmentd00d27config_versioncv_Nkill_observed→文字列)。ABAdCollector の追跡窓外(D28+)送信を gate。ga4DayLabel に範囲+実在日(round-trip)validation。保存schemaのInt/Boolは不変。
  • 5.5.4 配信後の運用(v9 config): experiment_id は据置(アプリ制約で config 変更不可)。min_build=50504で5.5.3を除外+解析は enrollment_day >= 5.5.4配信日 でフィルタenrollment窓を5.5.4ライブ日起点で取り直す=実質再スタート。雛形 ab_test_config.v9.template.json 用意済(DECISIONS 2026-06-28)。
  • リリース済&着弾検証成功(2026-07-07): 型ズレ修正版は Apple 5.2.3 却下対応で YouTube機能一時非表示+5.5.5 として 2026-07-06 リリース(5.5.4 は未リリース)。翌7/7 GA4 Data API で appVersion=5.5.5 の5 dimension 全て non-empty 着弾を確認(target_cohort_day/enrollment_day=ISO・days_since_enrollment=d00〜d13・config_version=cv_8・kill_observed=false)。旧5.5.3は(not set)。型ズレ修正は本番で成功。汚染(5.5.3既enroll→5.5.5更新勢の古いenrollment_day)も実測で確認=解析は enrollment_day>=2026-07-06 で除外。
  • v9 配置=実質再スタート完了(2026-07-08 00:55 JST): v8→v9 を S3 配置(aws s3 cp --acl public-read --profile thomson-ik・min_build=50505・窓7/6-8/17・config_version=9)。公開URLで HTTP200・v9着弾確認、旧v8はscratchpadにバックアップ、GA4メモ記入、点火ログ表追記(op 323e779)。12時間後 cv_9 着弾89%(2839件/5.5.5)を確認=クリーン再スタート成功
  • 現在地(2026-07-08 セッション終了時点)=A/B再スタート一区切り。実験は v9・新コホート(窓7/6-8/17)で稼働中。次チェックポイント: dev-timer #30(7/11) 再スタート健全性確認・#31(7/22) 成熟後残差率初回read。本番解析(残差率・円/人)は9月中旬頃、母集団は enrollment_day>=2026-07-06 かつ cv_9。site-ad-scoring WIP は ios stash@{0} 温存(別レーン)。

テーマ(2026-06-19〜・起動時広告A/B 実装レビュー round1〜10 完了・sign-off 確定・実装レーン完了/点火前チェックリスト待ち)

実装コードレビュー(gods-talk)を round10 まで回し、App Open / native lifecycle / kill属性 / TTL / 計測契約の全 high/medium を解消。round8 で sign-off 取得 → round9 で R7-#2 revert(render 失敗の過大計上)を medium 差し戻し → round10 で gate 復元(ios 311e3ad)を確認し「round9 medium close・round8 sign-off 回復・新 high/medium ブロッカーなし」を取得。実装レビューレーンは完了。 本番は中立(active=false / v7 / ttl14日)、5.5.3 据え置きで全修正積載。reviewer は通常 native 表示の短い実機再スモークを「望ましいが medium close の必須前提にしない」と明示(gate は sign-off 済み実装へ復帰・A-1 で rendered=true/描画/ILRD/GA4 impression 確認済み・Release 追加差分はコメントのみ)。残るは運用・外部タスクのみ(GA4 2件登録・5.5.3 提出・点火)。 5.5.3 実機スモーク A-0〜A-3 は前段で全PASS済(expire 人工注入 hook -ABNativeSimulateExpire#if DEBUG 隔離・本番無効)。

点火前チェックリスト(コード外・必須。完了後に active=true)

  1. GA4 custom 定義の登録完了(2026-06-19・You が管理画面で手登録): 追加2件(AB Kill Observed dim/kill_observedAB Applied Interval Minutes metric/applied_interval_minutes・分)を登録済=全23件。着弾の DebugView 目視は点火後に実施。登録前データは遡及されないため点火前登録の順序は満たした。
  2. enrollment 窓・判定値確定済(2026-06-19): interval_a=50/interval_b=1440・締切42日/追跡28日/ITT・required_assignment_version=1・min_build=50503(5.5.3+限定)・ttl14日。enrollment窓は窓長42日固定・起点は5.5.3リリース日(+猶予)を点火時セット(旧6/22は過去化で不使用)。詳細は runbook 点火前チェックリスト。
  3. 初期 experiment_active=false での本番 config 取得確認確認済(2026-06-19): 本番 ab_test_config.json 実取得=200・valid・active=false・v7・全互換値(experiment_id/assignment_version=1/event_schema_version=v1/mp_2026q3_r1/interval 50・1440/ttl14日)が ABProtocol と一致。enrollment窓は旧値だが active=false で無効(点火時 v8 で再設定)。on-device 非参加挙動は round4 実機PASS。
  4. 点火・停止時刻を記録する運用準備準備済(2026-06-19): runbook に「点火・停止ログ」表を新設(停止境界の正典・GA4アノテーション併用)。現状 v7/active=false を記録、点火/停止行は実施時記入。 → 点火前タスクの状況(2026-06-19 時点): GA4 2件登録=完了5.5.3 native スモーク=完了(A-0〜A-3 全PASS)5.5.3=ビルド準備完了・App Store 申請は2026-06-20 予定(リリース=審査通過後)(点火の enrollment窓起点は store でライブになった日+猶予)。**申請前に実況板バグ(もっと見る→上に戻るでライブカード消失)を修正済(ios f275fed・実機OK)=5.5.3 に同梱。**残る点火手順: S3 を v8/active=true・enrollment窓(リリース日起点42日)・min_build=50503 で配置+点火ログ記入+同時刻に GA4 アノテーション。 配置は --acl public-read・profile=thomson-ikv8 雛形 ab_test_config.v8.template.json 用意済(固定値埋め済・enrollment 2日付けのみ点火時にリリース日から算出)/runbook に生成・配置手順(date→epoch コマンド込み)を新設。 ライブ ab_test_config.json は v7/active=false のまま据え置き。

実装レビュー(gods-talk)round5〜8 の到達(push済)

  • round5 ios 0089f65 / op ab066e4: R5-#1 native確定destroy(lease制・固定0.6秒撤去・実機スモークPASS)/ #2 pending timeout(30s)+expire / #3 kill属性 snapshot↔applied分離・clear順序。
  • round6 ios 3f64ade / op 546dd0a: R6-#1 callback main集約+timeout世代照合 / #2 dequed-expire無効化 / #3 GA4-only整合(kill_observed必須化) / #5 TTL省略可統一。
  • round7 ios 31dc22b / op 07aa09f: R7-#1 stale判定をcellForRow前段へ+同一pass再deque / #2 renderable !expired + render成功時のみimpression / #3 self-test fail-hard / #4 kill_observed_at削除。
  • round8: 点火 sign-off。残 low(render-false即時再取得なし=fail-closed/self-test範囲/廃止param説明文/runbook commit表記)は全て非ブロッカー。
  • round9 ios 311e3ad / op 4e489da: sign-off 後の R7-#2 revert を reviewer が medium 差し戻し(SDK 契約上 false=render失敗で計上不可)。→ impression gate 復元(true 時のみ recordImpression・false 時 fail-closed)+ expire hook を #if DEBUG 隔離。Debug/Release 両ビルド 0 errors。
  • round10 op(本コミット): gate 復元(311e3ad)を確認し round9 medium close・round8 sign-off 回復・新 high/medium ブロッカーなし を取得。実行ロジックは 31dc22b の R7-#2 と機能的に同等へ復帰。通常 native 表示の短い実機再スモークは「望ましいが medium close の必須前提にしない」と明示。実装レビューレーン完了
  • 検証: 各round sim build 0 errors・改修ファイル新規警告なし・self-test 7/7。limitation: sim は MAX native fill なしで timeout/expire の end-to-end は未実行(destroy判定は純粋関数 self-test+外部レビューで担保)。

5.5.3 実機スモーク結果(2026-06-19・iPhone 実機・Xcode直挿し)

  • A-0 self-test: PASS(-ABNativeSelfTestdestroy-logic 7/7・abort せず)。
  • A-1 描画・ILRD・impression: PASS。fn_ad_impression(middle_native) が DebugView ライブに出ず調査したが、実はコードバグではなかった: renderNativeAdView は rendered=true(描画成功)で recordImpression→logEvent まで毎回到達し、GA4 Realtime で fn_ad_impression 増加を確認収集できている。DebugView 非表示は表示/タイミングの癖で、カスタム定義の登録有無とは無関係(DebugView は登録不問で全イベントを出す。登録が効くのは Explore/レポートのみ)。fn_ad_impression は収集OK・slot/format/network も既登録=レポートでも使える=やること無し
    • 補足の訂正: 途中で出した「Bool gate が impression を握り潰している」という見立ては誤り(rendered=true なので R7-#2 有無に関係なく元々計上されていた)。ただし R7-#2 revert(ios 5eaf3be)を「無駄&危険な分岐の簡素化」として残す判断をした。
    • 【round9 で覆った】この revert は overreach だった。reviewer 指摘: SDK 契約上 renderNativeAdView=false は render 失敗を意味し、観測で false が出なかったことは「false を成功扱いしてよい」根拠にならない(別 network/view 再利用/将来 adapter 差分で false がありうる)。→ impression gate を復元(true 時のみ計上・false 時 fail-closed、ios で revert を差し戻し)。R7-#2 を本アプリの計上判定に使わない、という結論は撤回。
  • A-2 lease 負荷: PASS(スクロール往復・pull-to-refresh・More連打でクラッシュ0・空白固まりなし)。
  • A-3 expire→再deque: PASS。-ABNativeSimulateExpire(load 成功 ad を10秒後に人工 expire)で連続 churn 耐久 → クラッシュ0・新 ad 差し替え継続・stock 暴走なし。debug hook 追加(ios 777128f・launchArg 限定・本番無効)。

確定した実装仕様(GATE凍結済み)

  • experiment_id="app_open_interval_2026q3"assignment_version=1measurement_protocol_version="mp_2026q3_r1"
  • 割当: SHA256(UTF-8("experiment_id:install_uuid")) 先頭8バイト big-endian % 100[0,50)→A。新 experiment は自動再無作為化。
  • enrollment窓: 2026-06-22 0:00〜2026-08-03 0:00 JST(42日)。追跡 trackingDays=28(JST暦日)。TTL=14日。
  • 詳細値は正典 SPEC.md「measurement_protocol 凍結値」と ABProtocol

詰まっていること・未決事項

  • R2-#4 / R2-#3 が点火ブロッカー(上記 残 must-fix)。R2-#3 は実機/TestFlight検証必須。
  • ga4 MCP が本セッション未接続=GA4着弾の DataAPI 裏取り不可(点火後検証は接続要 or DebugView 目視)。
  • 月次総売上(iOS・FN・対象月)はトムソン提供待ち=解析フェーズ入力(点火ブロッカーではない)。
  • S3配置は --acl public-read 必須(footballnextバケットはオブジェクト単位ACL運用・無ACLだと403)。profile=thomson-ik
  • Swift の SWIFT_ACTIVE_COMPILATION_CONDITIONS に DEBUG 未設定→seed/AB_EVENT検証時は上書き要。seed は実fetchをスキップし決定化済(config_version=999999)。
  • 正典 SPEC.md(rev6)が唯一の source of truth。

旧テーマの継続タスク(インフラ・前回まで)

  • API Gateway 飛び地の原点直叩き移行 → footballnext API GW 撤去。iOS OshiraseViewController デッドコード整理(削除済み footballnext-oshirase-v2OshiraseViewController.swift:30 が参照中・据え置き)。rss-crawler の cron 一元化。
  • iOS 5.5.1(YouTube WKWebView 化込み)を App Store 提出(実機再生確認は未実施)。footballnext-ios に別件未コミット変更(push通知系・Podfile 等)残置中。
  • (継続)xcuserstate 追跡、android 未push、cake_sessions Fatal 放置。

別件で要対応(サーバ側、今回も触らず)

  • ⚠️ RDS SG が 3306/tcp を 0.0.0.0/0 開放
  • ⚠️ DB パスワードが database.php に平文コミット
  • cron EC2 が Amazon Linux AMI 2018.03(EOL)
  • リードレプリカ復活 / gp2 → gp3 はコスト追加 NG 方針につき凍結中

前テーマの残タスク(多角レビュー・2026-06-05)

  • 改善実装の着手判断(最優先=App Open Ad スロットル復活、広告挿入間隔の緩和)。効果検証はGA4の離脱シグナルで。
  • iOS 5.5.1 を Xcode アーカイブ→App Store 提出。レビュー結論と元データは review-20260605.html

2026-06-19 14:30 時点のスナップショット(Codex round3 high3+medium4+low1 修正+実機回帰PASS・round4待ち)

点火→緊急停止→round1/2修正→実機A〜D PASS→round3で新high3(判断ミス2件含む)→修正+実機回帰スモークPASS。本番中立(active=false/v7/ttl14日)、5.5.3据え置き。R3-#1/#2 5db2975(decision/show一体claim+placement照合)・#3 5d26ee6(native cap撤去→deinit destroy)・#4-7 ef4d47e・#8 791952f+runbook 0789c73。実機(iPhone14/seed): A native+ILRD / B content_reached / C 群B処置(decision=interval) / D #11 全PASS。次は round4 で R3 確認→5.5.3提出。(→ round5〜8 で native lifecycle/kill/計測の残 high/medium を全解消し round8 で点火 sign-off に到達)

2026-06-19 08:20 時点のスナップショット(点火直前:段階A配置+pre-flight PASS)

テーマ: 点火前チェックリストを全消化。不活性config(active=false)を本番S3配置済み・シミュレータで本番S3取得経路を実証。残りは段階B点火のみ、Youのgo待ち だった。enrollment窓 6/22〜8/3 JST 確定。GA4 custom定義21件登録済。トムソン3項目HTML公開(叩き台確定扱い)。→ この後 点火(active=true)したが Codex実装レビューで重大バグ多数→緊急停止→high/medium修正フェーズへ(上記「現在の状況」)。

2026-06-19 02:32 時点のスナップショット(点火準備フェーズ:BASE実装+GATE凍結+GA4登録完了・段階A配置前)

テーマ: 起動時広告 削減A/B は実装・GATE値凍結・GA4 custom定義登録(21件)まで完了。S3配置用 ab_test_config.json(active=false)+ ab_test_deploy_runbook.md 作成済。当時の残タスク=①トムソン通知 ②enrollment期間決定 ③S3配置→点火 ④Data API検証。本番影響は中立。→ この後 08:20 までに トムソン3項目HTML公開(叩き台確定扱い)・enrollment窓確定(6/22〜8/3 JST)・段階A=不活性config配置・シミュレータpre-flight PASS を完了(上記「現在の状況」)。残るは段階B点火のみ。

2026-06-19 01:03 時点のスナップショット(BASE A〜F 実装完了・点火準備着手前)

テーマ: 起動時広告A/B の計測基盤 BASE A〜F を実コードで実装完了(ios commit 4ebdeb6)。新規4ファイル+既存14ファイル配線。フルビルド成功・シミュレータ実機で experiment_enrolled→target_launch→app_open_ad_decision→content_reached 発火確認。本番影響は中立。当時の次タスク=GATE値確定・S3 config配置・GA4登録・native refactor。→ この後 02:32 までに GATE値叩き台凍結・config/runbook作成・native lifecycle修正・GA4 custom定義 全21件登録 を完了(上記「現在の状況」)。

2026-06-19 01:00 時点のスナップショット(実装計画フェーズ完了・実装着手前)

テーマ: トムソン承認後、実装計画HTMLを作成し Codex(gods-talk)5ラウンドで設計レビュー完了。実装計画 rev6 と正典 SPEC.md rev6 が同期済み。この時点でコード変更はまだ無し(資料・正典・レビュー往復のみ)。確定事項: assignment/activation/enrollment の3分離(3 artifact+版番号)、ForegroundCycle/TargetLaunchContext 2層+60秒gate、可視判定は状態機械、kill/crash対策に write-ahead OpenContextRecord+CAS、GA4送信 at-most-once、収益 canonical fn_ad_revenue 一本化、円/人 固定コホート、暦日 Asia/Tokyo。実装は3レーン(BASE着手可 / BASE契約5点 / GATE-値)。→ この後 2026-06-19 にBASE A〜F を実装完了(上記「現在の状況」)。

2026-06-18 23:55 時点のスナップショット(提案書フェーズ完了・承認前)

  • 起動時広告 削減A/B の提案書フェーズが完了していた状態。提案書 起動時広告A-Bテスト_提案書_20260618.htmlthomsons S3 に公開(URL: https://thomsons.s3.ap-northeast-1.amazonaws.com/起動時広告A-Bテスト_提案書_20260618.html)、Codex提案書レビュー2ラウンド(.gods-talk/ab-test-proposal-review-*)反映済み。
  • この時点の確定設計: 群A=現状(50分)/群B=24h rolling、ITT、GA4のみ(BigQuery不使用)、起動時は群別didPayRevenue実額・それ以外は混合eCPM概算、残り具合=コンテンツ未到達離脱率+セッション/人+D7/D14。正典 AB_TEST_MEASUREMENT_SPEC.md 初版。
  • 次アクションは「トムソンの承認待ち+計測仕様TODO消化」だった。→ その後承認が出て実装計画フェーズへ移行(上記「現在の状況」)。提案書時点のTODO(rolling24hのabsバグ・固定コホート・収益式の為替/対象範囲・設定取得SLA・事前テスト合格基準)は実装計画 rev6 で大部分が確定仕様化された。

2026-06-18 10:32 時点のスナップショット

テーマ: 起動時広告 削減A/B の測定設計(机上)。50/50無作為A/Bで実測判断する設計を、コードの現状確認込みで詰めた。当時の骨子(※この後の議論で更新済み・最新は「現在の状況」と SPEC 参照): 群A=現状(毎回と誤認)/群B=1日1回、判断②に launch_bounce、3パターン即採用、勝った間隔を S3 展開。コードの現状: 起動時広告は AppDelegate.swift:241 で明示 showAdIfReady()ifで群分け可)、appOpenAdInterval スロットル+GA4配線済み、間隔は S3(Const.baseS3Url/201811)→UserData.setAppSettings で変更可、広告はパッチワーク(MAX外に AdG/Zucks/LINE、didPayRevenue 空)。当時の次アクション=提案資料化。

2026-06-13 00:42 時点のスナップショット

直近の割り込み対応(完了): iOS YouTube プレイヤーの WKWebView 化。ナレッジ ios-wkwebview-youtube-inline-playback(7channel 由来)を YoutubePlayerView.swift に適用。UIWebView 廃止・baseURL/origin 一致(Const.baseURLString)・https 化・自動再生オフ(タップで再生)。arm64 シミュレータビルド成功。コミット a94aa84(この1ファイルのみ選択コミット)→ push 済み。You が 5.5.1 申請予定(実機再生確認は未実施)。footballnext-ios に別件の未コミット変更(push通知系・Podfile 等)残置中。

テーマ: 今後のアプデ前の足場固め=定期実行の一元化(cron 専用 EC2 へ寄せる)+ AWS Lambda 大掃除。

どこまで進んだか: TFN_cron を cron サーバーへ移管完了(8エンドポイントを crontab 化・Lambda 本体+ルール削除・~/crontab_backup_20260606-073553.txt 退避)。Lambda 大掃除完了 42関数 → 9関数。teiki/article API 経路解析済み(API GW → Dynamo キャッシュ Lambda → CakePHP api4 の三段、execute-api 経由は飛び地数本)。

次にやること(当時): API Gateway 飛び地の原点直叩き移行 → footballnext API GW 撤去。iOS OshiraseViewController デッドコード整理。rss-crawler の cron 一元化。多角レビューの改善実装着手判断(最優先=App Open Ad スロットル復活)。

詰まっていたこと: 削除済み footballnext-oshirase-v2 を iOS が参照中(デッド画面と判断し据え置き)。

2026-06-06 17:32 時点のスナップショット

テーマ: 今後のアプデ前の足場固め。定期実行の一元化(cron 専用 EC2 へ寄せる)+ AWS Lambda の大掃除。

どこまで進んだか: TFN_cron を cron サーバーへ移管完了(8エンドポイントを crontab 化・Lambda 本体+ルール削除・~/crontab_backup_20260606-073553.txt 退避)。Lambda 大掃除完了 42関数 → 9関数(残9は全て現役 or 保守ツール)。teiki/article の API 経路解析済み(API GW → Dynamo キャッシュ Lambda → CakePHP api4 の三段、execute-api 経由は飛び地数本のみ)。

次にやること(当時): API Gateway 飛び地の原点直叩き移行 → footballnext API GW 撤去。iOS OshiraseViewController のデッドコード整理。rss-crawler の cron 一元化。

詰まっていたこと: 削除済み footballnext-oshirase-v2 を iOS が参照中(デッド画面と判断し据え置き)。CloudWatch 長期間1バケット集計の過少値問題は7日日次合算で回避。

2026-06-05 19:50 時点のスナップショット

テーマ: Football NEXT の多角サービスレビュー(large-scale-service-review 試運転)。広告・収益・行動・コードの複数ソースを診断し review-20260605.html に出力。GA4 新規接続。

どこまで進んだか: レビュー完了・HTML出力済み(6ソース三角測量)。GA4 接続済み(MCP ga4 / property 211701453 / ADC流用)。元データ一式が repo ルートに存在。スキル改善済み。

レビュー結論: 広告過多によるデススパイラル(広告売上 2019¥17.5M→2025¥5.4M −69%、レビュー星 2022→2023 で 3.0→1.6 崩落+広告言及率93%、GA4 MAU 約2年−32%、全て時系列一致)。最優先=App Open Ad スロットル復活、次いで不快クリエイティブのブロック・ストア評価運用、中位=有料広告なし課金(StoreKit)・計測基盤。

詰まっていたこと: AdMob/AppLovin MAX が別管理者で面別広告収益・eCPM取得不可。iOS OneSignal 2.12.0 が arm64 simulator 非対応。

2026-06-03 17:15 時点のスナップショット

テーマ: 試合詳細に「得点者」表示を追加するフルスタック機能(サーバ:events_json 保存+V2 で scorers 配信+過去試合バックフィル用 updateFixture / iOS:得点者カード 5.5.1)。本番反映・トムソン確認OK・iOS タグ 5.5.1 push 済み。

どこまで進んだか:

  • サーバ(footballnext-server)完了・デプロイ済み: fnlive_fixtures.events_json 追加(ALTER)。Cron で events 保存、V2Controller::fixture()scorers(得点時間/選手/アシスト/PK/OG/home-away)を配信。終了試合も埋める updateFixture($id)/cron/updateFixture/{id})新設。AfterInstall でモデルキャッシュ自動クリア。直近10試合バックフィル済み(全件 scorers=得点数 一致)。
  • iOS(footballnext-ios)完了・push/タグ済み: 5.5.1。得点者カードを記事テーブルのヘッダー(LiveFixtureScorersView)。コミット a3793be、タグ 5.5.1
  • footballnext-android: 変化なし(Play Console アップロード待ち)。RDS/サーバ基盤: 平常運用。

次に何をするか: iOS 5.5.1 を Xcode アーカイブ→App Store 提出。任意で得点者まわりの dead code 掃除。過去試合の得点者は updateFixture で個別バックフィル。

詰まっていること: iOS dead code(未使用 xib + 空セル)。xcuserstate 追跡継続、android 7コミット未push、cake_sessions Fatal 放置。

2026-05-30 20:00 時点のスナップショット

テーマ: footballnext-ios の近代化(広告SDK全面更新・Zucks/Moloco 追加・GMA13 移行)完了+GitHub リポジトリ運用の正常化。

どこまで進んだか:

  • footballnext-ios 近代化(完了・push 28f5fe7): Xcode 26.5 ビルド復旧、Zucks/Moloco 追加、広告SDK一斉最新化、GMA13/iOS13 化、Liftoff+SKAdNetwork 補完。
  • リポジトリ正常化: operation 配下を GitHub クローン(master / Pods gitignore)へ置換、旧 CodeCommit クローンは ~/Downloads/footballnext-ios-codecommit-backup に退避。
  • footballnext-android: 16KB対応 AAB(3.0.7 / vc17)生成済み、Play Console アップロード待ち。
  • RDS/サーバ: 平常運用。

次に何をするか: 旧 CodeCommit バックアップ削除、実機で MAX Mediation Debugger 再確認、xcuserstate 追跡解除、android AAB アップロード、android 7コミット push 判断。

詰まっていること: GMA 13.3 止まり、dSYM 警告(無害)、xcuserstate 追跡継続、android 7コミット未push、cake_sessions Fatal 放置。

2026-05-21 19:35 時点のスナップショット

テーマ: footballnext-android の 16KB 対応リリース対応 ── コード作業は完了・実機検証済み、Play Console アップロード待ち。RDS/サーバ側は平常運用継続。

どこまで進んだか:

  • footballnext-android 16KB対応(対応期限 2026-05-31): 原因=Realm の native lib のみ。Realm 7.0.8→10.19.0(16KB対応版)で解消。
  • 4年分のツールチェーン近代化(Gradle 8.9 / AGP 8.7.3 / Kotlin 2.1 / targetSdk 35 / minSdk 24)、ViewBinding 全面移行(43ファイル)、依存刷新(OneSignal 5 / AdMob 23 / okhttp 4)、nend撤去、YouTubeプレイヤー差し替え。
  • 実機テストでクラッシュ4件を発見・修正。署名済みリリースAAB(versionCode 17 / 3.0.7)生成、16KBアラインメント検証OK。
  • footballnext-android リポジトリに7コミット(ローカル、origin 未push)。

次に何をするか:

  • AAB を Play Console にアップロード(内部テスト→製品版推奨)、動画/コメント/設定画面の手動確認、OneSignalプッシュ実配信テスト、ADG/i-mobileネイティブ広告の実機表示確認、7コミットの origin push 判断。

詰まっていること・未決事項:

  • footballnext-android の7コミットが未push。cake_sessions Fatal の許容度(放置・頻度上がったら見直し)。

2026-05-19 09:34 時点のスナップショット

テーマ: 平常運用フェーズ(RDS チューニング + baseballnext 統合は完了、エラーログスパイクは原因特定済み・対応保留)

どこまで進んだか:

  • footballnext RDS チューニング(performance_schema 不発弾撤去 / old_articles・impressions 空化 / articles OPTIMIZE / index A 群 DROP)→ DB 9.1GB → 4.1GB、ReadIOPS -61〜70%
  • baseballnext 統合(5/9 完了、5/11・5/19 観察で問題なし、buffer_pool ヒット率 99.99% 維持)
  • エラーログスパイクアラート(5/16・5/17・5/19)の原因特定 = cake_sessions Duplicate entry / bot のパススキャン由来

次に何をするか:

  • 即時アクションなし。観察モード
  • cake_sessions Fatal の頻発が続くようなら対応(A: 攻撃パス拒否 / B: Session→File or Redis)

詰まっていること・未決事項:

  • cake_sessions Fatal の許容度(今は放置、頻度上がったら見直し)

2026-05-09 19:00 時点のスナップショット

テーマ: RDS (footballnext) のチューニング → baseballnext 統合受け入れ準備フェーズへ

どこまで進んだか:

  • performance_schema 不発弾撤去(pending-reboot 解消)
  • old_articles 空化(約 2GB 解放)
  • articlesOPTIMIZE TABLE(2.8GB → 0.76GB、index 75%減)
  • articles インデックス A 群 4 個を Step 1+2 一気に実行(INVISIBLE → 即 DROP、index 499 → 369MB / -130MB)
  • Step 2 効果検証完了: ReadIOPS -61〜70%(FreeableMemory はほぼ変化なし)
  • impressions の機能廃止(INSERT コメントアウト+デプロイ+TRUNCATE で 730MB 解放)
  • DB 全体: 9.1GB → 4.1GB(-54%)
  • baseballnext RDS 実態調査(2.24GB / 90 テーブル / 現役稼働 / SELECT 5.8/秒)

次に何をするか:

  • baseballnext 統合作業は baseballnext-operation プロジェクト側 で進行中。こちらは受け入れ側として現状維持
  • 統合作業中・直後の footballnext RDS のメトリクス観察(FreeableMemory・CPU・IOPS・ヒット率)
  • 統合後にあらためて buffer_pool / インスタンスサイズの判断(Step 3 / medium 昇格 / 現状維持)

2026-05-08 02:24 時点のスナップショット

テーマ: RDS (footballnext / db.t4g.small) の FreeableMemory カイゼン

どこまで進んだか:

  • performance_schema 不発弾撤去(pending-reboot 解消)
  • old_articles 空化(約 2GB 解放)
  • articlesOPTIMIZE TABLE 完了(2.8GB → 0.76GB、特に index 75%減)
  • DB 全体 9.1GB → 5.0GB、FreeStorageSpace +2.66GB
  • hot_keyword_rankings の利用パターン調査(最新dir top2 のみ参照、過去データは死蔵、書き込みも実質停止)
  • 19-20時 FreeableMemory スパイクは「自分の調査クエリ起因の人為的事象」と切り分け完了

次に何をするか(優先順):

  1. インスタンス昇格 db.t4g.small → medium の段取り(メモリ問題の本命解、+$30/月)
  2. CloudWatch アラーム新設(FreeableMemory < 100MB)
  3. hot_keyword_rankings のデータ整理(直近 N 日分残し or TRUNCATE、実行可否は別途判断)
  4. articles のインデックス整理 A 群 4 個(優先度下がった、追加効果は数百MB)

別件で要対応: RDS SG 0.0.0.0/0 開放、DB パスワード平文、cron EC2 AMI 2018.03 EOL、インスタンス昇格


2026-05-07 21:47 時点のスナップショット

テーマ: RDS (footballnext / db.t4g.small) の FreeableMemory カイゼン

どこまで進んだか:

  • 不発弾 performance_schema = 1 (pending-reboot) を engine-default (0) に戻した(再起動時の事故防止)
  • old_articles を TRUNCATE して約 2GB 解放(phpMyAdmin 経由)
  • 現状把握: buffer_pool 1024MB / ヒット率 99.996% / working set < 1GB
  • articles の断片化(data_free 1.1GB)と過剰インデックス(22個 / 2GB)を特定

次に何をするか(優先順):

  1. articles のインデックス A 群 4 個(article_site_id / image_width / title / del)を INVISIBLE に切り替え → 1〜数日観察 → 問題なければ DROP INDEXOPTIMIZE TABLE(約 1〜1.5GB 圧縮見込み)
  2. CloudWatch アラーム新設(FreeableMemory < 100MB)
  3. 余裕があれば B 群(replicated / comment_update_unixtime)も同手順で

別件で要対応: RDS SG 0.0.0.0/0 開放、DB パスワード平文、cron EC2 AMI 2018.03 EOL、インスタンス昇格

ROADMAP(計画)

ロードマップ

今週

  • ダイジェスト新機能: トムソンへDM返信→実装許可取得(2026-07-22 完了)
  • ダイジェスト新機能: 実装フェーズ M0〜M7(2026-07-22 完了・Codex分業+Claudeレビュー、R-1〜R-18全消化・最終承認)。見積もりステップは実装許可が同日に出たため省略
  • ダイジェスト: 運用適用(順序厳守)(2026-07-23 完了): DDL適用→server push→crontab→S3配信・後方互換smoke まで実施
  • ダイジェスト: 対象選手マスター初期投入+日本語名解決パイプ(Claude)+チームname_ja整備(2026-07-23 完了・07-24 に代表11名を追録して計65名)
  • ダイジェスト: 試合詳細の選手欄刷新+本番データ不具合修正(2026-07-24 完了): クラブ/日本代表の選手欄を仕様に統一、related_articles のデコード型ずれ修正、team_id=0 のチーム誤解決ガード
  • ダイジェスト: iOS実機確認+TestFlight配布(2026-07-24 完了・iPhone 14 で通し確認)
  • ダイジェスト: トムソンのフィードバック対応(1回目)(2026-08-01 完了): FBシートの指摘2件(対象0人試合の日本人選手欄非表示・日本代表初期表示期間への解釈変更)を server 2コミット+iOS 2コミットで対応。デプロイ・TestFlight配布・DM返信済み
  • ダイジェスト: 設定値の入力上限を0〜365へ拡張(3層一致)(2026-08-03 完了・server 6634a4d・デプロイ済み): トムソンの99入力要望対応。モデルバリデーション+presenter/providerクランプを統一
  • ダイジェスト: トムソンのフィードバック対応(2回目・仕様v2.7)(2026-08-04 完了): 代表試合詳細の選手一覧を状態別1行表示へ(試合前=国旗+クラブ名+ポジション、発表後=先発チップ、LIVE=出場中/交代済チップ)。server e59c037+iOS e032349/5ad53eb。行重なりバグ修正済み。TestFlightビルド9配布済み(ビルド8は欠陥で破棄)
  • ダイジェスト: 対象リーグにエールディヴィジ(88)・スーペルリーガ(119)を追加(2026-08-04 完了・server 5c3a226・デプロイ済み): 夏移籍5選手の対象外化を解消
  • ダイジェスト: 久保・上田の「最近5試合」整備(2026-08-04 完了): 久保=ソシエダ6試合バックフィル、上田=昨季フェイエ5試合をfixture手動INSERT+updateFixtureで投入(渡辺剛5試合・小川1試合も副産物)
  • ダイジェスト: トムソンのフィードバック対応(3回目・仕様v2.11)(2026-08-05 完了): 項目6・7=FINISHED選手行の日の丸+配色、項目12=代表PRE_MATCHのリーグ国旗、項目11=選手詳細ヘッダーの国旗、項目9・10=確認代行で差異発見→クラブLINEUP/LIVEに状態チップ+ポジション追加。server 18af16c+iOS 609ab04/bd8de88/5480d48
  • ダイジェスト: 試合タブ空表示バグ修正(2026-08-05 完了・iOS 2278819): ダイジェストタブ出現時のタブ再構築と試合一覧取得の起動時競合。まとめてappend+reloadData方式へ(実況版一覧も同修正)。ビルド9に残存
  • ダイジェスト: TestFlightビルド10(5.6.0(10))配布(2026-08-05 完了): シェアレーン差分を退避してクリーンな 5480d48 からCLIアーカイブ→アップロード
  • ダイジェスト: 本番DBに 004(設定説明文の「初期表示期間」化UPDATE)適用を確認db/digest/004_rename_national_mode_wording.sql・説明文のみ・値不変)
  • ダイジェスト: トムソンへDM送付+ビルド10確認(2026-08-05 完了・トムソンLINEで「修正確認できました」)
  • ダイジェスト: 第4次FB対応(仕様v2.12 項目13〜18)+シェアレーン統合+ビルド11(5.6.0(11))配布・DM返信(2026-08-05 完了・iOS 3b25410/b41fffa。試合一覧3段構成化・シェア画像の国旗画像化/拡大/平均評価緑/合計欄強調。before/afterスクショは fb-items13-18-shots/
  • ダイジェスト: トムソンのビルド11確認→追加指摘対応(2026-08-08 完了・確認過程で試合一覧の選手名非表示2件の指摘→08-07調査回答、第5次FB=シート項目19〜21として受領)
  • ダイジェスト: 第5次FB対応(仕様v2.14 項目19〜21)+ビルド12(5.6.0(12))配布・返信(2026-08-08 完了・server 1e59b44+iOS 3fa1a05。対象大会を海外組14大会/日本代表5大会に分割=UECL848/クラブW杯15/E-1(23)追加・MVP候補条件+順位決定・シェア画像クラブ情報の右揃え。検証スクショは fb-items19-21-shots/
  • ダイジェスト: トムソンのビルド12確認→追加指摘対応(2026-08-08 完了・確認過程でMVP順位再変更の提案→逆提案合意=第6次FB=項目22・23として受領)
  • ダイジェスト: 第6次FB対応(項目22・23=MVP並び順共通化・仕様v2.15+ザルツブルク国旗)+ビルド13(5.6.0(13))配布・返信(2026-08-08 完了・server ac07f72+iOS 2423d53。欠損下位の共通並びで一覧先頭=MVP保証・fixtureクエリにcountry追加でcountry_code全クラブnull配信を根本修正・NEC登録漏れ修正)
  • ダイジェスト: トムソンのビルド13確認→追加指摘対応(2026-08-19 完了扱い・確認過程の指摘は08-10のバッジ#24=ビルド14と、第7次FB=シート項目25〜38として受領)
  • ダイジェスト: 更新バッジ#24修正+ビルド14(5.6.0(14))配布・返信(2026-08-10 完了・iOS b248d50。先読み生成時の即時既読化を排し実表示時のみ既読化+フォアグラウンド復帰時の再判定。※DEVLOG未記録セッション・返信 トムソン返信_更新バッジ修正_20260810.md
  • ダイジェスト: 第7次FB対応(シート項目25〜38・仕様v2.16=チームロゴ/表示仕様統一)+ビルド15(5.6.0(15))配布・返信(2026-08-19 完了・server efdb9dc+iOS ee88a7d。#25=API側スタッツ配信遅延の特定・9試合データ修復・backfill72h窓リトライ化/試合一覧=チームロゴ・揃え変更・2名常時実名・リーグロゴ縮小/ダイジェスト=ロゴ+クラブ別グループ表示・進行中/これから共通化)
  • ダイジェスト: トムソンのビルド15確認→追加指摘対応(2026-08-23 完了扱い・進行中の実画面確認での指摘=第8次FB=シート項目39〜47として受領)
  • ダイジェスト: 第8次FB対応(シート項目39〜47・仕様v2.17=試合状態/スコア表示/代表経由)+ビルド16(5.6.0(16))配布・返信文作成(2026-08-23 完了・server 329b484+iOS 926f17f。#45=代表直近3試合が大会フィルタで常時空だった根本修正/#40=livesにlineup_announced追加配信+青「スタメン」/試合状態ピル3色+LIVEパルス・チップ固定幅・スコア所属クラブ基準・キックオフ前vs。全項目のbefore/after比較画像は fb-items39-47-shots/compare/
  • ダイジェスト: 返信文+比較画像9枚+パルス動画のYou送付→トムソンのビルド16確認→追加指摘があればシートの「状況」列とあわせて対応(項目4・5・9・10の「本番確認待ち」・項目22=ザルツブルク国旗の確認も継続)
  • 運用: 本番 /api4/jpPlayer の間欠的な30秒超タイムアウトの原因調査(2026-08-05に2回観測・リトライ即成功・player_not_found経路は常時約35秒)
  • ダイジェスト: 一時設定を仕様値へ戻す(days_before=3 / days_after=1 / upcoming=7 / recent=1)※リリース前に必須・2026-08-04 トムソン側で実施することに
  • ダイジェスト: アーカイブ → App Store提出(リリース時期はトムソンと調整)
  • ダイジェスト: 板倉の昨季アヤックス分バックフィル(任意・トムソンから指名があれば。上田と同手順で数分)
  • 実況板: リーグV3追加画面(addConfirm)のデプロイ→トムソン依頼3リーグ(119 Superliga / 848 UEFA Europa Conference League / 88 Eredivisie)を新画面から投入(2026-08-01 完了・本番でリーグ登録とEredivisie 96試合/ECL 4試合の取り込みを確認)
  • 海外組: 対象選手Edit画面のチーム設定UI改修(2026-08-01 完了・server 602de4c デプロイ済み): 空プルダウン問題を「数値入力+fnlive_teams名前検索補助+存在バリデーション」で解消。addのPK入力も解禁
  • 海外組: 選手↔クラブ紐づけ+コペンハーゲンpublish+Superliga取り込み(2026-08-04 完了): 海外7名はYou実施済みと判明、J組3名(大迫=広島282/早川=鹿島290/長友=FC東京292)をSQL投入(冨安のみ0=クラブ名省略が正)。コペンハーゲン is_publish=1・スーペルリーガ20試合取り込み済み
  • 起動時広告A/B コホートparam型ズレ修正(2026-06-28〜07-08 完了)。first-lookで日付/版/bool paramが全件(not set)→emit境界の非数値ラベル化で解消。Codex3ラウンド。ios 28af0f1/698cf5f/83a4796
  • Apple 5.2.3却下対応(YouTube discovery)+5.5.5リリース(2026-07-06 リリース)。YouTube動画のメニュー項目+トップ新着カルーセルを一時非表示(可逆)。ios 98c67e2
  • 本番着弾検証+v9 config配置で実験クリーン再スタート(2026-07-08 完了)。5.5.5で5 dimension全着弾確認→v9(min_build=50505・窓7/6-8/17)をS3配置→cv_9着弾89%確認。
  • 起動時広告A/B 再スタート後の健全性確認(dev-timer #30・2026-07-11 完了・全項目クリーン): SRMなし(A=145/B=144)・cv_8急減(→68件/日)・enrollment_day ISO形成正常・applied_interval/kill_observed/funnel/収益 全て健全。留意: クリーンコホート蓄積 ~100人/日=窓終了までで総勢約4,000人規模。
  • 起動時広告A/B 成熟後残差率の初回read(dev-timer #31・2026-07-22 前倒し完了・ガードレール④ PASS): A=5.38%/B=3.67%・群間差1.71pp・SRMなし。次は #36(8/17)窓クローズ確認・#37(9/15)本解析。
  • 起動時広告A/B enrollment窓クローズ確認+残差率/SRM中間再read(dev-timer #36・2026-08-17 完了・全PASS): 窓は8/17 0時に正常クローズ・クリーンコホート最終規模3,141人(A=1,548/B=1,593)・SRMなし(p≈0.42)・残差率 A=5.51%/B=3.84%(差1.67pp・基準内)。次は #37(9/15)本解析。
  • 起動時広告A/B 本番解析(成熟後 残差率・円/人)(9月中旬頃・dev-timer #37=9/15): 窓終了8/17+追跡28日+成熟3日。母集団 enrollment_day>=2026-07-06 かつ cv_9。
  • 起動時広告A/B YouTube動画のしれっと復活(ほとぼり冷めた頃・別バージョン): コメントアウト解除+#if false除去。
  • RDS performance_schema の pending-reboot 解消(2026-05-07 完了)
  • old_articles のデータ廃棄(2026-05-07 完了、約 2GB 解放)
  • articles の OPTIMIZE TABLE(2026-05-08 完了、2.8GB → 0.76GB、index 75%減)
  • articles のインデックス整理 A 群 4 個(2026-05-08 完了、INVISIBLE → 即 DROP、index -130MB / ReadIOPS -61〜70%)
  • impressions の機能廃止と空化(2026-05-08 完了、730MB 解放)
  • 19-20 時 FreeableMemory スパイク調査(2026-05-08 完了、人為的と確定)
  • Step 2(DROP INDEX)効果検証(2026-05-09 完了、dev-timer #14 経由)
  • baseballnext 統合作業(baseballnext-operation 側で進行中、こちらは受け入れ準備状態を維持)
  • footballnext-android: 16KB対応版(v3.0.7 / versionCode 17)を Play Console にアップロード(AAB 生成済み)
  • footballnext-ios: 旧 CodeCommit バックアップ(~/Downloads/footballnext-ios-codecommit-backup)の削除(新環境ビルド確認済み)
  • footballnext-ios: 実機で MAX Mediation Debugger 再確認(Liftoff が Completed か、各SDK が LATEST 表示か)
  • footballnext-ios 5.5.1(試合詳細の得点者表示 + YouTube プレイヤー WKWebView 化・自動再生オフ)を Xcode アーカイブ → App Store 提出(You が申請作業中)
  • 【マネタイズ改善・最優先】起動時広告 削減A/B のトムソン提案資料化(2026-06-18 完了)。提案書HTML作成→Codex2ラウンド反映→thomsons S3に公開アップロード。→トムソン承認済み
  • 起動時広告A/B の実装計画HTML作成+Codex 5ラウンド設計レビュー(2026-06-18 完了)。起動時広告A-Bテスト_実装計画_20260618.html rev6 + 正典 AB_TEST_MEASUREMENT_SPEC.md rev6 同期。round5で設計レビュー完了判定。
  • 起動時広告A/B 実装フェーズ(BASE A〜F)(2026-06-19 完了)。footballnext-ios コミット 4ebdeb6。新規4ファイル(ABTest/ABLaunchTracker/ABAdCollector/ABConfigLoader)+既存14ファイル配線。BASE契約5点(OpenContextRecord/CAS・at-most-once・3 artifact+版番号・canonical fn_ad_revenue一本化・MAX枠collector接続)実装。フルビルド成功・シミュレータ実機で全ファネル発火確認。本番影響は中立(S3 config 未配置の間は現行挙動)
  • 起動時広告A/B GATE-値を measurement_protocol として凍結(2026-06-19 完了・叩き台)。割当ハッシュ/clock_anomaly/fail-safe/maturation/円人コホート/residual/収益正規化/事前テスト合格しきい値①〜⑧。要トムソン最終確認は3項目(コホート定義・residual/正規化・合格しきい値)
  • 起動時広告A/B GA4 custom定義 登録(2026-06-19 完了)。ディメンション18(イベント16+ユーザー2)+指標3=計21件、実コードと一致(スクショ検証済)
  • 起動時広告A/B native loader-ad lifecycle 修正(2026-06-19 完了・ios 8dc3b5a)。loader を ad とペアで retain。検証用 seed/NSLog は二重ガードのため残置
  • 起動時広告A/B 点火準備(2026-06-19 完了): トムソン3項目HTML公開・enrollment窓確定・段階A配置・pre-flight PASS。
  • 起動時広告A/B 一度点火→Codex実装レビューで重大バグ→緊急停止(2026-06-19)。active=false / v7 / ttl14日 へ戻し中立化。5.5.3 据え置き。
  • 起動時広告A/B Codex実装レビュー round1(high8) / round2(high4) の修正大半(2026-06-19): #1状態分離・R2-#1 context世代snapshot+TTL14日・#5#7 callback・#6/R2-#m6 impression・#3 build・#11 群A・#9a・#10/#8/R2-#m2/#m4/#m5/#m1/#m3。すべて ios master へ push。
  • 起動時広告A/B R2-#4 起動overlay gap 修正(2026-06-19 bcc9950): LaunchManager 連鎖に boot covering token。負の確認(早期発火しない)済、正の確認は実機へ。
  • 起動時広告A/B R2-#3 native loader handle方式(2026-06-19 55fbe06・compile確認のみ)。実機で native描画+ILRD 検証必須(A/B点火ブロッカー)。
  • 起動時広告A/B 実機検証(You・Xcode→実機): 手順書 ab_test_device_verification.md の A〜D。TestFlight必須でなく実機直挿しで足りる。
  • (任意)Codex round3(R2修正群の最終確認)/R2 low(build符号化文書/fail-close・DEBUG定義+XCTest)。
  • 起動時広告A/B 実装レビュー(gods-talk)round5〜8 完了・点火 sign-off 取得(2026-06-19)。native lifecycle(lease/確定destroy/timeout/expire/同一pass再deque)・kill属性 snapshot↔applied分離・GA4-only整合・TTL統一を全解消。round8 で「点火前チェックリスト完了後 active=true 可・high/medium ブロッカーゼロ」。ios 0089f653f64ade31dc22b、op ab066e4546dd0a07aa09f
  • 起動時広告A/B 点火前チェックリスト(コード外・必須。完了後に active=true): ①GA4 custom定義の実登録(kill_observed/applied_interval_minutes 追加分) ②enrollment窓・判定値の確定 ③初期active=falseでの本番config取得確認 ④点火/停止時刻の運用記録。
  • 起動時広告A/B 5.5.3 実機で MAX native の表示・expire/reload・impression スモーク(reviewer 優先#2。lease本体は round5 実機PASS済、expire/reload は未観測)。
  • 起動時広告A/B 実機スモーク+点火前チェックリスト合格 → 5.5.3 提出→承認後リリース→ S3 再点火(active=true・config_version+1・enrollment窓を実リリース日に調整)→ GA4着弾を Data API 検証(要 ga4 MCP 接続)+事前テスト①〜⑧。
  • 起動時広告A/B low(任意・非ブロッカー): build符号化の検証/文書化、通常DebugにDEBUG定義+fake loader/entry で timeout/expire/render false の XCTest整備、render false後の安全なrow reload、runbook の実コードcommit表記更新。
  • 起動時広告A/B のトムソン依頼を握る: ①GA4 Viewer→Editor 権限 ②「iOS・Football NEXT・対象月」の月次総売上。S3 app_open_ad_interval=50(群A基準)は実測確認済み。処置差(50分→24h の抑制割合)は事前テストで実測
  • 不快クリエイティブのブロック設定(成人/マッチング/ショッキングをメディエーション管理画面でブロック)
  • 広告挿入間隔の緩和(トップ一覧10件中2枠・コメント/動画5件ごと → 間隔拡大)

今月

  • ダイジェスト新機能の実装着手(2026-07-22 前倒し完了。実装自体がM0〜M7で完了、残りは運用適用=今週欄参照。日本語名解決パイプの実運用だけ未着手)

  • 有料「広告なし/サポーター」課金の新設(StoreKit) ─ 月額¥300–500 or 買い切り。収益多様化+ロイヤル層の受け皿(review-20260605.html 由来)

  • ストア評価の底上げ運用(広告改善とセットでレビュー依頼+ストア返信)

  • リテンション/広告影響の計測基盤(GA4の screen 命名整備・ad_impression 送信・面別計測)

  • footballnext-ios: OneSignal を arm64 simulator 対応の XCFramework(5.x) へ更新(シミュレータ起動不可=QA阻害の解消)

  • (任意)digest対象リーグのハードコード14件(_isDigestTargetLeague)に 88 Eredivisie / 119 Superliga を追加するか判断(現状は is_publish=1 のクラブ経由で取り込めており必須ではない。is_publish に依存しない取り込みにしたくなったら)

  • API Gateway 飛び地の原点直叩き移行: /article/{id}(footballnext-article)+/footballnext-teiki-notification-articles/{id} をクライアント側で https://fnext.thomsonsapp.com/api4/... 直叩きに差し替え → 浸透後に Lambda+ルート削除 → 最終的に footballnext API Gateway(7isghft565) を撤去。論点は DynamoDB READキャッシュの代替(必要なら CakePHP 側キャッシュ)。原点は HTTPS 200・iOS は ATS 無効で障壁なし

  • iOS OshiraseViewController のデッドコード整理(削除済み footballnext-oshirase-v2 参照 /oshirase-v2/{id} の除去。90日invocation 0 のデッド画面)

  • baseballnext 統合後の footballnext RDS メトリクス観察(FreeableMemory・CPU・IOPS・ヒット率)

  • CloudWatch アラーム新設: FreeableMemory < 100MB

  • RDS Security Group sg-083bace7c7ee9e450 の 3306/tcp 0.0.0.0/0 開放を是正

  • DB 認証情報を database.php から SSM Parameter Store / Secrets Manager に移管

  • footballnext-android: リリース後の確認(動画/コメント/設定画面の手動確認、OneSignalプッシュ実配信テスト、ADG/i-mobileネイティブ広告の実機表示確認)

  • footballnext-android: 7コミットを origin へ push するか判断

  • footballnext-ios: xcuserstate の追跡解除(.gitignore 済みだが過去コミットで追跡継続)

  • 得点者まわりの dead code 掃除(方針転換で残った未使用 LiveFixtureScorersCell.xib + 空セル)

今四半期

  • footballnext-ios: MinimumOSVersion を 13.0→15.0以上へ引き上げ(2027年春以降 15.0未満はApp Store Connectへアップロード不可。ビルド11アップロード時に警告検出。次の通常アップデートで対応)
  • baseballnext 統合後の最終チューニング判断(buffer_pool 維持 / 縮小 / medium 昇格)
  • articles のインデックス整理 B 群(replicated / comment_update_unixtime、優先度低)
  • cron EC2 (Amazon Linux AMI 2018.03 / EOL) の刷新
  • rss-crawler の cron 一元化: rss-crawler-notice-prod-crawlArticle(RSS発見→SQS投入の実ロジック・毎分稼働)をサーバ側エンドポイント化して cron から叩く。curl ラッパーではないため移行に設計が必要(残存する数少ない定期実行 Lambda の最後の砦)
  • hot_keyword_rankings 書き込み停止(最新 2026-04-09)の真因調査(cron 停止 / 別マシン稼働 / 廃止意図)
  • hot_keyword_rankings の古いデータ整理判断
  • footballnext-ios: Google AdMob を 13.4 へ(Google 製 InMobi アダプタが GMA 13.4 に対応したら)

いつか

  • articles の本文格納方式の見直し(body カラムが varchar(512) で空のまま、本文はどこ?を整理)
  • hot_keyword_rankings / search_logs / temp_rss_load_times のリテンションポリシー策定
  • CakePHP 2 系の依存からの脱却検討
  • gp2 → gp3 ストレージへ移行(コスト追加 NG 方針につき凍結中、必要が出たら再開)
  • リードレプリカ復活(コスト追加 NG 方針につき凍結中)
  • cake_sessions Duplicate entry Fatal の対応(bot パススキャン由来、頻度上がったら格上げ)。候補: ALB/Apache で攻撃パス拒否 / Session 保存先を File or Redis に移行 / try-catch 握り潰し
  • footballnext-ios: AdAttributionKit (iOS17.4+) 対応(全ネットワーク横断、現状は SKAdNetwork のみ)
  • 過去試合の得点者バックフィル拡大(必要に応じ updateFixture で個別。全件一括はクォータ的に非現実的)

DECISIONS(意思決定)

意思決定記録

このプロジェクトで下した重要な意思決定を記録する。 最新が上に来る。


2026-08-23: 新着上部のスタメン発表状態はcaption_idの新値ではなく追加フィールドで配信する

背景: シート項目40(新着上部の試合情報をスタメン発表後に青「スタメン」へ切替)で、当初はサーバーの statusToCaption に caption_id=4 を新設して実装した。しかしlivesのS3 JSONは全配布ビルド共有で、旧ビルドは caption_id 0〜3 しか分岐を持たないため、未知の4を受けるとキャプションが白文字・無背景(xib既定値)で不可視になり、非表示のはずのスコア0-0まで表示される。バージョン別の出し分けはできない。

決定: caption_id/caption_titleは従来のまま維持し、lineup_announced(0/1・fnlive_fixtures.digest_lineups_available 由来)を行に追加配信する。新ビルドのみ caption_id==0 && lineup_announced で青背景#3c76d3「スタメン」に切り替える。共用composerの statusToCaption は変更せず、試合一覧(liveHistories)は従来どおり(仕様v2.17でも試合一覧にスタメンラベルは設けない)。

理由: 旧ビルドはJSONの未知キーを無視するため、追加フィールド方式は後方互換が構造的に保証される。caption_id=4案は配布直後から全既存ユーザーの新着画面を壊すため不採用。副作用として並べ替えバケットの変更も不要になった。

背景: ヘンク vs ウェステルロー(8/15)の試合詳細で日本人選手欄が空になった(シート項目25)。原因はAPI-Football側の選手成績配信が試合翌日まで遅延したことで、FT後3〜24時間に1回だけ再取得する従来のbackfillは、空振り(players_available=0)でも「劣化レスポンスではない」として digest_backfill_done=1 を立ててしまい、二度と再試行しなかった。直近30日で同症状が9試合あった。

決定: backfillの完了条件を「取得実績(digest_players_available=1)」に変更し、スタッツが取れるまで3〜72時間窓内の10分間隔cronで再試行する(1回きり→成功までリトライ)。窓も24h→72hへ延長。

理由: doneフラグの意味を「試行した」から「目的を達成した」に正す方が、遅延配信・一時的なAPI劣化のどちらにも効く。APIコストは最悪でも432req/試合×同時数件で1日クォータ7,500reqに対し無視できる。代替案の「窓だけ延長して1回きり維持」は試行タイミングの運任せが残るため不採用。EL/ECL予選のようにAPIが選手スタッツを恒久的に持たない試合は窓経過で自然に打ち切られる。

背景: トムソンから「MVP順位決定を一覧と同じG+A→評価→出場時間へ戻し、一覧先頭=MVPにしたい」と提案があった。しかし共通ソートは欠損項目をスキップして登録順フォールバックするため、統計未取得の選手が一覧先頭に来ることがあり(本番実例: 全statなしの北野が先頭・MVPは上田)、順位決定を揃えるだけでは一致が保証できなかった。

決定: 「今日の海外組」の選手一覧・MVP選定・シェア画像に限り、共通の並び(G+A合計→評価→出場時間、G/Aなしは並び替え上0として比較、評価なし・出場時間なしは取得済みより下位、表示は「-」維持)を使う。MVPは「ソート済み一覧の先頭に来る候補者」とし、シェア画像のMVP差し込み処理は削除。試合詳細など他画面の並び(欠損スキップ)は変更しない。こちらの逆提案をトムソンが承認し、シート項目23+仕様書v2.15として正式化された。

理由: 候補条件(1G以上/1A以上/評価7.0以上)と欠損下位ルールの組み合わせでは候補が非候補に必ず勝つため、候補がいる限り「一覧先頭=MVP」が構造的に成立し、差し込み等の特例処理が全廃できる。単純に順位決定だけ揃える案は欠損選手が先頭に残り一致が崩れるため、実例を示して仕様側の追加変更を引き出した。本決定により同日昼の「MVP先頭配置はシェア画像のみ適用」決定は失効。

2026-08-08: ダイジェスト対象大会は海外組/日本代表の2正典に分割し、cron取り込みは和集合で判定する

背景: 仕様v2.14で対象大会がID単位で明文化され(海外組14大会・日本代表5大会)、「試合機能とダイジェストの対象大会は個別管理」の方針が加わった。従来は単一の digestLeagueIds() に海外組と代表の大会が混在し、代表分は別途ハードコードもあった。

決定: DigestPresenteroverseasCompetitionIds()nationalCompetitionIds() の2正典を持たせ、表示側ガード・DataProviderクエリは各リストを参照する。Cronのfixture取り込み判定(_isDigestTargetLeague)だけは両リストの和集合 digestLeagueIds() を使う。

理由: 表示の判定基準は仕様書の2表と1対1に対応させたほうが今後のFB対応で迷わない。一方で取り込みは「海外組か代表かを問わず対象大会の試合を落とさない」ことが目的なので和集合が正しい。単一リスト維持+用途別フィルタの案は、今回のUECL漏れ(実況板には追加したがダイジェスト対象に入れ忘れ)と同種の事故を再発させやすいため不採用。

2026-08-08: MVPの「先頭配置」はシェア画像のみ適用し、アプリ内一覧は並び順を維持してMVP該当カードを強調する

背景: 仕様v2.14の新MVP順位決定(G+A→ゴール数→評価→出場時間)は一覧の並び(G+A→評価→出場時間)とタイブレークが異なるため、MVPが一覧先頭と一致しないケースが生じる。仕様書はシェア画像(10-4)にのみ「MVPは先頭配置」を明記し、アプリ内の並び替えには言及がない。

決定: シェア画像はMVPを先頭へ移動し(並び順上位3人に入らない場合もMVPを優先して掲載)、アプリ内の選手一覧は仕様明記の並び順を維持してMVP該当カードだけを強調する。この解釈は返信文の補足でトムソンに明示した。

理由: 一覧の並び順は仕様に明文があるため勝手に崩さない。シェア画像は「MVP先頭」が明文なのでそちらに従う。両者の乖離が起きるのはG+A同点でゴール数差があるなどの稀なケースで、疑義があればトムソン側から指摘が返る導線(返信補足)を用意した。

背景: 仕様v2.12の項目14〜18(シェア画像調整)が、温存していたシェアレーン未コミット差分(トムソン提供背景share_bgへの差し替え・シェア導線UI)と同じファイル(DigestShareImageRenderer等)に乗るため、コミットと配布の切り分けを決める必要があった。

決定: 部分stashで分離せず、シェアレーン差分と項目14〜18をまとめてコミットし(iOS b41fffa)、ビルド11に同梱して配布する。項目13は独立ファイルのため別コミット(3b25410)。

理由: レンダラー内で差分が絡み合っており分離コストが高い。提供背景の差し替えもトムソン確認対象として自然で、隠す理由がない。これで「シェアレーン温存中」の変則状態が解消され、以後の作業ツリーがクリーンになる。

背景: 仕様v2.11(項目11・12)で代表PRE_MATCH選手行と選手詳細クラブ版ヘッダーに所属リーグ国旗が必要になった。「絵文字ではなくアプリ管理の国旗画像」指定で、国旗画像のソースと国の判定方法を決める必要があった。

決定: serverが fnlive_teams.country(API-Football由来のクラブ本拠地国)を小文字国コード(nl/gb-eng等)へ変換して配信し、iOSは既存の日の丸アイコンと同じプログラム描画(13カ国・キャッシュ付き)で表示する。未知の国コードは国旗省略で安全側に倒す。国の判定に競技ID(competition.id)は使わない。

理由: ①api-sportsの国旗はSVGでSDWebImage非対応、flagcdn等の外部CDNは新規依存になる、アセット同梱は素材調達が必要——に対しコード描画は依存ゼロで「アプリ管理」の文言にも合致(既存の日の丸描画と同パターン)。②competition.idは次戦がCL/ELだと国が取れないため、クラブ本拠地国=国内リーグ国とみなすteam.country基準が仕様の意図(所属クラブが参加するリーグの国)に合う。モナコ等の例外国は現対象選手に不在で許容。

2026-08-04: 国内組選手も選手マスタのteam_idで紐づけ、対象リーグガードで副作用を遮断する(「J組=0が正」を撤回)

背景: 仕様v2.7の画面イメージで、招集メンバー全員(国内組含む)に所属クラブ名を表示することが確定。従来は「J組はteam_id=0のままが正」(team_idを入れるとJリーグの試合詳細に日本人選手欄が出る副作用があるため)としていた。

決定: J組選手にもJクラブのteam_idを紐づけ、代わりに「日本人選手欄・実況板ラベルはダイジェスト対象リーグの試合のみ」というガードをサーバー側に追加して副作用を遮断する。スカッド専用のクラブ列追加(DDL+新画面+二重管理)は採らない。

理由: 選手↔クラブの紐づけを選手マスタ1箇所に保てば既存のEdit画面(チーム名検索UI)がそのまま使え、移籍時の更新も1箇所で済む。ガード自体は本来あるべき防御で、追加して損がない。なお実況板にはJリーグの試合がそもそも配信されていないと後で判明し、ガードは保険として機能する形になった。

2026-08-04: エールディヴィジ(88)とスーペルリーガ(119)をダイジェスト対象リーグに追加する

背景: 2026夏の移籍で板倉(アヤックス)・上田/渡辺剛(フェイエノールト)・小川(NEC)・鈴木淳之介(コペンハーゲン)が両リーグ所属になり、実装時に固定した14リーグのままでは看板級5選手の試合が「今日の海外組」「これからの海外組」「最近5試合」に一切出ない状態だった(トムソンの「上田で確認したい」依頼で顕在化)。

決定: 対象リーグ正典 DigestPresenter::digestLeagueIds() に88・119を追加し、CronControllerに残っていたリーグ配列のコピーも共通参照へ一本化する。

理由: 対象リーグは「日本人選手の所属実態」に追従すべきもので、リストの二重管理は今回のような追加漏れの温床になる。API消費増はエールディヴィジ9試合/節+スーペルリーガ6試合/節程度で予算内。

2026-08-04: ダイジェスト期間設定の入力上限を0〜365に統一する(3層一致)/リリース前の設定戻しはトムソン側で実施

背景: トムソンが確認用に99を入力したいが「0〜30」バリデーションで弾かれていた(08-03)。制限はフォームだけでなくpresenter/providerの読み取りクランプにもあり、フォームだけ緩めても内部で丸められる構造だった。

決定: モデルバリデーション・presenterクランプ・providerクランプの3層すべてを0〜365に統一。確認後の設定戻し(3/1/7/1)はトムソン自身が管理画面で行う運用にする。

理由: 値の意味が「日数」なので365が自然な上限(代表戦のDB取得窓も±365日でそれ以上は無意味)。層ごとに異なる上限は「入力できたのに効かない」混乱の元。設定変更の主体をトムソンに移すことで、確認期間の調整を先方のペースで完結できる。

2026-08-01: 日本人選手欄の非表示判定は「所属ベース+実データ残存時は表示維持」にする

背景: トムソンFB指摘①は「両チームに表示対象の日本人選手が所属していない試合では日本人選手セクション全体を非表示」。文言どおり所属だけで判定すると、選手が移籍した後に旧クラブの過去試合を開いたとき、実際に出場した記録(stats行)まで消えるという副作用がある。

決定: server は「表示行が0件」かつ「両チームに表示対象選手が所属していない」の両方を満たすときだけ japanesePlayers=null にする(実データが残る試合は表示維持)。iOS は表示行0件のブロックをセクションごとスキップする防御を重ね、古いキャッシュJSONにも効かせる。

理由: トムソンの本質的な要望は「空の見出し+空カードを出さない」ことで、実データのある画面を消すことではない。所属だけの判定(文言どおり)は移籍後の過去試合で情報を失い、行有無だけの判定(iOSのみ)はサーバーJSONの意味が不正確なまま残る。両方の組み合わせが完了条件①〜⑤を満たしつつ実コンテンツを消さない唯一の解だった。

2026-08-01: ダイジェスト仕様書は v2.6 のまま維持し、FB対応は実装のみで行う

背景: トムソンFB指摘①の補足に「HTML仕様書のクラブ試合詳細にも同じ条件を追記してほしい」とあり、指摘②を反映した v2.6 にも①は未反映だった。一度 v2.7(①追記版)を作成した。

決定: 仕様書は v2.6 のまま修正せず(v2.7 は破棄)、フィードバック対応はアプリ/サーバーの実装のみで行う。トムソンへのDMにも仕様書更新には触れない。

理由: You の判断。仕様書の版管理はトムソンとのやりとりの中で必要になったタイミングでまとめて行えばよく、実装と同時に版を上げる必然性がない。

背景: 対象選手(fn_jp_players)のEdit画面のチーム欄が、FormHelper の *_id 自動select化+belongsTo無しの合わせ技で「空のプルダウン」になり設定不能だった。トムソンからチームID紐づけの方法を聞かれ、画面から操作できる状態にする必要があった。

決定: FnJpPlayer 専用の scaffold.form.ctp(1画面だけの差し替え・共通テンプレ不変)で Team ID を数値入力にし、直下に「現在のチーム名の常時表示+fnlive_teams の日本語/英語名LIKE検索→候補クリックでID自動入力」の補助を付ける。保存時は「0 or fnlive_teams 実在ID」のみ許可するバリデーションを追加。add 画面のPK(API-Football選手ID)入力も同テンプレで解禁。

理由: belongsTo でプルダウンを出す案は fnlive_teams が数千件規模でUIが破綻。ID直打ちのみの案は誤入力事故(id=0行への誤参照でゾリャ・ルハーンシク表示になった前例)に弱い。採用案はリーグ追加(addConfirm)と同じ「確認してから保存」の思想で、検索先がローカルDBなので API-Football のクォータも消費しない。

2026-08-01: 実況板リーグ追加は「API確認つき専用ページ」方式にし、cron補完経路は温存する

背景: トムソンからのリーグ追加依頼のたびに phpMyAdmin で fnlive_v3_leagues へ id を手挿入していた。ポータルには scaffold の「リーグV3」があるが、CakePHP FormHelper が主キーを強制 hidden にするため add で id を指定できず、実質使えなかった。

決定: scaffold の add を復活させる(フォーム差し替えで id 入力可にする)のではなく、専用ページ addConfirm(ID入力→API-Footballでリーグ名・種別・国・ロゴを取得して確認→追加)を新設。INSERT は id+name+type+logo を入れつつ team_created=0 のままにして、5分毎 cron fillLeaguesForV3 の実績ある補完経路を変えない。scaffold の New ボタンは disableIndexActions'Add' を拡張して非表示にし、追加導線を addConfirm に一本化。

理由: 生の id 打ち込みは打ち間違いで意図しないリーグが登録されるリスクがあり、追加前に API でリーグ名を目視確認できる方が運用ミスに強い(今回のユーザー要望そのもの)。また INSERT で team_created=1 まで自前で完結させる案は cron 経路とロジックが二重になるため、従来の phpMyAdmin 運用と同一挙動になる「id 投入だけ UI 化」に留めた。


2026-07-24: 海外クラブ非所属の代表選手も選手マスタに載せ、クラブ成績ブロックは丸ごと非表示にする

背景: 日本代表試合詳細から冨安選手をタップすると「選手情報が見つかりません」になった。原因は、代表の選手リストが代表戦の成績テーブル由来なのに対し、選手詳細APIは海外組ウォッチ用の選手マスタ(fn_jp_players)照合を通ることで、マスタ未登録の選手は必ず弾かれるため。代表招集されているがどの海外クラブにも所属していない選手(冨安・長友など)が該当し、代表スカッド26名中11名がこの状態だった。

決定: 該当11名を team_id=0(海外追跡クラブなし)でマスタに登録し、選手詳細ではクラブ直近5試合ブロック(見出し・評価グラフ・試合リスト・合計)を丸ごと非表示にする。あわせてAPI側で team_id<=0 のときはチーム解決自体をスキップする。

理由: 代替案は①マスタに載せず行をタップ不可にする ②載せてクラブ欄を空表示、の2つ。①は代表戦で活躍した選手の詳細が永久に開けず、代表直近3試合と関連ニュースという有用な情報まで捨てることになる。②は空のグラフや「出場0試合」の合計行が並んで壊れて見える。③(採用案)なら代表成績とニュースは見せつつ、持っていない情報は最初から出さないので破綻がない。なお team_id=0 のままチーム解決すると fnlive_teams の id=0 のゴミ行に誤一致して無関係なクラブ名(ゾリャ・ルハーンシク)が出るため、APIガードは必須。


2026-07-22: ダイジェスト実装レビューでの主要裁定3件(試合一覧拡大・代表対象大会・fixture運用規約)

背景: Codex分業実装(M0〜M7)のマイルストーンレビュー中に、仕様の解釈・運用に関わる裁定が必要になった。

決定:

  1. 試合一覧への試合増加は既定=許容: 日本人選手所属チームの試合取り込みで既存「試合」タブに試合が増える件は、仕様の意図(日本人選手あり表示の追加)と整合するため許容で実装。トムソンに確認中で、否なら後付けできるよう fixture採用判定を CronController::_shouldImportFixture() 1箇所に集約した。
  2. 代表戦の対象大会にアジアカップ(league 7)・W杯アジア予選(league 30)を追加: 仕様書は代表戦の大会を限定しておらず、W杯・親善のみだと2027年1月のアジアカップで代表戦ナビが空になるため。リーグIDは実APIで確認済み。
  3. iOSテストデータの手書き禁止: 手書きfixtureと実サーバー契約のドリフトがLIVE表示バグ(経過時間二重表示)を隠したため、iOSの dev-fixtures/ はサーバーの test_digest_presenter.php 生成物を sync_from_server.sh で同期する方式に規約化。

理由: 1は仕様整合+エンゲージメント面のプラスで、リスクは1箇所集約により可逆。2は「過去・現在・未来の代表戦を横断」という仕様の趣旨を、実カレンダー(アジアカップが半年後)に照らして優先。3はcontract driftという再発性の高い事故クラスへの構造的対策。

2026-07-22: トムソン提案「海外組・日本代表ダイジェスト」を実装可能と回答・データ取得は既存ovserveFixtures拡張方式を基本に

背景: トムソンから新機能の開発仕様書を受領。仕様書内で「現行APIプラン内で運用可能かは実装前に検証(要確認)」とされており、着手判断の前にAPI実現性の確定が必要だった。

決定: RapidAPI API-Football を実叩き検証(約25req)した結果に基づき「実装可能」と回答する。データ取得は新規ポーリング系を作らず、既存 ovserveFixtures(v3 /fixtures?id==events/lineups/選手stats同梱1req・2分間隔)の監視対象を日本人選手所属チームの試合へ拡張し、現在捨てている選手statsをDB保存する方式を基本とする。

理由: 全対象リーグ+CL/EL+代表戦(親善含む)で評価・出場時間・G/A・lineupsの提供を実データで確認。最繁忙日でも+2,700〜3,400req/日=現行プラン7,500req/日内に収まる試算。既存機構の拡張なら実装量・リクエスト数とも最小で済む(ピーク超過が見えたらポーリング3〜5分間隔化で調整可能)。

2026-07-22: 選手・クラブの日本語名はClaude併用の名前解決で初期リリースから対応する

背景: API-Football の名前はローマ字表記のみ。仕様書は「日本語名が間に合わなければ既存表示名(英語)で先行し早期アップデートで切替」というフォールバックを許容していた。

決定: フォールバックを採らず、初期リリースから日本語名表示を狙う。ローマ字名+所属チームを入力に Claude Code(サブスク内)で日本語名を解決し、WebSearch裏取り(クラブ公式/JFA/Wikipedia日本語版)+needs_reviewフラグで人間確認を挟んで選手マスターへ反映する。DB反映経路は初期はCSV/管理画面取り込み(人間confirm自然発生)→精度確認後に管理API直叩きへ移行。クラブ名・リーグ名の日本語表記も同じパイプで整備(チームマスターに日本語名カラム追加)。

理由: 対象規模が小さく(海外組50〜100人+代表招集の増分)サブスク内運用で十分回る。有名選手はほぼ確実、無名若手・カットオフ後移籍のローマ字→漢字曖昧性(一対多)だけが精度リスクで、WebSearch裏取り+review運用で実用精度にできる。トムソンへは「日本語名は初期リリースから対応見込み」と格上げして回答できる。

2026-07-07: 起動時広告A/B 修正版5.5.5リリース済・コホートparam本番着弾を確認・v9はmin_build=50505で確定

背景: 型ズレ修正版は Apple 5.2.3 却下(YouTube discovery)対応で YouTube機能を一時非表示にし 5.5.5 として 2026-07-06 リリース(5.5.4 は未リリース)。翌 7/7 に GA4 Data API で着弾検証。

結果(合格):

  • appVersion=5.5.5 で 5つの dimension が全て non-empty 着弾を確認(7/6-7/7): target_cohort_day=ISO(2026-07-06等)・enrollment_day=ISO・days_since_enrollment=d00d13config_version=cv_8kill_observed=false。旧 5.5.3 は従来どおり (not set)=型ズレ修正が本番で成功。
  • 予測どおりの汚染を実測: 5.5.3で既enroll→5.5.5更新勢が古い enrollment_day(6/24等)+d07〜d13 を送出。→ 解析は enrollment_day >= 2026-07-06 フィルタで除外する。

決定:

  • v9 config の min_build は 50505(当初 50504 は修正版=5.5.4 想定だったが実リリースは 5.5.5)。5.5.3/未リリース5.5.4 を確実に除外。
  • enrollment 窓 = 7/6 起点(start=1783263600 / end=1786892400 = 2026-07-06〜08-17 JST・42日)。
  • 雛形 ab_test_config.v9.template.json を確定値で更新済。S3 配置(aws s3 cp ... --acl public-read --profile thomson-ik)は You が実施。配置で experiment 実質再スタート。

理由: 実リリースが 5.5.5 になったため min_build を 50505 に合わせないと未修正ビルドが混入し得る。着弾検証が通ったので、あとは v9 配置で新コホートをクリーンに観測開始できる状態。


2026-06-28: 起動時広告A/B 5.5.4で型ズレ修正・experiment_id据置で v9(min_build=50504)実質再スタート

背景: コホート日付param型ズレを 5.5.4 で修正(別エントリ)。v8/5.5.3 期間の cohort データは復元不能のため、5.5.4 配信後を新コホート境界として実験を実質再スタートしたい。クリーンに切るなら新 experiment_id で再無作為化が理想だが、アプリ側 ABProtocol.experimentId(ハードコード)+validate() が config の experiment_id / measurement_protocol_version / event_schema_version 一致を要求するため、config だけでは experiment_id を変えられない(変えると 5.5.4 が fail-close で実験OFF)。

決定:

  • アプリバージョンを 5.5.4 に更新(build符号 50504)。型ズレ修正の配信版。
  • experiment_id は据え置き("app_open_interval_2026q3")。新 experiment_id 再無作為化は更なるアプリ再ビルドが要るため今回はやらない。
  • config v9 で min_build=50504 に引き上げ=5.5.3 端末を実験から完全除外(5.5.3 では config invalid→experiment OFF)。
  • enrollment 窓を 5.5.4 ライブ日起点で取り直す(旧 6/24 起点窓は壊れデータ期間を含むため流用しない)。窓長42日据え置き。
  • 解析母集団は enrollment_day >= 5.5.4配信日 でフィルタ=5.5.3→5.5.4 更新でドラッグインする既enroll勢(古い enrollment_day・初期に壊れた param)を除外。
  • v9 雛形 ab_test_config.v9.template.json を用意(固定値埋め済・enrollment 2日付けのみ配信日から算出)。配置は 5.5.4 が store ライブ後。

理由: experiment_id 変更が config 単独で不可という実装制約(fail-close 設計)下で、クリーン境界を「min_build による旧ビルド除外」+「enrollment_day による解析フィルタ」の2点で代替する。後者は今回 enrollment_day を実際に着弾させたからこそ機能する。窓の取り直しは初日コホート空・壊れ期間混入を避けるため。再無作為化を諦める代わりにアプリ再ビルドを増やさず、既存の永続割当を保ったまま新規 cohort をクリーンに観測できる。


2026-06-28: 起動時広告A/B maturation明け first-look=機構健全だがコホート日付param全件空(解析の根幹が欠落)

背景: dev-timer の「maturation明け」通知(点火 2026-06-22 20:40・enrollment窓 6/24〜8/5・maturation 3日)を受け、watermark=6/25 で first-look サニティを GA4(property 211701453・experiment_id=app_open_interval_2026q3)で実施。これは最終判定ではなくガードレール④(成熟後 残差率)が初めて回せる段階の健全性確認。

発見:

  • 機構は健全: SRM 無し(A=4720/B=4712 installs・χ²≈0.007・p≈0.93)/applied_interval=A:50・B:1440 で群別policy反映/kill_observed=true は0件/主要funnel event は両群で着弾。
  • 🔴 コホート"日付"系 param が全イベントで100% (not set)/空: target_cohort_day enrollment_day days_since_enrollment が experiment_enrolled・target_launch・content_reached・launch_no_content・fn_ad_revenue のすべてで値を持たない。experiment_id/assigned_group/applied_interval_minutes/kill_observed は正常着弾=永続enrollレコードから算出する日付3 param のみが配線/値算出で死んでいる
  • 方向性(イベント日基準・未成熟混在の参考値): 離脱率 launch_no_content/target は A=19.7%・B=15.8%(B が ~3.9pp 低く仮説の向き)。ただし未解決率 A=6.5%・B=4.0% の群間差2.5pp がガードレール④の保留条件に該当し、精緻化に要る maturation 法が下記欠陥で使えない=現時点で結論は出せない

決定/帰結:

  • ガードレール④(target_cohort_day による成熟後 残差率)と 円/人(days_since_enrollment による enroll後N日売上÷割当人数)は現状データでは算出不能。assigned_group は生きているので群レベル比較は可能だが、maturation/cohort 解析契約は満たせない。
  • 欠陥修正は時間との勝負: 正典 SPEC明記の通り「事前テスト後の param 追加は過去データを復元できない」。放置するほど cohort 解像度を失う。→ 次アクションは footballnext-ios で日付3 param の送信箇所を特定し原因究明・修正(修正方針が見えた時点で You 判断)。

理由: maturation 明けの first-look の主目的が、判定前にこの種の計測欠陥を捕まえることそのもの。点火機構(割当・config反映・kill)は正常なので実験は走っているが、解析契約の中核 param が欠けたままでは主指標を裏取りできないため、修正を最優先に切り替える。


2026-06-19: 起動時広告A/B 点火前の判定値を確定(GA4追加2件・enrollment窓はリリース起点・min_build=50503)

背景: 実装レビュー sign-off 後、点火前チェックリストの「GA4登録」「enrollment窓・判定値の確定」を詰める段。GA4 Admin MCP は未接続のため登録は手作業、窓は 5.5.3 リリース日に依存。

決定:

  • GA4 追加登録は2件のみ(既登録21件に対し): kill_observed(event-scoped dimension)/applied_interval_minutes(custom metric・分)。登録後 DebugView で app_open_ad_decision に乗るか確認。
  • enrollment 窓は窓長42日固定・起点は 5.5.3 が store ライブになった日(+反映猶予 数日)を点火時セット。旧叩き台 2026-06-22 起点は 5.5.3 未提出で過去化のため流用しない。end = start + 42日
  • build gating min_build=50503(5.5.3+ 限定)。round1〜8 の全修正は 5.5.3 積載のため旧ビルドを実験から除外。max_build なし。
  • interval_a=50 / interval_b=1440 / 締切42日 / 追跡28日 / ITT / required_assignment_version=1 / ttl 14日 は既凍結のまま。

理由: GA4 は登録前データが遡及されないため点火前必須だが差分は小さい。窓をリリース日に固定するのは「ビルドが store に出る前に窓が開くと初日コホートが空/旧ビルド混入で歪む」ため。5.5.3+ 限定は未修正コードの native 収益欠損・decision 矛盾を実験に混ぜないため。


2026-06-19: 起動時広告A/B native expire/lease lifecycle の最終形を確定・kill_observed_at 廃止・実装レビューで点火 sign-off

背景: 実装レビュー round5〜8 で、flag外の MAX native lifecycle(lease/timeout/expire)と kill 計測属性に残 medium が連続して出た。固定遅延 destroy・stock限定 expire・puts での無効化はいずれも段階的に不十分で、reviewer が更に詰めた。native は experiment flag 外で全ユーザーに作用するため収益欠損(無効広告の計上/枠消失)を出さない fail-closed が必須。

決定:

  • native の destroy は「released(row利用終了) or expired」かつ「displayLeases==0(表示中cell無し)」のときだけ(固定秒数廃止)。expire 済みは loader(for:) を nil にして render/impression を厳密に止める。
  • stale MAX ad の判定は CoreTableView.cellForRowAt の deque/nib選択より前に置き、同一 pass で新 stock を再deque(puts まで遅らせない)。manager→row 逆参照は持たず、Any? 型非依存 API で共通 CoreTableView を AppLovin 非依存に保つ。
  • impression は renderNativeAdView==true のときだけ計上(render失敗の過大計上を排除)。callback は全て main 集約+timeout 世代照合。
  • kill_observed_at を廃止。GA4-only では未登録 param を事後参照できないため、停止後区間の除外は必須 dimension kill_observed、global 停止境界は S3 で experiment_active=false にした時刻の運用記録で扱う。
  • 実装レーンは round8 sign-off をもって実質完了とし、以降は点火前チェックリスト(GA4登録・値確定・実機スモーク・点火/停止時刻記録)の運用準備へ移行。

理由: 逆参照を持たない一方向設計(destroy→loader nil→cellForRow前段でstale再deque)は CoreTableView の汎用性を保ちつつ実装が小さく、main 集約で race を避けられる。固定遅延 destroy は表示終了を保証せず、stock限定 expire は deque後4h超の再表示を取りこぼし、puts での無効化は再deque を保証できなかったため、いずれも棄却した。kill_observed_at は GA4-only 契約と整合せず実用価値が無いため削除。


2026-06-19: 起動時広告A/B 実コードのCodexレビューを必須化・点火を撤回して修正優先・5.5.3据え置き

背景: 提案書・実装計画の机上Codexレビューを通過し点火(active=true)まで実施したが、実装コードのCodexレビュー(ab-test-implementation-review)round1/2 で high/medium 多数が判明。特に「非参加者へのB処置漏れ(policy/measurement未分離)」と「session config固定+短TTLで日次ユーザーが永久非参加」は実験を不成立にする重大バグだった。

決定:

  • 点火を撤回し緊急停止(S3 config を active=false+config_version+1)。本番中立を最優先し、実装が正しいと検証できるまで再点火しない。
  • 実装コードのCodexレビューを必須プロセス化(机上レビューだけでは出ないバグがあるため)。round1→round2と回し、round3まで継続。
  • config snapshot は session全体固定でなく context世代方式(beginTargetContextで last-known-good を1回読み record に固定、fetch結果は次context有効)。session固定は日次ユーザーを壊すため却下。TTLは6h→14日(launch間隔より十分長く)。
  • policy/tracking は canEnroll でなく「既参加(enrolled)+追跡窓内(JST暦日<28)+active/fresh|cached」で判定(isParticipating)。非参加者へのB漏れと締切後の追跡停止を両方解消。
  • native loader 修正は点火ブロッカー(当初「A/B非交絡・収益別経路」で先送り判断したが、MANativeAdLoader.revenueDelegate が weak=誤対応付けでILRD欠損しうる、とのCodex指摘で撤回)。実機/TestFlight検証必須
  • バージョンは 5.5.3 据え置き(未提出のため番号再利用。全修正を5.5.3に積み、検証完了後に提出)。

理由: A/Bの主指標(1人あたり収益)と GATE(未到達率・収益再現・全枠callback品質)を満たすには、処置と計測の母集団一致・config世代の決定性・収益callbackの完全性が前提。これらが崩れたまま点火するとデータが汚れ無管理に減収する。机上で5ラウンド見ても実コードのTTL相互作用・delegate weak・状態未分離は出なかったため、実装レビューを独立工程として必須化した。


2026-06-19: 起動時広告A/B の enrollment 窓を確定・点火は段階A/Bの2段運用でトムソン確認は叩き台確定扱い

背景: 点火前の残作業(トムソン要確認3項目・enrollment期間・S3配置)を処理する段で、事前登録の作法と本番の不可逆性をどう両立させるかを決める必要があった。

決定:

  • enrollment窓 = 6週間(叩き台どおり)enrollment_start_at=1782054000(2026-06-22 月 0:00 JST)/enrollment_end_at=1785682800(2026-08-03 月 0:00 JST)。開始をJST深夜0時の月曜境界に置く(enrollment_day のコホート日が揃い解析がきれい)。canEnroll は now ∈ [start, end)。
  • トムソン要確認3項目は叩き台のまま確定扱い。Youより「自分が任されているのでトムソンへの確認は不要」と判断。通知物はHTML化して thomsons S3 に公開済(事前登録の記録として残す)が、合意待ちで点火をブロックしない。
  • 点火を段階A/Bの2段運用に分離: 段階A=不活性config(active=false)を本番S3へ先行配置(中立・挙動不変、本番S3取得経路の確認用)。段階B=experiment_active=trueconfig_version=2 で再アップロード(実点火)。今回は段階Aまで実行し段階Bは保留(Youの明示goで実行)。

理由: enrollment窓開始を未来日(6/22)に置くと、段階Bを今点火しても窓開始まで誰もenrollしない=S3配置の機械的タイミングと解析窓の開始を分離でき、週末をpre-flight期間にできる。段階A/B分離は、本番S3への書き込み・パース健全性を実ユーザー挙動を変えずに先行検証するため。トムソン確認は事業判断としてYouに委譲されており、叩き台で十分動くため点火ブロッカーから外した。


2026-06-19: 起動時広告A/B の割当ハッシュ定数を凍結・計測モジュールは Foundation-only で実装

背景: BASE 実装着手にあたり、群割当の具体定数(GATE-値の一部)を決める必要があった。また、計測ロジックは本番起動パス・広告収益コードに深く絡むため、検証可能性を担保したかった。

決定:

  • 割当ハッシュを凍結: input=UTF-8("experiment_id:install_uuid") → SHA256 → 先頭8バイトを big-endian UInt64 として % 100 → bucket_map [0,50)→A / [50,100)→Bexperiment_id="app_open_interval_2026q3"assignment_version=1measurement_protocol_version="mp_2026q3_r1"。テストベクトルを ABTest.verifyTestVectors() に固定。ABProtocol(リポジトリ凍結=measurement_protocol 相当)に集約。
  • 新 experiment では再無作為化を採用(hash 入力に experiment_id を含むため、同一 install_uuid でも別 experiment では別 bucket になる)。bucket 再利用案は採らない。
  • 計測コア(ABTest.swift / ABLaunchTracker.swift)は Foundation のみに依存させ、GA4送信は emit クロージャ注入で外出し。→ アプリ全体をビルドせず standalone swiftc で状態機械を単体検証できる構成にする。広告SDK/Firebase に触る部分(ABAdCollector/ABConfigLoader/AppDelegate配線)は別ファイルへ分離。

理由: GATE-値のうち「割当の決定規則」は実装と同時にテストベクトル付きで固めないと検証できないため先行確定。再無作為化は実装が最も単純(hash入力にIDを含めるだけ)で、フェーズ2の experiment 切り替え時にも公平性が保てる。Foundation-only 分離は、本番クリティカルパスのロジックをフルビルド・実機なしで反復検証可能にし、安全に段階実装するための土台。


2026-06-18: 起動時広告A/B 実装計画を Codex 5ラウンドで設計レビュー完了(配送は at-most-once に転換)

背景: トムソン承認後、実装着手の前に実装計画HTMLを作り、Codex(gods-talk)で繰り返しレビューした。ローカルコードを直接読ませたことで、机上設計では見えなかった穴(delegate未配線・write-ahead保存の必要性・収益event二重計上・総売上母集団の非識別性など)が判明した。

決定:

  • 実装計画 rev6 と正典 AB_TEST_MEASUREMENT_SPEC.md rev6 をもって 設計レビューを完了とする。以降は再レビューラウンドを設けず、実装PRの受入チェックで確認する(round5でCodexが「追加の大規模レビューは不要」と判定)。
  • 実装は 3レーンで進める: BASE(着手可)/BASE契約(永続model・collector・logger API 確定前に潰す5点)/GATE-値(schema凍結前に measurement_protocol として固定)。
  • GA4への計測 event 配送は at-most-oncepending→attempted を先に確定して一度だけ送信、欠損は成熟後残差率に残す)。収益 event は fn_ad_revenue に一本化app_open_ad_paid 廃止)。設定は 3 artifact 分割(assignment_manifest / activation_config / measurement_protocol)。
  • 確定本文は 正典 SPEC.md を唯一の source of truthとし、実装計画HTMLはその投影と位置づける。

理由: GA4-onlyかつ launch_id を出さず BigQuery 不使用の制約では exactly-once も at-least-once(dedupe不能)も成立しないため、二重計上を防ぐ at-most-once を選択。代替(at-least-once+GA4 dedupe)は本構成では実現できないと判断。収益の二重計上回避のため canonical event を1つに固定。レビューは5ラウンドで指摘の質が逓減し収束したため、これ以上の往復より実装で詰める方が有効と判断。


2026-06-18: 起動時広告A/B 提案書を確定(Codex2ラウンド反映で設計を精緻化・同日の設計を一部上書き)

背景: 同日午前の設計(下のエントリ)を提案書化する過程で、議論と Codex(gods-talk)2ラウンドのレビューにより、いくつかの前提が誤り・脆弱と判明した。実装と解析の正典を固める必要があった。

決定(同日午前の設計を以下に更新):

  • 群Bは「1日1回」→「24時間に1回(rolling)」。群Aは「毎回」ではなく現状=最短50分に1回(本番S3 app_open_ad_interval=50 を実測確認)。
  • データ基盤は GA4のみ・BigQuery不使用(無料運用)ab_group 集計のため GA4 を Viewer→Editor へ権限変更をトムソンに依頼。
  • 判断は統計的有意差でなく 「事前固定した実務閾値での記述的判断」。主指標1(1人あたり売上)+ガードレール(継続率・クラッシュ率)+終了日 の最小骨子だけ事前固定、閾値は別紙で合意し計測誤差内は判定保留、最終採否はトムソン。「微減+良化=即採用」は撤回し回収試算の上で判断。
  • 解析は ITT(広告表示の有無に関わらず割当全員)。
  • お金: 起動時は群別 didPayRevenue 実額合計を主値(表示回数×eCPMは検算)、それ以外は混合平均eCPMで概算。月次総売上は「iOS・Football NEXT・対象月」に範囲を揃える(Android/旧版混在は不可)。
  • 残り具合: launch_bounce「コンテンツ未到達離脱率」 に改称。判定は起動秒数でなく「最初のバックグラウンドまでにコンテンツ接触ゼロか」、content_ready(描画)≠content_visible(広告の背後でなく実際に見えた)を分ける。D7/D14 起点=実験参加日。
  • 「起動時広告 vs 記事内広告 どちらが主因か」は本実験では因果確定せず(変えるのは起動時頻度のみ)→ 中立な表現に。
  • 計測の正典を .devnotes/AB_TEST_MEASUREMENT_SPEC.md に集約。提案書と矛盾したら SPEC を優先。

理由: BigQueryは Owner 権限(info@thomsons.jp)と課金が要り Sandbox でブロック=無料の GA4 のみが現実的。GA4集計だけでは厳密な有意差が出せないので表現を実務閾値判断に落とすのが誠実。群A実測が50分だったため処置差は想定より小さく、期間・最大減収はMAUでなく事前テストで再試算が必要。content_ready は全画面広告の背後で発火し群Aを過小評価しうるため content_visible 必須。

2026-06-18: 起動時広告A/B 提案書を thomsons S3バケットに公開アップロード

背景: 提案書(HTML)をトムソンに共有する必要。既存の review-20260605.html 等を置いている thomsons バケットがある。

決定: 提案書を thomsons バケット(profile thomson-ik/ap-northeast-1)に object ACL public-read で公開アップロード(既存HTMLと同方式)。URLは https://thomsons.s3.ap-northeast-1.amazonaws.com/起動時広告A-Bテスト_提案書_20260618.html

理由: 同バケットは公開HTML共有の前例があり BPA無し・ACL公開可。アップ前に秘密情報スキャンを実施し、メール/APIキー/GA4 property ID/広告ユニットID 等が含まれないことを確認済み。URLを知れば誰でも閲覧可なので取り扱いは注意。

2026-06-18: 起動時広告の削減は「全消し」でなく「50/50無作為A/Bで実測判断」する

背景: 多角レビューの最優先施策は「広告UX是正」。起動時広告は総売上の約25%(トムソンヒヤリング)で、減らすと収益の即時下落が確実な一方、リテンション改善は遅く不確実という非対称な賭け。トムソンは「満足度を測れない」と削減に消極的。決定権はトムソン側にあり、提案を通せる形にする必要があった。

決定:

  • 起動時広告を全消しせず、頻度を絞るダイヤルとして扱い、50/50 の無作為A/B(群A=現状/群B=1日1回)で得失を実測してから判断する。
  • 「満足度」ではなく行動指標で測る:①ユーザーあたり広告収益(impression×eCPM)、②離脱=launch_bounce・週次セッション・D7/D14継続。
  • 収益測定はトムソンの日次データに依存せず自前のimpressionカウントを土台にし、金額は月次総売上1個から2バケツ(起動時=MAX実額/それ以外=混合平均eCPM)に割る方式とする。
  • 採否は3パターン判断(同等以上→即採用/微減だが残り良化→採用/残り不変→戻す)。勝った間隔は S3 設定で全員展開。

理由:

  • 群Aに現状を半分残すので、最悪でも全体の半分は売上を維持=トムソンが許可しやすい(賭け額が見える)。前後比較では自然減(MAU −15%/年)に埋もれるが、無作為A/Bなら自然減が両群に等しくかかり相殺され因果がクリーン。
  • launch_bounce は「率」で自然減に埋もれず、起動時広告に最も因果がタイト。これがトムソンの「記事内広告が主因」説への反証材料にもなる。
  • 広告構成がパッチワーク(MAX外に AdG/Zucks/LINE)で MAX の didPayRevenue だけでは収益を捕捉しきれないため、全SDK共通で取れる impression カウントを測定の土台に据えるのが堅い。didPayRevenue は MAX スライスの実額+月次の答え合わせに用途を限定。

2026-06-13: iOS の YouTube 動画は自動再生せず、タップで再生開始にする

背景: YouTube プレイヤーの WKWebView 化(ナレッジ ios-wkwebview-youtube-inline-playback 適用)に伴い、2015年以来の autoplay=1(動画詳細を開いた瞬間に自動再生)を維持するか選ぶ必要があった。

決定: embed URL を autoplay=0 にし、ユーザーのタップで再生開始する挙動に変更する。

理由: You の明示指示。意図しない音出し・通信を避け、ナレッジの推奨値(7channel 実績構成)とも一致する。

背景: 定期実行の呼び出し元が「cron 専用 EC2」と「AWS Lambda(EventBridge)」に二分。Lambda 側の TFN_cron 等は fnext.thomsonsapp.com を叩くだけの curl ラッパーで、Baseball は同等処理を既に cron 上で実行済みだった。

決定: curl ラッパー型の定期実行は cron 専用サーバーに寄せ、対応する Lambda + EventBridge ルールは削除する。実ロジックを持つ Lambda(rss-crawler-notice-prod-crawlArticle 等)は当面残す。

理由: ラッパーは cron の1行で完全等価に置換でき、二重管理・nodejs8.10 廃止ランタイム・不要コストを解消できる。実ロジック型は移行に設計(サーバ側エンドポイント化)が要るため別扱いとした。

2026-06-06: 稼働ゼロ Lambda の削除は「invocation実測+トリガー+キャッシュ有無」で判定する

背景: 大量の旧 Lambda(TSP系・sports-paper系・test系)が残存。削除可否を安全に判断する必要があった。

決定: ①直近7日/90日の invocation を日次バケット合算で実測(長期間1バケットは過少値になるため不可)②resource-policy/EventSourceでトリガー有無を確認③API Gateway 経由はステージのキャッシュ無効を確認して「0回」が本物だと裏取り——の3点が揃えば削除。コードは毎回バックアップしてから実行。共有リソース(IAMロール ichirokisanuki_lambda、SNS IchiroKisanukiAlert)は触らない。結果 42→9関数。

理由: API キャッシュが効いていると invocation 0 でも現役の可能性がある。三点確認で誤削除リスクを排した。実際 footballnext-oshirase-v2(90日0)は iOS にコード参照が残っていたが、キャッシュ無効=真に未到達と確認できたため据え置き判断ができた。

2026-06-06: 削除済み footballnext-oshirase-v2 は復元しない(デッド画面として次回整理)

背景: 90日invocation 0 を根拠に削除した footballnext-oshirase-v2 を、iOS OshiraseViewController.swift:30 がまだ参照していると後から判明。

決定: 復元せず据え置き。次回アプリ改修で OshiraseViewController ごとデッドコードを除去する。

理由: ステージのAPIキャッシュ無効を確認済みで、90日ゼロ=実際に誰もこの画面に到達していない(到達不能 or 廃止導線)と判断。バックアップ(/tmp/lambda_cleanup3/footballnext-oshirase-v2.zip)は保持しているため、万一必要になれば復元可能。

2026-06-05: Football NEXT のマネタイズ方針 ─ 広告密度を「最大化」から「最適化」へ転換する

背景: 広告ネットワーク売上が2019ピークから2025で−69%、下落が加速。原因を多角レビュー(広告売上・全レビュー3,987件・GA4行動データ・Codecの広告実装解析の6ソース)で診断したところ、広告増量・評価崩落(平均星3.0→1.33)・MAU縮小(−32%)・売上下落が時系列で一致し、広告過多による「デススパイラル」と判定された。

決定: 広告impression最大化路線をやめ、離脱を止めて母数を維持する「最適化」路線へ転換する。具体的には ①App Open Ad のスロットル(AppDelegate でコメントアウト中の頻度ゲート)を復活、②不快クリエイティブのブロック、③広告挿入間隔の緩和を最優先とし、中期で有料広告なし課金(StoreKit)を新設して収益を多様化する。効果検証は AdMob面別データが取れないため GA4 の離脱シグナル(MAU/セッション/継続率)で代替する。

理由: 広告増量は短期impressionを稼ぐが、レビュー・GA4の両面でロイヤル層の離脱を招き、母数減でかえって総売上を押し下げている蓋然性が高い。残存ユーザーはエンゲージ率90%・月35セッションの高価値層で、広告で焼くより保全+課金する方が中長期の期待値が高い。

2026-06-05: GA4 を MCP として接続し、行動データをレビューの常用ソースにする

背景: 当初レビューは継続率・MAU等の行動データが無く代理指標で補っていた。Football NEXT は Firebase Analytics を実装済みで GA4 にデータがあると判明。

決定: Google公式 google-analytics-mcp(pipx) を claude mcp add ga4 -s user で登録(property 211701453=footballnext-bca77、認証は既存ADC流用、property固定)。今後の footballnext 分析で GA4 を常用ソースにする。

理由: Firebase連携の GA4 が既にデータを持っており、サービスアカウント新規発行より既存ADC流用が最短。user スコープにして他プロジェクトからも再利用可能にした。AWESOME FOOTBALL(366217595) は無関係なので除外。

2026-06-03: 得点者データは fnlive_fixtures.events_json に生 events を保存し、整形は配信API側で行う

背景: 試合詳細に得点者(得点時間・選手・アシスト・PK/OG)を表示するため、データの保存先と整形場所を決める必要があった。

決定: 専用テーブルは作らず、既存 scores_json と同パターンで fnlive_fixtures.events_json(TEXT)に API-Football の events 配列を生のまま保存。表示用の scorers 整形(Goal 抽出・home/away 判定・PK/OG・アシスト)は配信API V2Controller::fixture() に集約。OG は API が「得点が入る側」に team を割り当てているため team.id反転せず素直にマップ(実APIで検証しスコア一致)。

理由: 試合あたりの events は高々数十件で常にセット取得=JSON カラムで十分(新テーブル/関連は過剰)。生保存なら将来カード/交代表示に流用可。整形を1箇所(既存の status/score 整形と同じ場所)に集約すると保守が楽。単一試合レスポンスに events が同梱されるので追加 API コールも不要。


2026-06-03: iOS の得点者カードは記事テーブルの tableHeaderView に手動レイアウトで載せる

背景: 試合詳細はスコアボード用テーブル(高さ固定 184・自己サイズ非対応セル)+記事テーブルの2段構成。得点者カードをどこに置くか。

決定: スコアボード用テーブルには手を入れず、LiveFixtureScorersView(手動フレーム+sizeThatFits で高さ確定)を記事テーブルの tableHeaderView に設定する方式を採用。当初スコアボードテーブルに section 追加+動的高さで実装したが、固定 184 で見切れ・estimatedRowHeight=0 でスコアボードセルが崩壊した。

理由: スコアボードセルが自己サイズ非対応のため同テーブルでの自己サイズ運用は破綻しやすい。記事テーブルのヘッダーに手動レイアウトで載せる方式は自己サイズの曖昧さに依存せず確実で、視覚的にもスコアボード直下・記事の上で仕上がりイメージ通り。


2026-06-03: 過去試合の得点者は updateFixture で個別バックフィル(全件一括はしない)

背景: アプリは過去試合も履歴から閲覧できるため、デプロイ前に終了済みの試合(events_json が空)にも得点者を出したい。一方 RapidAPI にはクォータ制限がある。

決定: 全履歴の一括バックフィルはせず、updateFixture($id)(終了試合もスキップせず強制更新)で必要分を個別に埋める。今回は直近10試合のみ実施。今後終わる試合は ovserveFixtures が終了時に自動で events_json を保存するため対応不要。

理由: 何千試合もの一括取得はクォータを食い潰し非現実的。閲覧されうる直近分だけ個別に埋めれば実用上十分でコストを抑えられる。lazy 取得(閲覧時に API)はリクエスト経路に外部呼び出しが入るため見送り。


2026-05-30: footballnext-ios は GitHub クローンで作業し、Pods はコミットしない

背景: operation 配下の footballnext-ios が、GitHub 移管前の CodeCommit クローンのままだった(remote=CodeCommit、Pods を丸ごとコミットする旧運用)。GitHub 側は移管時に履歴を書き換えて>50MBバイナリを除去し Pods/ を .gitignore 済み。push しようとして発覚(CodeCommit 認証切れ)。

決定: GitHub ichirokisanuki/footballnext-ios(master) を正とし、operation 配下を GitHub クローンに置換。Pods は コミットしない(.gitignore、pod install で都度復元)。今回のソース変更のみを移植して push(28f5fe7)。旧 CodeCommit クローンは ~/Downloads/footballnext-ios-codecommit-backup へ退避。両リポジトリは履歴分岐のため相互 push/force は禁止。

理由: GitHub は100MB超ファイルを拒否し、今回 Pangle バイナリが 124MB=そもそも Pods は載せられない。Pods 非コミット+pod install 運用が唯一現実的で、移管時の方針とも一致する。CodeCommit は実質終了済み。


2026-05-30: footballnext-ios の最小 iOS を 12→13 に引き上げ(iOS12 サポート終了)

背景: MAX の広告SDKを最新化する中で Google Mobile Ads を 12→13 に上げる必要があり、GMA 13 は最小 iOS 13 が必須。

決定: デプロイメントターゲットを iOS 13 に統一し、iOS 12 サポートを終了。これにより GMA 13.3 / InMobi 11.3 等の最新アダプタ群を採用する。

理由: GMA 13 だけでなく InMobi 最新アダプタも GMA 13 を要求するため、最新化するなら iOS13 化は不可避。iOS 12 は2026年では実利用ほぼゼロで実害なし。API 破壊はほぼ無く(GADNativeAdNativeAd の3箇所のみ)移行コストは小さかった。


2026-05-21: footballnext-android は全面近代化して16KB対応リリースする

背景: Google Play から「16KBメモリページサイズ非対応」の指摘(対応期限 2026-05-31)。footballnext-android の前回リリースは2021年10月で、ビルド環境は4年前のまま。

決定: 16KB対応(Realm 7.0.8→10.19.0)だけでなく、ツールチェーンを全面近代化する。Gradle 6.7→8.9 / AGP 4.2→8.7.3 / Kotlin 1.3→2.1 / JDK 17+ / targetSdk 30→35 / minSdk 19→24。kotlin-android-extensions 廃止に伴い ViewBinding へ全面移行。nend SDK は撤去(サービス終了済み)、広告は AdMob/ADG/i-mobile を維持。YouTubeプレイヤーは旧APIの提供終了により android-youtube-player に差し替え。

理由: 16KB対応の native lib(Realm 10.19.0)と Play 要件の targetSdk 35 を載せるには AGP 8系が必須で、それが Gradle/Kotlin/JDK 更新を芋づる式に強制する。jcenter 閉鎖で旧構成はそもそもビルド不能であり、16KBだけの部分対応は不可能。minSdk 24 は Android 6以下(世界シェア1%未満・2026時点で実害なし)を切る判断で、ネイティブmultidex等でビルドが安定する副次効果がある。


2026-05-21: OneSignal は 5.1.38 に固定(最新5.x系を使わない)

背景: footballnext-android の OneSignal を 3.x から 5.x へ移行する際、バージョンをレンジ指定すると最新の 5.9.2 に解決された。

決定: com.onesignal:OneSignal:5.1.38 にバージョンを固定する。

理由: OneSignal 5.8以降は OpenTelemetry / okhttp5 / wire を推移的に引き込み、kotlin-stdlib を 2.2系へ強制昇格させてしまう。プロジェクトの Kotlin コンパイラ整合とAPKサイズの観点から、これらを引き込まない最後の軽量系列 5.1.x の最新(5.1.38)に固定するのが筋がよい。


2026-05-19: cake_sessions Duplicate entry の PHP Fatal は当面放置

背景: 5/16・5/17・5/19 に CloudWatch 経由でエラーログスパイクのアラートメールが来ていた。調査の結果、原因は fnext.thomsonsapp.com(本番 API ドメイン)に対する bot のパススキャン由来。bot が同一セッション Cookie を使い回して並列リクエストを打つため、CakePHP の DatabaseSession::write()cake_sessions.PRIMARY の race condition で Duplicate entry を起こして Fatal になる。

決定: 即時対応はせず保留。データ実害なし・RDS への影響軽微・正規アクセスへの影響なしの判断。頭の片隅に置いておき、頻度が上がるか実害が出始めたら対応する。

理由: 攻撃トラフィック自体はどこのサービスでも常時発生しており、いま手を動かす緊急性がない。対策案 A〜E(攻撃パス拒否 / Session→Redis / try-catch 握り潰し / アラート緩和 / WAF 導入)はいずれもコストか副作用があるので、観察モードのまま維持する方が筋がよい。中長期で Session 保存先を File or Redis に移す案は副次効果(DB 負荷減)が大きいので、別タイミングで検討する価値あり。


2026-05-09: Step 3 (innodb_buffer_pool_size 1024→768MB 縮小) は保留

背景: 当初は FreeableMemory 直接対策として buffer_pool 縮小を計画(Step 1+2 のインデックス整理に続く 3 段目として)。しかし baseballnext を同 RDS に統合する計画があることが判明し、統合後はホット working set が 1.25-1.5GB 規模に増える見込み。

決定: Step 3 は実施せず保留。baseballnext 統合シナリオが確定したあとに「buffer_pool 1024MB 維持 / 縮小 / medium 昇格」を再評価する。

理由: 統合前にメモリを絞ると、統合後にすぐ戻すか medium 昇格する二度手間になる。Step 1+2 の効果(ReadIOPS -61〜70%)で I/O 余裕は得られたので、メモリ周りは統合計画の解像度が上がるまで動かさない方が筋がよい。


2026-05-08: articles インデックス A 群は INVISIBLE 観察期間をスキップして即 DROP

背景: 通常は INVISIBLE 化で 1〜数日観察してから DROP INDEX する保守的フローを取るが、A 群 4 個(article_site_id / image_width / title / del)はいずれも他複合 index の完全 prefix で、戻す可能性が極めて低い。

決定: INVISIBLE 化の直後に同セッション内で DROP INDEX まで実行(Online DDL / ALGORITHM=INPLACE / LOCK=NONE)。

理由: A 群は機能的に完全な上位互換が存在するため、観察してもクエリ計画の変動はほぼ起こり得ないと予測。万一戻すとしても CREATE INDEX で再構築可能(数分、Online)。Step 3 の判断材料を早く揃えるためにも観察期間の機会損失を避けた。実測では ReadIOPS -61〜70% で副作用なしを確認。


2026-05-08: impressions テーブルの機能を廃止

背景: 730 MB / 6.98M 行のテーブル。INSERT は V1Controller.php:296 1 箇所のみ、読み取りは管理画面 PortalController の集計用 2 箇所のみで、エンドユーザー API からは未参照。

決定: ユーザーが INSERT 行をコメントアウトしてデプロイ → TRUNCATE で全データ廃棄。テーブル定義は当面残置(ストレージ影響軽微)。

理由: 機能としての価値が低く、毎秒 0.7 INSERT 程度の継続的な書き込みコストを払う意味がない。管理画面の article_from_twitter 集計が空になる影響はあるが、ユーザー判断で許容。


2026-05-08: 19-20 時の FreeableMemory スパイクは cron 由来ではなく人為的と確定

背景: 5/7 19:20 に FreeableMemory が 1 分以内に 148 → 55 MB へ急落するスパイクを観測。当初は cron 起動による定常パターンを疑った。

決定: 過去 7 日同時間帯と比較した結果、5/7 のみ突出した急落。同時刻に走らせていた articles + old_articles への AVG(LENGTH(body)) のフルスキャン集計クエリが原因と特定。cron 由来ではない・調査クローズとして扱う。

理由: 過去 6 日の同時間帯は min 119-151 MB のベースライン揺らぎ範囲で、特異なスパイクなし。「ほかプロジェクト談」で話題になった FreeableMemory 課題は、特定時刻スパイクではなく構造的な低空飛行(avg 141 MB / min 115 MB あたり)の方であり、本命の対策は インスタンス昇格 に帰着するという理解。


2026-05-08: hot_keyword_rankings の古いデータ整理は判断保留

背景: 7.4 年分・約 915 万行の死蔵データが堆積。アプリは「最新 dir の rank top2」しか参照していないため、過去データは完全に不要と判断できる。一方で書き込み自体が 2026-04-09 で止まっており、cron 停止か別マシン稼働かが切り分け未済。

決定: データ削除は今セッションでは実行しない。判断材料を蓄積した上で別途検討する。

理由: 削除自体に技術的リスクは低いが、書き込み停止の真因が不明なまま削除すると「機能の生死」を誤判定する可能性がある。書き込み停止が意図的なのか障害なのかを先に切り分けたい。


2026-05-07: footballnext-operation はサブプロジェクト3つを .gitignore で除外

背景: footballnext-ios / footballnext-android / footballnext-server はそれぞれ独立した Git リポジトリで管理されている。footballnext-operation はそれらを跨いで運用情報をまとめる「指揮塔」用途。

決定: 3 サブプロジェクトのディレクトリを本リポの .gitignore に追加して、追跡対象から外す。

理由: ネスト Git の二重管理を避け、本リポは運用メモ・ドキュメント・スクリプト類に集中させる。


2026-05-07: RDS の performance_schema は OFF を維持

背景: カスタムパラメータグループ general-mysql84performance_schema = 1 が user-set かつ pending-reboot 状態で残っていた。再起動すると有効化される「不発弾」の状態。db.t4g.small (2GB) の FreeableMemory は Min 55MB / Avg 141MB と既に黄信号。

決定: reset-db-parameter-group で user override を削除し、engine-default (0) に戻す。

理由: Performance Schema は db.t4g.small クラスでは 200〜400MB 追加で消費する。現状すでにメモリが厳しく、有効化するとさらに悪化する。観測性を取り戻したくなったらインスタンス昇格とセットで再検討。


2026-05-07: old_articles テーブルのデータを廃棄

背景: old_articles は articles 2.0GB 規模のアーカイブテーブル。footballnext-server 内に参照コードはゼロ、過去のアーカイブ運用の名残として DB にだけ残っていた。

決定: phpMyAdmin から TRUNCATE してデータを全廃棄。テーブル本体は当面残置。

理由: アプリ参照ゼロかつ運用も止まっているため、データ保持コストが純粋な無駄。テーブル定義(とインデックス枠)まで消すのは物理影響が軽微なので後回しでよい。


2026-05-07: articles のインデックス整理は調査結果に留めて実行は保留

背景: 22個のインデックスのうち、4個(article_site_id / image_width / title / del)は他複合 index の prefix で明確に冗長。ただし本番運用中のテーブルでロックを伴う変更は慎重に進めたい。

決定: 削除候補リストと推奨手順(INVISIBLE → 観察 → DROP → OPTIMIZE)の作成までで一旦保留。次セッションで実行する。

理由: ロールバック容易性を最大化したい。MySQL 8 の ALTER INDEX ... INVISIBLE を使えば Online でオプティマイザから外せて、問題があれば瞬時に戻せる。今日は調査メモとして残し、メンテ時間帯を意識して実行する。


DEVLOG(作業ログ)

開発日誌

このプロジェクトでの作業を時系列で記録する。 最新のエントリが上に来る。


2026-08-23

21:21 - 第8次FB対応(シート項目39〜47・仕様v2.17=試合状態/スコア表示/代表経由)+全項目before/after比較画像+ビルド16配布・返信文作成

テーマ: トムソンLINEで第8次FB受領(シート項目39〜47の9件+HTML仕様書v2.17。進行中の実画面を初確認しての指摘が中心)。全項目を同日中に消化し、今回は各項目の「修正前|修正後」比較画像も作成。serverデプロイ・ビルド16配布・返信文作成まで完了(返信+添付のYou送付待ち)。

やったこと:

  • シート読解: 項目39〜47(高=39/40/42/43/44/45、中=41/46/47)。v2.17更新履歴と突合し食い違いなしを確認
  • 修正前スクショ: 本番データで#40/43/44/45/47、ダミーJSONで#39/41/42/46(新規fixture3種: fixture-club-lineup-fb39=status試合前×PRE_MATCH_LINEUP、fixture-club-live-fb41overseas-fb3947=LIVE/LINEUP/PRE 3状態入り。ロゴはmedia.api-sports.io実URL)
  • #45の原因特定(サーバー): DigestDataProvider::getPlayerMatches が日本代表戦(team 12)にも海外組14大会フィルタを適用→代表5大会(W杯=1等)と交差ゼロで national_recent全選手で常に空だった。経過日数条件ではない。チーム別に大会集合を切替えて修正(server 329b484)。デプロイ後、冨安の選手詳細で直近3試合(ブラジル1-2・スウェーデン1-1・チュニジア4-0)表示を本番確認。ビルド15でも直る
  • #40(サーバー+iOS): livesに lineup_announcedfnlive_fixtures.digest_lineups_available 由来)を追加配信。iOSは caption_id==0 && lineup_announced で青背景#3c76d3「スタメン」へ切替(スコア領域は従来どおり非表示)。caption_id=4新設案は実装後に撤回(旧配布ビルドが未知IDを受けると白文字・無背景で不可視+0-0表示になるため。DECISIONS参照)
  • iOS実装(926f17f: #39=選手ブロックstateからラベル導出・PRE_MATCH_LINEUPは青ピル「スタメン発表」(LiveFixtureStatusBadgeLabel 新設・xibカスタムクラス化)/#41=ステータスチップを「登録外」基準の共通固定幅に(ポジション列縦揃え)/#42=ダイジェスト選手欄の状態テキスト全削除(ロゴ+名前のみ)/#43・#44=スコアを所属クラブ基準へ(DigestFormatters.perspectiveScore/compactPerspectiveScore 新設。シェア画像内の同表記2箇所も統一。代表直近3試合の行は日本基準)/#46=DigestFixtureStatePill 新設(グレー試合前/青スタメン発表/薄赤●+経過分。LIVEのみ外側薄赤円が1.35秒周期で1.0→1.3倍拡大しつつ透明化するパルス。didMoveToWindowで再アタッチ)/#47=caption_id==0 は「vs」表示(LiveHistoryListRowDataにcaptionId追加・構築2箇所)/#40クライアント=Live.lineup_announced追加・Realm schemaVersion 4→5
  • 検証: simビルドSUCCEEDED・本番データで#43(鎌田/冨安「0 - 2」)・#44(「8/22 vs エヴァートン 0-2」)・#45・#47目視PASS。パルスは6秒録画→ffmpegフレーム抽出でハロの拡大・消失を確認。#40は本番APIを3分間隔監視するスクリプトでPSV戦スタメン発表(21:03)を捕捉し、新着上部の「赤LIVE|青スタメン|黄時刻」3状態同居の実画面を撮影
  • 比較画像: fb-items39-47-shots/compare/項目NN_*.png 9枚(PILで修正前|修正後を1枚に合成・タイトル帯+赤/緑ラベル。項目41のみ3パネル=LINEUP/LIVE)。#46は after/46_LIVEパルス動画.mp4
  • ビルド16: ASC APIで最新実数=15を確認→16採番。クリーンtree(xcuserstateはstash退避→復元)から CURRENT_PROJECT_VERSION=16 でアーカイブ→app-store-connect/uploadでアップロード→processingState=VALID確認。dSYM警告4件(AppLovin/DTB/InMobi/Moloco)は既知・無害
  • 返信文: トムソン返信_項目39-47対応完了_20260823.md 作成(項目45を先頭・原因説明+ビルド15でも確認可を明記。項目40に対象試合限定/切替タイミング約1時間前/旧ビルド無影響の補足3点)

詰まったこと / 気づき: ①全ビルド共有のJSON(S3 lives)にenum新値を足すと旧ビルドの表示を壊しうる。追加フィールド方式なら旧ビルドは未知キーを無視するので安全。②statusToCaption はlives/liveHistories共用のため、livesだけ変えたい変更は呼び出し側で分岐する。③スタメン発表のような「実データでしか発生しない状態」は、本番APIをポーリングする監視スクリプト+発火時にsim撮影で捕まえられる(今回21:03のPSV戦で成功)。④シミュレータの初回起動は利用規約→購読設定→通知→ATTのプロンプト連鎖があるので、キャッシュクリア(アンインストール)を伴う検証は時間に余裕を持つ

次回やること: ①返信文+比較画像9枚+パルス動画のYou送付 ②トムソンのビルド16確認待ち ③設定4項目戻し(トムソン側)④App Store提出(要再アーカイブ)


2026-08-19

21:41 - 第7次FB対応(シート項目25〜38・仕様v2.16=チームロゴ/表示仕様統一)+ビルド15配布・返信送付

テーマ: トムソンLINEで第7次FB受領(シート項目25〜38・#32は欠番+HTML仕様書v2.16)。ヘンク戦バグ3件+試合一覧のチームロゴ追加・揃え変更+ダイジェスト2セクションのロゴ/クラブ別グループ表示を同日中に全消化し、serverデプロイ・ビルド15配布・返信送付まで完了。

やったこと:

  • シート読解: Drive MCP read_file_content→ローカルpython解析(メモリの定石どおり)。新規は項目25〜38の13件(#32欠番)。#22(ザルツブルク国旗)は「未対応」のままだが、これは8/10返信で合意済みの「ザルツブルクが今日の海外組に載るタイミングで確認」待ち=作業なし。EL 8/21 ザルツブルク戦後の確認を返信で再依頼
  • 項目25 原因究明(ヘンク vs ウェステルロー 試合詳細の日本人選手欄が空): fixture 1558593 は digest_players_available=0・スタッツ0行のまま digest_backfill_done=1API-Football側で選手成績の配信が試合翌日まで遅延しており(8/16 8:53のbackfill時点でも空)、1回きりのbackfillが「劣化レスポンスではない=done」扱いにして再試行しなかったのが根本原因。現在はAPIに全データあり(伊東7.5・横山6.9ほか5選手)
  • 項目25 修復+再発防止(server efdb9dc デプロイ済み): ①直近30日の同症状9試合(8/15前後のベルギー3・オランダ2・EL/ECL予選4)を既存 /cron/updateFixture/{id} で一括再取り込み→リーグ戦5試合は全て dpa=1・スタッツ入り(ヘンク戦=5選手・本番APIで配信確認)。②backfillを「選手スタッツが取れるまでdoneを立てず3〜72時間窓で再試行」に変更(cron10分間隔=最大432req/試合で許容)。EL/ECL予選4試合はAPI側に選手スタッツ自体が無くlineupsのみ=これ以上は不可
  • 項目26〜31 試合一覧(iOS ee88a7d+server同コミット): #26の崩れは「日本人選手ラベルの centerX=チーム名 制約が長いラベルでチーム名を押し動かす」(チーム名にleading固定なし)のが原因。xibを全面改修=チームロゴ追加(左=チーム名→ロゴ28pt→スコア/右=鏡像・buildLiveHistories に home/away_team_logo 追加)・日本人選手ラベルは左=ロゴ右端に右揃え/右=ロゴ左端に左揃え・リーグロゴ25→22pt。#27は label.bounds.width 実測での選手名⇔人数切替がレイアウト前後で揺れるのが原因→2名以下は常に実名・溢れは「…」(幅実測切替を全廃)
  • 項目33〜38 ダイジェスト(iOS のみ・serverは既に全データ配信済みだった): DigestFixtureCell を改修=リーグロゴ16pt+両チームロゴ22pt+日本人選手を「[クラブ小ロゴ15pt] 選手A 選手B」のクラブ単位グループ表示(余白=選手間10/クラブ間18・折り返しはクラブグループ単位・1グループが1行超のときのみ選手名単位)。進行中/これからの試合はレイアウト共通・LIVE情報のみ差分。overseas.json は competition.logo_url/team.logo_url/jp_players[].team_id を既に持っていたためserver改修不要
  • 検証: sim(iPhone 17)ビルドSUCCEEDED・本番データで試合一覧(ヘンク行含む)とこれからの試合を目視PASS・Auto Layout競合警告0件。進行中カードはローカルHTTP+-DigestBaseURL/-DigestOverseasPath のダミーJSON(2クラブ6選手・出場中/途中交代/出場なし混在)でグループ折り返しまで目視PASS。仕様書v2.16の完成イメージ2枚(試合一覧・これからの試合)をpythonでHTMLから抽出して突合
  • ビルド15: ASC APIで最新ビルド確認中に8/10にビルド14(#24バッジ修正・iOS b248d50)が配布済みと判明(DEVLOG未記録セッション・返信 トムソン返信_更新バッジ修正_20260810.md あり)→今回は15として、クリーンtreeから CURRENT_PROJECT_VERSION=15 でアーカイブ→app-store-connect/uploadでアップロード成功→ASCで processingState=VALID 確認。dSYM警告(InMobi/Moloco)は既知・無害
  • 返信: トムソン返信_項目25-38対応完了_20260819.md をYou送付済み

決めたこと: digest backfillのリトライ方針(DECISIONS.md参照)

詰まったこと / 気づき: ①「対応済みのはず」の外部APIデータも配信タイミング次第で穴が開く。1回きりの補完処理は「取れなかった」を成功扱いにしないこと(doneフラグは目的達成=スタッツ取得で立てる)。②ビルド番号はローカル記録(WIP)だけでなくASC APIで実数確認してから採番する(未記録セッションの配布があり得る)。③試合一覧のUI崩れはxib制約のcenterX起因で、データ修復(#25)とは独立の問題だった

次回やること: トムソンのビルド15確認待ち→追加指摘対応。#22はEL 8/21ザルツブルク戦後にトムソン確認。設定4項目戻し(トムソン側)→App Store提出


2026-08-17

16:11 - 起動時広告A/B: dev-timer #36 enrollment窓クローズ確認+残差率/SRM中間再read(全PASS)

テーマ: dev-timer #36(8/17 17:00予定)を前倒し実行。①enrollment窓クローズ確認 ②SRM再read(全窓) ③残差率中間再read。GA4 Data API(property 211701453)+S3 config実取得。

結果(全項目PASS):

  • ① 窓クローズ正常: S3稼働config=v9のままローカル正本と完全一致(enrollment_end_at=1786892400=2026-08-17 00:00 JST・active=true)。GA4の enrollment_day は 07-07〜08-16が最終・08-17のenroll 0件canEnroll 窓ゲート機能。(not set) enroll(5.5.3勢)A=304/B=263 は解析除外・無害。target_cohort_day=08-17 の funnel イベントは既enroll勢の起動によるもので正常。
  • ② SRMなし: クリーンコホート最終規模 計3,141人(A=1,548/B=1,593=49.3/50.7%)・χ²≈0.65・p≈0.42。実績約75人/日=見込み約4,000人よりやや小さい着地。9月本解析の検出力見積もりはこの実数が前提。
  • ③ 残差率 中間再read PASS(ガードレール④維持): 成熟 target_cohort_day 07-08〜08-14(maturation3日/watermark08-14)・cv_9 で A=5.51%(10,796/196,022)・B=3.84%(7,540/196,385)=絶対両方<10% ✓・群間差1.67pp<3pp ✓。日次10%超えゼロ(最大 A 07-31=9.35%)・群間差5pp超えゼロ。#31同範囲(07-08〜07-19)の再現値 A=5.37/B=3.66 が記録値(5.38/3.67)と一致=手法整合。

気づき(要留意・9月本解析向け):

  • 7/23以降は汚染コホート勢が D28+ ゲートで追跡窓外に抜け母数急減(1.2〜1.7万→600〜800件/日)。クリーンコホート主体期(07-23〜08-14)の残差率は A=7.07%/B=5.81%(差1.26pp) とやや高め=小母数の分散増。基準内だが本解析はこの水準感が前提。
  • 参考(非確定): 未到達離脱率 A=18.09%/B=14.76%(B が3.33pp低・仮説の向き・#31整合)。ただしクリーン主体期のみだと A=17.34%/B=15.49%(差1.85pp)=新規ユーザーでは効果量が縮む兆候。
  • 中間評価の肌感: 機構は健全・UXはB優勢の向きが3回のread(first-look/#31/#36)で一貫。ただし D7/D14・円/人はユーザー単位 n≈1,550/群で検出力小さめ=本解析は funnel系(イベントベース)中心+円/人の点推定と幅で判断する見込み。Bの採用可否は収益減が許容範囲かどうかに帰着。

後始末: dev-timer #36 を done 化・#37(9/15 17:00 本解析)の pending 登録確認済み。計算スクリプトは scratchpad(一時領域・未保存)。


2026-08-08

19:47 - 第6次FB対応(項目22・23=MVP並び順共通化・仕様v2.15+ザルツブルク国旗)+ビルド13配布・返信送付

テーマ: ビルド12確認過程のトムソン提案(MVP順位を一覧と同一ルールへ戻したい)に「そのままだと一覧先頭=MVPが成立しない」実例付きで逆提案→合意→シート項目22・23+仕様書v2.15として正式化。同日中に全消化し、serverデプロイ・ビルド13配布・返信送付まで完了。

やったこと:

  • 仕様往復: トムソンの「MVP順位決定をG+A→評価→出場時間に戻す」提案に対し、共通ソートの欠損スキップ仕様のため統計未取得選手が一覧先頭に来る実例(本番=北野が先頭・MVPは上田)を示し、「一覧側も欠損下位へ寄せて完全一致を保証する」案を返信→トムソン合意(G/Aなしは0比較・評価/出場時間なしは下位・表示は「-」維持・対象は今日の海外組のみ)。項目23+v2.15として明文化された
  • 項目23実装(server ac07f72/iOS 2423d53: DigestPlayerSorter::sortOverseasToday 新設(欠損下位の共通並び・今日の海外組専用)、pickOverseasMvp は「ソート済み一覧の先頭に来る候補を返す」だけに簡素化(候補は非候補に必ず勝つため候補がいれば構造的に一覧先頭=MVP)。iOSはシェア画像のMVP差し込み処理・不要デバッグバリアントを削除しサーバー配信順をそのまま使用。試合詳細の並び(欠損スキップ)は不変
  • 項目22原因特定(ザルツブルク国旗): iOSの国旗描画は at 対応済み・serverの国名→コード変換も対応済みだったが、fixtureFields()country 列がなく全クラブで country_code がnull配信だった。国内リーグ勢はiOSの「大会ID→国」フォールバックで隠れ、フォールバック外の欧州大会(EL)のザルツブルクだけ露呈。ホーム/アウェイの country を取得列に追加して修正。さらにNECナイメヘンの country 登録漏れ(空)を発見しDB修正(Netherlands・CL試合で同症状が出る前に解消)。対象26クラブの他は正常
  • 検証: リモートPHPハーネス6ケース全PASS(統計null下位・評価/出場時間なし下位・候補0でnull・G+Aなし0比較・一時キー非漏出・表示用データ非破壊)。simビルド→シェア画像で配信順のままMVPラベル先頭・候補0人でバッジなしを目視PASS。デプロイ後の本番overseas.jsonで**一覧先頭=MVP=上田綺世・統計null勢が最下位・全クラブcountry_code配信(ザルツブルク=at)**の3点PASS
  • 配布: Pipeline Succeeded(ac07f72)→ ビルド13(5.6.0(13))をクリーンtreeからアーカイブ→アップロード成功。返信 トムソン返信_MVP並び順共通化_20260808.md(項目22・23対応版)をYou送付済み。項目22はserver修正のみ=配布済みビルドにも効く
  • 運用: セッション途中で回線IPが変わりSSH不通→SG(port22)に現IP追加(claude digest 20260808・過去運用と同方式)

決めたこと: 今日の海外組の並び順・MVP選定の共通化と欠損下位ルール(DECISIONS.md参照)

詰まったこと / 気づき: 「配信済みのはず」のcountry_codeが実は全クラブnullでも、クライアント側フォールバックが国内リーグを救っていたため2週間気づけなかった。フォールバックは症状を隠すので、配信側の欠落はフォールバック無しの経路(欧州大会)で最初に露呈する

次回やること: トムソンのビルド13確認待ち→追加指摘対応。設定4項目戻し(トムソン側)。App Store提出時は再アーカイブ。

13:22 - 試合一覧日本人選手表示の調査回答(8/7)+第5次FB対応(仕様v2.14 項目19〜21)+ビルド12配布・返信送付

テーマ: 8/7晩のトムソンLINE(試合一覧の選手名非表示2件の原因確認依頼)に調査回答→翌8/8にシート項目19〜21+HTML仕様書v2.14を受領し、同日中に全消化。serverデプロイ・TestFlightビルド12配布・返信送付まで完了。

やったこと:

  • 調査回答(8/7): ①アヤックス/コペンハーゲンで選手名が出ない件=選手登録・チーム紐づけ・表示ONは全て正常(DB実査: 板倉滉→194/鈴木淳之介→400)で、原因は該当2試合がUEFAカンファレンスリーグ(848)=digestLeagueIds() 対象外という「対象大会の判定」。848は8/4に実況板リーグとしては投入済みだがダイジェスト対象には未追加で、元仕様書の対象大会がCL/ELのみだった経緯も特定。②ザルツブルク「日本人選手2名」=iOSの幅フォールバック(2名以下は名前表示を試み、ラベル119pt超で人数表示。仕様v2.7明記どおり)。返信 トムソン返信_試合一覧日本人選手表示調査_20260807.md 送付済み
  • 第5次FB読解(8/8): シート項目19(シェア画像クラブ情報の右揃え)・20(MVP候補条件・選定ルール)・21(対象大会14大会への整理)とv2.14(対象大会ID明文化版)を突合。対象9大会は全て fnlive_v3_leagues に登録・クロール中でリーグ側の追加登録は不要と確認
  • server(1e59b44 デプロイ済み): 対象大会リストを overseasCompetitionIds()(14大会・848/15追加)と nationalCompetitionIds()(1,7,10,23,30・E-1=23追加。従来getNationalFixturesにハードコード)に分割し、試合一覧/試合詳細ガード・DataProvider全クエリを海外組リスト参照へ。cron取り込み判定は和集合。MVPは pickOverseasMvp を新ルールで実装(候補条件=1G以上/1A以上/評価7.0以上、順位=G+A→ゴール数→評価→出場時間、評価なしは取得済みより下位、候補0人でmvp=null)。ローカルPHPなしのためWeb1号機のphpでlint+シート確認観点7ケースをハーネス検証し全PASS
  • iOS(3fa1a05 push済み): シェア画像のクラブ情報を「クラブ名→所属リーグ国旗」右揃え・国旗右端を全カード固定(drawTextWithFlag にFlagEdge.trailing追加、長名は縮小→末尾…)。mvp=null時に先頭カードをMVP扱いする旧フォールバックを撤去(アプリ内一覧・シェア画像とも)。シェア画像はMVP先頭配置(並び順上位3人外でもMVP優先掲載)。デバッグ書き出しに「MVP候補0人」「MVP先頭配置」バリアントを追加
  • 検証: simビルドSUCCEEDED→dev-fixturesローカルHTTP配信+-DigestExportShareImages でシェア画像5種を取得し目視PASS(国旗右端3カード一致/候補0人でバッジ・枠なし/2番目指定選手の先頭強調。fb-items19-21-shots/)。落とし穴: HTTP配信はiosリポジトリルートから行う(リクエストパスに dev-fixtures/ プレフィクスが含まれる。フィクスチャdir直下で立てると404)
  • 本番反映確認: Pipeline Succeeded(rev 1e59b44)→ liveHistoriesでUECL3試合にラベル出現(板倉滉・鈴木淳之介・伊藤敦樹)、overseas.jsonのMVPが新ルールで上田綺世(2G・8.2)に。一覧先頭は統計オールnullの選手=旧ルールならこれがMVPだった実例をもって指摘バグの解消を確認
  • バックフィル: /cron/updateFixture/1607572・1607580 実行。板倉・鈴木はAPI-Football側でもマッチデースカッド外→not_registered が正しい状態(8/7時点でstatsが空プレースホルダだったのは対応前の取り込みガードのため)
  • ビルド12: クリーンtree(xcuserstate等をstash退避)から CURRENT_PROJECT_VERSION=12・Apple IDセッション認証でアーカイブ→exportArchive(app-store-connect/upload)で 5.6.0(12) アップロード成功。dSYM警告(InMobi/Moloco)は既知・無害。返信 トムソン返信_項目19-21対応完了_20260808.md をYou送付済み

決めたこと: 対象大会の2正典分割+cron和集合判定、MVP先頭配置の適用範囲(DECISIONS.md参照)

次回やること: トムソンのビルド12確認待ち→追加指摘対応。設定4項目戻し(トムソン側)。App Store提出時は再アーカイブ。


2026-08-05

19:14 - 第4次FB対応(仕様v2.12 項目13〜18)+シェアレーン統合+ビルド11配布・DM返信済み

テーマ: トムソンLINE(ビルド10確認OK+シート項目13〜18追記+HTML仕様書v2.12受領)を受け、追加6項目を同日中に全消化。温存していたシェアレーン差分もビルド11へ統合し、TestFlight配布→DM返信まで完了。

やったこと:

  • 仕様読解: シートの新規「未対応」6件(項目13〜18)とv2.12更新履歴を突合し、シート外の隠れ仕様変更なしを確認。項目1〜3・6・7・11・12=対応済、8=HTML修正のみ、**4・5・9・10=「本番確認待ち」(LINEUP/LIVE実データ発生時にトムソン側で確認)**の状況も整理
  • 修正前スクショ: 既存デバッグ機構(-DigestExportShareImages+dev-fixturesローカルHTTP配信+-DigestLiveHistoriesPath)でシェア画像5種+sim試合一覧をbeforeとして取得
  • 項目13(試合一覧3段構成): LiveHistoryListCell.xib でコメント吹き出しを右下→上段右端(日付・リーグ名と縦中央揃え)、日本人選手情報を「試合結果」と同高の下段へ、リーグ名×吹き出しの重なり防止制約を追加。行高108→96pt(試合タブ・実況版一覧の2箇所)→ iOS 3b25410
  • 項目14〜18(シェア画像): DigestShareImageRenderer の国旗を絵文字→DigestPlayerRowParts.leagueFlagImage 共用のコード描画画像へ。国判定は team.country_code(server全team payload配信済み・DigestPresenter確認)優先+リーグID→国コード対応表でフォールバック。ヘッダー旗46px(代表版=日の丸・クラブ版=リーグ国旗・サイズ基準統一)、カード旗34px全カード統一、平均評価=緑(なしは「-」グレー)、合計欄数値のみ29→38px・heavy・ベースライン2pt下げ → シェアレーン温存差分(トムソン提供 share_bg 差し替え・DigestShareControl/DigestShareIcon)と合わせて iOS b41fffa
  • 修正後スクショ+項目番号バッジ: 再ビルドでafter取得し、PILで全16枚(before/after各8枚)に赤枠+「項目N」バッジを焼き込み → fb-items13-18-shots/
  • ビルド11配布: クリーンtreeからCLIアーカイブ(CURRENT_PROJECT_VERSION=11・Apple IDセッション認証)→ exportArchive(app-store-connect/upload)で 5.6.0(11) アップロード成功。返信文 トムソン返信_仕様v2.12対応完了_20260805.md(簡潔版・スクショ言及なし)を作成しYou送付済み

気づき:

  • 【新規警告】MinimumOSVersion: 2027年春以降、最低OS 15.0未満のアプリはApp Store Connectへアップロード不可。現在13.0のため来年までに引き上げ対応が必要(ROADMAP今四半期へ追加)
  • dSYM警告4件(AppLovin/DTBiOS/InMobi/Moloco)は従来どおり無害
  • シェア画像のbefore/after取得は DigestShareDebugExporter(ダイジェスト表示+選手詳細表示で自動PNG書き出し)が便利。全パターン(評価なし・長名・未知リーグ)を1回の操作で網羅できる

次回やること: トムソンのビルド11確認待ち→追加指摘があれば対応。設定4項目戻し(トムソン側)。App Store提出時は再アーカイブ(xcarchiveはscratchpad一時領域)。

16:49 - 第3次FB対応(仕様v2.11)フル完了+試合タブ空表示バグ修正+ビルド10配布

テーマ: トムソンLINEの追加指摘(シート項目6〜12+HTML仕様書v2.11)を全消化。途中でYou報告の「試合タブが本番で空」の原因調査から起動時競合バグを特定・修正。実装確認事項もダミーデータ検証で消化し、クラブLINEUP/LIVEの仕様差異を発見→修正。ビルド10をCLIでTestFlight配布し、トムソン向けDM文書まで作成。

やったこと:

  • 仕様読解: シート「FN海外組」タブ全12項目+添付HTML仕様書v2.11(v2.8〜2.11はトムソン側更新)を突合し作業リスト化。リーグ国旗の表示箇所は「代表PRE_MATCH選手行」「選手詳細クラブ版ヘッダー」の2箇所のみ、代表LINEUP=先発11名のみ/LIVE=出場中・交代済のみ等の明文化を確認
  • 項目6・7(iOS 609ab04: 代表/クラブ試合詳細のFINISHED選手行に日の丸追加・G/A青→グレー・評価黒→緑・クラブ見出し青→黒。日の丸描画を共通部品 DigestPlayerRowParts へ切り出し。並び順・出場者絞り込みはserver実装済みをコード確認(sortOverseasMvp=G+A→評価→出場時間、statusRank=bench→not_registered)
  • 試合タブ空表示バグ(iOS 2278819: 本番でも空、の報告を受け調査。/v1/liveHistories は正常(50件0.05秒・全行デコード可)→LLDBアタッチで処理を遅らせると表示される=起動時競合と確定。ダイジェスト表示可否判明時の rebuildPageViewControllerLiveHistoryPage の取得完了が競合し、未初期ロードのテーブルへ「配列append→1行ずつinsertRows」する構造が行数不整合を起こす(一時ログ計測でInvalid batch updatesクラッシュ経路も確認)。didFetchDataを「まとめてappend→reloadData」方式へ変更、同型の実況版一覧(LiveHistoryList)も同修正。通常起動2連続+実況版一覧でPASS確認。発症条件は「ダイジェストタブが出る」こと=8月の欧州開幕でoverseasが埋まって顕在化。5.6.0系のみでApp Store版5.5.xは無関係だが、ビルド9には残存
  • 項目12・11(server 18af16c+iOS bd8de88: リーグ国旗対応。server=presentTeamに country_code、代表PRE_MATCH選手行に clubCountryCode を追加(fnlive_teams.countryの英語国名→コード変換 teamCountryCode 新設・未知国null)。テスト3件追加(46+35アサーションPASS・php7.4コンテナ)・デプロイ済み。iOS=国旗をコード描画(13カ国・キャッシュ付き・未知コードは省略)で実装し、代表PRE_MATCH行(16×11・日の丸22×15より小・クラブ名欠損時は国旗ごと省略)とクラブ版ヘッダー(18×12・国名テキスト廃止)へ表示。DigestShareFlagMap に88/119を補完(フェイエ/コペンハーゲンの(国名)欠け解消)
  • シェアレーンとの分離: 項目11の修正対象 JpPlayerDetailCells.swift に温存中シェア差分が被っていたため、該当1ファイルだけstash→クリーン状態で修正・コミット→pop→競合1箇所(プロパティ行)を手動マージ。復元後の全体ビルドPASS・バックアップもscratchpad保持
  • 実装確認事項の消化: dev-fixturesをローカルHTTP配信+デバッグ起動引数(DigestBaseURL/DigestLiveFixturePath等)で全状態を再現し仕様7章と照合。代表LINEUP(11名のみ・先発チップ)/代表LIVE(出場中・交代済のみ・ポジション)/クラブFINISHED(ベンチ短縮行・評価null「-」・並び)=PASS。クラブLINEUP/LIVEに差異発見(LINEUP=選手名のみ・LIVE=成績列表示)→試合前〜LIVEを「日の丸→選手名→状態チップ→ポジション→矢印」へ刷新し状態チップを共通部品化(ベンチ/登録外=白地グレー枠を追加)(iOS 5480d48)。修正後ダミー描画でPASS
  • ビルド10配布: シェアレーン差分を git stash -u で全退避→クリーンな 5480d48 から xcodebuild archive(CURRENT_PROJECT_VERSION=10・Apple IDセッション認証)→-exportArchive(app-store-connect/upload)で 5.6.0(10) アップロード成功。内部グループ自動アクセスで配布操作不要。退避分は復元済み(xcuserstateの衝突はアーカイブ中のXcode書き込みが原因・現在分破棄で解決)
  • トムソン報告: DM用プレーンテキストを トムソン返信_仕様v2.11対応完了_20260805.md に作成(対応5項目+試合タブ件+ビルド10で確認依頼+設定4項目戻し依頼)
  • スクショ: fb-items6-7-shots/ に before/after 8枚+確認用5枚を保存(項目6/7/11/12の前後、LINEUP/LIVE/ベンチ行検証)

決めたこと:

  • リーグ国旗は「server=fnlive_teams.countryから国コード配信+iOS=コード描画画像」方式(DECISIONS.md 参照)
  • 試合タブ修正は「まとめてappend→reloadData」方式(提示2案からYou選択①)

気づき・注意:

  • 本番 /api4/jpPlayer が間欠的に30秒超→タイムアウトする事象を2回観測(リトライで即成功・存在しないIDは常時約35秒)。トムソン確認中に「選手詳細が読み込めない」と言われる可能性あり。原因未調査(player_not_found経路が重い疑い)
  • 試合タブの空表示は非決定的(空/正常/最悪クラッシュの3パターンを確認)。デバッガで遅らせると再現しない典型的な競合だった
  • アップロード時のInMobi/Moloco dSYM警告はサードパーティバイナリ由来で従来どおり非ブロッキング
  • simの試合タブ検証で「引数なし起動でダイジェストタブが出ない」前回観察は今回未再現(毎回表示された)

次回やること: TestFlight処理完了確認→YouがDM送付→You実機でビルド10確認→トムソン再確認→設定4項目戻し(トムソン側)→アーカイブ再利用でApp Store提出。


2026-08-04

18:27 - 仕様v2.7対応(代表選手一覧1行表示)→ビルド9配布→久保・上田の「最近5試合」整備→対象リーグ2追加まで一気通貫

テーマ: トムソンの第2次フィードバック(シート指摘3+確認依頼2件+仕様書v2.7)をフルサイクルで対応。server 2コミット+iOS 2コミットのデプロイ、TestFlightビルド9のCLI配布、選手マスタ紐づけ完了、過去試合バックフィル、ダイジェスト対象リーグ拡張まで完了し「トムソン最終確認待ち」へ。

やったこと:

  • 仕様読解: シート追記(指摘3=試合前選手一覧の1行表示・優先度高)+未番号2行(スタメン発表後/LIVEの確認代行依頼)+v2.7差分(PRE_MATCH 1行表示の明文化のみ)を突合。発表後/LIVEはコード読解で「差異あり」と判定し今回まとめて修正する方針をトムソンへ返信(You送付)。
  • server(e59c037: ①招集メンバーの所属クラブを選手マスタ(team_id)→fnlive_teams解決で club_team 付与(未紐づけはクラブ名省略で行維持)②試合前=GK→DF→MF→FW・背番号順、LIVE=ポジション優先・同ポジション内出場中→交代済のソート追加 ③日本人選手欄・実況板ラベルをダイジェスト対象リーグ限定にするガード ④リーグ正典を DigestPresenter::digestLeagueIds() に一本化。テスト8件追加(43件PASS・php7.4コンテナ)。
  • iOS(e0323495ad53eb: NationalFixturePlayersCell を状態別1行表示に刷新(日の丸プログラム描画・クラブ名グレー…省略・先発/出場中=塗り緑チップ・交代済=白地緑枠・ポジション・矢印。FINISHEDは従来維持)。行重なりバグをYou発見→行高26pt vs nationalPlayersRowHeight() の1行24pt計算のズレ52pt分が原因→24pt統一で修正。
  • TestFlight: ビルド8(行重なり欠陥・破棄)→ビルド9(5.6.0(9))をCLIでアップロード。手法確立: xcodebuild -exportArchive(method=app-store-connect / destination=upload)+-allowProvisioningUpdates認証はXcodeログイン済みApple IDセッションが正解(ASC APIキー2本はissuer不一致/ロール空でクラウド署名不可)。ビルド番号はpbxproj不変のままCLI上書き(CURRENT_PROJECT_VERSION=9)。配布は内部グループ「App Store Connect Users」全ビルド自動アクセスで追加操作不要。
  • 選手マスタ: J組3名をSQLで紐づけ(大迫敬介=282広島 / 早川友基=290鹿島 / 長友佑都=292FC東京)。海外7名は既にYou実施済みと判明。冨安は無所属のため0のまま(You判断・クラブ名省略表示)。
  • 久保の最近5試合: ソシエダ昨季6試合を updateFixture バックフィル→5件+グラフ3点+平均★6.6。
  • 上田の最近5試合(トムソン指名): 昨季フェイエ戦がDBに無い(Eredivisie取り込みは今季分のみ)+updateFixture既存行のUPDATE専用と判明→API-Football(キーはserverリポジトリの ApiFootball.php)から昨季5試合を取得し fnlive_fixtures へ手動INSERT(必須列は event_time/event_date、他はupdateFixtureが肉付け)→5件+グラフ4点+3G。副産物で渡辺剛5試合・小川1試合も蓄積。
  • 対象リーグ拡張(5c3a226: 夏移籍で板倉(アヤックス)・上田/渡辺剛(フェイエ)・小川(NEC)=エールディヴィジ、鈴木淳之介=スーペルリーガとなり5選手がダイジェスト対象外になる問題を発見→ digestLeagueIds() に88・119を追加、CronController側の取り込み判定コピーも共通参照へ一本化。デプロイ済み。
  • テーマ3宿題消化: コペンハーゲン is_publish=1/cron/crawlNextFixtures/119 でスーペルリーガ20試合取り込み。
  • 検証: sim4状態(ダミー)+本番実データE2E(アジア杯1/11 PRE_MATCH 26人・クラブ名・並び順・/api4/jpPlayer 久保/上田)。S3毎分生成の継続も確認。

気づき・注意:

  • バックフィルの finished_timetime()(現在時刻)になるため「今日の海外組」に一時的に載る(設定戻し後24hで自然消滅・確認用にはむしろ好都合)。
  • 引数なしsim起動でダイジェストタブが自動表示されない観察(overseas has_display_content=trueでも)。タブ表示ロジックは今回未変更=ビルド7と同一挙動のはず。実機ビルド9で要確認、再現するなら別レーン調査。
  • LIVEの画面イメージにはポジション列が無いが、仕様書本文とシート期待結果に従いポジションありで実装(トムソンへ注記済み)。
  • デバッグ起動(DigestBaseURL指定)中は選手詳細がダミーURL参照になるため選手行タップは正しく動かない(本番検証は実機/APIで)。

次回やること: トムソンへ完了報告送付(ドラフト済み・ビルド9表記)→You実機でビルド9最終確認→トムソン再確認→設定4項目戻し(トムソン側で実施: 3/1/7/1)→アーカイブ→App Store提出。

2026-08-03

23:00 - fnDigestSetting の入力上限を0〜365へ拡張(3層一致)+トムソン返信

テーマ: トムソンLINE「national_days_after / overseas_recent_days に99を入れたい」対応。

やったこと: 制限が3層(モデルバリデーション0〜30・presenterの読み取りクランプ0〜30・providerのクランプ1〜90/0〜30)あると特定し、全て0〜365に統一(フォームだけ緩めても内部で丸められる構造だった)。テスト3スイートPASS。Youがcommit(6634a4d)・デプロイし、翌朝「99入力可+overseas_recent_daysは日数窓であって試合数指定ではない」の統合返信を送付。overseas_recent_days=65等の現行値はSQL直接投入だったためフォームを素通りしていた。


2026-08-01

19:56 - トムソンFBシート読解→ダイジェスト指摘2件の実装対応→デプロイ・DM返信完了

テーマ: トムソンのスプレッドシート「今後のご相談」に新設された「FNダイジェスト_フィードバック」タブの指摘2件(いずれも優先度:高)を読解し、server/iOS 両方の実装対応→コミット→デプロイ(You)→トムソンへDM返信まで完了。ダイジェストは「フィードバック1回目対応済み・トムソン再確認待ち」フェーズへ。

シートの読み方(今後も使う手法):

  • シートは約247MB・45タブの巨大ファイルで、Drive コネクタの xlsx/zip/pdf エクスポートは全滅(too large / timeout)。csv は先頭タブのみ。
  • 確実な読み方: read_file_content を includeComments なしで呼ぶ→結果が大きすぎて tool-results 配下のローカルファイルに保存される→python で解析。タブ見出しは無いが markdown テーブルの列数と先頭行で各タブを特定できる(コーパスは全タブ分入らず末尾切れ)。includeComments ありだとタブ名一覧とコメントだけでセルデータが空になる。手法はメモリにも記録済み。

指摘内容と対応:

  • 指摘① 試合詳細の日本人選手欄: 対象選手が両チームに1人もいない試合でも「日本人選手」見出し+空カードが表示される→セクション全体を非表示に。
    • server a33feee: buildFixturePlayerBlocks で表示行0件かつ両チームに表示対象選手(display_on=1team_id)が所属しない場合 japanesePlayers=null。所属者ゼロでも stats 行が残る過去試合(移籍直後)は表示維持。test_digest_presenter.php にテスト3件追加(計35アサーション PASS・php7.4コンテナ)。
    • iOS 92e2e4e: 試合詳細ヘッダーで選手0行のブロックは見出しごとスキップ(クラブ・代表両セル)。古いキャッシュJSON対策の保険。
    • 既存TestFlightビルドは japanesePlayers=null なら元々出さない作りなので、①はサーバーデプロイだけでも旧ビルドで直る
  • 指摘② 日本代表の期間設定の解釈変更: 「日本代表モード」→「日本代表初期表示期間」。期間は表示可否ではなく初期サブタブ切替専用に。
    • iOS 79bad62: NationalDigest の表示ガードから nationalMode を除去。空状態はデータ0件のときのみ。②はアプリ配信必須(原因がiOS側ガードのため)。
    • 調査の結果それ以外は既に仕様どおりだった: サブタブ常時両方・期間内は日本代表初期表示(DigestPage)・タブ非表示は「期間外かつ海外組なし」のみ(DigestAvailability)・初期選択は現在日時最近傍/同距離未来優先・サーバーの代表戦抽出も期間非依存(±365日)。
    • server 0f30889: 管理画面の設定説明文(DB description)を「初期表示期間」文言へ。seed(003)更新+本番反映用 004_rename_national_mode_wording.sql 新規(説明文のみのUPDATE・値不変)。

やったこと(その他):

  • v2.6 仕様書HTML(日本代表初期表示期間・更新履歴追加版)を Downloads から repo へ取り込み・読解。指摘①は v2.6 未反映だが仕様書は v2.6 のまま修正しない方針(You 判断)。一度作った v2.7 は削除。
  • iOS シミュレータビルド SUCCEEDED(iPhone 17)。シェア機能まわりの既存未コミット差分(DigestCells/DigestShareImageRenderer/share アセット等)には触れず温存。
  • デプロイ: server push(Pipeline)・iOS TestFlight 配布とも You 側で完了。トムソンへDM返信済み(トムソン返信_ダイジェストFB対応_20260801.md)。

気づき:

  • シートのフィードバックタブは「No/画面・機能/現状/修正後/修正指示/再現手順/完了条件/補足/優先度/状況」の10列構造で、完了条件まで書かれており実装にそのまま落とせた。この形式は今後のFBラウンドでも機能する。

次回やること: ①トムソン再確認待ち→追加指摘あればシートの「状況」列とあわせて対応 ②本番DBへ 004(設定説明文UPDATE)の適用確認 ③確認完了後に一時設定戻し(1/7/1)→アーカイブ→App Store提出。

15:26 - トムソン質問「チームID紐づけ」→本番調査→対象選手Edit画面のチーム設定UI改修

テーマ: トムソンから「海外組でチームID未紐づけの選手にIDを紐づけたい」と質問。調査の過程で対象選手Edit画面のチーム欄が設定不能と判明し、UI改修(数値入力+名前検索補助)までデプロイした。

調査でわかったこと:

  • team_id=0 の11名の内訳を確定: 海外クラブ7名(要紐づけ)+Jクラブ3名(長友・大迫敬介・早川)+無所属1名(冨安)。後者4名は仕様上 0 のままが正。
  • 本番RDS確認(Web1号機経由SSHトンネル・現IPはSG許可済みだった): 3リーグ(88 Eredivisie / 119 Superliga / 848 ECL)は投入済み・cron補完済み。fixture取り込みは Eredivisie 96試合・ECL 4試合、Superliga は0件
  • Superliga 0件の原因: crawlNextFixtures の取り込み判定 _shouldImportFixture は「どちらかのチームが is_publish=1」or「digest対象のハードコード14リーグ × is_jp_target」のみ。88/119/848 は14リーグに含まれず、コペンハーゲン(400)は is_publish=0 のため全滅。アヤックス等は既に is_publish=1 だったので Eredivisie は部分的に入っていた。
  • fillLeaguesForV3 はリーグ名補完のみで、チーム行の生成は fixture 取り込み時(crawlNextFixtures 内)に行われる。
  • 7名の現所属を API-Football(squads/transfers・6req)で裏取り: 板倉=アヤックス194(#4)・上田=フェイエノールト209(#9)・渡辺剛=209(#4)・小川=NECナイメヘン413(#18)・鈴木淳之介=コペンハーゲン400(#20)・菅原=サウサンプトン41(6/29にブレーメンから復帰・英2部=未追跡)・中村敬斗=ランス93(仏2部=未追跡)。

やったこと(footballnext-server・You がコミット 602de4c・デプロイ済み):

  • 対象選手Edit画面のチーム欄が空プルダウンで設定不能と判明(FormHelper の *_id 自動select化+belongsTo無し+選択肢変数無しの合わせ技)。プルダウン化はチーム数(数千件)的に不成立なので、以下で対応:
  • app/View/FnJpPlayer/scaffold.form.ctp 新規(この画面だけの差し替え・共通テンプレ不変): Team ID を数値入力に+直下に補助パネル(現在のチーム名を常時表示fnlive_teams の日本語/英語名LIKE検索→候補クリックでID自動入力)。検索はローカルDBのみで API 消費なし。
  • あわせて add 画面の ID(API-Football選手ID)入力を解禁。リーグと同じ「PK強制hidden」問題で、選手の画面からの新規追加も実は不可能だった。
  • FnJpPlayerController::searchTeam 新設(JSON・admin認証下・q=LIKE / id=完全一致・20件)。
  • FnJpPlayer にバリデーション追加: team_id は「0 or fnlive_teams 実在ID」のみ(id=0行誤参照事故の再発防止。cron側は validation を通らない経路なので影響なし)。
  • php:7.4 コンテナで3ファイル lint 全PASS。

詰まったこと / 気づき:

  • ローカルから RDS 直結は不可(ホスト名がVPCプライベートIPに解決される)。7/23に確立した Web1号機(3.112.232.196)SSHトンネル経路が引き続き有効で、現IPが SG 許可済みだったのでそのまま使えた。
  • 「リーグを追加すればチームもすぐ揃う」は半分だけ正しい。チーム行は fixture 取り込みの副産物なので、is_publish のチームが1つもないリーグ(今回のSuperliga)はリーグを足しても何も取り込まれない。

次回やること: ①新Edit画面で7名を紐づけ ②コペンハーゲン(400) is_publish=1 化(Portalチーム画面のEditで可) ③/cron/crawlNextFixtures/119 を一度叩く(放置でも24h後に自動巡回) ④overseas.json への反映確認 → トムソンへ紐づけ手順の返信。

13:36 - 実況板リーグ追加のポータル化(API確認つき追加画面)

テーマ: トムソンからのリーグ追加依頼(119 Superliga / 848 UEFA Europa Conference League / 88 Eredivisie)を機に、これまで phpMyAdmin で fnlive_v3_leagues に id を手挿入していた運用をポータルから行えるようにした。

調査でわかったこと:

  • ポータルには scaffold の「リーグV3」ページ(FnliveV3LeagueController)が既に存在していたが、CakePHP の FormHelper が主キーを強制 hidden にするため add で id を指定できず、それが phpMyAdmin 運用になっていた原因。
  • 「id だけ挿入すればうまくいく」仕組みの正体は 5 分毎の cron fillLeaguesForV3team_created=0 の行を API-Football v3 から name/type/logo 補完して team_created=1 化)。

やったこと(footballnext-server・未コミット):

  • FnliveV3LeagueControlleraddConfirm アクションを新設: ID入力 →「APIで確認」で API-Football からリーグ名・種別・国・ロゴを取得して表示 →「このリーグを追加する」(JS confirm つき)で INSERT の2段階フロー。ビュー add_confirm.ctp 新規。
  • INSERT は id + name + type + logo を入れつつ team_created=0 のままにして、実績ある cron 補完経路を温存。登録済み ID は赤字警告を出し追加ボタンを出さない(追加時にも再チェックで重複 INSERT 防止)。不正 ID / API 未ヒットはエラー表示で再入力へ。
  • Portal/index.ctp 実況板セクションに「リーグV3追加」リンクを追加。
  • scaffold 共通テンプレ(カスタマイズ済みの lib/Cake/View/Scaffolds/index.ctp)の disableIndexActions'Add' 対応を追加し、FnliveV3League だけ「New Fnlive V3 League」ボタンを非表示に(id を指定できない壊れた導線を排除し、追加を addConfirm に一本化)。他の scaffold 画面には影響なし。
  • 認証は既存の Auth(admin ロール)配下に自動で入る。php:7.4 コンテナで全変更ファイル php -l PASS。

詰まったこと / 気づき:

  • docker で PHP lint する際、ホストの lib/ をコンテナの /lib にマウントするとシステムライブラリを潰して entrypoint ごと死ぬ。マウント先は /src 等の無害なパスにする。

次回やること: You がコミット→CodeCommit push→Pipeline デプロイ → 新画面からトムソン依頼の3リーグ(119 / 848 / 88)を投入して動作確認。

2026-07-24

20:30 - 試合詳細の選手欄刷新+本番データでの不具合修正+トムソン確認用の配布準備

テーマ: クラブ/日本代表の試合詳細の日本人選手欄を仕様に寄せて作り直し、本番データで初めて露見した不具合を潰して、トムソンに触ってもらえる状態にした。

やったこと(iOS・UI):

  • クラブ試合詳細の選手欄を刷新: 選手カードを上部固定エリアから関連記事テーブルのヘッダーへ移し、得点者・記事と一緒にスクロールするように変更。見出し「日本人選手」を得点者と同列のカード外見出しへ、チーム名の左にチームロゴを追加。選手間の境界線・「#10 MF・フル出場」のサブ行・「ほか◯名を表示」の開閉を削除して常時全員表示。行高42→32pt。出場列に status 表示(bench=出場なし / not_registered=登録外)と行末シェブロンを追加。
  • 日本代表試合詳細の選手欄も同仕様へ統一(行高は24pt)。
  • 記事テーブルの初回ロード時の自動スクロールを抑止CoreTableView)。追加読み込み用の「ちょっとスクロール」が初回にも走っていて、選手カードがヘッダーに入って高さが出たことで目に見えるズレになっていた。
  • 選手詳細の細部: セクション見出しの文字・アイコンを約0.8倍、読み込み中スピナーがタイトルと重ならないようアイコン枠へ移動、関連ニュースのサムネイル読み込み中を既存記事セルと同じ loading_l に統一、記事間の境界線を削除、選択ハイライトを新着の記事セルと同じ標準挙動に。
  • 代表戦ナビのカード幅を内容追従に(固定138/124 → 左右8pt余白・下限90pt・上限138pt)。

やったこと(サーバ・DB):

  • 代表スカッド未登録の11名を選手マスタへ追録(冨安・長友・板倉・上田・菅原・中村敬斗・小川・渡辺剛・大迫・早川・鈴木淳之介)。海外追跡クラブ非所属なので team_id=0。本番RDSへ適用してマスタ54→65名。シード正本 digest-jp-players-seed_20260723.sql にも追記(operation ce1545d)。
  • jpPlayer API で team_id=0 のときチーム解決をスキップ(server 2a54c6a・デプロイ済み)。fnlive_teams の id=0 行に誤一致して冨安の所属が「ゾリャ・ルハーンシク」と表示されていた。
  • 前セッションで作った related_articles 追加(server c354fa5)もこの日にデプロイ。Pipeline 2回とも Succeeded、本番 /api4/jpPlayer で確認。

やったこと(本番データで露見した不具合):

  • 関連記事のデコードが本番で失敗していた(「データのフォーマットが正しくない」)。CakePHP がDB値を文字列で返すため "id": "2460525" となり、Int 決め打ちの DigestRelatedArticle が落ちていた。数値/文字列の両対応に修正し、同じ型ずれをローカルでも踏めるようダミーJSONも文字列型へ揃えた。
  • 海外クラブ非所属の選手はクラブ直近5試合ブロックごと非表示に(見出し・グラフ・リスト・合計をまとめて出さない)。

やったこと(配布・ドキュメント):

  • 実機(iPhone 14)へ直挿しビルドで一通り確認。TestFlight 配布は You 側で実施。
  • トムソン確認依頼の説明文を作成: トムソン確認依頼_ダイジェスト_20260724.md。画面ごとの見どころ、管理画面(対象選手 / 表示期間設定)の説明、シーズンオフゆえのデータ薄さと一時設定を広げている旨、評価グラフの3試合条件までカバー。

詰まったこと / 気づき:

  • ダミーJSONと本番レスポンスの型ずれはローカルでは絶対に検出できない。今回の related_articles がまさにそれで、シミュレータのダミーでは通るが本番だけ落ちた。ダミーは本番と同じ型で持つ、を徹底する。
  • シミュレータを再起動すると -Digest*Path 引数が落ちて本番データに戻る。ダミーで見たいときは xcrun simctl launch で毎回明示する。逆に本番で見たいときは引数なしで起動すればよい。
  • 「評価グラフが出ない」「試合が2件しかない」は仕様どおり(正本に「評価データ3試合以上」と明記)。ダイジェスト用の成績テーブルは収集開始以降の試合しか持たないため、シーズンオフの今は薄く見える。

次回やること: トムソンのフィードバック対応 → リリース前に一時設定を戻す(national_days_after=1 / overseas_upcoming_days=7 / overseas_recent_days=1)→ アーカイブして App Store 提出。


2026-07-23

13:40 - 今日の海外組ダイジェストのプレビュー対応(遡り期間の設定値化+5月試合バックフィル)

  • 今日の海外組が確認できない件: 対象8リーグがオフシーズンで、終了24時間以内の試合がゼロ(最新は5月末の前シーズン最終節)。実データで自然表示されるのは8月開幕後。
  • プレビュー対応(server 93120eb): 「今日」の遡り日数を overseas_recent_days(既定1=24時間・1〜90)として fn_digest_settings に追加。DataProvider取得窓とPresenterのtodayフィルタ両方が参照。buildOverseasは省略時1の互換引数でテスト35+32 PASS維持。
  • 一時設定+バックフィル: overseas_recent_days=65 に設定し、5/20〜5/31の対象試合15件を updateFixture/{id} でバックフィル。MVP=横山歩夢(ヘンク・★8.2 1G)ほか実データで「今日のMVP」「今日の海外組」一覧が正常表示。シミュレータで確認(更新バッジ点灯も確認)。
  • ⚠️ 一時設定(要戻し)に追加: overseas_recent_days=65 も after=30/upcoming=30 と同様、確認後 or リリース前に 1 へ戻す。

12:50 - You レビューのUI指摘対応(タブ中央寄せ・サブタブアイコン)

  • 上部タブの右寄り解消(ios ffa6be9): TabViewの中央寄せが「中央タブ境界を画面中心に合わせる」実装で、幅広の「ダイジェスト」タブがあると全体が右に流れて見えた。タブ群全体の幾何中心を画面中心に合わせる方式へ変更(4タブ/3タブ両方で目視確認済み)。
  • サブタブのアイコン欠落(ios 8b5d4da): 正本画像のサブタブは「地球 海外組」「日の丸 日本代表」だが実装は文字のみだった。絵文字はiOS 26のUISegmentedControlで欠字(⍰)になるため、SF Symbol(globe.asia.australia.fill)+Core Graphicsで描いた日の丸をテキストと合成した画像で実装。VoiceOverラベル維持。
  • You のレビュー継続中(追加指摘があれば随時対応)。

11:40 - トムソン確認用の実データ表示化(海外組日数の設定値化+一時拡大・代表モードON・W杯バックフィル)+PHP5.3バグ修正

目的: トムソンに実物UIを確認してもらうため、仕様の枠内でダイジェストを「今」表示させる。

  • 海外組「これから」日数を設定値化(server fb02648): overseas_upcoming_days を fn_digest_settings に追加(既定7=仕様値・0〜30・管理画面可変・削除保護)。DataProvider取得窓とPresenter表示フィルタの両方が参照。テスト35+32 PASS維持。
  • 一時設定: national_days_after=30(日本最終戦6/30から7/30まで代表モードON)・overseas_upcoming_days=30いずれもトムソン確認後 or リリース前に 1 / 7 へ戻すこと。
  • バックフィル: 日本のW杯4試合+アイスランド戦を updateFixture/{id} で投入(各24〜26行のstats)。MVP実データ: ブラジル戦=冨安★7.7・チュニジア戦=上田★9.7・オランダ戦=鈴木彩艶★7.9 等。
  • 🐛 本番E2Eで3個目のバグ発見・修正(server a437757): 本番はPHP 5.3.29で、5.3のクロージャはクラススコープを持たないため DigestPlayerSorter のusortコールバック内private呼び出しがFatal error。stats投入後に初めて実行される経路でローカルPHP7.4テストでは検出不能だった。該当メソッドをpublic化し、本番機のPHP5.3で全Lib lint+ソート経路スモークPASSを確認して修正。
  • E2E確認: S3 overseas.json=upcoming 12件(セルティック開幕戦〜JPL各節〜ベティスvsソシエダ・日本人選手名入り)/ national.json=mode ON・W杯実データ。シミュレータのクリーンインストールでタブ自然出現→海外組カード・代表ナビ・MVP表示すべて正常。既知の軽微事象: 空データ時代のアプリ内キャッシュが残る起動ではタブ出現が1起動遅れることがある(新規インストールユーザーには無関係)。
  • 残り: You が 5.6.0 を TestFlight へ → トムソン実物確認 → 設定戻し(after=1・upcoming=7)→ リリース調整。

10:00 - ⑦iOS準備完了(5.6.0バージョン設定・sim実データ確認・push済み)+チーム名整備の追い足し

  • バージョン5.6.0設定(build符号化50600・A/B min_build=50505を満たしエンロール継続)→ ios a7d5c17 としてcommit・9コミットをGitHubへpush済み
  • シミュレータで本番データE2E確認: 5.6.0ビルドSUCCEEDED・起動OK。ダイジェストタブは表示対象なし(海外組空+代表モード外)で非表示=仕様2-5どおり。DEBUG引数で本番S3を指定して強制表示し、海外組空状態・日本代表空状態の文言表示を確認。既存の試合一覧(W杯決勝まで)・新着も正常。
  • 取り込み拡張の実動確認: セルティック開幕戦(8/4)・JPL各節・ソシエダ戦など対象クラブの将来fixtureが既に取り込み済み。8月開幕が7日以内に入り次第overseas.jsonに載りタブが自然表示される見込み。
  • チーム名の追い足し整備: 将来fixtureに登場する名前未整備12チーム(ロンメルSK・ベフェレン・ザンクト・ガレン・マラガ等)の name_long/medium/short/name_ja を投入→未整備ゼロに。
  • 残り(You作業): 実機確認(Xcode直挿し)→ アーカイブ → App Store提出(リリース時期はトムソンと調整)。

09:10 - ダイジェスト運用適用①〜⑤完了(DDL・デプロイ・選手マスター54名・crontab・S3配信)

テーマ: レビューア側TODO(運用適用)を①〜⑤まで一気に実施。トムソン確認は You 回答「試合一覧増加OK」で解消。ダイジェストのサーバー側は本番稼働開始

やったこと:

  • ①DDL適用: 3ファイルを本番RDSへ適用(MySQL 8.4.8・DB既定utf8mb3だが新テーブルはutf8mb4指定・接続encoding=utf8mb4で整合)。002が fnlive_teams に存在しない del 列参照で失敗→インデックスを is_jp_target 単独に修正して適用(server bf9b38b)。対象リーグ14件は全件登録済み・del=0だった(id 7/30も既存)。
  • ②server push=デプロイ: 6コミットpush→Pipeline Succeeded。後方互換smoke PASS(/v1/liveHistories・/v2/fixture・/api4/articles 全200)。fixture取り込み判定は選手マスター空なら現状同一(試合増加は③以降)と確認してからpush。
  • ③選手マスター初期投入: API-Footballで8リーグ×2025全選手走査(283req・日本人56名抽出)→transfers照会56req→超WORLDサッカー7/22版で2026-27夏移籍26件を突合→JPL 2026-27構成・ラスパルマス昇格失敗をAPI/Webで確認→29クラブの現行スカッドAPIで在籍検証(背番号取得)→54名投入(active 52・needs_review 22・南野/守田は契約満了でdisplay_on=0)。seed SQLは digest-jp-players-seed_20260723.sql に保存。
    • 落とし穴: Le Havre のAPI ID誤り(79=リールだった→111)。fnlive_teams実データとの突合で発見。
  • チームname_ja: 既存 name_mediumname_ja へ一括コピー(666件)→ダイジェスト表記が既存アプリと同一に。syncJpTargetTeams 実行で26クラブ+日本代表に is_jp_target=1
  • 日本代表スカッド: crawlNationalSquad 初回実行(26名・W杯2026メンバー)+name_ja 26/26投入(crawlはname_jaを上書きしない設計)。
  • ④crontab適用: cron EC2に4行追加(バックフィル10分・スカッド日次4:17・S3生成毎分×2)。適用前バックアップ ~/crontab_backup_20260723.txt
  • ⑤S3配信・E2E: 本番E2Eでバグ1件発見・修正: DigestDataProvider が親モデルだけ useRO() し belongsTo先が default のままでdatasource名不一致→CakePHPがJOINを生成せず FnliveHomeTeam.id Unknown column(M7で「DataProvider本番E2E未検証」と明記されていたリスク領域そのもの)。関連3モデルもROに揃えて修正(server 2eca081・デプロイ済み)。S3 digest/overseas.json(空構造・8月開幕まで正常)/digest/national.json(代表戦実データ・日本語表記OK)公開200確認。/api4/jpPlayer/32862 で久保建英の全項目返却OK。管理画面3URLは302(Portal認証)疎通のみ確認。

接続経路メモ: RDSは非公開→Webサーバー1号機(3.112.232.196)経由SSHトンネル。cron EC2経由はMySQLユーザーのホスト制限で不可。SG(sg-0461787f6302fd838)のport22に現IPを追加(claude digest deploy 20260723)。MySQL 9.6クライアントは MYSQL_PWD 非対応・パスワードは -p 直渡し。

残タスク: ⑥管理画面の目視確認(Portal→対象選手/表示期間設定)・⑦iOS実機確認→バージョン設定→App Store提出(トムソンとリリース時期調整)・トムソンへ実装完了報告。needs_review 22名の精査(S.Saito/N.Schmidt/K.Kosugiの日本語名、移籍直後組のスカッド反映待ち)。任意: 直近代表戦の過去fixtureを updateFixture/{id} で個別バックフィル(評価/出場データ埋め)。


2026-07-22

23:22 - ダイジェスト新機能 実装フェーズ完走(Codex分業 M0〜M7・全レビュー承認)

テーマ: トムソン実装許可→Codex実装指示書作成→Codex分業でM0(実装計画)〜M7(総合検証)を1日で完走。Claudeが各マイルストーンをレビューし、指摘18件(R-1〜R-18)を全消化して最終承認。

体制と成果物:

  • 指示書 codex-digest-implementation_指示書.md(M0レビューゲート・push禁止・API実コール上限100req・既存API後方互換などの絶対ルール)+仕様書正本をリポジトリに取り込み(5041cdf)。
  • サーバー5コミット(収集基盤/配信API/管理画面+レビュー修正2)・iOS8コミット(海外組/選手詳細/日本代表/試合画面+バッジ+レビュー修正3)。全てローカルcommitのみ・push未実施(server push=本番デプロイのため)。
  • 記録は codex-digest-notes.md に全マイルストーンの完了報告+レビュー結果を蓄積。スクショは codex-digest-shots/ に約30枚。
  • 開発中のAPI-Football実コールは累計4req。CLIテスト: lib 35 / presenter 32 assertions PASS。最終HEADのiOSビルドはレビュー側でも独立実行し BUILD SUCCEEDED。

レビューで本番到達前に潰した主な問題:

  • チーム登録の insertIgnore→upsert化で手動整備済み日本語チーム名が毎クロールAPI英語名で上書きされる回帰(R-3。本番 /v1/liveHistories の実データ確認で発覚)
  • アジアカップ(2027年1月)・W杯予選が代表戦の取り込み対象外でリリース半年でナビが空になる問題(R-7。リーグID 7/30 を実APIで確認して追加)
  • 手書きiOSテストデータと実サーバー契約のドリフトによるLIVE経過時間の二重表示(R-14→再発防止としてR-16: fixture手書き禁止・サーバーPresenterテスト生成物を sync_from_server.sh で同期する規約化)
  • 過去代表戦で選手名が空になるスカッドキャッシュ依存(R-8→stats名スナップショット)、KO+3.5h超で終わる試合が「今日」から漏れる観測窓エッジ(R-1)ほか

設計上の要点(実装で確定):

  • v3 /fixtures?id= 全部同梱1reqを活かし、既存 ovserveFixtures の監視対象拡張+捨てていたstats保存だけでデータ蓄積(新規ポーリングなし)。ダイジェスト2種はS3静的JSON(cron毎分生成・preview引数付き)、選手詳細は /api4/jpPlayer/{id}(DBCache 30秒)。既存APIはキー追加のみで後方互換。
  • 更新バッジは端末保存の確認済みfixture ID集合との差集合方式。サブタブlazy生成により「代表側初期表示では消えない」仕様を構造で担保。

次回やること: 運用適用(notes「レビューア側TODO」参照): DDL適用→server push→対象選手マスター・日本語名パイプ→crontab→S3確認→実機確認→App Store提出。トムソンへ実装完了報告(試合一覧の試合増加の確認回答も待ち)。

13:57 - トムソン新機能提案「海外組・日本代表ダイジェスト」仕様読解+API-Football実叩き検証(実装可能と判断)

テーマ: トムソンから開発仕様書HTML(~/Downloads/FootballNEXT_海外組_日本代表ダイジェスト_開発仕様書_構成調整版.html・6.8MB・画面イメージ21枚埋込)を受領。読解→仕様書内「要確認」だったAPI実現性を実叩きで検証→DM返信文面作成まで。

仕様概要:

  • 新規3画面(海外組ダイジェスト=主役/選手詳細/日本代表ダイジェスト)+既存修正3画面(試合一覧・クラブ試合詳細・日本代表試合詳細)+上部ダイジェストタブ・更新バッジ(ピンクドット)。
  • 対象: 現行8リーグ(スコットランド/ベルギー/ラ・リーガ/セリエA/プレミア/リーグ・アン/ブンデス/ポルトガル)+CL/EL の日本人選手。国内カップ戦・対象外リーグは初期スコープ外。
  • 状態別表示(PRE_MATCH/LINEUP/LIVE/FINISHED)・欠損処理・共通ソート・日本代表モード期間(試合3日前〜翌日・管理画面可変)まで定義済みで練度は高い。
  • トムソン側の数値目標: DAU 1万(MAU1.7万の60%前提)・インストール+500/月・収益50万円/月。X運用(月30投稿・CTR3%/CVR10%)前提入り=開発側コミット数字ではない扱いに注意。

API検証結果(RapidAPI API-Football・実叩き約25リクエスト)→ 現行プランで実現可:

  • 現行プランは 7,500req/日(レスポンスヘッダーで確認)。直近24hの実消費 ~85req(オフシーズン)。
  • カバレッジ: 対象8リーグ+CL/EL+親善試合+W杯すべてで events/lineups/statistics_players(=評価・出場時間・G/A)提供あり。未開幕2026-27だけ全falseだが、これは毎年開幕時にtrueへ切り替わる仕様(2023〜2025は全true)。
  • 実データ確認: ベルギー(ヘント対ヘンク・橋岡大樹の評価7.9取得)・スコットランド(セルティック対ハーツ 40人中32人評価あり。欠けたのは昇降格プレーオフのみ=仕様の欠損処理で吸収可)・日本代表(W杯ブラジル戦・親善アイスランド戦とも評価あり)・代表招集26人は /players/squads?team=12 で取得可。
  • 決定的事実: v3 /fixtures?id=X は events/lineups/選手stats(出場時間・評価・G/A・ポジション・先発フラグ)全部同梱で1リクエスト。既存 ovserveFixtures(KO-1h〜+3.5hを2分間隔・1試合1req)が既にこのエンドポイントを叩いている → 監視対象を日本人選手所属チームの試合へ拡張+今捨てている選手statsをDB保存するだけでダイジェストのデータはほぼ賄える。選手詳細の直近5試合も蓄積から構成。
  • 見積もり: 最繁忙日(土曜に日本人試合20〜25件重複)でも +2,700〜3,400req/日=現行プラン内。必要ならダイジェスト専用試合のポーリングを3〜5分間隔へ調整で安全圏。
  • APIで取れないもの: 日本語名(ローマ字のみ)→ Claude(サブスク内Claude Code)でローマ字名+チーム→日本語名解決・WebSearch裏取り・needs_reviewフラグ運用で初期リリースから対応可能と判断。クラブ名・リーグ名の日本語表記も同パイプで対応(チームマスターにも日本語名カラムが必要)。

やったこと(その他):

  • トムソンへのDM返信文面を作成(実装可能・API検証済みで追加費用不要見込み・日本語名も初期対応見込み・工数見積もり後送)。数値目標への言及は意図的に非含有。
  • 仕様書テキスト・画面イメージは scratchpad に抽出(一時ファイル・未保存)。

次回やること: トムソン返信後に工数・スケジュール見積もり+フェーズ分割案の作成。

08:30 - 起動時広告A/B: dev-timer #31 成熟後残差率の初回read(ガードレール④ PASS)

テーマ: dev-timer #31(7/22 17:05予定)を前倒し実行。ガードレール④=成熟後残差率の初回read。GA4 Data API(property 211701453)。

母集団/定義: config_version=cv_9 かつ成熟 target_cohort_day 07-08〜07-19(maturation=3日・watermark=07-19、07-20/21/22は未成熟で除外)。残差率 = 1 − (content_reached + launch_no_content)/target_launch(群別・イベント数)。5.5.3レガシー勢((not set)/cv_8)は母集団外として除外。

結果(PASS):

  • A(50分/対照): target_launch 150,185 / content_reached 114,947 / launch_no_content 27,159 → 残差 8,079 = 5.38%
  • B(1440分/介入): target_launch 148,656 / content_reached 121,476 / launch_no_content 21,730 → 残差 5,450 = 3.67%
  • 判定: 絶対 A5.38%/B3.67% 両方 <10% ✓、群間差 1.71pp <3pp ✓ガードレール④ PASS。主指標を判定保留にしない。
  • 日次安定: per-day で A5.2-5.5% / B3.7-3.9%、外れ日なし(配送グリッチ・実装漏れの兆候なし)。
  • SRM: クリーン新コホート(enrollment_day 07-06〜07-19)A=582 / B=564 users(50.8/49.2%・χ²≈0.28・p≈0.59)→ SRM無し ✓。
  • GA4品質メタ: 約30万event(≪1000万の sampling 閾値)・低カーディナリティ → sampling/thresholding/(other) の結論影響なし。

参考(主指標の向き・非確定): 未到達離脱率 launch_no_content/target_launchA=18.08% / B=14.62%(B が 3.46pp 低い)=仮説の向き。前回 first-look(A19.7/B15.8)と整合。終了日固定・途中判定しない方針につき結論ではない(本解析は9月中旬)。

後始末: dev-timer #31 を done 化。次チェックポイント登録=#36(8/17)enrollment窓クローズ確認+残差率/SRM 中間再read#37(9/15)本解析(残差率確定・未到達離脱率本判定・円/人・D7/D14/母集団 enrollment_day>=2026-07-06 かつ cv_9)。


2026-07-11

17:22 - 起動時広告A/B: dev-timer #30 v9再スタート健全性確認(GA4 Data API・全項目クリーン)

テーマ: v9 配置(7/8 00:55)から3日後の健全性確認。dev-timer #30 の通知起点。GA4 Data API(property 211701453)で6クエリ実施。

結果(全項目クリーン):

  • ① 新コホートSRM なし: experiment_enrolled(ISO enrollment_day)A=145/B=144(7/7〜7/11途中・χ²≈0.003・p≈0.95)。日別も 7/8 31/31・7/9 48/47・7/10 50/51 と均衡。
  • ② cv_8 残滓は急減: 1,568(7/8)→873→658→68(7/11途中)。キャッシュv8のTTL/再取得で消滅方向。cv_9 は 23.6k→61.9k→85.1k(7/10) と主流化。(not set) 大量(7/10=369k)は5.5.3勢のキャッシュv8実験分で日々減少中(556k→369k)・cv_9フィルタで解析除外のため無害。
  • ③ enrollment_day 形成正常: ISO 2026-07-0707-11 で形成。(not set) enroll(A=273/B=232)は5.5.3勢=解析除外。
  • ④ 追加確認: applied_interval A=50.0/B=1440.0 ぴったり(cv_9 decision A=31,232/B=30,752件)。kill_observed 全61,984件が文字列"false"・true=0件。days_since_enrollment は (not set) ゼロ・d00〜d17のみ(d13〜d17の山=汚染コホート勢・d00〜d05=クリーン新コホート)・D28+なし=窓外gate機能。funnel全イベント着弾。fn_ad_revenue A=4,637人/B=4,696人 両群着弾。

気づき(要留意):

  • クリーンコホートの蓄積は約100人/日(新規インストール由来のみ。既存5.5.5ユーザーの大半は旧enrollment_dayを引きずり除外)。窓終了8/17までで**総勢約4,000人(各群約2,000人)**見込み=6月点火時の初速(3日で9,400人)よりだいぶ小さい。実質再スタートの織り込み済みの帰結だが、9月中旬の本解析の検出力見積もりはこの規模感が前提
  • ga4 MCP の dimension_filter は snake_case(field_name/string_filter/and_group)でないと Invalid structure で弾かれる。

次回やること:

  • dev-timer #31(7/22 17:05・登録済み確認済) 成熟後残差率の初回read(D0-D7)。
  • 本番解析(残差率・円/人)は9月中旬頃。母集団 enrollment_day>=2026-07-06 かつ cv_9。

2026-07-08

17:07 - 起動時広告A/B: コホートparam型ズレ修正〜5.5.5リリース〜v9再スタートまで完了(複数日の一連作業まとめ)

maturation明けの first-look を起点に、計測欠陥の発見〜修正〜リリース〜実験クリーン再スタートまでを一気通貫で完遂。

やったこと(時系列):

  • 6/28 first-look で重大欠陥発見: GA4 で機構は健全(SRM無し A4720/B4712・p≈0.93/applied_interval A50/B1440/kill 0件)だが、target_cohort_day/enrollment_day/days_since_enrollment/config_version/kill_observed が全件(not set)。根本原因=数値/Bool param を text の event-scoped custom dimension に送っていた型ズレ(String dimensionの assigned_group・metricの applied_interval は正常)。
  • 修正(emit境界で非数値ラベル化): ABTest.ga4*Label helper 追加(保存schemaのInt/Boolは不変)。日付→ISO 2026-07-06、days_since→d00d27、config_version→cv_N、kill_observed→"true"/"false"。ABAdCollector の追跡窓外(D28+)送信を gate、ga4DayLabel に範囲+実在日validation。
  • gods-talk で Codex 3ラウンドレビュー(方針→実コード→最終確認)。high2件(数字だけ文字列不可・config_version同型ズレ)他すべて反映、release可判定。ios master 28af0f1/698cf5f/83a4796
  • Apple 5.2.3 却下対応: 5.5.4 提出→YouTube discovery で却下。Thomson合意でハンバーガーの「Youtube動画」+トップ新着カルーセルを一時非表示(Menus.movie と switch をコメントアウト、HorizontalMoviesRow#if false。可逆)。返信せず新ビルドで再申請方針。
  • バージョン迷走→5.5.5で確定: ASCで5.5.5版が作れず一時5.5.4戻し(勝手な版上げを指摘され修正)、最終的に You 側で 5.5.5 として TestFlight→申請。7/6 リリース
  • 7/7 本番着弾検証成功: appVersion=5.5.5 で 5 dimension 全て non-empty 着弾確認(旧5.5.3は(not set))。予測どおりの汚染(5.5.3既enroll→5.5.5更新勢の古いenrollment_day d07〜d13)も実測。
  • 7/8 v9 config を S3 配置(実質再スタート): aws s3 cp --acl public-read --profile thomson-ik で v8→v9 差し替え(min_build=50503→50505・enrollment窓 6/24-8/5→7/6-8/17・config_version=9)。公開URLで HTTP200・v9着弾確認。旧v8はscratchpadにバックアップ。GA4メモ(v9再スタート・7/8)記入。
  • cv_9 着弾確認: 配置12時間後、5.5.5 の約89%が cv_9(2839件)。クリーン再スタート成功

決めたこと(DECISIONS 2026-06-28×3・2026-07-07 に記録済):

  • experiment_id は据置(アプリ側ハードコード+validate一致要求で config 変更不可)。クリーン境界は min_build=50505+解析フィルタ enrollment_day>=2026-07-06+窓取り直しで担保。
  • DebugView 提出前確認は任意化(dimensionマッピングを判定できないため)、確定検証は配布翌日 Data API に一本化。

次回やること:

  • dev-timer #30(7/11) 再スタート後の健全性確認(新コホートSRM・cv_8残り消滅・enrollment_day>=7/6形成)。
  • dev-timer #31(7/22) 成熟後残差率の初回read(D0-D7)。
  • 本番解析(成熟後 残差率・円/人)は 9月中旬頃(窓終了8/17+追跡28日+成熟3日)。母集団は enrollment_day>=2026-07-06 かつ cv_9。
  • site-ad-scoring WIP は ios stash@{0} 温存中(別レーン)。

2026-06-20

実況板(LiveHorizontalListCell)が「もっと見る→上に戻る」で壊れる不具合を修正(5.5.3 申請前の単発バグ潰し)

テーマ: 5.5.3 申請前に、AB テストとは無関係の UI バグを1件修正。新着タブ先頭の実況板(横スクロールのライブカード)が、記事一覧の「もっと見る」(offset>0 load)を一度してから上に戻ると、ライブカードが消えて緑の「過去 実況版一覧へ」だけが左端に残る壊れ方をしていた(ios f275fed)。

真因: TopArticleTableView.importContents が offset 問わず毎回 realm.delete(realm.objects(Live.self))content["lives"] から再追加していた。もっと見る(offset>0)のレスポンスは lives が空なので全削除だけ走り Live が DB から消滅。一方、先頭の LiveHorizontalListRow(offset==0 で生成)は古い liveIds を保持し続け、LiveHorizontalListCell.puts は毎回 DB.objects(Live.self).byID(liveId) で引き直すため nil → カード0枚。row.liveIds.count > 0 は保たれるので moreView だけ offsetX=0 に出ていた。

やったこと:

  • 修正①(根本): Live の更新(delete+再追加)を offset==0(フル取得/プルリフレッシュ)時のみに限定。もっと見るでは Live を消さない。
  • 修正②(防御): puts で moreView を「カードが1枚以上描画できたとき(offsetX>0)」だけ出す。あわせて再利用時クリーンアップを LiveListViewLiveListMoreView 両方 remove に拡張し、横スクロール位置を .zero リセット(累積・残留の予防)。

検証: 実機テストで「もっと見る→上に戻る」でカードが正しく残ることを確認(You)。Debug build 0 errors・改修2ファイル新規警告なし。最初 cell 側(moreView 累積)だけ直して的外れ→スクショで真因がデータ側(Live 全削除)と判明し②から①へ。

気づき/判断: importContents は LiveArticleId / LogoLink / Enquete も同様に offset 問わず delete-all している(もっと見るで一時的に消え得る潜在バグ)。今回は報告のあった Live のみ修正。残り3つは表示が DB 引き直しでないか別経路のため即時破綻はしていないが、いずれ offset==0 ゲートで揃えるのが望ましい。5.5.3 申請に本修正を含める

23:00 - 起動時広告A/B 実装レビュー round10: round9 medium close・sign-off 回復・実装レビューレーン完了

テーマ: round9 で差し戻された medium(R7-#2 revert による render 失敗の過大計上)の修正(gate 復元+#if DEBUG 化、ios 311e3ad / op 4e489da)を gods-talk round10 でレビュー。clean close

round10 feedback(ab-test-implementation-review-010):

  • [high] 該当なし / [medium] 該当なしrenderNativeAdView の戻り値を保持し true 時のみ recordImpression → round9 medium「render 失敗の過大計上」は解消。false 経路(releaseCurrentLeasereleaseAdadObject=nil→hidden)は main 直列で race・二重 destroy・負 lease・retain cycle なし。loader(for:)!expired 維持で「live entry render 失敗=Bool gate」「expire 済み再render=loader 取得失敗」両方が閉じる。実行ロジックは 31dc22b の R7-#2 と機能的に同等へ復帰(差分はコメント+expire hook の Debug 限定化のみ)。round9 medium は close
  • [low] expire hook の #if DEBUG 隔離 完了 — release に残る参照経路なし・Release build 成功で確認。round9 low 推奨を満たす。
  • [low] round8 残 low(render false 即時 reload なし/timeout・render false の XCTest 未整備/kill_observed_at 説明文)は非ブロッカーの将来課題
  • 点火可否: 311e3ad で round9 medium 解消・round8 sign-off 回復。現行コードで 5.5.3 提出可、点火前チェックリスト完了後に S3 active=true で開始可。新 high/medium ブロッカーなし。通常 native 表示の短い実機再スモークは望ましいが medium close の必須前提にしない(gate は sign-off 済み実装へ復帰・A-1 で rendered=true/描画/ILRD/GA4 impression 確認済み・Release 追加差分はコメントのみ)。

気づき/判断: round9 で私が入れた誤った revert を round10 で正式に巻き戻し close できた。reviewer が実機再スモークを必須前提から外したため、コードレビュー上のブロッカーはゼロ=実装レビューレーン完了。残るは純粋に運用・外部タスク(GA4 2件登録・5.5.3 提出・点火)。

現在地: 実装レビュー round1〜10 完了・sign-off 確定。本番中立(active=false/v7/ttl14日)、5.5.3 据え置きで全修正積載。

次回やること: 5.5.3 提出/リリース(任意で提出前に通常 native 表示の短い実機スモーク)→ GA4 custom定義 2件を手登録 → S3 を v8/active=true・enrollment窓(リリース日起点42日)・min_build=50503 で点火+点火ログ記入。

22:30 - 起動時広告A/B 実装レビュー round9: R7-#2 revert を差し戻し(impression gate 復元)+expire hook を #if DEBUG 化

テーマ: round8 sign-off 後に入れた2変更(R7-#2 revert・expire 人工注入 hook)を gods-talk round9 でレビュー。reviewer が R7-#2 revert を medium 点火ブロッカーとして差し戻し。指摘どおり gate を復元し、hook を #if DEBUG 隔離。Debug/Release 両ビルド 0 errors を確認。

round9 feedback(ab-test-implementation-review-009):

  • [medium] R7-#2 revert は「render 失敗の過大計上」を再導入=点火ブロッカー。MAX SDK 13.6.2 MANativeAdLoader.hrenderNativeAdView:withAd: の戻り値を「正常 render されたら YES」と明記。revert 後は戻り値を捨てて常時 recordImpression するため、SDK が false を返した render 失敗も実表示として計上される。「実機で false が観測されなかった」は「false を成功扱いしてよい」根拠にならない(別 network/view 再利用/将来 adapter 差分で false がありうる)。loader(for:)!expired ガードは expire 後 render を防ぐが live entry の render 失敗は扱わない。回復性(false 時 hide)と計上 gate(true 時のみ)は分離可能で、最低限 gate を復元すべき。私の「churn リスク」懸念は false 頻発前提で的外れ(churn でなく失敗 ad の回復処理)。
  • [low] expire hook は安全だが Release 除外推奨#if DEBUG)。[weak self]・main 直列化で leak/race/二重 destroy なし。実 SDK didExpire と同経路の代理として妥当。単独ブロッカーではない。
  • [low] round8 残 low: low1(render false 即時再取得なし)は false 分岐削除で表面上消えたが上記 medium に置換=解消ではない。low2(self-test 範囲)は expire 経路を hook で実機 end-to-end 確認した分前進。low3(kill_observed_at 説明文)は現行契約に誤って含めておらず現状で OK。
  • 点火可否: 現行 777128f のままでは sign-off 維持されず提出・点火を承認しない。gate 復元+Debug/Release build+通常 native 表示の短い再スモークで medium は閉じる。

やったこと(この会話で修正):

  • ① impression gate 復元 MiddleAppLovinMaxNativeAdCell.swift: let rendered = loader.renderNativeAdView(...) の戻り値を保持し if rendered { recordImpression } else { releaseCurrentLease()+releaseAd()+adObject=nil+hidden+return }(round8 の R7-#2 を実質復元・fail-closed)。コメントに revert が誤りだった旨と論点を明記。
  • ② expire hook を #if DEBUG 隔離 AppDelegate.swift(launchArg 設定)+MiddleAppLovinNativeAd.swiftdebugSimulateExpire 宣言・asyncAfter 発火の2箇所)。全6参照が #if DEBUG 内に収まることを grep 確認。

検証: XcodeBuildMCP(iPhone 17 / iOS 26.5)で Debug build SUCCEEDED 0 errorsRelease build SUCCEEDED 0 errors。改修3ファイルに新規警告なし。Release で debugSimulateExpire#if DEBUG 除外され参照漏れなしを確認。

気づき/判断: 私の R7-#2 revert は overreach だった。核心は「観測で false が出なかった」と「false は安全」の混同で、SDK 契約上 false は render 失敗を意味するので観測の有無に関係なく計上すべきでない。reviewer 指摘は妥当・却下なし。修正は round8 の sign-off 済み状態への復帰に等しいため round10 再レビューは任意(reviewer が close 条件を明示済み)。

現在地: round9 medium をコードで解消・両ビルド 0 errors。残るは通常 native 表示の短い実機再スモーク(You)。点火前チェックリストは GA4 2件手登録・5.5.3 提出が未。

次回やること: 通常 native 表示の実機再スモーク → 5.5.3 提出/リリース → GA4 2件登録 → S3 を v8/active=true・enrollment窓(当日起点)・min_build=50503 で点火。任意で round10 close 確認。

15:45 - 起動時広告A/B 実装レビュー round5〜8 完了・点火 sign-off 取得

テーマ: gods-talk 実装レビューを round5→8 まで回し、native lifecycle / kill属性 / 計測契約の残 high/medium を全解消。round8 で「5.5.3 提出・点火前チェックリスト完了後に S3 active=true で A/B 開始可・high/medium ブロッカーゼロ」の最終 sign-off を取得。実装レーンは実質完了し、以降は運用準備へ移行。

round5(medium3) ios 0089f65 / op ab066e4:

  • R5-#1 native 確定destroy: entry を class化し released(row利用終了)×displayLeases(描画中cell数)、両成立で destroy(固定0.6秒撤去)。cell が puts で lease 取得・gone/prepareForReuse/deinit で解放。実機スモーク PASS(scroll往復/pull-to-refresh/More連打: クラッシュ0・描画失敗0・loader nil 0)。
  • R5-#2 pending timeout(30s)+didExpireNativeAd(stock限定の失効・補充)。
  • R5-#3 kill属性: effective_interval_minutes(snapshot)を保ち applied_interval_minutes 分離・user property clear を SDK初期化guard前へ。

round6(medium3+low2) ios 3f64ade / op 546dd0a:

  • R6-#1 callback を onMain 集約+didLoad で pendingHandles 在籍世代照合(timeout済み遅延成功は即destroy・cap超過防止)。SDKヘッダで didLoad/didFail のthread保証なしが根拠。
  • R6-#2 didExpire を deque済み含む全entryへ・destroy条件 (released||expired)&&leases==0・cell puts で loader nil 検出→row無効化(4h超の無効広告 render/計上を停止)。
  • R6-#3/#5 GA4-only整合(kill_observed 必須dimension化)+TTL省略可統一。launchArg self-test(-ABNativeSelfTest)導入 7/7。

round7(medium2+low2) ios 31dc22b / op 07aa09f:

  • R7-#1 stale判定を CoreTableView.cellForRowAt の deque/nib選択前へ移動+同一pass再deque(Any?型非依存API で共通table を AppLovin非依存に)。
  • R7-#2 loader(for:)!expired 必須化+renderNativeAdView==true 時のみ impression(render失敗の過大計上を解消)。
  • R7-#3 self-test を precondition で fail-hard 化。R7-#4 kill_observed_at 削除(GA4-only で事後参照不能)。

round8 → 点火 sign-off:

  • high/medium ブロッカーゼロ。残 low(render-false 即時再取得なし=fail-closed/self-test範囲/廃止param名の説明文/runbook commit表記)は全て非ブロッカー。
  • 点火前チェックリスト(コード外・必須): ①GA4 custom定義の実登録(kill_observed/applied_interval_minutes 含む)②enrollment窓・判定値の確定 ③初期 active=false での本番config取得確認 ④点火/停止時刻の運用記録。

検証: 各round sim build 0 errors・改修ファイル新規警告なし。self-test 7/7(fail-hard後も通過)。R5-#1 lease は実機スモークPASS。limitation: sim は MAX native fill なしのため timeout/expire の end-to-end は未実行(destroy判定は純粋関数 self-test+外部レビューで担保)。

気づき/判断: round を追うごとに残課題が native expire 経路へ収束。私の R5-#2(stock限定)→R6-#2(puts無効化) は段階的に甘く、reviewer が cellForRow前段stale判定・renderable !expired・render Bool gate へ正した(毎回妥当・却下なし)。native は experiment flag 外で全ユーザーに作用するため fail-closed が必須。実装は完了、以降は GA4登録・値確定・実機スモークの運用準備へ。

14:30 - 起動時広告A/B Codex round3 の high3+medium4+low1 を修正+実機回帰スモークPASS

テーマ: Codex 実装レビュー round3 が新たに high3/medium6/low2 を指摘(私の判断ミス2件含む)。点火可否=「3 highが残ブロッカー」との総括。high3+medium4+low1 を修正し、実機で回帰スモーク(decision/native)を確認。

Codexの訂正(私の取りこぼし):

  • R3-#1: 私の decision 修正(完了ハンドラ再評価・d103ad5)が不完全だった。完了時は普通 ready=false で not_ready が latch → 後で ready になり show すると「not_ready なのに displayed」の同型矛盾が再発。
  • R3-#2: 「attempt紐付けは原理的に不可」は誤り。AppLovin SDK に showAdForPlacement: があり MAAd.placement で照合できる(=未実装だっただけ)。
  • R3-#3: R2-#3 の native cap(entries.count>20 無条件 destroy)を残置していた=表示中ad破棄・全ユーザー作用。

やったこと(high):

  • R3-#1/#2 5db2975: decision と show attempt を context 単位で一体・一度だけ claim。emitAdDecisionFinal/noteAdDecisionPending/finalizePendingAdDecision に再設計(非eligible→即final / eligible&ready→claim+show+final(shown) / eligible&not_ready→保留しcontext閉鎖時にnot_readyをfinal / SDK未init→保留)。claimAdAttempt() で show を context 1回に限定。placement に token(=launchId)を載せ show(forPlacement:)、callback は ad.placement 一致(isCurrentAttempt)時のみ funnel へ。完了ハンドラ再評価は app active 時のみ。→ decision と実表示が常に整合・旧attempt遅延callbackの誤帰属を排除。
  • R3-#3 5d26ee6: native cap 撤去。releaseAd(_:) を新設し NativeAdRow.deinit(row利用終了)で生成元loaderへdestroy。表示中ad破棄・上スクロール再表示時のloader(for:)nilを解消。entriesはlive row数で自然有界。 (medium/low):
  • R3-#4/#5/#6/#7 ef4d47e: kill-switchをshow直前に最新last-known-goodで確認(isKillSwitchActive・open contextにも反映)/strictIntの2^53飽和回避・ttl上限60日・config_version上限・samePolicyのttl比較Double化/applyConfigをmainへ集約(thread race排除)/時計巻き戻しfail-close(isWithinTrackingWindow)。
  • R3-#8 791952f+runbook 0789c73: build符号化を成分数・範囲でfail-close(衝突回避)+符号化規則/数値上限/kill-switch反映時間をrunbookに明記。
  • R3-#9(DEBUG定義+XCTest)は test基盤の別作業として先送り

実機回帰スモーク(iPhone 14・seed): decision=interval が実結果と整合(群B 24h以内→起動時広告抑止・矛盾なし)・content_reached発火・native middle_native の imp/rev 健在(スクロール往復で APPLOVIN_EXCHANGE/Liftoff)・クラッシュ0・描画失敗0。R3 変更の回帰なし。

気づき/判断: 実装レビューは round を重ねるごとに「実機/エッジでしか出ない」層(SDK初期化タイミング・placement照合・table cell lifecycle・thread race・時計異常)まで掘られた。私の誤判断(#2不可・decision修正の不完全・native cap残置)を round3 が捕捉。Codexの指摘は妥当で却下なし。

現在地: Codex round3 の点火ブロッカー(high3)+medium4+low1 を修正・push 済み。実機回帰スモークPASS。残るは Codex round4(R3修正の確認・推奨)か、R3が挙げた個別エッジ(not_ready→ready/21件超native/kill-switch mid-context)の的を絞った実機再検証、その後 5.5.3 提出。

次回やること: Codex round4 で R3 修正を確認 → 必要なら個別エッジ実機検証 → 5.5.3 提出→リリース→点火。R3-#9(DEBUG/XCTest基盤)は別途。

13:30 - 起動時広告A/B 実機検証(iPhone 14・iOS26.5)A〜D 全PASS+実機で発見したdecision矛盾を修正

テーマ: Xcode→実機直挿し(devicectl・Debug+DEBUG override・--console でNSLog捕捉・seed/-FIRDebugEnabled)で検証A〜Dを実施。全PASS。検証中に新たなdecisionバグを1件発見し修正・再検証した。

やったこと/結果:

  • 検証A(native描画+ILRD・最優先)PASS: 記事一覧スクロールで fn_ad_impression/fn_ad_revenue(slot=middle_native・ad_unit=562ca14298b68765・network=Liftoff Monetize・precision=exact)発火=R2-#3 handle方式で native ILRD 欠損なし。footer_banner も発火。クラッシュ0。観測用に ABAdCollector へ #if DEBUG NSLog 追加(d103ad5)。
  • 検証B(content_reached・R2-#4)PASS: ATT表示中で止めた時点で content_reached 0件(連鎖中抑止=負の確認)。プロンプト完了後のコールド再起動で content_reached 1件・time_to_content=1(正の確認)。途中の launch_no_content は「content前に背景化(Mac切替)」の正常動作と確定。
  • 検証C(群B処置)PASS: この端末は群Bバケット。decision_reason=interval・policy_eligible=false・effective_interval_minutes=1440・app_open非表示=24h間隔の処置が実機で動作
  • 検証D(#11・更新直後)PASS: -ABSimulateUpdaterState(旧キー=30分前・新キー=0 を注入)で移行発火を確認。updater_migration old=… new=(同値)=新キーへ旧値引き継ぎ成功、decision=interval・app_open非表示=アップデート直後の追加表示なしfd913dd)。
  • 実機で発見→修正したdecision矛盾d103ad5): コールド起動の最初の didBecomeActive は SDK未初期化のため、R2-#9aの sdk_not_initialized decision が emitAdDecisionOnce(R2-#m1)の「1回」を消費し、SDK初期化後の本当のdecision(interval/shown)を抑止していた(app_open_ad_displayedが出るのにdecision=sdk_not_initialized の矛盾)。→ SDK未初期化時はdecisionを出さず保留し、ALSdk初期化完了ハンドラで showAdIfReady を再評価して本当のdecisionを1回送る方式に変更。再検証で decision_reason=interval を確認。

気づき:

  • TestFlightは検証に不要(Youの指摘どおり)。広告SDKがdevice-onlyなだけで、Xcode→実機直挿し(Debug+実広告配信・ILRD発火)で完結。devicectl の app 引数は -- 区切りが必要(-ABSeedTestConfig が短縮フラグ列に誤解釈され -t で失敗する)。
  • 実機検証はコールド起動の SDK 初期化タイミングという、sim では出ない条件を踏んで decision バグを炙り出した=実機検証の価値。
  • seed は同一version・enrollmentStartArt相対値のため2回目以降は same-version-immutability(R2-#m5)で reject され cached 継続(テスト専用の挙動・本番影響なし)。

現在地: 実機検証 A〜D 全PASS・decisionバグ修正済み。S3 config は中立(active=false/v7/ttl14日)。5.5.3 据え置きで全修正+検証完了=5.5.3 提出可能な状態。残るは任意の Codex round3/R2 low。

次回やること: (任意)Codex round3 で R2修正群+実機発見のdecision修正を最終確認 → 5.5.3 を App Store 提出 → 承認後リリース → 普及を見て S3 点火(active=true・config_version+1・enrollment窓を実リリース日に調整)→ GA4着弾を Data API 検証+事前テスト①〜⑧。

12:30 - 起動時広告A/B Codex round2 残りの must-fix(R2-#4 / R2-#3)完了+実機検証手順を整備

テーマ: 11:30 の続き。Codex round2 の残 must-fix だった R2-#4(起動プロンプト連鎖の content_reached 誤確定)R2-#3(native loader 再構成) を実装し、実機検証手順書を作成。これで round2 の must-fix を全消化。

やったこと:

  • R2-#4 bcc9950: 起動プロンプト連鎖(Splash→EULA→RSS→TopReload→Push→ATT)を単一 boot covering 化。bootSequenceActive を onDidFinishLaunching でON、終端 InitialATTAction 解決(ATT非提示)または ATT VC dismiss で bootSequenceEnded()→content再評価。プロンプト中・隙間での content_reached 早期/群依存確定を抑止。sim(初回=プロンプト出っぱなし)で早期発火しないこと(負の確認)済。returning-user 経路の正の発火は実機検証へ。
  • R2-#3 55fbe06: native を 1 load = 1 delegate handleNativeAdLoadHandle)方式へ。各 loader を自分の callback に紐付け、並行 load の誤対応付け・revenueDelegate weak によるILRD欠損・誤 destroy を解消。loader(for:)/deque/didPayRevenue/cap destroy は維持、owner weak で循環断ち。compile確認のみ(sim で MAX native load 不可)→ 実機検証が必須関門
  • 実機検証手順書 ab_test_device_verification.md(op 81fc50f): 検証A native描画+ILRD(収益クリティカル・最優先)/B オンボーディング完走後content_reached/C AB funnel全体(seed or 一時active)/D 群A=現行どおり(#11)。観測は GA4 DebugView(-FIRDebugEnabled)。

気づき/判断:

  • 「TestFlight 必須」は誤り(Youの指摘)。広告SDKが device-only なだけで、Xcode→実機直挿し(Debug でも実広告配信・ILRD発火)で native 検証は完結。Release 構成で実機ビルドすれば本番経路(seedなし・最適化ON・DEBUG除外)も同時確認できる。TestFlight は最終配布の関門であって修正検証の必須要件ではない。
  • R2-#3 は revenue クリティカルでブラインド改修リスク高。描画は loader(for:) 経由(MiddleAppLovinMaxNativeAdCell.swift:57)で最新 .loader は不使用と確認したうえで実装。検証A 不合格ならリリース不可。

現在地: Codex round2 の must-fix を全 code-complete(ios master へ push 済み)。S3 config は緊急停止のまま(active=false / v7 / ttl14日)=本番中立。5.5.3 据え置きで全修正積載。残るは実機検証(You が Xcode→実機)と、任意の Codex round3 / R2 low

次回やること: 実機検証(手順書 ab_test_device_verification.md の A〜D)→ 合格で 5.5.3 提出→承認→リリース→ S3 点火(active=true・config_version+1・enrollment窓を実リリース日に調整)→ ga4 MCP 接続のうえ GA4 着弾検証+事前テスト①〜⑧。任意で Codex round3(R2修正群の最終確認)。

11:30 - 起動時広告A/B 点火→Codex実装レビューで重大バグ発覚→緊急停止→high/medium修正群(5.5.3据え置き)

テーマ: enrollment窓を決めて**点火(active=true)**まで実施した後、トムソン通知HTML公開・5.5.3バージョンバンプ・実装コードのCodexレビュー(gods-talk ab-test-implementation-review)を回したところ、実コードに重大バグが多数見つかり、緊急停止して順次修正した。本番は中立(active=false)に戻し、5.5.3 提出も検証完了まで保留。

時系列でやったこと:

  • 点火(段階B): ab_test_config.json を active=true / config_version=2 にして s3://footballnext/ab_test_config.json へ配置(op 851ee7a)。enrollment窓6/22開始なので週末は挙動不変の設計。
  • トムソン要確認3項目を HTML化→thomsons S3 公開起動時広告A-Bテスト_点火前最終確認_20260619.html)。Youより「確認は任されている」=叩き台確定扱い。
  • iOS版状況の確認(App Store Connect MCP): 現行公開は 5.5.2(6/12ビルド・AB計測コード未収録)。master HEAD にAB実装あり。番号が5.5.2のままだったので 5.5.3 へバンプ(ios 39fee19)。※5.5.3は未提出=番号据え置きで再利用可。
  • シミュレータ pre-flight(段階A健全性): 本番S3取得→パース→保存を実証済(前エントリ)。さらに seed test(-ABSeedTestConfig/-FIRDebugEnabled・要 SWIFT_ACTIVE_COMPILATION_CONDITIONS=DEBUG)で experiment_enrolled→target_launch→content_reached→app_open_ad_decision の GA4 送信(Analytics.logEvent)到達を確認。ga4 MCP は本セッション未接続でDataAPI裏取り不可→DebugView目視はYou側、と整理。
  • Codex 実装レビュー round1ab-test-implementation-review-001): high 8 / medium 5 / low 2。最重要は #1「showAdIfReadyがcanEnroll/窓を見ずB処置を適用=非参加者にB漏れ・点火済みconfigと相互作用」。
  • 緊急停止: 指摘#1を受け S3 を active=false+config_version+1 に戻す(op 、最終 v6)。5.5.3 提出も保留。
  • high/medium 修正(実機検証なしで安全な範囲を実装・各 seed 回帰確認):
    • #1 状態分離(isParticipating=既参加&&追跡窓内&&active/fresh|cached・trackingDays=28・beginTargetContextを既参加追跡/新規enrollに分離)3df1b59
    • #2 config単一snapshot+cached実装 fb79e31(※後述 R2-#1 で再設計)
    • #5/#7 callbackをadStateガード+hidden/failed emitを context クローズ前に・watchdogをcontext-scoped 4b5316e
    • #6 impression冪等キーを creative→広告instance×slot 63824d4
    • #3 build範囲 fail-close+dotted版パース、#11 群A旧キー移行、#9(a) SDK未初期化decision補完 6020850
    • #10 割当再作成防止、#8 config検証強化(schema互換/version>0/min<=max)5f6f589
    • native loader再構成(#6/#7 native)・#9(b) は当初「先送り」とした。
  • Codex round2ab-test-implementation-review-002): round1修正を確認しつつ 新規 high 4 / medium 6 / low 2。最重要 R2-#1「#2のsession全体固定+TTL6hで、cold launch間隔>6hの日次ユーザーが永久に非参加=実験不成立」。また R2-#3 で「revenueDelegateはweak=loader誤対応付けでILRD欠損しうる→nativeは先送り不可」と私のrationale誤りを訂正。R2-#4「起動overlay間gapでcontent_reachedが広告有無=群依存で誤確定(GATE交絡)」。
  • R2 修正群(実装・seed検証・push):
    • R2-#1 snapshotを session固定→context世代方式 に再設計(beginTargetContextがlast-known-goodを1回読みrecordに固定・fetchは次context有効)+既定TTL 6h→14日・seed時は実fetchスキップで決定化 4a9c516、config ttl 14日化 2115035
    • R2-#m2 追跡窓をJST暦日(daysSince<trackingDays)へ・R2-#m4 assignmentVersion fail-close・R2-#m5 厳密JSON型検証(strictBool/Int/Double)+同一version不変性 835e91f
    • R2-#m1 decisionをcontext単位1回(emitAdDecisionOnce)・R2-#m3 非参加化時にuser property clear f32fe12
    • R2-#m6 impression冪等を weak identity box 3e7fac2
    • R2-#2 達成可能分は adStateガードで対応済(残差=MAX callbackがtokenを返さない原理的制約として記録)

気づき/判断:

  • 提案書・実装計画の机上Codexレビューを5ラウンド通しても、**実コードでしか出ない重大バグ(policy/measurementの状態未分離、TTLとsnapshotの相互作用)**が出た。実装レビューは別物として必須。
  • 私の #2(session全体固定)は筋が悪く R2-#1 で日次ユーザーを壊した。context世代方式(record固定+fetchは次context)が正解。
  • native loader は「A/B妥当性に非影響・収益別経路」と判断したが、revenueDelegate weak の指摘で収益欠損あり=点火前ブロッカーへ格上げ。実機検証必須。

現在地: S3 config は active=false / v7 / ttl14日(中立)。5.5.3 据え置きで全修正を積載。残る must-fix は R2-#4(起動overlay gap・オンボーディング連鎖の covering token 化)R2-#3(native loader再構成・実機/TestFlight検証必須)

次回やること: R2-#4 を慎重に実装(LaunchManager連鎖に covering token)→ R2-#3 を実機検証とセットで → Codex round3 → 全通過で 5.5.3 提出→承認後リリース→ S3 再点火(active=true・config_version+1)。

08:20 - 起動時広告A/B 点火準備の続き(トムソン確認3項目HTML公開/enrollment窓確定/段階A=不活性config配置/シミュレータpre-flight PASS)

テーマ: BASE実装・GATE凍結・GA4登録の完了を受け、点火に向けた残作業を進めた。トムソン要確認3項目の通知物をHTML化してS3公開、enrollment期間を確定してconfigに設定、不活性config(active=false)を本番S3へ先行配置(段階A)、シミュレータで本番S3取得経路を実証(段階B=点火は保留)。

やったこと:

  • トムソン要確認3項目をHTML化→thomsons S3に公開起動時広告A-Bテスト_点火前最終確認_20260619.html)。3項目=①円/人コホート(締切6週/追跡28日/ITT)②収益正規化・residual(月平均eCPM・感度0.5–1.5×・普及率70%/旧版上限30%・月次総売上提供依頼)③事前テスト合格しきい値①〜⑧。提案書と同体裁、事業判断レベルに噛み砕き。URL: https://thomsons.s3.ap-northeast-1.amazonaws.com/起動時広告A-Bテスト_点火前最終確認_20260619.html(HTTP 200確認)。Youより「トムソン確認は任されているので不要」と判断 → 叩き台のまま確定扱い
  • enrollment窓を確定(叩き台6週間): enrollment_start_at=1782054000(2026-06-22 月 0:00 JST)/enrollment_end_at=1785682800(2026-08-03 月 0:00 JST・+42日)。JST深夜0時の月曜境界=コホート日が揃う。ab_test_config.json に2キー追加。解析データ成熟の目安は最終enroll日2026-08-02+追跡28日+maturation3日=〜2026-09-02。
  • 段階A(中立)= 不活性config を本番S3へ先行配置: s3://footballnext/ab_test_config.json(バケットfootballnextルート=Const.baseS3Url)。experiment_active=false=全員非参加で挙動不変。初回アップロードはpublic-read ACL未付与で403→--acl public-read付与で再アップロード→HTTP 200取得可に。配置先プロファイルは thomson-ik(footballnextバケットにアクセス可)。
  • シミュレータ pre-flight(段階Aの健全性確認)= PASS: iPhone 17 Pro sim でフルビルド→install→launch。ABConfigLoader.fetch()(AppDelegate:129)が本番S3を取得→パース→検証→UserDefaults保存まで通すことを、保存キー ab_config_app_open_interval_2026q3 の中身で実証(取得1秒)。全項目一致、enrollment窓epochがNSDate(reference-date 2001)として 1782054000/1785682800 にビット一致デコード。active=false なので canEnroll=false=enroll/AB event は出ず挙動不変も確認。これまで未実証だった本番S3取得経路を初めて実証(過去検証は seed 経路のみ)。

気づき/判断:

  • footballnext バケットはオブジェクト単位 public-read ACL運用(teiki等は200・新規アップロードは無ACLだと403)。S3配置時は --acl public-read 必須。
  • 段階Aで確認できるのは「本番S3取得+パース+保存」まで。GA4のAB funnel(experiment_enrolled等)は active=false では出ない(点火か seed test が必要)。
  • 点火しても enrollment窓開始(6/22 0:00 JST)まで誰もenrollしない設計(canEnroll が窓開始前 false)=今点火しても週末は挙動不変、月曜の最初の対象起動から群Bが24h間隔に切替。

点火前チェックリストの到達: ①GATE値叩き台✅ ②GA4 custom定義✅ ③トムソン3項目(叩き台確定・通知物公開)✅ ④enrollment期間決定✅ ⑤段階A配置・pre-flight✅。残: ⑥段階B=点火(experiment_active=trueconfig_version=2で再アップロード)のみ。Youのgo待ち。

次回やること: 段階B点火(Youのgo時)。点火後 GA4 dimension にデータが乗るか Data API 検証。緊急停止は active=false+版+1 で即可能。

02:32 - 起動時広告A/B 点火準備(GATE値叩き台凍結/S3 config正本/runbook/GA4 custom定義 登録完了)

テーマ: BASE 実装完了後、点火に向けた準備物を整備。GATE値の叩き台を凍結し、S3 配置用 config と手順書を作り、GA4 の custom 定義を全件登録(管理画面操作は You が実施)。

やったこと:

  • 残りGATE値の叩き台を確定・凍結(commit ios 2bae96d / op 8b9fb4c):
    • client 実行時値 → ABProtocol: 許容clock skew=300s・未来clamp=400日・既定TTL=6h。showAdIfReady の時計判定を skew許容+未来clamp回復で精緻化。ABConfigLoader の ttl_seconds を任意化。
    • 解析側値 → 正典 measurement_protocol(mp_2026q3_r1): maturation=3日/円人コホート(締切42日・追跡28日・分母=割当全員ITT)/D7D14単日(JST)/residual(月別平均eCPM・感度0.5–1.5×・旧版売上比率上限30%/普及率≥70%)/収益正規化(gross/USD/対象版・月)/事前テスト合格しきい値①〜⑧/GA4 Cohort再現手順。要トムソン最終確認は3項目(コホート定義・residual/正規化・合格しきい値=事前登録として握るだけ、実装は既定値で動く)。
  • S3配置用 ab_test_config.json(正本)と ab_test_deploy_runbook.md(点火/緊急停止/版番号規則/GA4登録ワークシート/wire format)を新設(commit op b35bf2f)。config 初期値 experiment_active=false=配置しても誤発進しない。ABConfigLoader のパース+検証通過を standalone で確認。
  • MAX native の loader-ad lifecycle を修正(commit ios 8dc3b5a): load毎に差し替わる loader を ad とペアで retain(weak delegate で過去adのrender/impression/revenue callback欠損を解消)。cell は loader(for: ad) で生成元loaderを使う。destroyAd→Swift destroy(_:) 改名対応。フルビルド成功。
  • GA4 custom定義の登録ワークシートを実コードに整合(commit op 22720b0): 実送信 param キーと1対1照合。未送信の config_age_hours は登録不要と明記(spec §4 訂正)。
  • GA4 custom定義を全件登録完了(GA4 Editor権限 取得済→You が管理画面で実施・スクショで検証): カスタムディメンション18件(イベント範囲16+ユーザー範囲2=assigned_group(user)/app_open_ad_interval(user))+カスタム指標3件(time_to_content秒/revenue_usd標準/effective_interval_minutes分)。param名・スコープ・単位すべて一致=計21件 過不足なし。

気づき/判断:

  • GA4 custom定義の作成は接続中の ga4 MCP(Data API=読み取り専用)とADC未設定では自動実行不可。登録ワークシートを用意し You が手動登録、私は Data API で事後検証する分担に。
  • 「トムソン合意が要る」を再整理: 実ブロッカーは GA4 Editor 権限(取得済)だけ。しきい値・コホート定義は事前登録の作法上トムソンに通知/握るが、実装は叩き台既定値で動く。月次総売上は解析フェーズ入力で点火ブロッカーではない。
  • 検証用 seed/NSLog(#if DEBUG-ABSeedTestConfig 二重ガード)は実ビルドに混入せず S3 なしで funnel 再検証できる唯一の手段なので残す。

点火前チェックリストの到達: ①GATE値叩き台 ✅/②GA4 custom定義登録 ✅。残: ③トムソンへ要確認3項目を通知 ④enrollment期間を決め config に設定 ⑤ab_test_config.json を S3 配置→experiment_active=trueconfig_version+1 で点火。

次回やること: トムソンへの通知文面づくり/enrollment 期間の決定/S3 配置・点火。点火後 or seed DebugView で GA4 dimension にデータが乗るか Data API 検証。

01:03 - 起動時広告A/B 計測基盤 BASE A〜F をフル実装+シミュレータ検証+コミット

テーマ: 実装計画 rev6/正典 SPEC.md rev6 を元に、起動時広告 削減A/Bテストの計測基盤を実コードで一気に実装(BASE A〜F)。footballnext-ios(GitHub/master)に実装し、フルビルド成功・シミュレータ実機で全ファネル発火を確認・コミット/push まで完了。

やったこと:

  • 新規4ファイルfootballnext-ios/FootballNEXT2015/):
    • ABTest.swift(BASE-A/F core): 群割当 SHA256("experiment_id:install_uuid") 先頭8バイト big-endian % 100→bucket_map([0,50)→A)、永続install UUID(IDFA不使用)、experiment_id単位の原子保存・再計算禁止、enrollment追記、型付きABActivationConfigの全項目検証+last-known-good+canEnroll/config_status+fail-safe interval、JST暦日(cohortDay/daysSince)、verifyTestVectors()
    • ABLaunchTracker.swift(BASE-C): ForegroundCycle/TargetLaunchContext、write-ahead OpenContextRecord+terminal CAS、at-most-once送信(emission_state pending→attempted)、可視状態機械+content provider前面チェック、presentation watchdog、kill/crash時 previous_launch_unresolved(元属性で送信)。Foundation-only(emit注入式)で standalone テスト可能。
    • ABAdCollector.swift(BASE-E): 全MAX枠 共通 fn_ad_impression/fn_ad_revenue(冪等・ILRD一本化、experiment共通paramのみ)。
    • ABConfigLoader.swift(BASE-F): S3 ab_test_config.json の非同期取得→手動パース→検証付き applyConfig(fail-safe)。
  • 既存14ファイル配線: AppDelegate(lifecycle 4種・群別頻度制御の時計安全化(abs廃止/clock_anomaly)・app_open_ad_decision(BASE-D)・delegate/revenueDelegate配線)、UserData(didDisplay起点の永続キー)、ViewController(content前面provider)、SplashViewFadeoutAction(content可視)、EULA/RSS/Push/ATTの4モーダルVC(overlay covering)、AdView/MaxByAppLovinBannerAd/AppLovinUnderArticleBannerAd/MiddleAppLovinNativeAd(+Cell)(collector接続)。
  • pbxproj に4新規ファイルを手動登録(plutil -lint OK)。
  • 検証: ①ABTest+ABLaunchTracker を standalone swiftc でコンパイル+状態機械テスト全PASS(非参加/enroll+target_launch/content_reached/launch_no_content/kill→unresolved/at-most-once/CAS/割当ベクトル)。②フルビルド成功(iPhone 17 Pro sim・Debug)。③シミュレータ実機起動で experiment_enrolled→target_launch→app_open_ad_decision(not_ready)→content_reached の実発火をログ確認。
  • 正典 AB_TEST_MEASUREMENT_SPEC.md の未決GATEから「SHA256→bucket定数・テストベクトル・新experiment時の再無作為化」を確定へ移動(ABProtocolへ凍結)。
  • footballnext-ios にコミット 4ebdeb6(18ファイル/+1262 −63)→ GitHub master へ push。

気づき:

  • このプロジェクトは Swift の SWIFT_ACTIVE_COMPILATION_CONDITIONSDEBUG が未設定(GCC側のみ DEBUG=1)。→ Swift の #if DEBUG は通常 Debug ビルドでも全除外。検証用 seed/NSLog は #if DEBUG+起動引数 -ABSeedTestConfig の二重ガードで、通常ビルドには絶対混入しない(今回は SWIFT_ACTIVE_COMPILATION_CONDITIONS=DEBUG 上書きで検証)。副作用として verifyTestVectors() の起動時自己チェックも通常は走らない(standaloneで検証済みのため実害なし)。

本番影響: S3 に ab_test_config.json を置くまで全context非参加=AB event 不発・広告挙動は現行どおり(中立)。config 配置が実験開始トリガー。

次回やること: GATE-値(トムソン合意要の数値含む)を measurement_protocol として凍結 → ab_test_config.json 作成・S3配置 → GA4 custom定義登録 → native loader-ad lifecycle の深いrefactor(pre-test ③/⑦で検証)→ 検証用 seed/NSLog の最終扱い判断。


2026-06-18

23:55 - 起動時広告A/B 実装計画HTMLの作成+Codex 5ラウンドレビュー(gods-talk)→ rev6 で設計レビュー完了(〜06-19 00:00)

テーマ: トムソン承認が出た提案書を受けて、**実装計画(HTML)**を作成し、Codex(gods-talk)で繰り返しレビュー。rev1→rev6 まで反映し、正典 AB_TEST_MEASUREMENT_SPEC.md も同期。コード変更なし(資料・正典・レビュー往復のみ)。

やったこと:

  • 実装計画 起動時広告A-Bテスト_実装計画_20260618.html を新規作成。実際のiOS実装(AppDelegate.swift showAdIfReady()/広告delegate、UserData.setAppSettings() のS3配信、Firebase=GA4、永続UUID未実装)を読んで具体化。
  • gods-talk 新トピック ab-test-implementation-plan-review5ラウンドのレビューを往復(依頼書5本+feedback5本)。各ラウンドで指摘を rev に反映:
    • round1(high9): 可視判定をdidHide限定にしない状態機械化、appOpenAd.delegate/revenueDelegate 未配線、LaunchContext一意化、割当原子保存、fail-safe ITT保持、未確定検出、GA4 ad_impression衝突回避、円/人の固定コホート、混合eCPMの層別。
    • round2(high7): 正典が旧仕様のまま→同期、assignment/activation/enrollment 3分離、型付きconfig原子保存、ForegroundCycle/TargetLaunchの60秒gate、presentation pending、unresolvedを成熟後残差率に格下げ、総売上1個ではslot別点推定不可。
    • round3(high7): experiment_id同梱manifest互換規則、context単位config snapshot、背景化outcome確定、target_cohort_day、fallback param配線、収益範囲条件復活、全MAX枠ILRD。→「条件付きGO」。
    • round4(high4): write-ahead OpenContextRecord、3 manifest分割+版番号、canonical revenue event一本化、総売上母集団の演算式(感度変数化)。→「限定付きGO・追加ラウンド不要」。
    • round5(high1): 配送を at-least-once→at-most-once に方針転換(GA4-onlyでexactly-once不可・logEventにack無し)、per-event emission state、medium4件の内部矛盾同期。→「設計レビュー完了。実装PRの受入チェックで確認できる範囲」。
  • HTMLに 3レーンの実装ゲート(BASE着手可 / BASE契約=API確定前に潰す5点 / GATE-値=schema凍結前に固定)を新設。
  • 正典 .devnotes/AB_TEST_MEASUREMENT_SPEC.md を rev6 まで全面同期(write-ahead/at-most-once配送・3 artifact・canonical event・総売上母集団・enroll許可config/kill-switch・param出所分離・MAX native lifecycle・暦日TZ Asia/Tokyo)。

決めたこと(DECISIONS にも転記):

  • 設計レビューはここで完了。次は実装フェーズ(BASE着手+GATE値を measurement_protocol として凍結)。再レビューは必須にせず実装PRの受入チェックで確認。
  • 配送意味論は at-most-once(pending→attempted を先に確定して一度だけ送信、欠損は残差率に残す)。

気づき:

  • Codexはローカルコードを直接読むため、appOpenAd.delegate=self 未配線のような「コードを見ないと出ない」指摘が出た。提案書段階の机上設計では見えなかった穴。
  • 5ラウンドで指摘の質が「設計の穴→境界/原子性→配線漏れ→収束仕上げ」と逓減し、自然に収束した。

次回やること:

  • 実装着手(BASE)。GATE-値(skew/TTL/maturation日数/暦日具体値/residual単価根拠 等)を measurement_protocol として凍結してから本番 event schema を確定。

13:38 - 起動時広告A/B 提案書の作成・Codex2ラウンドレビュー反映・S3公開アップロード

テーマ: 10:32 の測定設計を、トムソンに出せる**提案書(HTML)**に仕上げる。議論で設計を精緻化し、別LLM(Codex)に2ラウンドのレビューを受けて反映、最後に公開アップロードまで。コード変更なし(資料・メモのみ)。

やったこと:

  • 提案書 起動時広告A-Bテスト_提案書_20260618.html を新規作成(既存 review-20260605.html と同じ体裁)。md版は破棄しHTMLのみ。
  • 議論で固めた設計の更新点(提案書に反映):
    • 群B「1日1回」→「24時間に1回」(実装のローリング挙動に一致)。
    • BigQuery不使用=GA4のみ・無料に決定。ab_group 集計のため GA4 を Viewer→Editor へ権限変更をトムソンに依頼、と明記。
    • 起動時広告の枠を実コードで棚卸し(フォーマット4種+ネイティブ生存5系統。Movie/HotKeyword/SnsMovie は休眠。全ネイティブは CoreTableView.dequeAd() 集約)。
    • 表示カウントは「起動時/それ以外」の2系統。ネット内訳は送らず generic +1。
  • Codex レビュー(gods-talk, .gods-talk/ab-test-proposal-review-*)2ラウンドを実施・反映:
    • R1: 2バケツeCPMの単価構成仮定/launch_bounce の交絡・起動秒数で群A機械的不利/GA4のみで有意差は出せない(→実務閾値判断に降格)/imp定義 等。
    • R2: 群Aは「毎回」でなく現行50分間隔(S3実測 app_open_ad_interval=50 確認)/3パターンに「判定保留」追加/content_readycontent_visible(広告の背後発火問題)/主因の因果主張を弱める/即停止表現を現実化/売上範囲を iOS・対象アプリ・対象月に限定/ITT明記/D7D14起点=実験参加日 等。
    • 指標名を launch_bounce →「コンテンツ未到達離脱率」に改称。
  • 提案書を thomsons S3バケットに公開アップロード(profile thomson-ik / ap-northeast-1 / object ACL public-read、既存HTMLと同方式)。秘密情報スキャン済み。URL: https://thomsons.s3.ap-northeast-1.amazonaws.com/起動時広告A-Bテスト_提案書_20260618.html
  • 計測仕様の正典 .devnotes/AB_TEST_MEASUREMENT_SPEC.md を新規作成(コンテンツ未到達離脱率の計測フル+確定前提+残りTODO)。

気づき/確定事実:

  • 本番S3 app_open_ad_interval = 50(群Aの基準は「毎回」ではない)。処置差は想定より小さく、期間・最大減収は事前テストで再試算が必要。
  • Firebase/GCP プロジェクトは footballnext-bca77(Owner=info@thomsons.jp、You は Editor)。GA4 property は Viewer のみ=カスタムディメンション登録不可。BQエクスポート有効化は Owner 権限が要り Sandbox(課金なし)でブロック → GA4のみ方針の裏付け。

次回やること:

  • .devnotes/AB_TEST_MEASUREMENT_SPEC.md の TODO(willDisplay計測・App Openコールバック・rolling24hのabsバグ・固定コホート・収益式・設定取得SLA 等)を埋める。
  • トムソンの提案レスポンス待ち → 承認後に実装着手。

10:32 - 起動時広告 削減A/Bの測定設計(トムソン提案に向けた机上検討)

テーマ: review-20260605.html(多角レビュー)を起点に、トムソン側との議論をどう前に進めるかを検討。起動時広告(App Open Ad、総売上の約25%=ヒヤリング値)を「どこまで減らせるか/減らすと得か損か」を実測で判断する50/50無作為A/Bの測定設計を、コードの現状確認込みで詰めた。コード変更はなし(設計のみ)。

前提(トムソンとのやりとりで判明):

  • トムソンは「記事内広告がウザがられてる」認識が強い。You は「起動時広告への不満も大きい」と主張。→ どちらが主因かは片面ずつ絞って実測すれば決着がつく。
  • トムソンは「起動時広告を減らして満足度を測れない」と渋る。→ You が「離脱率(行動)で測れる」と返した。これが今回の設計の出発点。
  • 起動時広告の売上 ≈ 総売上の25%(ヒヤリング済み)。全消しではなく「絞るダイヤル」として扱う方針。
  • You は一開発者(決定権はトムソン)だが仕様提案は可能。提案は「測定設計込みのパッケージ」にして通しやすくする。

コードの現状確認(footballnext-ios / FootballNEXT2015):

  • ①パターン確定: 起動時広告は AppDelegate.swift:241showAdIfReady() をアプリが明示的に呼ぶ(SDK自動表示ではない)→ if で囲むだけでA/B群分けが成立。
  • 時間間隔スロットルは既に実装済み: showAdIfReady()(:546)内に appOpenAdInterval(分)による表示間隔ガード+直近60s background復帰の抑制あり。0で実質毎回、1440で1日1回。
  • GA4イベント配線済み: app_open_ad_show {interval_minutes} / app_open_ad_suppress {reason: interval/recent_background/not_ready} / user property app_open_ad_interval
  • サーバ設定チャネルが存在: 起動時に S3 JSON(Const.baseS3Url/201811)→ app_settingUserData.setAppSettingsUserData.swift:184)で UserDefaults に流し込む作り。app_open_ad_interval はキー名整合あり=全ユーザー一律ならアプリ更新なしで間隔変更可能(ただし無作為A/Bではなく前後コホート比較になる)。「ハードコード」認識は半分だけ正しかった。
  • 広告構成はパッチワーク: 起動時広告・記事下バナー(AdView)は AppLovin MAX(MAAdRevenueDelegate.didPayRevenue で表示単位の実収益が取れる)。だが記事一覧の差し込みは自前ローテNativeAdManagerAdNativeRate で AdG[Supership AdGeneration]/Zucks/LINE系 default を切替)=MAX外の直SDK。impression本数が一番多い面がMAX外なので、MAXのdidPayRevenueだけでは収益の大半を捕捉できない
  • didPayRevenue の実装は空MiddleAppLovinNativeAd.swift:61 等)。ad.revenue を拾えば実額が取れるのに今は何も記録していない。

測定設計(合意した骨子):

  • 方式: 50/50 無作為A/B。バケットをアプリに仕込む(永続UUID→%2)。群A=現状(毎回)/群B=起動時1日1回。ab_group を GA4 user property+各イベントに付与。
  • 判断軸①「お金」: 土台は全SDK共通の impression カウント(AdG/Zucks/MAX全部、表示の瞬間は必ずコードを通るので自前で数えられる)。金額換算は2バケツの eCPM —「起動時(MAX実額)」と「それ以外(=月次総売上−起動時実額 を それ以外impで割った混合平均eCPM)」。各社ダッシュボードを漁らずトムソンの月次総売上1個で導出可能。
    • eCPMが割り算で出る理由=出るのは混合平均だが、A/Bで使うのは群間のimpression“差”の金額で、差の中身の構成比はローテ配分が群無関係ゆえ全体平均と一致 → 平均を掛けても近似で正しい(数値例で確認)。偏りが気になればネット別imp計測+ネット別月次売上で精密化。
    • didPayRevenue は「全部の実額が取れる」ではなく「MAXスライスの実額+月次の答え合わせ」に役割降格
  • 判断軸②「残り具合」: launch_bounce(起動Xs以内・コンテンツ0で終了)↓ / 週次セッション/人 ↑ / D7・D14継続 ↑。launch_bounce は「率」なので自然減(MAU −15%/年)に埋もれない=起動時広告への一番タイトな指標。
  • 判断ロジック(3パターン): 群Bの[お金/残り] が ①同じ以上/良化→即採用 ②微減/良化→残る分で回収するので採用 ③減/不変→効果なし戻す。最適 interval は多腕 or 階段で後追い。勝った値は S3 で全員展開(再リリース不要)。

仕込む実体(次セッション以降の実装対象):

  1. A/Bバケット割当(永続UUID→%2、ab_group 付与)
  2. launch_bounce イベント新設
  3. 全面・全ネット別の impression カウント(群タグ付き)
  4. didPayRevenue の空メソッドを埋める(MAX実額・補助)/起動時の出し分けは AppDelegate.swift:241if で囲む

未確認(次セッションで詰める):

  • S3 app_settingapp_open_ad_interval が現在入っているか/現在値(群Aの基準値の確定)。Const.baseS3Url 実体の中身確認。
  • AdG/Zucks 等の自前ローテ面に「表示された」確実なフック(didDisplay相当 or deque直後)があるか=全面impカウントの可否。
  • 月次総売上をトムソンからどの粒度でもらえるか(総額1個 or ネット別)。

次回: この設計をトムソンへの提案資料にまとめる(You が別セッションで継続予定)。


2026-06-13

00:39 - YouTube プレイヤーの WKWebView 化(割り込み対応・申請前の最終変更)

テーマ: 7channel 由来のナレッジ ios-wkwebview-youtube-inline-playback を footballnext-ios の動画詳細プレイヤーに適用。deprecated な UIWebView 実装(2015年製)を WKWebView 構成に置き換えた。

やったこと:

  • FootballNEXT2015/YoutubePlayerView.swift を全面書き換え(変更はこの1ファイルのみ)
    • UIWebView 継承 → WKWebView を内包する UIView(既存 SnsMoviePlayerView と同じプロジェクト内イディオム)。xib(素の <view> + customClass)と呼び出し側 load()/clear() API は互換のため他ファイル変更なし
    • インライン再生の必須2点セット: allowsInlineMediaPlayback = true + mediaTypesRequiringUserActionForPlayback = []
    • ナレッジ最大のハマりポイント対応: baseURL: nil をやめ、Const.baseURLStringhttps://fnext.thomsonsapp.com)を baseURL と embed URL の origin= で一致させる構成に
    • embed URL を http://https:// 化、enablejsapi=1 / allow="autoplay; encrypted-media; fullscreen" / allowfullscreen 追加
    • target="_blank" リンク無反応対策として WKUIDelegate.createWebViewWith で同一 WebView ロード
    • 自動再生をやめたautoplay=1autoplay=0、ユーザー指示)。タップで再生開始に変更
  • arm64 シミュレータビルドで BUILD SUCCEEDED を確認(DEVELOPER_DIR に Xcode 26.5 指定)
  • footballnext-ios へ選択コミットa94aa84、YoutubePlayerView.swift のみ)→ origin/master へ push 済み。別件の未コミット変更(push通知系・Podfile 等)は混ぜずに残置

詰まったこと / 気づき:

  • baseURLStringApp ではなく Const のメンバー(どちらも App.swift 内に定義があり紛らわしい)。最初 App.baseURLString と書いてコンパイルエラー
  • ナレッジ第4項(埋め込み不可動画のフォールバック、サーバー側 status.embeddable フラグ)はサーバー改修を伴うため今回は見送り

次回やること:

  • You がこの変更を含めて App Store 申請(5.5.1 アーカイブ)
  • 実機での動画詳細画面の再生確認(ビルド確認のみ実施済み)

2026-06-06

17:32 - 定期実行の一元化 + Lambda 大掃除(今後のアプデ前の足場固め)

テーマ: 定期実行の呼び出し元が「cron 専用 EC2」と「AWS Lambda(EventBridge)」に二分されている状態を整理。cron 側へ寄せつつ、稼働実態のない Lambda を一掃した(AWSアカウント thomson-ik / 865107862340 / ap-northeast-1)。

やったこと:

  • TFN_cron(毎分の curl ラッパー Lambda)を cron 専用サーバーへ移管
    • 中身は fnext.thomsonsapp.com の8エンドポイントを叩くだけ(Baseball は既に cron 上で同等処理を実行済み)。
    • 移管手順: 8エンドポイントの200疎通確認(cronサーバーから)→ 実 crontab に8行追加(sleepでずらし配置、FootballNEXTセクションの movieUseSqs 直後)→ cronログで全8ジョブの起動確認(07:36:02)→ EventBridge ルール TFN_cron を disable。
    • サーバー上に ~/crontab_backup_20260606-073553.txt を退避。後日 Lambda 本体+ルールも削除済み。
  • 死んだ/不要な Lambda を段階的に削除(コードは毎回 /tmp にバックアップ後に実行)
    • TFN_cronUpdateOldCurrentData(2016 current データ用・起動0・トリガー無し)
    • TSP_* 5関数(TSP_cron/TSP_crawlArticle/TSP_cron10/TSP_uploadToS3OfIchimens/TSP_sendScheduledNews)+ 対応 EventBridge ルール4本。叩く先 spp.thomsonsapp.com(The Sports Paper)が HTTP 000=死亡を確認。
    • sports-paper-* 4関数 + API Gateway sports-paper(c2770nol99) + テスト残骸 hello-dev-hello/testDetach/lbTesttest-rule
    • 孤児 EventBridge ルール Per10Minutes / sposhin-rss-crawler-notice-crawl-article(2019 temp.json 指し)。
    • 残存関数の稼働精査(7日/90日 invocation、APIキャッシュ無効も確認=「0回」は本物)→ 完全ゼロの19関数 + 捨て API Gateway 4本(prod-test-crawler / prod-rss-crawler / custom-runtime-php71-demo2 / rss-clawert)を削除。本番 football/baseball の API GW(7isghft565 / a0s3exiz21)と rss-crawler-notice API は現役なので残置。
    • 結果: Lambda 42関数 → 9関数(残9は全て現役か保守ツール)。
  • teiki / article エンドポイントの呼び出し経路を解析
    • footballnext-teiki-notification-articles(24h 897 / 7d 7,095 / 30d 29,322・エラー0・平均162ms)と footballnext-article(24h 126 / 7d 547)は、いずれも API Gateway(7isghft565) → Lambda(DynamoDB READキャッシュ) → MISS時のみ http://fnext.thomsonsapp.com/api4/...(CakePHP Api4Controller) の三段構成。
    • 呼び出し元はアプリ本体(iOS ArticleViewController/TeikiArticleSuiteTableView、Android ArticleDetailActivity/GogaiListViewModel/TeikiNotificationSuiteModel)。teiki は「定期プッシュ通知タップ → 記事一覧」の導線。
    • アプリは既に大半のAPIを https://fnext.thomsonsapp.com に直叩きしており、execute-api 経由は /article/{id}/footballnext-teiki-notification-articles/{id} の2つ(iOSは加えて /oshirase-v2/{id})の飛び地のみ。原点は HTTPS 200・iOS は ATS 無効なので、直叩き移行の障壁は無し。

詰まったこと / 気づき:

  • CloudWatch GetMetricStatistics の「30日を1バケット集計」は高頻度関数で過少値を返す(rss-crawler が 24h 4320 なのに 30d 10 等)。7日を日次バケットで合算する方式に切替えて正確化。
  • API Gateway の delete-rest-api は連続実行でレート制限(429 / 約30秒に1回)。バックオフ再試行で対応。
  • 削除した footballnext-oshirase-v2(90日invocation 0)を iOS OshiraseViewController.swift:30 がまだ参照していることを後から発見。90日ゼロ=到達不能のデッド画面と判断し、復元せず据え置き(次回アプリ改修でコードごと整理)。

次回やること:

  • API Gateway 飛び地(/article/{id}/footballnext-teiki-notification-articles/{id})を https://fnext.thomsonsapp.com/api4/... 直叩きへ移行 → 最終的に footballnext API Gateway を撤去。論点は Dynamo READキャッシュの代替(必要なら CakePHP 側キャッシュ)。
  • iOS OshiraseViewController のデッドコード整理(削除済み footballnext-oshirase-v2 参照の除去)。
  • rss-crawler の cron 一元化(RSS発見ロジックのサーバ側エンドポイント化)は引き続き宿題。

2026-06-05

19:50 - large-scale-service-review の試運転(Football NEXT 多角レビュー)+ GA4 接続 + Codex UXオーディット

やったこと:

  • large-scale-service-review スキルを Football NEXT で試運転。複数ソースを集めて多角レビューを実施し、結果を review-20260605.html(HTMLダッシュボード、チャート付き)に出力。
  • データソースを6つ集約して三角測量: ①広告売上推移 FootballNext売上推移.html(13年分、Youが提供)②全レビュー3,987件 ..._AppStore_20260605.json(Youが提供)③ASCレビュー/売上 ④GA4行動データ ⑤GA4コホート ⑥Codexによる広告実装の静的解析。
  • GA4 を接続: Football NEXT は Firebase Analytics 実装済→ GA4 property 211701453(footballnext-bca77) を特定。Google公式 google-analytics-mcp(pipx) を claude mcp add ga4 -s user で登録(ADC流用・property固定)。MAU/セッション/コホート/クラッシュ等を取得。
  • Codex の Build iOS Apps に UXオーディットを依頼(指示書 codex-ios-ux-audit_指示書.md)。実機起動は OneSignal 2.12.0 の arm64 simulator 非対応で失敗したが、静的コード解析で広告挿入ロジックを file:line 付きで特定(findings codex-ios-ux-findings.md)。
  • スキル本体を改善~/.claude/skills/large-scale-service-review/SKILL.md): 収益モデル先行確定・ローカル成果物の能動スキャン・複数ソース三角測量・カバレッジ可視化を追加。

主要な発見(レビュー結論):

  • 広告ネットワーク売上は 2019ピーク¥17.5M → 2025 ¥5.4M(−69%)、下落が加速(2023 −23%/2024 −29%/2025 −34%)。直近月¥332k。
  • 全レビュー分析: 平均星が2022→2023で崩落(3.0→1.6→1.33)、「広告」言及率 20%→93%。売上下落と時系列一致=広告過多によるデススパイラル
  • GA4: MAU 26,076→17,609(約2年−32%)、セッション−41%。クラッシュ影響は1%未満で安定性は無問題。94%iOS・ほぼ100%日本。エンゲージ率90%・月35セッションでロイヤル層はヘビー。
  • Codex: App Open Ad のスロットル(頻度ゲート)がコメントアウトされている=広告抑制が外された物証。一覧10件中2枠・コメント5件ごと等の密度も判明。

気づき:

  • 広告型アプリは ASC の proceeds=0 が正常で、実収益はメディエーション/ローカル集計にある。最初に収益モデルを確定しないとデータの在処を外す。
  • AdMob/MAX は別管理者で面別収益は取得不可。効果検証はGA4の離脱シグナルで代替する方針。

次回やること:

  • 改善実装の着手判断(最優先=App Open Ad のスロットル復活)。
  • 任意: OneSignal を監査用ビルドでスタブ化して Codex に実機UX再検証させる。

2026-06-03

17:11 - 試合詳細に「得点者」表示を追加(iOS 5.5.1 + サーバ/フルスタック機能追加)

やったこと

  • サーバ(footballnext-server):
    • API-Football v3 の単一試合レスポンスに events(得点・カード・交代)が同梱されることを実APIで確認 → 追加の RapidAPI コール無しで得点者が取れる。CronController::ovserveFixtures の更新に events_json 保存を追加し、取得&更新の本体を _updateFixtureFromApi($id) に共通化。
    • 終了試合も強制更新できる updateFixture($id) を新設/cron/updateFixture/{id}、Match Finished スキップ無し)=過去試合のバックフィル手段。
    • V2Controller::fixture()(iOS が叩く /v2/fixture/{id} の実体)に scorers 整形を追加:type=Goal 抽出、team.id で home/away 判定、OG は反転せず素直にマップ、PK/OG 種別、アシスト。当初 V1Controller を誤編集→スモークテストで気づき V2 へ移して V1 は撤回。
    • AfterInstall.sh にモデルスキーマキャッシュ自動クリア(rm -f app/tmp/cache/models/*)を追加。
    • DB(master)の fnlive_fixturesevents_json TEXT NULL を追加(ALTER)。
    • デプロイ後、/v2/fixture で scorers キー・500なし、/cron/ovserveFixtures 200無エラーを確認。直近10試合を updateFixture でバックフィル(全件 scorers=得点数 一致)
  • iOS(footballnext-ios / 5.5.1):
    • LiveFixturescorers(表示用 computed property 付き)を追加。
    • 得点者カードを LiveFixtureScorersView(記事テーブルの tableHeaderView・手動レイアウト)で実装:タイトル外出し+白い角丸カード、ホーム左/アウェイ右、live_ball 画像インライン、PK/OG、アシストはボール幅インデント、行間調整。
    • バージョン 5.5.0→5.5.1、MAX Mediation Debugger OFF を同梱。コミット a3793be・push・タグ 5.5.1(規則 v…_byXcode… とは別の素タグ、ユーザー指定)。トムソン確認OK。

詰まったこと / 気づき

  • エンドポイント取り違え: /v2/fixture は V1Controller ではなく V2Controller。スモークテスト(scorers キー欠落)で発覚。手を入れる前に「実際に叩かれるルート」を確認すべし。
  • CakePHP 2.x スキーマキャッシュ: カラム追加後、app/tmp/cache/models/ を消さないと save() が新カラムを黙って捨てる(エラーも出ない)→ AfterInstall で自動クリア。
  • レイアウト試行錯誤: スコアボード用テーブルは storyboard で高さ固定 184・自己サイズ非対応。section 追加や estimatedRowHeight=0 で見切れ・崩壊。最終的にスコアボード側は触らず、記事テーブルのヘッダーに手動レイアウトで載せて解決。
  • OG の team 帰属: API-Football は OG を「得点が入る側(受益チーム)」に既に割り当てている(ブラジル6-2パナマで実検証、home6/away2 がスコアと一致)→ 反転不要。
  • 起動経路: ovserveFixtures はリポジトリ内から呼ばれず、cron 専用サーバの */2 * * * * curl http://fnext.thomsonsapp.com/cron/ovserveFixtures(公開URL→web サーバで実行)で回っていた。

次回やること

  • iOS 5.5.1 を Xcode アーカイブ → App Store 提出。
  • 任意: 方針転換で残った未使用 LiveFixtureScorersCell.xib + 空セルの掃除。

2026-05-30

20:00 - footballnext-ios の近代化(広告SDK全面更新・Zucks/Moloco追加・GMA13移行・GitHubリポジトリ正常化)

やったこと

  • Xcode 26.5 でビルド不可を解消: Reachability pod 3.7.6→3.7.7(pod update Reachability --no-repo-update)。新SDKで private 扱いになった #import <netinet6/in6.h> が 3.7.7 で除去済みのため解決。Ads-Global のスクリプト失敗はビルド中断の巻き添えで、Reachability 修正後に消えた。
  • Zucks ネイティブ広告セルを追加: 記事枠(ZucksNativeAd/ZucksNativeAdCell+xib)とミドル枠(MiddleZucks*)を既存パターン(CoreNativeAd 派生ローダー → NativeAdRow → セル)に倣って実装。AdNativeType.zucks(=15) / AdMiddleNativeType.zucks(=7) 追加、各 NativeAdManager と CoreRow に配線。frameId は記事 _f7758a97de / ミドル _02f98424fe
  • Moloco を MAX に追加: Podfile に AppLovinMediationMolocoAdapter(→4.6.1.1、MolocoSDKiOS 4.6.1)。アプリ側コード不要(MAX が自動ロード)。Moloco の SKAdNetwork ID は既存で網羅済みだった。
  • 広告SDK/アダプタ一斉最新化(MAX Mediation Debugger 指摘分)をリスク順(低→中→高)に段階更新:
    • AppLovin 13.6.2 / Mintegral 8.1.4 / Pangle 8.1.0.6 / Amazon 5.6.2 / Line(FiveAd) 3.0.1 / Google Mobile Ads 13.3 / InMobi 11.3。
    • FiveAd 2→3 はメジャーだが旧APIが deprecated 維持+警告非エラー設定でコード変更不要。pod 'FiveAd' が直書きロックされていたため pod update AppLovinMediationLineAdapter FiveAd で同時更新。
    • GMA 12→13 は最小 iOS 13 必須のため iOS12→13 化(プロジェクト/Pods/post_install を 13.0 統一)。GADNativeAdNativeAd(NS_SWIFT_NAME 化)で CoreRow の 3 箇所を修正。削除バナー関数等は能動使用なしで API 破壊ほぼ無し。
    • GMA は 13.3 止まりGoogleMobileAdsMediationInMobi 11.3.0.0GMA ~> 13.3.0 を要求。13.4 は Google 製 InMobi アダプタの追従待ち)。InMobi の AppDelegate 使用(IMSdk init/setLogLevel)は 11.3 でも互換。
  • Liftoff(Vungle): アダプタ 7.6.2.0→7.7.3.0(VungleAds 7.7.3)。「SKAdNetwork Incomplete」は公式XML(155件)と差分し不足2件(apzhy3va96/ln5gz23vtd)を Info.plist に補完。
  • アーカイブ時の「Upload Symbols Failed」警告(AppLovin/DTBiOS/InMobi/Moloco の dSYM 無し)は、ベンダーが dSYM 非同梱のため無害・除去不可と判断(クラッシュ症号化のみ影響、配信に支障なし)。

重大な気づき / 是正

  • operation 配下の footballnext-ios が「GitHub移管前の CodeCommit クローン」だった。GitHub 側は移管時に履歴を書き換えて>50MBバイナリを除去し Pods/ を .gitignore 済みなのに、ローカルは remote=CodeCommit のまま・Pods を丸ごとコミットする旧運用。push しようとして発覚(CodeCommit 認証切れ)。
  • 是正: GitHub ichirokisanuki/footballnext-ios(master) を新規clone → 今回のソース変更のみ(Pods 除外、17ファイル)をパッチ移植 → pod install → push(28f5fe7)。GitHub クローンを operation 配下に再配置し、旧 CodeCommit クローンは ~/Downloads/footballnext-ios-codecommit-backup へ退避。
  • 今回 Pangle バイナリが 124MB=GitHub の 100MB 制限超で、Pods は載せられない/載せない設計の妥当性を再確認。

次回やること

  • 旧 CodeCommit バックアップ(~/Downloads/footballnext-ios-codecommit-backup)の削除(新環境のビルド確認済み)。
  • 実機で MAX Mediation Debugger 再確認(Liftoff が Completed になるか、各SDKが LATEST 表示か)。
  • xcuserstate の追跡解除(.gitignore 済みなのに過去コミットで追跡され続けている)。

2026-05-21

19:33 - footballnext-android の 16KB 対応リリース対応(全面近代化)

やったこと

  • 背景: Google Play から「16KBメモリページサイズ非対応」の指摘(対応期限 2026-05-31)。footballnext-android の前回リリースは2021年10月。
  • 16KB問題の診断: アプリ自体にネイティブコードは無く、.so を持ち込むのは Realm のみと特定。librealm-jni.so が唯一のネイティブlib。Realm Java 10.19.0 が「16KB ELF packaging」対応版(Realm Java は EOL、これが事実上の最終対応版)。
  • ツールチェーン全面近代化: Gradle 6.7.1→8.9、AGP 4.2.0→8.7.3、Kotlin 1.3.50→2.1.0、JDK 8→17+(Android Studio同梱JBR 21使用)、jcenter 除去。compileSdk/targetSdk 30→35、minSdk 19→24。
  • Realm 7.0.8→10.19.0(16KB対応の本丸)。
  • ViewBinding 全面移行: kotlin-android-extensions 廃止に伴い、synthetic を使う43ファイルを ViewBinding 化。
  • 依存刷新: OneSignal 3→5(5.1.38固定)、AdMob 19→23、okhttp 3→4、AndroidX各種、Firebase BoM。
  • 撤去・差し替え: nend SDK 撤去(サービス終了済み)、旧 YouTubeAndroidPlayerApi.jar(提供終了)を android-youtube-player に差し替え。
  • 署名鍵の確定: Dropbox の footballNEXT_key2.jks を keytool 検証。端末上の現行版アプリの証明書 SHA-256 と完全一致 → 正しい署名鍵と確定(Play App Signing 未使用、この鍵で直接署名)。keystore.properties 方式で署名設定を整備。
  • 実機テスト(Huawei YAL-L21 / Android 10)でクラッシュ4件を発見・修正:
    1. Realm 10 の UIスレッド書き込み禁止 → allowWritesOnUiThread(true)
    2. SplashActivity の DialogFragment.show() が onSaveInstanceState 後に落ちる → state ガード
    3. クリック連打防止の窓が3500ms と長く、記事一覧のタップが1回で効かない(既存バグ)→ 600ms
    4. SiteListFragment / TvProgramListFragment がコンストラクタ引数を持ち、復元時に InstantiationException(既存バグ)→ 引数なし+arguments方式
  • 署名済みリリースAAB(versionCode 17 / versionName 3.0.7)生成。16KBアラインメント検証OK(全4ABIで .so LOADセグメント align=16KB、APK/AAB両方で zipalign -P16 整合)。
  • footballnext-android リポジトリに7コミット。

気づき

  • 16KB対応そのものは Realm のバージョン更新のみ。だが「新規リリースを出す」には4年分の近代化が芋づる式に必要だった(jcenter閉鎖で旧構成はビルド不能、AGP8がGradle/Kotlin/JDK更新を強制)。
  • OneSignal 5.8+ は OpenTelemetry/okhttp5/wire を引き込み kotlin-stdlib を2.x系へ強制昇格させる → 5.1.x系最新(5.1.38)に固定して回避。
  • クラッシュ2件(連打防止・コンストラクタ引数Fragment)は2021年版から潜在していた既存バグ。モダンな Fragment ライブラリで顕在化した。

次回やること

  • You: footballnext-android/app/build/outputs/bundle/release/app-release.aab を Play Console にアップロード(内部テスト→製品版推奨)。
  • 動画/コメント/設定画面の手動確認、OneSignalプッシュの実配信テスト、ADG/i-mobileネイティブ広告の実機表示確認。
  • footballnext-android の7コミットを origin へ push するか判断。

2026-05-19

09:33 - エラーログスパイクアラートの原因特定(cake_sessions Duplicate entry)

やったこと

  • 直近のエラーログスパイクアラートメール 3 件(5/16 16:57 / 5/17 07:48 / 5/19 04:51)の原因調査
  • 各時刻の RDS メトリクス確認 → CPU・接続数・IOPS とも正常範囲、5/17 のみ FreeableMemory min 74MB とやや低めだが他は通常
  • CloudWatch Alarms 履歴を辿ってアラート源を特定:
    • footballnext-apache-error-log-all-spike(LogMetrics 名前空間、Apache error log のスパイク監視)
    • 3 件とも同じアラームが ALARM 遷移していた
  • メトリクスフィルタからロググループ FootballNEXT/Apache/error_log を特定
  • 各時刻 ±5 分の Apache error_log を取得 → 3 件とも完全に同じパターン:
    • PHP Fatal: PDOException SQLSTATE[23000] Integrity constraint violation: 1062 Duplicate entry 'xxx' for key 'cake_sessions.PRIMARY'
    • 短時間に数十件発生 → アラート発火
  • referer 解析: すべて fnext.thomsonsapp.com/<不審パス> から
    • /dwcc/, /lottery/, /api.php/, /Public/, /qiantaizhuanyon_chaxun/, /iexchange/webtrader/, /melody/, /appx/, /syn/, /djacgkjs/ ほか
    • 中国語パス・賭博/抽選/取引系・admin パネル探索・PHP scanner の典型パターン
  • ユーザー確認: fnext.thomsonsapp.com本番 API ドメイン(捨てるわけにはいかない)
  • 構造の理解: bot が API ドメインに対してパススキャンを並列で打ってきて、同一セッション Cookie を使い回すので CakePHP の DatabaseSession::write()INSERT INTO cake_sessions が race condition で衝突 → Fatal

決めたこと

  • 緊急性なしと判断しいったん放置。攻撃トラフィック由来でデータ実害なし・RDS への影響も軽微、ただし将来の対応候補として頭の片隅に置く
  • 対応するなら候補:
    • A. ALB or Apache で攻撃パス拒否(短期、即効)
    • B. CakePHP Session 保存先を File or Redis に変更(恒久、副次効果大)
    • C. cake_sessions 書き込みを try-catch で握り潰し(症状対症療法)
    • D. CloudWatch アラームしきい値緩和
    • E. AWS WAF / Cloudflare 導入

詰まったこと / 気づき

  • 「エラーログのスパイク」と聞くと RDS や Web サーバの本格的な異常を疑いがちだが、実態は bot 攻撃由来の Fatal 連発だった
  • fnext.thomsonsapp.com というドメインを当初「正体不明の第三者ドメイン」と勘違い。本番 API ドメインを user が運用してることを最初に確認すべきだった(聞いてしまえば 30 秒だが、聞かないと方向を間違える)
  • CakePHP の DatabaseSession は race condition に弱い設計(INSERT を素直に投げる、ON DUPLICATE KEY UPDATE 等を使わない)。普通のサービスでも稀に Duplicate が発生する可能性

次回やること

  • もし cake_sessions Fatal が頻発するようなら ROADMAP の「いつか」項目を「今月」「今四半期」に格上げする
  • baseballnext 統合後の RDS 状況は引き続き問題なし(5/11 dev-timer #15 経過、本日 5/19 朝の確認でも安定)

2026-05-09

17:45 - dev-timer #14 経由で Step 2 効果検証 / baseballnext 統合方針

やったこと

  • dev-timer #14(5/9 16:00 設定)の通知を受けて作業再開
  • articles インデックス DROP(5/8 02:54 実施分)の Before/After を CloudWatch で比較
    • FreeableMemory: min 55 → 63 MB / avg 137 → 133 MB(ほぼ変化なし、想定通り)
    • CPU avg: 5.4 → 4.9% / max: 60.5 → 58.6%(軽微な改善)
    • ReadIOPS avg: 5.9 → 2.3(-61%) / max: 2193 → 649(-70%) ← 本質的な効果
    • スロークエリ・体感異常なし
  • Step 3 (buffer_pool 1024→768MB 縮小) は baseballnext 統合計画の存在により 保留確定

決めたこと

  • DROP した 4 個(article_site_id / image_width / title / del)は実際に冗長で、本番影響ゼロ。Step 2 成功判定
  • buffer_pool 縮小は baseballnext 統合シナリオ確定後に再評価。今は触らない
  • baseballnext 統合作業は baseballnext-operation プロジェクト側で進行(footballnext-operation はサーバー受け入れ準備状態を維持)

詰まったこと / 気づき

  • 「ReadIOPS が大幅に減る」のはインデックス整理の主効果として理解しておく価値あり。FreeableMemory 改善を期待すると外す
  • gp2 100 IOPS ベースの環境では IOPS 削減はメモリ削減と同じくらい価値がある
  • baseballnext 統合計画の存在で「buffer_pool 縮小」が筋が悪くなったように、設計判断は前提条件次第で逆転する(メモリ縮減 vs 統合余地確保が対立する)

次回やること

  • baseballnext 統合作業の進捗待ち(baseballnext-operation 側)
  • 統合作業中・直後の footballnext RDS のメトリクス観察(FreeableMemory・CPU・IOPS)
  • 統合後にあらためて Step 3 の判断(buffer_pool 1024MB 維持 / 縮小 / medium 昇格)

2026-05-08

02:54 - articles インデックス Step 1+2 一気実行 / impressions 廃止 / baseballnext 統合計画把握

やったこと

  • articles インデックス整理 Step 1 (INVISIBLE) を実施
    • A 群 4 個(article_site_id / image_width / title / del)を ALTER INDEX ... INVISIBLE で隠す
    • 即時切替・即時リバート可能の保険フェーズ
  • 通常は INVISIBLE で 1〜数日観察の流れだが、A 群が冗長 prefix 確定で戻す可能性が極めて低いため 観察期間をスキップして即 Step 2 (DROP INDEX) に進む判断
    • ALTER TABLE articles DROP INDEX ... ALGORITHM=INPLACE, LOCK=NONE で 4 個を 1 文で物理削除(実時間 0.27 秒)
    • 結果: index 499 → 369 MB(-130MB / -26%)、インデックス数 22 → 18
  • impressions テーブルの利用調査
    • INSERT は V1Controller.php:296 1 箇所のみ(他はコメントアウト済み)
    • 読み取りは管理画面 PortalController の集計用 2 箇所のみ(エンドユーザー API からは未参照)
    • ユーザーが INSERT をコメントアウトしてデプロイ → 自分で TRUNCATE 実行
    • 結果: 6,982,221 行 / 730 MB → 0 / 0.03 MB(約 730 MB 解放
  • DB 全体: 5,006 → 4,145 MB(開始時 9,102 MB から -54%
  • baseballnext を footballnext RDS に統合する計画があることをユーザーから共有
    • baseballnext RDS(db.t4g.micro / MySQL 8.4.8 / 20GB / Single-AZ)の実態調査
      • 合計 2.24 GB / 90 テーブル
      • articles 632 MB(5/8 08:43 更新 = 現役稼働)、old_articles 522 MB、sns_movie_tweets 472 MB、movies 146 MB ほか
      • SELECT 5.8/秒、INSERT 0.73/秒、UPDATE 0.24/秒、buffer_pool ヒット率 99.775%
      • 「アイドル」ではなく現役稼働中のサービス
    • 統合シナリオ B(t4g.small 維持)で約 +$15/月削減、ただしホット working set が 1.25-1.5GB 規模になりヒット率は要観察
    • C(medium 昇格セット)も選択肢、コスト的には現状(micro+small)と大差なし
  • セッション内で起きた誤認の訂正
    • hot_keyword_rankings の古いデータ削除を「buffer_pool 効率↑」と説明したのは雑だった
    • 正しくは「読まれていないデータは buffer_pool に乗らない → 削除しても FreeableMemory は変わらない」。ストレージ(FreeStorageSpace)には効くが、メモリには効かない
    • 同様に old_articles / impressions の TRUNCATE もメモリには効かなかった(実測でも変化なし)

決めたこと

  • articles インデックス A 群は INVISIBLE 観察期間をスキップして即 DROP(戻す可能性 ≒ 0、CREATE INDEX で再構築可能)
  • impressions の機能を廃止(INSERT 停止 + 全データ廃棄)
  • Step 3 (buffer_pool 1024→768MB 縮小) は 保留。baseballnext 統合計画と相性が悪い
  • baseballnext 統合の実作業は baseballnext-operation プロジェクト側で進める(こちら footballnext-operation は受け入れ側として現状維持)
  • メモリに「直接効く / 間接的に効く / 効かない」の切り分けを今後は混同しない

詰まったこと / 気づき

  • 「アイドルに見えた baseballnext が実は現役」だったように、CloudWatch DatabaseConnections avg だけで稼働判断するのは危険(短時間接続はサンプリングで漏れる)
  • インデックス整理は「メモリには効かないが ReadIOPS には大きく効く」の代表例
  • 設計判断は前提条件(baseballnext 統合の有無)で容易に反転する

次回やること

  • 5/9 16:00 dev-timer #14 で Step 2 効果検証
  • baseballnext 統合計画の進捗(baseballnext-operation 側で進行)と footballnext RDS への影響観察
  • 統合後にあらためて buffer_pool / インスタンスサイズの判断

02:24 - articles OPTIMIZE 結果検証 / hot_keyword_rankings 調査 / メモリスパイク切り分け

やったこと

  • 深夜にユーザーが phpMyAdmin で実行した OPTIMIZE TABLE articles の結果検証
    • data 812 → 263 MB(-549 MB / -68%)
    • index 2010 → 499 MB(-1511 MB / -75%)← インデックス側のページ充填率がかなり悪かった
    • total 2822 → 762 MB(-2060 MB / -73%)
    • data_free 1101 → 7 MB(断片化解消)
    • avg_row_bytes 1448 → 426(妥当な値に戻った)
  • DB 全体 9102 → 5006 MB(-4096 MB / -45%、old_articles 空化分と合算)
  • CloudWatch FreeStorageSpace: 5.86 GB → 8.53 GB(+2.66 GB)
    • テーブル削減 4GB に対して free 増分が小さいのは InnoDB の共有テーブルスペースや一時ファイルが OS に即返却されないため
  • FreeableMemory はほぼ変わらず(OPTIMIZE 後も avg 110-126 MB)
    • buffer_pool ヒット率 99.996% で working set が小さい以上、テーブルが縮んでもメモリ消費は同じ
  • hot_keyword_rankings テーブルの利用調査
    • 7.4 年分(最古 2018-11-25 / 最新 2026-04-09)、全 9,153,398 行のうち直近 1 ヶ月の追加が 495 行のみ(事実上書き込み停止)
    • dir = YYYYMMDD_HHMM フォーマット(10分ごとのスナップショット)、distinct 386,983 件
    • コード参照は すべて「最新 HotKeywordHistory.dir に紐づく rank top2」のみ(V1Controller / S3UploaderController / CronController / CurrentDataComponent / Portal)
    • 過去データは完全に死蔵
  • 19-20 時の FreeableMemory スパイク(min 55MB)の原因切り分け
    • 過去 7 日同時間帯の min は 119-151 MB(特異なパターンなし)
    • 5/7 19:20 のみ 1 分ピンポイントで 148 → 55 MB に急落
    • 同時刻に流していたクエリ: articles + old_articles への AVG(LENGTH(body)) などフルスキャン集計 ×2
    • 結論: 自分の調査クエリが buffer_pool を瞬間的に奪った人為的スパイク。cron 由来の定常パターンではない

決めたこと

  • hot_keyword_rankings の古いデータ整理は判断保留(実行は別途)
  • 19-20 時スパイクは「人為的・cron 由来ではない」と確定。深追いせずクローズ
  • メモリ問題の本質は構造的(db.t4g.small 2GB に MySQL 8.4 + 9GB データ)であり、根本対策はインスタンス昇格に帰着するという再確認

詰まったこと / 気づき

  • 本番 RDS への AVG(LENGTH(...)) FROM 大きいテーブル は瞬間的にメモリを圧迫する。今後は EXPLAIN で I/O 規模を確認するか、リードレプリカ復活後にそちらで実行する
  • OPTIMIZE TABLE は data_free(断片化)だけでなく インデックス側の充填率改善も大きい。articles の index 75% 削減は予想以上だった
  • ストレージ削減は FreeableMemory には直接効かない(buffer_pool ヒット率がもともと高い場合)

次回やること

  • インスタンス昇格 db.t4g.small → medium の段取りを詰める
  • CloudWatch アラーム新設(FreeableMemory < 100MB)
  • articles のインデックス整理は OPTIMIZE で index も縮んだため優先度を下げる(追加効果は数百MB 程度)
  • hot_keyword_rankings のデータ整理(実行可否は別途判断)

2026-05-07

21:47 - RDS (footballnext) FreeableMemory カイゼン調査

やったこと

  • .gitignore にサブプロジェクト3つ (footballnext-ios/, footballnext-android/, footballnext-server/) を追加。3者はそれぞれ独立した Git リポジトリで管理されているため、本リポは指揮塔ポジションとして除外する形にした
  • AWS CLI で RDS の現状把握
    • インスタンス: db.t4g.small / MySQL 8.4.8 / gp2 20GB / Single-AZ / リードレプリカなし
    • パラメータグループ: general-mysql84 (custom)、ステータスが pending-reboot
    • SG: sg-083bace7c7ee9e450 で 3306/tcp が 0.0.0.0/0 開放(PubliclyAccessible=False で実害なしだが要対応)
  • CloudWatch で FreeableMemory 7日間の数値把握
    • Min 55 MB / Avg 141 MB / Max 173 MB ← 黄信号〜赤信号ゾーン
  • パラメータグループの中身を確認し、performance_schema = 1 が user-set かつ pending-reboot 状態で残っていることを発見
    • エンジンデフォルトは 0、過去7日間に再起動イベントなし → 実効値は 0 のまま
    • このまま再起動すると Performance Schema が起動して 200〜400MB 追加で食う「不発弾」状態
  • reset-db-parameter-groupperformance_schema を engine-default に戻した(即実行)
  • EC2 (cron インスタンス、52.194.87.168) 経由で RDS に SSH トンネル張って実調査
    • footballnext ユーザーから見える DB は footballnext 1個のみ、9.1 GB
    • database.php に書かれている kowai / search_article は実体なし
    • 大きいテーブル: articles 2.8GB、old_articles 2.0GB、hot_keyword_rankings 1.2GB
    • buffer_pool ヒット率 99.996%(read_requests 152億 vs disk reads 60万)→ working set < 1GB
    • articlesdata_free 1.1 GB / data 0.8 GB(135% スカスカ=激しい断片化)
    • articles のインデックス: 22個・合計 2.0 GB、4個は明確に冗長 prefix
  • articles の自動アーカイブ・期間削除処理を footballnext-server/ で grep → 存在しない(コメントアウトのみ)
  • old_articles を空化(ユーザーが phpMyAdmin で TRUNCATE 実行)→ 2,036 MB → 0.34 MB、約 2 GB 解放
  • articles のインデックス削除候補を分析(A群4個: article_site_id / image_width / title / del、いずれも他複合 index の prefix)→ 実行は保留

決めたこと

  • サブプロジェクト 3 リポは本リポから .gitignore で除外
  • performance_schema = 0 を維持
  • old_articles のデータは廃棄、テーブル本体は当面残置(容量影響軽微)
  • articles のインデックス整理は今日は実行せず、調査結果のみ記録

詰まったこと / 気づき

  • 「old_articles の方が行数 3 倍なのに半分のサイズ」謎の答えは InnoDB のページ充填率の差。articles は INSERT/UPDATE/DELETE が活発で data_free が data 本体を上回るほど断片化していた
  • 接続不要で取れる情報(CloudWatch / parameter group)の段階で「performance_schema 不発弾撤去」までできたのは収穫
  • buffer_pool ヒット率が異常に高い → メモリ問題の正解はチューニング側ではなくインスタンス昇格 or DB ダイエット側にある可能性が高い

次回やること

  • articles のインデックス整理(A 群を INVISIBLE → 様子見 → DROP → OPTIMIZE)
  • CloudWatch アラーム新設(FreeableMemory < 100MB)
  • old_articles 空化後の CloudWatch FreeStorageSpace 反映確認

最近のコミット

README

footballnext-operation

概要

(記入予定)

セットアップ

(記入予定)

使い方

(記入予定)