summaryrefslogtreecommitdiff
path: root/test/fs.py
Commit message (Collapse)AuthorAge
* A file name with a newline is refused naming it once: `pardes: ↵Gabriel Schneider3 days
| | | | | | | | | | two\nlines.txt: a file name is one line...`, not `a file name: a file name ...` The refusal's subject was the words "a file name", and its reason began with them again. It now names the name given, its newline shown `\n`, as /log and /index show one. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A name under a directory that may not be searched or written is refused, ↵Gabriel Schneider3 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]>
* `--` ends pardes's options: `pardes -- -dash.txt` opens the file named -dash.txtGabriel Schneider3 days
| | | | | | | It said `no such option: --`. What follows `--` is a name, one at most, as for any other positional. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* /index names a command's pane kind cmd, not term: a script picking a ↵Gabriel Schneider3 days
| | | | | | | | | | terminal by kind no longer picks a pane with no shell A command pane runs one line and takes no typing; a script choosing `$2=="term"` to type into got one. Its kind is now cmd; a shell's stays term. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* --wait waits on the pane its look answered, read on the look's own open, not ↵Gabriel Schneider3 days
| | | | | | | | | | | | | one found again by name in /index The forwarded look was a write and nothing more, and --wait then looked its file up in /index. /index shows names escaped (`\\` for a backslash, `\xNN`), so a file named with one matched no pane and --wait exited 0 at once, as if the pane were already closed. The look is now asked on one open (Client.ask) and its answer is the pane waited on; /index by name stays only for a look that answers none. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* pardes - asks /pager for its +Pager and reads the serial on the same open: ↵Gabriel Schneider3 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]>
* A detached session's pty is sized in pixels too, at the last frontend's cell ↵Gabriel Schneider3 days
| | | | | | | | | | | | size or a nominal 8x16 before one attached forkShell sized a new pty in pixels (ptyWinsize), but the detached server's resize set xpixel and ypixel to 0, and a pane is resized as it is placed, so a program in a detached session with no frontend (yazi) saw a pty with no pixels and drew no images. CSI 14/16/18 t were already answered at the nominal 8x16 cell. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* pardes - writes the whole text through one open of the body, so a page over ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | 8 MiB arrives whole, not as its last chunk Each 8 MiB chunk went in by its own Client.write, which opens a body with OTRUNC, so every chunk replaced the one before and a long page kept only its tail, with exit 0. The text now goes in through one open, truncated once and written at rising offsets; a long write is timed by its progress, each 9P write answered within 30 s rather than the whole transfer within 2. fs.py pages 11 MiB of numbered lines and compares the body byte for byte, and pages past the 256 MiB cap and checks the first 256 MiB and the cut note. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* pardes - keeps one +Pager per directory, refilled, named so from its first ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | log record; streams the text in bounded writes; never loses it; handles \r, NUL and BEL as a terminal does Each paged command made another +Pager until placement failed, and then git's text was lost with exit 0; a 130 MB page timed out half written and dirty; a directory whose name held a newline left a stray +New; and the log said new +New then rename. The root ctl's `pager <dir>` now makes the directory's +Pager (named before it is announced, placed without the keyboard) or empties the one there; the text goes into it a line-ended 8 MiB chunk a write, one undo step, up to the file limit and its note, and is left clean. A directory a ctl line cannot name pages into the session's. Any failure prints the text to stderr with why and exits 1. A carriage return keeps a progress line's last state and CRLF is a newline; NUL, BEL, SO and SI go; empty stdin makes no pane. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* With Pager pardes, a terminal's shell also gets SYSTEMD_PAGER and ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | SYSTEMD_PAGERSECURE=0, and the pager's path is quoted only when it needs it journalctl and systemctl read SYSTEMD_PAGER, not PAGER, and in their secure mode run only less, so a pty/run of either still waited in it; and systemd, splitting a pager on blanks and exec'ing it, never ran the quoted path. Where the user's environment sets neither, a terminal now gets SYSTEMD_PAGER as PAGER is set and secure mode off (this pager has no shell to escape to), and the path is single-quoted only when it holds a blank or shell character. Command panes keep cat. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* pardes - reads stdin into a +Pager pane, and is a terminal's PAGER and ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | | GIT_PAGER, Pager pardes|off to opt out; command panes page through cat A paging command in a terminal, or run through its pty/run, sat in less, and a pty/run never answered. `pardes -` now reads stdin to its end, strips terminal escapes (colour, OSC, overstrike), and shows it: inside a session as a clean <cwd>/+Pager pane made through pane/new and back at once, outside one as a new editor whose first pane it is, as `vim -` does. A terminal's shell gets PAGER and GIT_PAGER set to this pardes's own path and `-` where the user's environment sets neither; `Pager off`, a setting in /ctl, init and DumpConfig, leaves terminals started after it to the environment. A command pane's one-shot shell keeps cat, its output being a pane already. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A command run from a pane gets acme's $winid, the serial of the pane it was ↵Gabriel Schneider3 days
| | | | | | | | | | | | run from; one from a column's or the workspace's tag gets none PARDES_PANE names the command pane itself, so a script a click ran had no way to address the pane it was run in, as acme's win scripts do with $winid. The command pane now remembers that pane and its child gets winid set to it; a header tag's command, which no pane owns, gets winid unset. PARDES_PANE is unchanged. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Forwarding pardes FILE for a new name in a directory it may not read or ↵Gabriel Schneider3 days
| | | | | | | | | | | write is refused with words, not a pane that only fails at Save A new name forwarded into a directory that is there but locked opened an empty pane named for it, and the failure came only at its Save, long after the launch. The directory is checked first: one that cannot be read, written and entered is refused, exit 1, no pane made. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A second pardes --detach=NAME while NAME runs says "a session named NAME is ↵Gabriel Schneider3 days
| | | | | | | | | | | already running" and exits 1, with no error trace The second session built its whole core before its bind failed, then returned an error that main printed with a Zig error-return trace. The name is now checked first: a live session's socket is said, and the process exits 1 having made nothing. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* An editor killed with SIGTERM or SIGHUP removes its 9P socket, and a start ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | sweeps the pardes-9p-<pid>.sock files whose pid is gone The runtime directory had gathered a hundred sockets of dead editors: only a clean exit removed its own, and a signal or a crash left it. The terminal's kill handler and, where nothing else handles the signals, a handler of the listener's own now unlink the socket before the signal's death. At listen, each pardes-9p-<pid>.sock whose pid kill(pid, 0) answers ESRCH for, that is a socket of this user's and that nothing answers on, is removed; a live pid's, a named session's and anything else are never touched. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* name takes any byte a file name can hold, controls, edge blanks and bytes ↵Gabriel Schneider3 days
| | | | | | | | | | | | not UTF-8 included, so what name reads writes back as it was A pane opened on a file whose name held a tab, a control byte or bytes not UTF-8 read that name from its name file, and the same bytes written back were refused. Only what no file name holds is refused now: a second line and a NUL. The ctl word name still refuses a second blank after the word, as a line's words are split at blanks. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Forwarding pardes @pN for a pane the session has not is refused, "this ↵Gabriel Schneider3 days
| | | | | | | | | | | | session has no such pane", exit 1 and no pane made; one it has is looked at @p999:1 was taken as a new file's name, so a pane named <cwd>/@p999 was made and the launch succeeded; an @pN the session did have was the same stray pane. A pane address is now checked against the session: one it has is a look at it, one it has not is refused with the reason and exit 1, as a refused name is. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Forwarding to a running pardes says why in words: a name with a newline is ↵Gabriel Schneider3 days
| | | | | | | | | | | "a file name is one line", never NotOneLine The forwarding refusal printed the error's name for anything the session itself did not answer: NotOneLine, NotAFileName, NoWorkingDirectory. Those are now sentences, a dial failure is said as the message row says one, and any other error is its name's words. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* diff -u in a command pane names its files though the terminal turned the tab ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | before each timestamp into blanks A `---`/`+++` path was cut at the tab before diff -u's timestamp. Run in a command pane, the terminal had turned that tab into blanks, so the name took the timestamp with it and a look at a changed line opened nothing. The name is now also cut where blanks run up to a timestamp, either GNU diff's `2026-09-30 10:00:00...` or ctime's `Wed Sep 30 10:00:00 2026`. One blank is enough, since a tab can expand to a single blank, and the stamp is strict, so a name with blanks and no stamp after them stays whole. Tested on the parsed text and, in fs.py, on real `diff -u` output in a command pane, where a look at `+TWO` opens the new file. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Release Pardes 0.8Gabriel Schneider3 days
|\ | | | | | | Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * pardes --wait for a new name waits on the pane it made, by serial, and a ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | pane closed before it looks is done, not a failure fs.py's `pardes --wait 'wait new.txt'` failed two runs in three on the 0.0.8 merge. The stress case repeats the test's steps: 4 of 50 failed under load, each with "pardes: --wait: no pane shows that file". A new name's launch makes its pane, names it, then found it again by path in /index. The script had already seen the name, saved and removed the pane by then, so the lookup missed and --wait exited 1. Now the launch hands --wait the serial pane/new gave it. After a look, finding no pane on the file means that pane is already closed, which is the end --wait waits for: exit 0. The follow with its /index recheck covers a close in between, as before. Under the same load the stress case went 0 of 100. The same load showed a test race too, at fs.py's second Restore. The exec write is answered before the hang-up, but the client's clunk after it can meet the connection already cut. The test now requires the answer and tolerates the reset. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A path typed with a leading ~ is the home directory, at every entry point ↵Gabriel Schneider3 days
|/ | | | | | | | | | | | | | | | | | | | | | | | | | | | that takes one, through one helper Only DumpDir (dump.zig) and ShaderBuild expanded `~/`, each its own way. So a look at `~/notes.txt`, `name ~/x`, `Save ~/x`, ThemeFile, Restore and a quoted `pardes '~/x'` all took `~` as a directory of that name under the pane's. There is now one rule, filesystem.expandHome. A leading `~`, alone or before `/` or `:`, is $HOME, else the passwd entry's home; `~user` is that user's (getpwnam). As in a shell, it applies even beside a file named `~`, and `./~` names that file. Every entry point runs its typed path through it: - look, B3 and 9P, before any parsing, so `~/x:12`, `~/x:12:3` and `~/x:/re/` all work; - name, and a tag's rename; - Save <path>; - ThemeFile, DumpDir, Restore and ShaderBuild's shader files; - the forwarding launch. Recent rows are not shown with `~` and need nothing. How names are shown is unchanged. Tests: expandHome's cases, and HOME unset (passwd), with HOME passed in rather than set, since setenv moves the environ a spawned child reads. fs.py covers Save into missing directories under `~`, `name ~/x`, a look at `~/x:3`, and a forwarded `pardes '~/x'`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Save makes the directories its file goes in, always: for the pane's own name ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | and Save path alike Whether a Save made missing directories depended on how the name had come about. Config's pane did, a name written into a missing directory did (since kkrqxtsp), and `Save path` and every other name did not, failing ENOENT instead. There is now one rule: a Save makes the parent directories of the file it writes, a pane's own or a terminal's scrollback included, and the make_dirs flag is gone. A directory it cannot make still fails the write with why, as fs.py's /nonexistent-pardes-dir case shows. fs.py now also saves into missing directories by Save path and by a written name. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* winsize refuses fewer than 2 rows, saying so, and its range reads 1x2 to ↵Gabriel Schneider3 days
| | | | | | | | | | | | | 4096x4096 as the docs say `winsize 20 1` was taken and quietly raised to 2 rows, so the write succeeded with a size other than the one asked for. The range refusal also said "1x1 to 4096x4096", although fs.md says at least 2 rows. One row is now refused with "invalid winsize: at least 2 rows", and the range text starts at 1x2. size already refused anything under 20x6 with a text that names it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A tty editor killed with SIGTERM or SIGHUP puts its terminal back before it diesGabriel Schneider3 days
| | | | | | | | | | | | | | A nested pardes killed in a pane died with the default action. That left the pane's terminal on the alternate screen, in raw mode, with mouse and paste reporting on, so the shell under it was unusable until a reset. The tty frontend now handles both signals: it writes the resets a clean exit writes (keyboard protocol, mouse modes, focus, bracketed paste, SGR, cursor, main screen) and restores the cooked termios, all async-signal-safe. Then it dies of the same signal, since SA_RESETHAND put the default back. fs.py kills a tty session with each signal and checks for the main-screen switch and cooked mode; it fails without the handler. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* pardes FILE in a pane whose session answers never falls back to a nested ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | | editor: the refusal is printed, exit 1, and no stray pane is left When the environment named a live session that answered, several failures broke out of forwarding and started a whole editor inside the pane that asked: a refused name, a pane the session has not, a failed look write. Its screen was drawn over the shell. Now, once the session answers (Client.probe), every refusal is printed as `pardes: <file>: <why>`, in the session's own words (the Rerror, now kept by the client), and the launch exits 1, as acme's B does. A new name the session refuses deletes the pane made for it, so no empty +New is left. A missing environment or a session that does not answer still starts a separate editor. fs.py checks a refused name and a stale PARDES_PANE. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* pardes NEWFILE in a pane forwards even when its directory is missing, and ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | Save makes the directories fs.md said FILE must already exist, while `pardes new.txt` in fact forwarded, making a pane named for it that Save creates. With a missing directory (`pardes notes/new.txt`) the launch gave up forwarding and started a nested editor inside the pane. Now the name is made absolute as written and forwarded the same way. A name written to a pane's name file that goes into a directory not there has its Save make the directories, as Config's pane does. A Save <path> into a missing directory still fails ENOENT. The Forwarding section now says what a new FILE does. fs.py launches one into a missing directory and saves it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Zig files go to zls as a child process through the protocol client, as every ↵Gabriel Schneider3 days
| | | | | | | | other language does: the in-process ZLS backend goes lsp_zls.zig (1,522 lines), the zls dependency, build.zig's zls wiring and the import graph it baked into the options only for it, and main.zig's reference go. lsp_client's table gains a zls row (PARDES_LSP_ZIG overrides it); Hover, Rename, Diagnostics, WsSymbols, Lspinfo and gd answer through it (checked with lspprobe and the snapshots against zls 0.16.1-dev). What changes for a user: zls must be on PATH; a completion's rows are zls's candidates at the cursor, where the linked analyser gave each one's declaration; document symbols carry no signature. The Zig snapshots (lsp, lspcomplete, lspcompletemoved, lspdebug, lsprelpath) run the real zls now: lspdebug reads the client's explain and report, lsprelpath spells gr's rows where it spelled a completion's, and their goldens are recorded again (stable over three runs). Lspinfo still opens with its backend line. The ReleaseFast tty binary is 105.35 MB to 96.28 MB, the Debug one 296.5 MB to 245.9 MB. docs/lsp.md, web.md, design.typ and the README say so. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fs.py waits for a new file's pane to be named, not only madeGabriel Schneider3 days
| | | | | | pardes --wait NEWFILE reads pane/new and then writes the pane's name, two requests; the test checked /index for the name as soon as the pane showed, and a gate run caught it between them. It waits for the name now. Co-Authored-By: Claude Opus 5.5 <[email protected]>
*---. Merge: loose end + msize 64K + docs slimming + docs-audit fixes + monkey-gui ↵Gabriel Schneider3 days
|\ \ \ | | | | | | | | | | | | + tty ssh acked frames
| * | | The 9P server offers 64 KiB frames, not 8 KiBGabriel Schneider3 days
| |/ / | | | | | | | | | | | | | | | 8192 was kept for round 24 because 64 KiB seemed to hang fs-test's restore detached space; that was the batch-wake overflow fixed in kmqtwqsm, which any msize could hit, and fs.py passes at 64 KiB with it. A client asking for more now gets it, so a write up to 64 KiB less 24 is one Twrite, and round 24's no-newline cutoff (write_room, the negotiated msize less 24) follows: fs.py checks a 20000-byte ctl line from a 64 KiB client is taken whole. Each of the 16 connection slots holds its buffers at the larger size. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* / / pardes NEWFILE in a pane opens a pane named it in the session, as acme's B ↵Gabriel Schneider3 days
|/ / | | | | | | | | | | | | | | does, and a start with no terminal says so A name that did not exist yet failed resolveOs, so the launch fell through to starting an editor of its own, which on a pty with no controlling terminal (a detached session's) ended in a NoDevice error trace. A new name's directory is now resolved and the name kept: the launch reads pane/new and writes that path to the pane's name, so Save creates the file, and --wait waits on that pane (the git commit case). A standalone start whose /dev/tty will not open prints why and exits 1. fs.py covers both; the known-wrong line in docs/divergences.md goes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | pardes --wait (-w) in a pane returns only once the pane its file landed in ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | is deleted, so it can be $EDITOR A launch inside a pane forwards its file to the session's look and returned at once, so fish's Ctrl-O (edit_command_buffer), git commit and crontab -e read their still-unedited file back and went on. With --wait, as acme's E against B and read the way plan9port's E reads acme's log, it finds the pane /index names the file by (the one already showing it, if open), follows /log on one connection with 'follow new', reads /index once more to catch a Del that came first, and then blocks with no deadline until that pane's del record: exit 0, or 1 when the connection ends with the session. The 9P client's one-shot requests keep their 2 s deadline; its new follow takes it for the setup only. Outside pardes nothing changes. --help, docs/fs.md, the README and the 9P skill say to set EDITOR='pardes --wait', which GIT_EDITOR follows. Tested in fs.py (it returns within 50 ms of the Del, a second -w waits on the same pane, one whose session is killed exits 1) and end to end: fish 4.8 in a detached session, echo hi, Ctrl-O, the waiting launch idle in poll, the line edited over 9P, Save, Del, and fish ran echo edited. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Mini with no argument is wrong #args, EINVAL; Find, Grep and Mini say their ↵Gabriel Schneider3 days
|/ | | | | | | | names capitalised Mini needs a file but did not say so, so a bare Mini ran and failed EIO. It now requires its argument, refused wrong #args (EINVAL) as every such word is. Its failures, Find's and Grep's start with the builtin's own name (Mini:, Find:, Grep:), as the others' do. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Delcol on the last column leaves the window, empty but for its workspace ↵Gabriel Schneider3 days
| | | | | | | | tag, as acme's does The last column's Delcol ended the session. The window now stays, with only the workspace tag; Newcol, and New (which makes the column it goes in), start it again. A word run with no pane at all runs with a stand-in that is no pane, the root ctl takes session words, and pane/new makes its scratch in the session's directory. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Not-found failures say it once, in plain words: grep: pattern not found, ↵Gabriel Schneider3 days
| | | | | | | | callers not found The ENOENT that 9ns reads from a failure's words came from a tacked-on suffix, grep: no match, not found, definition: nothing found, not found, no shell "x", not found. They now say it once: grep: pattern not found, find: name not found, Callers: callers not found, definition: not found, shell "x" not found (...). Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A ThemeFile that cannot be read or is no theme fails its write, and a bad ↵Gabriel Schneider3 days
| | | | | | | | path says why The theme is read when the host performs the request, after the write had been answered, so a missing file or a bad theme only showed a message; and a path not ending .zon said only invalid theme path. Load failures are now the waiting write's (late, as a Save's), and the path refusal says a theme file is a .zon. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A path with a part over 255 bytes is refused, never a panicGabriel Schneider3 days
| | | | | | std's statFile takes the kernel's ENAMETOOLONG for a bug (errnoBug), so a name, look, DumpDir or Save path with a part over 255 bytes panicked the editor (exec.kindOf via writeName, recentKeeps, dumpFailed). Every non-test statFile now goes through fs.statPath, which refuses such a name as NameTooLong first; fs.py drives long, looping, not-a-directory and not-ours paths through name, look, DumpDir, Dump and Save. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Recent goes on across a Restore, and is kept after itGabriel Schneider3 days
| | | | | | A Restore makes a new session, which never loaded the kept list, and saveRecent gives up until it has: Recent was empty after a Restore and nothing opened afterwards was kept. The new session now takes the old one's list (a boot from a dump reads the kept file). Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A pty/ctl exec that cannot start its shell keeps the one runningGabriel Schneider3 days
| | | | | | Each host closed the running shell before it forked the new one, so an exec whose shell failed left the pane with none and later runs answered error shell gone. A shell not there is now refused up front, and every host starts the new shell first, replacing the old only once the close-on-exec pipe says it ran; a failure there is only said (restartFailed). Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Recent lists the files opened lately, closed ones too, and a jump back to a ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | closed file reopens it A file closed by accident, its path forgotten, could not be found again: the jumplist dropped closed panes. Every file opened (a look, a Save of a scratch, a Restore, a rename) goes first in a list of 200, kept once, its dot noted when it closes, kept across sessions in $XDG_STATE_HOME/pardes/ recent. `Recent` (SPC f r) shows it in a reused +Recent, each row `path:line:col open|closed` a look reopens; /recent reads `open|closed <path>`. The jumplist keeps a closed file's entries, +Jumps marks them `(closed)`, and Back to one opens the file at its place. acme has no such thing; its dump and Load are the nearest. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A shell that cannot start fails Tty and pty/ctl exec with why, before either ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | answers A shell whose exec failed (a script's missing interpreter) was reported started: Tty and pty/ctl exec succeeded, then the pane died. The child now reports a failed chdir or exec through a close-on-exec pipe the parent reads before acknowledging the shell; the host's spawn fails with the reason (`shell not found`, ENOENT), which fails the waiting write. A Tty whose first shell never ran leaves no pane; a terminal restarted by pty/ctl exec keeps its pane. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A shell started again by pty/ctl exec is guarded against the rename flap as ↵Gabriel Schneider3 days
| | | | | | | | | | | a new one is The guard that keeps a shell from taking this process's directory before its chdir lasted only until the first shell spoke; a shell started again in the same pane (pty/ctl exec) was asked with the old shell's word still standing. A spawn resets it. fs.py's naming test restarts one too. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fs.py's workflow canary: one session driven as the dogfood agents drive itGabriel Schneider3 days
| | | | | | | | | | | The round-18 Grep that found nothing slipped past every narrower test. One broad run now looks files, runs Grep and Find hits and misses, looks a plain word, Edits two files and Saves them, gets one back from disk, runs pty/run failing then passing, Kills a command pane, sends to a REPL, makes and removes a column, answers an ask, and Dumps and Restores, checking the log and the read-backs at each step, in under 30 s. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A Save into a directory that is not there says no such directory, ENOENTGabriel Schneider3 days
| | | | | | | | It failed EIO with `file not found`, which named no file that was missing. It says `Save <path>: no such directory`, and a late failure saying `no such` answers ENOENT, as a builtin's does. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A shell given a directory is not taken to be in this process's before it has ↵Gabriel Schneider3 days
| | | | | | | | | | | | | spoken, so it logs no rename to the session's and back Each host read /proc/<pid>/cwd right after forking the shell and at every frame after; before the shell's chdir that says this process's directory, a rename to it, then the real one a frame later, a rename back. A shell given a directory is named by it already (newShell) and is no longer asked at fork; and until it has printed anything, an answer naming this process's directory is not taken. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A detached session no frontend has sized is 160x50, not 80x24Gabriel Schneider3 days
| | | | | | | At 80x24 a script's third or fourth pane/new was refused for want of rows. `size` still sets any other size, and a frontend attaching its own. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A pty/ctl winsize gives a terminal two rows at least, so it no longer reads ↵Gabriel Schneider3 days
| | | | | | | | | | | | busy for ever A one-row pty loses the prompt's input mark as soon as the prompt wraps, and busy detection never sees the shell idle again. Placement already keeps a new terminal at tag + 2 rows; a winsize write now keeps the pty at two. A pane dragged down to one body row keeps a one-row pty, since a taller pty than the pane shows would put its cursor below the pane. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* One rule for failing builtins: the write fails, one err, no msgGabriel Schneider3 days
| | | | | | | | | | | | | | | | A builtin that failed through a ctl failed the write and logged its err, but through look, exec, tagexec or a column's exec it only said so on the message row, logged as a msg, and the write succeeded. Now every click write runs its builtin as a ctl line does (ctl.captured): a failure fails the write, with its words (EINVAL for malformed input, else EIO or what the words name), logs one err and no msg or announcement. get's Modified and a look miss are shown but logged once, as their err. A control character's refusal names its reason, a failed click reads back nothing, and look never reads back a pane closed since. The special case for a cut-short Edit through exec goes (the rule covers it); fs.md's table of exceptions becomes the one rule, as does the skill. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* /commands takes each word's own doc comment, whole, without citationsGabriel Schneider3 days
| | | | | | | | | | | Repl showed Joincol's first line and Joincol another's, because a comment had drifted above the wrong declaration in builtins.zig; Kill's was cut mid-sentence and Exit's cited exec.c:892. The drifted line is back on Joincol, a parenthesis citing source (.c, .zig) is dropped, and the first sentence is kept whole up to 240 bytes, then cut between words with an ellipsis. A test checks every description opens its own word's comment. Co-Authored-By: Claude Opus 5.5 <[email protected]>