ee301abd
feat: muxweb argv adds tiles to the wall, never overwrites it
a73x 2026-08-20 00:50
Commit message
src/webhub_main.zig
| Old | New | ||
|---|---|---|---|
| @@ -233,12 +233,23 @@ pub fn main() !u8 { | |||
| 233 | else => return err, | 233 | else => return err, |
| 234 | }; | 234 | }; |
| 235 | defer hub.deinit(); | 235 | defer hub.deinit(); |
| 236 | // Argv is the override, so it is also what the next run restores. Saved | 236 | // Argv overrides the VIEW — this run shows the tiles it named, and only |
| 237 | // only when there WAS argv: a restore that rewrites what it just read | 237 | // those — but it no longer overwrites the FILE. That file stopped being |
| 238 | // would turn a read failure into a lost wall. | 238 | // "the last wall muxweb was told to show" when attaches started writing |
| 239 | if (parsed.tiles.items.len != 0) wall.save(&hub.wall_state, state_path) catch |err| { | 239 | // to it: it is the user's attach history now, and one |
| 240 | std.debug.print("muxweb: cannot save wall {s}: {s}\n", .{ state_path, @errorName(err) }); | 240 | // `muxweb HOST` would have silently erased every tile every `mux` had |
| 241 | return 2; | 241 | // recorded. So each argv tile is ADDED (deduped by spelling, wall.zig) |
| 242 | // and nothing is removed. Forgetting stays explicit, which is the whole | ||
| 243 | // "remove is detach" doctrine: the page's `×`, the wall's `x`, | ||
| 244 | // `mux wall rm`. | ||
| 245 | // | ||
| 246 | // Written only when there WAS argv, still: a restore that rewrote what | ||
| 247 | // it just read would turn a read failure into a lost wall. | ||
| 248 | if (parsed.tiles.items.len != 0) for (hub.wall_state.targets.items) |spelling| { | ||
| 249 | _ = wall.record(alloc, state_path, spelling) catch |err| { | ||
| 250 | std.debug.print("muxweb: cannot save wall {s}: {s}\n", .{ state_path, @errorName(err) }); | ||
| 251 | return 2; | ||
| 252 | }; | ||
| 242 | }; | 253 | }; |
| 243 | 254 | ||
| 244 | const addr = std.net.Address.parseIp("127.0.0.1", parsed.port) catch unreachable; | 255 | const addr = std.net.Address.parseIp("127.0.0.1", parsed.port) catch unreachable; |
test/e2e.sh
| Old | New | ||
|---|---|---|---|
| @@ -4562,10 +4562,16 @@ curl -s "$DWORIG/tiles" | grep -q "^\[{\"id\":0,\"label\":\"--sock $SOCK25#b\"" | |||
| 4562 | echo "e2e FAIL: dyn wall: restart lost the persisted wall; it holds:" | 4562 | echo "e2e FAIL: dyn wall: restart lost the persisted wall; it holds:" |
| 4563 | curl -s "$DWORIG/tiles"; exit 1; } | 4563 | curl -s "$DWORIG/tiles"; exit 1; } |
| 4564 | 4564 | ||
| 4565 | # Restart WITH argv: the explicit override replaces the file rather than | 4565 | # Restart WITH argv: the override is the VIEW's, not the file's. This |
| 4566 | # appending to it, and is itself saved as the next run's wall. The tile is | 4566 | # assertion INVERTED with the attach-history phase and did not merely move. |
| 4567 | # spelled as ONE quoted argument — the wall file's own spelling, handed | 4567 | # It used to read "argv appended to the wall instead of replacing it" — a |
| 4568 | # back to the binary that wrote it; the grep below is what makes it a pin. | 4568 | # pin on muxweb overwriting the state file with whatever it was told to |
| 4569 | # show. That file is the user's attach history now, written by every `mux` | ||
| 4570 | # attach, so one `muxweb HOST` overwriting it would silently erase the lot. | ||
| 4571 | # Argv tiles are ADDED (deduped by spelling) and nothing is removed; | ||
| 4572 | # forgetting stays explicit — the page's `x`, the wall's `x`, `mux wall rm`. | ||
| 4573 | # The tile is spelled as ONE quoted argument — the wall file's own | ||
| 4574 | # spelling, handed back to the binary that wrote it. | ||
| 4569 | kill "$W4PID" 2>/dev/null || true | 4575 | kill "$W4PID" 2>/dev/null || true |
| 4570 | wait_pid_gone "$W4PID" "dyn wall: restored hub killed by tracked pid" | 4576 | wait_pid_gone "$W4PID" "dyn wall: restored hub killed by tracked pid" |
| 4571 | XDG_STATE_HOME="$DWSTATE" "$MUXWEB" "--sock $SOCK25" --port "$WPORT4" > "$OUT.dwh3" 2>&1 & | 4577 | XDG_STATE_HOME="$DWSTATE" "$MUXWEB" "--sock $SOCK25" --port "$WPORT4" > "$OUT.dwh3" 2>&1 & |
| @@ -4573,10 +4579,17 @@ W4PID=$! | |||
| 4573 | wait_for "$OUT.dwh3" "serving" 10 || { | 4579 | wait_for "$OUT.dwh3" "serving" 10 || { |
| 4574 | echo "e2e FAIL: dyn wall: overriding hub never reported serving"; cat "$OUT.dwh3"; exit 1; } | 4580 | echo "e2e FAIL: dyn wall: overriding hub never reported serving"; cat "$OUT.dwh3"; exit 1; } |
| 4575 | grep -qxF -- "--sock $SOCK25" "$DWSTATE/mux/wall" || { | 4581 | grep -qxF -- "--sock $SOCK25" "$DWSTATE/mux/wall" || { |
| 4576 | echo "e2e FAIL: dyn wall: argv did not replace the wall file; it holds:" | 4582 | echo "e2e FAIL: dyn wall: argv did not reach the wall file; it holds:" |
| 4577 | cat "$DWSTATE/mux/wall"; exit 1; } | 4583 | cat "$DWSTATE/mux/wall"; exit 1; } |
| 4578 | [ "$(wc -l < "$DWSTATE/mux/wall")" = "1" ] || { | 4584 | # ...and the line that was already there SURVIVED it. Both halves, because |
| 4579 | echo "e2e FAIL: dyn wall: argv appended to the wall instead of replacing it:" | 4585 | # either alone is satisfied by the wrong behaviour: the grep above passes |
| 4586 | # on an overwrite, and a count alone would not say which lines are which. | ||
| 4587 | grep -qxF -- "--sock $SOCK25#b" "$DWSTATE/mux/wall" || { | ||
| 4588 | echo "e2e FAIL: dyn wall: argv ERASED the wall it was added to — an" | ||
| 4589 | echo " attach history overwritten by one muxweb invocation:" | ||
| 4590 | cat "$DWSTATE/mux/wall"; exit 1; } | ||
| 4591 | [ "$(wc -l < "$DWSTATE/mux/wall")" = "2" ] || { | ||
| 4592 | echo "e2e FAIL: dyn wall: the wall file is not the two lines argv added to:" | ||
| 4580 | cat "$DWSTATE/mux/wall"; exit 1; } | 4593 | cat "$DWSTATE/mux/wall"; exit 1; } |
| 4581 | 4594 | ||
| 4582 | kill "$W4PID" 2>/dev/null || true | 4595 | kill "$W4PID" 2>/dev/null || true |
| @@ -4584,7 +4597,7 @@ wait_pid_gone "$W4PID" "dyn wall: overriding hub killed by tracked pid" | |||
| 4584 | W4PID="" | 4597 | W4PID="" |
| 4585 | assert_stopped "$SOCK25" "$D22PID" "dyn wall" "$OUT.dwstop" | 4598 | assert_stopped "$SOCK25" "$D22PID" "dyn wall" "$OUT.dwstop" |
| 4586 | D22PID="" | 4599 | D22PID="" |
| 4587 | ok "the wall is runtime state: add, remove, reorder, restore, argv overrides" | 4600 | ok "the wall is runtime state: add, remove, reorder, restore, argv adds" |
| 4588 | 4601 | ||
| 4589 | # ---- the CLI wall: the hub's multiattach, in one terminal -------------- | 4602 | # ---- the CLI wall: the hub's multiattach, in one terminal -------------- |
| 4590 | # One daemon, two named sessions, one `mux wall` on a real pty showing | 4603 | # One daemon, two named sessions, one `mux wall` on a real pty showing |