Add armed file watches without serializing unrelated IO

This commit is contained in:
Sausage
2026-11-01 20:51:08 +00:00
parent 959ddf46ff
commit 3992611b76
18 changed files with 1277 additions and 23 deletions
+33
View File
@@ -222,6 +222,7 @@ these areas fails.
/proc/
core/
PROTOCOL.md this file
discover.json core.rpc.discover document
graph.json plugins, imports, and the result of each check
graph.dot the same graph for Graphviz
locks.json held locks and waiting processes
@@ -249,6 +250,36 @@ Rules:
Principle: **inspect through files, act through calls.**
### File watching
`core.watch {"path": "/state/session/mailbox"}` stays open for the caller
process's lifetime. It needs a request ID and a running plugin caller.
Its first `core.notify` is `"armed"`, with empty params. Only then scan the
files. Writes before arming are visible in that scan; writes after arming
produce `"changed"` notifications (also with empty params). Rescan after
each notification; files, not event counts, are the truth.
Notifications follow successful plugin-originated file mutations: creation,
truncation, descriptor/stream writes, rename (both source and destination),
and deletion, including mounted `/run` and `/self` files. Paths are normalized
in the virtual namespace; `/self` means the caller's `/proc/<pid>` folder.
A watch matches itself and descendants, not sibling prefixes. Moving or
removing an ancestor also invalidates it. Paths need not exist when armed.
Open file writes follow plugin renames; unlinked/replaced handles no longer
name visible files. External host writes are outside this contract.
Changes may coalesce, including a change already visible in the initial
scan, but a change after the last notification always produces another.
Delivery holds no filesystem locks. Observation does not serialize descriptor
IO; only overlapping opens and rename/removal wait for one another.
Watch the narrowest useful path: delivery is not rate-limited, so watching a
busy parent can cause a rescan for every write.
The watch ends with its caller; there is no cancellation API or polling/retry
loop. SDK callers should poll the watch future alongside their handler's work
and drop it on return, not keep it in a background task that prevents an idle
process from exiting. Dropping the future does not cancel the host watch.
## 5. Endpoints
An endpoint is a JSON-RPC interface that a running process serves over TCP,
@@ -355,7 +386,9 @@ At start, the core starts each service in `/config/boot.yaml`, in order:
| Method | Params | Result |
| --- | --- | --- |
| `core.rpc.discover` | none | OpenRPC core API document (also `/proc/core/discover.json`) |
| `core.call` | `method`, `params`, `detach`? | the result of the method |
| `core.watch` | `path` | stays open; `armed`, then `changed` notifications (section 4) |
| `core.notify` | `name`, `params` | none (a notification) |
| `core.stop` | `pid` | `{}` when the process has ended |
| `core.kill` | `pid` | `{}` |