-
Notifications
You must be signed in to change notification settings - Fork 1
tools(budget): daily-cost-report.sh wrapper — visibility surface scaffold for task #287 #611
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,138 @@ | ||||||||||||||
| #!/usr/bin/env bash | ||||||||||||||
| # tools/budget/daily-cost-report.sh — daily cost-monitoring entry point. | ||||||||||||||
| # | ||||||||||||||
| # Wraps snapshot-burn.sh + project-runway.sh + writes the human-readable | ||||||||||||||
| # projection to docs/budget-history/latest-report.md. Designed to be the | ||||||||||||||
| # single entry point for the daily /schedule remote-trigger routine | ||||||||||||||
| # (task #287 visibility surface for the human maintainer). | ||||||||||||||
| # | ||||||||||||||
| # Why this exists (the human maintainer 2026-04-26): | ||||||||||||||
| # "we need to get that resource/costs monitoring done in the next | ||||||||||||||
| # few days ... so we can see the costs" | ||||||||||||||
| # | ||||||||||||||
| # The two existing tools (snapshot-burn.sh + project-runway.sh) are | ||||||||||||||
| # correct primitives but require manual orchestration to produce a | ||||||||||||||
| # glanceable surface. This wrapper produces docs/budget-history/ | ||||||||||||||
| # latest-report.md so the maintainer can `cat` / preview that one | ||||||||||||||
| # file to see runway state. | ||||||||||||||
| # | ||||||||||||||
| # Usage: | ||||||||||||||
| # tools/budget/daily-cost-report.sh # full run, writes report | ||||||||||||||
| # tools/budget/daily-cost-report.sh --dry-run # snapshot dry-run; still | ||||||||||||||
| # # writes the report from | ||||||||||||||
| # # whatever snapshots exist | ||||||||||||||
| # tools/budget/daily-cost-report.sh --skip-snapshot # only regenerate the | ||||||||||||||
| # # report from existing | ||||||||||||||
| # # snapshots (for testing) | ||||||||||||||
| # | ||||||||||||||
| # Exit codes: | ||||||||||||||
| # 0 success | ||||||||||||||
| # 1 if any wrapped step fails (snapshot-burn.sh or project-runway.sh) | ||||||||||||||
| # 2 on CLI-argument errors | ||||||||||||||
| # | ||||||||||||||
| # Composes with: | ||||||||||||||
| # - tools/budget/snapshot-burn.sh (data-capture primitive) | ||||||||||||||
| # - tools/budget/project-runway.sh (projection primitive) | ||||||||||||||
| # - docs/budget-history/README.md (methodology + field reference) | ||||||||||||||
| # - docs/budget-history/snapshots.jsonl (append-only data store) | ||||||||||||||
| # - docs/budget-history/latest-report.md (the visibility surface this | ||||||||||||||
| # wrapper produces; OVERWRITTEN each run, not append-only) | ||||||||||||||
|
|
||||||||||||||
| set -uo pipefail | ||||||||||||||
|
|
||||||||||||||
| snapshot_args="" | ||||||||||||||
| skip_snapshot="false" | ||||||||||||||
|
|
||||||||||||||
| while [ $# -gt 0 ]; do | ||||||||||||||
| case "$1" in | ||||||||||||||
| --dry-run) snapshot_args="--dry-run"; shift ;; | ||||||||||||||
| --skip-snapshot) skip_snapshot="true"; shift ;; | ||||||||||||||
| -h|--help) | ||||||||||||||
| sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; | ||||||||||||||
| *) | ||||||||||||||
| echo "error: unknown argument '$1'" >&2 | ||||||||||||||
| exit 2 ;; | ||||||||||||||
| esac | ||||||||||||||
| done | ||||||||||||||
|
|
||||||||||||||
| script_dir="$(cd "$(dirname "$0")" && pwd)" | ||||||||||||||
| repo_root="$(cd "$script_dir/../.." && pwd)" | ||||||||||||||
| report_path="$repo_root/docs/budget-history/latest-report.md" | ||||||||||||||
| snapshots_path="$repo_root/docs/budget-history/snapshots.jsonl" | ||||||||||||||
|
|
||||||||||||||
| # Step 1 — capture snapshot (unless skipped) | ||||||||||||||
| if [ "$skip_snapshot" = "false" ]; then | ||||||||||||||
| echo "==> snapshot-burn.sh $snapshot_args" | ||||||||||||||
| if [ -n "$snapshot_args" ]; then | ||||||||||||||
| "$script_dir/snapshot-burn.sh" $snapshot_args || { | ||||||||||||||
| echo "error: snapshot-burn.sh failed (exit $?)" >&2 | ||||||||||||||
| exit 1 | ||||||||||||||
| } | ||||||||||||||
| else | ||||||||||||||
| "$script_dir/snapshot-burn.sh" || { | ||||||||||||||
| echo "error: snapshot-burn.sh failed (exit $?)" >&2 | ||||||||||||||
| exit 1 | ||||||||||||||
| } | ||||||||||||||
| fi | ||||||||||||||
| else | ||||||||||||||
| echo "==> snapshot-burn.sh SKIPPED per --skip-snapshot" | ||||||||||||||
| fi | ||||||||||||||
|
|
||||||||||||||
| # Step 2 — run projection (text mode) | ||||||||||||||
| if [ ! -f "$snapshots_path" ]; then | ||||||||||||||
| echo "==> project-runway.sh SKIPPED (no snapshots yet); writing bootstrap report" | ||||||||||||||
| projection="No snapshots captured yet. The first snapshot-burn.sh run will append a baseline row to docs/budget-history/snapshots.jsonl. Once N >= 2 snapshots exist across LFG merges, projection becomes available." | ||||||||||||||
| else | ||||||||||||||
| echo "==> project-runway.sh" | ||||||||||||||
| projection="$("$script_dir/project-runway.sh" 2>&1)" || { | ||||||||||||||
| echo "error: project-runway.sh failed (exit $?)" >&2 | ||||||||||||||
|
||||||||||||||
| echo "error: project-runway.sh failed (exit $?)" >&2 | |
| status=$? | |
| if [ -n "$projection" ]; then | |
| printf '%s\n' "$projection" >&2 | |
| fi | |
| echo "error: project-runway.sh failed (exit $status)" >&2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The script always prints OK: daily cost report regenerated even if writing latest-report.md fails, because cat > "$report_path" <<EOF is not checked and set -e is not enabled. In environments with a read-only checkout, missing parent directory, or disk-full condition, this produces a false-success signal for the daily monitoring job while leaving the report stale or absent.
Useful? React with 👍 / 👎.
Copilot
AI
Apr 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: In the generated report, the bullet “Re-run with --copilot-rate to model rate changes” is ambiguous about which command to rerun (this wrapper doesn’t accept that flag). Consider spelling out tools/budget/project-runway.sh --copilot-rate ... (or documenting that the wrapper forwards flags, if you add forwarding later) to avoid user confusion.
| - **\`Copilot projected USD\`** — assumed-30-day span at the current seat count and rate. Re-run with \`--copilot-rate\` to model rate changes. | |
| - **\`Copilot projected USD\`** — assumed-30-day span at the current seat count and rate. Re-run \`tools/budget/project-runway.sh --copilot-rate ...\` to model rate changes. |
Copilot
AI
Apr 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P0: Report generation writes directly to latest-report.md via a heredoc. If the write fails (permissions/disk full) or the process is interrupted mid-write, the file can be left missing/truncated, and because set -e is not enabled this failure won’t necessarily stop the script. Write to a temp file and mv it into place (atomic on same filesystem), and ensure the write/rename failures are checked so the script exits non-zero when the report can’t be updated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: Avoid passing optional flags via an unquoted string (
$snapshot_args). Even though it’s currently only--dry-run, this pattern triggers ShellCheck (SC2086) and is brittle if more args are added later. Prefer an array (e.g.,snapshot_args=()+snapshot_args+=(--dry-run)), and invoke snapshot-burn with"$script_dir/snapshot-burn.sh" "${snapshot_args[@]}"(or no array when empty).