a73x

af32c00c

docs+build: record the diff-since deferral; the agent e2e becomes a gate

a73x   2026-08-13 20:17

Commit message
docs+build: record the diff-since deferral; the agent e2e becomes a gate

Two findings from the whole-branch review, both about things that were
silently true rather than decided.

`capture --diff-since` is promised three times in the spec — the verb
list, the TUI-driving composition, the testing plan — and was dropped in
execution with no code and no record. It is now recorded as deferred in
both files, with the consequence the review identified: `status_reply`'s
seq carries the RETURN WATERMARK, which is what an await's since_seq
wants and useless as the SEQ a diff quotes, so the live stream seq is on
no reply an agent can read. A future --diff-since needs a protocol field
of its own, not a re-reading of a field that already means something
else. Two smaller narrowings go down beside it: fish's injection arm is
unit-tested only (no fish on this box; zsh and bash are driven live), and
the spec's version-skew fixture was never built (the behaviour is pinned
through the dead-daemon paths, an old binary is not). The roadmap also
names `muxa capture` as the one verb with no automated coverage at all —
the test and the flag are owed together.

`zig build agent` runs test/agent.sh against the muxd and muxa this build
produced, mirroring the e2e step's shape. Its own step rather than a
place in e2e, because its nine scenarios spend ~51s mostly waiting on
real idle timeouts. This is M7's rule paid again: an end-to-end property
that only runs when somebody types the script is the shape a regression
ships through. Failure propagation verified by breaking one scenario's
expected exit code — the step exits 1 and names the scenario — then
restored.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

build.zig
Old New
@@ -639,6 +639,19 @@ pub fn build(b: *std.Build) void {
639 const e2e_step = b.step("e2e", "Run end-to-end test"); 639 const e2e_step = b.step("e2e", "Run end-to-end test");
640 e2e_step.dependOn(&e2e.step); 640 e2e_step.dependOn(&e2e.step);
641 641
642 // The agent surface gets a step of its own rather than a place in e2e:
643 // its nine scenarios spend ~51s mostly waiting on real idle timeouts and
644 // a 20s quiet await, which is a cost the paint-and-convergence suite
645 // should not have to carry on every run. A step is what makes it a gate
646 // at all — M7's rule, paid for twice: an end-to-end property that only
647 // runs when somebody types the script is the shape a regression ships
648 // through.
649 const agent = b.addSystemCommand(&.{"test/agent.sh"});
650 agent.addArtifactArg(exe);
651 agent.addArtifactArg(muxa_exe);
652 const agent_step = b.step("agent", "Run the agent-surface end-to-end suite");
653 agent_step.dependOn(&agent.step);
654
642 const soak = b.addSystemCommand(&.{"test/soak.sh"}); 655 const soak = b.addSystemCommand(&.{"test/soak.sh"});
643 soak.addArtifactArg(exe); 656 soak.addArtifactArg(exe);
644 soak.addArtifactArg(mux_exe); 657 soak.addArtifactArg(mux_exe);
docs/decisions.md
Old New
@@ -3116,6 +3116,31 @@ single-shot `muxa drive` (`send` + `await` + `capture` composes it). Added
3116 by the reviews: the `.zshenv` shim, bash-preexec coexistence, and the 3116 by the reviews: the `.zshenv` shim, bash-preexec coexistence, and the
3117 shim-directory startup sweep. 3117 shim-directory startup sweep.
3118 3118
3119 **`capture --diff-since` was promised and not built, and this is the
3120 record.** The spec names it three times — the verb list, the TUI-driving
3121 composition ("`send` + `status` + `capture --diff-since`"), and the testing
3122 plan — and execution dropped it with no code and no deferral written
3123 anywhere; the whole-branch review is the only reason it is written down
3124 now. `muxa capture` ships as the whole grid, `--vt` or plain. What a future
3125 one owes is more than the flag: `status_reply`'s `seq` carries the RETURN
3126 WATERMARK (`last_return`'s seq, 0 when nothing has returned this session),
3127 which is exactly what an await's `since_seq` wants and useless as the SEQ a
3128 diff quotes — after this milestone the live stream seq is on no reply an
3129 agent can read. So `--diff-since` needs a protocol field of its own, and
3130 must not be built by re-reading a field that already means something else.
3131
3132 **Two smaller narrowings against the spec, recorded rather than left to be
3133 rediscovered.** (1) The **fish** injection arm is unit-tested only:
3134 `prepare` writes `fish/vendor_conf.d` and prepends `XDG_DATA_DIRS`, and
3135 that is pinned, but no test anywhere runs fish — there is none on this box.
3136 zsh and bash are both driven live against a real session. (2) The spec's
3137 **version-skew test** — `muxa status` against an old-protocol daemon
3138 reports the structured error — was not written. The behaviour is real and
3139 not unpinned (a daemon that drops the frame and a daemon that never answers
3140 are the same wait and the same `{"error":"status: no reply"}`, which the
3141 dead-daemon paths do exercise), but the named fixture, an actual old
3142 binary, does not exist.
3143
3119 **Paid in a separate commit, and only after the e2e pinned the spellings:** 3144 **Paid in a separate commit, and only after the e2e pinned the spellings:**
3120 `parseQuicAddr`/`resolveHost` now live in `quic.zig` as one owner for the 3145 `parseQuicAddr`/`resolveHost` now live in `quic.zig` as one owner for the
3121 dial-address grammar, taking an allocator (`client.zig`'s copy hardcoded 3146 dial-address grammar, taking an allocator (`client.zig`'s copy hardcoded
docs/roadmap.md
Old New
@@ -271,6 +271,19 @@ sweep. The `parseQuicAddr`/`resolveHost` dedup that review endorsed is
271 **paid**, not deferred — one owner in `quic.zig`, landed after the e2e had 271 **paid**, not deferred — one owner in `quic.zig`, landed after the e2e had
272 pinned the spellings. 272 pinned the spellings.
273 273
274 **Dropped in execution, not decided — the one item here that nobody chose:**
275 `muxa capture --diff-since SEQ`. The spec promises it three times and no
276 code was written; the whole-branch review found it. A future one needs a
277 protocol field of its own for the live stream seq, because `status_reply`'s
278 seq now carries the return watermark — right for awaits, wrong for a diff
279 to quote (decisions.md). Two coverage narrowings ride with it: the **fish**
280 injection arm is unit-tested only (no fish on this box; zsh and bash are
281 driven live), and the spec's **version-skew fixture** was never built (the
282 behaviour is pinned through the dead-daemon paths, an old binary is not).
283 And `muxa capture` is **the one verb with no automated coverage at all** —
284 `test/agent.sh` drives run, await, send and status; capture is where
285 `--diff-since` would land, so the test and the flag are owed together.
286
274 ## M16 candidates — and still outranked by trial feedback 287 ## M16 candidates — and still outranked by trial feedback
275 288
276 What remains is what was ranked behind M13 and M14 and survived M15, 289 What remains is what was ranked behind M13 and M14 and survived M15,