<feed xmlns='http://www.w3.org/2005/Atom'>
<title>duet/tests/test-duet-pane.el, branch main</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.
</subtitle>
<id>https://git.cjennings.net/duet/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/duet/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/duet/'/>
<updated>2026-06-06T20:36:25+00:00</updated>
<entry>
<title>feat: add the local transfer execution engine and queue</title>
<updated>2026-06-06T20:36:25+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-06T20:36:25+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/duet/commit/?id=3b244ba0492fd86fca051713196067f833f34a1b'/>
<id>urn:sha1:3b244ba0492fd86fca051713196067f833f34a1b</id>
<content type='text'>
Phase 5 turns the pure transfer-specs from Phase 3 into running transfers. duet--run-transfer spawns the backend over make-process, and a serial queue (duet-max-concurrent-transfers, default 1) holds the rest until a slot frees. Each transfer carries a state machine: queued, running, stalled, cancelling, then a terminal done, failed, or cleanup-unverified.

The process filter is the hot path, so it stays cheap: it counts output, bounds it to a trailing window, and schedules one coalesced log render. It never refreshes panes. Pane refresh runs once per batch from the sentinel, also coalesced. A move deletes its sources only after the copy exits 0, so a failed move leaves the source untouched. Cancellation kills the process and, for a backend declaring verifiable cleanup, checks the destination for stray temp files before settling on failed versus cleanup-unverified.

The process boundary and the temp-file lister are injectable, so the queue and classification logic test against fake results while real rsync gets its own slow integration file. duet-copy, duet-move, duet-mkdir, and duet-delete are wired to the engine. The viewer actions stay stubs until their phase. In-process TRAMP and both-remote rsync still refuse to run here. They land with Phase 6.

172 tests, duet.el at 100% line coverage, compile/lint/complexity green.
</content>
</entry>
<entry>
<title>fix: quit the whole commander on q, not just one pane</title>
<updated>2026-06-06T19:14:48+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-06T19:14:48+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/duet/commit/?id=0155eb670c2f9e072c34671537d95c716a54e011'/>
<id>urn:sha1:0155eb670c2f9e072c34671537d95c716a54e011</id>
<content type='text'>
In a pane, q ran dired's quit-window, which closes only the window it is in, so quitting left the other pane behind. Binding q to duet-quit in duet-mode-map makes it tear down both panes and restore the pre-launch layout, the same as F10. q is the key a commander user reaches for, so it should mean "leave the commander," not "close this window."
</content>
</entry>
<entry>
<title>feat: add the commander mode, F-key map, and two-pane entry</title>
<updated>2026-06-06T18:51:28+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-06T18:51:28+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/duet/commit/?id=a4b332929099ad42c893b6940dcf43f1d48914a6'/>
<id>urn:sha1:a4b332929099ad42c893b6940dcf43f1d48914a6</id>
<content type='text'>
The duet command lays out two side-by-side dired panes, each in the buffer-local duet-mode, which carries the mc/Norton F-key map (F3 view, F4 edit, F5 copy, F6 move, F7 mkdir, F8 delete, F10 quit). The map is a minor-mode keymap, so the single-key actions fire only inside a pane and leave the global F-keys untouched elsewhere. duet-quit restores the window layout from before launch (Phase 4 in the design spec).

duet--other-pane is the explicit other-pane resolution that replaces dired-dwim-target, the dirvish#36 fix. Its logic lives in a pure helper, duet--sibling-pane, that takes the active window and the list of commander panes and returns the sibling, erroring clearly when there are not exactly two. Keeping the decision pure makes the dangerous part testable without a live frame.

DUET does not depend on dirvish. A pane is a plain dired buffer, and when the user has dirvish rendering dired buffers it renders as dirvish with no extra work. That keeps dirvish recommended but never required, and avoids binding the package to a specific dirvish entry point.

The transfer and file actions are stubs that announce themselves until their phases land, so the keys are bound and their precedence is tested now. The pure selector, the keymap, and minor-mode precedence are unit-tested; the live two-pane launch and the F-key feel in a running Emacs are a manual check.
</content>
</entry>
</feed>
