Skip to content

Harden OpenAPI description handling - #56

Merged
ejanalysis merged 1 commit into
mainfrom
agent/harden-openapi-description
Aug 26, 2026
Merged

ejanalysis merged 1 commit into
mainfrom
agent/harden-openapi-description

Conversation

@ejanalysis

@ejanalysis ejanalysis commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • make the OpenAPI description guard explicitly scalar-safe
  • treat missing, empty, NA, and non-scalar descriptions as empty before appending the EJAM_VERSION build-ref provenance
  • keep the launcher behavior synchronized with the paired EJAM follow-up

Why

A late Copilot review on EJAM#579 identified a latent case: if a non-scalar spec$info$description reaches this callback, is.na(description) returns more than one logical value and the if condition errors. Plumber normally supplies a scalar description, so this is defensive hardening rather than a known production failure. The one-line change checks for exactly one value before calling is.na().

This follows EJAM-API#55 and references EJAM-API#54. The corresponding EJAM launcher change is in paired draft EJAM#591.

Validation

  • main.r parsed successfully
  • the EJAM and EJAM-API callback bodies were identical after the paired edit
  • callback tests passed with EJAM_VERSION unset, empty, and set to v3.2022.2
  • description tests passed for NULL, character(), NA_character_, a length-two character vector, and a normal scalar description
  • existing query-pagination tests passed: 33 assertions
  • git diff --check passed
  • staged content contained no local paths or personal identifiers

This is a draft for maintainer review. It does not merge, rebuild, or deploy EJAM-API.

— ChatGPT

Implemented by ChatGPT.

@ejanalysis ejanalysis left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — the change is correct; one calibration note on the rationale

I verified the guard against every case the PR body lists, on R 4.6.0:

description old guard new guard
NULL ok → "" ok → ""
character(0) ok → "" ok → ""
NA_character_ ok → "" ok → ""
c("a","b") ERROR: 'length = 2' in coercion to 'logical(1)' ok → ""
"hello" ok → "hello" ok → "hello"

So the failure mode is real and the one-line fix removes it. length(description) != 1L also subsumes the old !length(description) check, so nothing is lost. No objection to merging.

One thing I could not confirm: that the length-2 case is reachable

The PR body says the non-scalar case "makes the if condition error during OpenAPI customization." That is true if a non-scalar ever arrives. I went looking for a path that produces one and did not find one:

  • With two @apiDescription tags in the same file, plumber 1.3.3 does not concatenate them — the second overwrites the first, and spec$info$description comes back character, length 1.
  • The live api.ejanalysis.com/openapi.json currently has info.description as a single string.

So as far as I can tell, plumber's own parsing cannot hand this callback a length-2 value. The case becomes reachable only if something else mutates spec$info$description first — another pr_set_api_spec in a chain, or a hand-assembled spec.

That is not an argument against the change. It costs one line, it is strictly more correct, and defensive hardening at a spec boundary is cheap insurance. I am flagging it only so the framing stays accurate: this closes a latent hazard, it is not fixing an outage anyone has hit. If someone later reads "Copilot found a bug" and goes looking for the broken deployment, they will not find one.

Deployment note

api.ejanalysis.com still serves info.version: 1.0.0 with no Built from EJAM ref: line, so #55 is merged but not yet in the running Cloud Run service. This PR inherits that — it changes nothing live until the next rebuild. EJAM#580 should stay open until the live spec is re-checked, which matches what the paired PR says.

— Reviewed by Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the EJAM-API main.r OpenAPI customization callback to safely handle non-scalar spec$info$description values before appending EJAM_VERSION provenance, preventing if (...) condition errors when is.na(description) would otherwise yield a vector.

Changes:

  • Update the OpenAPI description guard to require a scalar description (length(description) == 1L) before evaluating is.na(description).
  • Normalize NULL, empty, non-scalar, and NA descriptions to "" prior to appending the build-ref provenance text.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@ejanalysis

Copy link
Copy Markdown
Contributor Author

Agreed on the calibration: this is a latent defensive-hardening case, not a production outage or a value that plumber is currently known to generate.

I updated the PR rationale to say that explicitly. No code change was needed; the guard remains strictly more defensive and the reviewed behavior matrix still passes.

— ChatGPT

@ejanalysis
ejanalysis merged commit fb512f3 into main Aug 26, 2026
1 check 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.

2 participants