プログラムを作る総合ルールと、作業効率化のひな型を実アプリで検証した記録。
実証アプリ: 君斗りんくのWEBサイト健康診断(2026-06)ほか
キット: web-ios-android テンプレ / リポ: web-health-check-app
関連: 💬 LINE botの無応答ゼロ化の仕組み(同じ「1回で通す・止めない」思想をLINE bot側に適用した知見)
app.config.json(Bundle ID / パッケージ名 / ascAppId / 連絡先)appDataUsages は 404)ascAppId を config にios-appstore-release.yml → ビルド → IPA → スクショ30枚 → appstore-submit.mjsWAITING_FOR_REVIEWasc-set-availability.yml または npm run asc:set-availability:*workflow_dispatch で brand=reverse-hack + IOS_APPSTORE_PROFILE_RH_BASE64会社ロゴ文字は小サイズで潰れる → 紺地 + 白抜きルーペ+チェック(generate-app-icon.mjs)
§4.8: 第三者/ソーシャルログイン(Google・X(Twitter)・Facebook 等)で主アカウントを作る/認証するアプリは、
同等の選択肢として Sign in with Apple(SIWA) を併設しなければならない。server.url 連動型でも
ログイン画面は本番Webそのものなので、アプリ内に出るログインボタンが §4.8 の対象になる。
partnership_program_website(applyIOSGoogleHide() + CSS + MutationObserver の3層)。app.config.json に
appleSiwaServiceId を予約 → Clerk→Apple Developer 設定後 siwaEnabled:true。
Google OAuth は埋め込み WebView を UA で検出して拒否(403 disallowed_useragent)。
X(Twitter) OAuth も同方針。Bパターンは SIWA 併設に加え、必要なら
@capacitor/browser で OAuth を SFSafariViewController に逃がす。
x.com / *.twitter.com / *.clerk.com / *.clerk.accounts.dev)が
遮断されアプリ内ログインが必ず失敗する。capacitor.config の server.allowNavigation に必ず足す。出典: _docs/apple-reject-knowledge-base.md §4.8(リバースハック実通過の返信文つき)/
kimito.link(X専用=Bパターンの実適用)。
playAppId + assetlinks 用 SHA-256 を AI に渡すbundleRelease → play-publish.mjsgenerate-play-assets.mjs / listing: play-set-listing.mjsplay-setup-production.mjs / play-submit-review.mjsplayVersionCode を必ず increment(例: 6→7)app.config.reverse-hack.json + パッケージ別 assetlinks fingerprintbuild-zip.ps1 + publish-cws.ps1 の2コマンド。
審査に送るところまでコマンドで完了します(ダッシュボードを開く必要なし)chrome.enabled: false パターン)📷 手順 → Chrome 追体験
Chrome 拡張の申請をAIに頼んだら、「ダッシュボードへのログインと提出ボタンは代行できません」 と断られました。過去に何度もやってもらっていたのに、です。3回言われて、ようやく原因が分かりました。
AIは2つの別々の事実を混同していました。
「画面を操作できない」と「提出できない」は別の話でした。
実際にはコマンド1本(npm run publish:cws)で審査キューに入ります。
| ファイル送信 | 掲載文の入力 | 審査に送る | |
|---|---|---|---|
| Chrome | 自動 | 手動 | 自動 |
| Google Play | 自動 | 自動 | 手動 |
Play は掲載文まで自動で入れられるのに、最後の「審査に送信」だけは人間がボタンを押します。 Chrome はその逆で、審査送信は自動なのに掲載文の貼り替えだけ手動です。 「片方でこうだったから、もう片方も同じはず」が通じません。
ls scripts/ | grep -i publish の1行で済む)_docs/chrome-web-store-submission-playbook.md はその結果です
このキットの CLAUDE.md は「全プロジェクトが従う設計の心臓部」を掲げています。
では実際に、別のアプリを作業中のAIセッションが知見をここへ書き戻しに来たとき、何が起きるのか。
2026-09-01、malwarecheck.site を担当していた別セッションが、IAP(アプリ内課金)実装で得た
知見をこの CLAUDE.md に書き戻しに来ました。
generate-hub-dashboard.mjs を実際に読み、
ハブは ai-hub/index.json 経由の別経路でCLAUDE.mdの中身は読んでいないこと、
Web公開の実体は「このリポ自体がGitHub上でpublicだから」だと突き止めて訂正した皮肉なのは、③で書かれかけた誤りが、まさに①②で実践されていた原則 (実在確認してから通す・自分の持ち場のコードは自分で検証する)を、 提供側自身が一瞬破っていた、という点です。それを正せたのは、受け取り側が 「越境してきた知見でも無条件で採用せず、自分のコードで裏取りしてから通す」 という一手間を挟んだからでした。
CLAUDE.md に明文化され、
次に別プロジェクトから来るAIセッションは同じつまずきを繰り返さないSCREENSHOT_UPLOADS_IN_PROGRESSretry-ios-submit.mjs)gh run watchSNSで見かけた投稿(@fre2dm さんの発信)の要旨は、 AIにコードを直してもらうたびに別の場所が動かなくなり、それをまたAIに聞いて直す というループが続く、というものでした。その状態を指して 「AIと一緒に迷路を歩いてる感」と表現されていました。
これを他人事として読めませんでした。このキット自身が、直前の2つのカード (サムネイル・ID・アイコン・名前の話/ヘッダー・フッターの話)で、まさにこの型を 踏んでいます。1箇所直したつもりが、別のプロジェクト・別のページで同じ不具合が 直っていない。指摘されてから気づく。直す。また別の場所で同じ形が見つかる。
ai-hub/find・templates/への格上げ・
知見の書き戻しにこだわるのは、この迷路そのものを狭くするため。
「同じ調査をもう一度AIにやらせない」が目的であって、AIの性能の話ではない上のカード(迷路化の話)の教訓②で「同じ調査をもう一度AIにやらせない」と書きましたが、 これはまだ文章のルールでした。AI開発の外部調査(大企業向けマイクロサービス基盤の 設計論)を検討する機会があり、Backstage・Pact Broker・OTel/SLO運用のような大掛かりな 基盤はこのキットの規模には過剰と判断して不採用にしましたが、1点だけ持ち帰りました。 「AIの行動を監視するのではなく、必要な工程を踏んだ証拠が無ければ完了扱いにできない 仕組みにする」という考え方です。
最初にCLAUDE.mdへ「実装着手前の非交渉ルール」10項目(既存実装を検索する・
ai-hubで共通部品を確認する・同等機能があれば新規実装しない、等)を追加しました。
ただしこれだけではまた文章のルールに戻ってしまいます。そこで、既に持っていた
「証明3点台帳」(検査が実対象で赤・緑の両方を観測した記録を機械的に見る仕組み)を拡張し、
ai-hub find --logで検索した事実・git diffから自動取得した変更
ファイル・各Gateの実行結果を1つの記録(ProofEntry)へ束ねました。
実装の途中で本質的な抜けが2つ見つかり、直しました。1つ目は、証拠が欠けていても 表示されるだけで完了判定を止めていなかったこと。2つ目はさらに根が深く、 検索した証拠として台帳に残していたのが「検索ログがあった場所(パス文字列)」 だったことです。そのログが一時ファイルで後から消えると、パス文字列だけが残り、 検索していなくても「証拠あり」として通り続けてしまう状態でした。
git diff --name-onlyとgit ls-files --others --exclude-standard
(追跡済み変更+未追跡の新規ファイル)から機械的に取得すれば、申告漏れが起きない
新しい台帳やGateの仕組みを増やすのではなく、既にあった証明3点台帳・ai-hub・
check-drift・check-gates-are-wiredを組み合わせて実現しました。
「AIが確認を飛ばすと機械的に完了できない」という状態まで到達したところで、
いったんここで機能追加を止めています。
AIに調査させると、コードを読んで「たぶんここが原因」と推論します。 それらしい答えが返るので、つい信じて修正に進みます。 でも読んだファイルが、実際には使われていないことがあります。
3回とも、コードは十分に読み込んだ上で外しています。 解決したのは、実際に送信されている中身を1回だけ丸ごと覗いたときでした。 想像していた構造と全く違い、指示が16,956文字あって肝心の入力データは末尾にありました。
grep で確認する
「性能の高いものに変えれば直る」は、原因を特定する前にやると費用だけ増えます。
詳細は _docs/runtime-truth-verification-knowledge-base.md に残しました。
姉妹プロジェクトから「AIが実装前に、既にある機能を知らずに新規実装しようとする型の失敗が 複数のプロジェクトで見つかった」という報告が届きました。ルールとして重複実装を避けることは 3箇所にすでに書いてあり、文章としては十分でした。それでも起きていました。
返信を書き終えた直後、まさに同じことを自分がやっていたと分かりました。 新しい機能(複数のチャット相手をひとつの案件としてまとめるAI秘書)に、 メッセージの送信者を表示するタイムラインを新しく作ったのですが、 「役割名+名前」のテキストだけで済ませていました。
一方、姉妹プロジェクトには既に「人物」の表し方の正本がありました。 アイコン画像 or 名前から決まる安定した色つきの頭文字、IDは改名しても同一人物と分かる鍵として保持する、 という形です。しかもそのコードには 「2026-08-21実機で『サムネイルが出ない』として発覚した」という実際の事故の記録まで コメントに残っていました。それを読まずに、劣化版を新しく書いてしまったのです。
この件は横断の索引(ai-hub/index.json)にも登録し、次に別プロジェクトで
同じ判断を迫られたときにfind --tag duplicationで見つかるようにしました。
正本はこのキットの_docs/に残し、索引には要約だけを置く、という置き場所の使い分けも、
「正本を2つ作らない」という同じ教訓の実践です。
上のカードのすぐ後、実際のスクリーンショットを見せられました。別プロジェクトの LP(複数ページの静的サイト)で、ページごとにヘッダー・フッターがバラバラ になっていました。共通のナビゲーションが無く、フッターの中身もページによって 文言が違います。
調べると、このキット自身のsite/(今あなたが読んでいるこのサイト)には、
ヘッダー・フッターを1箇所で管理する実装が既にありました。
24ページ以上に生HTMLをコピペしていたのを2026-08-22に廃止し、JSが実行時に
差し込む方式に直した実績があります。それなのに、この実装はsite/の
中だけに閉じていて、templates/(他プロジェクトが金型として使う場所)
へは一度も格上げされていませんでした。
「このキットは、全プロジェクトが従う設計の心臓部であるべき」という指摘を受けて、
site/scripts/site-chrome.jsからキット固有の値(ナビ項目・ブランド名)を
抜き、templates/web/site-chrome/として一般化しました。複数ページの
静的サイトを新規に作るときは、この金型をコピーしてブランド情報だけ書き換えれば、
最初からヘッダー・フッターが1箇所管理になります。
姉妹プロジェクト(クリエイター向けダッシュボード)で、複数の外部プラットフォーム (Chatwork・ココナラ・ランサーズ・ChatGPT・X等)からの返信サジェストデータを 1つの画面に集約する機能のスクリーンショットを見せてもらいました。同じ形の 「サムネイル+件数」カードが数十枚、連携先の数だけ並んでいました。
実際にコードを調べると、ダッシュボードの白いカードの外枠 (角丸・枠線・影のCSSクラス文字列)が、7ファイル18箇所に独立して 手書きされていました。 ファイルごとに枠の色が微妙に違う2系統(中立色と ブランド色)が混在しており、それが意図的な使い分けなのか、単にコピー元が バラバラだった結果なのか、コードだけでは判別できない状態でした。
ここで既存のルール(基準⑤⑥「共有部品はプロジェクト内で1箇所にまとめる」)
は既に効いていたはずでした。それでも「同じ名前の関数」を探す機械検査
(check-shared-parts-used.mjs)では、この重複は1件も検出できませんでした。
重複していたのはUIのクラス文字列であり、関数の名前や構造ではなかったからです。
「見つけたのが偶然(スクリーンショットを見せてもらえたから)」という点が
一番の問題でした。人間が持つ「これ前にも書いた気がする」という既視感が、
AIには初期状態で備わっていません。そこで、行を正規化して連続する
一致の塊を数える検査(check-near-duplicates.mjs)を新規に作り、
「同じ名前」ではなく「似た形」を機械的に見つけられるようにしました。
実際にこのキット自身に対して実行すると、53件の似た塊が見つかりました (多くは意図的に共有しているテンプレートHTMLとの一致で、これは「正しく 共有できている証拠」でした)。過去に同じ画面部品が3実装に割れていた 別プロジェクト(reply-copilot-openrouter-v2)に対して実行すると、 その実装がきちんと重複クラスタとして検出されることも確認できました。
重複していた18箇所は、既存の使用箇所を1つずつ確認し、抽出前後で描画結果が 1バイトも変わらないことを写真(スナップショット)で確認しながら、 共有コンポーネント1つへ切り出しました。同じ手書きが再発しないよう、 コミット前に自動で止める検査も一緒に配線しています。
| 用途 | Secret 名 |
|---|---|
| iOS 署名 | IOS_DIST_CERT_* / IOS_APPSTORE_PROFILE_BASE64 / IOS_APPSTORE_PROFILE_RH_BASE64 / APPLE_TEAM_ID |
| ASC API | APPSTORE_CONNECT_KEY_ID / ISSUER_ID / API_KEY_P8_BASE64 |
| Android | ANDROID_KEYSTORE_* / GOOGLE_PLAY_SA_JSON_BASE64 |
| Chrome | .env.cws(リポジトリ外・gitignore) |
りんく(案内役): AIにぜんぶを1人でやらせると、「できたつもり」で間違えることがあるんだ。
だから、係を3人に分けたよ。
reality-checker)store-guard)harvester)
こん太: 「失敗してもだいじょうぶ! きろく係がノートに書けば、二度目はないんだ!」/
「けんさ係は作った本人と別の人だから、遠慮なくダメ出ししてくれるよ!」
たぬ姉: 係は増やしすぎちゃダメ。3人で足りるのに10人にすると、
だれが何の係かわからなくなるからね。
世界では「AIの係の人を230人つくった」道具(agency-agents)が話題だけど、
うちはこの家の暮らしに本当にいる3人だけを選んで置いた ——
考え方は借りて、数は借りない、という話。
置き場: ~/.claude/agents/(正本は ai-hub/agents/・設計は
ai-hub-DESIGN.md 追補H)。Claude Code が名前で呼ぶと係が出てくる。
りんく(案内役): 「君斗りんくのAI返信秘書」をお店に出したとき、Play Console の
入力欄(11項目・配信国の選択・広告IDの確認・審査に出すボタンの手前まで)は、ぜんぶ
ブラウザを動かせるAIが自分で画面をクリックして埋めたよ。人がキーボードで打ったのは、
ほとんど無いんだ。
こん太: 「めんどうな入力ぜんぶAIまかせにできるの、すごくラクだね! ぼくらは最後の
『よし出そう!』ってボタンを押すだけでいいんだ!」
たぬ姉: ただし最後の一押しは、いまも人がやる約束。取り消せないボタンを
AIまかせにしないの。これは「AIがサボってる」んじゃなくて、わざと人に残してるんだよ。
不具合を直すとき、いちばん時間を食うのは修正そのものではありません。 AIが「実機でどうなっているか分からない」ので、毎回あなたに見てもらう往復です。 「開いて、そこを押して、どう見えるか教えてください」——これが1日に何度も起きます。
直し方は、アプリ自身に自分の状態を数字で申告させることでした。 画面を見なくても、コマンド1回で今の状態が1枚のテキストで出てきます。
これで、調査の往復から人が外れました。 「目視をお願いして説明を待つ」が「コマンド1回・数秒」になっています。
考え方の全体はキットの説明書(AIが読む用)に入っています。 新しいアプリを作るときは、作り始める前に読ませてください。 後から計器を足すのは、作りながら足すより何倍も高くつきます。
デスクトップPCとノートPCの間で開発を引き継ぐ仕組み(handoff.mdと、
正本 CLAUDE.md への自動到達経路)を設計した際、その土台となる
「共有フォルダの実体は何か」を、2回続けて検証せずに決めつけました。
OneDrive という
文字列が含まれていたため、「OneDrive同期でデスクトップPC・ノートPC間に
自動で伝わる」と実機確認せずに設計・記述したGet-Process・実行ファイルの有無・レジストリのアカウント登録・
フォルダの属性(クラウド同期を示す reparse point の有無)を確認したところ、
OneDrive同期エンジンはこのPC上に一切存在しなかったResilio を見て、今度は「Resilio Sync(P2P同期ソフト)が実体では」
と、これも実機確認せずに決めつけかけたgithub (¥¥BESTTRUST) (Z:))の
スクリーンショットで、Windows標準のSMBネットワーク共有
(デスクトップPCのホスト名 BESTTRUST が github
フォルダを共有し、ノートPCがそれを Z: ドライブとしてマウントしている)
だったと判明した
パス名に残っていた OneDrive と Resilio は、どちらも
過去の経緯で残った命名に過ぎず、現在使われている共有方式とは無関係でした。
2回とも、名前から推測しただけで「たぶんこれだろう」を確認済みの事実として扱って
しまっていました。
Get-Process・Get-SmbShare のような実機コマンド、
または相手が実際に見ている画面のスクリーンショットで裏取りしてから設計する
(基準③「完膚なきまでの裏取り」は、コードだけでなくインフラの前提にも同じ強度で効く)
設計の結論自体(github/CLAUDE.md への1行インポートで、配下の
全プロジェクトに正本が自動で伝わる)は、共有方式がSMBであっても変わらず有効でした
——ノートPC側もZドライブ経由で同じファイルシステムの実体を見ているためです。
変わったのは「なぜ両PCに伝わるのか」という前提の理解だけでした。
アプリの名前を「マルウェアチェック.site」から「サイト侵害チェック」へ変えて審査に出す直前、 App Store Connect(ASC)には旧いキーワード(マルウェア・感染・スキャン)・旧い名前・旧いサブタイトルが 残ったままでした。提出スクリプトが「前の版の値」を「リポのファイルの値」より優先して引き継ぐ作りで、 ファイルをどう直しても反映されなかったからです。機械は何も止めず、人が提出の直前に ASC を読み戻して初めて気づきました。
appstore-submit.mjs の [7g])identity.formerNames)で検査し、
掲載文の正本ファイルが無いことは IPA を作る前に止める取り消せない操作(push・マージ・デプロイ)の前に「CLAUDE.md を現在の内容のまま全文読んだか」を確かめる門番を作りました。 ところが指示書は1300行を超え、1回では読めません。分けて読むのですが、1行目から読み直すとそれ以前の読みを捨てる作りで、 後半を先に読むと後半が消え、同じ巨大ファイルを何度も読み直す羽目になりました。
offset/limit の形で出すcore:end マーカーを置き、必読の核(431行)だけを判定対象にした。核が太らないよう、上限450行を機械で見張る使用量ダッシュボードに各頭脳の残量を出したところ、Grok Build の残高切れ(402)・Alibaba の全6モデルの無料枠切れ・OpenRouter の残高切れが同時に起きていました。 自動の切り替えは Cloudflare まで落ちていました。Cloudflare の公式 MCP で今日(UTC)の使用量を取ると、 約72,000 ニューロンで、無料枠 10,000 の約7倍でした。
dispatch に「今日の無料枠を超えていたら、自動では Cloudflare を飛ばす」ガードを入れた(明示指定は止めない・記録が読めなければ止めない)トップページの「実際に使った例」に、改名前の名前「マルウェアチェック.site」と、 「マルウェア感染疑いを簡易診断」という旧い説明が残っていました。審査に出した新しい説明(端末のウイルススキャンはしない)と食い違う、 公開ページの記載でした。手で書く一覧は、名前も状態も古くなります。