@@ -63,7 +63,9 @@ int main() {
6363 }
6464
6565 // 2. Register capabilities explicitly
66- zi_file_fs25_register ();
66+ // Filesystem is async-first in 2.5: file/aio + sys/loop.
67+ zi_file_aio25_register ();
68+ zi_sys_loop25_register ();
6769 zi_net_tcp25_register ();
6870 zi_proc_argv25_register ();
6971 zi_proc_env25_register ();
@@ -75,6 +77,7 @@ int main() {
7577 // zi_async_default25_register_selectors();
7678
7779 // Now zi_cap_open, zi_ctl, etc. work
80+ return 0;
7881}
7982```
8083
@@ -137,19 +140,51 @@ int32_t n = zi_ctl((zi_ptr_t)(uintptr_t)req, 24,
137140
138141**2.5**: All open parameters are explicit in the open request blob.
139142
140- **Example** (`file/fs` ):
143+ **Example** (`file/aio` OPEN job ):
141144
142145```c
143- uint8_t params[20];
144- write_u64le(params + 0, (uint64_t)(uintptr_t)"/demo.txt");
145- write_u32le(params + 8, 9); // path_len
146- write_u32le(params + 12, ZI_FILE_O_READ);
147- write_u32le(params + 16, 0); // mode
148-
146+ // 1) Open the file/aio queue (open params are empty)
149147uint8_t open_req[40];
150- build_cap_open_req(open_req, "file", "fs", params, 20);
151-
152- zi_handle_t h = zi_cap_open((zi_ptr_t)(uintptr_t)open_req);
148+ build_cap_open_req(open_req, "file", "aio", NULL, 0);
149+ zi_handle_t aio = zi_cap_open((zi_ptr_t)(uintptr_t)open_req);
150+
151+ // Also open sys/loop and WATCH the aio handle for readable.
152+ // (sys/loop requests are ZCL1 frames; see abi/SYS_LOOP_PROTOCOL.md)
153+ uint8_t loop_open_req[40];
154+ build_cap_open_req(loop_open_req, "sys", "loop", NULL, 0);
155+ zi_handle_t loop = zi_cap_open((zi_ptr_t)(uintptr_t)loop_open_req);
156+
157+ uint64_t watch_id = 1;
158+ sys_loop_watch(loop, /*handle=*/aio, /*events=*/0x1 /* readable */, watch_id);
159+
160+ // Helper notes (pseudocode):
161+ // - sys_loop_watch(): send a sys/loop WATCH request (op=1) for (aio, readable, watch_id).
162+ // - read_frame_wait(): read until a full ZCL1 frame is buffered; if zi_read() returns ZI_E_AGAIN,
163+ // call sys/loop POLL (op=5) and retry.
164+ // See abi/FILE_AIO_PROTOCOL.md (“Waiting for completions via sys/loop”) for a concrete helper.
165+
166+ // 2) Submit an OPEN job (payload is the standard 20-byte file open params)
167+ uint8_t open_pl[20];
168+ write_u64le(open_pl + 0, (uint64_t)(uintptr_t)"/demo.txt");
169+ write_u32le(open_pl + 8, 9); // path_len
170+ write_u32le(open_pl + 12, ZI_FILE_O_READ | ZI_FILE_O_CREATE);
171+ write_u32le(open_pl + 16, 0644);
172+
173+ uint8_t fr[24 + 20];
174+ build_zcl1_req(fr, /*op=*/1 /*OPEN*/, /*rid=*/1, open_pl, 20);
175+ (void)zi_write(aio, (zi_ptr_t)(uintptr_t)fr, sizeof(fr));
176+
177+ // 3) Read the immediate ACK.
178+ // For a completion-based queue like file/aio, ACK and EV_DONE are both frames read from the same handle.
179+ // The correct blocking rule is: if zi_read() returns ZI_E_AGAIN, block in sys/loop.POLL, then retry.
180+ uint8_t ack[1024];
181+ int got = read_frame_wait(loop, aio, watch_id, ack, sizeof(ack));
182+
183+ // 4) Read the EV_DONE completion frame.
184+ uint8_t done[65536];
185+ got = read_frame_wait(loop, aio, watch_id, done, sizeof(done));
186+
187+ // See abi/FILE_AIO_PROTOCOL.md for normative framing and parsing recipes.
153188```
154189
155190Sandboxing is still via env vars (` ZI_FS_ROOT ` , ` ZI_NET_ALLOW ` ), but the path is in params.
@@ -184,15 +219,14 @@ zi_handle_t h_err = zi_handle25_alloc(&fd_ops, &s_err, ZI_H_WRITABLE);
184219
185220All golden caps are production-ready with tests, docs, and sandboxing:
186221
187- - **file/fs**: Filesystem with `ZI_FS_ROOT` sandbox.
188222- **file/aio**: Async filesystem jobs + completions (await via `sys/loop`).
189223- **proc/argv, proc/env**: Read-only access to args/env.
190224- **proc/hopper**: Safe arena allocator with catalog + optional direct-call ABI.
191225- **net/tcp**: TCP client with DNS resolution, gated by `ZI_NET_ALLOW`.
192226- **async/default**: Async invocation with futures, cancellation, event streams.
193227
194228Notes:
195- - `file/fs@v1` is compiled but not registered by default; prefer `file/aio@v1` for an async-first model .
229+ - zingcore 2.5 exposes an async-first filesystem surface via `file/aio@v1`.
196230- `sys/loop@v1` is the unified blocking primitive (WATCH + POLL + retry on `ZI_E_AGAIN`).
197231
198232### 2. Hopper Direct-Call ABI (`zi_hopabi25`)
@@ -307,7 +341,8 @@ zi_async_register(&sel);
307341
308342```c
309343#include "zingcore25.h"
310- #include "zi_file_fs25.h"
344+ #include "zi_file_aio25.h"
345+ #include "zi_sys_loop25.h"
311346#include "zi_proc_argv25.h"
312347#include "zi_runtime25.h"
313348#include "zi_sysabi25.h"
@@ -327,7 +362,8 @@ int main(int argc, char **argv) {
327362 zi_runtime25_set_mem(&mem);
328363
329364 // 3. Register caps
330- zi_file_fs25_register();
365+ zi_file_aio25_register();
366+ zi_sys_loop25_register();
331367 zi_proc_argv25_register();
332368
333369 // 4. Wire argv
@@ -336,15 +372,13 @@ int main(int argc, char **argv) {
336372 // 5. Use the ABI
337373 printf("zABI version: 0x%08x\n", zi_abi_version());
338374
339- // 6. Example: open file/fs
340- uint8_t params[20];
341- // ... build params ...
375+ // 6. Example: open file/aio queue
342376 uint8_t req[40];
343- // ... build open request ...
344- zi_handle_t h = zi_cap_open((zi_ptr_t)(uintptr_t)req);
345- if (h >= 3) {
346- // ... use handle ...
347- zi_end(h );
377+ // ... build open request for kind="file", name="aio", params empty ...
378+ zi_handle_t aio = zi_cap_open((zi_ptr_t)(uintptr_t)req);
379+ if (aio >= 3) {
380+ // ... write ZCL1 OPEN/READ/WRITE/CLOSE jobs and await completions via sys/loop ...
381+ zi_end(aio );
348382 }
349383
350384 return 0;
0 commit comments