From 5abd7541f39d89ae1f2ef685ca43e8b8519d7204 Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 23 Dec 2025 12:06:17 -0500 Subject: [PATCH 1/3] Add missing newlines in api docstrings. At the moment, some docstrings in the generated docs lack newlines, and string together multiple sentences without punctuation. For example, the `provisioned` field in the `silo_utilization_list` method is a bit garbled: https://docs.oxide.computer/api/silo_utilization_list. > Accounts for resources allocated by in silos like CPU or memory for running > instances and storage for disks and snapshots Note that CPU and memory > resources associated with a stopped instances are not counted here This patch adds the missing newlines and punctuation to make this docstring readable. Note that these changes won't show up in the generated docs until we cut a new release. Note: if this change makes sense, I'll also ask Claude to review all docstrings in this crate for similar issues. --- nexus/types/src/external_api/views.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/nexus/types/src/external_api/views.rs b/nexus/types/src/external_api/views.rs index 7f91e98d286..c019ff33723 100644 --- a/nexus/types/src/external_api/views.rs +++ b/nexus/types/src/external_api/views.rs @@ -89,12 +89,13 @@ pub struct SiloQuotas { /// View of the current silo's resource utilization and capacity #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct Utilization { - /// Accounts for resources allocated to running instances or storage allocated via disks or snapshots + /// Accounts for resources allocated to running instances or storage allocated via disks or snapshots. + /// /// Note that CPU and memory resources associated with a stopped instances are not counted here - /// whereas associated disks will still be counted + /// whereas associated disks will still be counted. pub provisioned: VirtualResourceCounts, - /// The total amount of resources that can be provisioned in this silo - /// Actions that would exceed this limit will fail + /// The total amount of resources that can be provisioned in this silo. + /// Actions that would exceed this limit will fail. pub capacity: VirtualResourceCounts, } @@ -104,8 +105,9 @@ pub struct Utilization { pub struct SiloUtilization { pub silo_id: Uuid, pub silo_name: Name, - /// Accounts for resources allocated by in silos like CPU or memory for running instances and storage for disks and snapshots - /// Note that CPU and memory resources associated with a stopped instances are not counted here + /// Accounts for resources allocated by in silos like CPU or memory for running instances and storage for disks and snapshots. + /// + /// Note that CPU and memory resources associated with a stopped instances are not counted here. pub provisioned: VirtualResourceCounts, /// Accounts for the total amount of resources reserved for silos via their quotas pub allocated: VirtualResourceCounts, From b0ca8f5e0e3bdbb04b09b96676429d11d64ac1e8 Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 23 Dec 2025 12:55:34 -0500 Subject: [PATCH 2/3] More copy-edits from Claude. --- nexus/types/src/external_api/views.rs | 28 ++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/nexus/types/src/external_api/views.rs b/nexus/types/src/external_api/views.rs index c019ff33723..8f75fb12c4b 100644 --- a/nexus/types/src/external_api/views.rs +++ b/nexus/types/src/external_api/views.rs @@ -91,7 +91,7 @@ pub struct SiloQuotas { pub struct Utilization { /// Accounts for resources allocated to running instances or storage allocated via disks or snapshots. /// - /// Note that CPU and memory resources associated with a stopped instances are not counted here + /// Note that CPU and memory resources associated with stopped instances are not counted here, /// whereas associated disks will still be counted. pub provisioned: VirtualResourceCounts, /// The total amount of resources that can be provisioned in this silo. @@ -105,11 +105,12 @@ pub struct Utilization { pub struct SiloUtilization { pub silo_id: Uuid, pub silo_name: Name, - /// Accounts for resources allocated by in silos like CPU or memory for running instances and storage for disks and snapshots. + /// Accounts for the total resources allocated by the silo, including CPU and memory for + /// running instances and storage for disks and snapshots. /// - /// Note that CPU and memory resources associated with a stopped instances are not counted here. + /// Note that CPU and memory resources associated with stopped instances are not counted here. pub provisioned: VirtualResourceCounts, - /// Accounts for the total amount of resources reserved for silos via their quotas + /// Accounts for the total amount of resources reserved for silos via their quotas. pub allocated: VirtualResourceCounts, } @@ -253,10 +254,10 @@ pub struct Image { /// Hash of the image contents, if applicable pub digest: Option, - /// size of blocks in bytes + /// Size of blocks in bytes. pub block_size: ByteCount, - /// total size in bytes + /// Total size in bytes. pub size: ByteCount, } @@ -308,7 +309,7 @@ pub struct Vpc { } /// A VPC subnet represents a logical grouping for instances that allows network traffic between -/// them, within a IPv4 subnetwork or optionally an IPv6 subnetwork. +/// them, within an IPv4 subnetwork or optionally an IPv6 subnetwork. #[derive(ObjectIdentity, Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct VpcSubnet { /// common identifying metadata @@ -384,14 +385,14 @@ pub struct InternetGatewayIpAddress { /// The associated internet gateway. pub internet_gateway_id: Uuid, - /// The associated IP address, + /// The associated IP address. pub address: IpAddr, } // IP POOLS /// A collection of IP ranges. If a pool is linked to a silo, IP addresses from -/// the pool can be allocated within that silo +/// the pool can be allocated within that silo. #[derive(ObjectIdentity, Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct IpPool { #[serde(flatten)] @@ -578,7 +579,7 @@ pub struct MulticastGroupMember { // RACKS -/// View of an Rack +/// View of a Rack #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct Rack { #[serde(flatten)] @@ -1015,8 +1016,9 @@ pub struct SshKey { #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq)] pub struct DeviceAccessToken { /// A unique, immutable, system-controlled identifier for the token. + /// /// Note that this ID is not the bearer token itself, which starts with - /// "oxide-token-" + /// "oxide-token-". pub id: Uuid, pub time_created: DateTime, @@ -1301,8 +1303,8 @@ impl PartialEq for WebhookReceiver { pub struct WebhookReceiverConfig { /// The URL that webhook notification requests are sent to. pub endpoint: Url, - // A list containing the IDs of the secret keys used to sign payloads sent - // to this receiver. + /// A list containing the IDs of the secret keys used to sign payloads sent + /// to this receiver. pub secrets: Vec, } From 151c22ca8739ffa31e7e177863923ce85cfab812 Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 23 Dec 2025 13:34:40 -0500 Subject: [PATCH 3/3] More docs lint. --- nexus/types/src/external_api/params.rs | 41 +++++------ nexus/types/src/external_api/shared.rs | 5 +- nexus/types/src/external_api/views.rs | 12 ++-- prompts/openapi_lint.md | 97 ++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 28 deletions(-) create mode 100644 prompts/openapi_lint.md diff --git a/nexus/types/src/external_api/params.rs b/nexus/types/src/external_api/params.rs index 6ec29550d2f..6711ad2c590 100644 --- a/nexus/types/src/external_api/params.rs +++ b/nexus/types/src/external_api/params.rs @@ -207,7 +207,7 @@ pub struct SamlIdentityProviderSelector { // The shape of this selector is slightly different than the others given that // silos users can only be specified via ID and are automatically provided by -// the environment the user is authetnicated in +// the environment the user is authenticated in #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)] pub struct SshKeySelector { /// ID of the silo user @@ -542,9 +542,9 @@ pub struct SiloAuthSettingsUpdate { /// Create-time parameters for a `User` #[derive(Clone, Deserialize, JsonSchema)] pub struct UserCreate { - /// username used to log in + /// Username used to log in pub external_id: UserId, - /// how to set the user's login password + /// How to set the user's login password pub password: UserPassword, } @@ -641,11 +641,11 @@ pub struct UsernamePasswordCredentials { #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct DerEncodedKeyPair { - /// request signing public certificate (base64 encoded der file) + /// Request signing public certificate (base64 encoded DER file) #[serde(deserialize_with = "x509_cert_from_base64_encoded_der")] pub public_cert: String, - /// request signing RSA private key in PKCS#1 format (base64 encoded der file) + /// Request signing RSA private key in PKCS#1 format (base64 encoded DER file) #[serde(deserialize_with = "key_from_base64_encoded_der")] pub private_key: String, } @@ -765,25 +765,25 @@ pub struct SamlIdentityProviderCreate { #[serde(flatten)] pub identity: IdentityMetadataCreateParams, - /// the source of an identity provider metadata descriptor + /// The source of an identity provider metadata descriptor pub idp_metadata_source: IdpMetadataSource, - /// idp's entity id + /// IdP's entity ID pub idp_entity_id: String, - /// sp's client id + /// SP's client ID pub sp_client_id: String, - /// service provider endpoint where the response will be sent + /// Service provider endpoint where the response will be sent pub acs_url: String, - /// service provider endpoint where the idp should send log out requests + /// Service provider endpoint where the IdP should send log out requests pub slo_url: String, - /// customer's technical contact for saml configuration + /// Customer's technical contact for SAML configuration pub technical_contact_email: String, - /// request signing key pair + /// Request signing key pair #[serde(default)] #[serde(deserialize_with = "validate_key_pair")] pub signing_keypair: Option, @@ -992,7 +992,7 @@ pub struct InstanceNetworkInterfaceUpdate { /// Create-time parameters for a `Certificate` #[derive(Clone, Deserialize, Serialize, JsonSchema)] pub struct CertificateCreate { - /// common identifying metadata + /// Common identifying metadata #[serde(flatten)] pub identity: IdentityMetadataCreateParams, /// PEM-formatted string containing public certificate chain @@ -1328,7 +1328,7 @@ pub struct InstanceCreate { #[serde(default)] pub auto_restart_policy: Option, - /// Anti-Affinity groups which this instance should be added. + /// Anti-affinity groups to which this instance should be added. #[serde(default)] pub anti_affinity_groups: Vec, @@ -1694,7 +1694,7 @@ impl JsonSchema for BlockSize { schemars::schema::Schema::Object(schemars::schema::SchemaObject { metadata: Some(Box::new(schemars::schema::Metadata { id: None, - title: Some("disk block size in bytes".to_string()), + title: Some("Disk block size in bytes".to_string()), ..Default::default() })), instance_type: Some(schemars::schema::InstanceType::Integer.into()), @@ -1733,7 +1733,7 @@ impl From for PhysicalDiskKind { pub enum DiskSource { /// Create a blank disk Blank { - /// size of blocks for this Disk. valid values are: 512, 2048, or 4096 + /// Size of blocks for this disk. Valid values are: 512, 2048, or 4096. block_size: BlockSize, }, @@ -1817,7 +1817,7 @@ pub struct AddressLotCreate { pub blocks: Vec, } -/// Parameters for creating an address lot block. Fist and last addresses are +/// Parameters for creating an address lot block. First and last addresses are /// inclusive. #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct AddressLotBlockCreate { @@ -1849,6 +1849,7 @@ pub struct LoopbackAddressCreate { pub mask: u8, /// Address is an anycast address. + /// /// This allows the address to be assigned to multiple locations simultaneously. pub anycast: bool, } @@ -2198,7 +2199,7 @@ pub struct BgpAnnouncementCreate { pub network: IpNet, } -/// Parameters for creating a BGP configuration. This includes and autonomous +/// Parameters for creating a BGP configuration. This includes an autonomous /// system number (ASN) and a virtual routing and forwarding (VRF) identifier. #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct BgpConfigCreate { @@ -2378,7 +2379,7 @@ pub struct Distribution { /// Create-time parameters for an `Image` #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct ImageCreate { - /// common identifying metadata + /// Common identifying metadata #[serde(flatten)] pub identity: IdentityMetadataCreateParams, @@ -2397,7 +2398,7 @@ pub struct ImageCreate { /// Create-time parameters for a `Snapshot` #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct SnapshotCreate { - /// common identifying metadata + /// Common identifying metadata #[serde(flatten)] pub identity: IdentityMetadataCreateParams, diff --git a/nexus/types/src/external_api/shared.rs b/nexus/types/src/external_api/shared.rs index 6af5b2dc6e8..abc0d2ba589 100644 --- a/nexus/types/src/external_api/shared.rs +++ b/nexus/types/src/external_api/shared.rs @@ -422,8 +422,9 @@ pub struct BfdStatus { pub mode: BfdMode, } -/// Opaque object representing link state. The contents of this object are not -/// yet stable. +/// Opaque object representing link state. +/// +/// The contents of this object are not yet stable. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SwitchLinkState { link: serde_json::Value, diff --git a/nexus/types/src/external_api/views.rs b/nexus/types/src/external_api/views.rs index 8f75fb12c4b..4b9ed90164e 100644 --- a/nexus/types/src/external_api/views.rs +++ b/nexus/types/src/external_api/views.rs @@ -254,10 +254,10 @@ pub struct Image { /// Hash of the image contents, if applicable pub digest: Option, - /// Size of blocks in bytes. + /// Size of blocks in bytes pub block_size: ByteCount, - /// Total size in bytes. + /// Total size in bytes pub size: ByteCount, } @@ -294,10 +294,10 @@ pub struct Vpc { #[serde(flatten)] pub identity: IdentityMetadata, - /// id for the project containing this VPC + /// ID for the project containing this VPC pub project_id: Uuid, - /// id for the system router where subnet default routes are registered + /// ID for the system router where subnet default routes are registered pub system_router_id: Uuid, /// The unique local IPv6 address range for subnets in this VPC @@ -312,7 +312,7 @@ pub struct Vpc { /// them, within an IPv4 subnetwork or optionally an IPv6 subnetwork. #[derive(ObjectIdentity, Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct VpcSubnet { - /// common identifying metadata + /// Common identifying metadata #[serde(flatten)] pub identity: IdentityMetadata, @@ -340,7 +340,7 @@ pub enum VpcRouterKind { /// should be sent depending on its destination. #[derive(ObjectIdentity, Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct VpcRouter { - /// common identifying metadata + /// Common identifying metadata #[serde(flatten)] pub identity: IdentityMetadata, diff --git a/prompts/openapi_lint.md b/prompts/openapi_lint.md new file mode 100644 index 00000000000..a2f8dbc5eed --- /dev/null +++ b/prompts/openapi_lint.md @@ -0,0 +1,97 @@ +# API Docstring Style Guide + +This guide covers public docstrings (`///` or `/** ... */`) on structs, fields, +and enums in `nexus/types/src/external_api/`. These docstrings become user-facing +API documentation. + +## Capitalization + +- Start all docstrings with a capital letter. + +```rust +// Bad +/// the source of an identity provider metadata descriptor +pub idp_metadata_source: IdpMetadataSource, + +// Good +/// The source of an identity provider metadata descriptor. +pub idp_metadata_source: IdpMetadataSource, +``` + +## Punctuation + +- End all sentences with periods, including single-sentence docstrings. +- Non-sentence fragments (e.g., short labels) do not need to end with periods. + +```rust +// Bad +/// The IP address held by this resource +pub ip: IpAddr, + +// Good +/// The IP address held by this resource. +pub ip: IpAddr, + +// Also acceptable - fragment, not a sentence +/// Common identifying metadata +pub identity: IdentityMetadata, +``` + +## Paragraph Separation + +- Separate distinct thoughts or notes with a blank `///` line. This produces +proper paragraph breaks in rendered documentation. + +```rust +// Bad - renders as one run-on paragraph +/// A unique, immutable, system-controlled identifier for the token. +/// Note that this ID is not the bearer token itself, which starts with +/// "oxide-token-". +pub id: Uuid, + +// Good - renders as two paragraphs +/// A unique, immutable, system-controlled identifier for the token. +/// +/// Note that this ID is not the bearer token itself, which starts with +/// "oxide-token-". +pub id: Uuid, +``` + +## Acronyms and Abbreviations + +- Use standard casing for acronyms: + - "IdP" (Identity Provider) + - "SP" (Service Provider) + - "SAML", "ID", "IP", "VPC", "CPU", "RAM" + +## Doc Comments vs Regular Comments + +- Use `///` for public API documentation that users will see. +- Use `//` for internal implementation notes that should not appear in generated +docs. + +```rust +// Bad - this won't appear in API docs +// A list containing the IDs of the secret keys. +pub secrets: Vec, + +// Good - this will appear in API docs +/// A list containing the IDs of the secret keys. +pub secrets: Vec, +``` + +## Scope + +These rules apply to: +- Public struct docstrings (`/// View of a Silo`) +- Public field docstrings (`/// The IP address held by this resource.`) +- Public enum variant docstrings (`/// The sled is currently active.`) + +These rules do NOT apply to: +- Private functions or structs +- Internal comments (`//`) +- Module-level documentation (`//!`) + +## General + +- Follow standard English grammatical rules.