{
  "$comment": [
    "オートパイロットの実費台帳。VISION/Runbookが『判断の根拠はファイルに落とす』",
    "と定めているのと同じ理由で、AIの実費も散文ではなくファイルに置く。",
    "",
    "【なぜ必要か】2026-08-22時点で、この運用のトークン実費は『1回だけ観測された",
    "0.81 USD』しか無かった。予算の話は全部その1点の周りの推測で行われており、",
    "『予算に応じて配分する』と外に言える状態ではなかった。まず測る。",
    "",
    "【誰が書くか】obsidian-autopilot.yml の post ステップが、claude-code-action の",
    "実行ログ（claude-execution-output.json）から total_cost_usd / num_turns を取って",
    "1行追記する。手で書かない。追記に失敗しても出荷は止めない（continue-on-error）",
    "— フォント導入と同じで、計測は記事より優先されてはいけない。",
    "",
    "【何を測れていないか（重要）】CCR Routine（副系07:30 / 再試行09:20）の実費は",
    "ここに入らない。スケジュール起動セッションのログは外部から読めないため",
    "（Runbook §0-2）。2026-08-22時点で実出荷はすべて副系が行っているので、",
    "この台帳がカバーしているのは『主系の消費』だけであり、運用全体の実費ではない。",
    "route フィールドでどちらか分かるようにしてあり、集計もrouteごとに出す。",
    "measured_coverage に、その月に主系が何回動いたかを併記する。",
    "",
    "【上限の使われ方】scripts/autopilot-budget.mjs --check が当月実績を集計し、",
    "monthly_usd_cap を超えていれば exit 1 を返す。obsidian-autopilot.yml の Gate が",
    "これを見て run=false にする。つまり上限は表示用ではなく、実際に自分を止める。"
  ],
  "budget": {
    "monthly_usd_cap": 280,
    "cap_set_by": "owner",
    "cap_note": "**2026-09-03、オーナーが $40 → $280 に決めた。**placeholder ではなくなった（承認記録は data/spend-approvals.json の seq=2）。\n\n【$280 の位置】主系の実測 n=6 を日次出荷×30日に伸ばすと、中央値 $259/月・平均 $254/月・当月の per shipped（$11.4733）で $344/月。**$280 はこの幅の中にある。**\n\n【まだ実測そのものではない】run 単位の実測からの外挿であって、2週間ぶんの月次実測ではない。そして**副系CCRの実費は観測できない**ので、この $280 が縛るのは主系だけ —— 実際の総額はこれより多い。**したがって「予算に応じて配分している」は、まだ対外的に言わない。**言えるようになるのは、月次の実測が2週間ぶん貯まって、そこから決め直したとき。\n\n【いったんの値】オーナーの言葉は「日次出荷なら $280/月 ↑いったんこれで」。撤回条件は financial-policy.json の withdraw_conditions（成果物ゼロ3回連続 / 単価が中央値の3倍 / 異常消費が2週連続）で、損失上限は $420。",
    "on_exceed": "skip_run",
    "on_exceed_note": "上限超過時は当日の主系runをGateで止める。副系CCRは別経路なので止まらない（止める手段が無い — Runbook §0-2の『CCRのログは外部から読めない』と同じ制約）。ここも正直に書いておく。",
    "anomaly": {
      "$comment": [
        "異常消費の検知。**絶対額のしきい値は置かない。**",
        "実績が1点しか無い段階で『$5を超えたら異常』と決めると、それは推測であって基準ではない。",
        "代わりに**直近の中央値との比**で見る。中央値は実績が増えるほど正確になるので、",
        "基準そのものが自己較正される。",
        "",
        "median_window … 中央値を取る直近run数",
        "multiple      … 中央値の何倍を異常とみなすか",
        "min_samples   … これ未満の実績数では判定しない（**判定できないことを異常と言わない**）",
        "min_absolute  … 中央値が極小のとき比だけだと過敏になるので、この額未満は無視する"
      ],
      "median_window": 14,
      "multiple": 4,
      "min_samples": 5,
      "min_absolute_usd": 2
    },
    "task_budgets": {
      "article": {
        "monthly_usd_cap": 180,
        "note": "毎朝のレーンA〜E。**2026-09-03、12 → 180。**上限が $40→$280 になったので実測から引き直した。観測した article は $7.2967 / $7.8771 で、日次出荷なら月20〜23回ぶん。**この運用の本体なので、枠の大半をここに置く。**"
      },
      "repair": {
        "monthly_usd_cap": 60,
        "note": "レーンF。**2026-09-03、18 → 60。**観測した repair は $9.3726 / $11.9329 / $13.5739 で、月4〜6回ぶん。直近10日で3回走っているが、**故障が減れば使わない枠**なのでarticle より薄くしてある。記事より先に走るので独立させてある点は変えていない。"
      },
      "analysis": {
        "monthly_usd_cap": 12,
        "note": "計測・レポート・台帳の更新。**実績0回。**1回上限 $1.50 に対する予備枠で、**走った実績から決めた数字ではない。**走り始めたら引き直す。"
      },
      "pr": {
        "monthly_usd_cap": 12,
        "note": "広報原稿と素材。配信は月1回程度。**実績0回**（1回上限 $3.00 に対する予備枠）。"
      },
      "qa_triage": {
        "monthly_usd_cap": 8,
        "note": "**実績0回**（1回上限 $1.00 に対する予備枠）。"
      },
      "audit": {
        "monthly_usd_cap": 8,
        "note": "独立監査（週1）。**実績0回**（1回上限 $1.00 に対する予備枠）。"
      }
    },
    "$task_budgets_note": [
      "種別ごとの月次枠。**合計は monthly_usd_cap を超えてはいけない**（超えると枠が装飾になる）。",
      "月次上限だけだと、記事で使い切った月に修理が走らない（あるいはその逆）が起きる。",
      "",
      "**この枠も暫定。** monthly_usd_cap 自体が placeholder なので、そこから割った枠も placeholder。",
      "実測が貯まるまで『タスクごとに最適配分している』とは対外的に言わない。",
      "",
      "種別ごとの超過では**主系全体を止めない**。止めるのは月次上限の役目で、",
      "ここは『この種別はもう枠を使い切っている』を日報と警告に出すためのもの。",
      "種別の枠で全体を止めると、記事の枠切れが修理まで巻き込む。"
    ],
    "task_budgets_set_by": "derived_from_placeholder",
    "cap_basis": "extrapolation",
    "$cap_basis_note": "上限が何に基づくか。**'measured' になるまで「予算に応じて配分している」と対外的に言わない。**\n  placeholder    … 実測1点からの粗い外挿（〜2026-09-02）\n  extrapolation  … run 単位の実測（n=6）を日次×30日へ伸ばした外挿（2026-09-03〜）\n  measured       … 月次の実測が2週間ぶん貯まり、そこから決め直した\n**[なぜ足したか] 自制が散文だけになりかけた。**この注記を出す条件は cap_set_by === 'placeholder' で、オーナーが額を決めた瞬間に**注記ごと消える**形になっていた。だが消えてよいのは「オーナー未確認」の部分だけで、「まだ実測ではない」ほうは残る。**額を誰が決めたか（cap_set_by）と、何に基づくか（cap_basis）は別。**"
  },
  "runs": [
    {
      "date_jst": "2026-08-22",
      "route": "actions",
      "run_id": "32528028588",
      "job_id": "96914156036",
      "total_cost_usd": 0.8149038,
      "num_turns": 30,
      "outcome": "no_artifact",
      "note": "permission_denials_count=14 で成果物ゼロのまま success 終了。modelUsage は claude-haiku-4-5 と claude-sonnet-5 の2つ。実費は発生しているが記事は出ていない — 『成功したのに何も出ていない』回のコストがここに残る。",
      "models": [
        "claude-haiku-4-5-20251001",
        "claude-sonnet-5"
      ],
      "task_kind": "article"
    },
    {
      "date_jst": "2026-08-23",
      "route": "actions",
      "run_id": "32599191984",
      "total_cost_usd": 7.296703800000001,
      "num_turns": 150,
      "outcome": "shipped",
      "note": "日次アクチュエータが自動追記（ジョブログの result 行） ／【2026-08-25 追記】**task_kind が空だった。**アクチュエータは lane/action から種別を導けるとき『だけ』書く規則で、この行は導出が入る前に追記されている。**種別が無い run は1回あたりの上限を判定しない**（判定していない ≠ 上限内）ので、空のままだと article の上限 $2.00 に対する $7.2967（3.6倍）が素通りする。手で補った。",
      "job_id": "97094799837",
      "task_kind": "article",
      "models": [
        "claude-haiku-4-5-20251001",
        "claude-sonnet-5"
      ],
      "cap_review": {
        "at": "2026-08-25",
        "by": "owner",
        "why": "主系の初出荷回。前日の故障（ap-20260822-actions）の修理を含む150ターン・17分の実行で、内容そのものは妥当と判断した。上限$2.00は実績n=1（$0.8149）から置いた暫定値で、上限側が過小。実測5件で異常検知が自己較正されるので、そこから再導出する。オーナー承認 2026-08-25"
      }
    },
    {
      "date_jst": "2026-08-24",
      "route": "actions",
      "run_id": "32667079679",
      "total_cost_usd": 0,
      "num_turns": 1,
      "outcome": "failed",
      "note": "日次アクチュエータが自動追記（ジョブログの result 行）"
    },
    {
      "date_jst": "2026-08-25",
      "route": "actions",
      "run_id": "32779337325",
      "total_cost_usd": 0,
      "num_turns": 1,
      "outcome": "failed",
      "note": "日次アクチュエータが自動追記（ジョブログの result 行）"
    },
    {
      "date_jst": "2026-08-25",
      "route": "actions",
      "run_id": "32816234185",
      "job_id": "97704991438",
      "total_cost_usd": 11.932870250000008,
      "num_turns": 104,
      "task_kind": "repair",
      "outcome": "shipped",
      "note": "レーンF（修理）。ap-20260824-actions / ap-20260825-actions の即時失敗を修理し、両方に resolved_at が入った（data/autopilot-runs.json の ap-20260825-actions-force）。104ターン・27分。**repair の1回上限 $3.00 に対し 3.98倍。**月枠 $8.00 も、この1回だけで超えている。**この行は2026-08-25に手で入れた** — 日次アクチュエータの追記は09:00 JSTで、この run が終わったのは15:44 JST。次の追記は翌日09:00 JSTになるが、翌日の主系は06:17 JSTに走るので、**このままだとゲートが超過を知らないまま1日走る**　【2026-08-25 訂正】この note は当初「認証系即時失敗」と書いていたが、**種別は誤りだった。**実際は上流 claude-code-action の版の破損（upstream_action_regression）で、data/autopilot-runs.json の failure_class_original / reclassified_note に経緯がある。ただし同runの直前にオーナーが CLAUDE_CODE_OAUTH_TOKEN も更新しているため、**版とトークンのどちらが効いたかはこの観測からは分離できていない**（版の相関は強いが排除ではない）。",
      "cap_review": {
        "at": "2026-08-25",
        "by": "owner",
        "why": "2026-08-25のセッションでオーナーが明示的に権限を委譲して解除（「権限を与えるのでやって」）。支出そのものは妥当と判断した: 104ターン・27分で ap-20260824/25 の2件を修理して出荷しており、内容に無駄な反復は無い。**ただし本質は支出ではなく上限の較正。** 実測5件のうち task_kind が付いた3件を見ると、実際に仕事をして出荷した run は2件（article $7.2967 / repair $11.9329）で、そのどちらも1回上限を超えている。**上限を満たしたのは失敗した run（$0.8149）だけ。** 上限 $2.00/$3.00 は失敗runのn=1から外挿した placeholder で、成功runを一度も許容していない。さらに repair は monthly_usd_cap $8 に対し1回が $11.93 で、月枠が1回分に足りていない。上限の再設定は monthly_usd_cap の決定を含み権限表で human_only なので、ここでは解除だけを行い、較正は数値つきで台帳に残す（act-budget-recalibrate）"
      }
    },
    {
      "date_jst": "2026-08-26",
      "route": "actions",
      "run_id": "32900786201",
      "total_cost_usd": 7.877065000000003,
      "num_turns": 162,
      "task_kind": "article",
      "outcome": "shipped",
      "note": "日次アクチュエータが自動追記（ジョブログの result 行）"
    },
    {
      "date_jst": "2026-08-30",
      "route": "actions",
      "run_id": "33279844326",
      "total_cost_usd": 0,
      "num_turns": 1,
      "outcome": "failed",
      "note": "日次アクチュエータが自動追記（ジョブログの result 行）"
    },
    {
      "date_jst": "2026-08-31",
      "route": "actions",
      "run_id": "33340960317",
      "total_cost_usd": 0,
      "num_turns": 1,
      "outcome": "failed",
      "note": "日次アクチュエータが自動追記（ジョブログの result 行）"
    },
    {
      "date_jst": "2026-09-01",
      "route": "actions",
      "run_id": "33454414490",
      "total_cost_usd": 9.3726075,
      "num_turns": 99,
      "task_kind": "repair",
      "outcome": "shipped",
      "note": "本日（09-02）のセッションがジョブログの result 行から追記。09-01 の主系はレーンFで PR #749 を出しており、運転台帳には行が無い（act-ledger-sync として未処理）。"
    },
    {
      "date_jst": "2026-09-02",
      "route": "actions",
      "run_id": "33568838189",
      "total_cost_usd": 13.5739125,
      "num_turns": 139,
      "task_kind": "repair",
      "outcome": "shipped",
      "note": "日次アクチュエータが自動追記（ジョブログの result 行）"
    }
  ],
  "$backfill_note": "**2026-08-25 更新。**旧記述は『runs は 2026-08-22 の1件のみ／この台帳に欠落は無い』だったが、**それは run 1〜11 までの話で、その後の3回を数えていなかった。**run 12（08-23・$7.2967・出荷）が抜けており、当月の実費を $0.8149 と表示していた。実際は $8.1116。\n\n抜けた原因は構造にあった。ワークフローの実費ステップは**金額を出力して『翌日のセッションが台帳へ入れること』と書くだけ**で、追記する主体が翌日の実行そのもの。その翌日（08-24）の主系が即時失敗したため、追記する者がいなかった。**記録が最も要る回＝失敗した回に、記録が落ちる。**\n\n**この穴は日次アクチュエータ（scripts/autopilot-act.mjs の append-cost）で塞がれた。**ジョブログから読んで自動追記するので、翌日の実行が落ちても記録は残る。ただし**種別（task_kind）は lane/action から導けるときだけ書く**規則なので、導出できない回は空のまま残る。空の行は1回あたりの上限を判定しないため、**種別の欠落は上限の素通りに直結する。**08-23 の行はこれに当たり、手で補った。\n\n現在の内訳: run 1〜8 Gateスキップ（Claude Code未実行＝実費ゼロ）／9 apt詰まりで cancelled／10 allowed_bots で failure／11 成果物ゼロ $0.8149／**12 出荷 $7.2967**／13・14（08-24・08-25）は初回モデル呼び出しの時点で失敗し modelUsage が空・$0。",
  "$schedule_note": "**追記のタイミングと、1回上限ゲートの関係。**（2026-08-25）\n\n**定時実行に穴は無い。**主系 obsidian-autopilot は 06:00 JST 起動・timeout-minutes 90 なので、遅くとも 07:30 JST には終わる。日次アクチュエータ（autopilot-act の append-cost）は 09:00 JST なので、その日の実費を必ず同じ朝のうちに拾う。翌日 06:00 の主系は、前日分が入った台帳を読む。\n\n**穴があるのは臨時実行だけ。**`workflow_dispatch` で走らせて **09:00 JST を過ぎて終わった回**は、アクチュエータの次の追記が翌日09:00になる。翌日の主系は06:00＝それより前に走るので、**その回の超過を知らないまま1日走る。**実際に run 32816234185（15:44 JST 終了・$11.9329）で起きたので、この行は手で `--append` した。**臨時実行を撃ったら、終了時刻を見て手で入れること。**\n\n**⛔ アクチュエータを主系より前へ動かして直そうとしないこと。**05:00 JST に動かすと、前日分は前日09:00に追記済みで新しいものが無く、その日の実費は翌日05:00まで入らない。**いま動いているサイクルを1日ぶん悪化させる。**\n\n**訂正:** PR #570 の本文とコミット文に『同じ順序である限り毎回1日遅れる』と書いたが、**これは誤り。**定時実行では起きない。Actionsのログが UTC 表示であるのを JST と読み違えたことによる一般化の誤りで、run 32816234185 についての記述自体（15:44 JST 終了）は正しい。"
}
