Skip to main content
Welcome. This site supports keyboard navigation and screen readers. Press ? at any time for keyboard shortcuts. Press [ to focus the sidebar, ] to focus the content. High-contrast themes are available via the toolbar.
serard@dev00:~/cv

9. Selecting Versioned Output Contracts

Output compatibility is selected independently from command availability. A versioned help snapshot can justify a C# option without proving that a parser understands the messages printed by that option.

The runtime therefore selects a fresh parser from an explicit output contract. The version, command path and requested format all matter.

What a profile promises

A profile names the output contract the parser intends to recognize for a version interval. It does not certify every plugin, locale, server response or arbitrary application log produced by that tool.

Unknown versions remain available as raw output with terminal status. This is useful product behavior: transport continues to expose evidence while semantic guarantees stay within the reviewed boundary.

The implementation matrix reports output profiles separately from help collection. A wide command API and a narrow output profile can coexist.

Factory lifecycle and version precedence

IOutputParserFactory<TEvent> has CreateParser(OutputParserContext). The context carries the command and the version chosen by the executor:

command.TargetVersion ?? binding.DetectedVersion

This is an expression excerpt from the existing selection path. The executor resolves a binding once, creates the parser once before starting the process, feeds both streams and calls Complete(actualExitCode) after drainage.

An explicit command target takes precedence. The executor does not replace that target, rerun generated construction guards or prove it matches the installed executable. Supply truthful version metadata when composing the application.

Factories return fresh parser instances because parsers can retain JSON buffers, artifact state or protocol context. Repeated or concurrent executions must not share such state.

Strategy entries and interval selection

OutputParserStrategy<TEvent> is an immutable factory containing named OutputParserProfile<TEvent> entries and a fallback factory. Each entry has an inclusive minimum and exclusive maximum; either can be unbounded.

Matching uses semantic-version precedence. Numeric prerelease identifiers compare numerically. Build metadata does not affect precedence. A null version always chooses the fallback.

When profiles overlap, the greatest matching minimum wins. Equal minima are resolved by the last registered entry. WithProfile adds or replaces a named entry and returns a new strategy, leaving the original unchanged.

Diagram
Version selection narrows the eligible output contract; it does not request an output format from the executable.

Stable patches and prerelease boundaries

Vagrant's built-in reviewed patch uses [2.4.9, 2.4.10-0). That includes stable 2.4.9 and its build metadata, while excluding 2.4.9 prereleases and every prerelease of the next patch.

Using 2.4.10 as the upper bound would also admit 2.4.10 prereleases. The -0 boundary intentionally closes that gap.

Git has broader intervals in its implementation: 2.30.0 to 2.41.0, and 2.41.0 to 3.0.0-0. That is the configured compatibility assumption for its reviewed protocols, not a claim that every minor version has identical diagnostics or that all future Git 2 releases were exercised.

Dotnet also has a profile for an exact reviewed RC SDK. “Prerelease” is not synonymous with unsupported; support depends on an explicitly reviewed profile.

Extending an existing strategy

The following complete offline example registers an application-named profile using the already reviewed Vagrant parser. It demonstrates extension mechanics without inventing a future protocol.

using FrenchExDev.Net.BinaryWrapper;
using FrenchExDev.Net.BinaryWrapper.Lib;
using FrenchExDev.Net.Vagrant;

var version = SemanticVersion.Parse("2.4.9");
var strategy = VagrantOutputStrategies.MachineReadable.WithProfile(
    new OutputParserProfile<VagrantEvent>(
        "application-reviewed-vagrant-2.4.9",
        version,
        SemanticVersion.Parse("2.4.10-0"),
        _ => new VagrantMachineReadableParser()));
var command = new VagrantUpCommand(version) { MachineReadable = true };
var context = new OutputParserContext(version, command);
var first = strategy.CreateParser(context);
var second = strategy.CreateParser(context);
Console.WriteLine($"Fresh instances: {!ReferenceEquals(first, second)}");
var line = new OutputLine(
    "1700000000,web,state,running", OutputSource.StdOut);
foreach (var observation in first.ParseLine(line))
    Console.WriteLine($"{observation.OutputVersion}/{observation.OutputProfile}");

For an actual new format, the application must supply a parser justified by new evidence. Merely widening a version interval does not establish that evidence.

Direct parser overloads remain available. Their caller owns state and reuse decisions. The factory path is the safer default when state isolation is required.

Format selection is another decision

Git distinguishes porcelain, references, diff and Trace2. Docker distinguishes events, formatted objects and inspect arrays. Packer distinguishes machine-readable and human output. Their default factories inspect the command's path and serialized options.

An explicit ForFormat factory selects parsing; it does not add --json, --format or another switch to the command. Request the format through command properties or an environment variable where the tool supports one.

Compose's JSON progress illustrates the interaction: COMPOSE_PROGRESS requests progress encoding, while the output strategy chooses which reviewed decoder to apply. Application logs can still be present in the same execution.

A maintenance decision with evidence

To add a version, identify the relevant emitter revision and any independent server/plugin protocol. Obtain source-shaped fixtures or bounded recordings. Verify field types, framing, channels and terminal behavior. Then add a profile interval narrow enough for that evidence.

Test its lower and upper edges, prereleases, build metadata, null version, malformed records and concurrent state isolation. Keep the previous strategy behavior stable unless an intentional migration changes it.

Profile names and event metadata make a production observation attributable to the chosen contract. They help explain why a familiar message was typed on one version and retained raw on another.

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/src/FrenchExDev.Net.BinaryWrapper.Contracts/OutputParserContracts.cs
  • BinaryWrapper/src/FrenchExDev.Net.BinaryWrapper/OutputParserStrategy.cs
  • BinaryWrapper/src/FrenchExDev.Net.BinaryWrapper/Code.cs
  • BinaryWrapper/test/FrenchExDev.Net.BinaryWrapper.Tests/OutputParserStrategyTests.cs
  • Vagrant/src/FrenchExDev.Net.Vagrant/VagrantOutputStrategies.cs

Previous · Series index · Next

⬇ Download