Skip to content

Strict descriptions + endpoint-libs 2.0 lockstep - #27

Merged
pathscale merged 4 commits into
mainfrom
strict-descriptions
Jul 25, 2026
Merged

Strict descriptions + endpoint-libs 2.0 lockstep#27
pathscale merged 4 commits into
mainfrom
strict-descriptions

Conversation

@pathscale

@pathscale pathscale commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Note

Unblocked. pathscale/endpoint-libs#41 is merged and endpoint-libs 2.0.0-alpha.1 is published to crates.io. The temporary [patch.crates-io] override has been removed (bb41d73) and the dependency now resolves from the registry. Ready to merge.

Two independent pieces of work that happened to share a working tree, committed separately.

1. Strict descriptions (b856571)

Descriptions in the RON are not cosmetic. An endpoint description is the MCP tool description an agent reads to decide whether to call the tool; enum-variant descriptions are emitted into generated JSON schemas and doc comments. A blank one produces a tool an agent cannot use correctly and a doc page that says nothing — with no signal at generation time.

Generation now fails on missing or whitespace-only descriptions for endpoints (EndpointSchema and EndpointSchemaList) and enum variants (Enum and EnumList). Violations are collected across all files and reported together, so one run tells you everything to fix rather than one item per run. Each line names the file, service and item:

Error: Empty-description validation failed for 2 item(s). ...
./config/010_test.ron: service 'testApi' endpoint 'BadEndpoint': missing or empty description
./config/020_enums.ron: enum 'userRole' variant 'Guest': missing or empty description

--allow-empty-descriptions restores the previous behaviour for consumers not ready to annotate everything.

Also stops emitting an empty /// line for blank descriptions, which tripped clippy::empty_docs downstream. Fixed in both enum-emitting paths — EnumElement::to_rust_decl (RON enum definitions) and Type::to_rust_decl (shared Type::Enum) — which are separate near-identical code paths, hence a test for each.

Struct fields are deliberately not validated: Field.description is #[serde(skip)] upstream, so a RON file cannot express one.

2. Error-code validation (9c710a2)

ErrorCodeList was falling through a catch-all _ => {} arm. That looked like an oversight rather than a decision — the test asserting the exclusion was named ..._ignores_structs_and_error_codes but only exercised structs, and its comment justified only the struct case.

Error-code descriptions are load-bearing the same way: they become the doc comments on the generated EnumErrorCode variants and the third column of docs/error_codes/error_codes.md. A blank one yields an error a caller cannot interpret from either artifact.

  • Blank error-code descriptions are now reported, naming the code and its number.
  • The catch-all is replaced with an explicit Struct | StructList arm, so a future Definition variant becomes a compile error here rather than another silent gap — the same failure mode this commit fixes.
  • The misnamed test is split into ..._ignores_structs and a new ..._flags_blank_error_codes.
  • The top-level failure message now names all three validated kinds; it previously understated what runs.

3. endpoint-libs 2.0 lockstep (d9e67c3, bb41d73)

2.0 makes several schema-model types #[non_exhaustive] so OpenAPI/AsyncAPI emission can ship as a 2.1 minor rather than a 3.0. That forbids struct literals and exhaustive matches from outside that crate, breaking endpoint-gen in three places:

  • From<EndpointSchemaElement> for EndpointSchema was a field-by-field copy. Now a move of the inner schema — what it always meant, and it stops silently dropping fields added later (it would have dropped the new meta field).
  • The Type match in to_rust_ref gains a wildcard arm that panics with an actionable message. Panicking is right: emitting Rust for an unknown type would produce silently wrong generated code.
  • A test constructing EndpointErrorSchema by literal now uses the constructor added upstream for this purpose.

Verification

Not just unit tests — the real CLI was driven against scratch RON files:

  • strict mode fails with a blank endpoint description and a blank enum variant, both listed
  • strict mode fails with a blank error-code description, naming code and number
  • --allow-empty-descriptions generates successfully
  • generated model.rs has a doc comment on the documented variant, no doc line on the blank one, and zero bare /// lines
  • documented error-code text appears in both error_codes.md and the generated model
  • 11 tests green, clippy clean (-D warnings)

b856571 was verified to build and test green against published endpoint-libs 1.9.0 before the lockstep commit was applied, so the feature stands on its own. The final state is verified against the published 2.0.0-alpha.1 from crates.io, not a local path.

Note the exact-version pin is deliberate: 2.0.0-alpha.1 is a pre-release, and a plain "2.0" requirement would not match it.

🤖 Generated with Claude Code

meh and others added 3 commits July 25, 2026 18:00
Descriptions in the RON are not cosmetic: an endpoint description becomes the
MCP tool description an agent reads to decide whether to call the tool, and
enum-variant descriptions are emitted into the generated JSON schemas and doc
comments. A blank one produces a tool an agent cannot use correctly and a doc
page that says nothing, with no signal at generation time.

Generation now fails on missing or whitespace-only descriptions for endpoints
(both EndpointSchema and EndpointSchemaList) and enum variants (both Enum and
EnumList). Violations are collected across all files and reported together —
one run tells you everything to fix, rather than one item per run — and each
line names the file, the service and the item.

`--allow-empty-descriptions` restores the previous behaviour for consumers not
ready to annotate everything.

Also stops emitting an empty `///` line for blank descriptions, which tripped
clippy::empty_docs in downstream crates. This is fixed in both enum-emitting
paths — EnumElement::to_rust_decl for RON enum definitions and
Type::to_rust_decl for shared Type::Enum definitions — which are separate
near-identical code paths, hence a test for each.

Struct fields are deliberately not validated: Field.description is
#[serde(skip)] upstream, so a RON file cannot express one.

Verified end to end, not just by unit test: a RON with one blank endpoint
description and one blank enum variant fails with both violations listed;
--allow-empty-descriptions generates successfully; and the generated model.rs
contains a doc comment for the documented variant, no doc line for the blank
one, and zero bare `///` lines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
endpoint-libs 2.0 makes several schema-model types #[non_exhaustive] so that
OpenAPI/AsyncAPI emission can ship as a 2.1 minor rather than a 3.0. That
forbids struct literals and exhaustive matches from outside that crate, which
breaks endpoint-gen in exactly three places:

- `From<EndpointSchemaElement> for EndpointSchema` was a field-by-field copy.
  It is now a move of the inner schema — which is what it always meant, and
  which also stops it silently dropping fields added later (it would have
  dropped the new `meta` field).
- The `Type` match in `to_rust_ref` gains a wildcard arm that panics with an
  actionable message. Panicking is right: emitting Rust for a type this
  version does not understand would produce silently wrong generated code.
- A test constructing `EndpointErrorSchema` by literal now uses the
  constructor added upstream for this purpose.

The `[patch.crates-io]` override is TEMPORARY and must be removed once
endpoint-libs 2.0 is published — endpoint-libs 2.0.0-alpha.1 is not on
crates.io, so the pair cannot build together without it.

Verified: endpoint-gen builds and all 10 tests pass against the local
endpoint-libs 2.0.0-alpha.1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The previous commit validated endpoints and enum variants but skipped
ErrorCodeList through a catch-all `_ => {}` arm. That looks like an oversight
rather than a decision: the test asserting the exclusion was named
`..._ignores_structs_and_error_codes` but only exercised structs, and its
comment justified only the struct case.

Error-code descriptions are load-bearing in the same way. They become the doc
comments on the generated `EnumErrorCode` variants (via
EnumVariant::new_with_description in rust.rs) and the third column of
docs/error_codes/error_codes.md. A blank one yields an error a caller cannot
interpret from either artifact.

- `description_violations` now reports blank error-code descriptions, naming
  the code and its numeric value.
- The catch-all arm is replaced with an explicit `Struct | StructList` arm, so
  a future Definition variant is a compile error here rather than a silent
  gap — the same failure mode this commit is fixing.
- The misnamed test is split into `..._ignores_structs` (unchanged behaviour,
  honest name) and a new `..._flags_blank_error_codes`.
- The top-level failure message now names all three validated kinds, since it
  previously understated what runs.

Verified end to end: a RON with a blank error-code description fails with the
code and number named; documenting it generates successfully and the text
appears in both error_codes.md and the generated model.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drops the temporary [patch.crates-io] override. endpoint-libs 2.0.0-alpha.1
is now on crates.io, so the pair resolves normally — Cargo.lock records
`source = "registry+https://github.com/rust-lang/crates.io-index"` rather
than a local path.

Note the exact-version requirement is deliberate: 2.0.0-alpha.1 is a
pre-release, and a plain "2.0" requirement would not match it.

Verified against the published crate: builds, 11 tests green, clippy clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pathscale
pathscale marked this pull request as ready for review July 25, 2026 11:24
@pathscale
pathscale merged commit ae0f8ca into main Jul 25, 2026
3 of 4 checks passed
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.

1 participant