Description
Blog post notes
Raw material for a future solrevdev.com post about reviewing, testing and speeding up ytx. This is not the post. It collects what the post needs in one place, so nothing depends on local scratch files that will be deleted.
Status: v1.0.8 is released on GitHub. PR #6 (NativeAOT) is an open draft. The caption bugs in #3 are still open.
Rule for the post: do not publish captured YouTube caption text. Use titles, video IDs, byte counts, exit codes and timings only.
Links
The hook
The October 2025 post that introduced ytx published this caption ordering and listed it under "Key Learnings" as "Intelligent Caption Selection":
.OrderByDescending(t => t.Language.Name.Contains("English", StringComparison.OrdinalIgnoreCase))
.ThenByDescending(t => t.IsAutoGenerated)A year later the review found that this exact line is bug 1. It ranks auto-generated tracks above human-written ones. The Contains match is the root of bug 2. That is an honest and strong opening for the follow-up.
Timeline (8 Oct 2026)
Times are UTC. Add one hour for UK time (BST).
| Time | What happened |
|---|---|
| 17:52 | Asked Claude Code to grab the transcript of a SaaS-idea video (aCi6BhrvjhM) with "ytx or yt_json". It chose ytx and saved transcript.json and transcript.md. |
| 17:54–18:02 | Downloaded the PRD the video links to, built a small project folder with a README, committed it and moved it into a projects folder. |
| 18:03 | Asked which tool it used. Answer: ytx, because it is one installed tool. yt_json chains yt-dlp, jq and uvx. |
| 18:04 | "ytx is my tool. What do you think of it?" Three agents start in parallel: source review, live testing, competitor survey. |
| 18:05–18:11 | Source review done in about 1.5 min. Survey in about 2 min. Live testing in about 7 min. |
| 18:17 | Asked for one issue with everything, plus a separate look at NativeAOT and allocations. Two more agents start: AOT prototype and allocation profiling. |
| 18:19 | Issue #3 opened (review). |
| 18:32 | Allocation profiling done (about 14.5 min). |
| 18:37 | AOT prototype done (about 19.5 min). |
| 18:38 | Issue #4 opened (AOT and allocations). |
| 18:54 | Asked for branches: the JSON fix as a PR, and AOT as an extendable branch. Two agents start. |
| 18:55 | Follow-up comment on #3 pointing to #4. |
| 19:01 | PR #5 opened. Agent took about 7.5 min. |
| 19:13 | PR #6 opened as a draft, stacked on #5. Agent took about 16.5 min. Status comment on #4 with the exit code correction. |
| 19:13–19:17 | First native.yml run on #6: all 5 platforms built and smoke-tested. |
| 19:22 | Asked to merge #5. Merged with a merge commit (5 commits kept) and --delete-branch. |
| 19:22:59 | PR #6 closed automatically, because its base branch was deleted. |
| 19:23 | publish.yml released v1.0.8 to GitHub Releases and pushed to NuGet. |
| 19:34 | Restored perf/source-gen-json from its last SHA, reopened #6, retargeted it to master, deleted the branch again. |
| 19:34–19:37 | native.yml ran again on #6 against master. All 5 platforms green. |
| 19:34 | Two more agents start: one scopes the #3 fixes and their order, one gathers blog material (these notes). |
| 19:36 | NuGet still listed 1.0.7 as latest. The installed global tool was still 1.0.7. |
From "grab a transcript" to a released performance fix took about 1 hour 30 minutes.
Key numbers
From the review (#3)
- Tests at v1.0.7: 26 passing. After PR perf: source-generated JSON, streamed output and fewer allocations #5: 70 passing.
- ytx vs yt_json: 1.3–1.6 s against 3.1–4.9 s for a typical video. 5h15m podcast: about 2.2 s against 7.2 s.
- Random slow calls: about 1 in 8 calls took about 11.5 s instead of about 1.5 s. One took 21.7 s.
- Long output: the 5h15m video gives 1.1 MB of JSON.
transcriptis about 2.9x the size oftranscriptRaw. - Wrong track: Lex Fridman #501 gave 8,144 auto-caption lines from ytx against 4,773 manual lines from yt_json.
NativeAOT (#4, net10.0, osx-arm64, Apple M1)
| Build | Binary / archive | --version |
Full run CPU | Peak RSS (full run) |
|---|---|---|---|---|
| Installed tool shim 1.0.7 | runtime + 1.3 MB | 38.0 ms | 416 ms | 132 MB |
| Self-contained, trimmed + R2R | 35.1 / 13.7 MB | 27.4 ms | 208 ms | 97 MB |
| NativeAOT | 10.3 / 4.6 MB | 8.6 ms | 73 ms | 61 MB |
- Headline: startup 38 ms to 8.6 ms. About 6x less CPU per call. Half the memory. No .NET runtime.
- Instructions retired on
--metadata-only: 2.54 billion (framework-dependent) against 0.47 billion (AOT). - Hybrid RID-specific tool package (.NET 10):
--versionin 7.3 ms against 33.5 ms for the shim. Breaks installs on SDK 8. - Worst build: trimmed self-contained without R2R. 51.9 ms startup and 589 ms CPU.
- Linux arm64 AOT: 11.5 MB plus an 18 MB
.dbg. Needslibssl3t64andca-certificates. Built on noble, it needs GLIBC 2.34.
Allocations (#4)
- Where time goes on the 5h video: 2.1–2.2 s and 186 MB in total. ytx's own code is 28 ms + 15 ms and 7.9 MB + 26.3 MB.
- Share: ytx is about 2% of run time and about 18% of allocations. The rest is network and YoutubeExplode.
NormalizeCaptionover 16k captions: 754,614 B to 904 B, 3,408 µs to 581 µs.- Format, serialise, write (5h, BenchmarkDotNet): 7,947 µs / 10.5 MB (v1.0.7) to 3,974 µs / 4.5 MB (Tier 1) to 3,440 µs / 58 KB (Tier 2).
- Cold single run, 5h data, no network: 42–43 ms / 34.2 MB (v1.0.7) to 23 ms / 28.5 MB (Tier 1 + source-gen) to 20 ms / 5.3 MB (Tier 2).
- Verdict: Tier 1 is about 30 lines and worth it. Tier 2 is about 150 lines for 3 ms more. Vanity.
PR #5 byte checks
Built master and the branch, ran both on net8.0 and net10.0, compared with cmp. All identical:
| Case | Bytes |
|---|---|
| default | 66,092 |
--compact |
66,071 |
--metadata-only |
1,779 |
stdin {"url":...} |
66,124 |
stdin {"URL":...} with --compact |
66,071 |
Error paths (invalid JSON, JSON array, {}, invalid ID) gave the same stderr and exit 2.
CI evidence
| Run | Jobs | Result |
|---|---|---|
| native.yml, 37830351761 | osx-arm64, osx-x64, linux-x64, linux-arm64, win-x64 builds, plus osx-x64 smoke test on macos-15-intel |
All green. Release step skipped (no tag). About 3 min. |
| native.yml, 37832924672 | Same, after retarget to master |
All green |
| publish.yml, 37831516382 | publish | Green. Released v1.0.8 in 38 s |
No CI binary has been run against YouTube.
v1.0.7 vs v1.0.8 measured on the released package
Both versions were installed from NuGet: 1.0.7 into a separate tool path, 1.0.8 as the global tool. Framework-dependent, macOS osx-arm64, interleaved runs, medians shown.
| Case | Runs | 1.0.7 wall / CPU (ms) | 1.0.8 wall / CPU (ms) |
|---|---|---|---|
--version |
20 | 35 / 32 | 35 / 31 |
| stdin JSON path (invalid ID, no network) | 20 | 53 / 50 | 47 / 44 |
--metadata-only |
8 | 1037 / 337 | 988 / 325 |
| Full run, 17 min video | 8 | 1661 / 454 | 1613 / 469 |
| Full run, 5h15m video | 6 | 2246 / 889 | 2079 / 812 |
What this shows:
- No visible change for one person running it once. The JSON path is about 6 ms faster, as NativeAOT builds and allocation work: findings and plan #4 predicted. Everything else is within network noise.
- Same output.
ytx aCi6BhrvjhMfrom the 1.0.8 global tool was byte-identical to thetranscript.jsonthat 1.0.7 produced at the start of the session. - The real gains are in feat: NativeAOT builds, benchmarks and packaging #6. The framework-dependent startup cost (about 35 ms and 30 ms of CPU) only goes away with NativeAOT.
Suggested measurements: ytx --version startup (30 runs), stdin JSON path, a full run on aCi6BhrvjhM (8 round-robin rounds, compare medians and CPU), cold 5h run, and a cmp of both versions' output. NuGet had not listed 1.0.8 at 19:36 UTC, 13 minutes after the release.
Live test matrix (v1.0.7, macOS osx-arm64)
89 recorded runs from a small harness (run.sh), covering about 40 behaviours. Each run saved stdout and stderr and logged exit code, wall time, byte count and whether stdout was valid JSON. Main test video: aCi6BhrvjhM (17 min, auto captions). Language tests: TED talk iG9CE55wbtY, which has many tracks including manual English.
| Area | Cases | Exit | Time | Result |
|---|---|---|---|---|
| URL forms | watch URL, bare ID, youtu.be (with ?si=&t=), &t=&list=&index=, /embed/, music., m., /live/, no scheme, -u |
0 | 1.0–1.8 s (first run 4.6 s) | Correct, about 66 KB JSON each |
| Shorts | real short, normal video via /shorts/, no www |
0 | 1.0–1.5 s, some 11.5 s | Correct. The 11.5 s runs were the general slow-call issue |
| Repeat runs | 3 shorts + 3 watch URLs alternated | 0 | 3 of 6 took 3.5–11.5 s | Slow calls hit any URL form |
| stdin | {"url":...}, with -c |
0 | 1.5 s | Works |
| stdin | plain ID, not JSON | 2 | fast | Raw System.Text.Json error text |
| stdin | wrong key, empty | 2 | fast | "A YouTube URL or video ID is required." |
| stdin | extra keys, stdin plus positional | 0 | Extra keys ignored, positional wins, no warning | |
| Output modes | --metadata-only, -c, both with -l fr |
0 | 1.0–1.4 s | Correct. -l with metadata-only accepted silently |
| Language, correct | French, french, de, it, ar, ko, fr-CA, zh-TW, Chinese |
0 | 1.3–3 s, Chinese once 21.7 s |
Correct track |
| Language, wrong | -l fr, -l es, -l ja |
0 | about 1.4 s | Afrikaans, Chinese and Azerbaijani. Exact tracks existed |
| Language, silent fallback | -l pt, -l zh, -l xx, -l Klingon |
0 | about 1.5 s | English, no warning |
| Default track | TED, Lex #501 | 0 | Auto-generated chosen over manual English | |
-l misuse |
-l '', -l with no value |
2 | 0.05 s | "Option '-l' requires a value." |
| No captions | real short, two live streams, upcoming premiere | 0 | 1.2–1.6 s | Placeholder text inside transcript, exit 0 |
| Past livestream | x6p9Ri0DlNE |
0 | 2.0 s | 454 KB, works |
| Long video | Lex #501 NYFGCESmikA, 5h15m, 4 runs |
0 | 2.1–2.3 s, once 12.3 s | 1.1 MB, timestamps up to 05:15:39 |
| Unavailable | deleted ID, private video | 1 | 0.4 s | "Video '...' is not available." |
| Invalid input | junk string, non-YouTube host, 3 and 14 char IDs, playlist URL, channel URL | 2 | 0.05 s | "... is not a valid YouTube URL or video ID." |
| Parser | unknown flag, two positionals, -u plus positional, --help --foo |
2 | 0.05 s | Clear message and a --help hint |
IDs starting with - |
-lsFs2615gw bare, after --, after -u |
2 / 0 / 0 | Works with -- or -u. Help does not say so |
|
| Piping | ytx ... | jq, | head |
0 | No BOM, trailing newline, nothing on stderr, no broken-pipe error | |
| yt_json comparison | 4 videos, both tools | 0 | ytx 1.3–2.2 s, yt_json 3.1–7.2 s | Byte-identical on 2 of 4. Different on TED and Lex because of the default-track bug |
The harness, for the post:
#!/bin/zsh
# usage: run.sh name args...
D=./ytx-test
n=$1; shift
s=$(perl -MTime::HiRes=time -e 'printf "%.3f", time')
~/.dotnet/tools/ytx "$@" > $D/$n.out 2> $D/$n.err
rc=$?
e=$(perl -MTime::HiRes=time -e 'printf "%.3f", time')
t=$(echo "$e - $s" | bc)
valid=$(jq -e . $D/$n.out >/dev/null 2>&1 && echo json || echo notjson)
echo "$n rc=$rc t=${t}s out=$(wc -c < $D/$n.out)B $valid err=$(head -c 300 $D/$n.err | tr '\n' ' ')"The process: which agents did what
All work ran in one Claude Code session. The main session planned, delegated, merged results and wrote to GitHub. Background agents did the heavy work, mostly in parallel, each on its own fresh clone in a scratch folder.
| Agent | Started (UTC) | Took | Output |
|---|---|---|---|
| Review ytx source code | 18:04 | ~1.5 min | Found the ThenByDescending and Contains bugs by reading Program.cs |
| Hands-on test ytx CLI | 18:04 | ~7 min | The 89-run live matrix above, and the yt_json comparison |
| Survey YouTube transcript tools | 18:04 | ~2 min | Competitor table with stars and downloads from gh, NuGet, npm and PyPI |
| Prototype NativeAOT build | 18:18 | ~19.5 min | Six deployment shapes, startup, network and memory numbers, Linux in Docker, the RID tool experiment |
| Profile allocations and hot paths | 18:18 | ~14.5 min | Phase instrumentation, BenchmarkDotNet project, Tier 1 and Tier 2 variants with a byte-for-byte verify harness |
| Branch and PR for JSON perf fix | 18:55 | ~7.5 min | PR #5 with golden-output tests |
| Prepare AOT branch content | 18:55 | ~16.5 min | PR #6 contents: write-up, scripts, benchmark project, native.yml, packaging templates |
| Scope issue #3 fixes | 19:34 | running | Fix order for #3, a track-selection prototype, YoutubeExplode 6.6.2 with AOT |
| Audit blog-post material | 19:34 | running | These notes |
Points worth making in the post:
- Parallel is where the time went. Three review agents finished inside 7 minutes. The two performance agents ran side by side for about 20 minutes.
- The agents checked each other. The source reviewer found the ordering bug in code. The live tester confirmed it on a real TED talk and found the
frto Afrikaans case. - Evidence over claims. Every perf change was checked byte for byte against v1.0.7 before it was proposed.
- The human made the calls. Merge perf: source-generated JSON, streamed output and fewer allocations #5, keep feat: NativeAOT builds, benchmarks and packaging #6 as a draft, fix Review of v1.0.7: caption track bugs, gaps and comparison with other tools #3 later.
Mistakes and corrections
- The AOT
--metadata-onlyexit code. Issue NativeAOT builds and allocation work: findings and plan #4 said the unfixed AOT build exited 0 on--metadata-only. A re-run showed it exits 1 with the reflection error and nothing on stdout. Corrected in a comment on NativeAOT builds and allocation work: findings and plan #4 and in the write-up. Lesson: re-run a surprising result before publishing it. - PR feat: NativeAOT builds, benchmarks and packaging #6 was closed by deleting its base branch. feat: NativeAOT builds, benchmarks and packaging #6 was stacked on perf: source-generated JSON, streamed output and fewer allocations #5's branch. Merging perf: source-generated JSON, streamed output and fewer allocations #5 with
--delete-branchdeleted that branch, and GitHub closed feat: NativeAOT builds, benchmarks and packaging #6 three seconds later. The fix: recreate the branch ref from its last commit (223924a) withgh api, reopen feat: NativeAOT builds, benchmarks and packaging #6, retarget it tomaster, then delete the branch again. Lesson: retarget stacked PRs before deleting the base branch. - Windows newlines. The Tier 1 sketch in NativeAOT builds and allocation work: findings and plan #4 used
'\n'. PR perf: source-generated JSON, streamed output and fewer allocations #5 keptEnvironment.NewLine, so Windows output is unchanged. Noted on NativeAOT builds and allocation work: findings and plan #4. - The 2025 post's "intelligent" ordering. The code praised in the original post is bug 1. Worth owning in the post.
- NuGet lag. NuGet accepted the push at 19:23 UTC. The flat container listed 1.0.8 at about 19:50, but
dotnet tool update -gkept installing 1.0.7. Asking for--version 1.0.8failed with "not found in NuGet feeds" for several more minutes. Lesson: the package page, the flat container and the install index update at different times. - Startup numbers drift. A later re-run measured AOT at 7.3 ms and framework-dependent
--helpat 33.4 ms, against 8.6 ms and 38 ms earlier. The ratio held. Say so if quoting milliseconds. - Benchmarks are warm, CLIs are cold. BenchmarkDotNet showed Tier 1 halving the cost. Cold single runs were needed to show the real 20 ms. Worth a short paragraph.
Competitor table (from #3, 8 Oct 2026)
| Tool | Size | Strength | Weakness |
|---|---|---|---|
| youtube-transcript-api (Python) | 8.4k stars, ~15.8M PyPI downloads a month | De facto standard, many formats, proxies | No metadata |
| yt-dlp (Python) | ~196k stars | Fullest metadata, best anti-blocking | No clean transcript text |
| YoutubeExplode (.NET) | 3.7k stars | ytx's base | A library, not a CLI |
| Fabric (Go) | 44k stars | fabric -y with transcript and metadata |
Large framework |
| jkawamoto/mcp-youtube-transcript | 487 stars | Transcript, metadata, chunking | MCP only |
| kimtaeyoon83/mcp-server-youtube-transcript | 600 stars | Language fallback | MCP only |
| zemse/yt (Rust) | new | JSON for agents | Few users |
| steipete/summarize | 6.7k stars | Whisper fallback | Built for summaries |
| Hosted APIs, browser extensions | Work from cloud IPs or a signed-in browser | Paid, or not scriptable |
Name clash: koguchic/ytx is a different Rust CLI installed as ytx-cli. Relevant for a Homebrew formula name.
Quotes and one-liners
- "ytx's own code is about 2% of run time."
- "A real call is about 90% network."
- "A person running ytx once won't notice. Agents and batch jobs that run it many times will."
- "Two bugs can silently return the wrong transcript, with exit 0 and nothing on stderr."
- "
frmatches Afrikaans,esmatches Chinese,jamatches Azerbaijani." - "The bigger wins for users are not in this document."
- "Tier 2 cuts allocations by 99% and saves 3 ms. Vanity."
- "The unmodified AOT binary fetched from YouTube, then failed when writing the JSON."
- Callback to bbx: "Test the exit code." Callback to orchat: "Trimming warnings are not noise."
Suggested titles
- Reviewing ytx a Year Later: Wrong Transcripts, NativeAOT and the 2% That Was Ours
- What Happened When I Asked Claude Code to Review My Own .NET Tool
- ytx v1.0.8: Source-Generated JSON, NativeAOT and Knowing When to Stop Optimising
- 38 ms to 8.6 ms: NativeAOT for a .NET Global Tool, and Why Allocations Did Not Matter
- Five Agents, Four Issues and PRs, One Release: Reviewing ytx
Suggested front matter
---
published: false
layout: post
title: <pick from above>
description: <one sentence for SEO>
summary: <one or two sentences for the home page>
cover_image: /images/ytx-review-aot-cover.svg
image: /images/ytx-review-aot-cover.png
tags:
- dotnet-global-tools
- dotnet
- dotnet-10
- csharp
- native-aot
- performance
- benchmarkdotnet
- system-text-json
- youtube
- transcripts
- claude-code
- ai-agents
- github-actions
- nuget
---Suggested outline (house style)
Bold title-case headings with one emoji each, as in the bbx, orchat and roll-forward posts. Target 3,000–4,500 words. Close with "Success! 🎉".
- Overview ☀: grabbed a transcript for a SaaS-idea video, asked "what do you think of it?", ended with a release and a draft AOT PR in 90 minutes. Link the 2025 post.
- The Line I Called Intelligent 🎯: the
ThenByDescending(t => t.IsAutoGenerated)callback. - How the Review Was Run 🤖: the agent table, parallel timing, fresh clones.
- Testing Against Live YouTube 🧪: the harness and a trimmed matrix. Avoid caption text.
- The Two Bugs That Exit 0 🐛: default track and
Containsmatching, thefr/es/jatable, silent fallback. - ytx Against the Competition 📊: short table and the niche ytx fills.
- Is NativeAOT Worth It? ⚡: the IL2026 and IL3050 warnings, the runtime failure, the 10-line fix, the build-shape table.
- Where the Time Actually Goes 🔍: the 2% finding, phase table.
- Warm Benchmarks, Cold CLIs 🧊: BenchmarkDotNet vs single-run numbers, Tier 1 vs Tier 2.
- Shipping v1.0.8 📦: PR perf: source-generated JSON, streamed output and fewer allocations #5, golden-output tests, byte checks, auto-release, NuGet lag. Then v1.0.7 vs v1.0.8 on the released package: the same for one run, 6 ms on the JSON path.
- The Mistakes 🧨: exit code correction, the auto-closed stacked PR, NuGet lag.
- The AOT Branch 🏗️: native.yml matrix, 5 green platforms, Homebrew and Scoop templates, the SDK 8 trap with RID tool packages.
- What I Took Away 💡: numbered list.
- What Is Next 🔮: the order chosen below.
A two-post split also works: "What a review of ytx found" and "NativeAOT and allocations in a .NET global tool". The roll-forward post shows single-topic posts work well too.
Diagrams to draw
- Session swimlane: main session plus agents on a time axis from 17:52 to 19:37, with GitHub events (Review of v1.0.7: caption track bugs, gaps and comparison with other tools #3, NativeAOT builds and allocation work: findings and plan #4, perf: source-generated JSON, streamed output and fewer allocations #5, feat: NativeAOT builds, benchmarks and packaging #6, v1.0.8) as markers.
- Where a run's time goes: stacked bar for the 5h video. Watch page, manifest, caption fetch, then ytx's 43 ms as a sliver.
- Build shapes: grouped bars for startup and CPU per call across the nine builds.
- Caption track selection, before and after: v1.0.7 ordering against the proposed order (exact code, exact name, prefix, substring, manual before auto).
- Stacked PR mishap:
master← perf: source-generated JSON, streamed output and fewer allocations #5 branch ← feat: NativeAOT builds, benchmarks and packaging #6, then the base deleted and feat: NativeAOT builds, benchmarks and packaging #6 closed, then restored and retargeted. - Release pipeline: publish.yml (version, pack, NuGet, release) beside native.yml (5-RID matrix, smoke test, archives and SHA256SUMS on tag).
Screenshots to take
- Issue Review of v1.0.7: caption track bugs, gaps and comparison with other tools #3 header and the language table.
- Issue NativeAOT builds and allocation work: findings and plan #4 results table.
- PR perf: source-generated JSON, streamed output and fewer allocations #5 merged, with its checks.
- native.yml run 37830351761 with all jobs green.
- PR feat: NativeAOT builds, benchmarks and packaging #6 timeline showing "deleted the base branch", "closed" and "reopened".
- v1.0.8 release page.
- NuGet page once 1.0.8 is listed, with the download count.
- Terminal:
ytx --versionfrom the shim and from the AOT binary, timed. - Terminal:
ytx -l fr iG9CE55wbtYshown with byte count and exit code only. Crop out any caption text. - Claude Code with several background agents running, if a capture exists.
What is still missing
- A Windows or Linux run against YouTube. Every live number is from one Mac.
- A cover image (
/images/ytx-review-aot-cover.svgand.png). - A decision on AOT release assets. The post can end open, but should say so.
What is next (decided order)
Status, 9 October 2026: steps 1, 2, 3 and 5 are done, released as v1.1.0, and chapters followed in v1.2.0. Step 4, NativeAOT (#6), is next. The rest of the story is in docs/investigations/2026-10-ytx-review-to-1.1.0.md, which covers everything through 1.2.0.
A scoping pass after v1.0.8 recommended fixing #3 first, in small PRs, and finishing AOT once the output contract settles:
- ✅ Track selection fix (fix: prefer manual captions and exact language matches #8, shipped in 1.1.0 rather than 1.0.9). Prototyped at +23/−7 lines plus 62 lines of tests, with 85 tests passing. On the TED talk the default gives the manual track, and
-l fr,esandjagive French, Spanish and Japanese. Trap found: with onlyThenBy,-l xxfell back to Afrikaans, so English stays the tiebreak. - ✅ Housekeeping (chore: update YoutubeExplode, add LICENSE, remove stale doc #9). YoutubeExplode 6.6.2 drops AngleSharp, which clears NU1902, keeps AOT warning-free and gives byte-identical output. Add LICENSE and remove the stale viewer doc.
- ✅ Output fields and
captionStatus(1.1.0) (feat!: report caption status, track and video metadata in output #10). One schema change, released as a minor version with[skip ci]and a manual run. - ⏭️ Next: finish feat: NativeAOT builds, benchmarks and packaging #6 on the settled schema, so Homebrew and Scoop users start on the final contract.
- ✅ Features:
--proxy,segments,--formatand--list-languages(fix(cli): add --timeout and Ctrl+C handling, tidy caption text and help #11, feat: add --format, --segments, --list-languages and --proxy #12). MCP mode was dropped. Chapters were added in 1.2.0 (feat: chapters from the video description #16, feat: read chapters from the video description #17).
The alternative was to ship AOT straight after steps 1 and 2 and save the schema change for a 2.0. The slow 11–21 s caption fetches are not fixed by 6.6.2 or by AOT, and only appear on caption fetches, never with --metadata-only. They need their own trace.
🤖 Generated with Claude Code
Activity
solrevdev commentedon Oct 8, 2026
Update: steps 1–3 and 5 of "What is next" are done and shipped as v1.1.0. #3 is closed.
captionStatus: feat!: report caption status, track and video metadata in output #10, released as 1.1.0 through[skip ci]merges and one manual minor run.--proxy,segments,--formatand--list-languages.ytx mcpwas dropped.The rest of the story, from the session handoff to the release, is in docs/investigations/2026-10-ytx-review-to-1.1.0.md. It has the timeline, each change with its evidence, the traps, measurements, the decisions not taken, a suggested outline and commands to reproduce. Together with this issue, it covers everything needed for the blog post.
Next is step 4, NativeAOT (#6). It is up to date with 1.1.0 and targets
master. The README now has install instructions for each OS and explains when to use the native binary.🤖 Generated with Claude Code
solrevdev commentedon Oct 8, 2026
Update: chapters shipped in v1.2.0 (#16, #17).
They had been left out of 1.1.0 because YoutubeExplode has no chapters API. ytx now parses them from the description using YouTube's own rules, and the Markdown transcript is split into sections by chapter. Section 6a of the write-up covers the design and the traps, so it can go in the post.
#6 (NativeAOT) is up to date with 1.2.0 and is still the next piece of work.
🤖 Generated with Claude Code