budget: capture first cost snapshot + bootstrap latest-report.md (task #287)#615
budget: capture first cost snapshot + bootstrap latest-report.md (task #287)#615
Conversation
…#287 sub-step 2 partial) 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.
There was a problem hiding this comment.
Pull request overview
Bootstraps the budget-monitoring visibility surfaces by committing the first captured cost snapshot and the corresponding generated “latest” markdown report under docs/budget-history/.
Changes:
- Add initial
docs/budget-history/snapshots.jsonlentry (first captured snapshot). - Add generated
docs/budget-history/latest-report.mdfor quick “cat-to-view” cost/runway status.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| docs/budget-history/snapshots.jsonl | Adds the first append-only JSONL snapshot used as projection input. |
| docs/budget-history/latest-report.md | Adds the initial auto-generated markdown report derived from the snapshot/projection scripts. |
…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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
docs/budget-history/snapshots.jsonl:2
- P0:
snapshots.jsonlcurrently has an extra blank line after the JSON object.project-runway.shusestail -n 1to read the last snapshot; with a trailing empty line it will try to parse an empty string withjq, producing errors and potentially invalid projection output. Remove the trailing blank line (ensure the file is strict 1-JSON-object-per-line JSONL with no empty lines).
{"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"]}}
| ## 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. |
There was a problem hiding this comment.
The report text says “Caveats in the projection text below”, but the projection (including the caveats section) appears above this “How to read this” section. Also, N \geq 3 renders literally inside inline code in Markdown; consider using N >= 3 (or N ≥ 3) so it reads correctly without LaTeX rendering.
| - **`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. | |
| - **`Per-PR Actions ms (naive)`** — rolling-window estimate of per-merged-PR Actions cost. Caveats in the projection text above; treat as proxy until `N >= 3` cumulative snapshots exist. |
…ation on row 14:51:40Z Two #621 review threads addressed in single edit on row 14:51:40Z: 1. **Codex P2 (NM59qX...)**: row's summary listed #618 in the merged set ('#615/#617/#618/#620/#596 merged') but #618 was CLOSED/superseded by #620, not merged. Fixed: summary now reads '#615/#617/#620/#596 merged + #618 closed/superseded by #620'. Item (4) also corrected to clarify only #617 merged at 14:38Z; #618 became sibling-DIRTY post-merge and was superseded. 2. **Copilot P1 xref (NM59qX...)**: row claimed Otto-349 lineage memory 'landed as <filename> user-scope + MEMORY.md indexed' but the file isn't in-repo (the in-repo memory/MEMORY.md does exist; the lineage file does not). Fixed: explicit clarification that the file landed at user-scope per CLAUDE.md memory layout; the user-scope memory store is distinct from in-repo memory/ — both exist by design. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ad sweep) (#621) * tick-history: 14:51:40Z — multi-tick consolidated burst row (5 PRs merged + #602 7-of-9 threads resolved) Tick-history was 41min dark (last row 14:10:55Z); per the heartbeat-never-dark discipline + Otto-2026-04-26 hour-bundle pattern composed with Otto-275-YET burst-discipline, landing one consolidated row at the natural stopping point rather than 5 sibling-DIRTY per-tick PRs. Coverage: Otto-349 lineage memory, Otto-275-YET refinement, #615 P1 privacy fix, #617+#618 markdownlint fixes, #620 clean-reapply, #596 review-fix (5 threads resolved + merged), #602 review-fix (7 of 9 threads resolved, 2 math threads deferred to task #286/Amara), Aaron's amara-files query, task #289 filed for #132 drain. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(tick-history): correct #618 status + Otto-349 user-scope clarification on row 14:51:40Z Two #621 review threads addressed in single edit on row 14:51:40Z: 1. **Codex P2 (NM59qX...)**: row's summary listed #618 in the merged set ('#615/#617/#618/#620/#596 merged') but #618 was CLOSED/superseded by #620, not merged. Fixed: summary now reads '#615/#617/#620/#596 merged + #618 closed/superseded by #620'. Item (4) also corrected to clarify only #617 merged at 14:38Z; #618 became sibling-DIRTY post-merge and was superseded. 2. **Copilot P1 xref (NM59qX...)**: row claimed Otto-349 lineage memory 'landed as <filename> user-scope + MEMORY.md indexed' but the file isn't in-repo (the in-repo memory/MEMORY.md does exist; the lineage file does not). Fixed: explicit clarification that the file landed at user-scope per CLAUDE.md memory layout; the user-scope memory store is distinct from in-repo memory/ — both exist by design. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Summary
Ran the just-merged
tools/budget/daily-cost-report.shon main to capture the first cost snapshot.docs/budget-history/latest-report.mdnow exists as a glanceable Aaron-decision surface (catto see costs in <2 seconds).First snapshot summary
Test plan
snapshots.jsonlhas 1 valid JSON linelatest-report.mdrendered cleanlyComposes with