diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-27 16:42:15 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-27 22:09:08 -0300 |
| commit | 98b62629795e24de19f535f1072b8a25dcf9f018 (patch) | |
| tree | fedea7e9b6648236220c054c30e6d2b4f8938288 | |
| parent | 147ebd4a36ec7199074ba05bcfb79d4a656c0b74 (diff) | |
| download | pardes-98b62629795e24de19f535f1072b8a25dcf9f018.tar.gz pardes-98b62629795e24de19f535f1072b8a25dcf9f018.zip | |
build: pardes builds without the ESP32-P4 toolchain checkout beside it
`build.zig.zon` named `../05-zig-p4` as a path dependency, and `build.zig`
`@import`ed it inside `if (-Desp32p4-firmware)`. But `@import` in a build script
is resolved when the SCRIPT is compiled, not when the branch that needs it is
taken -- so naming the package at all meant anyone without that sibling checkout
could not build pardes AT ALL. Not the firmware: the terminal shell, the SDL
shell, the tests. `zig build` failed with
build.zig:1073: error: no module named 'zig_p4' available within module 'root.@build'
from a line inside an `if` that was false.
Neither escape hatch works for a PATH dependency, and both were tried rather
than assumed. `.lazy = true` is about FETCHING; a path dep whose directory is
absent is generated as a package with no `build.zig` rather than one marked
unavailable, so `b.lazyImport` -- which exists for exactly this and is what the
standard library says is to `@import` what `lazyDependency` is to `dependency`
-- reaches a `@compileError` instead of returning null. Making it a fetched
dependency instead is not available either: the toolchain has no remote.
So the duplicate goes. That build tree's firmware block linked an image the
toolchain repository already knows how to link -- its own build.zig has
`-Dpardes`, `-Dapp=<root>` and `-Dpardes-obj=<path>`, and its comments record
having learned this same lesson from the other direction, where nesting pardes's
~30-package graph under it broke every build there. The object is the seam: it
crosses by PATH and never by package, and each repository builds what it owns
the pieces of.
zig build -Dplatform=esp32p4 # here, no toolchain needed
zig build -Dpardes # there, the console image
zig build -Dpardes -Dapp=<pardes>/src/esp32p4_9p.zig # there, the 9P image
For the second and third to work with no module map, `src/board9p.zig` and the
9P firmware root now reach the codec by PATH instead of through a named `ninep`
module that only pardes's own build.zig knew to inject -- which is also why the
root moved from `src/esp32p4/nine.zig` up to `src/esp32p4_9p.zig`, beside
`src/esp32p4.zig`: a path import may not escape its module's own directory. Both
files are now self-contained, and `zig test src/board9p.zig` works with no flags.
`-Desp32p4-port`, `-Desp32p4-prof` and `-Desp32p4-cpu-mhz` go with the block. An
option this build cannot honour is worse than no option, because it accepts the
flag and then ignores it; all three are spelled the same way in the toolchain.
Verified by moving ../05-zig-p4 out of the way: `zig build`, `zig build
-Dplatform=esp32p4` and `zig build unit-test` all pass without it. With it back,
the toolchain still links both images -- console 812,720 B, 9P 88,096 B.
next-steps.txt gains the six features the 9P chain shipped.
| -rw-r--r-- | build.zig | 245 | ||||
| -rw-r--r-- | build.zig.zon | 18 | ||||
| -rw-r--r-- | docs/9p.pdf | 6 | ||||
| -rw-r--r-- | docs/registry.pdf | 6 | ||||
| -rw-r--r-- | next-steps.txt | 50 |
5 files changed, 108 insertions, 217 deletions
@@ -451,19 +451,12 @@ pub fn build(b: *std.Build) void { // in region l2mem, overflowed by 76036 bytes`, that being the shell's shadow copy of the grid. const esp32p4_cols = b.option(u16, "esp32p4-cols", "the board's grid width in cells (-Dplatform=esp32p4 only)") orelse 56; const esp32p4_rows = b.option(u16, "esp32p4-rows", "the board's grid height in cells (-Dplatform=esp32p4 only)") orelse 14; - // THE OTHER THREE BOARD OPTIONS, registered HERE and not inside the - // `-Desp32p4-firmware` block that consumes them. Declared in there they - // existed only once the flag was already on, so `zig build - // -Dplatform=esp32p4 -Desp32p4-port=/dev/ttyACM0` was rejected as an - // "invalid option" and told the reader to consult a help menu that did not - // list it either. Worse for `-Desp32p4-cpu-mhz`: the ESP-IDF read inside - // `esp32p4.firmware()` happens before the block reaches the `b.option` - // call, so on a machine with no IDF checkout the build exited before the - // option was ever declared. An option is part of this build's interface - // whether or not this invocation reaches the code that uses it. - const esp32p4_port = b.option([]const u8, "esp32p4-port", "serial port the board is wired to (default /dev/ttyUSB0)") orelse "/dev/ttyUSB0"; - const esp32p4_prof = b.option(bool, "esp32p4-prof", "make the firmware print per-phase cycle counts for every frame") orelse false; - const esp32p4_cpu_mhz = b.option(u16, "esp32p4-cpu-mhz", "board HP CPU clock: 90 (bootloader default), 180 or 360") orelse 90; + // `-Desp32p4-port`, `-Desp32p4-prof` and `-Desp32p4-cpu-mhz` used to be + // declared here, for the block that linked and flashed the image. That + // block is gone — see "where the FLASHABLE image comes from" below — and so + // are they: an option this build cannot honour is worse than no option, + // because it accepts the flag and then ignores it. All three belong to the + // toolchain repository and are spelled the same way there. // ANIMATED THEME CHANGES, off on the board because there the animation is not an animation. // // A theme change moves the anchored chrome palette - taglines, boxes, line numbers, scroll bars - @@ -1051,189 +1044,37 @@ pub fn build(b: *std.Build) void { const obj = b.addObject(.{ .name = "pardes-esp32p4", .root_module = root_mod }); b.getInstallStep().dependOn(&b.addInstallFile(obj.getEmittedBin(), "pardes-esp32p4.o").step); - // ------------------------------------------------- the whole board, from this build tree + // ------------------------------------------------- and where the FLASHABLE image comes from // - // `-Dplatform=esp32p4` alone still emits nothing but the object above, which is what the - // toolchain repo's own `-Dpardes -Dpardes-obj=` path links and what every measurement in - // its `experiments/` was taken through. `-Desp32p4-firmware` adds the other half here: the - // firmware executable rooted at src/esp32p4/app.zig, the flashable image, and the steps that - // write it to a board and talk to it. + // Not from here, and this is the one deliberate asymmetry in the board build. + // `-Dplatform=esp32p4` emits the object above and needs no toolchain at all. Linking that + // object into an image needs the linker script, the app descriptor, the heap, the HAL and + // the flash tooling, all of which live in the `05-zig-p4` checkout — so the image is built + // THERE, by the repository that owns them: // - // OPT-IN, and not out of timidity: `esp32p4.firmware()` reads ESP-IDF's register headers at - // CONFIGURE time and exits(1) when there is no checkout. Merely DECLARING the dependency - // costs nothing — that package has no dependencies and its `build()` early-returns when it - // is not the root — but calling this acquires an ESP-IDF requirement, and a bare - // `-Dplatform=esp32p4` must not. + // zig build -Dpardes # the console firmware + // zig build -Dpardes -Dapp=<pardes>/src/esp32p4_9p.zig # the 9P server firmware // - // The seam stays the object. `src/esp32p4/app.zig` reaches the editor through the same eight - // `extern` C functions whichever repository drives the build, so neither path is the - // better-tested one: the image this produces is byte-identical to the one - // `05-zig-p4 -Dpardes -Dapp=…/src/esp32p4/app.zig` produces. - if (b.option(bool, "esp32p4-firmware", "also build the flashable ESP32-P4 firmware image and its board steps (needs an ESP-IDF checkout for the register headers)") orelse false) { - const esp32p4 = @import("zig_p4"); - // Every entry point in that package takes ITS OWN builder: it resolves source paths and - // the image step's cache inputs against its own build root, and its `build()` never - // runs when consumed, so `dep.module`/`dep.artifact` deliberately find nothing. - const pb = b.dependency("zig_p4", .{}).builder; - // One spelling of the ISA, asked of the package that owns the linker script and the - // other half of the image, so the two cannot drift. It is the same set `esp32p4_target` - // above names, and `f` is the member that decides the float ABI. - const fw_target = esp32p4.chipTarget(pb); - const fw = esp32p4.firmware(pb, .{ - .target = fw_target, - // ReleaseSmall for the PLATFORM half — a heap, a clock, a UART and a trap vector, - // none of it hot — while `obj` keeps the ReleaseFast that was measured on the die. - // Two objects, two modes, one link. - .optimize = .ReleaseSmall, - // 32768 and not that package's own 8192 default: the core recurses through layout - // and 8 KiB is not enough for it. It is baked into the generated script's `.stack`. - .stack_size = 32768, - .prof = esp32p4_prof, - .cpu_mhz = esp32p4_cpu_mhz, - }); - const fw_exe = b.addExecutable(.{ - .name = "pardes-esp32p4-firmware", - .root_module = b.createModule(.{ - .root_source_file = b.path("src/esp32p4/app.zig"), - .target = fw_target, - .optimize = .ReleaseSmall, - .strip = true, - .single_threaded = true, - .unwind_tables = .none, - .omit_frame_pointer = true, - .error_tracing = false, - // The four modules src/esp32p4/app.zig imports, and no more. `uart.zig` and - // `input_rescue.zig` are sibling FILES of that root, so they need no module. - .imports = &.{ - .{ .name = "config", .module = fw.config }, - .{ .name = "soc", .module = fw.soc }, - .{ .name = "hal", .module = fw.hal }, - .{ .name = "heap", .module = fw.heap }, - }, - }), - }); - fw_exe.root_module.addObject(obj); - // The linker script, `_start` as the entry, the app descriptor the bootloader reads at - // image offset 0x20, and the register census that gates compiling against `regs` — in - // one call. It deliberately leaves `--gc-sections` to the caller, because which - // sections an image keeps belongs to the image. - fw.attach(fw_exe); - fw_exe.link_gc_sections = true; - b.getInstallStep().dependOn(&b.addInstallArtifact(fw_exe, .{}).step); - - // ELF -> flashable image, and the four things one does with a board. The image's own - // defaults come from that package (`0x10000`, 16 MB, chip revision window 1.00-1.99). - const img = esp32p4.ImageStep.create(pb, fw_exe, .{ - .chip = .esp32p4, - .min_rev_full = 100, - .max_rev_full = 199, - .flash_size = .@"16MB", - .flash_offset = 0x10000, - }); - b.getInstallStep().dependOn(&b.addInstallBinFile(img.getOutput(), "pardes-esp32p4.bin").step); - const flash = esp32p4.FlashStep.create(pb, img, .{ - .port = esp32p4_port, - // The FLASHER's rate, negotiated by the ROM loader's SYNC auto-detect. Measured - // reliable on this board at 921600; 2000000 is not. - .baud = .b921600, - .verify = true, - .opts = img.opts, - }); - b.step("esp32p4-flash", "write pardes into the board's flash and start it (needs an ESP32-P4 on -Desp32p4-port)").dependOn(&flash.step); - - // INTERACT. The console is a host binary, not a step: it puts the terminal in raw mode - // and passes the board's bytes and the user's keystrokes through untouched, which is - // what makes the editor on the far end see a real terminal answering its capability - // queries. Inherited stdio for the same reason. It lives in the toolchain package - // because a serial console is useful to anything on this chip; the RUNNER is here - // because attaching to pardes is a pardes command. - // - // 115200 and not the flasher's rate: this one is whatever the running firmware - // programmed into UART0, and the bootloader leaves it at 115200. - const tools = esp32p4.hostTools(pb); - const console_args: []const []const u8 = &.{ "--port", esp32p4_port, "--baud", "115200" }; - const con = b.addRunArtifact(tools.console); - con.addArgs(console_args); - con.stdio = .inherit; - con.step.dependOn(&tools.console_install.step); - b.step("esp32p4-attach", "attach this terminal to the pardes already running on the board").dependOn(&con.step); - // Ordered on purpose: two independent steps let the console reset the board out from - // under the writer, and in practice the console wins and drives the OLD firmware. - const interact = b.addRunArtifact(tools.console); - interact.addArgs(console_args); - interact.stdio = .inherit; - interact.step.dependOn(&tools.console_install.step); - interact.step.dependOn(&flash.step); - b.step("esp32p4-run", "flash pardes into the board, then attach this terminal to it").dependOn(&interact.step); - - b.step("esp32p4-image-size", "print how much flash the firmware image uses, segment by segment (no board needed)") - .dependOn(&esp32p4.SizeStep.create(pb, img).step); - b.step("esp32p4-image-check", "print the firmware image's segments and say whether the bootloader would accept it (no board needed)") - .dependOn(&esp32p4.LayoutStep.create(pb, fw_exe, img.opts).step); - b.step("esp32p4-reset", "restart the board so the pardes already in its flash runs from the top") - .dependOn(&esp32p4.ResetStep.create(pb, esp32p4_port).step); - - // `zig build esp32p4-test` — the on-die suite, its OWN image and not this firmware. It - // deliberately links NO `pardes-esp32p4` object: it is not the editor, it is the set of - // claims about this board that only the board can answer — byte-at-a-time - // `std.mem.eql`, the lone-ESC decode, the transmit FIFO going full, the heap span the - // grid is cut from — and linking 1.5 MiB of editor into it would only make those - // claims slower to flash. - const st_exe = b.addExecutable(.{ - .name = "pardes-esp32p4-selftest", - .root_module = b.createModule(.{ - .root_source_file = b.path("src/esp32p4/selftest.zig"), - .target = fw_target, - .optimize = .ReleaseSmall, - .strip = true, - .single_threaded = true, - .unwind_tables = .none, - .omit_frame_pointer = true, - .error_tracing = false, - // The four package modules the suite names, and `input_rescue`, which is a - // sibling FILE here and stays a MODULE anyway: the root says - // `@import("input_rescue")`, and it must, because `FakePort`'s methods are - // `pub` precisely so the policy can reach them by duck typing ACROSS a module - // boundary. The toolchain repo compiles this same root with the same wiring, - // so one file serves both builds and neither is the tested one. - .imports = &.{ - .{ .name = "config", .module = fw.config }, - .{ .name = "soc", .module = fw.soc }, - .{ .name = "hal", .module = fw.hal }, - .{ .name = "heap", .module = fw.heap }, - .{ .name = "input_rescue", .module = b.createModule(.{ - .root_source_file = b.path("src/esp32p4/input_rescue.zig"), - .target = fw_target, - .optimize = .ReleaseSmall, - .single_threaded = true, - }) }, - }, - }), - }); - // Script, `ENTRY(_start)`, descriptor, register census — and deliberately NO - // `link_gc_sections`, unlike `fw_exe` above: the toolchain's own selftest image is - // built without it, and switching it on would change the bytes of the image every - // on-die measurement was taken against. - fw.attach(st_exe); - // Same image rules as the firmware's, by reusing `img.opts` rather than restating - // them: one flash offset, one revision window, one fitted size. - const st_img = esp32p4.ImageStep.create(pb, st_exe, img.opts); - const st_flash = esp32p4.FlashStep.create(pb, st_img, .{ - .port = esp32p4_port, - .baud = .b921600, - .verify = true, - .opts = img.opts, - }); - // Ordered flash-then-run rather than two independent steps: the suite must be the - // image that is running when the reader attaches, or the verdict belongs to whatever - // was on the board before. An absent `MARK SELFTEST DONE` inside the 20-second window - // is itself a failure, which is what makes a board that never got there fail loudly - // instead of passing quietly. - const st_run = esp32p4.SelftestStep.create(pb, esp32p4_port, 20); - st_run.step.dependOn(&st_flash.step); - b.step("esp32p4-test", "run pardes's on-die test suite on the board; any failed check fails the build") - .dependOn(&st_run.step); - } + // both taking `-Dpardes-obj=<pardes>/zig-out/pardes-esp32p4.o`, which is exactly the file + // installed above. + // + // This build tree used to do it too, under `-Desp32p4-firmware`, by declaring `.zig_p4` as + // a path dependency on that sibling checkout. That cost far more than the duplication was + // worth: `@import` in a build script is resolved when the SCRIPT is compiled and not when + // the branch that needs it is taken, so naming the package at all meant that anyone + // without a `../05-zig-p4` beside their pardes could not build pardes AT ALL — not the + // firmware, not the SDL shell, not the terminal one. `zig build` failed with + // `no module named 'zig_p4'` from a line inside an `if` that was false. + // + // Neither `.lazy = true` nor `b.lazyImport` rescues it: laziness is about FETCHING, and a + // path dependency whose directory is absent is generated as a package with no `build.zig` + // rather than as one marked unavailable, so `lazyImport` reaches a `@compileError` instead + // of returning null. Both were tried. The toolchain has no remote to make it a fetched + // dependency instead. + // + // So the arrangement is the one that repository had already arrived at from its own side, + // where its build.zig records the same lesson: the object is the seam, it crosses by PATH + // and never by package, and each repository builds what it owns the pieces of. web_step.dependOn(&b.addFail("web needs -Dplatform=web -Dtarget=wasm32-freestanding -Ddump=<dump.zon>").step); } else if (platform == .macos) { // The native macOS shell is a static library plus a Swift app: Zig @@ -1755,6 +1596,24 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/9p.zig"), }) }); unit_step.dependOn(&b.addRunArtifact(ninep_test).step); + // The board's own 9P tree, and the comptime table that generates it. Its own module for the + // reason its neighbours have: nothing `unit-test` compiles reaches src/board9p.zig — the + // core does not import it, because the whole point of it is that it does NOT import the + // core — so its eight tests would otherwise silently not exist. No `link_libc`: it imports + // `std`, `src/board_pins.zig` and, in its tests only, `ninep`, which is what lets the same + // source serve a real client here and drive `hal.gpio` on the die. + // + // `ninep` used to be injected here as a named module over src/9p.zig. + // It is a plain path import now (`src/board9p.zig` says why), so this + // test needs no module map at all — which is the same property that + // lets the toolchain repository root a firmware image at + // src/esp32p4_9p.zig without being told what to inject. + const board9p_test = b.addTest(.{ .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + .root_source_file = b.path("src/board9p.zig"), + }) }); + unit_step.dependOn(&b.addRunArtifact(board9p_test).step); // The board's input-rescue policy: drain the receiver while spinning on a full transmitter. // A measured bug — a 200-byte burst typed into a long frame lost 88 bytes on the die — so // it gets a test that fails without the fix, and it runs HERE rather than only on hardware. diff --git a/build.zig.zon b/build.zig.zon index 380a5d8a..2d745db6 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -179,24 +179,6 @@ .url = "git+https://github.com/allyourcodebase/freetype#884fd2235e6ae1ec4306eda30b3259f20930ed2e", .hash = "freetype-2.14.3-C3-WdXSKAAB-kHs4qgRoeoFnRkNQn6M_SSlUGZ6ywgBQ", }, - // The ESP32-P4 toolchain: the SoC/HAL/register layer, the generated - // linker script, the ELF->image builder, the ROM flasher and the - // interactive serial console. A PATH dependency, because it is a - // sibling checkout rather than something to pin a commit of, and - // because the firmware build reads an ESP-IDF checkout off this - // machine anyway. - // - // Safe to declare unconditionally, and that is measured rather than - // assumed. The failure this whole seam exists to avoid runs the OTHER - // way: nesting THIS package's ~30-package graph under that one broke - // every build there (std/Build.zig:2091's comptime branch quota over - // the enlarged dependency table, plus seven cached tree_sitter - // versions whose build.zig no longer compiles on 0.16) — see - // src/esp32p4.zig:8-15. In this direction there is nothing to enlarge: - // zig_p4 declares no dependencies at all, so it adds exactly zero - // packages here, and its `build()` early-returns when it is not the - // root package. Not lazy, because `build.zig` @imports it. - .zig_p4 = .{ .path = "../05-zig-p4" }, }, .minimum_zig_version = "0.16.0", .paths = .{""}, diff --git a/docs/9p.pdf b/docs/9p.pdf index 9d2dc054..961473bc 100644 --- a/docs/9p.pdf +++ b/docs/9p.pdf @@ -2773,12 +2773,12 @@ F��Mk�I���R�B|��&إ�n�ऋ��(�H�r�Tų��9�`��ըa�&�ʺ���:�l endstream endobj 748 0 obj -<</Creator(Typst 0.15.1)/ModDate(D:20260827161944-03'00)/CreationDate(D:20260827161944-03'00)>> +<</Creator(Typst 0.15.1)/ModDate(D:20260827220904-03'00)/CreationDate(D:20260827220904-03'00)>> endobj 749 0 obj <</Length 997/Type/Metadata/Subtype/XML>> stream -<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?><x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="xmp-writer"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/" xmlns:pdf="http://ns.adobe.com/pdf/1.3/" ><xmp:CreatorTool>Typst 0.15.1</xmp:CreatorTool><dc:language><rdf:Bag><rdf:li>en</rdf:li></rdf:Bag></dc:language><xmp:ModifyDate>2026-08-27T16:19:44-03:00</xmp:ModifyDate><xmp:CreateDate>2026-08-27T16:19:44-03:00</xmp:CreateDate><xmpTPg:NPages>12</xmpTPg:NPages><dc:format>application/pdf</dc:format><xmpMM:InstanceID>HljsDPgWqw9TTnj4ezsOYg==</xmpMM:InstanceID><xmpMM:DocumentID>HljsDPgWqw9TTnj4ezsOYg==</xmpMM:DocumentID><xmpMM:RenditionClass>proof</xmpMM:RenditionClass><pdf:PDFVersion>1.7</pdf:PDFVersion></rdf:Description></rdf:RDF></x:xmpmeta><?xpacket end="r"?> +<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?><x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="xmp-writer"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/" xmlns:pdf="http://ns.adobe.com/pdf/1.3/" ><xmp:CreatorTool>Typst 0.15.1</xmp:CreatorTool><dc:language><rdf:Bag><rdf:li>en</rdf:li></rdf:Bag></dc:language><xmp:ModifyDate>2026-08-27T22:09:04-03:00</xmp:ModifyDate><xmp:CreateDate>2026-08-27T22:09:04-03:00</xmp:CreateDate><xmpTPg:NPages>12</xmpTPg:NPages><dc:format>application/pdf</dc:format><xmpMM:InstanceID>eoOUAEv34jOjSGsTdiY5Ag==</xmpMM:InstanceID><xmpMM:DocumentID>eoOUAEv34jOjSGsTdiY5Ag==</xmpMM:DocumentID><xmpMM:RenditionClass>proof</xmpMM:RenditionClass><pdf:PDFVersion>1.7</pdf:PDFVersion></rdf:Description></rdf:RDF></x:xmpmeta><?xpacket end="r"?> endstream endobj 750 0 obj @@ -3538,7 +3538,7 @@ xref 0000178455 00000 n
0000179529 00000 n
trailer -<</Size 751/Root 750 0 R/Info 748 0 R/ID[(HljsDPgWqw9TTnj4ezsOYg==)(HljsDPgWqw9TTnj4ezsOYg==)]>> +<</Size 751/Root 750 0 R/Info 748 0 R/ID[(eoOUAEv34jOjSGsTdiY5Ag==)(eoOUAEv34jOjSGsTdiY5Ag==)]>> startxref 179734 %%EOF
\ No newline at end of file diff --git a/docs/registry.pdf b/docs/registry.pdf index b714d5ad..ce7d0ef6 100644 --- a/docs/registry.pdf +++ b/docs/registry.pdf @@ -6478,12 +6478,12 @@ x�}��Kq�?�U�X
94%MQKS��N�#Ԧ��Q�v��B�ˡ�h�Fk ������! endstream endobj 1872 0 obj -<</Creator(Typst 0.15.1)/ModDate(D:20260827181039-03'00)/CreationDate(D:20260827181039-03'00)>> +<</Creator(Typst 0.15.1)/ModDate(D:20260827220903-03'00)/CreationDate(D:20260827220903-03'00)>> endobj 1873 0 obj <</Length 997/Type/Metadata/Subtype/XML>> stream -<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?><x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="xmp-writer"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/" xmlns:pdf="http://ns.adobe.com/pdf/1.3/" ><xmp:CreatorTool>Typst 0.15.1</xmp:CreatorTool><dc:language><rdf:Bag><rdf:li>en</rdf:li></rdf:Bag></dc:language><xmp:ModifyDate>2026-08-27T18:10:39-03:00</xmp:ModifyDate><xmp:CreateDate>2026-08-27T18:10:39-03:00</xmp:CreateDate><xmpTPg:NPages>20</xmpTPg:NPages><dc:format>application/pdf</dc:format><xmpMM:InstanceID>ZR7RqpW3YzCeQ3Vzke06dw==</xmpMM:InstanceID><xmpMM:DocumentID>ZR7RqpW3YzCeQ3Vzke06dw==</xmpMM:DocumentID><xmpMM:RenditionClass>proof</xmpMM:RenditionClass><pdf:PDFVersion>1.7</pdf:PDFVersion></rdf:Description></rdf:RDF></x:xmpmeta><?xpacket end="r"?> +<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?><x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="xmp-writer"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/" xmlns:pdf="http://ns.adobe.com/pdf/1.3/" ><xmp:CreatorTool>Typst 0.15.1</xmp:CreatorTool><dc:language><rdf:Bag><rdf:li>en</rdf:li></rdf:Bag></dc:language><xmp:ModifyDate>2026-08-27T22:09:03-03:00</xmp:ModifyDate><xmp:CreateDate>2026-08-27T22:09:03-03:00</xmp:CreateDate><xmpTPg:NPages>20</xmpTPg:NPages><dc:format>application/pdf</dc:format><xmpMM:InstanceID>ifdbNKHCMpMjAW51d8hH/Q==</xmpMM:InstanceID><xmpMM:DocumentID>ifdbNKHCMpMjAW51d8hH/Q==</xmpMM:DocumentID><xmpMM:RenditionClass>proof</xmpMM:RenditionClass><pdf:PDFVersion>1.7</pdf:PDFVersion></rdf:Description></rdf:RDF></x:xmpmeta><?xpacket end="r"?> endstream endobj 1874 0 obj @@ -8367,7 +8367,7 @@ xref 0000402637 00000 n
0000403712 00000 n
trailer -<</Size 1875/Root 1874 0 R/Info 1872 0 R/ID[(ZR7RqpW3YzCeQ3Vzke06dw==)(ZR7RqpW3YzCeQ3Vzke06dw==)]>> +<</Size 1875/Root 1874 0 R/Info 1872 0 R/ID[(ifdbNKHCMpMjAW51d8hH/Q==)(ifdbNKHCMpMjAW51d8hH/Q==)]>> startxref 403916 %%EOF
\ No newline at end of file diff --git a/next-steps.txt b/next-steps.txt index 3546da42..60605edc 100644 --- a/next-steps.txt +++ b/next-steps.txt @@ -2,6 +2,56 @@ WHAT THIS IS: a wishlist, in the author's own words. The items below are left exactly as written; this header is the only thing kept in sync with the code, because a list that reports shipped work as pending is worse than no list. +9P (docs/9p.typ is the note, docs/registry.typ the argument). SIX NEW THINGS, +each verified against a running program and not assumed: + +1. A DETACHED SESSION CAN BE SCRIPTED. `pardes --detach=work --fs` mounts its + own /dev/fuse and polls it in the same poll(2) as its frontends and pane + shells, so it needs no wake thread — less than the desktop shells pay. The + one configuration whose panes outlive every terminal was the one no script + could reach. Proof: examples/acmefs/pardesctl panes|new|send|body|del. + +2. TERMINALS ARE SCRIPTABLE. Every terminal pane grows `pty/ctl`, `pty/status` + and `pty/data`. `winsize 80 24`, `sig INT|TERM|HUP|QUIT|KILL`, `exec`; a + write to `pty/data` is input to the process and a read is its RAW output, + escape sequences and all. A script could only ever write into a terminal + that already existed; now it can start one, resize one and signal one. + +3. THE TREE IS SERVED OVER 9P. `--fs9` puts acme's control filesystem on a unix + socket as base 9P2000 (src/9p.zig — codec and a sans-io server, no threads, + no allocator, builds freestanding). plan9port drives it: `9p -a <sock> ls /`, + `read /index`, `write /1/body`, `stat`, a walk through `..`. Errors come back + as STRINGS Linux's table knows, not numbers. `--fs` and `--fs9` serve the + same tree at once: a write over one reads back through the other. + +4. A PATHNAME WITHOUT MOUNTING ANYTHING OURSELVES. `9pfuse <sock> <dir>` and + ordinary tools work — ls -l with the right modes, cat, shell `>>`, permission + denied on the write-only files. That is the route macOS and the browser take, + neither of which has ever had a control filesystem. + +5. ONE PARDES READS ANOTHER. The `9p <dial> <path>` word walks another + instance's tree and opens the bytes in a pane. Before this, two instances + could shout one line at each other (nested.zig, write-only, no reply) or + REPLACE one another (`Attach` deinits the local core); they could not ask a + question. Proof: B read A's /1/body byte-identically to plan9port. + +6. THE BOARD SERVES ITS OWN TREE. A second ESP32-P4 image where UART0 carries + 9P and nothing else — no ANSI, no vaxis, no allocator — 88 KB against the + console image's 809 KB. Its GPIO tree comes from a comptime table + (src/board9p.zig) that is the single description: one entry fans out to a + directory per pin. The console firmware is untouched and still what + `-Dplatform=esp32p4` feeds. No board was attached, so only what builds and + what the host tests cover is claimed. + +AND ONE THING THAT WENT AWAY: pardes no longer depends on the ../05-zig-p4 +toolchain checkout to build. It used to be a path dependency named in +build.zig.zon, and because `@import` in a build script resolves when the SCRIPT +compiles rather than when the branch needing it is taken, anyone without that +sibling could not build pardes at all — not the firmware, the terminal shell. +The object is the seam: `-Dplatform=esp32p4` emits it here with no toolchain, +and the toolchain repository links the flashable images, which it already knew +how to do. + SHIPPED (verified against the tree, not assumed): - "version tracking" -> done. `build.zig` reads `.version` from build.zig.zon via an untyped `@import`, `gitCommit(b)` reads the commit at configure time, |
