Conversation
* ts(slice-13, wip 1/N): port git/push-with-retry (.sh→.ts) First script of slice 13. Thin retry wrapper over `git push` for transient GitHub 5xx errors. DST-ACCEPTED-BOUNDARY classification preserved (Otto-168, network I/O + retry-on-failure). Byte-equivalent on env-validation paths. Network-dependent retry path tested locally only (success-path requires real push; 5xx- retry-path requires real 5xx). Mechanical changes: - bash `[[ =~ $int_re ]]` regex → POSITIVE_INT_RE.test() - bash mktemp + tee + grep tmp file → spawnSync stderr-capture + TRANSIENT_5XX_RE.test() against captured string - bash `set +e; git push; exit_code=$?; set -e` → result.status ?? 1 - bash `sleep $backoff` → Atomics.wait(view, 0, 0, seconds * 1000) synchronous-busy-wait (preserves the script's synchronous flow that the bash original assumes; async setTimeout would change exit-code semantics) - Exponential backoff doubling preserved (`backoff *= 2`) Behavioural note: bash uses `tee` for live + capture; TS uses spawnSync for capture-then-replay. UX difference invisible for typical git-push runtimes (seconds). Documented in port header. Lint-clean: bun --bun tsc --noEmit + eslint strictTypeChecked + sonarjs all pass on the new file. * trajectory(ts-bun): slice 13 audit substrate + RESUME tracker - slice-audits.md: append slice-13 audit (1 port — git/push-with-retry). - RESUME.md: bump slice-11-merged → slice-12-merged (#885, commit cfb5964). Milestone 33 → 34 (33 ported + 1 in-flight in slice-13). Bucket B 10 → 9. Bucket D ported list grew to 33 entries.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d73405f937
ℹ️ 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".
There was a problem hiding this comment.
Pull request overview
Ports one Git helper script from Bash to TypeScript/Bun as part of the ongoing TS/Bun migration trajectory, and updates the migration audit/trajectory docs to record slice 13.
Changes:
- Add
tools/git/push-with-retry.ts, a Bun/TS port of the existinggit pushtransient-5xx retry wrapper. - Record Slice 13 audit details in
slice-audits.md. - Update the trajectory dashboard (
RESUME.md) milestone/bucket counts to reflect the new in-flight port.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| tools/git/push-with-retry.ts | New TS/Bun implementation of the git push retry wrapper (transient 5xx handling + env validation). |
| docs/trajectories/typescript-bun-migration/slice-audits.md | Adds Slice 13 audit entry describing the port and equivalence notes. |
| docs/trajectories/typescript-bun-migration/RESUME.md | Updates trajectory status/milestones and Bucket B inventory to reflect slice 13. |
Five real findings from automated reviewers: Codex P2 — Guard null stderr from failed spawnSync: When spawnSync cannot start git (ENOENT etc.), result.stderr is null at runtime even though @types/node claims `string`. Added `?? ""` guard with eslint-disable + rationale comment so the downstream regex match is safe. Copilot P1 — spawnSync failure modes weren't being classified: Added classifySpawnFailure helper handling 4 cases: - status !== null: passthrough - error.code === ENOENT: return 127 (matches bash command-not-found) - other error.code: return 1 with error message - signal !== null: return 1 with signal name - otherwise: return 1 with "no exit code" note Each case writes a contextual message to stderr. Copilot P1 — sleepSeconds comment claimed nonexistent fallback: Comment claimed "falls back to a tight loop if not available" but no fallback was implemented. Reframed comment to state the actual Bun-vs-Node main-thread Atomics.wait behavior. No fallback needed: the shebang pins Bun, which supports main-thread Atomics.wait. Copilot P2 — eslint-disable lacked justification: Added rationale comment ABOVE the disable directive (not below — eslint-disable-next-line applies to the literal next line, so the rationale must precede the directive, not split it from the code). Same security posture as bash `git push "$@"`. Copilot P1 — line-count drift in audit: Audit said "129 → 138 lines" but the new TS file is now 184 lines after the spawn-failure classification. Updated the audit.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3d48e2d651
ℹ️ 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".
| const result = spawnSync("git", ["push", ...args], { | ||
| stdio: ["inherit", "inherit", "pipe"], | ||
| encoding: "utf8", | ||
| maxBuffer: 64 * 1024 * 1024, |
There was a problem hiding this comment.
Avoid aborting pushes on stderr buffer overflow
Using spawnSync with maxBuffer: 64 * 1024 * 1024 introduces a hard cap that the previous shell implementation did not have (tee wrote to a temp file). git push writes progress to stderr, so large/verbose pushes can exceed 64 MiB; when that happens spawnSync returns with a spawn error (for example ENOBUFS) and the wrapper reports a generic failure instead of propagating the real git push outcome or retrying based on the remote error. This turns valid long-running pushes into deterministic wrapper failures.
Useful? React with 👍 / 👎.
…tion (#894) * ts(slice-14, wip 1/N): port budget/snapshot-burn (.sh→.ts) First script of slice 14. Captures point-in-time LFG cost/burn snapshot via gh API + appends one JSON line to `docs/budget-history/snapshots.jsonl`. Composes with project-runway + daily-cost-report (Aaron's #287 visibility deadline). Byte-equivalent on argument-validation paths (--note, --help). Live --dry-run verified against GitHub API: produces snapshot with all the same fields as bash original. Mechanical changes: - bash `gh api ... | jq` pipelines → ghJson helper wrapping spawnSync - bash `mapfile` workaround (while-read for macOS bash 3.2 compat) → straightforward TS for-loop - bash heredoc `jq -n` snapshot composition → typed Snapshot interface + JSON.stringify - bash JSONL append (`printf '%s\n' >> "$out"`) → appendFileSync - Per-repo aggregation extracted into aggregateTimings + summarizePulls helpers under cognitive-complexity threshold - Optional fields elided via spread+conditional for exactOptionalPropertyTypes compliance Lint-clean: bun --bun tsc --noEmit + eslint strictTypeChecked + sonarjs all pass. The new lint (tsc tools) gate from #890 will validate this in CI. * trajectory(ts-bun): slice 14 audit substrate + RESUME tracker - slice-audits.md: append slice-14 audit (1 port — budget/snapshot-burn). Also flip slice-13 from PR-pending to merged-with-PR-number. - RESUME.md: bump slice-12-merged → slice-13-merged (#892, commit e9dc894). Milestone 34 → 35 (34 ported + 1 in-flight in slice-14). Bucket B 9 → 8. Bucket D ported list grew to 34 entries.
…gration (#896) * ts(slice-15, wip 1/N): port peer-call/grok (.sh→.ts) First script of slice 15. Otto's harness-side caller for invoking Grok via cursor-agent as a peer reviewer. Opens the peer-call cluster — sibling ports (gemini + codex) follow the same shape. Byte-equivalent on argument-validation paths. LLM-response equivalence is non-deterministic; only structural/UX equivalence verified. Mechanical changes: - bash arg-parse loop → classifyFlag helper + MutableArgState (main parseArgs stays under cognitive-complexity 15) - bash 'eval "$context_cmd" 2>&1 | head -c 20000' → spawnSync('/bin/sh', ['-c', contextCmd]) + slice to CTX_HEAD_BYTES - bash file-head read → readFileSync + Buffer.subarray - bash 'cursor-agent --print ...' → spawnSync with stdio:'inherit' (preserves live LLM-streaming UX) - PREAMBLE preserved verbatim (Otto's contribution to the four-ferry consensus protocol convention) - eslint-disable for no-os-command-from-path placed on the literal next line per directive-placement pattern from #892 review Lint-clean: bun --bun tsc --noEmit + eslint strictTypeChecked + sonarjs all pass. The lint (tsc tools) gate from #890 will validate this in CI. * trajectory(ts-bun): slice 15 audit substrate + RESUME tracker EOF
#907) Last git-cluster port: tools/git/batch-resolve-pr-threads.{sh→ts}. Slice 13 (push-with-retry) + slice 20 (batch-resolve-pr-threads) together complete the git-cluster. Behavioural improvements over bash original (deliberate, not drift): - jq pipelines replaced with native JSON parse + array operations; drops jq from runtime deps (only `gh` required now). - Pattern classification via TypeScript pattern arrays + `.some()` rather than bash `for pat in ; do [[ ]]; done` — same semantics, more type-safe with `Classification = "dangling-ref" | "name-attribution" | "unknown"` discriminated union. - ResolveError discriminated record with `stage: "reply" | "resolve"` so failure mode is visible in error messages. Byte-equivalence verified on this repo state: - Three argument-validation paths byte-equivalent (no args / bad pr-number / unknown second arg) — same exit code + same message. - Live dry-run on PR #902 (4 unresolved threads) byte-equivalent — empty diff against bash original. - Apply mode not exercised (would mutate live PR state); code path verified by inspection. All bash safety rails from PR #199 (Copilot/Codex) preserved: positive-integer validation, exact-`--apply` second-arg check, GraphQL errors array inspection, null-pullRequest detection, paginated thread fetch (50 per page) + paginated per-thread comments (50 per thread with truncation warning), positional -F args (avoids parameter-expansion-quote pitfall), printf-not-echo for review-comment bodies (handled via TypeScript string operations). Bucket B 2 → 1 (only tools/pr-preservation/archive-pr.sh 674L remains; bash+Python mix — slice 21). Composes with: - tools/git/push-with-retry.ts (slice 13, #892) — git-cluster siblings - docs/trajectories/typescript-bun-migration/RESUME.md updated - docs/trajectories/typescript-bun-migration/slice-audits.md slice 20 audit Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Summary
Slice 13 of the TS/Bun migration trajectory — one port:
tools/git/push-with-retry.{sh→ts}(git pushretry wrapper for transient GitHub 5xx; thin-wrapper-over-existing-CLI exemption perdocs/POST-SETUP-SCRIPT-STACK.md)DST classification preserved
ACCEPTED_BOUNDARY (external network I/O + retry-on-failure) per
docs/research/dst-accepted-boundaries.md§3 and Otto-168. Header comment carries the full registration context.Audit substrate
See
docs/trajectories/typescript-bun-migration/slice-audits.mdSlice 13 entry.Mechanical port notes
[[ =~ $int_re ]]regex →POSITIVE_INT_RE.test().mktemp+tee+grepon tmp file →spawnSyncwith stderr captured in-memory;TRANSIENT_5XX_RE.test()against captured string.set +e; git push; exit_code=$?; set -e→ straightforwardresult.status ?? 1.sleep $backoff→Atomics.wait(view, 0, 0, seconds * 1000)synchronous-busy-wait (preserves the script's synchronous flow; async setTimeout would change exit-code semantics).backoff *= 2).Behavioural note vs bash
Bash uses
teefor live-stream-AND-capture; TS usesspawnSyncfor capture-then-replay. UX difference invisible for typical git-push runtimes (seconds); for long pushes stderr appears in batches per attempt rather than streaming live. Documented in port header.Test plan
bun --bun tsc --noEmit -p tsconfig.jsonclean (the newlint (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 on the new file.GIT_PUSH_MAX_ATTEMPTS=fooandGIT_PUSH_BACKOFF_S=-1both produce identical messages and exit code 2).Trajectory
docs/trajectories/typescript-bun-migration/RESUME.md.🤖 Generated with Claude Code