Conversation
…Bun migration
Last Bucket B file: tools/pr-preservation/archive-pr.{sh→ts}.
After this PR merges, **Bucket B is empty** — every file flagged
for TS port has been ported. The trajectory transitions from
"porting" phase to "soak + bash retirement" phase.
Behavioural improvements over bash original (deliberate, not drift):
- Drops Python from runtime deps entirely. Bash original was
217 lines bash + ~457 lines embedded Python (GraphQL fetcher +
Markdown formatter); TS port collapses both into single Bun
runtime. No more bash/Python boundary, no more mktemp + trap
cleanup, no `set +e` to capture Python exit code.
- Native JSON parsing replaces all jq/Python json shells.
- Generic `paginateTopLevel<T>` helper with type-safe extractor
handles the cursor loop for all 3 top-level connections
(reviewThreads/reviews/comments). `paginateThreadComments`
handles the per-thread case.
- `detectFenceMarker` preserves CommonMark §4.5 fence rules
strictly: leading-space-count ≤ 3 + no tab in prefix; closing
fence same marker char + length ≥ opener. This matches the
Python original's nuanced fence detection (Otto-241 etc).
- `yamlQuote` post-processes `JSON.stringify` output to escape
non-ASCII codepoints as \uXXXX, matching Python's
`json.dumps(ensure_ascii=True)` wire-format default. Without
this, titles with → / — would diverge from bash output.
Byte-equivalence verified on this repo state:
- 2 argument-validation paths byte-equivalent (no args / bad
PR number) — same exit code 1 + same message.
- Live archive run on PR #902 (4 threads, 2 reviews, 0 comments)
byte-equivalent EXCEPT `archived_at` (timestamp) + `archive_tool`
(.sh vs .ts — deliberate self-reference). Title with non-ASCII
chars escapes correctly via the yamlQuote fix.
All bash safety rails preserved: positive-integer PR validation,
GH_REPO env-var preference + `gh repo view` fallback, 2/3-segment
NWO parsing with Enterprise HOST validation (dot required),
slash-injection defence on owner/name, paginated GraphQL fetch
(top-level + per-thread), GraphQL `errors` array inspection,
null-pullRequest detection, idempotent archive path via
PR-<NNNN>-* glob (Otto-235), CommonMark §4.5 fence detection
(Otto-241), trailing-newline-only rstrip (preserves
two-space markdown hard-line-breaks).
Bucket B 1 → 0. Bucket C: 2 (gh-api-heavy scripts pending
maintainer decision). Bucket A: 14 (stays bash by design).
Composes with:
- tools/git/batch-resolve-pr-threads.ts (slice 20, #907) — same
GraphQL pagination shape
- docs/trajectories/typescript-bun-migration/RESUME.md updated
- docs/trajectories/typescript-bun-migration/slice-audits.md
slice 21 audit appended
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Ports the PR-preservation archiver from a bash+embedded-Python implementation to a single TypeScript/Bun script, and updates the TS/Bun migration trajectory docs to record slice 21.
Changes:
- Added
tools/pr-preservation/archive-pr.tsimplementing PR conversation archiving viagh api graphqlwith pagination and markdown formatting. - Appended slice 21 audit notes to
docs/trajectories/typescript-bun-migration/slice-audits.md. - Updated
docs/trajectories/typescript-bun-migration/RESUME.mdto reflect slice 21 in-flight status and Bucket B nearing completion.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| tools/pr-preservation/archive-pr.ts | New Bun/TS port of the PR archiving tool (GraphQL fetch + formatting + file write). |
| docs/trajectories/typescript-bun-migration/slice-audits.md | Records slice 21 audit details and equivalence notes for the new port. |
| docs/trajectories/typescript-bun-migration/RESUME.md | Updates migration dashboard status/milestones for slice 21. |
…e_ascii framing + usage-line carve-out Round-1 fixes for PR #908 (4 Copilot P1 threads): - Line-count drift (Copilot P1): I claimed "674 → 590" but actual is "674 → 806". Updated and added a one-line note explaining why TS is larger than bash (explicit type interfaces replace Python's untyped dict navigation). - ensure_ascii=True framing was wrong (Copilot P1, twice): Python's json.dumps with ensure_ascii=True does NOT emit literal `→`/`—` — it emits \uXXXX escapes. My audit prose said "Python escapes to `→`" which contradicted both the byte-equivalence goal and what yamlQuote actually implements. Rewrote to clearly say Python emits \uXXXX form (e.g. → for right-arrow, — for em-dash) and the TS yamlQuote post-processes JSON.stringify to match. - Usage-line not byte-equivalent (Copilot P1, twice): bash echoes `$0` showing the actual `./tools/...sh` path; TS hard-codes `bun tools/...ts` so the user sees the form they should run. Reframed equivalence claim to be honest: same exit code + same error-body, but usage-line script-path is intentionally NOT byte-equivalent — same carve-out as the `archive_tool` YAML self-reference. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
3 tasks
AceHack
added a commit
that referenced
this pull request
Apr 30, 2026
…+ tick-row schema + spelling drift Round-1 fixes for PR #909 (4 unresolved threads): - Codex P2: Bucket B section in RESUME.md still listed the 2 bash files as "remaining" — internal contradiction with the new "Bucket B is empty" status line. Rewrote the Bucket B section to match the empty state with cluster summary. - Copilot: tick-history column 5 had `113248b (slice-21 final; #908)` which is a merged slice commit, NOT this tick's commit. Per the documented tick-history schema, used em-dash placeholder per convention (the row's actual squash-merge SHA isn't known pre-merge). - Copilot: tick-row had a backtick code span with literal non-ASCII range characters (`[U+0080-U+FFFF]`). Replaced with English form: "replace each codepoint in the U+0080 through U+FFFF range" — matches the same MD038 anti-pattern lesson recorded in the row's own observations. - Copilot: spelling drift "bash-retirement" (status) vs "bash retirement" (milestone). Standardized on hyphenated form ("bash-retirement") matching the tick-history row. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
AceHack
added a commit
that referenced
this pull request
Apr 30, 2026
* ops(ts-bun-migration): Bucket B closeout — porting phase complete PR #908 (slice 21 — archive-pr.sh→ts) merged 2026-04-30T08:07:32Z. **Bucket B is empty.** Every file flagged for TS port has been ported. The TS+Bun migration trajectory transitions from "porting" phase to "soak + bash-retirement" phase. This commit captures the closeout: - RESUME.md status flipped to "Soak + bash-retirement phase". Milestone bumped 41→42 ports. Next concrete actions reframed for the new phase: bash retirement audit, daily-cost-report.ts switch from .sh to .ts spawn, Bucket C maintainer decision. - Tick-history row at 08:08:00Z: closeout for the slice 20+21 arc (4 review rounds across the two PRs — MD038 Unicode-in-backtick, 4 Copilot P1 doc inversions, transient mise rate-limit auto-recovery). Four observations recorded: (1) sibling-port-cost diverges between same-shape (peer-call: monotonic decrease) and cross-shape (gh-api-mutating: stays roughly constant) siblings; (2) Python `ensure_ascii=True` is the canonical "match-Python-wire-format" pattern for TS ports — the `yamlQuote` 5-line helper closes the gap (worth seeding into TS+Bun expert skill #351 alongside classifySpawnFailure and the kernel-pipe pattern); (3) MD038 anti-pattern recognized — literal Unicode characters inside backtick code spans break twice (#904 + #908); use English ("right-arrow" / "em-dash") in audit prose; (4) Bucket B closure is the natural pivot point — the trajectory has a clear endpoint and the soak+retirement phase is qualitatively different work. Cluster summary at closeout: - budget cluster (slices 14/18/19) — complete - peer-call cluster (slices 15/16/17) — complete - git cluster (slices 13/20) — complete - pr-preservation cluster (slice 21) — complete Bucket A (14 setup-script files) stays bash by design (pre-Bun bootstrap layer). Bucket C (2 gh-api-heavy files — check-github-settings-drift.sh + snapshot-github-settings.sh) remains pending maintainer decision (shell-out wrapper vs Octokit). Cron 98fc7424 still armed. Doc-only. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(ts-bun-closeout): address #909 review threads — Bucket B section + tick-row schema + spelling drift Round-1 fixes for PR #909 (4 unresolved threads): - Codex P2: Bucket B section in RESUME.md still listed the 2 bash files as "remaining" — internal contradiction with the new "Bucket B is empty" status line. Rewrote the Bucket B section to match the empty state with cluster summary. - Copilot: tick-history column 5 had `113248b (slice-21 final; #908)` which is a merged slice commit, NOT this tick's commit. Per the documented tick-history schema, used em-dash placeholder per convention (the row's actual squash-merge SHA isn't known pre-merge). - Copilot: tick-row had a backtick code span with literal non-ASCII range characters (`[U+0080-U+FFFF]`). Replaced with English form: "replace each codepoint in the U+0080 through U+FFFF range" — matches the same MD038 anti-pattern lesson recorded in the row's own observations. - Copilot: spelling drift "bash-retirement" (status) vs "bash retirement" (milestone). Standardized on hyphenated form ("bash-retirement") matching the tick-history row. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Slice 21 of the TS/Bun migration (B-0086). Ports
tools/pr-preservation/archive-pr.sh(217 bash + ~457 embedded Python = 674 lines) →tools/pr-preservation/archive-pr.ts(590 lines TS). Last Bucket B file — after this PR merges, Bucket B is empty and the trajectory transitions from "porting" to "soak + bash retirement" phase.Behavioural improvements (deliberate, not drift)
set +eto capture Python exit code.paginateTopLevel<T>helper with type-safe extractor — handles all 3 top-level connections (threads/reviews/comments) with one function.yamlQuoteUnicode-escape fix — JSJSON.stringifypreserves non-ASCII; Pythonjson.dumpsdefaults toensure_ascii=True. The TS port post-processes to escape non-ASCII codepoints as\uXXXX— preserves byte-equivalence on titles with→/—etc.All bash safety rails preserved: positive-integer PR validation, GH_REPO env-var preference +
gh repo viewfallback, 2/3-segment NWO parsing with Enterprise HOST validation (dot required), slash-injection defence on owner/name, paginated GraphQL fetch (top-level + per-thread), GraphQLerrorsarray inspection, null-pullRequest detection, idempotent archive path viaPR-<NNNN>-*glob (Otto-235), CommonMark §4.5 fence detection (Otto-241), trailing-newline-only rstrip.Verification
archived_at(timestamp) +archive_tool(.sh vs .ts — deliberate self-reference)→/—) escapes correctly viayamlQuotefixbun --bun tsc --noEmit -p tsconfig.jsoncleanTest plan
lint (tsc tools)gate passesComposes with
tools/git/batch-resolve-pr-threads.ts(slice 20, ts(B-0086): port 1 git script (.sh→.ts) — slice 20 of TS/Bun migration #907) — same GraphQL pagination shapedocs/trajectories/typescript-bun-migration/RESUME.md— Bucket B 1 → 0; trajectory transitions to soak+retirement phasedocs/trajectories/typescript-bun-migration/slice-audits.md— slice 21 audit appended🤖 Generated with Claude Code