Skip to content

docs(cli): record and accept ADR-0067 on the exit codes as a published contract - #397

Merged
Reefact merged 1 commit into
mainfrom
claude/anyarray-distinct-nameof-8m5won
Jul 31, 2026
Merged

docs(cli): record and accept ADR-0067 on the exit codes as a published contract#397
Reefact merged 1 commit into
mainfrom
claude/anyarray-distinct-nameof-8m5won

Conversation

@Reefact

@Reefact Reefact commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Summary

Follows #396, which introduced ExitCodes and raised the question of whether the CLI's exit-code set is a compatibility promise. ADR-0067 records that it is. It was drafted Proposed; @Reefact took the decision and asked for the status flip to be made on their behalf, so it lands here Accepted.

Writing it turned up something worth more than the record itself: the set is already published, and it is not actually closed. The catalog-versioning reference carries an exit-code table per command and the CI guide's example pipelines branch on the code — so the promise exists whether or not it is written down. But command-line parsing is delegated to Spectre.Console.Cli, and an unknown subcommand exits -1 (255 to a POSIX shell) writing nothing to either stream. That fifth value appears in no table and was chosen by a dependency. The 2026-07-20 architecture and design audit had already flagged it.

Verified against the built binary rather than inferred:

$ dotnet fce.dll catalog frobnicate   → exit 255, stdout empty, stderr empty
$ dotnet fce.dll --help               → exit 0
$ dotnet fce.dll config show          → exit 0

So this ADR corrects a claim rather than restating one: #396's description called the set closed, and it is not.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Refactoring
  • Analyzer / diagnostic change
  • Tests
  • Documentation
  • Build / CI / tooling

Changes

  • Add ADR-0067 | Treat the CLI's exit codes as a closed, published contract, with its French twin, following the template and the banner convention.
  • Accept it on the maintainer's instruction: per ADR-0057 the Proposed: line is untouched and Accepted: is added below it. Both dates read 2026-07-31 because both states happened today — the log's way of saying a decision was ratified on sight, not that one date stands for two unknowns.
  • Index both in the ADR README, Accepted.

Two choices inside the record worth flagging:

  • Closing the -1 hole is a follow-up, not part of the decision. Which value an unparseable command line should return — reuse 1, or reserve a distinct code so a pipeline can tell a bad invocation from a failed run — is its own trade-off, and that is specification rather than decision.
  • The record states what it cannot do. Nothing checks the rule: a future command can return 3 and compile, and no test or analyzer will object. That is the arrangement ADR-0056 warns about, accepted here because the surface is small and reviewed — written into Risks rather than left for a reader to find out.

Numbering note: this was drafted as 0066, which a merge took in the meantime; renumbered to 0067 with both filenames, both banners and the index updated.

Testing

  • dotnet build FirstClassErrors.sln
  • dotnet test FirstClassErrors.sln
  • Analyzer tests pass (FirstClassErrors.Analyzers.UnitTests)

Documentation-only: no code changed, so no build or test was run for this branch. The CLI behaviour quoted above was observed on the binary built from main at 40505c7.

Documentation

  • Public API / error documentation updated
  • README / doc/ updated
  • French translation (doc/handwritten/for-users/README.fr.md) updated if user-facing behavior changed
  • No documentation change required

The pull request is the documentation change — an ADR pair plus its index entry. No user-facing behavior changed, so the user documentation is untouched; the exit-code tables it already carries are what the ADR records as the published contract.

Architecture decisions

  • No architectural decision in this pull request
  • New decision recorded — ADR drafted as Proposed: ADR-0067
  • Supersedes an existing ADR — successor proposed, status not flipped: ADR-____
  • ⚠️ Conflicts with an existing ADR — flagged for the maintainer: ADR-____

Drafted Proposed and flipped to Accepted only on @Reefact's explicit instruction in this session — the decision is theirs and the record names them as the decision maker. The merge stays with them.

Related issues

None. Follows #395 and #396.

@Reefact Reefact changed the title docs(cli): propose an ADR on the exit codes as a published contract docs(cli): record and accept ADR-0067 on the exit codes as a published contract Jul 31, 2026
Naming the exit codes was a habit; declaring the set closed is a decision,
because it binds every command added after it. ADR-0067 records that.
@Reefact took the decision and asked for the status flip to be made on
their behalf, so the record lands accepted; it names them as the decision
maker, which is what it named as a draft.

Writing it surfaced a fact worth more than the record. The set is already
published — the catalog-versioning reference carries an exit-code table
per command, and the CI guide's pipelines branch on the code — so the
promise exists whether or not it is written down. But the set is not
closed today: command-line parsing is delegated to Spectre.Console.Cli,
and an unknown subcommand exits -1 (255 to a POSIX shell) writing nothing
to either stream. That fifth value is in no table, was chosen by a
dependency, and the 2026-07-20 architecture audit had already flagged it.
Verified against the built binary, not inferred.

So the ADR corrects a claim rather than restating one: PR #396 described
the set as closed, and it is not. Closing it is listed as a follow-up
rather than decided here — which value an unparseable line should return
is its own trade-off, and that is specification.

The record also states what it cannot do: nothing checks the rule, a
future command can return 3 and compile. That is the arrangement ADR-0056
warns about, accepted here because the surface is small and reviewed, and
written into Risks rather than left for a reader to discover.

Per ADR-0057 the header carries both states: Proposed stays and Accepted
is added below it, never overwriting. Both dates read 2026-07-31 because
both happened today — the log's way of saying a decision was ratified on
sight rather than that one date stands for two unknowns.

Indexed in the ADR README, English and French twins.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SuSBsGG7wMPMnSeTHKZAom
@Reefact
Reefact force-pushed the claude/anyarray-distinct-nameof-8m5won branch from f2c7962 to 018397a Compare July 31, 2026 08:27
@Reefact
Reefact merged commit 748ccac into main Jul 31, 2026
29 checks passed
@Reefact
Reefact deleted the claude/anyarray-distinct-nameof-8m5won branch July 31, 2026 08:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants