summaryrefslogtreecommitdiff
path: root/.agents
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-21 22:37:06 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:14 -0300
commit5dcfade5f102256de787b2157b01293160780411 (patch)
tree256416f7a82eacc06233d6a543a1fdfec390daab /.agents
parent297e14cfc36e4613a8c1cb3b995597d0a9c2873b (diff)
downloadpardes-5dcfade5f102256de787b2157b01293160780411.tar.gz
pardes-5dcfade5f102256de787b2157b01293160780411.zip
Make a pane by opening /pane/new, and a Plan 9 idiom pass
The Tcreate that replaced acme's /new was a step away from the idiom dressed up as a step toward it. A pane is named by a server-assigned serial, so the create ignored the client's name: `mkdir /pane/foo` succeeded and left you /pane/12. A mkdir that does not make the directory you named is worse than the read-with-side-effect it replaced, and it broke in the shell workflow that motivated the change. `create` is out of the declared features, so Tcreate is EPERM again; Tremove stays, since `rm` to close a pane is unambiguously right. /pane/new is now opened, not created: the open makes the pane, the read of that fid answers its serial, two reads agree, and closing it leaves the pane. That is /net/tcp/clone's mechanism (kernel/network/ip/devip.c, in ipopen), not acme's, and the difference is deliberate. acme allocates during the walk and lands inside the new window, so /dev/new/body works in one step, and it can afford to list `new` because a Plan 9 directory read carries every entry's stat and nothing walks. A kernel or FUSE mount walks and stats each name a listing gave it, so allocate-on-walk would make a pane per `ls -l`. Allocating on open keeps `new` listed -- a stat is not an open -- at the cost of the one-step new/body. `new` stays unreachable from an editor path, because that resolution serves Look hover previews. The idiom pass behind it, read out of the Plan 9 tree at ~/05-genizah/principia-softwarica rather than recalled: Rerror carries a string, not an errno (man 5 error: `ename[s]`), and acme names every refusal. The five refusals pardes shares with acme now say what they mean; the generic sites keep their bare errno rather than invent strings acme does not have. body and tag declare DMAPPEND, which they had always behaved as (acme(4): "always appended; the file offset is ignored"), checked first against Linux's fs/9p, which never maps the bit. excl stays unset everywhere, because acme sets DMEXCL on nothing. Blocking reads, per-object addr scope and the readable pane ctl were already right. Real stat sizes and qid versions stay: acme reports length 0 and version 0 for everything, and Linux clients need better. One bug fell out of it. open reset the addr range, so `echo '#0,#5' >addr; cat addr` answered `0 0` and `cp addr dot` copied zeros. acme(4) makes the contract explicit -- "a regular expression may be evaluated by writing it to addr and reading it back" -- and acme gets away with resetting on the 0-to-1 open only because its clients hold the fid across both. A shell cannot: that is two opens. The register is cleared by truncating it now. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Diffstat (limited to '.agents')
-rw-r--r--.agents/skills/pardes-9p/SKILL.md38
1 files changed, 25 insertions, 13 deletions
diff --git a/.agents/skills/pardes-9p/SKILL.md b/.agents/skills/pardes-9p/SKILL.md
index 2cea4d4a..91a8e864 100644
--- a/.agents/skills/pardes-9p/SKILL.md
+++ b/.agents/skills/pardes-9p/SKILL.md
@@ -49,7 +49,7 @@ $m/exec write a line = a middle click: an editor command word, or a shell
$m/log one record per read: new|del|rename|save <serial> <name>; reads park
$m/screen the rendered screen as JSON, frozen per open
$m/listeners this session's dial addresses
-$m/pane/ mkdir opens a pane; rmdir <serial> closes it
+$m/pane/new open it to make a pane, read names it; rmdir $m/pane/<n> closes it
$m/os/ the host filesystem
```
@@ -60,7 +60,7 @@ reuse or close a pane.
```sh
cat "$m/index" # which panes exist
-mkdir "$m/pane/x"; n=$(awk 'END{print $1}' "$m/index") # open one, take its serial
+n=$(cat "$m/pane/new") # make one, take its serial
printf 'text\n' > "$m/pane/$n/body" # append
cat "$m/pane/$n/tag" # what its tagline offers
echo notes.txt > "$m/pane/$n/name" # rename the buffer
@@ -69,10 +69,20 @@ echo "/etc/hosts:3" > "$m/look"; cat "$m/look" # open a file, see where it land
rmdir "$m/pane/$n" # close it, dirty or not
```
-The name `mkdir` asks for is ignored: a pane is named by the serial the editor
-gives it, and because `/index` is ordered by serial its last row is the pane
-just made. Nothing else in the tree can be created or removed, and no read
-creates anything, so `ls`, `stat` and `find` over the whole tree are inert.
+**Opening** `$m/pane/new` is what makes a pane, and reading the open file
+answers its serial — `/net/tcp/clone`'s mechanism. Each open makes another one,
+two reads of the same open file answer the same serial, and closing it leaves
+the pane. A pane is named by the serial the editor gives it, never by a name
+you choose.
+
+A *stat* makes nothing, which is the whole reason the allocation sits on open:
+`new` is listed in `$m/pane`, so `ls` shows it, and `ls -l`, `find` and
+anything else that stats every name a listing handed it stay inert. acme
+allocates on the walk instead and lets it land inside the new window, so
+`/dev/new/body` works in one step — it can afford that because a Plan 9
+directory read carries every entry's stat and nothing walks. Under a kernel or
+FUSE mount that would be a pane per `ls -l`. Nothing else in the tree can be
+created or removed, and no read creates anything.
`look` and `exec` are the editor's two clicks, one per line of a write, at the
active pane from the root and at that pane from `$m/pane/<n>/look` and
@@ -107,11 +117,12 @@ search and reads empty until set; truncate it to lift it.
buffer differs from its file, whether a write pushes an undo point, and whether
a write scrolls. Truncating `tag` clears the part of the tag you may edit.
-Address state belongs to the pane, not to a client: opening `addr` resets it,
-so two clients addressing the same pane will interfere. `$pane/ctl` reads
-acme's window status line — serial, tag length, body length, a reserved zero,
-the dirty flag, the width in cells, the font and the tab width — and takes the
-one verb `get`.
+Address state belongs to the pane, not to a client: it keeps the last range
+written until someone writes or truncates it, so writing an address and reading
+it back evaluates it, and two clients addressing the same pane will interfere.
+`$pane/ctl` reads acme's window status line — serial, tag length, body length,
+a reserved zero, the dirty flag, the width in cells, the font and the tab
+width — and takes the one verb `get`.
Terminal panes have no file: writing their `body` sends child input, and
truncation does not erase terminal history.
@@ -209,8 +220,9 @@ with tempfile.TemporaryDirectory(prefix='pardes-9p-skill-') as directory:
PY
```
-`new_pane` is `mkdir` plus a read of the index; `execute` writes one line to a
-pane's `exec`. Both are in `test/fs.py`. For terminal tests, use
+`new_pane` walks to `/pane/new` and reads the serial off the directory it
+lands on; `execute` writes one line to a pane's `exec`. Both are in
+`test/fs.py`. For terminal tests, use
`session(..., tty=True)` and read
[test/agent_session.py](../../../test/agent_session.py) for bounded interactive
driving; its readiness text and history threshold are application-specific, and