Conversation
…tion * ts(slice-18, wip 1/N): port budget/daily-cost-report (.sh→.ts) Daily cost-monitoring entry point. Wraps snapshot-burn.sh + project-runway.sh and writes human-readable projection to docs/budget-history/latest-report.md (visibility surface for Aaron's #287 deadline). Note: this wrapper still spawns the bash siblings (snapshot-burn.sh + project-runway.sh), NOT the TS port — the bash versions are the soak-period reference until they retire. Once project-runway is also TS-ported, this wrapper can switch to spawning the .ts versions. Mechanical changes: - bash arg-parse → parseArgs with ParsedArgs | ArgError | help - bash 'cat > "$report_path" <<EOF...EOF' heredoc → buildReport() template literal returning the markdown string - bash subshell command capture ($(...)) → spawnSync with stdio ['inherit', 'pipe', 'pipe'] for project-runway combined output - bash heredoc concat across multi-line → resolved via separate argsSuffix variable (sonarjs no-nested-template-literals) - exit codes 0/1/2 preserved verbatim per bash original Lint-clean: tsc --noEmit + eslint strictTypeChecked + sonarjs all pass. Argument-validation byte-equivalent. Trajectory: 39 ports total after merge, 4 Bucket B files remain (1 budget project-runway + 1 git/batch-resolve + 1 pr-preservation + 1 in-flight = 4 remaining).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7bcae924c8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ex P2 Codex P2: existsSync returns true for directories and other non-regular paths; the bash original uses -f which checks regular-file existence. If snapshots.jsonl were ever a directory, existsSync would skip the bootstrap branch and the wrapper would try to spawn project-runway.sh against a non-file. Switched to statSync.isFile() with try/catch fallback to false.
Pull Request is not mergeable
There was a problem hiding this comment.
Pull request overview
Ports the tools/budget/daily-cost-report wrapper from bash to TypeScript/Bun as part of the ongoing TS/Bun migration, keeping behavior aligned with the existing bash entrypoint while still delegating to the bash “primitive” scripts during the soak period.
Changes:
- Add
tools/budget/daily-cost-report.tsas the new daily cost-report entrypoint. - Implement CLI parsing (
--dry-run,--skip-snapshot,--help) and orchestration ofsnapshot-burn.sh+project-runway.sh. - Generate/overwrite
docs/budget-history/latest-report.mdwith the latest projection output.
…ull-stream guards + header phrasing Three Copilot findings on #901: P0 — spawnSync launch failures collapsed into exitCode 1: Added classifySpawnFailure helper (4-case: status set / ENOENT → 127 / signal / other) reused from PRs #887, #898, #900. Both runSnapshotBurn and runProjectRunway now report a contextual note when the child can't start (e.g., 'snapshot-burn.sh: command not found on PATH (ENOENT)'). P0 — null stdout/stderr could yield 'nullnull': When a child fails to start, result.stdout / result.stderr can be null even with encoding: 'utf8'. Guarded with `?? ''` in runProjectRunway so the projection block doesn't end up as the literal string 'nullnull'. P2 — Header comment phrasing: Reworded 'snapshot-burn.sh ported in #894' to 'TS port snapshot-burn.ts landed in #894 but this wrapper still spawns the .sh during the soak period' to avoid implying the .sh file itself is the ported artifact.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…ary-condition - Extracted runSnapshotStep + runProjectionStep helpers to drop main() under cognitive-complexity 15. - Added eslint-disable on stdout/stderr ?? '' guards (typings claim string when encoding is set, but the runtime can return null when spawn fails — same pattern as #898). Lint clean: tsc + eslint strictTypeChecked + sonarjs all pass.
… per Copilot P1 Copilot caught: concatenating result.stdout + result.stderr does NOT preserve the original chronological ordering of merged streams. The bash original $(... 2>&1) merges at the kernel pipe level — if project-runway.sh emits warnings on stderr while writing success output to stdout, the messages interleave correctly. Switched to /bin/bash -c 'path 2>&1' so the merge happens shell-side (matches bash original semantics). Single stdout pipe = correct ordering. result.stderr is now unused (the inherit pipe still receives nothing).
…tion (#902) Closes the budget cluster: snapshot-burn (slice 14) + daily-cost-report (slice 18) + project-runway (this slice) are now all TS. Once this lands, daily-cost-report.ts can switch from spawning project-runway.sh to project-runway.ts. Behavioural improvements over bash original (deliberate, not drift): - File-existence check uses statSync().isFile() + try/catch rather than existsSync — bash `-f` rejects directories, existsSync accepts them (slice-18 mirror). - JSONL parsing is native (readFileSync + split + JSON.parse) rather than per-line jq spawn-out — projection script reads already-persisted JSON, so jq is a heavy dependency for what is structurally a typed reduce. snapshot-burn.ts still needs gh api for capture; this is projection only. - requireInt validation matches bash `case '$val' in ''|*[!0-9]*) ...` with TS `requireInt(flag, val)` returning `number | ArgError` — same exit code 2, same error wording (Codex P2 NM59qF00 + NM59qH2H, Copilot P1 NM59qGJ- on the bash original). Byte-equivalence verified on this repo state: diff <(bun tools/budget/project-runway.ts) \ <(./tools/budget/project-runway.sh) # empty diff <(bun tools/budget/project-runway.ts --json) \ <(./tools/budget/project-runway.sh --json) # empty Error paths verified equivalent: --stages abc → exit 2 with matching message; --file <missing> → exit 1; --bogus → exit 2. Tools used: tsc --noEmit clean; eslint clean per the existing tsc-tools CI gate (#890). Composes with: - tools/budget/snapshot-burn.ts (slice 14, #894) - tools/budget/daily-cost-report.ts (slice 18, #901) - docs/trajectories/typescript-bun-migration/RESUME.md - docs/trajectories/typescript-bun-migration/slice-audits.md Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…sure (#903) Per the consolidated-row-pattern (rows from 03:41Z + 05:01Z arc closures): when a session lands many small commits across multiple ticks, a single consolidated row summarizing the arc is more signal-dense than N minimal rows. Covers the slices 15-19 arc that landed after the 05:43Z slice-14 row: - #896 slice 15 (grok.ts) — peer-call cluster opens - #898 slice 16 (gemini.ts) — peer-call sibling - #899 backport to grok from #898 review-cycle findings - #900 slice 17 (codex.ts) — peer-call cluster closes - #897 B-0107 row — CodeQL dismissal pattern - #901 slice 18 (daily-cost-report.ts) — budget wrapper - #902 slice 19 (project-runway.ts) — budget cluster closes (in flight) Three new substrate observations recorded for future-Otto: - sibling-port-cost decreases monotonically (round-2/3 fixes bake into later siblings proactively) - kernel-pipe vs JS-space stream interleaving distinction (bash `2>&1` merges shell-side; `result.stdout + result.stderr` in JS does not preserve chronological ordering) - fix-the-bug + file-the-row + implement-the-row + closeout pattern is the durable shape (B-0106 + B-0107 are both worked examples) Cron 98fc7424 still armed. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…s updates (#904) * docs(ts-bun-migration): backfill slice-16/17/18 audit entries + status updates Backfills the slice-audits.md gap surfaced during the slice-19 close: slices 16, 17, 18 had merged but their audit entries were missing. Also flips slice 15 + slice 19 entries from "PR pending" to merged status (PR # + commit SHA). Slice 16 (peer-call/gemini, #898): three rounds of review-cycle fixes documented (round 1 exit-code 1 + commandAvailable PATH check; round 2 /bin/bash -c + shell-side truncation + ReadHeadResult; round 3 stderr concat for parse-error preservation). Slice 17 (peer-call/codex, #900): closes peer-call cluster. Documents the sibling-port-cost-decreases-monotonically pattern — slice 17 shipped 357 lines in a single commit with all known fixes pre-baked. Slice 18 (budget/daily-cost-report, #901): first wrapper-class port. Documents the kernel-pipe vs JS-space stream-ordering distinction that became substrate observation in the 07:21Z tick row. No code changes. Doc-only. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(ts-bun-audits): address #904 review threads — MD038, persona attribution, monotonic deltas Round-1 fixes for PR #904: - MD038 markdownlint failure (line 451) — slice-18 entry had nested backticks (` "${path}" 2>&1 ` inside an outer code span) that confused markdown's parser. Rephrased prose to avoid embedding backticks inside backticks. - Persona-name attribution in current-state doc (Copilot P1, twice) — slice-16 + slice-17 file descriptions used "Otto's harness-side caller for invoking ..." Per the .github/copilot-instructions.md no-name-attribution rule for current-state docs, switched to role-ref "the harness-side caller for invoking ...". Slice-15 pre-existing wording on main left alone (separate commit if needed). - Non-monotonic Bucket B deltas (Codex P2) — backfilled rows for slices 16/17/18 had stale numbers (9→8, 8→7, 7→6) that were inconsistent with the pre-existing slice-15 row's "8 → 7". Recomputed to monotonic sequence: slice 16 = 7→6, slice 17 = 6→5, slice 18 = 5→4. - Slice-19 outcome wording (Copilot) — "Once this lands" → "Now that this has landed" since slice 19 already merged at commit bfdadd9. Doc-only. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…rt pattern (#906) Captures the substrate finding from PR #901 (slice 18 daily-cost-report) Copilot P1 round 2 catch as durable memory. Per Otto-363 substrate-or-it-didn't-happen: this pattern was only living in commit messages and review threads — now indexed + reachable via memory/. Rule: when porting bash `$(... 2>&1)` to spawnSync, merge stdout+stderr at the kernel pipe boundary via `/bin/bash -c "<cmd> 2>&1"`, NOT in JS-space via `result.stdout + result.stderr` concat. Why: kernel pipe preserves the child's chronological write ordering across both streams. JS-space concat glues two pre-segregated buffers in a fixed order, losing the original interleaving. How to apply: documented in the memory file with edge cases (shell-parse-error stacking, defensive `?? ""`, `/bin/bash` over `/bin/sh` for bash-only features). Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Summary
Slice 18 of the TS/Bun migration:
tools/budget/daily-cost-report.{sh→ts}(daily cost-monitoring entry point; wraps snapshot-burn.sh + project-runway.sh + writes report todocs/budget-history/latest-report.md)This wrapper still spawns the bash siblings (snapshot-burn.sh + project-runway.sh), NOT the TS port — the bash versions are the soak-period reference until they retire. Once project-runway is also TS-ported, this wrapper can switch to spawning the .ts versions.
Test plan
bun --bun tsc --noEmitclean (lint-tsc-tools gate from ci(B-0106): add lint-tsc-tools gate job — tsc --noEmit on tsconfig.json #890 will validate this in CI).eslintclean.--help→ 0;--bogus→ exit 2.Trajectory
After-merge: 39 ports total, 4 Bucket B files remain:
tools/budget/project-runway.sh(297 lines, projection math)tools/git/batch-resolve-pr-threads.sh(390 lines, gh GraphQL + pagination)tools/pr-preservation/archive-pr.sh(674 lines, largest)🤖 Generated with Claude Code