<feed xmlns='http://www.w3.org/2005/Atom'>
<title>duet/tests/test-duet-backend.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-06T16:37:39+00:00</updated>
<entry>
<title>test: cover the remaining failure, contract, and safety branches</title>
<updated>2026-06-06T16:37:39+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-06T16:37:39+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/duet/commit/?id=0930ec3c259d93a68fc59854677cdc7fef634e71'/>
<id>urn:sha1:0930ec3c259d93a68fc59854677cdc7fef634e71</id>
<content type='text'>
The bug fixes raised line coverage on duet.el to 92.3%. This closes the rest. The new tests exercise the branches a review would want locked down before execution and UI build on this core: each arm of the minimal failure normalizer (missing executable, stall, signal cancellation, unknown-without-exit), a :match predicate rather than a regexp, the rsync normalizer mapping known stderr to permission/space/protocol classes, the capability-tier contract check, an explicit :async override, the case-collision path through the safety composite's injected predicates, and redaction of a pattern with no capture group.

Line coverage on duet.el is now 100%. The one deliberately trivial case left is the entry command, a not-yet-implemented stub that errors. Its test asserts that and will change when the pane layout lands.
</content>
</entry>
<entry>
<title>fix: make the in-process execution mode explicit in specs and contract checks</title>
<updated>2026-06-06T16:23:29+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-06T16:23:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/duet/commit/?id=7757f6909bfcc20211e8ae1f4ad364082ca924f5'/>
<id>urn:sha1:7757f6909bfcc20211e8ae1f4ad364082ca924f5</id>
<content type='text'>
Two coupled holes surfaced in the Phase 0-3 review. duet--transfer-spec copied only :argv, :default-directory, and :process-environment out of a backend's command result, dropping the :tramp marker, so a TRAMP-routed spec arrived as :argv nil with nothing telling the executor to copy in process. And duet--check-command leaned on listp, where nil is a list in Elisp, so a command builder returning nil or a bare :argv nil passed the minimum tier.

Both turn on the same idea, so they share a fix. duet--command-spec-executable-p defines a runnable spec: a non-empty plist with either a non-empty :argv of strings (a CLI backend) or a declared in-process mode such as :tramp. The contract checker rejects anything else, and transfer-spec now carries :tramp through, so the TRAMP fallback has a positive execution signal rather than an ambiguous nil argv.

The legitimate TRAMP backend keeps passing because it declares its mode. A broken backend that forgets argv no longer slips through.
</content>
</entry>
<entry>
<title>feat: add the transport-backend registry and contract checks</title>
<updated>2026-06-06T15:50:10+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-06T15:50:10+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/duet/commit/?id=fdc5f550d35d97272e3e5cac2f46ca7f892dda09'/>
<id>urn:sha1:fdc5f550d35d97272e3e5cac2f46ca7f892dda09</id>
<content type='text'>
This is the seam every transport plugs into (Phase 2 in the design spec). A duet-backend struct carries a scorer, a command builder, capability flags, and contract metadata; duet-register-backend keeps a registry where re-registering a name replaces the prior backend, which is how a user or plugin overrides a built-in. duet--select-backend asks every backend to score an endpoint pair and picks the lowest cost, breaking ties toward the most recently registered.

The failure-normalizer interface turns a raw process failure into a class, a cause, evidence, a safety outcome, and next actions. Backends supply a pattern table via duet-define-cli-failure-patterns; anything unmatched falls back to a minimal normalizer that covers launch failure, missing executable, stall, signal, and unknown exit. This is what will let DUET explain a failure instead of dumping stderr.

Secrets are redacted before they reach a log: a pattern keeps its group-1 field label and strips the value, and a backend with no secret surface declares :none rather than an empty pattern list, so a forgotten declaration (nil) stays distinguishable from a deliberate one.

The tiered contract checks (minimum, publishable, capability) return a list of violations a backend author asserts is empty in one ERT test. The built-in rsync and TRAMP backends register through this same API in Phase 3, once their command builders land; here the machinery is exercised by fake backends.
</content>
</entry>
</feed>
