From 9120f5c8b3379327482301a403ab844b4874dd97 Mon Sep 17 00:00:00 2001 From: Aaron Stainback Date: Sun, 26 Apr 2026 09:57:59 -0400 Subject: [PATCH 1/3] budget: capture first cost snapshot + bootstrap latest-report.md (task #287 sub-step 2 partial) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ran tools/budget/daily-cost-report.sh on main (just landed via PR #611) to bootstrap the first snapshot in docs/budget-history/snapshots.jsonl + the glanceable latest-report.md. ## What this snapshot captures (LFG, 2026-04-26T13:57:01Z) - Copilot: Business plan, 1 active seat, $19/month single-span projection - Zeta repo: 20 last-runs / 513s total duration / 0 billable_ms (public-repo included minutes) / 5 recent merged PRs - N=1 — projection is "insufficient data" per the script's honest reporting; needs N>=3 across >=2 LFG merges before decision-ready ## What this gives the maintainer `cat docs/budget-history/latest-report.md` → see costs in <2 seconds. Replaces manual GitHub UI checking (the failure mode Aaron surfaced 2026-04-26 with the LFG Copilot $1.90/$0 over-budget alert + the $3.80 actual seat-rate clarification). The report's "Projection parameters" section makes the $19/month single-seat assumption visible alongside the spend. ## Why N=1 is fine to commit now Each future daily run (when scheduled) appends another snapshot row to snapshots.jsonl AND overwrites latest-report.md. The N>=3 projection threshold becomes meaningful with snapshot accumulation; the bootstrap-with-N=1 here seeds the time-series. Per Otto-275 log-don't-implement: NOT scheduling the daily routine in this PR — that's task #287 sub-step 2 (full) pending Aaron's /schedule confirmation. This commit is the manual one-shot to seed visibility today. Composes with task #287, PR #611 (the wrapper), tools/budget/snapshot-burn.sh, tools/budget/project-runway.sh, docs/budget-history/README.md. --- docs/budget-history/latest-report.md | 83 ++++++++++++++++++++++++++++ docs/budget-history/snapshots.jsonl | 1 + 2 files changed, 84 insertions(+) create mode 100644 docs/budget-history/latest-report.md create mode 100644 docs/budget-history/snapshots.jsonl diff --git a/docs/budget-history/latest-report.md b/docs/budget-history/latest-report.md new file mode 100644 index 00000000..0c60de6c --- /dev/null +++ b/docs/budget-history/latest-report.md @@ -0,0 +1,83 @@ +# Latest cost projection — auto-generated + +**Generated:** `2026-04-26T13:57:09Z` +**Factory git SHA:** `744e268dd6f57ba230deab8d77616ae19e38cf2f` +**Source:** `tools/budget/daily-cost-report.sh` (wraps snapshot-burn.sh + project-runway.sh) + +This file is **OVERWRITTEN** on each daily run. Historical snapshots live in +`docs/budget-history/snapshots.jsonl` (append-only); historical projections +can be reconstructed from any snapshot subset via `tools/budget/project-runway.sh`. + +--- + +## Projection text + +```text +Budget projection — three-repo-split Stages 1-4 +================================================ + +Evidence source: /Users/acehack/Documents/src/repos/Zeta/docs/budget-history/snapshots.jsonl +Samples (N): 1 +First snapshot: 2026-04-26T13:57:01Z +Latest snapshot: 2026-04-26T13:57:01Z +Latest factory SHA: 744e268dd6f57ba230deab8d77616ae19e38cf2f + +Latest state +------------ + Copilot plan: business + Copilot seats: 1 + Actions total_duration_ms (last 20 runs, all repos): 513000 + Actions billable_ms cumulative: 0 + +Projection parameters +--------------------- + Estimated extra PRs for Stages 1-4: 20 + Copilot Business seat rate (USD/mo): $19 + Actions free-tier allowance (ms): 180000000 + Assumed migration span (days): 30 + +Projection +---------- + Per-PR Actions ms: insufficient data (N<2 or no duration delta) + Projected Actions ms: unavailable + Gate status: cannot project — accumulate more snapshots + Copilot projected USD (single span): $19 + +Human-maintainer-decision surface +---------------------- + N=1; BACKLOG row requires N>=3 across >=2 LFG merges before + projection is considered decision-ready. Keep accumulating. + +Caveats +------- + * recent_merged is a rolling-window count (last 10 closed PRs), + not a cumulative counter. Per-PR-ms uses it as a proxy — + introduces error when the 20-run window doesn't roll forward + between snapshots. A cumulative PR counter would be a + substrate improvement (BACKLOG follow-up). + * last_billable_ms on public repos is typically 0 (included + minutes). Projection still meaningful for macOS runs and + any future private-repo work. + * Copilot projection assumes constant seat count over the span. + Seat-count changes require rerunning projection. +``` + +--- + +## How to read this + +- **`Actions billable_ms cumulative`** — cumulative GitHub-Actions billable runtime across captured snapshots. On public repos this is typically 0 (included minutes); meaningful for macOS / private-repo / Enterprise-plan accounts. +- **`Per-PR Actions ms (naive)`** — rolling-window estimate of per-merged-PR Actions cost. Caveats in the projection text below; treat as proxy until `N \geq 3` cumulative snapshots exist. +- **`Actions fit`** — whether projected Stages 1-4 burn fits the configured free-tier allowance. If `EXCEEDS`, the gate-conditions section names escape valves. +- **`Copilot projected USD`** — assumed-30-day span at the current seat count and rate. Re-run with `--copilot-rate` to model rate changes. + +--- + +## Source data + +- Snapshots: `docs/budget-history/snapshots.jsonl` +- Methodology: `docs/budget-history/README.md` +- Wrapper: `tools/budget/daily-cost-report.sh` (this run) +- Capture script: `tools/budget/snapshot-burn.sh` +- Projection script: `tools/budget/project-runway.sh` + diff --git a/docs/budget-history/snapshots.jsonl b/docs/budget-history/snapshots.jsonl new file mode 100644 index 00000000..ca18135e --- /dev/null +++ b/docs/budget-history/snapshots.jsonl @@ -0,0 +1 @@ +{"ts":"2026-04-26T13:57:01Z","factory_git_sha":"744e268dd6f57ba230deab8d77616ae19e38cf2f","org":"Lucent-Financial-Group","note":null,"copilot_billing":{"seat_breakdown":{"pending_invitation":0,"pending_cancellation":0,"added_this_cycle":1,"total":1,"active_this_cycle":1,"inactive_this_cycle":0},"seat_management_setting":"assign_selected","plan_type":"business","public_code_suggestions":"allow","ide_chat":"enabled","cli":"enabled","platform_chat":"enabled"},"repos":[{"repo":"Lucent-Financial-Group/Zeta","agg":{"total_runs":20,"total_duration_ms":513000,"billable_ubuntu_ms":0,"billable_macos_ms":0,"billable_windows_ms":0},"pr":{"recent_merged":5,"last_merged_at":"2026-04-26T13:54:29Z"},"last_20_runs":[{"id":24958345708,"name":"Copilot code review","conclusion":null,"run_started_at":"2026-04-26T13:56:13Z","updated_at":"2026-04-26T13:56:27Z"},{"id":24958344924,"name":"CodeQL","conclusion":"success","run_started_at":"2026-04-26T13:56:10Z","updated_at":"2026-04-26T13:56:32Z"},{"id":24958344908,"name":"gate","conclusion":null,"run_started_at":"2026-04-26T13:56:10Z","updated_at":"2026-04-26T13:56:14Z"},{"id":24958344765,"name":"Automatic Dependency Submission (NuGet)","conclusion":"success","run_started_at":"2026-04-26T13:56:10Z","updated_at":"2026-04-26T13:56:54Z"},{"id":24958344331,"name":"Code Quality: PR #614","conclusion":null,"run_started_at":"2026-04-26T13:56:08Z","updated_at":"2026-04-26T13:56:13Z"},{"id":24958344166,"name":".github/workflows/github-settings-drift.yml","conclusion":"failure","run_started_at":"2026-04-26T13:56:08Z","updated_at":"2026-04-26T13:56:08Z"},{"id":24958324676,"name":"Copilot code review","conclusion":null,"run_started_at":"2026-04-26T13:55:09Z","updated_at":"2026-04-26T13:55:20Z"},{"id":24958323549,"name":"gate","conclusion":null,"run_started_at":"2026-04-26T13:55:06Z","updated_at":"2026-04-26T13:55:09Z"},{"id":24958323544,"name":"CodeQL","conclusion":"success","run_started_at":"2026-04-26T13:55:06Z","updated_at":"2026-04-26T13:55:27Z"},{"id":24958322995,"name":"Code Quality: PR #613","conclusion":"success","run_started_at":"2026-04-26T13:55:04Z","updated_at":"2026-04-26T13:56:54Z"},{"id":24958318853,"name":".github/workflows/github-settings-drift.yml","conclusion":"failure","run_started_at":"2026-04-26T13:54:52Z","updated_at":"2026-04-26T13:54:52Z"},{"id":24958318721,"name":"Automatic Dependency Submission (NuGet)","conclusion":"success","run_started_at":"2026-04-26T13:54:51Z","updated_at":"2026-04-26T13:55:36Z"},{"id":24958316252,"name":"Automatic Dependency Submission (NuGet)","conclusion":"success","run_started_at":"2026-04-26T13:54:43Z","updated_at":"2026-04-26T13:55:23Z"},{"id":24958313052,"name":"gate","conclusion":null,"run_started_at":"2026-04-26T13:54:32Z","updated_at":"2026-04-26T13:54:36Z"},{"id":24958313042,"name":"CodeQL","conclusion":null,"run_started_at":"2026-04-26T13:54:32Z","updated_at":"2026-04-26T13:54:44Z"},{"id":24958313038,"name":"scorecard","conclusion":"success","run_started_at":"2026-04-26T13:54:32Z","updated_at":"2026-04-26T13:55:08Z"},{"id":24958312685,"name":".github/workflows/github-settings-drift.yml","conclusion":"failure","run_started_at":"2026-04-26T13:54:31Z","updated_at":"2026-04-26T13:54:31Z"},{"id":24958312663,"name":"Code Quality: Push on main","conclusion":"success","run_started_at":"2026-04-26T13:54:31Z","updated_at":"2026-04-26T13:56:16Z"},{"id":24958285907,"name":"Copilot code review","conclusion":"success","run_started_at":"2026-04-26T13:53:13Z","updated_at":"2026-04-26T13:54:43Z"},{"id":24958285505,"name":"gate","conclusion":null,"run_started_at":"2026-04-26T13:53:12Z","updated_at":"2026-04-26T13:53:15Z"}]}],"scope_coverage":{"has_read_org":true,"has_admin_org":false,"covered":["copilot-seats","actions-runs-per-run-timing"],"missing_requires_admin_org":["actions-billing","packages-billing","shared-storage-billing"]}} From a7fb1dbd47b020ffb56e0a63935dc70dd7000aec Mon Sep 17 00:00:00 2001 From: Aaron Stainback Date: Sun, 26 Apr 2026 10:29:43 -0400 Subject: [PATCH 2/3] fix(budget): MD012 trailing blank line in latest-report.md + heredoc template CI markdownlint flagged docs/budget-history/latest-report.md:84 with MD012 multiple-consecutive-blanks. Root cause was the heredoc template in tools/budget/daily-cost-report.sh having a blank line before EOF, which produced \n\n termination on every regenerated report. Fix removes the blank line in the heredoc and strips the trailing blank from the materialized file. Single-trailing-newline convention restored. Co-Authored-By: Claude Opus 4.7 --- docs/budget-history/latest-report.md | 1 - tools/budget/daily-cost-report.sh | 1 - 2 files changed, 2 deletions(-) diff --git a/docs/budget-history/latest-report.md b/docs/budget-history/latest-report.md index 0c60de6c..d385a3ad 100644 --- a/docs/budget-history/latest-report.md +++ b/docs/budget-history/latest-report.md @@ -80,4 +80,3 @@ Caveats - Wrapper: `tools/budget/daily-cost-report.sh` (this run) - Capture script: `tools/budget/snapshot-burn.sh` - Projection script: `tools/budget/project-runway.sh` - diff --git a/tools/budget/daily-cost-report.sh b/tools/budget/daily-cost-report.sh index b55c28f3..68d1b471 100755 --- a/tools/budget/daily-cost-report.sh +++ b/tools/budget/daily-cost-report.sh @@ -131,7 +131,6 @@ $projection - Wrapper: \`tools/budget/daily-cost-report.sh\` (this run) - Capture script: \`tools/budget/snapshot-burn.sh\` - Projection script: \`tools/budget/project-runway.sh\` - EOF echo "==> wrote $report_path" From 228ccbfd56dd2bf409e97421e3d4cf228e938d00 Mon Sep 17 00:00:00 2001 From: Aaron Stainback Date: Sun, 26 Apr 2026 10:37:21 -0400 Subject: [PATCH 3/3] fix(budget): strip absolute path from latest-report.md evidence-source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copilot review on PR #615 flagged P1 — the auto-generated latest-report.md was emitting an absolute filesystem path (`/Users/acehack/Documents/src/repos/Zeta/docs/budget-history/snapshots.jsonl`) leaking the generator's machine/username and breaking reproducibility for other clones. Fix: strip the repo-root prefix in tools/budget/project-runway.sh emit using bash parameter expansion (`${file#"$repo_root"/}`). The displayed evidence path is now repo-relative (`docs/budget-history/snapshots.jsonl`). When users override via --file with an external path, the absolute path is preserved (correct — they're naming a file outside the repo). Regenerated latest-report.md to apply the fix to the materialized report. Co-Authored-By: Claude Opus 4.7 --- docs/budget-history/latest-report.md | 6 +++--- tools/budget/project-runway.sh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/budget-history/latest-report.md b/docs/budget-history/latest-report.md index d385a3ad..673a56a6 100644 --- a/docs/budget-history/latest-report.md +++ b/docs/budget-history/latest-report.md @@ -1,7 +1,7 @@ # Latest cost projection — auto-generated -**Generated:** `2026-04-26T13:57:09Z` -**Factory git SHA:** `744e268dd6f57ba230deab8d77616ae19e38cf2f` +**Generated:** `2026-04-26T14:37:11Z` +**Factory git SHA:** `a7fb1dbd47b020ffb56e0a63935dc70dd7000aec` **Source:** `tools/budget/daily-cost-report.sh` (wraps snapshot-burn.sh + project-runway.sh) This file is **OVERWRITTEN** on each daily run. Historical snapshots live in @@ -16,7 +16,7 @@ can be reconstructed from any snapshot subset via `tools/budget/project-runway.s Budget projection — three-repo-split Stages 1-4 ================================================ -Evidence source: /Users/acehack/Documents/src/repos/Zeta/docs/budget-history/snapshots.jsonl +Evidence source: docs/budget-history/snapshots.jsonl Samples (N): 1 First snapshot: 2026-04-26T13:57:01Z Latest snapshot: 2026-04-26T13:57:01Z diff --git a/tools/budget/project-runway.sh b/tools/budget/project-runway.sh index 243e7d4c..53821236 100755 --- a/tools/budget/project-runway.sh +++ b/tools/budget/project-runway.sh @@ -214,7 +214,7 @@ cat <