refactor!: remove the transport and print paths nothing reaches #35

Merged
james.maes merged 14 commits from refactor/2.0-remove-unreachable-transport-20261110 into release/2.0 2026-11-10 23:23:18 +00:00
Owner

Deletes the production code that nothing in the product reaches since the command line's legacy mode (pull request 28) and the runtime's polling path (pull request 24) were removed. There is now one composition: an API client built from a session with a realtime channel, and one way a document reaches Windows: from a staged file, through the print helper. Closes audit finding W30.

Removed

  • PrintApiClient: the device token and bearer header, the non-session constructor, the non-session enroll route (devices/enroll remains), the HTTP fallbacks for inventory, events and heartbeat, the in-memory download, the HTTP control poll and HTTP command receipt, and the client's own download pacing (the runtime's throttle is now required).
  • PrintWorker: its own recovery loop, heartbeat, inventory sync, in-memory download and submit; its options are required.
  • IFilePrinterBackend is folded into IPrinterBackend (Discover() plus PrintFileAsync), so a backend that cannot take a file no longer compiles; the in-memory PrintAsync is gone from every backend, from WindowsPrinters and from the helper's protocol.
  • IJobJournal.Incomplete(), DeviceControlPoll, JobClaim.

For the installed product: no change in printing, transport, pairing or history. One error message was reworded, and the helper refuses a print operation the service never sent.

Tests: Core goes from 793 to 766 cases: 30 that covered only removed code are deleted, 3 are added. Every worker test now runs through a harness that composes the worker as the service does, with the exposure filter in front of the printer. No Windows test was deleted.

Size: Core loses about 290 source lines, Windows about 120.

Verification, local, on this head (on 7610dec): build/Verify.ps1 passed twice with no failing test: 766 Core, 629 Windows/command-line and 352 presentation tests, zero warnings; Core 100%, command line 100%, presentation 100%, Windows 71.6%. The layout and UI test projects build (not run). Before the replay the branch passed three times on its earlier base.

Review: independent; nothing at should-fix or above. The reviewer confirmed each deleted member was unreachable from every project, that the HTTPS-or-loopback check is still enforced by the session client, that the submission deadline is enforced as before by ending the helper, that startup recovery covers what the worker's loop did, and that the four changed test expectations are right. Left for later steps: AgentRuntime's printer backend parameter still defaults to an in-process backend that has no deadline (the service always passes the isolated one), and the helper's request size bound is larger than any request now needs.

🤖 Generated with Claude Code

Deletes the production code that nothing in the product reaches since the command line's legacy mode (pull request 28) and the runtime's polling path (pull request 24) were removed. There is now one composition: an API client built from a session with a realtime channel, and one way a document reaches Windows: from a staged file, through the print helper. Closes audit finding W30. **Removed** - `PrintApiClient`: the device token and bearer header, the non-session constructor, the non-session `enroll` route (`devices/enroll` remains), the HTTP fallbacks for inventory, events and heartbeat, the in-memory download, the HTTP control poll and HTTP command receipt, and the client's own download pacing (the runtime's throttle is now required). - `PrintWorker`: its own recovery loop, heartbeat, inventory sync, in-memory download and submit; its options are required. - `IFilePrinterBackend` is folded into `IPrinterBackend` (`Discover()` plus `PrintFileAsync`), so a backend that cannot take a file no longer compiles; the in-memory `PrintAsync` is gone from every backend, from `WindowsPrinters` and from the helper's protocol. - `IJobJournal.Incomplete()`, `DeviceControlPoll`, `JobClaim`. **For the installed product**: no change in printing, transport, pairing or history. One error message was reworded, and the helper refuses a `print` operation the service never sent. **Tests**: Core goes from 793 to 766 cases: 30 that covered only removed code are deleted, 3 are added. Every worker test now runs through a harness that composes the worker as the service does, with the exposure filter in front of the printer. No Windows test was deleted. **Size**: Core loses about 290 source lines, Windows about 120. **Verification**, local, on this head (on `7610dec`): `build/Verify.ps1` passed twice with no failing test: 766 Core, 629 Windows/command-line and 352 presentation tests, zero warnings; Core 100%, command line 100%, presentation 100%, Windows 71.6%. The layout and UI test projects build (not run). Before the replay the branch passed three times on its earlier base. **Review**: independent; nothing at should-fix or above. The reviewer confirmed each deleted member was unreachable from every project, that the HTTPS-or-loopback check is still enforced by the session client, that the submission deadline is enforced as before by ending the helper, that startup recovery covers what the worker's loop did, and that the four changed test expectations are right. Left for later steps: `AgentRuntime`'s printer backend parameter still defaults to an in-process backend that has no deadline (the service always passes the isolated one), and the helper's request size bound is larger than any request now needs. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
james.maes added 14 commits 2026-11-10 23:23:16 +00:00
Nothing in the product calls the control poll: the runtime takes desired
state and commands from the realtime channel and delivers every receipt
over it. The poll, its batch validation, the DeviceControlPoll record, the
advertised capability list and the twenty second control limit go, and
ControlReceiptAsync now sends only over the channel. Validation of a single
realtime command is unchanged.

Tests removed with the code they covered: DeviceControlApiTests (the poll
against the shared sample and three unusable replies), the control poll
rows and the HTTP receipt rows in PrintApiClientTests, and the twenty
second control limit rows in InjectedClockTests.

Signed-off-by: James Maes <james@kof22.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The worker tests built a worker the product never builds: a device-token
API client with no realtime channel, no staging folder and an in-memory
printer. They now build it as the service does, through a WorkerHarness:
the API client carries a realtime channel, job events travel as
acknowledged frames, documents are staged and handed to a file-taking
printer, and every hook the runtime supplies is present. The fake server
receives the events from the channel and acknowledges or refuses each.

Each test keeps what it asserted. A pass that processed a job now returns
true whatever the outcome, as a lane's pass does, where the serial worker
returned true only for a spooled job. Tests that exercise only the paths
the service never takes are left alone here and go with that code.

Two tests are new: events left from a previous run are delivered in
recorded order before any claim, and a connection lost while the
SUBMITTING receipt awaits its acknowledgement is FAILED with nothing
submitted and replayed after the reconnect.

Signed-off-by: James Maes <james@kof22.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The worker had two shapes: the lane the runtime composes, and a serial
worker that recovered the journal itself, sent its own heartbeat and
inventory, downloaded into memory and submitted from memory. Only the
first is ever built. The second goes, with the optional parameters and
option defaults that selected it:

- PrintWorker takes its log, both predicates and its options as required
  parameters, takes a file-taking backend by type, so the check made at
  construction is made by the compiler, and RunOnceAsync takes its token.
- PrintWorkerOptions requires the staging folder, both settings, the
  gates and every hook; RecoverIncomplete and SynchronizeInventory are
  gone because the runtime always switched both off.
- IJobJournal.Incomplete is gone: the runtime reads unfinished jobs from
  its own store at start, and nothing asks the worker's journal for them.

A pass now returns true for any job it processed, as a lane's pass always
did. Nothing the runtime observes changes.

Tests removed with the code they covered, in PrintWorkerTests: a prior
SUBMITTING marked unknown by the worker's own recovery, the recovery of an
acknowledged interrupted state (two rows), and the recovery of an
unacknowledged submission; in PrintWorkerOutcomeTests: the refusal at
construction of a backend that does not take files. The runtime's own
recovery is covered by AgentRealtimeRestartTests. The outbox scale test in
the Windows suite now delivers its ten thousand events over a realtime
connection that acknowledges each.

Signed-off-by: James Maes <james@kof22.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
With the serial worker gone, nothing reaches the API client without a
realtime channel, so what it did in that case is unreachable:

- InventoryAsync and EventAsync lose their HTTP fallbacks (PUT
  device/printers, POST device/jobs/{id}/events). With no channel attached
  a send is refused as a closed connection, the failure the runtime already
  replays after reconnecting.
- Both HeartbeatAsync overloads go: the runtime sends its heartbeat frame
  on the channel itself, and only the serial worker called these.
- UsesRealtime goes with the only code that asked.
- The in-memory DownloadAsync goes: every document is staged to a file.

Tests removed with the code they covered: the in-memory download's
manifest refusals (three rows, PrintApiClientTests) and its request limit
(InjectedClockTests); the staged download has the same refusals and limits
under test. Tests moved: the heartbeat's profile shape and the inventory's
queue identity (ProfileProtocolTests) are now read off the frames the
service sends; the document larger than the control cap is staged
(PrintApiBoundsTests); the provider test sends an event. One test is new:
without a channel nothing is sent and nothing falls back to HTTP.

Signed-off-by: James Maes <james@kof22.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The API client paced a download itself, from MaxDownloadBytesPerSecond,
only when it was given no throttle. The worker always passes the runtime's
shared throttle, which is what holds the bandwidth limit across lanes, so
the client's own pacing never ran. DownloadFileAsync now requires the
throttle and the fallback is gone.

Test removed with the code it covered: the per-download rate limit on the
client's clock (InjectedClockTests). The limit the product enforces is the
runtime's, covered by AgentRealtimeBandwidthTests.

Signed-off-by: James Maes <james@kof22.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Nothing submits a document from memory any more, so the in-memory print
goes everywhere it was declared or passed through, and what is left is one
way to print:

- IPrinterBackend is Discover plus PrintFileAsync(job, path, limit,
  token). IFilePrinterBackend is gone, and with it the overload without a
  limit and the default that forwarded to it.
- ExposedPrinterBackend and ProfiledPrinterBackend forward the file to
  their inner backend, which now always takes one. The refusal they raised
  for an inner backend without file support, after SUBMITTING was already
  recorded, cannot occur and is removed.
- IsolatedPrinterBackend loses PrintAsync and the overload without a limit.
- WindowsPrinters loses PrintAsync and its helpers PrintRaw and
  PrintPdfAsync on bytes. PrintFileAsync takes the limit and does not
  apply it: the service enforces it by ending the helper process.
- PrinterWorkerHost loses the "print" operation and the Content field of
  its request. The --printer-worker protocol is otherwise unchanged:
  discover, print-file and cancel.

Tests removed with the code they covered, in PrintingSupportTests: the
exposure filter refusing a file for an inner backend that takes none, and
a file backend without the limit overload. Tests moved: the exposure
recheck at submission (Core and Windows suites) and the media refusal
across the helper boundary now submit a file with a limit. The two Core
printer fakes are one.

Signed-off-by: James Maes <james@kof22.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The API client could be given a device token, which it sent as a bearer
credential on every request, and without a session it enrolled on the
device route. The product builds the client only through ForSession, so
neither was reachable. The token parameter and the Authorization block
are gone, and EnrollAsync always posts to the session's enrollment route.
The client now holds no credential at all: what authenticates a request
is added by the session it is sent through.

Test removed with the code it covered: the device-route row of the
enrollment route test (PrintApiClientTests). The operator action test now
also requires that the client attaches no credential of its own.

Signed-off-by: James Maes <james@kof22.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
AgentStore.SaveConfiguration is internal: within the product only pairing
writes the connection, through StagePairing. It is not private because the
Windows suite seeds enrolled state with it.

The update installer's message for a missing helper said "diagnostic CLI".
The command line tool manages the installed service and has no diagnostic
mode any more, so the message names it plainly.

Signed-off-by: James Maes <james@kof22.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
With the device token gone, a client built without a session was one that
could authenticate nothing, and the product never built it: both places
that create a client call ForSession. What only that client could do was
unreachable, and goes:

- The public constructor taking a server address and a handler. The
  constructor is private and takes the session; ForSession is the one way
  in. The address check goes with it, because the session client has
  already required its origin to be HTTPS or allowed loopback HTTP.
- The /printing/v1/ base address, which ForSession replaced at once.
- The catalog's refusal for a client without a session.
- The document request sent outside the session.

The client now cannot send a request outside a session by construction.

Tests removed with the code they covered: the plain-http address refusal
(PrintWorkerTests) and the catalog's session refusal
(OperatorPrinterCatalogTests). Every other test that built a client over
its own fake server now builds it through a session whose requests that
server answers, so the routes they assert are the printing extension's.

Signed-off-by: James Maes <james@kof22.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The API client has read a claim through PrintJobContract since the claim
boundary was hardened, and nothing in the product has used the JobClaim
record since. The shared claim sample is now read in ProtocolFixtureTests
the way the client reads it, and the fake server writes its claims without
the record.

Signed-off-by: James Maes <james@kof22.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
REALTIME says what travels only on the channel, what the worker does and
what the runtime does for it, and replaces the note on the file
requirement (and the wrapper gap behind it) with the single way to print.
The threat model no longer says the bearer constructor still ships: no
shipped code can send a device token outside a session, and it says why.
It also states the one path a document takes to Windows and the three
operations the print helper accepts. DEVELOPMENT and CLAUDE name the three
rules of the composition and the test harness; the printer approval note
drops the HTTP inventory route; the changelog lists what was removed.

Signed-off-by: James Maes <james@kof22.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The entry listed "the device enrollment route" among what is gone, which
reads as if enrollment was removed. Enrollment posts to the session's
devices/enroll route as before; only the enroll route of a client without
a session was removed.

Signed-off-by: James Maes <james@kof22.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
After the heartbeat moved off the API client, this test built its own
frame, so it checked how a profile is serialized and not what the service
sends. It now tests the serializer directly and says so: an explicit clear
is kept, an unset member is left out, and a profile with nothing set is an
empty object. The half that sent a frame with no profile at all is
dropped, because the service never sends one. The heartbeat the service
sends is read in the Windows suite (AgentRuntimeProfileTests).

Signed-off-by: James Maes <james@kof22.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The service hands a worker its backend behind ExposedPrinterBackend; the
harness handed it the bare fake. Worker tests now discover and submit
through the same filter, reading a set of shared printers the test can
change. No test changed outcome. The profile wrapper the service adds
between the filter and the backend lives in the Windows layer and is not
part of this composition.

Signed-off-by: James Maes <james@kof22.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
james.maes merged commit b868e9e792 into release/2.0 2026-11-10 23:23:18 +00:00
Sign in to join this conversation.