2. Architecture Across Three Times
BinaryWrapper operates at three times: collecting evidence, compiling APIs and executing commands. Separating them allows a build to use cached inputs, business code to use contracts and a process execution to choose a fresh parser.
The architecture is easier to understand by following dependencies than by treating every project containing “Design” as a runtime library.
Three times and three sets of inputs
Design time runs collectors and help acquisition. Its outputs are versioned JSON trees and reusable help evidence. Build time reads those artifacts and declarative catalogues through Roslyn. Runtime consumes compiled command types, a binary binding and output parser factories.
Failures belong to the boundary where they occur. An image recipe failure is an acquisition problem. A malformed JSON input is a compilation-input problem. An unknown output version is a parsing-contract problem. A nonzero process exit is an execution result.
Framework project responsibilities
The project names describe actual ownership in the inspected source.
| Project | Responsibility |
|---|---|
BinaryWrapper.Contracts |
Shared commands, process values, output records, parser/collector contracts, runner and executor interfaces, environment abstractions. |
BinaryWrapper.Lib |
Semantic-version values and comparison used across generation and execution. |
BinaryWrapper.Attributes |
Descriptor and environment declaration metadata. |
BinaryWrapper.SourceGenerator |
Roslyn command, builder, client, environment-attribute and forwarding generation. |
BinaryWrapper |
Concrete process execution, binary resolution, parser strategies and runner implementation. |
BinaryWrapper.Design |
Help models, parsers, scraping and container-related support. |
BinaryWrapper.Design.Lib |
Design pipeline orchestration and, in a separate target, environment catalogue generation. |
BinaryWrapper.Testing |
Fake transports, commands and helpers for testing execution and scraping. |
The runtime and contracts inspected here target net11.0. Roslyn components use netstandard2.0. These are the current project targets, rather than a statement about every past release.
The two faces of Design.Lib
BinaryWrapper.Design.Lib targets both netstandard2.0 and net11.0, with different compiled source sets.
Its net11.0 target supplies the pipeline runner, CLI, image plans and Windows-binary acquisition. Its netstandard2.0 target supplies the environment catalogue reader and incremental generator, with Roslyn dependencies kept private.
The contracts project references the generator target as an analyzer. That reference does not make the Design executable or its container operations a runtime dependency of the contracts assembly. Environment generation explains the inputs read by this analyzer.
Consumer package ownership
A tool integration normally has a Design executable, a Contracts library, an implementation library and tests.
Contracts own generated commands, typed environments, output events, result records and IVagrantRunner. The implementation owns the concrete runner, parser strategies, parsers, collectors, generated builders and generated client. The existing tool namespace is retained even when assembly ownership changes.
This split is controlled by descriptor generation flags. A contracts descriptor requests BinaryWrapperGeneration.Commands. The implementation descriptor requests Builders | Client. Both use the same help data and environment type.
Runtime data flow
The generated client constructs commands. It does not implicitly execute them. The runner or executor resolves a binding, builds argument and environment snapshots, then asks IProcessRunner to produce a stream or buffered result.
For typed streaming, the executor resolves the binding once and creates a parser with the selected command/binary version. Both process channels reach that parser. After drainage, Complete(actualExitCode) emits final observations. A collector can aggregate the same events.
ICommandExecutor is an interface in shared contracts. A concrete tool runner can therefore be assembled with an injected execution service, while business code needs only its tool-specific runner interface.
Different artifacts have different lifecycles
Help snapshots are acquired and updated by Design tools. Roslyn output is recomputed from build inputs. Vagrant's source-derived UI catalogue is regenerated by a developer maintenance tool and checked into its projects. Runtime state belongs to a single execution.
These distinctions affect packaging and debugging. A missing command type sends you to inputs and generation flags. A mismatched UI template sends you to the pinned output catalogue and profile. A process hang sends you to stream lifetime and cancellation. Chapter 13 turns those boundaries into a maintenance workflow.
Source snapshot
Source inspection: 2 October 2026. These implementation paths are relative to Net/FrenchExDev in the local ecosystem checkout. The v2 changes are largely uncommitted; these paths identify inspected code, without claiming that public branch links contain it.
BinaryWrapper/doc/ARCHITECTURE.mdBinaryWrapper/src/FrenchExDev.Net.BinaryWrapper.Design.Lib/FrenchExDev.Net.BinaryWrapper.Design.Lib.csprojVagrant/src/FrenchExDev.Net.Vagrant.Contracts/FrenchExDev.Net.Vagrant.Contracts.csproj
Previous · Series index · Next