Repository navigation
Pin Ruby, TypeScript and Rust at their masters, re-record their MCP sessions, list the APIs Ruby's send-keys uses - #247
Merged
Conversation
why: libtmux-ruby's `LibTmux::Support` methods are typed `(::String value) -> ::String`. RBS allows a name after a positional type, but the extractor kept the whole text as the type, so the model held the type "::String value" and `ruby-layout.test.ts` could not give it the roles its definition gives the same tokens. The Ruby model could not move to a revision that has these methods. what: - Split a positional parameter into its type and its written name, and keep `argN` when it writes none. A union ends in its last member, not in a name: `::Integer | nil` is still one unnamed type. - Cover a named, an optional named, an unnamed union and a generic type in `native-adapters.test.ts`; the test fails without the change.
why: Docs CI read the Ruby example cache at 9b1545562, while the port's
own build reads the live file. `examples/api/capture.rb` stopped
attaching a control client at 2156612 ("Require tmux 3.7 before
attaching a control client"), so `core-apis.json` kept naming five
control-mode symbols that the port's build refused
("core-apis: ruby/send-keys names LibTmux::Server#open_control, but the
program never uses open_control", libtmux-ruby#90) and docs CI never
saw it. The cache is bound to the revision of the API model, so the
model has to move with it.
what:
- Regenerate the Ruby model from the committed bytes of master with
`gen-api-model --port ruby`: the four gems move from 0.1.0.alpha.1 to
0.1.1. `LibTmux::Support`, `Support::Versions`, `tmux_version` and
`supports_control?` on both servers, and `require_supported` and
`server_version` on both support modules are new; no symbol is
removed. `version_timeout` joins `Server.new`, `Server.open` and
`Async::Server.open`, `environment` joins `Endpoint.new` and
`MCP::CLI.run`, `PaneSnapshot#pid` may be nil and `Pane#break_out`
documents `ProtocolError`. Complete programs attach to the symbols
they exercise: 55 pages, up from 19.
- Regenerate the example cache. `capture.rb`, `new_window.rb`,
`async_cancel.rb`, `manifest.json`, `scripts/check`, `scripts/matrix`,
`installed_gems.rb` and `workspace_apply.rb` changed; `quickstart.rb`
and `mcp_protocol.rb` only moved revision.
- Regenerate what is bound to the Ruby pin: product copy, the workspace
showcase (revision only) and the "Discussed in" index, which gains
the mentions of the guides master ships.
why: The four Ruby gems declare 0.1.1 at the new pin, so the manifest index and the destination evidence still named 0.1.0.alpha.1 and `destination-evidence.test.ts` refused four RubyGems records. That suite also holds every record to one check date, so the evidence has to be re-verified whole rather than record by record. what: - Regenerate the Ruby entries of `manifest-packages.json` at the pinned revision. The Elixir entry is not regenerated: it already differs from its manifests on main. - Re-run `verify-destinations` live and read-only. The four rubygems.org package pages each list 0.1.1. The same run moved the latest version the registries report for other ports (Maven Central and crates.io 0.1.1 to 0.1.2, Hex, LuaRocks and Swift Package Index 0.1.0 to 0.1.1), which this change does not own and records as found.
why: The Ruby usage data cited lines of 9b1545562, so `gen-tmux-usage --check` refused it once the model moved to 20744401e, and the dispatcher rules described a call chain that no longer exists: `Server#run` now hands its arguments to a private `dispatch`, which calls the executor, and `execute_typed` reaches that through `run`. what: - Carry each cited line of the Ruby rules to where the same text sits at 20744401e, following the diff of each file: 219 citations moved or stayed with their text, 8 where the line itself changed were found again by their text, and 1 stays because its two-line statement spans the cited line. - Replace the hops `execute_typed -> run -> @executor.run` of the seven Ruby dispatchers that pass through them with `execute_typed -> run -> dispatch -> @executor.run`, citing the executor call on the line of `dispatch`. - Regenerate `tmux-usage/ruby.json`. The entries differ in the lines they cite, and `Pane#break_out` now also sends `list-panes` when `break-pane` prints no window ID and `rename-window` after the repaired `break-pane` of tmux 3.7. Checked: `gen-tmux-usage --check --source --ports ruby` regenerates the port and matches the committed file; `gen-tmux-usage.negative.mjs` passes.
…s plan why: libtmux-ruby's `compatibility.yml` now plans its matrix in a `plan` job, and the `cell` job that runs the Core programs carries `if: needs.plan.outputs.run_cells == 'true'`. `workflowStep` refuses any job-level `if`, so `ruby/create-windows` and `ruby/send-keys` stopped being provable as executed by CI at the new pin. The port's `ci-plan` selects cells for every push and pull request, which this check cannot read; it can read that the flag is the plan job's own output. what: - Accept a job-level `if` only when it compares an output of a job this job `needs` with `'true'`, that job is in the same workflow, declares the output and is not itself conditional. - Keep refusing any other condition, a flag the plan does not declare, a different comparison, an added clause, a plan the job does not need, a conditional plan and `continue-on-error`. Each refusal has a test.
… uses why: `examples/api/capture.rb` no longer opens a control connection. It sends text, presses Enter and polls `pane.capture` until the output arrives. The send-keys list still named `Server#open_control`, `ControlConnection#exchange`, `ControlConnection#subscribe`, `ControlSubscription#next` and `ControlEvent#data`, so the Core page would have listed five APIs the program never calls, and the port's own build failed on the first of them. The capability itself is unchanged: its title is "Send keys" and nothing on the page says the program watches through control mode. what: - Drop the five control-mode symbols from `ruby/send-keys`. The list is now Server, Server.start, Server#new_session, CreationReceipt#pane, Pane#send_text, Pane#send_keys, Pane#capture and CommandResult#stdout, each resolved against the model at the pinned revision. - Derive the ports `core-apis.test.ts` checks from the capability table instead of a list of three, so a port that gains a showcase example cannot ship without a list of the APIs it uses. - Pin the incident in a test on the frozen program: the corrected list resolves against it, and a list that still names any of the five symbols is refused with the symbol's name. Checked: with the cache refreshed and this list unchanged, `core-apis.test.ts` failed on exactly `ruby/send-keys names LibTmux::Server#open_control, but the program never uses open_control`.
why: The site tests bind every displayed example block and recipe to a receipt from native runs. The Ruby model moved to libtmux-ruby 20744401e, so every Ruby page names a new revision and a Gemfile for 0.1.1, and 36 more pages carry a complete program than the 19 the receipt covered. what: - Rebind the 19 existing entries (sourceRevision and the hash and copy hash of the Gemfile) and add the 36 new ones. Each entry is derived from the displayed blocks of its page; the derivation reproduces the 19 committed entries byte for byte from the previous model. - Name 18 programs in `ruby-api-examples.test.ts`, up from 8. - Record what each program did. The two programs that attach a control client list tmux 3.7c only: the library refuses one below tmux 3.7. `workspace_apply.rb` lists no tmux release and says it is not verified: it loads `../workspace.yaml`, which no page displays. Checked with scripts/check-example-prose.py --example api --port ruby, one run per program on each of tmux 3.3a and 3.7c, at the committed revision, each restoring the gems from rubygems.org with Ruby 4.0.7 and Bundler 4.0.20: 15 programs passed on both releases and the 2 control programs passed on 3.7c. Both control programs stop on 3.3a with "control connections require tmux 3.7 or newer". workspace_apply.rb failed on both releases with LibTmux::Workspace::ConfigError: workspace file could not be read. That is a defect of the page the port ships (its program is not self-contained), not of this receipt; it needs a fix in libtmux-ruby.
why: libtmux-ruby 0.1.1 adds `LibTmux::Support`, the namespace that checks which tmux releases the library supports. No navigation bucket claims a module of that kind, so `check-nav` failed with "unmatched [ruby] — 1 symbols no bucket claims: Support". The bucket named "Terminal capabilities" is about terminal features, not releases. what: - Record `LibTmux::Support` as unsettled with the reason. Its nested `Versions` module and its functions are already placed. - Recompile `ruby.nav.json`; only the diagnostics change. Checked: `check-nav` reports every symbol of the 16 ports placed or named.
why: The Ruby API model moved to libtmux-ruby 20744401e but the guide cache still recorded 9b1545562. stage-port-docs.mjs refuses a cache whose revision differs from the model, so `--cached` threw "guide source ... differs from integrated model" on every build: each port's publisher build and this branch's test job, which failed at "Stage integrated source guides". what: - Regenerate site/src/data/port-guides/ruby.json with `stage-port-docs.mjs --port ruby --from-source --refresh-cache` from a clean clone at 20744401e. - The README header now centres its logo with a div instead of a paragraph, as the port ships it.
why: The Ruby API model moved to libtmux-ruby 20744401e, but the MCP protocol snapshot, the Ruby rows of the tool matrix and the recorded transcript still described 9b1545562. The tools' schemas changed in between, so the Ruby MCP pages documented a server that no longer exists at the pin. what: - gen-mcp-protocol.mjs --port ruby: snapshot of the server built from a clean clone at 20744401e. - gen-mcp-tools.mjs --port ruby: the Ruby rows follow the snapshot's schemas and the new source lines; other ports' rows are unchanged. - gen-mcp-transcript.mjs --port ruby: the two-step replay recorded against that server, with its contracts equal to the snapshot's. - Each generator's --check passes.
why: After the r12 wave the recorded contracts of the rs and ts transcripts no longer equal the tool schemas at each port's master, so the live replay falls back to "Requests only". rs differs in the output schema of run_shell_command; ts in those of run_shell_command and list_windows. The input schemas still match. what: - gen-mcp-protocol.mjs --port rs and --port ts: snapshots of the servers built from clean clones at libtmux-rs 6942c1e60 and libtmux-ts 073b9d7de. - gen-mcp-tools.mjs --port rs and --port ts: their rows follow the snapshots. ts registers five program tools more than before (start_program, read_program, run_program, wait_for_exit, stop_program), so the matrix lists five more tools. - gen-mcp-transcript.mjs --port rs and --port ts: the worked example replayed against those servers. Each transcript's contracts equal its snapshot's and the matrix rows' schemas. - Each generator's --check passes.
why: plannedRun accepted any `needs.X.outputs.Y == 'true'` whose job
declared an output of that name, so it proved less than it claimed. A
cell gated on `run_development` counted as run by CI; so did a plan
whose output was hard-wired to 'false' or read a step that does not
exist, a plan whose only step was `if: false`, a `needs: [other] # plan`
whose plan lived in a comment, and a plan that itself needed a
conditional job. A job-level `if` with `# continue-on-error: false` in
its comment also passed the check that lets a tolerant job through.
what:
- The capability entry records the one flag a job may wait on
(`ci.planOutput`, `run_cells` for both Ruby entries). A job-level
`if` is accepted only as `needs.<plan>.outputs.<flag> == 'true'`
with that flag, and an entry that names none accepts no `if`.
- The plan job must list no `needs`, `if` or tolerated failure, and
publish the flag as `${{ steps.<id>.outputs.<flag> }}` of a step
that exists, has that id and carries no `if` or tolerated failure.
- Comments are stripped before `needs` is read, so a name that only a
comment holds is not a dependency.
- `continue-on-error` is false only when its value is false, not
when a comment says so.
- Tests: one case for each counterexample, a `needs: other` case that
pins the membership check, the entry's flag, the name validation,
and the real Ruby workflow at 20744401e refused without its flag.
Against the previous predicate sixteen of these fail.
why: `toBe(18)` was a bare count of the distinct source files, which a refresh that adds or drops a program silently invalidates, and it could not say which file was missing. what: - Compare the receipt's source files with the files the model's example blocks link, as sets, so a mismatch names the file.
why: plannedRun checked the plan job and then ignored the other jobs a cell needs. A cell with `needs: [plan, release_artifacts]` counted as run by CI although release_artifacts has an `if` and does not run on a push or pull request, and a skipped dependency skips the cell. what: - Every job in `needs` other than the plan must run whenever the workflow does: no `if`, no tolerated failure, and every job it needs the same, followed to the end. A job the workflow does not define, or a cycle, is refused. - The plan keeps its own rule: no `needs`, `if` or tolerated failure. - Tests: a conditional job beside the plan in a flow list and a block list, a tolerated one, one that needs a conditional job, one the workflow does not define, one that needs the cell, and an unconditional sibling that is accepted. Six of them fail without the check.
why: The MCP matrix and snapshots were recorded at libtmux-ts 073b9d7de and libtmux-rs 6942c1e60, but the API models still described 46cccfef2 and a6fc2a656. A tool page builds its source link from the model's revision, so read_program, start_program and stop_program linked packages/mcp/src/tools/programs.ts, which does not exist at 46cccfef2, and run_program and wait_for_exit cited files that do not register them. Everything bound to the pin lagged the same way. what: - Regenerate the TypeScript and Rust models with `gen-api-model --port ts` and `--port rs` from clean read-only clones of the two masters. TypeScript gains 68 symbols and loses none (the program API: Server.spawn, Program, ProgramStartError and their options). Rust gains 13 and loses three constants (PANE_BORDER_FORMAT_PER_PANE, PROMPT_HISTORY, SERVER_ACCESS). - Regenerate the example cache, product copy and workspace showcase. The showcase drops its TypeScript pin: the model now records a revision whose tests load tmuxp's examples, and the generator refuses a pin once the model has caught up with it. - Follow the Rust justfile in core-capabilities.json: `script-env` now sits between `api-examples` and `examples` in `check`, so the evidence string starts there. - Update what the refresh moved in the tests: the prose-link review lists for the new TypeScript and Rust summaries, TypeScript constants that now carry their own descriptions and the line of ResizeAdjustmentDirection, the showcase test for an unpinned TypeScript, and the Rust payload-field count in nav-config.test.ts, which becomes a guard against an empty model, not a copy of the model's size. Checked: `gen-api-model --check --port ts` and `--port rs`, `gen-example-sources`, `gen-product-copy` and `gen-workspace-showcase` in their `--check` modes, and their negative controls.
why: `check-nav` refuses a symbol that no bucket claims and that is not named as unsettled. The TypeScript program API and a Rust doctest marker arrived with the refreshed models and fit no bucket. what: - Record `common.assertSupportedTmux`, `program.Program`, `types.ProgramPlacement` and `types.ProgramExit` as unsettled for TypeScript, each with the reason: the handle and its options belong to `Server.spawn`, whose pane sits with Pane, and the release check has no tmux object of its own. - Record `src.SandboxCanary` as unsettled for Rust: it is the doctest sandbox marker, test support and not tmux API. - Recompile `ts.nav.json` and `rs.nav.json` with `gen-api-model --nav`. Checked: `check-nav` reports every symbol of the 16 ports placed or named and no rule dead; `check-nav.negative.mjs` passes.
… 0.1.2 why: libtmux-ts and libtmux-rs declare 0.1.2 at the new pins, so the manifest index and the destination evidence still named 0.1.0-alpha.12 for the four npm packages and 0.1.0-alpha.15 and 0.1.0-alpha.16 for the four crates. what: - Regenerate the TypeScript and Rust entries of `manifest-packages.json` at the pinned revisions. The Elixir entry is not regenerated: it already differs from its manifests on main. - Run `verify-destinations` live and read-only. crates.io lists 0.1.2 for all four crates. The four npm package pages answer 403 to this client as before, and the record keeps saying so. No other record changed. Checked: `verify-destinations --check` agrees with the inventory, and `gen-manifest-packages` run to a separate file differs from the committed one only in the Elixir lines.
why: The TypeScript and Rust rules cite lines of the old pins, so `gen-tmux-usage --check --source` refused them once the models moved, and some of the descriptions no longer matched the code. what: - Carry each cited line to where the same text sits at libtmux-ts 073b9d7de and libtmux-rs 6942c1e60. The negative controls' expected messages follow the moved lines. - TypeScript: the MCP launch no longer starts the server or reads its options. `inspectServerStartup` asks whether the server is alive and reads the socket path only from one that is, so the `start-server` and `show-options` claims go and `display-message` is now sent when the server is already running. `Server.validateLayouts` probes the daemon's version through `checkDaemonVersion`, which sends `run-shell -C`, not `display-message`. The break-pane quirk of `Pane.breakOut` is named by its tag, `break-pane-misnames-windows`. - Rust: the version gate in `internal/support/versions.rs` is now a hop of the dispatcher chains: `dispatch_checked`, then `versions::dispatch`, then the executor. The exclusion for `Plan::run_over_control_mode` is gone: it now reads the version the connection attached under and no longer asks with `display-message`. - Regenerate `tmux-usage/ts.json` and `tmux-usage/rs.json`. Checked: `gen-tmux-usage --check --source --ports ts,rs` regenerates both ports and matches the committed files, and `gen-tmux-usage.negative.mjs` passes.
why: The site tests bind every displayed example block and recipe to a receipt from native runs. The Rust model moved to libtmux-rs 6942c1e60, so each Rust page names a new revision and a recipe that adds libtmux 0.1.2, and the receipt's record of the runs described the previous pin. what: - Rebind the Rust entries: sourceRevision, the `cargo add` line of the recipe, and the verification scope. The program files are byte-identical at the new revision, so no hash changes. - Record the runs of this revision. Each of the Rust programs (construction, listings, creation, query, cardinality, input, capture) ran as displayed with `scripts/check-example-prose.py --example api --port rs` on tmux 3.3a and 3.7c, in a new directory each, with Rust 1.99.0. `cargo add libtmux@0.1.2` resolved to 0.1.2 from crates.io on 2026-11-03, and all programs exited 0 with their expected output on both releases.
why: A tool page links `file#Lline` at the revision of the port's API model, and nothing checked that the file was there. With the matrix ahead of the TypeScript model, read_program, start_program and stop_program linked packages/mcp/src/tools/programs.ts at 46cccfef2, where it does not exist, and run_program and wait_for_exit cited files that do not register them and kept no line. The link audit reads declarations, not registrations. what: - Regenerate the matrix rows of TypeScript and Rust so their source links name the revisions the models now record. - Add `registrationFileProblems`, which holds every registration's file to the committed file list of its model (`<port>.paths.json`), and run it from `mcp-source-links.test.ts` over the committed matrix. It needs no checkout, so it runs in CI. The same test requires the TypeScript and Rust registrations to be on their model's revision, so the check cannot pass by judging nothing. - Add `registrationLinkProblems` and `scripts/check-mcp-source-links.mjs`, which read each registration from a checkout at the revision it cites: the file exists, the line is inside it, and the tool's wire name is written as a whole word within its own registration. A port with no checkout, or without the revision, is reported as skipped. Add it to the publication audit after the matrix check. - Negative cases: a missing file, a revision that lacks the file, a file that does not register the tool, a line past the end or none, a name that is only part of another or lies in the next registration, and the script run over a repository that lacks the cited file (exit 1, naming the tool). Checked: on the pushed state of this branch the script reports the five tools above for TypeScript and nothing for Rust; on the regenerated matrix every registration of the ten ports resolves at the revision it cites. `gen-mcp-tools --check` passes for ts, rs and ruby.
…arted why: The Rust server describes a pane the moment it splits it. tmux names the forked child `tmux` and reports no path until the shell has replaced it, so the split answer came back as `tmux` with a path, or `sh` with none, depending on timing. The committed recording held the second form, and a fresh recording of the first failed `mcp-replay.test.ts`, which draws the pane list from the recorded split. Of thirteen recordings, five caught the `tmux` form. what: - `verifySettledPanes` requires every pane an answer describes to run the shell (`sh`, or `cat` for the Ruby session that borrows one) and to name a path, whatever spelling the server uses for the keys. A pane described earlier throws `UnsettledPaneError`. - `gen-mcp-transcript.mjs` records again on that error, stopping the servers of its own sandbox first, up to six recordings, and throws if the last is still unsettled. A borrowed session cannot be restarted from the recorder, so it fails at once. Nothing is written for an unsettled answer. - Record the Rust transcript again: `split_window` now answers `sh` with `/home/user/project`. The other fields are unchanged. - Tests: the check accepts settled panes and refuses `tmux`, a null or empty path, another spelling of the keys and a pane inside a list; it leaves alone an object that is not a pane; an unsettled pane is not a tool error; and every committed recording satisfies it. Checked: `gen-mcp-transcript --check` passes for ts, rs and ruby. The repeated Rust recordings that were unsettled recorded again and the file they wrote equals the committed one.
why: CI failed at f12be07 in `checkCompleteApiExamples` (site/scripts/check-clipboard.mjs) with `LibTmux::Workspace#plan: HTTP 404`. The nine Ruby programs on `Workspace` declarations named `ports/ruby/reference/<slug>`, but the site serves a workspace declaration under its product, `ruby/<version>/workspace/reference/<slug>` (`productApiHref`). The browser check reads the route from the receipt and stops at the first failure, so the program pages after that one had not been checked. `ruby-api-examples.test.ts` spelled the same core path, so it agreed with the receipt and not with the site. what: - Point the nine receipt entries at `ports/ruby/workspace/reference/`. - Replace the hard-coded core path in `ruby-api-examples.test.ts`. - Add a test over every receipt entry of every port that its page is the one `productApiHref` gives the declaration, and one that a workspace declaration is served under the workspace reference. The first fails on the old pages with the symbol that failed in CI. Checked: a development server built from this tree answers every program page of the receipt, and `checkCompleteApiExamples` passes for all of them. With the nine old pages put back it fails with `LibTmux::Workspace#plan: HTTP 404`, as in CI.
why: The check that keeps the MCP matrix on the revisions of its models required only TypeScript and Rust. The Ruby rows are recorded at the revision of the Ruby model too, so a Ruby matrix that ran ahead of its model would have been judged by neither check and the test would still have passed. what: - Require ruby beside ts and rs in the `covered` assertion of `mcp-source-links.test.ts`. With the revision the Ruby model records moved, the assertion now fails (received the other ports but not ruby); the previous one passed.
why: `registrationLinkProblems` ended a tool's registration at the next line that any cited registration named. A tool that cited its neighbour's line no longer cited its own, so the span ran on through the neighbour into the tool's own registration, found the tool's name there and passed. A tool page would link another tool's code: get_window_info citing get_session_info's line in TypeScript (target.ts#L190), in Rust (contract.rs#L555 for #L579), and tmux_run citing tmux_snapshot's line in Ruby (catalog.rb#L6). what: - Read each registration's reach from the file: the cited line to the first line that names the tool. No two reaches in a file may share a line, so a citation of the neighbour's line, or of a line above the neighbour's name, overlaps it. The reach does not depend on the neighbour's citation, so moving one citation cannot stretch it into the next. An overlap is reported once, naming both tools and their reaches. - Add cases shaped like the three ports (a name after `registerTool(`, a name after an attribute whose description mentions the next tool, and names in constant lists far from the branch that describes them): each registration at its own line passes, and a tool that cites its neighbour's line, or a line before the neighbour's name, is refused. Before, the six refusals passed; the existing case of a name that lies in the next registration now reports the overlap. - A line in a neighbour's body, past the line that names it, is not refused: the file alone does not say where that body ends. - Measured with the checkouts at the revisions the matrix cites: the committed matrix resolves for all ten ports as before. In a copy of the matrix where the three citations above were altered, `check-mcp-source-links.mjs --port ts|rs|ruby` exited 0 before and exits 1 now. Moving every tool of every port to the cited line of each neighbour is refused in all ten.
why: `neededJobs` read the first line of a flow list and then only `- ` block items, so `needs: [plan,` with `lint]` on the next line was read as `plan` alone. A cell that waits on a skippable job named on the wrapped line counted as run by CI. what: - Read a flow list that opens a bracket without closing it through the line that closes it. - Add cases: a wrapped list that names the plan on either line is accepted, and a skippable or tolerated job on the second or third line of a wrapped list is refused, as is a wrapped job that itself needs a skippable one. Before, four of the cases failed: the wrapped accepts and three refusals.
why: The header of `gen-mcp-transcript.mjs` listed the ways a recording fails but not that an unsettled pane is recorded again, so a reader of the script met the retry loop without its reason. what: - State that a pane caught before its shell started is never written, that the recorder stops the tmux servers of its sandbox and records again up to ATTEMPTS times, and that a server which borrows a running session fails on the first attempt.
why: The comment above the fixture told how the stale cache once hid a refused list. That belongs here, not in the source: a maintainer needs what the test guards. what: - Replace the comment with the guard: the list is held to a frozen copy of the program that polls Pane#capture, it must resolve against it, and a list that names a control-mode symbol the program never calls is refused. The history, for the record: examples/api/capture.rb stopped attaching a control client when libtmux-ruby began requiring tmux 3.7 for one. The cache still held a program that called open_control, exchange, subscribe, next and data, and the list for the example named all five. The port's build read the live file and refused the list while this suite read the stale cache and passed (libtmux-ruby#90).
why: The Astro build failed its dangling-reference ceiling at 62
unresolved mentions, over a ceiling of 60 (CI run 41739010615). Of the
committed list, the Ruby guides cached at the new pin add four spans
that name nothing in the Ruby model and drop one: Enter, Example.check
(on two pages) and error.message. Each is not API, and the build and
`gen-mentions` could say so: they only lacked the words that decide it.
what:
- Treat `Example.<member>` as the example fixture, as `Example` already
is. The guide calls it "the `Example.check` assertion helper from the
complete program".
- Add `notApiInContext`, asked only after a span resolves to nothing,
so a name a port defines still links wherever it is written:
a capitalised word in "key names such as `Enter`" names a tmux key,
and a dotted lowercase path in a sentence about JSON output
("With `--json`, the refusal is in `error.message`") is a field of
that output. The cue is read in the span's own sentence, so the next
sentence's spans are unaffected.
- Regenerate `mentions.json`: the four spans leave its unresolved
list; its mentions are unchanged.
- Cases for each rule and for what stays dangling: the same spellings
without the cue, a cue in the previous sentence, and a span shaped
unlike what the cue names.
- The ceiling is not raised. Measured with the real linking plugin
over the staged Ruby guides, Ruby reports six unresolved before and
two after (Enumerable and LibTmux, both already on main). The CI
tally of 62 is the 60 of the committed list plus two PowerShell
mentions the list does not hold; the list now holds 56, so the build
reports 58.
why: Three complete programs attach to eleven Ruby reference pages and
two of them cannot be followed as written. `workspace_apply.rb` loads
`../workspace.yaml`, which no page shows, so saved alone it stops with
Workspace::ConfigError. `control.rb` and `async_control.rb` open a
control client, and the intro the generator writes for every program
says "tmux 3.3a or newer" while the library refuses a control client
below tmux 3.7. Publishing text known to be wrong is worse than
publishing no program, so the pages hold back until libtmux-ruby is
fixed.
No floor for the control programs can be read from data the docs hold:
the manifest entries have no tmux field, and the 3.7 in the model is
prose ("tmux 3.7 and newer" in the doc of `Server#supports_control?`),
so the 3.3a stays out of the pages rather than being replaced by a
guess.
what:
- Add `HELD_RUBY_PROGRAMS`, one list of programs by path with the
reason each is held and what brings it back.
`attachCompleteRubyExamples` attaches none of a held program's
symbols, and fails when the list names a program the manifest does
not select, so a hold cannot outlive its program. `extractRuby`
passes the list; a test export passes its own.
- Regenerate the Ruby model with `gen-api-model --port ruby`: the
eleven symbols lose their four example blocks and nothing else
changes. Drop their eleven entries from the example receipt, which
the test over the model requires to match.
- Test the generator (held program unattached, attached once released,
neighbours unaffected, unknown program and blank reason refused) and
the committed data: no held program is attached to any page, every
held program is still selected by the manifest at the model's
revision, and every selected program that is not held is attached.
Before, with the committed model, the first and third failed.
…covers it why: libtmux-ruby 20744401e rewrote the libtmux-workspace README into task sections. Its "Command-line interface" heading is gone, and the `libtmux-workspace load` command is now documented under "Create the session". The Ruby Workspace guides page still linked the old anchor, the one broken internal link CI found at fa7b989. what: - Point the source-owned CLI guide link at #create-the-session.
why: The new pins add API surface. Ruby's model moves to libtmux-ruby 20744401e and gains declarations whose signatures name Ruby's core classes, and Rust's moves to 6942c1e60. The CI type-name gate at db000ee (run 41760766017) counted ruby 2109 -> 2129, led by String, Integer, Numeric, Symbol and Array, and rs 420 -> 421, led by the impl keyword. Those names were plain before this change too; the count grows with the surface, not with a resolution that broke. The gate says to state that and raise deliberately, as the Python and Lua ceilings were raised before. what: - ruby ceiling 2109 -> 2129, rs ceiling 420 -> 421.
tony
marked this pull request as ready for review
November 4, 2026 09:15
tony
temporarily deployed
to
docs-preview-cleanup
November 4, 2026 09:15 — with
GitHub Actions
Inactive
This was referenced Nov 5, 2026
tony
added a commit
to libtmux/libtmux-ruby
that referenced
this pull request
Nov 5, 2026
why: The docs site shows each API example as a program a reader saves in an empty directory with a one-gem Gemfile, and states one tmux floor, 3.3a, for every page. examples/api/workspace_apply.rb loaded a file from the checkout, and control.rb and async_control.rb need tmux 3.7, so libtmux/docs#247 held all three back (#92). what: - workspace_apply.rb writes the workspace file it loads, so it runs saved alone; a test keeps its document equal to examples/workspace.yaml. - Version 2 of examples/manifest.json lets an API example list the tmux capabilities it needs in api.capabilities, checked against the library's capability registry; the control programs list control-mode. The docs export adds tmux_releases, the release each API page states. A version 1 manifest from an earlier release is checked and exported as before, and the check loads the registry only for version 2, so docs rebuilds of earlier releases are unchanged. - capabilities.json lists more routes to each refusal: the Async server's open_control, delete_buffer, new_session, run and run_group, Server#run_group, ControlConnection.new and Async::ControlConnection#exchange. - The installed-example test expects UnsupportedFeatureError for the listed capabilities a daemon lacks, by the library's own available?.
This branch was previously deployed
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.
Why
libtmux-ruby's own docs build failed in libtmux-ruby#90 (step "Build the selected Ruby documentation tree"):
core-apis: ruby/send-keys names LibTmux::Server#open_control, but the program never uses open_control.examples/api/capture.rbstopped attaching a control client when the library began requiring tmux 3.7 for one, and now pollspane.capture, butcore-apis.jsonstill listed the five control-mode symbols it used to call. Docs CI passed because it reads the example cache, which is bound to the revision of the Ruby API model, and that pin was a week behind libtmux-ruby master. The port's build reads the live file.A guard for this already exists.
apisUsedis run over every cached showcase program bycore-apis.test.ts, andprogramProblemsbycore-capabilities.test.tsandportCapabilities. The incident was a stale input, not a missing check: with only the model and cache refreshed,core-apis.test.tsfails on exactly the message above.What
The Ruby pin moves to libtmux-ruby 20744401e (master). The model, the example cache and everything bound to the pin are regenerated with the repository's generators from the committed bytes of a fresh read-only clone. The refresh brings unrelated API changes, kept as generated:
LibTmux::Support,Support::Versions,tmux_versionandsupports_control?onServerandAsync::Server, andrequire_supportedandserver_versionon both support modules. None is removed.version_timeoutjoinsServer.new,Server.openandAsync::Server.open;environmentjoinsEndpoint.newandMCP::CLI.run;PaneSnapshot#pidmay be nil;Pane#break_outdocumentsProtocolError; theEndpointandServerdocs gain text.Pane#break_outnow also sendslist-paneswhenbreak-paneprints no window ID, andrename-windowafter its repaired tmux 3.7break-pane.ruby/send-keyslists what the program calls. The five control-mode symbols (Server#open_control,ControlConnection#exchange,ControlConnection#subscribe,ControlSubscription#next,ControlEvent#data) are gone. The list isServer,Server.start,Server#new_session,CreationReceipt#pane,Pane#send_text,Pane#send_keys,Pane#captureandCommandResult#stdout, each resolved against the refreshed model. The capability's title is "Send keys" and the data holds no summary, so nothing on the Core page claimed the program watches through control mode, andcore-capabilities.jsonandcore-home.jsonneeded no change. The Core home still says Ruby can watch via control mode, which stays true:LibTmux::ControlConnectionis in the model.The guard now covers its own incident.
core-apis.test.tstakes the ports to check from the capability table instead of a list of three, and pins the polling program as a frozen fixture: the corrected list resolves against it, and a list that still names any of the five control-mode symbols is refused with that symbol's name.What the refresh forced (each its own commit):
tmux-usage: the Ruby rules cite lines of the old source. 219 citations were carried to where the same text sits at the new revision, and the eight dispatcher chains gain theruntodispatchhop thatServer#runnow has.(::String value)), which the newSupportmethods use.rbsSignaturekept::String valueas the type; it now reads the name, and a union still ends in its last member.LibTmux::Supportis recorded as unsettled innav-config.ts.manifest-packages.jsonand the destination evidence move to 0.1.1; the evidence is re-verified whole because its tests hold every record to one check date.Decision for the reviewer
The Ruby
celljob now carriesif: needs.plan.outputs.run_cells == 'true', and the oldworkflowSteprefused any job-levelif, soruby/create-windowsandruby/send-keyscould no longer be shown as run by CI. The commit "Accept a job that waits on its workflow's plan" addedplannedRun, and the review follow-up below narrowed it. It now allows one form only. The capability entry names the flag (ci.planOutput,run_cellsfor both Ruby entries). The job'sifmust be that flag from a job itneeds, compared with'true'. That plan job must have noneeds,ifor tolerated failure, and must publish the flag as${{ steps.<id>.outputs.<flag> }}of a step that exists and carries no condition. Every other condition is still refused, with a test for each. The port'sci-planselects cells for every push and pull request; this check cannot read that. If you would rather keep the rule strict, drop that commit and the two Ruby capabilities leave the showcase.For the wave tooling (outside this repository)
The r12 packet's
check-core-capabilities-heads.pyshould also runapisUsedover the programs it reads at each port's head, with the same lists ascore-apis.json, so a port-side change that drops a listed symbol is caught before the port's build is. Port the function as it is: comments and strings are blanked first, a method counts only in its call forms (.name,::name,&:name), and a type or constant counts as a bare word.Verification
core-apis,core-capabilities,core-home,port-landing-page,product-copy,example-sources,ruby-api-examples) passed 182 tests on main. With only the model and cache refreshed they failed 24, among themcore-apisonruby/send-keys names LibTmux::Server#open_control, but the program never uses open_control. On this branch they pass.plannedRunwas added.gen-tmux-usage --check --source --ports rubyregenerates Ruby from the checkout and matches;gen-tmux-usage.negative.mjs,check-example-blocks.negative.mjs,gen-product-copy.negative.mjsandgen-workspace-showcase.negative.mjspass;check-nav,check-api-links, and the--checkmodes of the model, example-source, mentions, workspace-showcase, workspace-entities, product-copy, tmux-catalog and shell-ports generators pass.astro checkreports no errors andoxlint --type-awareno warnings in the changed files.scripts/check-example-prose.py --example api --port ruby, one per program on tmux 3.3a and 3.7c, restoring the gems from rubygems.org with Ruby 4.0.7: 15 programs passed on both releases, and the two control-mode programs passed on 3.7c and stop on 3.3a withcontrol connections require tmux 3.7 or newer.LIBTMUX_DOCS_CHECKOUT_RUBY.Not done or failing:
workspace_apply.rbfails on both tmux releases withLibTmux::Workspace::ConfigError: workspace file could not be read. It is new at this pin and loads../workspace.yaml, which no page displays, so the four pages that show it are not self-contained. Their receipt entries say "Not verified" and list no tmux release. This needs a fix in libtmux-ruby; I made no port change.gen-manifest-packages --checkstill reports Elixir, as on main: its committed entry names an older revision than its model.check-clipboard, and the browser checks. The full destination run also moved registry versions for other ports, recorded as found.Review follow-ups
An independent review returned changes needed. The findings and what this branch does about each:
The Ruby source-guide cache was stale (high).
site/src/data/port-guides/ruby.jsonstill recorded libtmux-ruby 9b1545562 while the model recorded 20744401e.stage-port-docs.mjsrefuses that (ruby: guide source ... differs from integrated model ...) andbuild-site.shruns it with--cachedon every build, so every port's publisher build and this PR'stestjob failed, at "Stage integrated source guides". The cache is regenerated with--port ruby --from-source --refresh-cachefrom a clean clone at 20744401e. From the committed bytes,--from-sourcewith the CI checkouts,--cached --check,--cached(a plain build and withLIBTMUX_DOCS_PORT=rs) andgen-mentions --checkpass.The Ruby MCP caches were stale (high). The protocol snapshot, the Ruby rows of the tool matrix and the recorded transcript are regenerated at 20744401e with
gen-mcp-protocol.mjs --port ruby,gen-mcp-tools.mjs --port rubyandgen-mcp-transcript.mjs --port ruby, each followed by its--check. The other ports' matrix rows are unchanged.The Rust and TypeScript replays fell back to "Requests only". After the r12 wave the recorded contracts of both transcripts differed from the tool schemas at each port's master:
run_shell_command's output schema for Rust, andrun_shell_command's andlist_windows's for TypeScript. All input schemas matched. Both are recorded again at libtmux-rs 6942c1e60 and libtmux-ts 073b9d7de, each built and run by the repository's own generators from a clean read-only clone, in a private tmux sandbox. Every contract in each transcript now equals the snapshot's and the matrix row's for that tool, for all three of Ruby, Rust and TypeScript. TypeScript's master also registers five program tools (start_program,read_program,run_program,wait_for_exit,stop_program) that the committed snapshot predates, so the matrix lists them. The first Rust recording caught the split pane's command astmux, the moment before its shell started; three repeats gavesh, as the previous recording and TypeScript's show, and the committed file is one of those repeats.plannedRunaccepted too much (medium). A cell gated onrun_development, a plan output hard-wired to'false'or reading a step that does not exist,needs: [other] # plan, a plan that needs a conditional job, and a plan whose only step isif: falseall counted as run by CI. See the rule under "Decision for the reviewer". Comments are stripped beforeneedsis read. A job-levelifwhose comment readscontinue-on-error: falseis refused too; the old check let it through. Each counterexample has a test,needs: otherhas one that pins the membership check, and the real Rubycompatibility.ymlat 20744401e still passes for both entries and is refused once the entry names no flag. Sixteen of the new tests fail against the previous predicate.A hard-coded count in
ruby-api-examples.test.ts(low).toBe(18)is now a comparison, as sets, of the receipt's source files with the files the model's example blocks link, so a mismatch names the file.Destination-evidence churn (low). Left as is. Keeping only the four RubyGems records and main's other records fails three age and future-date tests in
destination-evidence.test.ts, which hold every record to the file's one check date, so the file cannot be split cleanly. The churn is thecheckedAtbump on every record plus the versions the registries of other ports now report (Maven Central, crates.io, hex.pm, LuaRocks, the Swift Package Index).Verification of this follow-up (it supersedes the counts above):
astro checkreports no errors or warnings, andoxlint --type-awareis clean on the changed files.gen-manifest-packages --checkstill reports Elixir, as on main.gen-mcp-tools.mjs --checkandgen-mcp-protocol.mjs --checkwithout--port, because this machine's Python, Julia and Go checkouts sit at other heads than their committed snapshots, which fails the same way on main. The--portchecks for Ruby, Rust and TypeScript pass. Also not run: the site build, the full publication audit and the browser checks.Port follow-ups (libtmux-ruby)
Two problems in the Ruby API example pages need a change in libtmux-ruby; this branch makes none. It holds the eleven pages that show the affected programs back (see "Third review follow-ups"), so no page states the wrong text, and they come back when these fixes land. The native runs record the problems.
Four reference pages tell readers to save a program that needs a file they were not shown.
Workspace.load,Workspace#plan,Workspace::Plan#stepsandWorkspace::Plan#applyshowexamples/api/workspace_apply.rbafter a Gemfile. The program callsLibTmux::Workspace.load(File.expand_path("../workspace.yaml", __dir__)), which isexamples/workspace.yaml, and no page displays it. Saved alone, the program stops withLibTmux::Workspace::ConfigError: workspace file could not be read. Fix inexamples/api/workspace_apply.rb: write the document to a temporary directory inside the program and load it from there, which keepsWorkspace.loadin use and the expected output (planned steps: 11,applied: true,sessions: work) unchanged.Keep the
begin,docs:begin mainandrescueframe the file has now. The raw heredoc (<<~'YAML') keeps the\ninprintfliteral. Saved alone in an empty directory, the file at the pin stops with theConfigErrorabove, and this version prints the expected three lines (checked on tmux 3.8a, not on the receipt's 3.3a and 3.7c). After the port moves its pin, refresh this repository's example receipt so the four entries are verified on both tmux releases.Two control-mode programs say "tmux 3.3a or newer" although the library needs 3.7.
examples/api/control.rbandexamples/api/async_control.rbshow on seven pages (Server#open_control,ControlConnection#exchange,ControlConnection#subscribe,ControlSubscription#next,Async::Server#open_control,Async::ControlConnection#events,Async::ControlSubscription#next). A control client below tmux 3.7 is refused withcontrol connections require tmux 3.7 or newer(gems/libtmux/lib/libtmux/capabilities.rb, capabilitycontrol-mode,since: "3.7"). The sentence is not in libtmux-ruby:attachCompleteRubyExamplesinpackages/api-model/src/languages/ruby-examples.tswrites the same floor into the intro of every complete program, and the port's manifest has nowhere to say otherwise. The fix therefore has two halves. In libtmux-ruby, add the floor to the two manifest entriesapi_controlandapi_async_controlinexamples/manifest.json, as"tmux": "3.7"besidedescriptionandoutputunderapi; the other programs omit it. In this repository, once that lands, readapi.tmuxinattachCompleteRubyExamplesand use it in place of the fixed3.3a, defaulting to3.3a, and regenerate the Ruby model and receipt.These 11 pages come back when the fixes land.
HELD_RUBY_PROGRAMSinpackages/api-model/src/languages/ruby-examples.tslistsexamples/api/workspace_apply.rb,examples/api/control.rbandexamples/api/async_control.rb, each with its reason. When libtmux-ruby merges theworkspace_apply.rbchange, move the Ruby pin, delete that entry, regenerate the model (gen-api-model --port ruby) and refresh the example receipt for the fourWorkspacepages. When its manifest declaresapi.tmuxforapi_controlandapi_async_control, read it inattachCompleteRubyExamplesin place of the fixed 3.3a, delete the other two entries and refresh the receipt for the seven control-mode pages. An entry left behind after the manifest drops its program fails the generator andruby-api-examples.test.ts.Second review follow-ups
A second independent review at f12be07 confirmed the earlier fixes and asked for four more changes. CI at that head also failed in the browser matrix. This section supersedes the statements above where it differs: the Rust Core cache is now refreshed at the Rust pin (Go's is not), and
check-clipboard's complete-examples check has been run.The TypeScript and Rust API models now match the MCP matrix (medium). The matrix and snapshots were recorded at libtmux-ts 073b9d7de and libtmux-rs 6942c1e60, while the models still described 46cccfef2 and a6fc2a656, and a tool page builds its source link from the model's revision. So
read_program,start_programandstop_programlinkedpackages/mcp/src/tools/programs.ts, which does not exist at 46cccfef2, andrun_programandwait_for_exitcited files that do not register them and kept no line. Both models are regenerated withgen-api-modelfrom clean read-only clones of the two masters, and everything bound to the pin follows, each from its own generator and checked in its--checkmode: the example cache, product copy, workspace showcase (which drops its TypeScript pin, as the generator requires once the model has caught up), the manifest index at 0.1.2 and the destination evidence for it (a live read-only run: crates.io lists 0.1.2 for all four crates, the npm pages answer 403 as before), the tmux usage rules and data, the navigation sidecars, the Rust example receipt, the prose-link review lists, and the MCP matrix rows. TypeScript gains 68 symbols and loses none (theServer.spawnprogram API). Rust gains 13 and loses three constants (PANE_BORDER_FORMAT_PER_PANE,PROMPT_HISTORY,SERVER_ACCESS).registrationFileProblemsholds every registration's file to the committed file list of its model, andmcp-source-links.test.tsruns it over the committed matrix, so it runs in CI with no checkout. It also requires the TypeScript and Rust registrations to sit on their model's revision, so it cannot pass by judging nothing.registrationLinkProblemsandscripts/check-mcp-source-links.mjs, added to the publication audit, read each registration from a checkout at the revision it cites: the file exists, the line is inside it, and the wire name is a whole word within its own registration. A port with no checkout, or without the revision, is reported as skipped. Negative cases cover a missing file, another revision, a file that does not register the tool, a line past the end or none, a name that is only part of another or belongs to the next registration, and the script run over a repository that lacks the cited file.Server.spawnbrings (program.Program,types.ProgramPlacement,types.ProgramExit) andcommon.assertSupportedTmux, and Rust'ssrc.SandboxCanary, are recorded as unsettled with their reasons, asLibTmux::Supportwas. A reviewer who wants a TypeScript "Programs" bucket would replace those three entries.check-example-prose.py --example api --port rson tmux 3.3a and 3.7c, Rust 1.99.0,cargo add libtmux@0.1.2resolving to 0.1.2 on crates.io: 14 runs, all exited 0 with their expected output. TypeScript has no complete API programs.The Rust split-pane answer no longer races (low). The Rust server describes a pane the moment it splits it, and tmux names the forked child
tmuxand reports no path until the shell has replaced it. Of thirteen recordings, five caught thetmuxform; the committed file held the other unsettled form (shwith a null path). A server cannot be waited on from outside its answer, so the recorder now rejects an unsettled answer and records again:verifySettledPanesrequires every pane an answer describes to run the shell (catfor the Ruby session that borrows one) and to name a path, andgen-mcp-transcript.mjsrecords up to six times before it fails, stopping only the servers of its own sandbox between attempts. The Rust transcript is recorded again with a settled pane, and a unit test requires every committed recording to satisfy the check.mcp-replay.test.tskeeps pinningsh, which is now what the recorder guarantees.plannedRunrefuses a cell that waits on a skippable job (low). 54147eb is kept as written and pushed.alwaysRunsfollows every other job inneedsto the end and refuses anif, a tolerated failure, a job the workflow does not define, and a cycle. Its six new tests fail against the previous predicate (checked by putting the previous file back) and pass with it; the real Rubycompatibility.ymlstill passes for both entries.The CI failure was a wrong route in the receipt.
checkCompleteApiExamplesfailed withLibTmux::Workspace#plan: HTTP 404. The nine Ruby programs onWorkspacedeclarations namedports/ruby/reference/<slug>, but the site serves a workspace declaration under its product,ruby/latest/workspace/reference/<slug>(productApiHref). The check reads the route from the receipt and stops at the first failure, so it had not looked at the pages after that one, andruby-api-examples.test.tsspelled the same core path and agreed with the receipt. The nine entries now name the workspace reference. A test over every receipt entry requires the page to be the oneproductApiHrefgives the declaration, plus one that a workspace declaration is served under the workspace reference. The browser matrix's other checks had passed in that run; the log held no other failure.Verification of this follow-up
checkCompleteApiExamplespasses for all 295 programs on 285 pages. With the nine old pages put back it fails withLibTmux::Workspace#plan: HTTP 404, as in CI. The server mirrorscheck-dev.mjs's own setup; the rest of that matrix was not run.LIBTMUX_DOCS_CHECKOUT_<PORT>: 256 files and 8267 tests passed; 5 files and 351 tests are skipped because nothing is assembled. Without them, six tests fail on this machine only because its standard ts and rs checkouts lack the new commits (port-landingandcore-capabilitiesread the workflow at the model's revision); CI has no such checkout and skips them. The api-model suite passes (60 files, 1305 tests, one skipped).astro checkreports 0 errors and 0 warnings; it needs--max-old-space-size=8192here.oxlintreports nothing new in the changed files.gen-mcp-protocol --check,gen-mcp-tools --checkandgen-mcp-transcript --checkpass.gen-api-model --checkpasses for ts and rs;gen-example-sources,gen-product-copy,gen-workspace-showcase,gen-mentionsandverify-destinationspass in their--checkmodes;gen-tmux-usage --check --source --ports ts,rsregenerates both and matches;check-nav,check-api-links,check-source-linksfor ts, rs and ruby, andcheck-mcp-source-linkspass, and so do the negative controls of tmux usage, the workspace showcase, product copy and example sources.stage-port-docs.mjs --cachedpasses plain and withLIBTMUX_DOCS_PORT=rs.gen-mcp-toolsandgen-mcp-protocolwithout--port(this machine's other checkouts sit at other heads).gen-manifest-packages --checkstill reports Elixir, as on main. The line and wire-name half ofcheck-mcp-source-linksneeds a checkout, so CI runs only its file half.Third review follow-ups
A third independent review at b2d6807 confirmed the pins, regenerations, transcripts, the retry loop and the receipt routes, and asked for the changes below. Root ruled that text known to be wrong is not published. CI at b2d6807 also failed in the Astro build; that is the last item. This section supersedes the statements above where it differs.
Eleven Ruby pages no longer show a program (medium). The pages of
Workspace.load,Workspace#plan,Workspace::Plan#stepsandWorkspace::Plan#applytold readers to saveworkspace_apply.rb, which fails alone withConfigError. The seven control-mode pages (Server#open_control,ControlConnection#exchangeand#subscribe,ControlSubscription#next,Async::Server#open_control,Async::ControlConnection#events,Async::ControlSubscription#next) said "tmux 3.3a or newer" while the library refuses a control client below 3.7.attachCompleteRubyExamplesnow takes a list of held programs, each with a reason, and attaches none of a held program's symbols. A list entry the manifest does not select fails the generator, andruby-api-examples.test.tsfails if a held program is attached to any page, if a held program is no longer selected by the manifest at the model's revision, or if a program that is not held is missing. The Ruby model is regenerated withgen-api-model --port ruby(only the eleven symbols change, each losing its four example blocks) and the eleven receipt entries are removed. Complete programs now attach to 44 Ruby pages, not 55. The other programs keep their receipt entries.I did not derive a floor for the control programs. The manifest entries have no tmux field, and the 3.7 the docs hold is prose: the Ruby model says "tmux 3.7 and newer" in the doc of
Server#supports_control?, and the cached source guides say "Control connections need tmux 3.7 or newer". Reading a version out of prose would be a guess, so the pages hold back instead.A link into a neighbouring tool's registration passed (medium).
registrationLinkProblemsended a span at the next line that any cited registration named, so a tool that cited its neighbour's line no longer cited its own, and the span ran on into its own registration.get_window_infocitingget_session_info's line in TypeScript,contract.rsline 555 for 579 in Rust, andtmux_runcitingtmux_snapshot's line in Ruby all passed. A registration now reaches from its cited line to the first line that names its tool, read from the file, and no two reaches in a file may share a line. Negative cases shaped like the three ports (TypeScript's name afterregisterTool(, Rust's after an attribute whose description names the next tool, Ruby's in constant lists far from the branch) pass at each tool's own line and refuse the neighbour's line and the lines before its name. With the pinned checkouts, in a copy of the matrix with those three citations altered,check-mcp-source-links.mjs --port ts,rsandrubyexited 0 before and exit 1 now; the committed matrix resolves for all ten ports, and moving each tool of each port to each neighbour's cited line is refused in all ten. A line in a neighbour's body, past the line that names it, is not refused: the file alone does not say where that body ends. The existing case of a name that lies in the next registration now reports the overlap in one message naming both tools.Smaller changes.
mcp-source-links.test.tsnow requires Ruby, not only TypeScript and Rust, among the registrations on their model's revision (low).neededJobsreads a flow list that wraps onto further lines through its closing bracket, soneeds: [plan,withlint]below is read as both (low). The header ofgen-mcp-transcript.mjsdescribes the settle-and-retry (low). The branch history is out of thecore-apis.test.tscomment and in the commit message (low). The Rustjustfileevidence text incore-capabilities.jsonchanged toscript-env examples example-tables; it is true at 6942c1e60, where thecheckrecipe listsscript-env examples example-tablesin place of the earlierdoctest api-examples examples example-tables(low).The dangling-reference ceiling. The Astro build failed with "62 unresolved mentions exceeds the ceiling of 60". The tally by port in the CI log (fsharp 28, swift 10, ruby 6, scala 6, kotlin 4, lua 3, powershell 2, csharp 1, py 1, ts 1) is the 60 unresolved mentions the committed
mentions.jsonholds plus two PowerShell mentions it does not; main's list holds 57. The Ruby guides cached at the new pin added four spans that name nothing in the Ruby model and dropped one:Enter("tmux key names such asEnter"),Example.checkon two pages ("theExample.checkassertion helper from the complete program") anderror.message("With--json, the refusal is inerror.message"). The TypeScript and Rust refreshes added none; TypeScript's[Symbol.asyncDispose]is on main too. Each of the four is not API, and the resolver now says so rather than the ceiling moving.Example.<member>is the example fixture, asExamplealready was. A capitalised word in a sentence about "key names" names a tmux key, and a dotted lowercase path in a sentence about JSON is a field of that output; this is read from the span's own sentence and only after the name failed to resolve, so a name a port defines still links. The ceiling is unchanged at 60. Run through the real linking plugin over the staged Ruby guides, Ruby reports six unresolved before and two after (EnumerableandLibTmux, both on main); the committed list goes from 60 to 56, so the build reports 58. Cases cover each rule, the same spellings without the cue, a cue in the previous sentence and a span shaped unlike what the cue names.Verification of this follow-up
LIBTMUX_DOCS_CHECKOUT_<PORT>, through the heavy-job wrapper with two workers: 256 files and 8272 tests passed; 5 files and 351 tests are skipped because nothing is assembled. The api-model suite passes (60 files, 1316 tests, one skipped).oxlintreports nothing new in the changed files.needscases failed on the oldneededJobsand pass; the first and third of the new tests over the committed Ruby model failed with the eleven programs attached and pass; the Ruby coverage assertion fails when the Ruby model's recorded revision moves, where the old one passed.gen-api-model --port rubyandgen-mcp-transcript --port ruby(a comment-only change, but run) pass in their--checkmodes against a clean clone of libtmux-ruby at 20744401e, andgen-mentions --checkpasses;check-mcp-source-links.mjsresolves all ten ports at the revisions the matrix cites, with ts, rs and ruby cloned read-only at their pins and deleted afterwards. The TypeScript and Rust transcript checks were not re-run: the change to that script is a comment.check-api-links,check-nav,check-example-blocks,check-example-indentationand the--checkmodes of the example-source, workspace-showcase, workspace-entities and product-copy generators pass.