summaryrefslogtreecommitdiff
path: root/src/ninep/ctl.zig
Commit message (Collapse)AuthorAge
* Undo after Get file or Edit's e puts the old name back with the old text and ↵Gabriel Schneider4 days
| | | | | | | | | | its clean state, so a Save or Putall after it writes nothing over the loaded file; a Get file that fails, or is asked about unsaved edits, renames nothing; Get file in a directory pane is refused with how to open it ctl.getFile is the one load-under-a-name path: it reads first, then makes one undo step (whose snapshot now keeps the name and its saved state), renames and loads, and leaves the pane clean. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Edit runs acme's file and shell commands: < | > through the session's Shell ↵Gabriel Schneider4 days
| | | | | | off the loop (one undo step, a failure changes nothing, its stderr in +Errors, a 9P write answered when done), X and Y over the open text panes, b B D e r f w and the "file" address; checked against sam -d, and the reference's Edit section and Coming from acme row say so Co-Authored-By: Claude Opus 5.5 <[email protected]>
* The reference's acme words and its Coming from acme rows say what Get, ↵Gabriel Schneider4 days
| | | | | | Putall, Zerox, Tab, Incl, Load and Indent are now, and what covers Font, Send and ID; Load is tested as Restore Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Zerox opens a second pane on a file pane's buffer: one text, undo and ↵Gabriel Schneider4 days
| | | | | | | | | | | | | | | | unsaved state, a scroll and a cursor each; an edit in either is spliced into both at File.setContentSpan, O(the edit) more, and what a Save, a get, the disk watch or a rename changes on one is copied to the rest after each step Twins share the content buffer, the History and, while it is one, the line index; the other twin's cursor and scroll move past the splice as acme moves a clone's q0. Del of one twin asks nothing while another holds the text, Exit lists the text once, and a closed twin keeps a copy of its own until it is torn down. Both are in the index, each its serial, one name. An event reader on one hears no edit made through another (first cut). A dump keeps the twins as two panes on the file, not linked again. perf: edit-twin, typing beside a twin of the 50k-line file, gated within 3x of edit-char in the same run; the three baselines re-recorded for it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Tab N sets a tab's width in cells, 1 through 16, in every pane, drawn and ↵Gabriel Schneider4 days
| | | | | | | | | | | counted (wrapping, clicks, the cursor); bare Tab says it; the ctl read and a Dump keep it A session setting, not acme's per window: the width is read by File's pure width functions (~130 callers that are given no pane), so it is one value, config.tab_now, that Runtime.apply sets and a new session resets. A file:L:C column counts runes, as compilers do, and never depended on it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Incl dir ... adds directories a look at a name found nowhere else is tried ↵Gabriel Schneider4 days
| | | | | | in, as acme's includename, <stdio.h> too; bare Incl lists them and Incl - empties them; the session's, seeded with /usr/include and /usr/local/include, kept in a dump; Indent's refusal says Enter keeps the indent always Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Putall saves every pane holding unsaved edits to a file, each as its own ↵Gabriel Schneider4 days
| | | | | | Save, a refused one said and the rest saved all the same Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Get reads a pane's file again, refusing unsaved edits once as Del does, and ↵Gabriel Schneider4 days
| | | | | | Get file loads that file into the pane; in a directory pane it reads the directory again. Load is Restore, as Put is Save; Abort's refusal says it is acme's debugging word Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Back and Forward keep to the focused file: Ctrl-o, Ctrl-i, the mouse's side ↵Gabriel Schneider4 days
| | | | buttons (now the tty's 8 and 9 too), the builtins and 9P go to the nearest place that way in this pane, else in this file in another pane, else of any file; JumpScope all takes every place in order as before
* A look at a directory opens a pane listing it, as acme's directory window: ↵Gabriel Schneider4 days
| | | | | | named dir/, its entries in acme's columns (bytewise, dotfiles kept, a directory's marked /), a look at an entry from there, Get or a look again reads it again, never dirty, Save refused; DirLook terminal keeps ls in a terminal Co-Authored-By: Claude Opus 5.5 <[email protected]>
* An image's or a terminal's tag says petscii:on when it is drawn as glyph ↵Gabriel Schneider4 days
| | | | | | | | art, as it always is with no graphics; Petscii on|off still chooses for when there are The images and tagbottomimage goldens are re-recorded for the word. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Builtins are chips that link to a glossary generated from the registry, and ↵Gabriel Schneider4 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | the virtual filesystem is drawn as a tree #word draws a builtin as a chip on the tag's ground over a rule (Look and Exec in acme's look green and exec red), a <code class="word"> in HTML, and links it to its entry in a new Builtins chapter wherever that chapter is in the same output (the book, the site). The chapter is drawn from docs/typ/builtins-tty.json and builtins-gui.json, which `zig build builtins-json -Dplatform=tty|gui` writes from the registry and the Help rows (src/builtins_json.zig, tools/gen_builtins_json.zig; summaryOf in ninep/ctl.zig is now pub); style.typ merges them, marking the window-only words. The doc steps refresh their build's file first, and each build's unit-test fails when its file is stale. Everyday words first (`everyday` in style.typ), then groups by leader prefix as Help has them, then settings, the words with no path, debugging and internals, and last an A-Z index of every word. `zig build book` joins the doc steps. #fstree draws a directory tree with its connectors, a description column and links to the sections that document each file; reference.typ draws the served tree with it, each pane, pty and column file on its own line, and a test in config.zig checks it against ninep/tree.zig's files both ways. The cheatsheet has a two-level version. On the site the tree reads as `tree` text in w3m and wraps descriptions under the names on a phone. The site's tutor draws its B1, B2, B3 and 1-2 chords as mouse glyphs; the landing credits Plan 9 and acme, as does the book's title page; install.typ no longer lists the mirrors (the footer does). Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A setting only the other frontend has (Font, GripWidth in a terminal) is ↵Gabriel Schneider4 days
| | | | | | passed over in the init file, not failed: one init file serves the window and the terminal Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A +Pager keeps its program's colours: the session parses what /pager is ↵Gabriel Schneider4 days
| | | | written after its directory with ghostty-vt, SGR becomes spans drawn over the plain text, every other escape is dropped, and PagerColor off pages it plain
* Saving the init file in a pane (Config) applies its settings at once; its ↵Gabriel Schneider4 days
| | | | | | | | | | | | commands are not run a second time A setting changed in the init file took effect only at the next start. The host's Save now hands a written init file's text back to the core, which applies its setting lines again (each idempotent), with the same notice and err for one that fails; a command line in it (`Newcol`) belongs to the start and is left alone. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* The init file reads past a UTF-8 BOM, and every line it fails is said and ↵Gabriel Schneider4 days
| | | | | | | | | | | | logged with its line number, never silent A BOM made the first word unknown, a word no builtin has was skipped without a word, and a setting that refused its value said nothing (the init file ran with announcing off). Each failing line now leaves a notice and an `err - init file line N: <why>` record; `#` still starts a comment only at a line's start, so `#ff0000` stays a value. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* `answer z` quotes `z`, a bare `name` says it names nothing, and `Repl ↵Gabriel Schneider4 days
| | | | | | | | | | | | | klingon` is invalid (EINVAL), its message citing no doc path A refused answer quoted the word `answer`, not the choice refused. A `name` with nothing after it was an unknown control message. A Repl of no such language failed EIO with a pointer into the reference; it now says `invalid language` (EINVAL, here and through a mount) and how many more there are. A builtin's failure that says invalid anywhere is EINVAL, as 9ns reads it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Mount dials: a relative socket path is a bad dial address, a socket path not ↵Gabriel Schneider4 days
| | | | | | | | | | there says no such socket (ENOENT), and the hint says tcp! takes a numeric address `Mount peer rel/x.sock` dialled relative to wherever the editor ran; `Mount peer unix!/nonexistent` said `dial failed: no answer` (EIO); and the hint offered `tcp!host!port` where only a numeric address is taken. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* /pager takes one directory an open, as pty/run takes one line: a second ↵Gabriel Schneider4 days
| | | | | | | | | | line, in the same write or the next, is refused EINVAL Through a mount `printf 'a\nb\n' > pager` arrives a line a write, so the second asked for another +Pager on the same open, its answer replacing the first. An open that asked already refuses another. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* An unsaved pane restored from a dump that says nothing of its disk (no ↵Gabriel Schneider4 days
| | | | | | | | | disk_hash, an older dump) asks at its first Save, as for a file changed on disk What its text was read against is unknown, so a Save could write over a newer file without a word; it now asks once first. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A command or terminal pane refused for room is refused before anything is ↵Gabriel Schneider4 days
| | | | | | | | | | | | | | | logged or answered of it, and a serial is never handed out twice A command pane with no room logged `new N` and `run N ls`, was answered to its exec, then went with `del N` and no `exit N`; and sync gave N back (`next_serial -= 1`) to the next pane made, so a script holding N found another pane under it. Tty did the same. Both now take a refused placement back at once, before the run is logged or the serial answered (the pane was never announced, so it leaves no record), and fail the write with no room. The decrement is gone: gaps are fine, and a serial once said names one pane for good. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Grep's miss says `Grep: text not found`: it is a literal search, and ↵Gabriel Schneider4 days
| | | | | | | | | "pattern" suggested a regular expression Still ENOENT through a mount ("not found" is in 9ns's table, and the unit tests' errno-words check holds it so). The guide's quote follows. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A Mount dial with a network pardes does not dial (`nowhere!x`) is a bad dial ↵Gabriel Schneider4 days
| | | | | | | | | | | address, EINVAL, not a session name that does not answer A dial with a `!` and no `/` that was neither tcp!, quic! nor unix! was taken for a session's name, failing as `dial failed: no answer`, EIO. It is now refused as the malformed address it is, before any dial. A path with a `!` in it still dials. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* DumpDir refuses a relative directory and one that may not be written, up ↵Gabriel Schneider4 days
| | | | | | | | | | | | | front, and a Dump that could not make its directory says permission denied A relative DumpDir was resolved from wherever pardes started, and one under a directory that may not be written was taken, the Dump failing later as `no such directory`. DumpDir now takes an absolute or `~` path whose nearest directory there may be written, and refuses any other with what it takes; a Dump refused by a directory above says permission denied (fs.deniedAbove), as a Save does. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A ctl, exec or look line over 1 MiB is refused once, EINVAL in words a mount ↵Gabriel Schneider4 days
| | | | | | | | | | | | | maps, and the rest of it is dropped through its newline, not run as a second line Past the 1 MiB a line may hold, the write that crossed it was refused and the pending text dropped, but what followed of the same line was taken as a new line and ran when its newline came: a second refusal, `unknown control message "zzz…"`, and a second err. The open now drops the rest through the newline, and the refusal says `invalid write: a line or Edit block over 1 MiB`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A look of `./x` that is not there is a miss, as `x` and `/abs/x` are: the ↵Gabriel Schneider4 days
| | | | | | | | | | | write answered, the err logged, not ENOENT `./x` and `../x` failed their write with ENOENT while every other name that opens nothing was a miss the write was still answered with. They are misses now too, said and logged `look: ./x: no such file`. A pane's own directory gone stays a failure. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A pane's ctl read says Collapse and a PDF's PdfFit and PdfTint (`collapsed`, ↵Gabriel Schneider4 days
| | | | | | | | | | | | `fit:height tint:full`), and a Dump keeps the PDF's two Neither showed in the tag or the ctl, and a Restore brought a PDF back at width fit and the default tint. The ctl read ends with `collapsed` when it is, and a PDF's `fit:` and `tint:` in the words PdfFit and PdfTint take; the dump's image record carries pdf_fit and pdf_tint (empty in older dumps: the defaults). Co-Authored-By: Claude Opus 5.5 <[email protected]>
* /pager given a file that is not a directory says so, ENOTDIR, not `no such ↵Gabriel Schneider4 days
| | | | | | directory` Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Lspinfo's exec answer is the +Lsp pane it fills: any language answer that ↵Gabriel Schneider4 days
| | | | | | | | | | | fills a pane answers that pane, made or filled again The answer was taken from the asking pane's search pane, which a listing like +Lsp never sets, so Lspinfo answered the file pane. The pane the answer filled is now known from what fillResults made or refilled, whatever kind of listing it is. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Find's notes stay at the end, under the names it found, as Grep's do: `1 ↵Gabriel Schneider4 days
| | | | | | | | | | directory skipped` no longer sorts first Find's rows are sorted by path as they are filled in, and the note line was sorted with them, so it led the list. The rows above the first note are sorted, the notes left where they are. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Grep and Find say when a cap hid what they might have found: `cut at 512 ↵Gabriel Schneider4 days
| | | | | | | | | | | | | | | | hits`, `N files read only in part (first 256 KiB)`, `walk cut at N entries`; one that finds nothing leaves the +Search as it was Grep reads 256 KiB of a file and stops at 512 hits, and its walk at 20000 files or 100000 entries; Find stops at 512 names. A cap hit was silent, so a match past one read as `pattern not found`. The walk now notes each cap it hit (fs.WalkNotes) and the +Search says it in a line under the hits, as it says directories skipped; a search that found nothing but hit a cap opens the +Search with those lines, not a miss. A Find or Grep that finds nothing and passed nothing over leaves the directory's +Search with the last search's rows, its failure saying so, where it used to empty it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A Rename the server answers for this file alone, while other open files of ↵Gabriel Schneider4 days
| | | | | | | | | | | | | | | | | its language say the name, is previewed, never applied; Lspwhy shows the files synced first zls renaming at a declaration answers with that file's edits only, cold or warm, whatever it was told of (checked against zls directly: from a call site in main.zig it renames both files, from the declaration in util.zig it never does), so the first Rename applied in util.zig and left main.zig broken. The client now looks for the old name in the other open files of the language; when one says it and the answer left it alone, the edits are a preview with a row naming each such file, and the message row says the server renamed in this file only. Lspwhy carries the open files too, so its trace shows `synced X first` for the query it explains. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Restore reads a clean file from disk, and an unsaved one whose file changed ↵Gabriel Schneider4 days
| | | | | | | | | | | | | | | | | | | | | since the Dump is said changed, its first Save asking before overwriting it A Restore put back the dump's copy of every file pane and armed the watch with the disk as it now was, so a file changed on disk after the Dump came back as its old text, marked clean, and the next Save wrote the old text over the new without a word. A clean file pane's text is now not in the dump: Restore reads its file (an older dump's copy is read past too, and used only when the file is gone). An unsaved pane keeps its text once, base64, the plain copy beside it dropped, with the hash of the file it was read against (`disk_hash`); a Restore that finds another there says the file changed on disk, and Save asks once before overwriting it, as for a change seen while it was open. file_watch.zig's own tests were never collected (no test root imported it): main.zig's test block does now, and the restored watch test says what a clean restore now reads. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Petscii, Palette, Ascii, PdfFit and PdfTint take the state their tag word ↵Gabriel Schneider5 days
| | | | | | | | | | | | | names, set rather than flipped: `Petscii on`, `Palette terminal`, `PdfFit width` Each could only flip, so a script had to read the tag to know which way a write would go. Each now takes an optional argument, the value its tag word shows (on|off for Petscii and Ascii, commodore|terminal for Palette, width|height for PdfFit, disabled|filtered|full for PdfTint), and sets it; bare, it flips as before. Anything else is refused with what it takes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A second LookWord list look in a directory refills its +Search, as a ↵Gabriel Schneider5 days
| | | | | | | | | | terminal's word looks already did, rather than stacking another Each word looked at with LookWord list opened a +Search of its own in the same directory. Word looks now share the directory's one, the next word refilling it; a `/` search still keeps a +Search each. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* `@pN:` looks at pane N, its trailing colon dropped as any look's is, and a ↵Gabriel Schneider5 days
| | | | | | | | | | | line past the end is said one way: `<file>:N has no line N`, for `:N` as for `file:N` lookAt dropped a trailing `:` from what it read but resolved the target from the text as written, so `@p33:` was `no match for "@p33:"`. And `:99999` said `address out of range` while `file:99999` said `has no line 99999`: a bare line number past the end now says the latter too. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A name write to a terminal, image or PDF and Edit on a pane with no text are ↵Gabriel Schneider5 days
| | | | | | | | | | | refused EINVAL, in words a mount maps so: `invalid rename: ...` and the data write's own refusal `rename not allowed: ...` went out EPERM, and `Edit ,d` on a PDF ran and failed late, EIO, while a data write there was EINVAL. The rename refusal now says `invalid rename` with EINVAL, and Edit on a pane with no text is refused before anything runs with the data write's words. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* /pager resolves its directory, `~` and `..` alike, and refuses one that may ↵Gabriel Schneider5 days
| | | | | | | | | | | | | not be written, permission denied; `pardes -` from such a directory pages into the session's `/tmp/../etc` named a +Pager `/tmp/../etc/+Pager`, a second one beside `/etc/+Pager`, and `~/x` was refused as relative. The directory is now home-expanded and resolved before it is checked, and one that may not be written is refused as a file's name there is (fs.deniedAbove). `pardes -` run in such a directory (`git log` under /usr/src) asks for the session's +Pager instead, so its text is still paged. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A refused /pager write leaves its open no answer, not the +Pager it answered ↵Gabriel Schneider5 days
| | | | | | | | | | before writePager emptied the answer only once the directory had passed, so a read after a refused write on the same open still gave the last write's serial. It is emptied first, as a refused look or exec leaves none. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A Rename that previews other files answers its preview pane to the 9P write ↵Gabriel Schneider5 days
| | | | | | | | | | | | | that asked it, and says on the message row that it previewed and applied nothing A multi-file Rename filled a +Search preview but the waiting exec write answered the pane it was asked from, and nothing was said. lspResponse now notes the pane it filled (fs.lsp_result); the write that waited for the answer reads that pane back, as a look or an exec answers the pane it went to, and the message row says `Rename: N edit(s) across files, previewed, not applied`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* addr, dot and limit on a pane with no text are refused `invalid address: ↵Gabriel Schneider5 days
| | | | | | | | | this pane has no text to address`, which a mount reads as EINVAL, not EIO 9ns maps a refusal back to its errno by its words; "this pane has no text to address" held none it knows. The reference quotes the new words. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* /pager's refusals say invalid, so a mount's shell hears EINVAL: `pager: ↵Gabriel Schneider5 days
| | | | | | | | | | | invalid directory "relative": must be absolute` 9ns maps a refusal back to an errno by its words, and "the directory must be absolute" and "one directory a write" held none it knows, so a shell through the mount got EIO for both. They now say `invalid`, as every EINVAL here does, and the reference quotes the new words. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A Restore whose ThemeFile fails to load changes no theme: the session goes ↵Gabriel Schneider5 days
| | | | | | | | | | | | | on showing the one it did, its file still the one named A Restore makes a fresh core, which started at the default theme and asked for the dump's ThemeFile; when that file had broken since, the failure reverted to what the fresh core had loaded, nothing, and ink became orchard. A Restore that asks for a ThemeFile now carries over the theme the old session showed and the file it loaded (and its Theme, unless the dump names one), so a failure there changes nothing. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A pane ctl takes Tty+bash as exec and the tag do: a word that takes its ↵Gabriel Schneider5 days
| | | | | | | | | | argument after a + is no unknown control message there checkBuiltin split a line at its first blank only, so `Tty+bash` was one unknown word on a ctl while exec ran it. It now splits at the + for the words whose registry entry says they take one so. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* /pager refuses a directory that is not there, ENOENT, and a relative one, ↵Gabriel Schneider5 days
| | | | | | | | | | EINVAL: no +Pager named into a missing directory, none quietly the session's `/(/+Pager` was made for a missing absolute directory, and a relative one fell back to the session's without a word. An empty line still asks for the session's directory. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A body write to an image or a PDF says its body takes no writes, not that ↵Gabriel Schneider5 days
| | | | | | | | | | the pane has no text "(a terminal, ...)" A terminal's body takes writes (its keys), so the old words named the one kind that does. data, sel and dirty keep "this pane has no text", which is true of all three. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Restore and ThemeFile failures say why in words, a ZON one with its line: ↵Gabriel Schneider5 days
| | | | | | | | | | | | | | never FileTooLarge, ParseZon or a bare `parse zon` Restore read failures named the Zig error (FileTooLarge, NotAFile, ReadFailed), a file that is no dump said `not a pardes dump (ParseZon)`, and a ThemeFile that did not parse said `parse zon`. Messages.errorWords says an error in words (the few that read badly spelled out plainly, the rest spaced and lower case, as reportError did), and zonWhy gives the parser's first error with its line: `not a pardes dump: line 1: expected ...`, `theme file x.zon: line 3: ...`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A name under a directory that may not be searched or written is refused, ↵Gabriel Schneider5 days
| | | | | | | | | | | | | | permission denied, and a Save there says so, not no such directory `pardes noperm/a/b/c` and `pardes /proc/1/root/x` took the missing directory for one Save would make, opened a pane and exited 0; its Save then said "no such directory". fs.deniedAbove finds the nearest directory there and asks whether it may be searched and written: forwarding refuses such a name, exit 1, "permission denied", and a failed Save says "permission denied", which the writer's 9P error carries as EPERM (9ns: EACCES). Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A command pane an exec open was answered is leased to it while that open ↵Gabriel Schneider5 days
| | | | | | | | | | | | | | stays open: another client's command in the same directory gets a pane of its own A finished command pane was the next command's for its directory, from any client, so with four clients scripting one session, one read another's output as its own last run. An open of a look or exec file whose write was answered a pane's serial now holds that pane: reuse passes over it and a new command pane runs the line. The open's own next command may run there again, and once it is closed the directory's one command pane is reused as before. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* pardes - asks /pager for its +Pager and reads the serial on the same open: ↵Gabriel Schneider5 days
| | | | | | | | | | | | | | another client's exec meanwhile can never take the text into its pane pardes - wrote `pager <dir>` to /ctl, then read /exec on a fresh open, which answers the session's last answer: a client that exec'd in between had its pane overwritten by the paged text. /pager takes the directory and answers the +Pager's serial on the open that wrote it, as /pane/new answers its own open; the ctl verb is gone. Client.ask writes and reads on one open. The README's look recipe reads its answer on its own open too. Co-Authored-By: Claude Opus 5.5 <[email protected]>