From 350f837475a8c4591a7f6ebf43eb8fa2b8e02c78 Mon Sep 17 00:00:00 2001 From: michael-danko-passport Date: Tue, 21 Oct 2025 12:38:00 -0400 Subject: [PATCH 01/26] Update README.md Updates last updated timestamps for initial creation of draft-v1.1 branch. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2ef30b3..cf323c0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # CDS OpenAPI -**Status: Approved for v1.0** +**Status: Draft In Progress for v1.1** -_Last updated on Feb 9 2024_ +_Last updated on October 21st, 2024_ --- ## Overview @@ -16,7 +16,7 @@ Interactive OpenAPI documentation for the CDS APIs is visible on Stoplight's [OM The CDS schema versions are documented as different branches in the underlying GitHub project repo. The branch naming will only include the major and minor semantic versions. ### Public Releases -Any work on future releases of CDS will be completed in a development branch and will be identified by the `-dev` suffix in the repo name. For example, as CDS version 1.0 is available, users may view this version of the OpenAPI schema by choosing the `v1.0` branch. +Any work on future releases of CDS will be completed in a development branch and will be identified by the `dev-` prefix in the repo name. For example, as CDS version 1.0 is available, users may view this version of the OpenAPI schema by choosing the `v1.0` branch. ### Upcoming Releases -After the ratification and approval of a new version, such as CDS v1.1, a `v1.1-dev` branch will be created to demonstrate it is a work in progress. Once complete, the `v1.1-dev` branch will be merged into a `v1.1` branch and the latest version of the spec will exist within the `main` branch of the repo. +After the ratification and approval of a new version, such as CDS v1.1, a `draft-v1.1` branch will be created to demonstrate it is a work in progress. Once complete, the `draft-v1.1` branch will be merged into a `v1.1` branch and the latest version of the spec will exist within the `main` branch of the repo. From e98239892b5d0347a4867605fb694767fef26d04 Mon Sep 17 00:00:00 2001 From: mdanko Date: Tue, 18 Nov 2025 15:17:15 -0500 Subject: [PATCH 02/26] Removes old file that isn't in use --- original_CDS APIs.yaml | 1898 ---------------------------------------- 1 file changed, 1898 deletions(-) delete mode 100644 original_CDS APIs.yaml diff --git a/original_CDS APIs.yaml b/original_CDS APIs.yaml deleted file mode 100644 index 5f8c5d4..0000000 --- a/original_CDS APIs.yaml +++ /dev/null @@ -1,1898 +0,0 @@ -openapi: 3.1.0 -x-stoplight: - id: 4wvvx7c8j5ymv -tags: - - name: Curbs API - description: The Curbs API is a REST API allowing cities to specify areas of interest along the curb along with the rules for using them - - name: Events API - description: 'The Events API is a REST API allowing real-time and historic events at the curb to be sent to cities, and the ability to check on the status of any sensors.' - - name: Metrics API - description: The Metrics API is a REST API allowing historic metrics calculations based on Event activity that happened at defined Curb places -info: - title: Curb Data Specification - version: 1.0.0 - summary: 'CDS is a data specification to help cities manage their curb zone programs and surrounding areas, and measure the utilization and impact.' - description: 'The Curb Data Specification (CDS), a project of the Open Mobility Foundation (OMF), is a data standard and set of Application Programming Interfaces (APIs) that helps cities manage and companies use dynamic curb zones that optimize loading activities of people and goods, and measure the impact of these programs.' - contact: - url: 'https://github.com/openmobilityfoundation/curb-data-specification/' - name: Open Mobility Foundation - email: info@openmobilityfoundation.org - license: - name: Creative Commons Attribution 4.0 International Public License - url: 'http://www.apache.org/licenses/LICENSE-2.0.htmlhttps://github.com/openmobilityfoundation/curb-data-specification/blob/0706316a7bfb0d6b5104c75608d8f66cb8101fe7/LICENCE' -servers: [] -paths: - /curbs/zones: - get: - summary: Get Curb Zones - responses: - '200': - description: Zones Found - content: - application/json: - schema: - type: object - properties: - version: - $ref: '#/components/schemas/rest-response-version' - time_zone: - $ref: '#/components/schemas/rest-response-time_zone' - last_updated: - $ref: '#/components/schemas/rest-response-last_updated' - currency: - $ref: '#/components/schemas/rest-response-currency' - author: - $ref: '#/components/schemas/rest-response-author' - license_url: - $ref: '#/components/schemas/rest-response-license_url' - data: - type: array - items: - type: object - properties: - zones: - type: array - items: - $ref: '#/components/schemas/curb_zone' - '400': - description: BAD_REQUEST - '401': - description: UNAUTHORIZED - '403': - description: FORBIDDEN - '500': - description: SERVER_ERROR - '503': - description: SERVICE_UNAVAILABLE - operationId: get-curbs-zones - description: 'This required endpoint must be implemented by every Curbs API server. If attaching policies to curb zones, the Query Curb Policies endpoint is also required.' - parameters: - - schema: - $ref: '#/components/schemas/uuid' - in: query - name: area - description: 'The ID of a Curb Area. If specified, only return Curb Zones contained within this area.' - - $ref: '#/components/parameters/min_lat' - - $ref: '#/components/parameters/min_lng' - - $ref: '#/components/parameters/max_lat' - - $ref: '#/components/parameters/max_lng' - - $ref: '#/components/parameters/lat' - - $ref: '#/components/parameters/lng' - - $ref: '#/components/parameters/radius' - tags: - - Curbs API - x-stoplight: - id: rf9d12orekm1h - /curbs/areas: - get: - summary: Get Curb Areas - responses: - '200': - description: Areas Found - content: - application/json: - schema: - type: object - properties: - version: - $ref: '#/components/schemas/rest-response-version' - time_zone: - $ref: '#/components/schemas/rest-response-time_zone' - last_updated: - $ref: '#/components/schemas/rest-response-last_updated' - currency: - $ref: '#/components/schemas/rest-response-currency' - author: - $ref: '#/components/schemas/rest-response-author' - license_url: - $ref: '#/components/schemas/rest-response-license_url' - data: - type: array - items: - type: object - properties: - areas: - type: array - items: - $ref: '#/components/schemas/curb_area' - '400': - description: BAD_REQUEST - '401': - description: UNAUTHORIZED - '403': - description: FORBIDDEN - '500': - description: SERVER_ERROR - '501': - description: NOT_IMPLEMENTED - '503': - description: SERVICE_UNAVAILABLE - operationId: get-curbs-areas - description: 'Optional endpoint. If not implemented, the server should reply with 501 Not Implemented.' - parameters: - - $ref: '#/components/parameters/min_lat' - - $ref: '#/components/parameters/min_lng' - - $ref: '#/components/parameters/max_lat' - - $ref: '#/components/parameters/max_lng' - - $ref: '#/components/parameters/lat' - - $ref: '#/components/parameters/lng' - - $ref: '#/components/parameters/radius' - tags: - - Curbs API - security: [] - x-stoplight: - id: 2rmkdtzdroapa - /curbs/spaces: - get: - summary: Get Curb Spaces - responses: - '200': - description: Spaces Found - content: - application/json: - schema: - type: object - properties: - version: - $ref: '#/components/schemas/rest-response-version' - time_zone: - $ref: '#/components/schemas/rest-response-time_zone' - last_updated: - $ref: '#/components/schemas/rest-response-last_updated' - currency: - $ref: '#/components/schemas/rest-response-currency' - author: - $ref: '#/components/schemas/rest-response-author' - license_url: - $ref: '#/components/schemas/rest-response-license_url' - data: - type: array - items: - type: object - properties: - spaces: - type: array - items: - $ref: '#/components/schemas/curb_space' - '400': - description: BAD_REQUEST - '401': - description: UNAUTHORIZED - '403': - description: FORBIDDEN - '500': - description: SERVER_ERROR - '501': - description: NOT_IMPLEMENTED - '503': - description: SERVICE_UNAVAILABLE - operationId: get-curbs-spaces - description: 'Optional endpoint. If not implemented, the server should reply with 501 Not Implemented.' - parameters: - - $ref: '#/components/parameters/min_lat' - - $ref: '#/components/parameters/min_lng' - - $ref: '#/components/parameters/max_lat' - - $ref: '#/components/parameters/max_lng' - - $ref: '#/components/parameters/lat' - - $ref: '#/components/parameters/lng' - - $ref: '#/components/parameters/radius' - tags: - - Curbs API - security: [] - x-stoplight: - id: uw6x7xz5yqmmo - /curbs/policies: - get: - summary: Get Curb Policies - responses: - '200': - description: Policies Found - content: - application/json: - schema: - type: object - properties: - version: - $ref: '#/components/schemas/rest-response-version' - time_zone: - $ref: '#/components/schemas/rest-response-time_zone' - last_updated: - $ref: '#/components/schemas/rest-response-last_updated' - currency: - $ref: '#/components/schemas/rest-response-currency' - author: - $ref: '#/components/schemas/rest-response-author' - license_url: - $ref: '#/components/schemas/rest-response-license_url' - data: - type: array - items: - type: object - properties: - policies: - type: array - items: - $ref: '#/components/schemas/curb_policy' - '400': - description: BAD_REQUEST - '401': - description: UNAUTHORIZED - '403': - description: FORBIDDEN - '500': - description: SERVER_ERROR - '501': - description: NOT_IMPLEMENTED - '503': - description: SERVICE_UNAVAILABLE - operationId: get-curbs-policies - description: 'Optional endpoint, but required if Curb Zones contain policy_id references. If not implemented, the server should reply with 501 Not Implemented.' - parameters: - - schema: - type: string - in: query - name: ids - description: Comma-separated list of Policy IDs. - tags: - - Curbs API - security: [] - x-stoplight: - id: 8e7iad2rt96ow - '/curbs/zones/{id}': - get: - summary: Get a single Curb Zone - responses: - '200': - description: Zone Found - content: - application/json: - schema: - type: object - properties: - version: - $ref: '#/components/schemas/rest-response-version' - time_zone: - $ref: '#/components/schemas/rest-response-time_zone' - last_updated: - $ref: '#/components/schemas/rest-response-last_updated' - currency: - $ref: '#/components/schemas/rest-response-currency' - author: - $ref: '#/components/schemas/rest-response-author' - license_url: - $ref: '#/components/schemas/rest-response-license_url' - data: - type: array - items: - type: object - properties: - zone: - $ref: '#/components/schemas/curb_zone' - '400': - description: BAD_REQUEST - '401': - description: UNAUTHORIZED - '403': - description: FORBIDDEN - '404': - description: NOT_FOUND - '500': - description: SERVER_ERROR - '501': - description: NOT_IMPLEMENTED - '503': - description: SERVICE_UNAVAILABLE - operationId: get-curbs-zone - description: 'Fetch a single Curb Zone by ID. Optional endpoint. If not implemented, the server should reply with 501 Not Implemented.' - parameters: - - schema: - type: string - name: id - in: path - required: true - description: Curb Zone ID - - schema: - $ref: '#/components/schemas/timestamp' - in: query - name: time - description: 'The Curb Zone object will only be returned if its validity period includes this time; otherwise, the server should reply with 404 Not Found.' - - schema: - type: boolean - in: query - name: show_historic - description: 'Whether to return historic, retired curb zone data.' - tags: - - Curbs API - security: [] - x-stoplight: - id: yuag4b4aqbkf1 - '/curbs/areas/{id}': - get: - summary: Get a single Curb Area - responses: - '200': - description: Area Found - content: - application/json: - schema: - type: object - properties: - version: - $ref: '#/components/schemas/rest-response-version' - time_zone: - $ref: '#/components/schemas/rest-response-time_zone' - last_updated: - $ref: '#/components/schemas/rest-response-last_updated' - currency: - $ref: '#/components/schemas/rest-response-currency' - author: - $ref: '#/components/schemas/rest-response-author' - license_url: - $ref: '#/components/schemas/rest-response-license_url' - data: - type: array - items: - type: object - properties: - area: - $ref: '#/components/schemas/curb_area' - '400': - description: BAD_REQUEST - '401': - description: UNAUTHORIZED - '403': - description: FORBIDDEN - '404': - description: NOT_FOUND - '500': - description: SERVER_ERROR - '501': - description: NOT_IMPLEMENTED - '503': - description: SERVICE_UNAVAILABLE - operationId: get-curbs-area - description: 'Fetch a single Curb Area by ID. Optional endpoint. If not implemented, the server should reply with 501 Not Implemented.' - parameters: - - schema: - type: string - name: id - in: path - required: true - description: Curb Area ID - tags: - - Curbs API - security: [] - x-stoplight: - id: ad85cej1q50dp - '/curbs/spaces/{id}': - get: - summary: Get a single Curb Space - responses: - '200': - description: Space Found - content: - application/json: - schema: - type: object - properties: - version: - $ref: '#/components/schemas/rest-response-version' - time_zone: - $ref: '#/components/schemas/rest-response-time_zone' - last_updated: - $ref: '#/components/schemas/rest-response-last_updated' - currency: - $ref: '#/components/schemas/rest-response-currency' - author: - $ref: '#/components/schemas/rest-response-author' - license_url: - $ref: '#/components/schemas/rest-response-license_url' - data: - type: array - items: - type: object - properties: - space: - $ref: '#/components/schemas/curb_space' - '400': - description: BAD_REQUEST - '401': - description: UNAUTHORIZED - '403': - description: FORBIDDEN - '404': - description: NOT_FOUND - '500': - description: SERVER_ERROR - '501': - description: NOT_IMPLEMENTED - '503': - description: SERVICE_UNAVAILABLE - operationId: get-curbs-space - description: 'Fetch a single Curb Space by ID. Optional endpoint. If not implemented, the server should reply with 501 Not Implemented.' - parameters: - - schema: - type: string - name: id - in: path - required: true - description: Curb Space ID - - schema: - $ref: '#/components/schemas/timestamp' - in: query - name: time - description: Availability data (if supplied) will be returned as of this time. - tags: - - Curbs API - security: [] - x-stoplight: - id: u19hpktf0n1hl - '/curbs/policies/{id}': - get: - summary: Get a single Curb Policy - responses: - '200': - description: Policy Found - content: - application/json: - schema: - type: object - properties: - version: - $ref: '#/components/schemas/rest-response-version' - time_zone: - $ref: '#/components/schemas/rest-response-time_zone' - last_updated: - $ref: '#/components/schemas/rest-response-last_updated' - currency: - $ref: '#/components/schemas/rest-response-currency' - author: - $ref: '#/components/schemas/rest-response-author' - license_url: - $ref: '#/components/schemas/rest-response-license_url' - data: - type: array - items: - type: object - properties: - policy: - $ref: '#/components/schemas/curb_policy' - '400': - description: BAD_REQUEST - '401': - description: UNAUTHORIZED - '403': - description: FORBIDDEN - '404': - description: NOT_FOUND - '500': - description: SERVER_ERROR - '503': - description: SERVICE_UNAVAILABLE - operationId: get-curbs-policy - description: Fetch a single Curb Policy by ID. - parameters: - - schema: - type: string - name: id - in: path - required: true - description: Curb Policy ID - tags: - - Curbs API - security: [] - x-stoplight: - id: f891w4kmcowo1 - /events/events: - get: - summary: Get Events - tags: - - Events API - responses: - '200': - description: 'OK: operation successful.' - content: - application/json: - schema: - type: object - properties: - time_zone: - $ref: '#/components/schemas/rest-response-time_zone' - version: - $ref: '#/components/schemas/rest-response-version' - last_updated: - $ref: '#/components/schemas/rest-response-last_updated' - currency: - $ref: '#/components/schemas/rest-response-currency' - author: - $ref: '#/components/schemas/rest-response-author' - license_url: - $ref: '#/components/schemas/rest-response-license_url' - data: - type: array - items: - $ref: '#/components/schemas/curb-event' - operationId: get-events-events - parameters: - - $ref: '#/components/parameters/curb_area_id' - - $ref: '#/components/parameters/curb_zone_id' - - $ref: '#/components/parameters/curb_space_id' - description: '' - x-stoplight: - id: b5ex02kdfrljo - /events/status: - get: - summary: Get Status - tags: - - Events API - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - version: - $ref: '#/components/schemas/rest-response-version' - time_zone: - $ref: '#/components/schemas/rest-response-time_zone' - last_updated: - $ref: '#/components/schemas/rest-response-last_updated' - currency: - $ref: '#/components/schemas/rest-response-currency' - author: - $ref: '#/components/schemas/rest-response-author' - license_url: - $ref: '#/components/schemas/rest-response-license_url' - data: - type: object - properties: - status: - type: array - items: - type: object - properties: - data_source_device_id: - $ref: '#/components/schemas/uuid' - description: 'Unique identifier of this event source, whether sensor, vehicle, camera, etc.' - data_source_type: - $ref: '#/components/schemas/data_source_type' - description: General category of the source creating the event. - data_source_operator_id: - $ref: '#/components/schemas/operator_id' - sensor_status_is_commissioned: - type: boolean - description: 'If a sensor was used to capture this event, the commissioned status at the time that the event was reported. Indicates whether the sensor is currently in a state where it should be reporting data.' - sensor_status_is_online: - type: boolean - description: 'If a sensor was used to capture this event, the online status at the time that the event was reported. Indicates whether the sensor is currently online and reporting data.' - required: - - data_source_device_id - - data_source_type - '501': - $ref: '#/components/responses/501' - operationId: get-events-status - parameters: - - $ref: '#/components/parameters/curb_area_id' - - $ref: '#/components/parameters/curb_zone_id' - - $ref: '#/components/parameters/curb_space_id' - x-stoplight: - id: p1of272g09cjr - /metrics/sessions: - get: - summary: Get Sessions - tags: - - Metrics API - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - version: - $ref: '#/components/schemas/rest-response-version' - time_zone: - $ref: '#/components/schemas/rest-response-time_zone' - last_updated: - $ref: '#/components/schemas/rest-response-last_updated' - currency: - $ref: '#/components/schemas/rest-response-currency' - author: - $ref: '#/components/schemas/rest-response-author' - license_url: - $ref: '#/components/schemas/rest-response-license_url' - data: - type: array - items: - type: object - properties: - session_type: - type: string - enum: - - parking - - area - description: The type of session that happened for this event. - event_session_id: - $ref: '#/components/schemas/uuid' - description: 'If known and recorded to tie two Events together, then include the `event_session_id` from the [Curb Event](./docs/cds-openapi/6754f1a684eb4)' - event_id_start: - $ref: '#/components/schemas/uuid' - description: The globally unique identifier of the start/enter event that occurred. - event_id_end: - $ref: '#/components/schemas/uuid' - description: The globally unique identifier of the end/exit event that occurred. - event_location_start_latitude: - $ref: '#/components/schemas/latitude' - description: The geographic latitude point location where the start/enter of the event occurred. - event_location_start_longitude: - $ref: '#/components/schemas/longitude' - description: The geographic longitude point location where the start/enter of the event occurred. - event_location_end_latitude: - $ref: '#/components/schemas/latitude' - description: The geographic latitude point location where the end/exit of the event occurred. - event_location_end_longitude: - $ref: '#/components/schemas/longitude' - description: The geographic longitude point location where the end/exit of the event occurred. - event_time_start: - $ref: '#/components/schemas/timestamp' - description: Timestamp (date/time) at which the event started with the `park_start` or `enter_area` event types. - event_time_end: - $ref: '#/components/schemas/timestamp' - description: Timestamp (date/time) at which the event occurred. - curb_area_ids: - type: array - description: 'Unique IDs of the Curb Area where the event occurred. Since Curb Areas can overlap, an event may happen in more than one. Required for events that occurred in a known Curb Area for these event_types: `enter_area`, `exit_area`, `park_start`, `park_end`.' - items: - $ref: '#/components/schemas/uuid' - curb_zone_id: - $ref: '#/components/schemas/uuid' - description: Unique ID of the Curb Zone where the event occurred. Required for events that occurred at a known Curb Zone for **ALL** event types. - curb_space_id: - $ref: '#/components/schemas/uuid' - description: 'Unique ID of the Curb Space where the event occurred. Required for events that occurred at a known Curb Space for these event_types: `park_start`, `park_end`, `enter_area`, `exit_area`.' - vehicle_length: - type: number - description: 'Approximate length of the vehicle that performed the event, in centimeters. Required for sources capable of determining vehicle length.' - vehicle_type: - $ref: '#/components/schemas/vehicle_type' - required: - - session_type - '501': - $ref: '#/components/responses/501' - operationId: get-metrics-sessions - parameters: - - $ref: '#/components/parameters/curb_place_type' - - $ref: '#/components/parameters/curb_place_id' - - $ref: '#/components/parameters/min_lat' - - $ref: '#/components/parameters/min_lng' - - $ref: '#/components/parameters/max_lat' - - $ref: '#/components/parameters/max_lng' - - $ref: '#/components/parameters/lat' - - $ref: '#/components/parameters/lng' - - $ref: '#/components/parameters/radius' - - $ref: '#/components/parameters/start_time' - - $ref: '#/components/parameters/end_time' - description: |- - ## Required Query Parameters - The following sets of query parameters are required if `ANY` parameter in the set are provided. - * `curb_place_type`, `curb_place_id` - * `min_lat`, `min_lng`, `max_lat`, `max_lng` - * `lat`, `lng`, `radius` - x-stoplight: - id: owuyr2yiwj38o - /metrics/aggregates: - get: - summary: Get Aggregates - tags: - - Metrics API - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - version: - $ref: '#/components/schemas/rest-response-version' - time_zone: - $ref: '#/components/schemas/rest-response-time_zone' - last_updated: - $ref: '#/components/schemas/rest-response-last_updated' - currency: - $ref: '#/components/schemas/rest-response-currency' - author: - $ref: '#/components/schemas/rest-response-author' - license_url: - $ref: '#/components/schemas/rest-response-license_url' - data: - type: array - items: - type: object - properties: - curb_place_type: - type: string - description: The type of curb place this aggregate applies to from the Curbs API. - enum: - - area - - zone - - space - curb_place_id: - $ref: '#/components/schemas/uuid' - description: The ID of this curb place. - metric_type: - type: string - enum: - - total_sessions - - turnover - - average_dwell_time - - occupancy_percent - description: 'The metric this aggregate applies to. These calaculations are defined by the OMF in detail [here](https://github.com/openmobilityfoundation/curb-data-specification/tree/main/metrics#methodology).' - date: - type: string - description: 'The date the event occured in ISO 8601 format, local timezone, in "YYYY-MM-DD" format.' - example: '2021-04-01' - format: date - hour: - type: integer - description: 'The hour of the day the event occured in ISO 8601 format, local timezone, in "hh" format.' - example: '23' - value: - type: number - description: The results of the calculations for this metric. Note that "-1" means that the sensor/source was offline for the majority of the time. - example: '"2.9" "0.05"' - required: - - curb_place_type - - curb_place_id - - metric_type - - date - - hour - - value - required: - - version - - time_zone - - last_updated - - currency - - author - - license_url - - data - '501': - $ref: '#/components/responses/501' - operationId: get-metrics-aggregates - parameters: - - $ref: '#/components/parameters/curb_place_type' - - $ref: '#/components/parameters/curb_place_id' - - $ref: '#/components/parameters/min_lat' - - $ref: '#/components/parameters/min_lng' - - $ref: '#/components/parameters/max_lat' - - $ref: '#/components/parameters/max_lng' - - $ref: '#/components/parameters/lat' - - $ref: '#/components/parameters/lng' - - $ref: '#/components/parameters/radius' - - $ref: '#/components/parameters/start_time' - - $ref: '#/components/parameters/end_time' - description: |- - ## Required Query Parameters - The following sets of query parameters are required if `ANY` parameter in the set are provided. - * `curb_place_type`, `curb_place_id` - * `min_lat`, `min_lng`, `max_lat`, `max_lng` - * `lat`, `lng`, `radius` - x-stoplight: - id: vlnl7l63gqy1n -components: - schemas: - timestamp: - description: Timestamp in milliseconds since Epoch. - type: integer - minimum: 100000000000 - maximum: 99999999999999 - x-internal: false - title: Timestamp - x-stoplight: - id: 9h333ijy5gy1e - uuid: - type: string - format: uuid - x-internal: false - title: UUID - x-stoplight: - id: yan587cvuqppu - description: A UUID string value - latitude: - type: number - minimum: -90 - maximum: 90 - x-internal: false - title: Latitude - description: '' - x-stoplight: - id: w5n8fow8pavzx - longitude: - type: number - minimum: -180 - maximum: 180 - x-internal: false - title: Longitude - x-stoplight: - id: 1mf0ywpexjhtq - data_source_type: - title: Data Source Type - x-stoplight: - id: 807f64500e804 - type: string - enum: - - data_feed - - camera - - above_ground - - in_ground - - meter - - payment - - in_person - - other - description: The set of possible categories of sources that can publish a Curb Event. - vehicle_type: - title: Vehicle Type - x-stoplight: - id: zc8pejs5x7n8g - type: string - enum: - - bicyle - - cargo_bicycle - - car - - scooter - - moped - - motorcycle - - truck - - van - - freight - - other - - unspecified - description: |- - Type of the vehicle that performed the event. - - Required for sources capable of determining vehicle type. - operator_id: - $ref: '#/components/schemas/uuid' - x-stoplight: - id: jts5h9b4s9c8v - description: |- - Unique identifier of the entity responsible for operating the event data source. - - For example, IDs can identify a fleet operator sending a data feed, or the organization (company or city) operating the sensor. - - **Values are defined globally and are managed by the Open Mobility Foundation and operators may register [here](https://github.com/openmobilityfoundation/curb-data-specification/wiki/Adding-a-CDS-Data-Source-Operator-ID)** - title: Operator ID - examples: - - 55b617b9-730e-485e-83de-5e1a35fdc352 - curb_zone: - type: object - properties: - curb_zone_id: - allOf: - - $ref: '#/components/schemas/uuid' - - description: The ID of this Curb Zone. - geometry: - type: object - properties: - type: - type: string - coordinates: - type: array - items: - type: number - required: - - type - - coordinates - description: The spatial extent of this curb zone. - curb_policy_ids: - type: array - items: - $ref: '#/components/schemas/uuid' - description: 'An array of IDs of Policy objects. Together, these define the regulations of this Curb Zone.' - prev_policies: - type: array - items: - type: object - properties: - curb_policy_ids: - type: array - items: - $ref: '#/components/schemas/uuid' - description: 'An array of IDs of Policy objects. Together, these define the previous regulations of this Curb Zone.' - start_date: - allOf: - - $ref: '#/components/schemas/timestamp' - - description: The date/time that this policy started being active for this curb location. - end_date: - allOf: - - $ref: '#/components/schemas/timestamp' - - description: The date/time that this policy ended being active for this curb location. - required: - - curb_policy_ids - - start_date - - end_date - description: An array of information about what previous policies applied to a curb zone and when. - published_date: - allOf: - - $ref: '#/components/schemas/timestamp' - - description: The date/time that this curb zone was first published in this data feed. - last_updated_date: - allOf: - - $ref: '#/components/schemas/timestamp' - - description: The date/time that the properties of ths curb zone were last updated. - prev_curb_zone_ids: - type: array - items: - $ref: '#/components/schemas/uuid' - description: An array of IDs of previous curb zone objects. They are listed in order with the most recent ones first. - start_date: - allOf: - - $ref: '#/components/schemas/timestamp' - - description: The earliest time that the data for this curb location is known to be valid. - end_date: - allOf: - - $ref: '#/components/schemas/timestamp' - - description: The time at which the data for this curb location ceases to be valid. - location_references: - type: array - items: - type: object - properties: - source: - type: string - format: url - description: An identifier for the source of the linear reference. This MUST be a URL pointing to more information about the underlying map or reference system. - ref_id: - type: string - description: The linear feature being referenced (usually a street or curb segment). - start: - type: integer - description: The distance (in centimeters) from the start of the referenced linear feature to the start of the Curb Zone. - minimum: 0 - end: - type: integer - description: 'The distance (in centimeters) from the start of the referenced linear feature to the end of the Curb Zone. ''end'' MAY be smaller than start, implying that the direction of the Curb Zone is opposite to the direction of the referenced linear feature.' - minimum: 0 - side: - type: string - enum: - - left - - right - description: 'If the referenced linear feature is a roadway, the side of the roadway on which the Curb Zone may be found, when heading from the start to the end of the feature in its native orientation.' - required: - - source - - ref_id - - start - - end - description: 'A linear reference defines a Curb Zone''s position by reference to a linear feature, like a street centerline or edge-of-pavement line.' - description: One or more linear references for this Curb Zone. - name: - type: string - description: A human-readable name for this Curb Zone that identifies it to end users. - user_zone_id: - type: string - description: 'An identifier that can be used to refer to this Curb Zone on physical signage as well as within mobile applications, typically for payment purposes.' - street_name: - type: string - description: The name of the street that this Curb Zone is on. - cross_street_start_name: - type: string - description: The name of the cross street at or before the start of this Curb Zone. - cross_street_end_name: - type: string - description: The name of the cross street at the end of this Curb Zone. - length: - type: integer - description: 'The length, in centimeters, of the Curb Zone when projected along the street centerline. Note that this is the definitive length of the curb area, and not the edge length of the geographic polygon.' - exclusiveMinimum: 0 - available_space_lengths: - type: array - items: - type: integer - description: 'If availability information is present, an array of numbers containing the lengths (in centimeters) of all known non-overlapping available spaces within this Curb Zone.' - availability_time: - allOf: - - $ref: '#/components/schemas/timestamp' - - description: 'If availability information is present, the timestamp corresponding to the most recent time that availability was computed for this zone.' - width: - type: integer - description: 'The width, in centimeters, that the Curb Zone occupies from the curb to the roadway lane.' - exclusiveMinimum: 0 - parking_angle: - type: string - enum: - - parallel - - perpendicular - - angled - description: The angle in which passenger vehicles in this Curb Zone are meant to park. - num_spaces: - type: integer - description: The number of demarcated spaces within this Curb Zone. - street_side: - type: string - enum: - - 'N' - - NE - - E - - SE - - S - - SW - - W - - NW - description: The cardinal or subcardinal direction representing the side of the roadway that this curb is on. - median: - type: boolean - description: 'If "true", this curb location is on the median of a street, rather than its edge.' - entire_roadway: - type: boolean - description: 'If "true", this curb location takes up the entire width of the roadway (which may be impassible for through traffic when the Curb Zone is being used for parking or loading).' - curb_area_ids: - type: array - items: - $ref: '#/components/schemas/uuid' - description: The ID(s) of the Curb Areas that this Curb Zone is a part of. - curb_space_ids: - type: array - items: - $ref: '#/components/schemas/uuid' - description: The ID(s) of the Curb Spaces that this Curb Zone contains. - required: - - curb_zone_id - - geometry - - curb_policy_ids - - published_date - - last_updated_date - - start_date - example: - curb_zone_id: 7d8a5885-e949-4ac9-afb7-fa4d43b68530 - geometry: - type: Polygon - coordinates: - - - - -73.982105 - - 40.767932 - - - -73.973694 - - 40.764551 - - - -73.949318 - - 40.796918 - - - -73.958416 - - 40.800686 - - - -73.982105 - - 40.767932 - curb_policy_ids: - - cd0996d7-3765-4f0b-a72e-7caf7cf3fe21 - published_date: 1552678594428 - last_updated_date: 1552678594428 - start_date: 1552678594428 - description: A contiguous region of curb space on a single block face that is regulated in a particular way. - x-internal: false - x-stoplight: - id: 202654569fa9e - title: Model - Curb Zone - curb_area: - type: object - properties: - curb_area_id: - allOf: - - $ref: '#/components/schemas/uuid' - - description: The ID for the Curb Area. - geometry: - type: object - properties: - type: - type: string - coordinates: - type: array - items: - type: number - required: - - type - - coordinates - description: The spatial extent of this curb location. - name: - type: string - description: The name of this curb area for reference. - published_date: - allOf: - - $ref: '#/components/schemas/timestamp' - - description: The date/time that this curb area was first published in this data feed. - last_updated_date: - allOf: - - $ref: '#/components/schemas/timestamp' - - description: The date/time that the properties of ths curb area were last updated. - curb_zone_ids: - type: array - items: - $ref: '#/components/schemas/uuid' - description: The IDs of all the Curb Zones included within this Curb Area at the requested time. - required: - - curb_area_id - - geometry - - published_date - - last_updated_date - - curb_zone_ids - example: - curb_area_id: 91eb651d-f91f-4ba2-9553-75ca368c7e1f - geometry: - type: Polygon - coordinates: - - - - -73.982105 - - 40.767932 - - - -73.973694 - - 40.764551 - - - -73.949318 - - 40.796918 - - - -73.958416 - - 40.800686 - - - -73.982105 - - 40.767932 - name: Central business district - published_date: 1552678594428 - last_updated_date: 1552678594428 - curb_zone_ids: - - 7d8a5885-e949-4ac9-afb7-fa4d43b68530 - description: A particular neighborhood or area of interest that includes one or more Curb Zones. - title: Model - Curb Area - x-internal: false - x-stoplight: - id: e2235c918423f - curb_space: - type: object - properties: - curb_space_id: - allOf: - - $ref: '#/components/schemas/uuid' - - description: The ID of the Curb Space. - geometry: - type: object - properties: - type: - type: string - coordinates: - type: array - items: - type: number - required: - - type - - coordinates - description: The spatial extent of this curb location. - name: - type: string - description: The name of this curb space for reference. - published_date: - allOf: - - $ref: '#/components/schemas/timestamp' - - description: The date/time that this curb area was first published in this data feed. - last_updated_date: - allOf: - - $ref: '#/components/schemas/timestamp' - - description: The date/time that the properties of ths curb area were last updated. - curb_zone_id: - allOf: - - $ref: '#/components/schemas/uuid' - - description: The ID of the Curb Zone this space is within. - space_number: - type: integer - description: 'The sequence number of this space within its Zone. If specified, two spaces within the same Curb Zone MUST NOT share a space number, and space numbers SHOULD be consecutive positive integers starting at 1.' - length: - type: integer - description: Length in centimeters of this Space. - width: - type: integer - description: Width in centimeters of this Space. - available: - type: boolean - description: Whether this space is available for vehicles to park in at the specified time. - availability_time: - allOf: - - $ref: '#/components/schemas/timestamp' - - description: 'If availability information is present, the most recent time that availability was computed for this space.' - required: - - curb_space_id - - geometry - - published_date - - last_updated_date - - curb_zone_id - - length - example: - curb_space_id: 47c877ce-2670-4300-9ec2-6a1f3af707b6 - geometry: - type: Polygon - coordinates: - - - - -73.982105 - - 40.767932 - - - -73.973694 - - 40.764551 - - - -73.949318 - - 40.796918 - - - -73.958416 - - 40.800686 - - - -73.982105 - - 40.767932 - name: A487 - published_date: 1552678594428 - last_updated_date: 1552678594428 - curb_zone_id: 7d8a5885-e949-4ac9-afb7-fa4d43b68530 - space_number: 487 - length: 550 - width: 260 - available: false - availability_time: 1657213115826 - description: An individual demarcated space within a Curb Zone. - x-internal: false - x-stoplight: - id: acc7fbb358869 - title: Model - Curb Space - curb_policy: - type: object - example: - curb_policy_id: cd0996d7-3765-4f0b-a72e-7caf7cf3fe21 - published_date: 1552678594428 - priority: 1 - data_source_operator_id: - - b2046faf-2bc2-4f0e-b784-7cc746138555 - - aba63473-351c-4624-93ab-456db34f83a6 - - 984cae91-3a11-49eb-b68c-d325a6cc8970 - time_spans: - - days_of_week: - - mon - - tue - - wed - - thu - - fri - time_of_day_start: '10:00' - time_of_day_end: '16:00' - rules: - - activity: parking - max_stay: 15 - user_classes: - - rideshare - - electric - description: An object that allows or prohibits a particular set of users from using a particular curb at a particular time or times. - x-stoplight: - id: a3aab507b1c7e - title: Model - Curb Policy - properties: - curb_policy_id: - $ref: '#/components/schemas/uuid' - description: An ID that uniquely identifies this exact policy across Curb Zones. - published_date: - $ref: '#/components/schemas/timestamp' - priority: - type: integer - description: 'Specifies which other policies this one takes precedence over. If two Policies on the same Curb Zone have overlapping Time Spans and apply to the same user class, the one that applies at a given time is the one with the lowest priority.' - rules: - type: array - description: The rule(s) that this policy applies. - items: - type: object - description: 'A rule defines who is allowed to do what, and for how long, on a curb, per the policy.' - properties: - activity: - type: string - enum: - - parking - - no parking - - loading - - no loading - - unloading - - no unloading - - stopping - - no stopping - - travel - - no travel - description: The activity that is forbidden or permitted by this regulation. - max_stay: - type: integer - description: 'The length of time (in units of max_stay_unit) for which the curb may be used under this regulation. If not specified, the curb may be used under this regulation indefinitely.' - max_stay_unit: - $ref: '#/components/schemas/unit-of-time' - no_return: - type: integer - description: The length of time (in units of no_return_unit) that a user must vacate a Curb Zone before being allowed to return for another stay. - no_return_unit: - $ref: '#/components/schemas/unit-of-time' - description: The Unit of Time associated with the no_return value. - user_classes: - type: array - description: A user class represents any class of vehicles that is regulated by a city with respect to curb space. - items: - type: string - enum: - - bicycle - - bus - - cargo_bicycle - - car - - moped - - motorcycle - - scooter - - truck - - van - - handicap-accessible - - human - - electric_assist - - electric - - combustion - - autonomous - - construction - - delivery - - emergency_use - - freight - - parking - - permit - - rideshare - - school - - service_vehicles - - special_events - - taxi - - utilities - - vending - - waste_management - rate: - type: array - description: The cost of using this Curb Zone when this regulation applies. - items: - type: object - description: A Rate defines the amount a user of the curb needs to pay when a given rule applies. - properties: - rate: - type: integer - description: The rate for this space in cents (or the smallest denomination of local currency) per rate_unit. - rate_unit: - $ref: '#/components/schemas/unit-of-time' - description: The unit of time associated with the rate. - rate_unit_period: - type: string - enum: - - rolling - - calendar - description: The period of time that the rate_unit covers. - increment_duration: - type: integer - description: 'If specified, this is the smallest number of rate_units a user can pay for.' - increment_amount: - type: integer - description: 'If specified, the rate for this space is rounded up to the nearest increment of this amount, specified in the same currency units as rate.' - start_duration: - type: integer - description: The number of rate_units the vehicle must have already been present in the Curb Zone before this rate starts applying. - end_duration: - type: integer - description: The number of rate_units after which the rate stops applying. - required: - - rate - - rate_unit - required: - - activity - time_spans: - type: array - description: 'If specified, this regulation only applies at the times defined within.' - items: - type: object - description: 'A Time Span defines a period of time (that may occur once or repeatedly) during which a given regulation applies. When multiple fields are combined, all criteria must be met in order for a given Time Span to apply.' - properties: - start_date: - $ref: '#/components/schemas/timestamp' - end_date: - $ref: '#/components/schemas/timestamp' - days_of_week: - type: string - enum: - - sun - - mon - - tue - - wed - - thu - - fri - - sat - description: An array of days of the week when this Time Span applies. - days_of_month: - type: array - description: An array of days of the month when this Time Span applies. - items: - type: integer - minimum: 1 - maximum: 31 - months: - type: array - description: 'If specified, this Time Span applies only during these months (1=January, 12=December).' - items: - type: integer - minimum: 1 - maximum: 12 - time_of_day_start: - type: string - description: 'The local time that this Time Span starts to apply, as 24-hour "HH:MM".' - time_of_day_end: - type: string - description: 'The local time that this Time Span stops applying, as 24-hour "HH:MM".' - designated_period: - type: string - enum: - - snow emergency - - holidays - - school days - - game days - description: 'A string representing an arbitrarily-named, externally-defined period of time.' - designated_period_except: - type: boolean - description: 'If specified and true, this Time Span applies at all times not matching the named designated period.' - data_source_operator_id: - type: array - description: An array of Data Source Operator IDs that this policy only applies to. - items: - $ref: '#/components/schemas/operator_id' - required: - - curb_policy_id - - published_date - - priority - - rules - curb-event: - title: Model - Curb Event - x-stoplight: - id: cb0b7ebce9771 - type: object - properties: - event_id: - $ref: '#/components/schemas/uuid' - description: The globally unique identifier of the event that occurred. - event_type: - type: string - enum: - - comms_lost - - comms_restored - - decommissioned - - park_start - - park_end - - scheduled_report - - enter_area - - exit_area - description: |- - The type of event that has occurred. - - Possible values are defined in an enumeration. - event_purpose: - type: string - enum: - - construction - - delivery - - emergency_use - - parking - - passenger_transport - - special_events - - waste_management - - device_maintenance - - autonomous - - ems - - fire - - food_delivery - - parcel_delivery - - police - - public_transit - - ride_hail - - road_maintenance - - service_vehicles - - taxi - - utility_work - - vehicle_charging - - vehicle_parking - - vending - - unspecified - description: |- - **Conditionally Required** - - General event purpose that the vehicle performed during its event, discernible by observation, sensors, or self-reported in copany data feeds. New event purposes MAY be generated to reflect local curb uses, but when possible, the following well-known recommended values should be used. It may not always be knowable, but where it is possible this information should be conveyed. - - If multiple purposes apply, then use the more descriptive/specific value. - event_location: - type: string - description: The geographic point location where the event occurred. - event_time: - $ref: '#/components/schemas/timestamp' - event_publication_time: - $ref: '#/components/schemas/timestamp' - description: Time at which the event became available for consumption by this API. - event_session_id: - $ref: '#/components/schemas/uuid' - description: 'May be provided to tie known connected `park_start`and `park_end` event types together by a unique session ID. ' - curb_area_ids: - type: array - description: 'Unique IDs of the Curb Area(s) where the event occurred. Since Curb Areas may overlap, an event may occur in more than one. ' - items: - $ref: '#/components/schemas/uuid' - curb_zone_id: - $ref: '#/components/schemas/uuid' - description: | - Unique ID of the Curb Zone where the event occurred. - - Required for events that occurred at a known Curb Zone for ALL event_types. - curb_space_id: - $ref: '#/components/schemas/uuid' - description: | - Unique ID of the Curb Space where the event occured. - - Required for events that occurred at a known Curb Space, if known and used, for these event types: - * `park_start` - - * `park_end` - - * `enter_area` - - * `exit_area` - data_source_type: - $ref: '#/components/schemas/data_source_type' - description: The set of possible categories of sources that are sending this event. ' - data_source_operator_id: - $ref: '#/components/schemas/operator_id' - description: |- - Unique identifier of the entity responsible for operating the event data source. - - For example, IDs can identify a fleet operator sending a data feed, or the organization (company or city) operating the sensor. - - **Values are defined globally and are managed by the Open Mobility Foundation and operators may register [here](https://github.com/openmobilityfoundation/curb-data-specification/wiki/Adding-a-CDS-Data-Source-Operator-ID)** - data_source_operator_name: - type: string - description: |- - Name of the provider responsible for operating the vehicle, device, or sensor at the time of the event. - - May be sent along with `data_source_operator_id` or on its own for small operators at the discretion of the city. - data_source_device_id: - type: string - description: |- - Unique identifier of this event source, whether sensor, vehicle, camera, etc. Allows agencies to connect related Events as they are recorded by the same source. - - If coming from a provider, this is a generated UUID they use and not the same as the external vehicle_id. - - **If this field is needed for your use cases, review the OMF's [Privacy Guidance](https://github.com/openmobilityfoundation/curb-data-specification/blob/main/README.md#data-privacy).** - data_source_manufacturer: - type: string - description: Manufacturer of the data source hardware or vehicle reporting event data. - data_source_model: - type: string - description: Manufacturer of the data source hardware or vehicle reporting event data. - sensor_status_is_commissioned: - type: boolean - description: |- - If a sensor was used to capture this event, the commissioned status at the time that the event was reported. - - Indicates whether the sensor is currently in a state where it should be reporting data. - sensor_status_is_online: - type: boolean - description: |- - If a sensor was used to capture this event, the online status at the time that the event was reported. - - Indicates whether the sensor is currently online and reporting data. - sensor_status_is_online - copy: - type: boolean - description: |- - If a sensor was used to capture this event, the online status at the time that the event was reported. - - Indicates whether the sensor is currently online and reporting data. - vehicle_id: - type: string - description: |- - A vehicle identifier visible externally on the vehicle itself. - - **If this field is needed for your use cases, review the OMF's [Privacy Guidance](https://github.com/openmobilityfoundation/curb-data-specification/blob/main/README.md#data-privacy).** - vehicle_license_plate: - type: string - description: |- - The consistently placed vehicle license plate, usable by ALPR systems, when required for curb use. - - This field is potentially sensitive (depending on local, state, and national laws) and a data privacy framework is recommended for collecting, retention, deletion, obfuscation, and security. If this field is needed for your use cases, review our [Privacy Guidance](https://github.com/openmobilityfoundation/curb-data-specification/blob/main/README.md#data-privacy) - vehicle_permit_number: - type: string - description: 'If applicable, the assigned permit numbe for this vehicle from the city agency.' - vehicle_length: - type: integer - description: |- - Appoximate length of the vehicle that performed the event, in centimeters. - - Required for sources capable of determining vehicle length. - vehicle_type: - $ref: '#/components/schemas/vehicle_type' - description: |- - Type of the vehicle that performed the event. - - Required for sources capable of determining vehicle type. - vehicle_propulsion_types: - type: string - enum: - - human - - electric_assist - - electric - - combustion - description: |- - List of propulsion types used by the vehicle that performed the event. - - Required for sources capable of determining vehicle propulsion type. - vehicle_blocked_lane_types: - type: string - description: |- - Type(s) of lane blocked by the vehicle performing the event. If no lanes are blocked by the vehicle performing the event, the array should be empty. - - Required for sources capable of determining it for the following event_types: `park_start` - enum: - - travel_lane - - turn_lane - - bike_lane - - bus_lane - - parking - - shoulder - - median - - sidewalk - - unspecified - curb_occupants: - type: object - description: |- - Current occupants of the Curb Zone. If the sensor is capable of identifying the linear location of the vehicle, then elements are sorted in ascending order according to the start property of the linear reference. Otherwise, elements appear in no particular order. - - Required for sources capable of determining it for the following event_types: `park_start`, `park_end`, `scheduled_report` - properties: - type: - $ref: '#/components/schemas/vehicle_type' - length: - type: number - description: |- - The approximate length in centimeters of the vehicle. - - Required when the event source is capable of determining vehicle length. - linear_location: - type: array - description: |- - A two-element array that specifies the start and end of the occupant’s linear location relative to the start of the Curb Zone in that order. - - Required when the event source is capable of determining the linear location of occupants. - minItems: 2 - maxItems: 2 - items: - type: number - minimum: 0 - maximum: 0 - actual_cost: - type: integer - description: |- - If available from the source, the actual cost, in the currency defined in currency, paid by the curb user for this event. - - All costs should be given as integers in the currency's smallest unit. As an example, to represent $1 USD, specify an amount of 100 (for 100 cents). - required: - - event_id - - event_type - - event_location - - event_time - - event_publication_time - - data_source_type - - data_source_device_id - unit-of-time: - description: An enumeration for units of time. - type: string - enum: - - second - - minute - - hour - - day - - week - - month - - year - x-internal: false - title: Unit of Time - x-stoplight: - id: t22vz53uc0i6g - rest-response-time_zone: - title: REST Response - Time Zone - type: string - description: |- - This is a standard value that must be returned in the body of all API endpoints. - - The time zone that applies to parking regulations in this dataset. - - MUST be a valid [TZ database time zone name](https://www.iana.org/time-zones) (ex: "US/Eastern" or "Europe/Paris"). - x-internal: false - x-stoplight: - id: d9ojycvukf3ny - rest-response-version: - title: REST Response - Version - x-stoplight: - id: 3bxwqr73eu79g - type: string - description: |- - This is a standard value that must be returned in the body of all API endpoints. - - The version of CDS that the API conforms to. - example: 1.0.0 - x-internal: false - rest-response-last_updated: - $ref: '#/components/schemas/timestamp' - x-stoplight: - id: mwudsha7oso9v - description: |- - This is a standard value that must be returned in the body of all API endpoints. - - The last time the data in this API was updated. - x-internal: false - title: REST Response - Last Updated - rest-response-currency: - title: REST Response - Currency - x-stoplight: - id: 1y7ea7wbdav1u - type: string - description: |- - This is a standard value that must be returned in the body of all API endpoints. - - The ISO 4217 3-letter code for the currency in which rates for curb usage are denominated. All costs should be given as integers in the currency's smallest unit. As an example, to represent `$1 USD`, specify an amount of `100` (for 100 cents). - example: '100' - x-internal: false - rest-response-author: - title: REST Response - Author - x-stoplight: - id: mhmufpclryjhz - type: string - description: |- - This is a standard value that must be returned in the body of all API endpoints. - - The name of the organization that produces and maintains this data. - x-internal: false - rest-response-license_url: - title: REST Response - License URL - x-stoplight: - id: beyxkbcgpflk1 - type: string - description: |- - This is a standard value that must be returned in the body of all API endpoints. - - The licensing terms under which this data is provided. - x-internal: false - securitySchemes: - JSON Web Token: - name: Authorization - type: apiKey - in: header - description: |- - SON Web Token ([JWT](https://jwt.io/introduction/)) is **RECOMMENDED** as the token format. - - JWTs provide a safe, secure way to verify the identity of an agency and provide access to MDS resources without providing access to other, potentially sensitive data. - - Implementers **MAY** include any metadata in the JWT they wish that helps to route, log, permission, or debug agency requests, leaving their internal implementation flexible. - OAuth 2.0: - type: oauth2 - flows: - clientCredentials: - tokenUrl: '' - refreshUrl: '' - scopes: {} - description: |- - OAuth 2.0's `client_credentials` grant type (outlined in [RFC6749](https://tools.ietf.org/html/rfc6749#section-4.4)) is **RECOMMENDED** as the authentication and authorization scheme. - - OAuth 2.0 is an industry standard authorization framework with a variety of existing tooling. The client_credentials grant type facilitates generation of tokens that can be used for access by agencies and distributed to data partners. - - If implementers use this auth scheme, they **MAY** choose to specify token scopes that define access parameters like allowable time ranges. These guidelines **SHOULD** be encoded into the returned token in a parseable way. - responses: - '501': - description: Not Implemented - content: - application/json: - schema: - properties: - id: - type: string - parameters: - curb_place_type: - name: curb_place_type - in: query - required: false - schema: - type: string - description: '**Required with `curb_place_id`.** The type of curb place this aggregate applies to from the Curbs API: area, zone, space. ' - curb_place_id: - name: curb_place_id - in: query - required: false - schema: - type: string - description: '**Required with `curb_place_id`.** The type of curb place this aggregate applies to from the Curbs API: area, zone, space. ' - min_lat: - name: min_lat - in: query - required: false - schema: - type: string - description: 'Used to specify a latitude and longitude bounding box. Must be used together with `min_lng`, `max_lat`, and `max_lng`. If specified only return locations that intersect the supplied bounding box. This parameter is incompatible with `lat`, `lng`, and `radius`.' - min_lng: - name: min_lng - in: query - required: false - schema: - type: string - description: 'Used to specify a latitude and longitude bounding box. Must be used together with `min_lat`, `max_lat`, and `max_lng`. If specified only return locations that intersect the supplied bounding box. This parameter is incompatible with `lat`, `lng`, and `radius`.' - max_lat: - name: max_lat - in: query - required: false - schema: - type: string - description: 'Used to specify a latitude and longitude bounding box. Must be used together with `min_lat`, `min_lng`, and `max_lng`. If specified only return locations that intersect the supplied bounding box. This parameter is incompatible with `lat`, `lng`, and `radius`.' - max_lng: - name: max_lng - in: query - required: false - schema: - type: string - description: 'Used to specify a latitude and longitude bounding box. Must be used together with `min_lat`, `min_lng`, and `max_lat`. If specified only return locations that intersect the supplied bounding box. This parameter is incompatible with `lat`, `lng`, and `radius`.' - lat: - name: lat - in: query - required: false - schema: - type: string - description: 'Specifies a latitude and longitude bounding point and a radius away from that point. Must be used together with `lng` and `radius`. Returns only locations that are within radius centimeters of the point identified by lat/lng. Curb Zones in the response MUST be ordered ascending by distance from the center point. This parameter is incompatible with `min_lat`, `min_lng`, `max_lat`, and `max_lng`' - lng: - name: lng - in: query - required: false - schema: - type: string - description: 'Specifies a latitude and longitude bounding point and a radius away from that point. Must be used together with `lat` and `radius`. Returns only locations that are within radius centimeters of the point identified by lat/lng. Curb Zones in the response MUST be ordered ascending by distance from the center point. This parameter is incompatible with `min_lat`, `min_lng`, `max_lat`, and `max_lng`' - radius: - name: radius - in: query - required: false - schema: - type: string - description: 'Specifies a latitude and longitude bounding point and a radius away from that point. Must be used together with `lng` and `lng`. Returns only locations that are within radius centimeters of the point identified by lat/lng. Curb Zones in the response MUST be ordered ascending by distance from the center point. This parameter is incompatible with `min_lat`, `min_lng`, `max_lat`, and `max_lng`' - start_time: - name: start_time - in: query - required: false - schema: - type: string - description: The start of the time period to return data where the value is inclusive. - end_time: - name: end_time - in: query - required: false - schema: - type: string - description: The end of the time period to return data where the value is inclusive. - curb_area_id: - name: curb_area_id - in: query - required: false - schema: - type: string - description: 'The ID of a Curb Area. If specified, only return records occurring within this area.' - curb_zone_id: - name: curb_zone_id - in: query - required: false - schema: - type: string - description: 'The ID of a Curb Zone. If specified, only return records occurring within this zone.' - curb_space_id: - name: curb_space_id - in: query - required: false - schema: - type: string - description: 'The ID of a Curb Space. If specified, only return records occurring within this area.' From 901f30560294458d92a44cadc17ef21b29754690 Mon Sep 17 00:00:00 2001 From: mdanko Date: Tue, 10 Feb 2026 12:35:40 -0500 Subject: [PATCH 03/26] Adds proposed changelog file --- CHANGELOG.MD | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 CHANGELOG.MD diff --git a/CHANGELOG.MD b/CHANGELOG.MD new file mode 100644 index 0000000..c85869d --- /dev/null +++ b/CHANGELOG.MD @@ -0,0 +1,73 @@ +# CDS OpenAPI – Change Log + +This change log tracks how the CDS **OpenAPI schema** (this repo) reflects changes in the upstream +[CDS specification](https://github.com/openmobilityfoundation/curb-data-specification). + +--- + +## [1.1.0] – 2025-10-27 + +**Upstream spec:** CDS 1.1.0 ([release notes](https://github.com/openmobilityfoundation/curb-data-specification/releases/tag/1.1.0)) +**CDS versions:** From CDS 1.0.x → CDS 1.1.0 + +### High‑Level Features + +- **Objects** + - Add Curb Object data model to describe physical assets at or near the curb (e.g., signs, meters, sensors, devices). + - Add Curbs API endpoints for listing and retrieving objects. + - Include geometry (Point and LineString), ownership, external IDs/URLs, policy references, and other properties. + +- **Enforcement** + - Extend Events and Metrics definitions to capture violations, citations, double‑parking, and enforcement actions. + - Add enforcement‑related metrics (e.g., total enforcement events) to Metrics aggregates/demand data. + +- **Payment Methods & Channels** + - Add fields to Events to capture payment method, payment channel, and payment transaction IDs. + +- **Real‑Time Events (Push)** + - Add an optional POST Events endpoint for pushing events as they occur, in addition to existing pull/query endpoints. + +- **External References** + - Introduce a reusable External Reference structure to link CDS data to external systems/specs (e.g., WZDx, CWZ, GBFS, GTFS, MDS, websites, documents). + - Add external reference fields to relevant data objects (e.g., Events, Objects, Policies). + +- **Custom Attributes** + - Add a Custom Attribute Dictionary definition. + - Allow custom_attributes on core data objects (e.g., Events, Objects, Policies/Metrics) to support implementation‑specific fields. + +- **Authorization** + - Clarify that Curbs (and Events) endpoints may require authorization by the public agency. + - Note that event authorization is recommended but not strictly required so events can be made public when appropriate. + +- **Lines (LineString Geometry)** + - Allow Curb Zones to be defined with GeoJSON LineString geometry in addition to polygons. + - Update examples and descriptive text describing geometry preference and usage. + +- **New Curb Context Data** + - Add descriptive Curb‑level fields such as description, jurisdiction, owner name, address number, and available/available_spaces counts. + +- **Policy & Rule Enhancements** + - Policies: + - Add name and description fields. + - Add color and pattern fields to support real‑world and digital representations. + - Clarify that policy data must be unique and non‑duplicative. + - Rules: + - Add name and description. + - Extend temporal logic (weeks of the month, refined designated_period_except behavior). + +- **Temporal & Designation Updates** + - Update designated_period_except and related fields based on design and guidance. + - Add support for specifying weeks of the month when policies apply. + +- **Event Authorization Guidance** + - Clarify that event authorization is **recommended** but not required, to support public events data. + +- **Vehicle Properties & Computer Vision** + - Expand vehicle attributes (e.g., make, model, year, color, company). + - Add computer‑vision‑oriented fields such as confidence scores for detected properties. + +- **MDS Alignment** + - Update vehicle types and propulsion types to align with the latest Mobility Data Specification (MDS) enums. + +- **Metrics Event Tracking** + - Add new metrics fields for total event counts (including enforcement events) in Sessions/Aggregates. \ No newline at end of file From 8e52a11dde42abce7a8931344af7e1a48b5d5ba5 Mon Sep 17 00:00:00 2001 From: mdanko Date: Tue, 10 Feb 2026 16:40:19 -0500 Subject: [PATCH 04/26] API version bump, README date, fix last_updated refs --- .gitignore | 1 + APIs/Curbs API.yaml | 16 ++++++++-------- APIs/Events API.yaml | 2 +- APIs/Metrics API.yaml | 2 +- README.md | 2 +- 5 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4cb1d38 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +CHANGES_TASK_LIST.md \ No newline at end of file diff --git a/APIs/Curbs API.yaml b/APIs/Curbs API.yaml index b022e5c..c801540 100644 --- a/APIs/Curbs API.yaml +++ b/APIs/Curbs API.yaml @@ -3,7 +3,7 @@ x-stoplight: id: 83teyinnn1py6 info: title: Curb API - version: '1.0' + version: '1.1' description: 'The Curbs API is a REST API allowing cities to specify areas of interest along the curb along with the rules for using them: who is allowed to park, load, unload, pick up, drop off, etc., for how long, for what price (if any), at what times, and on which days. Locations defined in the Curbs API can be connected to event and metrics data, and can be shared with companies and the public, for purposes such as routing, finding legal parking, loading, and pick-up/drop-off spots, or analyzing curb utilization over time.' servers: - url: 'http://localhost:3000' @@ -81,7 +81,7 @@ paths: time_zone: $ref: ../Rest Responses/rest_response_time_zone.yaml last_updated: - $ref: ../Rest Responses/rest_response_last-updated.yaml + $ref: ../Rest Responses/rest_response_last_updated.yaml currency: $ref: ../Rest Responses/rest_response_currency.yaml author: @@ -128,7 +128,7 @@ paths: time_zone: $ref: ../Rest Responses/rest_response_time_zone.yaml last_updated: - $ref: ../Rest Responses/rest_response_last-updated.yaml + $ref: ../Rest Responses/rest_response_last_updated.yaml currency: $ref: ../Rest Responses/rest_response_currency.yaml author: @@ -185,7 +185,7 @@ paths: time_zone: $ref: ../Rest Responses/rest_response_time_zone.yaml last_updated: - $ref: ../Rest Responses/rest_response_last-updated.yaml + $ref: ../Rest Responses/rest_response_last_updated.yaml currency: $ref: ../Rest Responses/rest_response_currency.yaml author: @@ -232,7 +232,7 @@ paths: time_zone: $ref: ../Rest Responses/rest_response_time_zone.yaml last_updated: - $ref: ../Rest Responses/rest_response_last-updated.yaml + $ref: ../Rest Responses/rest_response_last_updated.yaml currency: $ref: ../Rest Responses/rest_response_currency.yaml author: @@ -282,7 +282,7 @@ paths: time_zone: $ref: ../Rest Responses/rest_response_time_zone.yaml last_updated: - $ref: ../Rest Responses/rest_response_last-updated.yaml + $ref: ../Rest Responses/rest_response_last_updated.yaml currency: $ref: ../Rest Responses/rest_response_currency.yaml author: @@ -321,7 +321,7 @@ paths: time_zone: $ref: ../Rest Responses/rest_response_time_zone.yaml last_updated: - $ref: ../Rest Responses/rest_response_last-updated.yaml + $ref: ../Rest Responses/rest_response_last_updated.yaml currency: $ref: ../Rest Responses/rest_response_currency.yaml author: @@ -366,7 +366,7 @@ paths: time_zone: $ref: ../Rest Responses/rest_response_time_zone.yaml last_updated: - $ref: ../Rest Responses/rest_response_last-updated.yaml + $ref: ../Rest Responses/rest_response_last_updated.yaml currency: $ref: ../Rest Responses/rest_response_currency.yaml author: diff --git a/APIs/Events API.yaml b/APIs/Events API.yaml index 428b2f4..40db258 100644 --- a/APIs/Events API.yaml +++ b/APIs/Events API.yaml @@ -3,7 +3,7 @@ x-stoplight: id: i4ivj21etaicj info: title: Event API - version: '1.0' + version: '1.1' description: 'The Events API is a REST API allowing real-time and historic events at the curb to be sent to cities, and the ability to check on the status of any sensors. Events can come from company data feeds, on street sensors, session payments, company check-ins, in-person parking personnel, and/or other city data sources. Data sent in the Events API can be connected to the Curbs API over time and space, and events are used for calculations in the Metrics API.' servers: - url: 'http://localhost:3000' diff --git a/APIs/Metrics API.yaml b/APIs/Metrics API.yaml index e62f723..591bfc6 100644 --- a/APIs/Metrics API.yaml +++ b/APIs/Metrics API.yaml @@ -3,7 +3,7 @@ x-stoplight: id: 2p76lk7uckivl info: title: Metrics API - version: '1.0' + version: '1.1' description: 'The Metrics API is a REST API allowing historic metrics calculations based on Event activity that happened at defined Curb places. Defines common calculation methodologies to measure historic dwell time, occupancy, usage and other aggregated statistics.' servers: - url: 'http://localhost:3000' diff --git a/README.md b/README.md index cf323c0..90be2a0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # CDS OpenAPI **Status: Draft In Progress for v1.1** -_Last updated on October 21st, 2024_ +_Last updated February 10, 2026_ --- ## Overview From 58e71a85ccdee91fa5d36fa8c1a25f53f8247130 Mon Sep 17 00:00:00 2001 From: mdanko Date: Tue, 7 Apr 2026 11:01:04 -0400 Subject: [PATCH 05/26] Changes to gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4cb1d38..29b5b77 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -CHANGES_TASK_LIST.md \ No newline at end of file +archive/* \ No newline at end of file From 52418c26339c73dafdf899d23c1a38ae5632c2cb Mon Sep 17 00:00:00 2001 From: mdanko Date: Tue, 7 Apr 2026 11:01:27 -0400 Subject: [PATCH 06/26] Adds AGENTS/CLAUDE MD files for AI help --- AGENTS.MD | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 18 +++++++++++ 2 files changed, 113 insertions(+) create mode 100644 AGENTS.MD create mode 100644 CLAUDE.md diff --git a/AGENTS.MD b/AGENTS.MD new file mode 100644 index 0000000..3663a7c --- /dev/null +++ b/AGENTS.MD @@ -0,0 +1,95 @@ +# Working with the CDS OpenAPI Project + +This document describes how to collaborate with an AI agent (or human contributor) on the `cds-openapi` project. + +## Project Purpose + +This repository maintains an **OpenAPI 3.1 specification** for the [Curb Data Specification (CDS)](https://github.com/openmobilityfoundation/curb-data-specification), managed by the Open Mobility Foundation. The spec is hosted publicly via Stoplight and serves as an interactive API browser for implementers. + +## Repository Structure + +``` +cds-openapi/ + APIs/ + Curbs API.yaml # Curb zones, areas, spaces, policies, objects + Events API.yaml # Curb events and sensor status + Metrics API.yaml # Sessions and aggregates + Shared Data Models/ # Reusable YAML schemas (curb_zone, curb_event, etc.) + Rest Responses/ # Common API response header schemas + toc.json # Stoplight table of contents + .spectral.mjs # Linting rules + .stoplight.json # Stoplight platform config + V1.1_IMPLEMENTATION_PLAN.md # Active implementation plan +``` + +## Branch Strategy + +| Branch | Purpose | +|--------|---------| +| `main` | Stable, latest released version | +| `v1.0` | Frozen v1.0 spec | +| `draft-v1.1` | Active development branch for v1.1 changes | + +Always work on the `draft-v1.1` branch (or a feature branch off it) when implementing v1.1 changes. Never push directly to `main`. + +## Upstream Spec Reference + +The authoritative CDS spec lives at https://github.com/openmobilityfoundation/curb-data-specification. When implementing changes: + +1. Always cross-reference the upstream spec text, not just PR summaries +2. The spec has three sections: `curbs/`, `events/`, `metrics/` — each with its own README +3. Field names, types, enumerations, and required/optional status must match the upstream spec exactly +4. The `general-information.md` and `data-types.md` files contain shared definitions + +## How to Make Changes + +### Workflow for each step in the implementation plan + +1. **Read the plan step** in `V1.1_IMPLEMENTATION_PLAN.md` +2. **Read the upstream spec** for the relevant section to confirm exact field names, types, enums, and descriptions +3. **Edit the YAML files** — maintain the existing style conventions: + - Use `$ref` for shared models rather than inline definitions + - Include `description` on every field + - Use `x-stoplight: id:` annotations if present on surrounding fields (Stoplight generates these) + - Keep `required` arrays up to date +4. **Validate** — run the Spectral linter (`.spectral.mjs`) after changes +5. **Commit** — use the commit message suggested in the plan step, or a similar conventional-commit style message + +### Style conventions + +- File names: `snake_case.yaml` for shared models +- Enum values: `snake_case` +- UUID fields: `type: string, format: uuid` +- Timestamps: `$ref: ./timestamp.yaml` +- Geographic fields: `$ref` to `geoJSON_point.yaml`, `geoJSON_polygon.yaml`, or `geoJSON_linestring.yaml` +- Arrays of references: `type: array` with `items: { $ref: ./some_model.yaml }` + +### Adding a new shared model + +1. Create the YAML file in `Shared Data Models/` +2. Add a `$ref` from any model or API that uses it +3. Add an entry to `toc.json` under the appropriate section +4. Verify the `$ref` path is correct relative to the referencing file + +## Validation + +The project uses Spectral for OpenAPI linting: + +```bash +# If spectral is installed globally +spectral lint "APIs/*.yaml" +``` + +The `.spectral.mjs` config extends a remote Stoplight ruleset. After making changes, always lint and fix any errors before committing. + +## Key Gotchas + +- **$ref paths are relative** to the file containing the reference. API files use `../Shared Data Models/foo.yaml`; shared models use `./foo.yaml`. +- **Spaces in file names**: `Curbs API.yaml` has a space — use URL encoding (`Curbs%20API.yaml`) in Markdown links but literal paths in `$ref`. +- **toc.json** controls what appears in the Stoplight browser — new models won't show up until added here. +- **x-stoplight IDs** are auto-generated by the Stoplight editor. When adding fields by hand, you can omit them and Stoplight will generate them later, or include them for consistency. +- **501 responses** must be documented on all optional endpoints per the CDS spec pattern. + +## Implementation Plan + +The active plan for v1.1 work is in `V1.1_IMPLEMENTATION_PLAN.md`. Follow the phases and steps in order — they are sequenced so that schema dependencies are in place before API paths reference them. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..b9399e8 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,18 @@ +# CLAUDE.md + +Read [AGENTS.MD](AGENTS.MD) for full project context, structure, conventions, and workflow instructions. + +## Quick Reference + +- **What this repo is:** OpenAPI 3.1 spec for the Curb Data Specification (CDS) +- **Active branch:** `draft-v1.1` +- **Implementation plan:** [V1.1_IMPLEMENTATION_PLAN.md](V1.1_IMPLEMENTATION_PLAN.md) +- **Upstream spec:** https://github.com/openmobilityfoundation/curb-data-specification +- **Upstream PR for v1.1 changes:** https://github.com/openmobilityfoundation/curb-data-specification/pull/201 + +## Before making any changes + +1. Confirm you are on the `draft-v1.1` branch +2. Read the relevant step in `V1.1_IMPLEMENTATION_PLAN.md` +3. Cross-reference the upstream CDS spec for exact field definitions +4. After editing, validate with the Spectral linter From 94fbcdf714ea67b452b8942218679ec38419875c Mon Sep 17 00:00:00 2001 From: mdanko Date: Tue, 7 Apr 2026 11:02:02 -0400 Subject: [PATCH 07/26] Adds CHANGELOG AND 1.1 implementation plan --- CHANGELOG.MD | 121 +++++--- V1.1_IMPLEMENTATION_PLAN.md | 590 ++++++++++++++++++++++++++++++++++++ 2 files changed, 663 insertions(+), 48 deletions(-) create mode 100644 V1.1_IMPLEMENTATION_PLAN.md diff --git a/CHANGELOG.MD b/CHANGELOG.MD index c85869d..4ec9cb6 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -7,67 +7,92 @@ This change log tracks how the CDS **OpenAPI schema** (this repo) reflects chang ## [1.1.0] – 2025-10-27 -**Upstream spec:** CDS 1.1.0 ([release notes](https://github.com/openmobilityfoundation/curb-data-specification/releases/tag/1.1.0)) -**CDS versions:** From CDS 1.0.x → CDS 1.1.0 +**Upstream spec:** CDS 1.1.0 ([release notes](https://github.com/openmobilityfoundation/curb-data-specification/releases/tag/1.1.0)) +**Upstream PR:** [#201](https://github.com/openmobilityfoundation/curb-data-specification/pull/201) -### High‑Level Features +### New Data Models -- **Objects** - - Add Curb Object data model to describe physical assets at or near the curb (e.g., signs, meters, sensors, devices). - - Add Curbs API endpoints for listing and retrieving objects. - - Include geometry (Point and LineString), ownership, external IDs/URLs, policy references, and other properties. +- **Curb Object** — new first-class model for physical infrastructure at or near the curb (bollards, planters, cameras, pay stations, lockers, etc.) with Point and LineString geometry, ownership, policy references, and external links. +- **Enforcement** — nested object on events capturing enforcement actions with citation IDs, warning flags, action taken, and cost. +- **Violation** — nested object within enforcement for individual violation codes, names, and costs. +- **Payment Channel** — enum for payment medium (meter, mobile_app, website, sms, telephone). +- **Payment Method** — enum for payment instrument (cash, credit_card, debit_card, digital_wallet, transit_card, prepaid_account, permit). -- **Enforcement** - - Extend Events and Metrics definitions to capture violations, citations, double‑parking, and enforcement actions. - - Add enforcement‑related metrics (e.g., total enforcement events) to Metrics aggregates/demand data. +### New API Endpoints -- **Payment Methods & Channels** - - Add fields to Events to capture payment method, payment channel, and payment transaction IDs. +- `GET /curbs/objects` — query curb objects by area, zone, space, time, and operator. +- `GET /curbs/objects/{id}` — fetch a single curb object by ID. +- `POST /events/events` — optional push endpoint for submitting events in real time. -- **Real‑Time Events (Push)** - - Add an optional POST Events endpoint for pushing events as they occur, in addition to existing pull/query endpoints. +### Curb Event Changes -- **External References** - - Introduce a reusable External Reference structure to link CDS data to external systems/specs (e.g., WZDx, CWZ, GBFS, GTFS, MDS, websites, documents). - - Add external reference fields to relevant data objects (e.g., Events, Objects, Policies). +- Add `payment_channel`, `payment_method`, `payment_transaction_id` fields. +- Add `enforcement` object (with nested violations). +- Add `curb_object_id` reference. +- Add vehicle computer-vision fields: `vehicle_license_plate_jurisdiction`, `vehicle_license_plate_confidence`, `vehicle_type_confidence`, `vehicle_color`, `vehicle_color_confidence`, `vehicle_company_name`, `vehicle_company_name_confidence`, `vehicle_run_id`, `vehicle_run_id_confidence`. +- Add `data_source_device_name`. +- Add `vehicle_type` as a direct field reference. +- Add `event_time` query parameter to `GET /events/events` (ISO 8601 UTC hour format). -- **Custom Attributes** - - Add a Custom Attribute Dictionary definition. - - Allow custom_attributes on core data objects (e.g., Events, Objects, Policies/Metrics) to support implementation‑specific fields. +### Curb Zone Changes -- **Authorization** - - Clarify that Curbs (and Events) endpoints may require authorization by the public agency. - - Note that event authorization is recommended but not strictly required so events can be made public when appropriate. +- Add optional `geometry_line` (GeoJSON LineString) alongside the existing polygon geometry. +- Add `curb_object_ids` array linking zones to physical objects. +- Add context fields: `description`, `jurisdiction_type` (public/private/other), `owner_name`, `address_number`. +- Add `external_references` and `custom_attributes`. -- **Lines (LineString Geometry)** - - Allow Curb Zones to be defined with GeoJSON LineString geometry in addition to polygons. - - Update examples and descriptive text describing geometry preference and usage. +### Curb Space Changes -- **New Curb Context Data** - - Add descriptive Curb‑level fields such as description, jurisdiction, owner name, address number, and available/available_spaces counts. +- Add `curb_object_ids` array (conditionally required). +- Add `external_references` and `custom_attributes`. -- **Policy & Rule Enhancements** - - Policies: - - Add name and description fields. - - Add color and pattern fields to support real‑world and digital representations. - - Clarify that policy data must be unique and non‑duplicative. - - Rules: - - Add name and description. - - Extend temporal logic (weeks of the month, refined designated_period_except behavior). +### Curb Area Changes -- **Temporal & Designation Updates** - - Update designated_period_except and related fields based on design and guidance. - - Add support for specifying weeks of the month when policies apply. +- Add `external_references` and `custom_attributes`. -- **Event Authorization Guidance** - - Clarify that event authorization is **recommended** but not required, to support public events data. +### Policy & Rule Changes -- **Vehicle Properties & Computer Vision** - - Expand vehicle attributes (e.g., make, model, year, color, company). - - Add computer‑vision‑oriented fields such as confidence scores for detected properties. +- Add `name`, `description`, and `primary_color` to curb policies. +- Loosen priority constraint: overlapping policies may share priority if they apply to disjoint user classes or activities. +- Add `name` and `description` to rules. -- **MDS Alignment** - - Update vehicle types and propulsion types to align with the latest Mobility Data Specification (MDS) enums. +### Temporal Changes -- **Metrics Event Tracking** - - Add new metrics fields for total event counts (including enforcement events) in Sessions/Aggregates. \ No newline at end of file +- Add `weeks_of_month` to time spans for week-of-month scheduling. +- Update `designated_period_except` behavior per v1.1 guidance. +- Fix `days_of_week` from single string to array type. + +### Enumeration Updates + +- **Event types:** add `vehicle_detected`, `vehicle_violation_start`, `vehicle_violation_end`, `citation_issued`. +- **Vehicle types:** add `bus`, `delivery_robot`, `scooter_standing`, `scooter_seated`; fix `bicyle` → `bicycle`. +- **Propulsion types:** add `combustion_diesel`, `hybrid`, `hydrogen_fuel_cell`, `plug_in_hybrid`. +- **User classes (rules):** replace `handicap-accessible` with `accessible`; add `disabled_parking_permit`. + +### External References + +- Add reusable `external_reference` schema for linking CDS data to external systems (WZDx, CWZ, MDS, GBFS, GTFS, websites, documents). +- Wire `external_references` array onto curb zones, areas, spaces, events, policies, and objects. + +### Custom Attributes + +- Add reusable `custom_attributes` schema for implementation-specific key/value data. +- Wire onto curb zones, areas, spaces, events, policies, sessions, aggregates, and objects. + +### Metrics Changes + +- Add `curb_object_id` to session model. +- Add enforcement-related metric types to aggregates (e.g., `total_events`, `total_enforcement_events`). + +### Authorization & API Behavior + +- Clarify that Curbs and Events endpoints may require authorization by the public agency. +- Event authorization is recommended but not required, allowing public event data. +- Standardize 501 Not Implemented responses on all optional endpoints. +- Add `operator` query parameter to Curbs API endpoints. + +--- + +## [1.0.0] + +Initial OpenAPI 3.1 specification for CDS 1.0. See the `v1.0` branch for this version. diff --git a/V1.1_IMPLEMENTATION_PLAN.md b/V1.1_IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..8489429 --- /dev/null +++ b/V1.1_IMPLEMENTATION_PLAN.md @@ -0,0 +1,590 @@ +# CDS OpenAPI v1.1 – Implementation Plan + +This plan captures every change required to bring the `draft-v1.1` branch of the +`cds-openapi` project into full alignment with the +[CDS 1.1.0 specification](https://github.com/openmobilityfoundation/curb-data-specification) +as finalized in Q4 2025 via +[PR #201](https://github.com/openmobilityfoundation/curb-data-specification/pull/201). + +It supersedes the earlier `CHANGES_TASK_LIST.md`. + +--- + +## How to Use This Plan + +Steps are grouped into **waves**. Within a wave, every step is independent and can be +worked on in parallel by separate agents or contributors. A wave cannot start until all +steps in the previous wave are complete. + +Each step lists: +- **Depends on:** which prior steps must be done first (if any beyond the wave gate) +- **Files:** exactly which files to create or edit +- **Upstream ref:** where to look in the CDS spec for authoritative field definitions +- **Commit:** suggested commit message + +Before starting any step, read `AGENTS.MD` for style conventions and the upstream spec +section listed in **Upstream ref** for exact field names, types, and descriptions. + +--- + +## Already Done + +| Item | Commit | Status | +|------|--------|--------| +| Bump `info.version` to `1.1` in all three API YAMLs | `8e52a11` | Done | +| Update `README.md` with draft-v1.1 status | `8e52a11` | Done | +| Fix `rest_response_last-updated` → `rest_response_last_updated` refs | `8e52a11` | Done | +| Remove unused legacy file `original_CDS APIs.yaml` | `e982398` | Done | + +The following shared model files already exist on the branch but are not yet wired +into all models that need them: + +- `external_reference.yaml` — used by `curb_event` and `curb_policy` only +- `custom_attributes.yaml` — used by `curb_event`, `curb_policy`, `session`, `aggregate` +- `geoJSON_linestring.yaml` — exists but not referenced by any model + +--- + +## Pre-Existing Bugs + +These exist on the current branch and are addressed in Wave 1. + +| ID | File | Bug | Fix | +|----|------|-----|-----| +| B1 | `curb_event.yaml` line 14 | `event_type` `$ref` points to `./event_purpose.yaml` | Change to `$ref: ./event_type.yaml` | +| B2 | `vehicle_type.yaml` | Enum value `bicyle` is misspelled | Change to `bicycle` | +| B3 | `time_spans.yaml` | `days_of_week` typed as `string` instead of `array` | Change to `type: array` with `items: { type: string, enum: [sun, mon, ...] }` | + +--- + +## Wave 1 – Bug Fixes, New Schemas, and Enum Updates + +All steps in this wave are independent and can run in parallel. + +### Step 1A – Fix pre-existing bugs + +Fix bugs B1, B2, and B3 listed above. + +- **Depends on:** nothing +- **Files:** `Shared Data Models/curb_event.yaml`, `Shared Data Models/vehicle_type.yaml`, `Shared Data Models/time_spans.yaml` +- **Upstream ref:** `events/README.md` (event fields table), `general-information.md` (vehicle types), `curbs/README.md` (time spans) +- **Commit:** `fix: correct event_type ref, vehicle_type typo, and days_of_week type` + +--- + +### Step 1B – Align external_reference.yaml with v1.1 spec + +The v1.1 spec defines external references with these fields: + +- `reference_url` (string/uri, **Required**) — URL to external data feed +- `name` (string, Optional) — feed identifier +- `public` (boolean, Optional) — feed accessibility +- `identifier_name` (string, Optional) — external field name +- `ids` (array of string, Optional) — IDs from external source + +The current file uses a different structure (`reference_type`, `id`, `url`, `description`). +Reconcile with the upstream spec. + +- **Depends on:** nothing +- **Files:** `Shared Data Models/external_reference.yaml` +- **Upstream ref:** `data-types.md` (external references section), `curbs/README.md` +- **Commit:** `fix(shared): align external_reference schema with v1.1 spec` + +--- + +### Step 1C – Create payment and enforcement schemas + +Create four new shared data model files: + +**payment_channel.yaml** — string enum: +`meter`, `mobile_app`, `website`, `sms`, `telephone` + +**payment_method.yaml** — string enum: +`cash`, `credit_card`, `debit_card`, `digital_wallet`, `transit_card`, `prepaid_account`, `permit` + +**enforcement.yaml** — object: +- `enforcement_id` (UUID, Required) +- `citation_id` (string, Optional) +- `is_warning` (boolean, Optional) +- `action_taken` (string enum: `citation_registered`, `citation_posted`, `citation_served`, `citation_emailed`, Optional) +- `citation_cost` (string, Optional) +- `violations` (array of `$ref: ./violation.yaml`, Optional) + +**violation.yaml** — object: +- `violation_code` (string) +- `violation_name` (string) +- `violation_cost` (string) + +- **Depends on:** nothing +- **Files:** New files in `Shared Data Models/` +- **Upstream ref:** `events/README.md` (payment fields, enforcement object), `data-types.md` +- **Commit:** `feat(shared): add payment_channel, payment_method, enforcement, and violation schemas` + +--- + +### Step 1D – Update event_type.yaml + +Add new event types: + +- `vehicle_detected` +- `vehicle_violation_start` +- `vehicle_violation_end` +- `citation_issued` + +- **Depends on:** nothing +- **Files:** `Shared Data Models/event_type.yaml` +- **Upstream ref:** `events/README.md` (event types table) +- **Commit:** `feat(shared): add enforcement and detection event types` + +--- + +### Step 1E – Update vehicle_type.yaml (new values) + +Add MDS-aligned vehicle types (the `bicycle` typo fix is in Step 1A): + +- `bus` +- `delivery_robot` +- `scooter_standing` +- `scooter_seated` + +- **Depends on:** nothing +- **Files:** `Shared Data Models/vehicle_type.yaml` +- **Upstream ref:** `general-information.md` (vehicle types), MDS spec alignment notes +- **Commit:** `feat(shared): add MDS-aligned vehicle types` + +--- + +### Step 1F – Update propulsion_type.yaml + +Add MDS-aligned propulsion types: + +- `combustion_diesel` +- `hybrid` +- `hydrogen_fuel_cell` +- `plug_in_hybrid` + +Update the description table to include new values. + +- **Depends on:** nothing +- **Files:** `Shared Data Models/propulsion_type.yaml` +- **Upstream ref:** `general-information.md` (propulsion types) +- **Commit:** `feat(shared): add MDS-aligned propulsion types` + +--- + +### Step 1G – Update rules.yaml (user_classes + name/description) + +Enum updates: +- Replace `handicap-accessible` with `accessible` +- Add `disabled_parking_permit` + +New fields: +- Add `name` (string, Optional) +- Add `description` (string, Optional) + +- **Depends on:** nothing +- **Files:** `Shared Data Models/rules.yaml` +- **Upstream ref:** `curbs/README.md` (rules table, user classes) +- **Commit:** `feat(shared): update rules with name, description, and accessibility user_classes` + +--- + +### Step 1H – Update curb_policy.yaml + +Add: +- `name` (string, Optional) +- `description` (string, Optional) +- `primary_color` (string, Optional — hex RGB for visual representation) + +Update `priority` description: "Two overlapping policies MAY have the same priority IF +they apply to disjoint User Classes OR disjoint Activities." + +- **Depends on:** nothing +- **Files:** `Shared Data Models/curb_policy.yaml` +- **Upstream ref:** `curbs/README.md` (policies table) +- **Commit:** `feat(shared): add name, description, primary_color to curb_policy` + +--- + +### Step 1I – Update time_spans.yaml (weeks_of_month) + +After the `days_of_week` array fix in Step 1A: +- Add `weeks_of_month` (array of integers 1–5) +- Review and update `designated_period_except` description per v1.1 + +Note: This step edits the same file as 1A. If running in parallel, coordinate +or combine into a single step. + +- **Depends on:** Step 1A (same file) +- **Files:** `Shared Data Models/time_spans.yaml` +- **Upstream ref:** `curbs/README.md` (time spans table) +- **Commit:** `feat(shared): add weeks_of_month to time_spans` + +--- + +## Wave 2 – Model Wiring and New Models + +All steps in this wave are independent and can run in parallel. +Requires Wave 1 to be complete (schemas they reference must exist). + +### Step 2A – Wire shared schemas into curb_zone.yaml + +Add: +- `external_references` (array of `$ref: ./external_reference.yaml`) +- `custom_attributes` (`$ref: ./custom_attributes.yaml`) +- `geometry_line` (`$ref: ./geoJSON_linestring.yaml`) — optional LineString geometry +- `curb_object_ids` (array of UUID strings) +- `description` (string, Optional) +- `jurisdiction_type` (string enum: `public`, `private`, `other`) +- `owner_name` (string, Optional) +- `address_number` (string, Optional) + +- **Depends on:** Step 1B (external_reference alignment) +- **Files:** `Shared Data Models/curb_zone.yaml` +- **Upstream ref:** `curbs/README.md` (curb zones table) +- **Commit:** `feat(shared): add LineString, objects, jurisdiction, context, external refs to curb_zone` + +--- + +### Step 2B – Wire shared schemas into curb_area.yaml + +Add: +- `external_references` (array of `$ref: ./external_reference.yaml`) +- `custom_attributes` (`$ref: ./custom_attributes.yaml`) + +- **Depends on:** Step 1B (external_reference alignment) +- **Files:** `Shared Data Models/curb_area.yaml` +- **Upstream ref:** `curbs/README.md` (curb areas table) +- **Commit:** `feat(shared): add external_references and custom_attributes to curb_area` + +--- + +### Step 2C – Wire shared schemas into curb_space.yaml + +Add: +- `external_references` (array of `$ref: ./external_reference.yaml`) +- `custom_attributes` (`$ref: ./custom_attributes.yaml`) +- `curb_object_ids` (array of UUID strings, conditionally required) + +- **Depends on:** Step 1B (external_reference alignment) +- **Files:** `Shared Data Models/curb_space.yaml` +- **Upstream ref:** `curbs/README.md` (curb spaces table) +- **Commit:** `feat(shared): add external_references, custom_attributes, curb_object_ids to curb_space` + +--- + +### Step 2D – Create curb_object.yaml + +New shared data model for physical curb infrastructure. + +**Required fields:** +- `curb_object_id` (UUID) +- `curb_object_type` (string — well-known values: bollard, planter, camera, pay_station, locker, etc.) +- `geometry` (`$ref: ./geoJSON_point.yaml`) +- `published_date` (`$ref: ./timestamp.yaml`) +- `last_updated_date` (`$ref: ./timestamp.yaml`) + +**Optional fields:** +- `name`, `description`, `owner`, `operator` (strings) +- `external_object_id` (string) +- `external_url` (string, format: uri) +- `curb_policy_id` (string, format: uuid) +- `geometry_line` (`$ref: ./geoJSON_linestring.yaml`) +- `external_references` (array of `$ref: ./external_reference.yaml`) +- `custom_attributes` (`$ref: ./custom_attributes.yaml`) + +- **Depends on:** Step 1B (external_reference alignment) +- **Files:** New `Shared Data Models/curb_object.yaml` +- **Upstream ref:** `curbs/README.md` (curb objects section) +- **Commit:** `feat(shared): add Curb Object data model` + +--- + +### Step 2E – Extend curb_event.yaml + +Add: + +**Payment:** +- `payment_channel` (`$ref: ./payment_channel.yaml`) +- `payment_method` (`$ref: ./payment_method.yaml`) +- `payment_transaction_id` (string) + +**Enforcement:** +- `enforcement` (`$ref: ./enforcement.yaml`) + +**Object reference:** +- `curb_object_id` (string, format: uuid) + +**Vehicle / Computer Vision:** +- `vehicle_type` (`$ref: ./vehicle_type.yaml`) +- `vehicle_license_plate_jurisdiction` (string) +- `vehicle_license_plate_confidence` (number, 0–1) +- `vehicle_type_confidence` (number, 0–1) +- `vehicle_color` (string) +- `vehicle_color_confidence` (number, 0–1) +- `vehicle_company_name` (string) +- `vehicle_company_name_confidence` (number, 0–1) +- `vehicle_run_id` (string) +- `vehicle_run_id_confidence` (number, 0–1) + +**Data source:** +- `data_source_device_name` (string) + +**Required fields:** review whether `event_publication_time` should be added to `required`. + +- **Depends on:** Steps 1A (bug fix), 1C (payment/enforcement schemas) +- **Files:** `Shared Data Models/curb_event.yaml` +- **Upstream ref:** `events/README.md` (event fields table) +- **Commit:** `feat(shared): add payment, enforcement, vehicle CV, and object ref to curb_event` + +--- + +### Step 2F – Update session.yaml + +Add: +- `curb_object_id` (string, format: uuid, Optional) + +- **Depends on:** nothing beyond Wave 1 gate +- **Files:** `Shared Data Models/session.yaml` +- **Upstream ref:** `metrics/README.md` (sessions table) +- **Commit:** `feat(shared): add curb_object_id to session model` + +--- + +### Step 2G – Update aggregate.yaml + +Add enforcement-related metric types to the `metric_type` enum (e.g., `total_events`, `total_enforcement_events`). + +Review v1.1 spec for any additional aggregate fields. + +- **Depends on:** nothing beyond Wave 1 gate +- **Files:** `Shared Data Models/aggregate.yaml` +- **Upstream ref:** `metrics/README.md` (aggregates table, methodology) +- **Commit:** `feat(shared): add enforcement metric types to aggregate model` + +--- + +## Wave 3 – API Endpoint Changes + +All steps in this wave are independent and can run in parallel. +Requires Wave 2 to be complete (models they reference must exist). + +### Step 3A – Curbs API: Add object endpoints + +Add to `Curbs API.yaml`: + +- `GET /curbs/objects` — list curb objects + - Query params: `area` (UUID), `zone` (UUID), `space` (UUID), `time` (timestamp), `operator` (string), plus existing geo params (bbox and lat/lng/radius) + - Response: standard wrapper with `data.objects` array of `$ref: curb_object.yaml` + - 501 response for optional endpoint +- `GET /curbs/objects/{id}` — get single curb object + - Path param: `id` (required) + - Response: standard wrapper with `data` as single `$ref: curb_object.yaml` + - 501 response + +- **Depends on:** Step 2D (curb_object model) +- **Files:** `APIs/Curbs API.yaml` +- **Upstream ref:** `curbs/README.md` (query curb objects endpoint) +- **Commit:** `feat(curbs): add GET /curbs/objects and GET /curbs/objects/{id} endpoints` + +--- + +### Step 3B – Curbs API: Add operator query parameter + +Add `operator` query parameter to zones, spaces, and areas endpoints. + +- **Depends on:** nothing beyond Wave 2 gate +- **Files:** `APIs/Curbs API.yaml` +- **Upstream ref:** `curbs/README.md` (query parameters) +- **Commit:** `feat(curbs): add operator query parameter to zone/space/area endpoints` + +--- + +### Step 3C – Events API: Add event_time query parameter + +Add `event_time` to `GET /events/events`: +- Type: string +- Format: ISO 8601 UTC hour (`YYYY-MM-DDTHH`) +- Description: returns all events within that hour; default when omitted is last 60 minutes + +- **Depends on:** nothing beyond Wave 2 gate +- **Files:** `APIs/Events API.yaml` +- **Upstream ref:** `events/README.md` (query events endpoint) +- **Commit:** `feat(events): add event_time query parameter` + +--- + +### Step 3D – Events API: Add POST /events/events endpoint + +Add optional push endpoint: +- Request body: array of curb_event objects +- Responses: 201 Created, 501 Not Implemented +- Description: optional real-time push endpoint + +- **Depends on:** Step 2E (curb_event updates) +- **Files:** `APIs/Events API.yaml` +- **Upstream ref:** `events/README.md` (POST events section) +- **Commit:** `feat(events): add POST /events/events endpoint for real-time push` + +--- + +### Step 3E – Events API: Authorization guidance + +Update endpoint descriptions to note: +- Event endpoints may require authorization by the public agency +- Authorization is **recommended** but not strictly required + +- **Depends on:** nothing beyond Wave 2 gate +- **Files:** `APIs/Events API.yaml` +- **Upstream ref:** `events/README.md` (authorization section) +- **Commit:** `docs(events): add authorization guidance` + +--- + +### Step 3F – Metrics API: Update responses + +Ensure session and aggregate response schemas reflect updated models. Add 501 +descriptions consistently on optional endpoints. + +- **Depends on:** Steps 2F, 2G (session/aggregate updates) +- **Files:** `APIs/Metrics API.yaml` +- **Upstream ref:** `metrics/README.md` +- **Commit:** `feat(metrics): update response schemas for v1.1 model changes` + +--- + +## Wave 4 – Cross-Cutting Polish + +All steps in this wave are independent and can run in parallel. +Requires Wave 3 to be complete. + +### Step 4A – Authorization and 501 standardization (all APIs) + +Across all three API YAMLs: +- Ensure Curbs API includes authorization descriptions where specified +- Confirm 501 Not Implemented is consistent on all optional endpoints +- Add pagination preference language from the spec if applicable + +- **Depends on:** nothing beyond Wave 3 gate +- **Files:** All three API YAMLs +- **Upstream ref:** `general-information.md` (authorization, pagination) +- **Commit:** `docs: standardize authorization and 501 behavior across all APIs` + +--- + +### Step 4B – Update toc.json + +Add entries for all new shared models under the appropriate sections: + +- Curb Object (under "CDS Models") +- Enforcement (under "Shared Models") +- Violation (under "Shared Models") +- Payment Channel (under "Shared Models") +- Payment Method (under "Shared Models") + +- **Depends on:** nothing beyond Wave 3 gate +- **Files:** `toc.json` +- **Upstream ref:** n/a +- **Commit:** `chore: add new v1.1 models to toc.json` + +--- + +### Step 4C – Validate and lint + +- Run `.spectral.mjs` linter and fix any errors +- Spot-check all `$ref` targets for correct paths +- Verify every new field has a `description` +- Verify `required` arrays are correct for all updated models +- Cross-check every field against the upstream v1.1 spec for completeness + +- **Depends on:** Steps 4A, 4B +- **Files:** All files +- **Commit:** `chore: lint, validate, and polish OpenAPI spec for v1.1` + +--- + +## Dependency Graph + +``` +Wave 1 (all parallel): + 1A Bug fixes (curb_event, vehicle_type, time_spans) + 1B Align external_reference schema + 1C Create payment/enforcement schemas (4 new files) + 1D Update event_type enum + 1E Update vehicle_type enum + 1F Update propulsion_type enum + 1G Update rules (user_classes, name, description) + 1H Update curb_policy (name, description, color) + 1I Update time_spans (weeks_of_month) [coordinate with 1A] + │ + ▼ +Wave 2 (all parallel): + 2A Wire curb_zone (depends on 1B) + 2B Wire curb_area (depends on 1B) + 2C Wire curb_space (depends on 1B) + 2D Create curb_object model (depends on 1B) + 2E Extend curb_event (depends on 1A, 1C) + 2F Update session + 2G Update aggregate + │ + ▼ +Wave 3 (all parallel): + 3A Curbs API: object endpoints (depends on 2D) + 3B Curbs API: operator param + 3C Events API: event_time param + 3D Events API: POST endpoint (depends on 2E) + 3E Events API: auth guidance + 3F Metrics API: responses (depends on 2F, 2G) + │ + ▼ +Wave 4 (all parallel, then 4C last): + 4A Auth + 501 standardization + 4B Update toc.json + 4C Lint and validate (depends on 4A, 4B) +``` + +--- + +## Summary Table + +| Step | Focus | Key Files | Wave | +|------|-------|-----------|------| +| 1A | Fix bugs (event_type ref, bicyle, days_of_week) | curb_event, vehicle_type, time_spans | 1 | +| 1B | Align external_reference schema | external_reference.yaml | 1 | +| 1C | Create payment + enforcement schemas | 4 new files | 1 | +| 1D | New event types | event_type.yaml | 1 | +| 1E | New vehicle types (MDS) | vehicle_type.yaml | 1 | +| 1F | New propulsion types (MDS) | propulsion_type.yaml | 1 | +| 1G | Rules: user_classes, name, description | rules.yaml | 1 | +| 1H | Policy: name, description, color | curb_policy.yaml | 1 | +| 1I | Time spans: weeks_of_month | time_spans.yaml | 1 | +| 2A | Zone: LineString, objects, context, refs | curb_zone.yaml | 2 | +| 2B | Area: external refs, custom attrs | curb_area.yaml | 2 | +| 2C | Space: object IDs, refs, attrs | curb_space.yaml | 2 | +| 2D | Curb Object model (new) | curb_object.yaml | 2 | +| 2E | Event: payment, enforcement, CV | curb_event.yaml | 2 | +| 2F | Session: object ref | session.yaml | 2 | +| 2G | Aggregate: enforcement metrics | aggregate.yaml | 2 | +| 3A | Curbs API: object endpoints | Curbs API.yaml | 3 | +| 3B | Curbs API: operator param | Curbs API.yaml | 3 | +| 3C | Events API: event_time param | Events API.yaml | 3 | +| 3D | Events API: POST endpoint | Events API.yaml | 3 | +| 3E | Events API: auth guidance | Events API.yaml | 3 | +| 3F | Metrics API: responses | Metrics API.yaml | 3 | +| 4A | Auth + 501 across all APIs | All 3 API YAMLs | 4 | +| 4B | Update toc.json | toc.json | 4 | +| 4C | Lint and validate | All files | 4 | + +--- + +## Differences From Prior Analysis + +The following issues were found in the earlier `CHANGES_TASK_LIST.md`: + +**Pre-existing bugs not captured:** B1 (event_type wrong $ref), B2 (bicyle typo), B3 (days_of_week wrong type). + +**Items said to "add" that already exist as files:** `external_reference.yaml`, `custom_attributes.yaml`, `geoJSON_linestring.yaml` — the real task is wiring them into models that don't reference them yet and aligning the external_reference schema with v1.1. + +**Missing from prior analysis:** new event types (vehicle_detected, enforcement events), enforcement/violation object schemas, payment enum schemas, 8+ vehicle CV confidence fields, data_source_device_name, accessibility terminology updates, external_reference schema reconciliation, toc.json updates, curb_object_ids on zone and space, jurisdiction_type/owner_name/address_number on zone. + +**Items that were vague:** "enforcement-related fields" is now a full Enforcement object with nested Violations; "vehicle properties" is now 8 specific CV confidence fields; "data feed metadata" is folded into external_references (no separate model needed). From dd466a3ad329f80335cf3bb5af22c279db80a8b8 Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 11:15:24 -0400 Subject: [PATCH 08/26] feat(shared): add external_reference, custom_attributes, and LineString schemas; wire into event, policy, session, aggregate models and toc.json Co-Authored-By: Claude Opus 4.6 --- Shared Data Models/aggregate.yaml | 5 ++++ Shared Data Models/curb_event.yaml | 12 +++++++++ Shared Data Models/curb_policy.yaml | 8 ++++++ Shared Data Models/custom_attributes.yaml | 15 +++++++++++ Shared Data Models/external_reference.yaml | 30 ++++++++++++++++++++++ Shared Data Models/geoJSON_linestring.yaml | 29 +++++++++++++++++++++ Shared Data Models/session.yaml | 5 ++++ toc.json | 15 +++++++++++ 8 files changed, 119 insertions(+) create mode 100644 Shared Data Models/custom_attributes.yaml create mode 100644 Shared Data Models/external_reference.yaml create mode 100644 Shared Data Models/geoJSON_linestring.yaml diff --git a/Shared Data Models/aggregate.yaml b/Shared Data Models/aggregate.yaml index 12541a3..c495f50 100644 --- a/Shared Data Models/aggregate.yaml +++ b/Shared Data Models/aggregate.yaml @@ -44,3 +44,8 @@ properties: x-stoplight: id: 2r1yhfhxhsuym description: 'The results of the calculations for this metric from the [Methodology](https://github.com/openmobilityfoundation/curb-data-specification/tree/main/metrics#methodology). Note that "-1" means that the sensor/source was offline for the majority of the time.' + custom_attributes: + $ref: ./custom_attributes.yaml + x-stoplight: + id: 3n7k2b5h9qx4z + description: Implementation-specific custom attributes for this aggregate record. diff --git a/Shared Data Models/curb_event.yaml b/Shared Data Models/curb_event.yaml index 2666046..63f83ff 100644 --- a/Shared Data Models/curb_event.yaml +++ b/Shared Data Models/curb_event.yaml @@ -163,6 +163,18 @@ properties: id: aifcs5wjnhqhu description: 'If available from the source, the actual cost, in the currency defined in currency, paid by the curb user for this event. The currency type is sent in with the REST Endpoints JSON object. All costs should be given as integers in the currency''s smallest unit. As an example, to represent $1 USD, specify an amount of 100 (for 100 cents).' example: 100 + external_references: + type: array + x-stoplight: + id: 7p2n4a8k3jw0c + description: External references that link this event to other systems or specifications. + items: + $ref: ./external_reference.yaml + custom_attributes: + $ref: ./custom_attributes.yaml + x-stoplight: + id: 4l3d9m8q2sk1v + description: Implementation-specific custom attributes for this event. required: - event_id - event_location diff --git a/Shared Data Models/curb_policy.yaml b/Shared Data Models/curb_policy.yaml index ebd2998..b694647 100644 --- a/Shared Data Models/curb_policy.yaml +++ b/Shared Data Models/curb_policy.yaml @@ -42,6 +42,14 @@ properties: **Values are defined globally and are managed by the Open Mobility Foundation and operators may register [here](https://github.com/openmobilityfoundation/curb-data-specification/wiki/Adding-a-CDS-Data-Source-Operator-ID)** items: $ref: ./operator_id.yaml + external_references: + type: array + description: External references that link this policy to other systems or specifications. + items: + $ref: ./external_reference.yaml + custom_attributes: + $ref: ./custom_attributes.yaml + description: Implementation-specific custom attributes for this policy. required: - curb_policy_id - published_date diff --git a/Shared Data Models/custom_attributes.yaml b/Shared Data Models/custom_attributes.yaml new file mode 100644 index 0000000..5fa8e7d --- /dev/null +++ b/Shared Data Models/custom_attributes.yaml @@ -0,0 +1,15 @@ +title: Custom Attributes +type: object +description: > + Implementation-specific custom attributes for this object. + Publishers MAY use any keys and value structures appropriate to their use case. + A simple `"key": "value"` structure is provided as an example only and does NOT + restrict the allowed shapes of `custom_attributes`. +additionalProperties: true +examples: + - color: "blue" + kiosk_id: "K-1234" + internal_tag: + source: "legacy_system" + code: 42 + diff --git a/Shared Data Models/external_reference.yaml b/Shared Data Models/external_reference.yaml new file mode 100644 index 0000000..b173412 --- /dev/null +++ b/Shared Data Models/external_reference.yaml @@ -0,0 +1,30 @@ +title: External Reference +type: object +description: > + A reusable structure for linking CDS objects to external systems, specifications, + or resources such as WZDx, CWZ, MDS, GBFS, GTFS, or public URLs and documents. +properties: + reference_type: + type: string + description: > + The external system or specification this reference points to + (for example, "WZDx", "CWZ", "MDS", "GBFS", "GTFS", "website", "document"). + id: + type: string + description: Identifier for this object within the external system. + url: + type: string + format: uri + description: Optional URL for the external resource when available. + description: + type: string + description: Optional human-readable description of this external reference. +required: + - reference_type + - id +examples: + - reference_type: wzdx + id: "road_event_feature_1234" + url: "https://example.com/wzdx/features/road_event_feature_1234" + description: "Linked WZDx road event feature" + diff --git a/Shared Data Models/geoJSON_linestring.yaml b/Shared Data Models/geoJSON_linestring.yaml new file mode 100644 index 0000000..07539ff --- /dev/null +++ b/Shared Data Models/geoJSON_linestring.yaml @@ -0,0 +1,29 @@ +title: GeoJSON LineString +type: object +description: > + The spatial extent of this location represented as a GeoJSON geometry of type + "LineString" as defined in RFC 7946 3.1.4. +properties: + type: + type: string + description: A GeoJSON type. + example: LineString + coordinates: + type: array + description: > + An array of positions for this geometry. Each position is an array of two + numbers representing longitude and latitude. + items: + type: array + items: + type: number +required: + - type + - coordinates +examples: + - type: LineString + coordinates: + - [-73.982105, 40.767932] + - [-73.973694, 40.764551] + - [-73.949318, 40.796918] + diff --git a/Shared Data Models/session.yaml b/Shared Data Models/session.yaml index 2403638..cc0d220 100644 --- a/Shared Data Models/session.yaml +++ b/Shared Data Models/session.yaml @@ -88,5 +88,10 @@ properties: x-stoplight: id: 07zos4ceswa84 description: Type of the vehicle that performed the event. Conditionally required for sources capable of determining vehicle type. + custom_attributes: + $ref: ./custom_attributes.yaml + x-stoplight: + id: 1c9m4p7s2vd8k + description: Implementation-specific custom attributes for this session. required: - session_type diff --git a/toc.json b/toc.json index 3a67ddd..41faf5f 100644 --- a/toc.json +++ b/toc.json @@ -58,6 +58,11 @@ "title": "Aggregate", "uri": "/Shared Data Models/aggregate.yaml" }, + { + "type": "item", + "title": "Custom Attributes", + "uri": "/Shared Data Models/custom_attributes.yaml" + }, { "type": "item", "title": "Coordinates", @@ -88,6 +93,11 @@ "title": "Event Type", "uri": "/Shared Data Models/event_type.yaml" }, + { + "type": "item", + "title": "External Reference", + "uri": "/Shared Data Models/external_reference.yaml" + }, { "type": "item", "title": "GeoJSON Point", @@ -98,6 +108,11 @@ "title": "GeoJSON Polygon", "uri": "/Shared Data Models/geoJSON_polygon.yaml" }, + { + "type": "item", + "title": "GeoJSON LineString", + "uri": "/Shared Data Models/geoJSON_linestring.yaml" + }, { "type": "item", "title": "Lane Type", From 9bda37cc64ab83c550f54df47549891e15686af4 Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 11:24:52 -0400 Subject: [PATCH 09/26] docs: restructure v1.1 implementation plan with wave-based parallel execution and inline progress tracking --- V1.1_IMPLEMENTATION_PLAN.md | 265 ++++++++++++++++++++---------------- 1 file changed, 149 insertions(+), 116 deletions(-) diff --git a/V1.1_IMPLEMENTATION_PLAN.md b/V1.1_IMPLEMENTATION_PLAN.md index 8489429..e3f7279 100644 --- a/V1.1_IMPLEMENTATION_PLAN.md +++ b/V1.1_IMPLEMENTATION_PLAN.md @@ -1,13 +1,11 @@ # CDS OpenAPI v1.1 – Implementation Plan -This plan captures every change required to bring the `draft-v1.1` branch of the -`cds-openapi` project into full alignment with the +This plan captures every change required to bring the `cds-openapi` project from +CDS 1.0 to full alignment with the [CDS 1.1.0 specification](https://github.com/openmobilityfoundation/curb-data-specification) as finalized in Q4 2025 via [PR #201](https://github.com/openmobilityfoundation/curb-data-specification/pull/201). -It supersedes the earlier `CHANGES_TASK_LIST.md`. - --- ## How to Use This Plan @@ -17,6 +15,7 @@ worked on in parallel by separate agents or contributors. A wave cannot start un steps in the previous wave are complete. Each step lists: +- **Status:** Done, Partial, or TODO - **Depends on:** which prior steps must be done first (if any beyond the wave gate) - **Files:** exactly which files to create or edit - **Upstream ref:** where to look in the CDS spec for authoritative field definitions @@ -25,29 +24,17 @@ Each step lists: Before starting any step, read `AGENTS.MD` for style conventions and the upstream spec section listed in **Upstream ref** for exact field names, types, and descriptions. ---- - -## Already Done - -| Item | Commit | Status | -|------|--------|--------| -| Bump `info.version` to `1.1` in all three API YAMLs | `8e52a11` | Done | -| Update `README.md` with draft-v1.1 status | `8e52a11` | Done | -| Fix `rest_response_last-updated` → `rest_response_last_updated` refs | `8e52a11` | Done | -| Remove unused legacy file `original_CDS APIs.yaml` | `e982398` | Done | - -The following shared model files already exist on the branch but are not yet wired -into all models that need them: +### Status Legend -- `external_reference.yaml` — used by `curb_event` and `curb_policy` only -- `custom_attributes.yaml` — used by `curb_event`, `curb_policy`, `session`, `aggregate` -- `geoJSON_linestring.yaml` — exists but not referenced by any model +- ~~Strikethrough~~ = Done (committed) +- **Partial** = some sub-tasks complete (committed or uncommitted), remainder noted +- Unmarked = TODO --- -## Pre-Existing Bugs +## Pre-Existing Bugs (v1.0 branch) -These exist on the current branch and are addressed in Wave 1. +These bugs exist on the v1.0 branch and are addressed in Wave 1. | ID | File | Bug | Fix | |----|------|-----|-----| @@ -57,11 +44,51 @@ These exist on the current branch and are addressed in Wave 1. --- +## Wave 0 – Housekeeping and Setup + +All steps in this wave are independent. + +### ~~Step 0A – Version bump and README~~ ✅ + +~~Bump `info.version` to `1.1` in all three API YAMLs. Update `README.md` with draft-v1.1 status and date.~~ + +- ~~**Files:** `APIs/Curbs API.yaml`, `APIs/Events API.yaml`, `APIs/Metrics API.yaml`, `README.md`~~ +- ~~**Commit:** `8e52a11` — `API version bump, README date, fix last_updated refs`~~ + +--- + +### ~~Step 0B – Fix last_updated ref typo~~ ✅ + +~~Replace `rest_response_last-updated.yaml` with `rest_response_last_updated.yaml` in all API files.~~ + +- ~~**Files:** `APIs/Curbs API.yaml`, `APIs/Events API.yaml`~~ +- ~~**Commit:** `8e52a11` (combined with 0A)~~ + +--- + +### ~~Step 0C – Remove unused legacy file~~ ✅ + +~~Delete `original_CDS APIs.yaml`.~~ + +- ~~**Files:** `original_CDS APIs.yaml` (deleted)~~ +- ~~**Commit:** `e982398` — `Removes old file that isn't in use`~~ + +--- + +### ~~Step 0D – Project documentation~~ ✅ + +~~Create `AGENTS.MD`, `CLAUDE.md`, `CHANGELOG.MD`, and `V1.1_IMPLEMENTATION_PLAN.md`.~~ + +- ~~**Files:** `AGENTS.MD`, `CLAUDE.md`, `CHANGELOG.MD`, `V1.1_IMPLEMENTATION_PLAN.md`~~ +- ~~**Commits:** `901f305`, `52418c2`, `94fbcdf`~~ + +--- + ## Wave 1 – Bug Fixes, New Schemas, and Enum Updates All steps in this wave are independent and can run in parallel. -### Step 1A – Fix pre-existing bugs +### Step 1A – Fix pre-existing bugs — TODO Fix bugs B1, B2, and B3 listed above. @@ -72,7 +99,7 @@ Fix bugs B1, B2, and B3 listed above. --- -### Step 1B – Align external_reference.yaml with v1.1 spec +### Step 1B – Align external_reference.yaml with v1.1 spec — Partial The v1.1 spec defines external references with these fields: @@ -82,8 +109,11 @@ The v1.1 spec defines external references with these fields: - `identifier_name` (string, Optional) — external field name - `ids` (array of string, Optional) — IDs from external source -The current file uses a different structure (`reference_type`, `id`, `url`, `description`). -Reconcile with the upstream spec. +The file exists (uncommitted) but uses a different structure (`reference_type`, `id`, +`url`, `description`). Reconcile with the upstream spec. + +**What exists (uncommitted):** `external_reference.yaml` file created with v1.0-era schema. +**Remaining:** Update field names and structure to match v1.1 spec. - **Depends on:** nothing - **Files:** `Shared Data Models/external_reference.yaml` @@ -92,17 +122,19 @@ Reconcile with the upstream spec. --- -### Step 1C – Create payment and enforcement schemas +### Step 1C – Create shared schemas (custom_attributes, geoJSON_linestring, payment, enforcement) — Partial -Create four new shared data model files: +**custom_attributes.yaml** — Done (uncommitted). File exists with `additionalProperties: true`. -**payment_channel.yaml** — string enum: +**geoJSON_linestring.yaml** — Done (uncommitted). File exists with LineString geometry definition. + +**payment_channel.yaml** — TODO. String enum: `meter`, `mobile_app`, `website`, `sms`, `telephone` -**payment_method.yaml** — string enum: +**payment_method.yaml** — TODO. String enum: `cash`, `credit_card`, `debit_card`, `digital_wallet`, `transit_card`, `prepaid_account`, `permit` -**enforcement.yaml** — object: +**enforcement.yaml** — TODO. Object: - `enforcement_id` (UUID, Required) - `citation_id` (string, Optional) - `is_warning` (boolean, Optional) @@ -110,7 +142,7 @@ Create four new shared data model files: - `citation_cost` (string, Optional) - `violations` (array of `$ref: ./violation.yaml`, Optional) -**violation.yaml** — object: +**violation.yaml** — TODO. Object: - `violation_code` (string) - `violation_name` (string) - `violation_cost` (string) @@ -122,7 +154,7 @@ Create four new shared data model files: --- -### Step 1D – Update event_type.yaml +### Step 1D – Update event_type.yaml — TODO Add new event types: @@ -138,7 +170,7 @@ Add new event types: --- -### Step 1E – Update vehicle_type.yaml (new values) +### Step 1E – Update vehicle_type.yaml (new values) — TODO Add MDS-aligned vehicle types (the `bicycle` typo fix is in Step 1A): @@ -154,7 +186,7 @@ Add MDS-aligned vehicle types (the `bicycle` typo fix is in Step 1A): --- -### Step 1F – Update propulsion_type.yaml +### Step 1F – Update propulsion_type.yaml — TODO Add MDS-aligned propulsion types: @@ -172,7 +204,7 @@ Update the description table to include new values. --- -### Step 1G – Update rules.yaml (user_classes + name/description) +### Step 1G – Update rules.yaml (user_classes + name/description) — TODO Enum updates: - Replace `handicap-accessible` with `accessible` @@ -189,15 +221,14 @@ New fields: --- -### Step 1H – Update curb_policy.yaml +### Step 1H – Update curb_policy.yaml (name, description, color) — Partial -Add: -- `name` (string, Optional) -- `description` (string, Optional) -- `primary_color` (string, Optional — hex RGB for visual representation) - -Update `priority` description: "Two overlapping policies MAY have the same priority IF -they apply to disjoint User Classes OR disjoint Activities." +**What exists (uncommitted):** `external_references` and `custom_attributes` fields already added. +**Remaining:** +- Add `name` (string, Optional) +- Add `description` (string, Optional) +- Add `primary_color` (string, Optional — hex RGB for visual representation) +- Update `priority` description: "Two overlapping policies MAY have the same priority IF they apply to disjoint User Classes OR disjoint Activities." - **Depends on:** nothing - **Files:** `Shared Data Models/curb_policy.yaml` @@ -206,7 +237,7 @@ they apply to disjoint User Classes OR disjoint Activities." --- -### Step 1I – Update time_spans.yaml (weeks_of_month) +### Step 1I – Update time_spans.yaml (weeks_of_month) — TODO After the `days_of_week` array fix in Step 1A: - Add `weeks_of_month` (array of integers 1–5) @@ -227,7 +258,7 @@ or combine into a single step. All steps in this wave are independent and can run in parallel. Requires Wave 1 to be complete (schemas they reference must exist). -### Step 2A – Wire shared schemas into curb_zone.yaml +### Step 2A – Wire shared schemas into curb_zone.yaml — TODO Add: - `external_references` (array of `$ref: ./external_reference.yaml`) @@ -246,7 +277,7 @@ Add: --- -### Step 2B – Wire shared schemas into curb_area.yaml +### Step 2B – Wire shared schemas into curb_area.yaml — TODO Add: - `external_references` (array of `$ref: ./external_reference.yaml`) @@ -259,7 +290,7 @@ Add: --- -### Step 2C – Wire shared schemas into curb_space.yaml +### Step 2C – Wire shared schemas into curb_space.yaml — TODO Add: - `external_references` (array of `$ref: ./external_reference.yaml`) @@ -273,7 +304,7 @@ Add: --- -### Step 2D – Create curb_object.yaml +### Step 2D – Create curb_object.yaml — TODO New shared data model for physical curb infrastructure. @@ -300,9 +331,10 @@ New shared data model for physical curb infrastructure. --- -### Step 2E – Extend curb_event.yaml +### Step 2E – Extend curb_event.yaml — Partial -Add: +**What exists (uncommitted):** `external_references` and `custom_attributes` fields already added. +**Remaining:** **Payment:** - `payment_channel` (`$ref: ./payment_channel.yaml`) @@ -339,10 +371,11 @@ Add: --- -### Step 2F – Update session.yaml +### Step 2F – Update session.yaml — Partial -Add: -- `curb_object_id` (string, format: uuid, Optional) +**What exists (uncommitted):** `custom_attributes` field already added. +**Remaining:** +- Add `curb_object_id` (string, format: uuid, Optional) - **Depends on:** nothing beyond Wave 1 gate - **Files:** `Shared Data Models/session.yaml` @@ -351,11 +384,12 @@ Add: --- -### Step 2G – Update aggregate.yaml - -Add enforcement-related metric types to the `metric_type` enum (e.g., `total_events`, `total_enforcement_events`). +### Step 2G – Update aggregate.yaml — Partial -Review v1.1 spec for any additional aggregate fields. +**What exists (uncommitted):** `custom_attributes` field already added. +**Remaining:** +- Add enforcement-related metric types to the `metric_type` enum (e.g., `total_events`, `total_enforcement_events`) +- Review v1.1 spec for any additional aggregate fields - **Depends on:** nothing beyond Wave 1 gate - **Files:** `Shared Data Models/aggregate.yaml` @@ -369,7 +403,7 @@ Review v1.1 spec for any additional aggregate fields. All steps in this wave are independent and can run in parallel. Requires Wave 2 to be complete (models they reference must exist). -### Step 3A – Curbs API: Add object endpoints +### Step 3A – Curbs API: Add object endpoints — TODO Add to `Curbs API.yaml`: @@ -389,7 +423,7 @@ Add to `Curbs API.yaml`: --- -### Step 3B – Curbs API: Add operator query parameter +### Step 3B – Curbs API: Add operator query parameter — TODO Add `operator` query parameter to zones, spaces, and areas endpoints. @@ -400,7 +434,7 @@ Add `operator` query parameter to zones, spaces, and areas endpoints. --- -### Step 3C – Events API: Add event_time query parameter +### Step 3C – Events API: Add event_time query parameter — TODO Add `event_time` to `GET /events/events`: - Type: string @@ -414,7 +448,7 @@ Add `event_time` to `GET /events/events`: --- -### Step 3D – Events API: Add POST /events/events endpoint +### Step 3D – Events API: Add POST /events/events endpoint — TODO Add optional push endpoint: - Request body: array of curb_event objects @@ -428,7 +462,7 @@ Add optional push endpoint: --- -### Step 3E – Events API: Authorization guidance +### Step 3E – Events API: Authorization guidance — TODO Update endpoint descriptions to note: - Event endpoints may require authorization by the public agency @@ -441,7 +475,7 @@ Update endpoint descriptions to note: --- -### Step 3F – Metrics API: Update responses +### Step 3F – Metrics API: Update responses — TODO Ensure session and aggregate response schemas reflect updated models. Add 501 descriptions consistently on optional endpoints. @@ -458,7 +492,7 @@ descriptions consistently on optional endpoints. All steps in this wave are independent and can run in parallel. Requires Wave 3 to be complete. -### Step 4A – Authorization and 501 standardization (all APIs) +### Step 4A – Authorization and 501 standardization (all APIs) — TODO Across all three API YAMLs: - Ensure Curbs API includes authorization descriptions where specified @@ -472,9 +506,10 @@ Across all three API YAMLs: --- -### Step 4B – Update toc.json +### Step 4B – Update toc.json — Partial -Add entries for all new shared models under the appropriate sections: +**What exists (uncommitted):** Entries added for Custom Attributes, External Reference, GeoJSON LineString. +**Remaining:** Add entries for: - Curb Object (under "CDS Models") - Enforcement (under "Shared Models") @@ -489,7 +524,7 @@ Add entries for all new shared models under the appropriate sections: --- -### Step 4C – Validate and lint +### Step 4C – Validate and lint — TODO - Run `.spectral.mjs` linter and fix any errors - Spot-check all `$ref` targets for correct paths @@ -506,15 +541,23 @@ Add entries for all new shared models under the appropriate sections: ## Dependency Graph ``` +Wave 0 (all parallel): ✅ ALL DONE + 0A Version bump + README + 0B Fix last_updated ref typo + 0C Remove legacy file + 0D Project documentation + │ + ▼ Wave 1 (all parallel): 1A Bug fixes (curb_event, vehicle_type, time_spans) - 1B Align external_reference schema - 1C Create payment/enforcement schemas (4 new files) + 1B Align external_reference schema [Partial] + 1C Create shared schemas (custom_attrs, linestring, [Partial] + payment, enforcement) 1D Update event_type enum 1E Update vehicle_type enum 1F Update propulsion_type enum 1G Update rules (user_classes, name, description) - 1H Update curb_policy (name, description, color) + 1H Update curb_policy (name, description, color) [Partial] 1I Update time_spans (weeks_of_month) [coordinate with 1A] │ ▼ @@ -523,9 +566,9 @@ Wave 2 (all parallel): 2B Wire curb_area (depends on 1B) 2C Wire curb_space (depends on 1B) 2D Create curb_object model (depends on 1B) - 2E Extend curb_event (depends on 1A, 1C) - 2F Update session - 2G Update aggregate + 2E Extend curb_event (depends on 1A, 1C) [Partial] + 2F Update session [Partial] + 2G Update aggregate [Partial] │ ▼ Wave 3 (all parallel): @@ -539,7 +582,7 @@ Wave 3 (all parallel): ▼ Wave 4 (all parallel, then 4C last): 4A Auth + 501 standardization - 4B Update toc.json + 4B Update toc.json [Partial] 4C Lint and validate (depends on 4A, 4B) ``` @@ -547,44 +590,34 @@ Wave 4 (all parallel, then 4C last): ## Summary Table -| Step | Focus | Key Files | Wave | -|------|-------|-----------|------| -| 1A | Fix bugs (event_type ref, bicyle, days_of_week) | curb_event, vehicle_type, time_spans | 1 | -| 1B | Align external_reference schema | external_reference.yaml | 1 | -| 1C | Create payment + enforcement schemas | 4 new files | 1 | -| 1D | New event types | event_type.yaml | 1 | -| 1E | New vehicle types (MDS) | vehicle_type.yaml | 1 | -| 1F | New propulsion types (MDS) | propulsion_type.yaml | 1 | -| 1G | Rules: user_classes, name, description | rules.yaml | 1 | -| 1H | Policy: name, description, color | curb_policy.yaml | 1 | -| 1I | Time spans: weeks_of_month | time_spans.yaml | 1 | -| 2A | Zone: LineString, objects, context, refs | curb_zone.yaml | 2 | -| 2B | Area: external refs, custom attrs | curb_area.yaml | 2 | -| 2C | Space: object IDs, refs, attrs | curb_space.yaml | 2 | -| 2D | Curb Object model (new) | curb_object.yaml | 2 | -| 2E | Event: payment, enforcement, CV | curb_event.yaml | 2 | -| 2F | Session: object ref | session.yaml | 2 | -| 2G | Aggregate: enforcement metrics | aggregate.yaml | 2 | -| 3A | Curbs API: object endpoints | Curbs API.yaml | 3 | -| 3B | Curbs API: operator param | Curbs API.yaml | 3 | -| 3C | Events API: event_time param | Events API.yaml | 3 | -| 3D | Events API: POST endpoint | Events API.yaml | 3 | -| 3E | Events API: auth guidance | Events API.yaml | 3 | -| 3F | Metrics API: responses | Metrics API.yaml | 3 | -| 4A | Auth + 501 across all APIs | All 3 API YAMLs | 4 | -| 4B | Update toc.json | toc.json | 4 | -| 4C | Lint and validate | All files | 4 | - ---- - -## Differences From Prior Analysis - -The following issues were found in the earlier `CHANGES_TASK_LIST.md`: - -**Pre-existing bugs not captured:** B1 (event_type wrong $ref), B2 (bicyle typo), B3 (days_of_week wrong type). - -**Items said to "add" that already exist as files:** `external_reference.yaml`, `custom_attributes.yaml`, `geoJSON_linestring.yaml` — the real task is wiring them into models that don't reference them yet and aligning the external_reference schema with v1.1. - -**Missing from prior analysis:** new event types (vehicle_detected, enforcement events), enforcement/violation object schemas, payment enum schemas, 8+ vehicle CV confidence fields, data_source_device_name, accessibility terminology updates, external_reference schema reconciliation, toc.json updates, curb_object_ids on zone and space, jurisdiction_type/owner_name/address_number on zone. - -**Items that were vague:** "enforcement-related fields" is now a full Enforcement object with nested Violations; "vehicle properties" is now 8 specific CV confidence fields; "data feed metadata" is folded into external_references (no separate model needed). +| Step | Focus | Key Files | Wave | Status | +|------|-------|-----------|------|--------| +| 0A | Version bump + README | All 3 API YAMLs, README | 0 | ✅ Done | +| 0B | Fix last_updated ref typo | API YAMLs | 0 | ✅ Done | +| 0C | Remove legacy file | original_CDS APIs.yaml | 0 | ✅ Done | +| 0D | Project documentation | AGENTS.MD, CLAUDE.md, etc. | 0 | ✅ Done | +| 1A | Fix bugs (event_type ref, bicyle, days_of_week) | curb_event, vehicle_type, time_spans | 1 | TODO | +| 1B | Align external_reference schema | external_reference.yaml | 1 | Partial | +| 1C | Create shared schemas | 6 files (2 exist, 4 new) | 1 | Partial | +| 1D | New event types | event_type.yaml | 1 | TODO | +| 1E | New vehicle types (MDS) | vehicle_type.yaml | 1 | TODO | +| 1F | New propulsion types (MDS) | propulsion_type.yaml | 1 | TODO | +| 1G | Rules: user_classes, name, description | rules.yaml | 1 | TODO | +| 1H | Policy: name, description, color | curb_policy.yaml | 1 | Partial | +| 1I | Time spans: weeks_of_month | time_spans.yaml | 1 | TODO | +| 2A | Zone: LineString, objects, context, refs | curb_zone.yaml | 2 | TODO | +| 2B | Area: external refs, custom attrs | curb_area.yaml | 2 | TODO | +| 2C | Space: object IDs, refs, attrs | curb_space.yaml | 2 | TODO | +| 2D | Curb Object model (new) | curb_object.yaml | 2 | TODO | +| 2E | Event: payment, enforcement, CV | curb_event.yaml | 2 | Partial | +| 2F | Session: object ref | session.yaml | 2 | Partial | +| 2G | Aggregate: enforcement metrics | aggregate.yaml | 2 | Partial | +| 3A | Curbs API: object endpoints | Curbs API.yaml | 3 | TODO | +| 3B | Curbs API: operator param | Curbs API.yaml | 3 | TODO | +| 3C | Events API: event_time param | Events API.yaml | 3 | TODO | +| 3D | Events API: POST endpoint | Events API.yaml | 3 | TODO | +| 3E | Events API: auth guidance | Events API.yaml | 3 | TODO | +| 3F | Metrics API: responses | Metrics API.yaml | 3 | TODO | +| 4A | Auth + 501 across all APIs | All 3 API YAMLs | 4 | TODO | +| 4B | Update toc.json | toc.json | 4 | Partial | +| 4C | Lint and validate | All files | 4 | TODO | From 5761e0bb2dcaad1ef32208d578decf939b3c7600 Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 11:26:26 -0400 Subject: [PATCH 10/26] Changes last updated date --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 90be2a0..cca96b2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # CDS OpenAPI **Status: Draft In Progress for v1.1** -_Last updated February 10, 2026_ +_Last updated April 7, 2026_ --- ## Overview From f8a7270d9c9e7f0f64a816f87fe0dbebc4895e80 Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 12:00:09 -0400 Subject: [PATCH 11/26] fix: correct event_type ref, vehicle_type typo, and days_of_week type - B1: curb_event.yaml event_type $ref was pointing to event_purpose.yaml; corrected to event_type.yaml - B2: vehicle_type.yaml enum had 'bicyle' misspelled; corrected to 'bicycle' - B3: time_spans.yaml days_of_week was typed as string instead of array; changed to type: array with items: { type: string, enum: [sun..sat] } --- Shared Data Models/curb_event.yaml | 2 +- Shared Data Models/time_spans.yaml | 20 +++++++++++--------- Shared Data Models/vehicle_type.yaml | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Shared Data Models/curb_event.yaml b/Shared Data Models/curb_event.yaml index 63f83ff..57e293d 100644 --- a/Shared Data Models/curb_event.yaml +++ b/Shared Data Models/curb_event.yaml @@ -11,7 +11,7 @@ properties: description: The globally unique identifier of the event that occurred. format: uuid event_type: - $ref: ./event_purpose.yaml + $ref: ./event_type.yaml x-stoplight: id: e9o3ixjkgnvna description: General curb usage purpose that the vehicle performed during the event. Required for sources capable of determining activity type for relevant event_types. diff --git a/Shared Data Models/time_spans.yaml b/Shared Data Models/time_spans.yaml index c785e75..756ebb3 100644 --- a/Shared Data Models/time_spans.yaml +++ b/Shared Data Models/time_spans.yaml @@ -19,16 +19,18 @@ properties: An integer representing a number of milliseconds since midnight, January 1st, 1970 UTC (the UNIX epoch) days_of_week: - type: string - enum: - - sun - - mon - - tue - - wed - - thu - - fri - - sat + type: array description: An array of days of the week when this time span applies. + items: + type: string + enum: + - sun + - mon + - tue + - wed + - thu + - fri + - sat days_of_month: type: array description: An array of days of the month when this time span applies. diff --git a/Shared Data Models/vehicle_type.yaml b/Shared Data Models/vehicle_type.yaml index 3ee9788..16d7ce3 100644 --- a/Shared Data Models/vehicle_type.yaml +++ b/Shared Data Models/vehicle_type.yaml @@ -3,7 +3,7 @@ x-stoplight: id: 1525213d884c3 type: string enum: - - bicyle + - bicycle - cargo_bicycle - car - scooter From debd2f90e5d6f243b96310a82636e224e8ff7d91 Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 12:35:30 -0400 Subject: [PATCH 12/26] chore: tighten OpenAPI lint metadata and plan tracking --- APIs/Curbs API.yaml | 31 +++++++++++++++++++++++++++ APIs/Events API.yaml | 17 +++++++++++++-- APIs/Metrics API.yaml | 17 +++++++++++++-- Shared Data Models/curb_area.yaml | 2 +- Shared Data Models/curb_space.yaml | 2 +- Shared Data Models/curb_zone.yaml | 2 +- Shared Data Models/geoJSON_point.yaml | 2 +- V1.1_IMPLEMENTATION_PLAN.md | 12 +++++++---- 8 files changed, 73 insertions(+), 12 deletions(-) diff --git a/APIs/Curbs API.yaml b/APIs/Curbs API.yaml index c801540..8b02519 100644 --- a/APIs/Curbs API.yaml +++ b/APIs/Curbs API.yaml @@ -5,12 +5,29 @@ info: title: Curb API version: '1.1' description: 'The Curbs API is a REST API allowing cities to specify areas of interest along the curb along with the rules for using them: who is allowed to park, load, unload, pick up, drop off, etc., for how long, for what price (if any), at what times, and on which days. Locations defined in the Curbs API can be connected to event and metrics data, and can be shared with companies and the public, for purposes such as routing, finding legal parking, loading, and pick-up/drop-off spots, or analyzing curb utilization over time.' + contact: + name: Open Mobility Foundation + url: 'https://www.openmobilityfoundation.org' + license: + name: Creative Commons Attribution 4.0 International Public License + url: 'https://creativecommons.org/licenses/by/4.0/' +tags: + - name: areas + description: Query and fetch curb areas. + - name: policies + description: Query and fetch curb policies. + - name: spaces + description: Query and fetch curb spaces. + - name: zones + description: Query and fetch curb zones. servers: - url: 'http://localhost:3000' paths: /curbs/zones: get: summary: Get Curb Zones + tags: + - zones responses: '200': description: Zones Found @@ -68,6 +85,8 @@ paths: /curbs/areas: get: summary: Get Curb Areas + tags: + - areas responses: '200': description: Areas Found @@ -115,6 +134,8 @@ paths: /curbs/spaces: get: summary: Get Curb Spaces + tags: + - spaces responses: '200': description: Spaces Found @@ -172,6 +193,8 @@ paths: /curbs/policies: get: summary: Get Curb Policies + tags: + - policies responses: '200': description: Policies Found @@ -219,6 +242,8 @@ paths: '/curbs/zones/{id}': get: summary: Get a single Curb Zone + tags: + - zones responses: '200': description: Zone Found @@ -269,6 +294,8 @@ paths: '/curbs/areas/{id}': get: summary: Get a single Curb Area + tags: + - areas responses: '200': description: Area Found @@ -308,6 +335,8 @@ paths: '/curbs/spaces/{id}': get: summary: Get a single Curb Space + tags: + - spaces responses: '200': description: Space Found @@ -353,6 +382,8 @@ paths: '/curbs/policies/{id}': get: summary: Get a single Curb Policy + tags: + - policies responses: '200': description: Policy Found diff --git a/APIs/Events API.yaml b/APIs/Events API.yaml index 40db258..2d2bb0f 100644 --- a/APIs/Events API.yaml +++ b/APIs/Events API.yaml @@ -5,13 +5,25 @@ info: title: Event API version: '1.1' description: 'The Events API is a REST API allowing real-time and historic events at the curb to be sent to cities, and the ability to check on the status of any sensors. Events can come from company data feeds, on street sensors, session payments, company check-ins, in-person parking personnel, and/or other city data sources. Data sent in the Events API can be connected to the Curbs API over time and space, and events are used for calculations in the Metrics API.' + contact: + name: Open Mobility Foundation + url: 'https://www.openmobilityfoundation.org' + license: + name: Creative Commons Attribution 4.0 International Public License + url: 'https://creativecommons.org/licenses/by/4.0/' +tags: + - name: events + description: Query curb events. + - name: status + description: Query curb sensor and source status. servers: - url: 'http://localhost:3000' paths: /events/events: get: summary: Get Events - tags: [] + tags: + - events responses: '200': description: OK @@ -69,7 +81,8 @@ paths: /events/status: get: summary: Get Status - tags: [] + tags: + - status responses: '200': description: OK diff --git a/APIs/Metrics API.yaml b/APIs/Metrics API.yaml index 591bfc6..098fd03 100644 --- a/APIs/Metrics API.yaml +++ b/APIs/Metrics API.yaml @@ -5,13 +5,25 @@ info: title: Metrics API version: '1.1' description: 'The Metrics API is a REST API allowing historic metrics calculations based on Event activity that happened at defined Curb places. Defines common calculation methodologies to measure historic dwell time, occupancy, usage and other aggregated statistics.' + contact: + name: Open Mobility Foundation + url: 'https://www.openmobilityfoundation.org' + license: + name: Creative Commons Attribution 4.0 International Public License + url: 'https://creativecommons.org/licenses/by/4.0/' +tags: + - name: aggregates + description: Query aggregated curb metrics. + - name: sessions + description: Query curb metric sessions. servers: - url: 'http://localhost:3000' paths: /metrics/sessions: get: summary: Get Metric Sessions - tags: [] + tags: + - sessions responses: '200': description: OK @@ -69,7 +81,8 @@ paths: /metrics/aggregates: get: summary: Get Metric Aggregates - tags: [] + tags: + - aggregates responses: '200': description: OK diff --git a/Shared Data Models/curb_area.yaml b/Shared Data Models/curb_area.yaml index 7efc2b4..dd4624f 100644 --- a/Shared Data Models/curb_area.yaml +++ b/Shared Data Models/curb_area.yaml @@ -38,7 +38,7 @@ properties: items: type: string format: uuid - example: ' 0eaa505d-c9e1-49ad-9fac-3c745ba58676 ' + example: '0eaa505d-c9e1-49ad-9fac-3c745ba58676' required: - curb_area_id - geometry diff --git a/Shared Data Models/curb_space.yaml b/Shared Data Models/curb_space.yaml index 65bdf5a..184a623 100644 --- a/Shared Data Models/curb_space.yaml +++ b/Shared Data Models/curb_space.yaml @@ -31,7 +31,7 @@ properties: description: The ID of the Curb Zone this space is within. The geometry of the specified Curb Zone MUST contain the geometry of this space. type: string format: uuid - example: ' 0eaa505d-c9e1-49ad-9fac-3c745ba58676 ' + example: '0eaa505d-c9e1-49ad-9fac-3c745ba58676' space_number: type: integer description: 'The sequence number of this space within its Zone. If specified, two spaces within the same Curb Zone MUST NOT share a space number, and space numbers SHOULD be consecutive positive integers starting at 1.' diff --git a/Shared Data Models/curb_zone.yaml b/Shared Data Models/curb_zone.yaml index 1d374ba..235d31b 100644 --- a/Shared Data Models/curb_zone.yaml +++ b/Shared Data Models/curb_zone.yaml @@ -19,7 +19,7 @@ properties: curb_zone_id: type: string format: uuid - example: ' 0eaa505d-c9e1-49ad-9fac-3c745ba58676 ' + example: '0eaa505d-c9e1-49ad-9fac-3c745ba58676' geometry: $ref: ./geoJSON_polygon.yaml description: The spatial extent of this Curb Zone. diff --git a/Shared Data Models/geoJSON_point.yaml b/Shared Data Models/geoJSON_point.yaml index 38c2c1a..762f36e 100644 --- a/Shared Data Models/geoJSON_point.yaml +++ b/Shared Data Models/geoJSON_point.yaml @@ -24,7 +24,7 @@ properties: x-stoplight: id: qskkkudk4ofyb type: number - example: '-73.982105,40.767932' + example: -73.982105 required: - type - coordinates diff --git a/V1.1_IMPLEMENTATION_PLAN.md b/V1.1_IMPLEMENTATION_PLAN.md index e3f7279..902025c 100644 --- a/V1.1_IMPLEMENTATION_PLAN.md +++ b/V1.1_IMPLEMENTATION_PLAN.md @@ -41,6 +41,10 @@ These bugs exist on the v1.0 branch and are addressed in Wave 1. | B1 | `curb_event.yaml` line 14 | `event_type` `$ref` points to `./event_purpose.yaml` | Change to `$ref: ./event_type.yaml` | | B2 | `vehicle_type.yaml` | Enum value `bicyle` is misspelled | Change to `bicycle` | | B3 | `time_spans.yaml` | `days_of_week` typed as `string` instead of `array` | Change to `type: array` with `items: { type: string, enum: [sun, mon, ...] }` | +| B4 | `APIs/Curbs API.yaml` | Query parameter schema references `#/components/schemas/uuid`, but no such component exists | Replace with the repository's standard UUID schema pattern (`type: string`, `format: uuid`) | +| B5 | `APIs/Curbs API.yaml` | Query parameter schema references `#/components/schemas/timestamp`, but no such component exists | Replace with a file ref to `../Shared Data Models/timestamp.yaml` | +| B6 | `APIs/Curbs API.yaml` | `ids` query parameter for `/curbs/policies` is modeled as a single `string` with `format: uuid`, but CDS 1.1 defines it as a comma-separated list of UUIDs | Model the parameter as an array of UUIDs serialized as comma-separated query values (`style: form`, `explode: false`) and update the example accordingly | +| B7 | `Shared Data Models/coordinates.yaml` | Array schema is missing an `items` definition, making the schema invalid OpenAPI | Add the appropriate `items` schema so the coordinate array is structurally valid and matches upstream CDS intent | --- @@ -90,12 +94,12 @@ All steps in this wave are independent and can run in parallel. ### Step 1A – Fix pre-existing bugs — TODO -Fix bugs B1, B2, and B3 listed above. +Fix bugs B1, B2, B3, B4, B5, B6, and B7 listed above. - **Depends on:** nothing -- **Files:** `Shared Data Models/curb_event.yaml`, `Shared Data Models/vehicle_type.yaml`, `Shared Data Models/time_spans.yaml` -- **Upstream ref:** `events/README.md` (event fields table), `general-information.md` (vehicle types), `curbs/README.md` (time spans) -- **Commit:** `fix: correct event_type ref, vehicle_type typo, and days_of_week type` +- **Files:** `Shared Data Models/curb_event.yaml`, `Shared Data Models/vehicle_type.yaml`, `Shared Data Models/time_spans.yaml`, `Shared Data Models/coordinates.yaml`, `APIs/Curbs API.yaml` +- **Upstream ref:** `events/README.md` (event fields table), `general-information.md` (vehicle types), `curbs/README.md` (time spans and Query Curb Policies parameter table), plus repository conventions in `AGENTS.MD` for UUID and timestamp schema usage +- **Commit:** `fix: correct refs and schema bugs in shared models and Curbs API` --- From d3e1d782f21f09c7db5c10376e192391055c1107 Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 12:46:43 -0400 Subject: [PATCH 13/26] chore: Adds emails to the YAML files to fix linting issues --- APIs/Curbs API.yaml | 1 + APIs/Events API.yaml | 1 + APIs/Metrics API.yaml | 1 + 3 files changed, 3 insertions(+) diff --git a/APIs/Curbs API.yaml b/APIs/Curbs API.yaml index 8b02519..4479381 100644 --- a/APIs/Curbs API.yaml +++ b/APIs/Curbs API.yaml @@ -8,6 +8,7 @@ info: contact: name: Open Mobility Foundation url: 'https://www.openmobilityfoundation.org' + email: membership@openmobilityfoundation.org license: name: Creative Commons Attribution 4.0 International Public License url: 'https://creativecommons.org/licenses/by/4.0/' diff --git a/APIs/Events API.yaml b/APIs/Events API.yaml index 2d2bb0f..65bb45c 100644 --- a/APIs/Events API.yaml +++ b/APIs/Events API.yaml @@ -8,6 +8,7 @@ info: contact: name: Open Mobility Foundation url: 'https://www.openmobilityfoundation.org' + email: membership@openmobilityfoundation.org license: name: Creative Commons Attribution 4.0 International Public License url: 'https://creativecommons.org/licenses/by/4.0/' diff --git a/APIs/Metrics API.yaml b/APIs/Metrics API.yaml index 098fd03..8f01181 100644 --- a/APIs/Metrics API.yaml +++ b/APIs/Metrics API.yaml @@ -8,6 +8,7 @@ info: contact: name: Open Mobility Foundation url: 'https://www.openmobilityfoundation.org' + email: membership@openmobilityfoundation.org license: name: Creative Commons Attribution 4.0 International Public License url: 'https://creativecommons.org/licenses/by/4.0/' From 3c3348737fe832ed06592f568c852d510050dbf5 Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 12:54:03 -0400 Subject: [PATCH 14/26] fix: clean up pre-existing defects and linter issues --- APIs/Curbs API.yaml | 54 ++++++----------------------- Shared Data Models/coordinates.yaml | 4 +++ Shared Data Models/curb_event.yaml | 2 +- V1.1_IMPLEMENTATION_PLAN.md | 14 ++++---- 4 files changed, 23 insertions(+), 51 deletions(-) diff --git a/APIs/Curbs API.yaml b/APIs/Curbs API.yaml index 4479381..06a79da 100644 --- a/APIs/Curbs API.yaml +++ b/APIs/Curbs API.yaml @@ -62,7 +62,6 @@ paths: description: 'This required endpoint must be implemented by every Curbs API server. If attaching policies to curb zones, the Query Curb Policies endpoint is also required.' parameters: - schema: - $ref: '#/components/schemas/uuid' type: string format: uuid in: query @@ -231,11 +230,17 @@ paths: description: 'Optional endpoint, but required if Curb Zones contain policy_id references. If not implemented, the server should reply with 501 Not Implemented.' parameters: - schema: - type: string - format: uuid - example: '1111ad2f-14ef-40c1-b8d9-b974c89f7fa9,884d11b8-4fb7-42f2-a3ee-6c4b7499c2b3' + type: array + items: + type: string + format: uuid + example: + - 1111ad2f-14ef-40c1-b8d9-b974c89f7fa9 + - 884d11b8-4fb7-42f2-a3ee-6c4b7499c2b3 in: query name: ids + style: form + explode: false description: 'If present, return only policies with the supplied UUIDs. Otherwise, return all policies.' security: [] x-stoplight: @@ -279,8 +284,7 @@ paths: required: true description: Curb Zone ID - schema: - $ref: '#/components/schemas/timestamp' - type: string + $ref: ../Shared Data Models/timestamp.yaml in: query name: time description: 'The Curb Zone object will only be returned if its validity period includes this time; otherwise, the server should reply with `404 Not Found`.' @@ -372,8 +376,7 @@ paths: required: true description: Curb Space ID - schema: - $ref: '#/components/schemas/timestamp' - type: string + $ref: ../Shared Data Models/timestamp.yaml in: query name: time description: Availability data (if supplied) will be returned as of this time. @@ -473,41 +476,6 @@ components: schema: type: number description: 'Specifies a latitude and longitude bounding point and a radius away from that point. Must be used together with `lng` and `lng`. Returns only locations that are within radius centimeters of the point identified by lat/lng. Curb Zones in the response MUST be ordered ascending by distance from the center point. This parameter is incompatible with `min_lat`, `min_lng`, `max_lat`, and `max_lng`' - start_time: - name: start_time - in: query - required: false - schema: - type: string - description: The start of the time period to return data where the value is inclusive. - end_time: - name: end_time - in: query - required: false - schema: - type: string - description: The end of the time period to return data where the value is inclusive. - curb_area_id: - name: curb_area_id - in: query - required: false - schema: - type: string - description: 'The ID of a Curb Area. If specified, only return records occurring within this area.' - curb_zone_id: - name: curb_zone_id - in: query - required: false - schema: - type: string - description: 'The ID of a Curb Zone. If specified, only return records occurring within this zone.' - curb_space_id: - name: curb_space_id - in: query - required: false - schema: - type: string - description: 'The ID of a Curb Space. If specified, only return records occurring within this area.' include_geometry: name: include_geometry in: query diff --git a/Shared Data Models/coordinates.yaml b/Shared Data Models/coordinates.yaml index 752a909..465f107 100644 --- a/Shared Data Models/coordinates.yaml +++ b/Shared Data Models/coordinates.yaml @@ -13,4 +13,8 @@ items: x-stoplight: id: zcetnhryqkc90 type: array + minItems: 2 + maxItems: 2 + items: + type: number description: Coordinates are defined as an array of collections/arrays where the nested arrays are 2 number values. These represent the latitude and longitude. diff --git a/Shared Data Models/curb_event.yaml b/Shared Data Models/curb_event.yaml index 57e293d..7dce5a1 100644 --- a/Shared Data Models/curb_event.yaml +++ b/Shared Data Models/curb_event.yaml @@ -14,7 +14,7 @@ properties: $ref: ./event_type.yaml x-stoplight: id: e9o3ixjkgnvna - description: General curb usage purpose that the vehicle performed during the event. Required for sources capable of determining activity type for relevant event_types. + description: General activity type that occurred during the event. Required for sources capable of determining activity type for relevant events. event_purpose: $ref: ./event_purpose.yaml x-stoplight: diff --git a/V1.1_IMPLEMENTATION_PLAN.md b/V1.1_IMPLEMENTATION_PLAN.md index 902025c..d3e8940 100644 --- a/V1.1_IMPLEMENTATION_PLAN.md +++ b/V1.1_IMPLEMENTATION_PLAN.md @@ -92,14 +92,14 @@ All steps in this wave are independent. All steps in this wave are independent and can run in parallel. -### Step 1A – Fix pre-existing bugs — TODO +### ~~Step 1A – Fix pre-existing bugs~~ ✅ -Fix bugs B1, B2, B3, B4, B5, B6, and B7 listed above. +~~Fix bugs B1, B2, B3, B4, B5, B6, and B7 listed above.~~ -- **Depends on:** nothing -- **Files:** `Shared Data Models/curb_event.yaml`, `Shared Data Models/vehicle_type.yaml`, `Shared Data Models/time_spans.yaml`, `Shared Data Models/coordinates.yaml`, `APIs/Curbs API.yaml` -- **Upstream ref:** `events/README.md` (event fields table), `general-information.md` (vehicle types), `curbs/README.md` (time spans and Query Curb Policies parameter table), plus repository conventions in `AGENTS.MD` for UUID and timestamp schema usage -- **Commit:** `fix: correct refs and schema bugs in shared models and Curbs API` +- ~~**Depends on:** nothing~~ +- ~~**Files:** `Shared Data Models/curb_event.yaml`, `Shared Data Models/vehicle_type.yaml`, `Shared Data Models/time_spans.yaml`, `Shared Data Models/coordinates.yaml`, `APIs/Curbs API.yaml`~~ +- ~~**Upstream ref:** `events/README.md` (event fields table), `general-information.md` (vehicle types), `curbs/README.md` (time spans and Query Curb Policies parameter table), plus repository conventions in `AGENTS.MD` for UUID and timestamp schema usage~~ +- ~~**Commit:** `fix: correct refs and schema bugs in shared models and Curbs API`~~ --- @@ -600,7 +600,7 @@ Wave 4 (all parallel, then 4C last): | 0B | Fix last_updated ref typo | API YAMLs | 0 | ✅ Done | | 0C | Remove legacy file | original_CDS APIs.yaml | 0 | ✅ Done | | 0D | Project documentation | AGENTS.MD, CLAUDE.md, etc. | 0 | ✅ Done | -| 1A | Fix bugs (event_type ref, bicyle, days_of_week) | curb_event, vehicle_type, time_spans | 1 | TODO | +| 1A | Fix bugs (event_type ref, bicyle, days_of_week) | curb_event, vehicle_type, time_spans | 1 | ✅ Done | | 1B | Align external_reference schema | external_reference.yaml | 1 | Partial | | 1C | Create shared schemas | 6 files (2 exist, 4 new) | 1 | Partial | | 1D | New event types | event_type.yaml | 1 | TODO | From 5b1ff5cfacbb8a24efa9dc50eda29b90483317ce Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 14:39:21 -0400 Subject: [PATCH 15/26] fix(shared): align external_reference schema with v1.1 spec --- Shared Data Models/external_reference.yaml | 54 ++++++++++++++-------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/Shared Data Models/external_reference.yaml b/Shared Data Models/external_reference.yaml index b173412..fcffe23 100644 --- a/Shared Data Models/external_reference.yaml +++ b/Shared Data Models/external_reference.yaml @@ -1,30 +1,44 @@ title: External Reference type: object description: > - A reusable structure for linking CDS objects to external systems, specifications, - or resources such as WZDx, CWZ, MDS, GBFS, GTFS, or public URLs and documents. + A reusable structure for linking CDS objects to an external data source that + impacts or provides information about a CDS object. properties: - reference_type: + reference_url: type: string + format: uri description: > - The external system or specification this reference points to - (for example, "WZDx", "CWZ", "MDS", "GBFS", "GTFS", "website", "document"). - id: - type: string - description: Identifier for this object within the external system. - url: + A web-accessible identifier URL for the source of the publicly or privately + accessible data feed, document, website, or similar resource. This must be + a full HTTPS URL pointing to a location that contains more information. + name: type: string - format: uri - description: Optional URL for the external resource when available. - description: + description: > + Name of the data source for reference, such as WZDx, CWZ, MDS, GBFS, GTFS, + or CDS. + public: + type: boolean + description: > + Indicates whether the reference URL can be viewed without authentication, + authorization, or an API key. + identifier_name: type: string - description: Optional human-readable description of this external reference. + description: > + The name of the data field or object referenced by the unique ids, such as + id, trip_id, vehicle_id, or RoadEventFeature. + ids: + type: array + description: > + One or more identifiers from the referenced URL that impact the use of or + relationship to part of CDS. + items: + type: string required: - - reference_type - - id + - reference_url examples: - - reference_type: wzdx - id: "road_event_feature_1234" - url: "https://example.com/wzdx/features/road_event_feature_1234" - description: "Linked WZDx road event feature" - + - reference_url: https://example.com/wzdx/road-events + name: WZDx + public: true + identifier_name: RoadEventFeature + ids: + - road_event_feature_1234 From b535c70540a9cc3f28f6d006d8376ca71544ec66 Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 14:41:21 -0400 Subject: [PATCH 16/26] Revised AGENTS.MD file --- AGENTS.MD | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/AGENTS.MD b/AGENTS.MD index 3663a7c..39b77cb 100644 --- a/AGENTS.MD +++ b/AGENTS.MD @@ -36,17 +36,19 @@ Always work on the `draft-v1.1` branch (or a feature branch off it) when impleme The authoritative CDS spec lives at https://github.com/openmobilityfoundation/curb-data-specification. When implementing changes: -1. Always cross-reference the upstream spec text, not just PR summaries -2. The spec has three sections: `curbs/`, `events/`, `metrics/` — each with its own README -3. Field names, types, enumerations, and required/optional status must match the upstream spec exactly -4. The `general-information.md` and `data-types.md` files contain shared definitions +1. Treat the upstream repository's `main` branch as the source of truth for the latest CDS specification +2. Always cross-reference the upstream spec text, not just PR summaries +3. Before editing local OpenAPI files, verify the relevant field names, types, enums, descriptions, and required/optional status against upstream `main` +4. When a discrepancy is found, label it explicitly as local drift, upstream ambiguity, or an inference +5. The spec has three sections: `curbs/`, `events/`, `metrics/` — each with its own README +6. The `general-information.md` and `data-types.md` files contain shared definitions ## How to Make Changes ### Workflow for each step in the implementation plan 1. **Read the plan step** in `V1.1_IMPLEMENTATION_PLAN.md` -2. **Read the upstream spec** for the relevant section to confirm exact field names, types, enums, and descriptions +2. **Read the upstream spec on `main`** for the relevant section to confirm exact field names, types, enums, descriptions, and required/optional status 3. **Edit the YAML files** — maintain the existing style conventions: - Use `$ref` for shared models rather than inline definitions - Include `description` on every field From a2917375a81138fb62db4ceb95bffd25e81573e0 Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 14:43:07 -0400 Subject: [PATCH 17/26] Add instructions to update the plan after the steps are worked on --- AGENTS.MD | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AGENTS.MD b/AGENTS.MD index 39b77cb..d0f1124 100644 --- a/AGENTS.MD +++ b/AGENTS.MD @@ -55,7 +55,8 @@ The authoritative CDS spec lives at https://github.com/openmobilityfoundation/cu - Use `x-stoplight: id:` annotations if present on surrounding fields (Stoplight generates these) - Keep `required` arrays up to date 4. **Validate** — run the Spectral linter (`.spectral.mjs`) after changes -5. **Commit** — use the commit message suggested in the plan step, or a similar conventional-commit style message +5. **Update the plan** — mark the completed step as done in `V1.1_IMPLEMENTATION_PLAN.md` +6. **Commit** — use the commit message suggested in the plan step, or a similar conventional-commit style message ### Style conventions From 2e03c8f62a3793ee1a50b592d1c3aafc59715ecb Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 14:43:39 -0400 Subject: [PATCH 18/26] Marks step 1B completed --- V1.1_IMPLEMENTATION_PLAN.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/V1.1_IMPLEMENTATION_PLAN.md b/V1.1_IMPLEMENTATION_PLAN.md index d3e8940..97c9c52 100644 --- a/V1.1_IMPLEMENTATION_PLAN.md +++ b/V1.1_IMPLEMENTATION_PLAN.md @@ -103,9 +103,9 @@ All steps in this wave are independent and can run in parallel. --- -### Step 1B – Align external_reference.yaml with v1.1 spec — Partial +### ~~Step 1B – Align external_reference.yaml with v1.1 spec~~ -The v1.1 spec defines external references with these fields: +~~The v1.1 spec defines external references with these fields:~~ - `reference_url` (string/uri, **Required**) — URL to external data feed - `name` (string, Optional) — feed identifier @@ -113,16 +113,16 @@ The v1.1 spec defines external references with these fields: - `identifier_name` (string, Optional) — external field name - `ids` (array of string, Optional) — IDs from external source -The file exists (uncommitted) but uses a different structure (`reference_type`, `id`, -`url`, `description`). Reconcile with the upstream spec. +~~The file exists (uncommitted) but uses a different structure (`reference_type`, `id`, +`url`, `description`). Reconcile with the upstream spec.~~ -**What exists (uncommitted):** `external_reference.yaml` file created with v1.0-era schema. -**Remaining:** Update field names and structure to match v1.1 spec. +~~**What exists (uncommitted):** `external_reference.yaml` file created with v1.0-era schema.~~ +~~**Remaining:** Update field names and structure to match v1.1 spec.~~ -- **Depends on:** nothing -- **Files:** `Shared Data Models/external_reference.yaml` -- **Upstream ref:** `data-types.md` (external references section), `curbs/README.md` -- **Commit:** `fix(shared): align external_reference schema with v1.1 spec` +- ~~**Depends on:** nothing~~ +- ~~**Files:** `Shared Data Models/external_reference.yaml`~~ +- ~~**Upstream ref:** `data-types.md` (external references section), `curbs/README.md`~~ +- ~~**Commit:** `fix(shared): align external_reference schema with v1.1 spec`~~ --- @@ -554,7 +554,7 @@ Wave 0 (all parallel): ✅ ALL DONE ▼ Wave 1 (all parallel): 1A Bug fixes (curb_event, vehicle_type, time_spans) - 1B Align external_reference schema [Partial] + 1B Align external_reference schema ✅ DONE 1C Create shared schemas (custom_attrs, linestring, [Partial] payment, enforcement) 1D Update event_type enum @@ -601,7 +601,7 @@ Wave 4 (all parallel, then 4C last): | 0C | Remove legacy file | original_CDS APIs.yaml | 0 | ✅ Done | | 0D | Project documentation | AGENTS.MD, CLAUDE.md, etc. | 0 | ✅ Done | | 1A | Fix bugs (event_type ref, bicyle, days_of_week) | curb_event, vehicle_type, time_spans | 1 | ✅ Done | -| 1B | Align external_reference schema | external_reference.yaml | 1 | Partial | +| 1B | Align external_reference schema | external_reference.yaml | 1 | ✅ Done | | 1C | Create shared schemas | 6 files (2 exist, 4 new) | 1 | Partial | | 1D | New event types | event_type.yaml | 1 | TODO | | 1E | New vehicle types (MDS) | vehicle_type.yaml | 1 | TODO | From b00c7cc5daa055479bd5f00dc9b1ce91738f01e8 Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 14:47:03 -0400 Subject: [PATCH 19/26] feat(shared): add payment_channel, payment_method, enforcement, and violation schemas --- Shared Data Models/enforcement.yaml | 48 +++++++++++++++++++++++++ Shared Data Models/payment_channel.yaml | 14 ++++++++ Shared Data Models/payment_method.yaml | 19 ++++++++++ Shared Data Models/violation.yaml | 23 ++++++++++++ V1.1_IMPLEMENTATION_PLAN.md | 26 +++++++------- toc.json | 22 +++++++++++- 6 files changed, 138 insertions(+), 14 deletions(-) create mode 100644 Shared Data Models/enforcement.yaml create mode 100644 Shared Data Models/payment_channel.yaml create mode 100644 Shared Data Models/payment_method.yaml create mode 100644 Shared Data Models/violation.yaml diff --git a/Shared Data Models/enforcement.yaml b/Shared Data Models/enforcement.yaml new file mode 100644 index 0000000..3973cab --- /dev/null +++ b/Shared Data Models/enforcement.yaml @@ -0,0 +1,48 @@ +title: Enforcement +type: object +description: > + A specific set of enforcement details from a data feed or API that is relevant + to an enforcement Curb Event. +properties: + enforcement_id: + type: string + format: uuid + description: > + An identifier unique to the enforcement incident, generated the first time + an enforcement event is recorded and referenced in future related + enforcement events. + citation_id: + type: string + description: A unique id that represents a single citation. + is_warning: + type: boolean + description: Indicates whether the enforcement action is being processed as a warning. + action_taken: + type: string + description: Indicates how the violation was enforced. + enum: + - citation_registered + - citation_posted + - citation_served + - citation_emailed + citation_cost: + type: string + description: The total cost of all violations associated to this enforcement action. + violations: + type: array + description: > + One or more violation objects associated with this enforcement event. + items: + $ref: ./violation.yaml +required: + - enforcement_id +examples: + - enforcement_id: 123e4567-e89b-12d3-a456-426614174000 + citation_id: C-2026-0001 + is_warning: false + action_taken: citation_posted + citation_cost: "6500" + violations: + - violation_code: NO_PARKING + violation_name: No parking in loading zone + violation_cost: "6500" diff --git a/Shared Data Models/payment_channel.yaml b/Shared Data Models/payment_channel.yaml new file mode 100644 index 0000000..e9894e0 --- /dev/null +++ b/Shared Data Models/payment_channel.yaml @@ -0,0 +1,14 @@ +title: Payment Channel +type: string +description: > + The medium or platform used to pay for a curb event. +enum: + - meter + - mobile_app + - sms + - telephone + - website + - other +examples: + - meter + diff --git a/Shared Data Models/payment_method.yaml b/Shared Data Models/payment_method.yaml new file mode 100644 index 0000000..7b7d72e --- /dev/null +++ b/Shared Data Models/payment_method.yaml @@ -0,0 +1,19 @@ +title: Payment Method +type: string +description: > + The method used to pay for a curb event. +enum: + - cash + - credit_card + - digital_wallet + - smart_card + - membership_card + - billing + - permit + - voucher + - courtesy + - test + - other +examples: + - credit_card + diff --git a/Shared Data Models/violation.yaml b/Shared Data Models/violation.yaml new file mode 100644 index 0000000..aa54dc0 --- /dev/null +++ b/Shared Data Models/violation.yaml @@ -0,0 +1,23 @@ +title: Violation +type: object +description: > + A violation associated with an enforcement action that can occur as a Curb Event. +properties: + violation_code: + type: string + description: > + The unique code created by the municipality, city, county, state, federal, + or enforcement agency to identify the type of rule being enforced. + violation_name: + type: string + description: > + The city/municipal, county, state, provincial, or federal code that was + violated. + violation_cost: + type: string + description: The original cost associated with the violation. +examples: + - violation_code: NO_PARKING + violation_name: No parking in loading zone + violation_cost: "6500" + diff --git a/V1.1_IMPLEMENTATION_PLAN.md b/V1.1_IMPLEMENTATION_PLAN.md index 97c9c52..f3a057b 100644 --- a/V1.1_IMPLEMENTATION_PLAN.md +++ b/V1.1_IMPLEMENTATION_PLAN.md @@ -126,19 +126,19 @@ All steps in this wave are independent and can run in parallel. --- -### Step 1C – Create shared schemas (custom_attributes, geoJSON_linestring, payment, enforcement) — Partial +### ~~Step 1C – Create shared schemas (custom_attributes, geoJSON_linestring, payment, enforcement)~~ -**custom_attributes.yaml** — Done (uncommitted). File exists with `additionalProperties: true`. +~~**custom_attributes.yaml** — Done (uncommitted). File exists with `additionalProperties: true`.~~ -**geoJSON_linestring.yaml** — Done (uncommitted). File exists with LineString geometry definition. +~~**geoJSON_linestring.yaml** — Done (uncommitted). File exists with LineString geometry definition.~~ -**payment_channel.yaml** — TODO. String enum: +~~**payment_channel.yaml** — TODO. String enum:~~ `meter`, `mobile_app`, `website`, `sms`, `telephone` -**payment_method.yaml** — TODO. String enum: +~~**payment_method.yaml** — TODO. String enum:~~ `cash`, `credit_card`, `debit_card`, `digital_wallet`, `transit_card`, `prepaid_account`, `permit` -**enforcement.yaml** — TODO. Object: +~~**enforcement.yaml** — TODO. Object:~~ - `enforcement_id` (UUID, Required) - `citation_id` (string, Optional) - `is_warning` (boolean, Optional) @@ -146,15 +146,15 @@ All steps in this wave are independent and can run in parallel. - `citation_cost` (string, Optional) - `violations` (array of `$ref: ./violation.yaml`, Optional) -**violation.yaml** — TODO. Object: +~~**violation.yaml** — TODO. Object:~~ - `violation_code` (string) - `violation_name` (string) - `violation_cost` (string) -- **Depends on:** nothing -- **Files:** New files in `Shared Data Models/` -- **Upstream ref:** `events/README.md` (payment fields, enforcement object), `data-types.md` -- **Commit:** `feat(shared): add payment_channel, payment_method, enforcement, and violation schemas` +- ~~**Depends on:** nothing~~ +- ~~**Files:** New files in `Shared Data Models/`~~ +- ~~**Upstream ref:** `events/README.md` (payment fields, enforcement object), `data-types.md`~~ +- ~~**Commit:** `feat(shared): add payment_channel, payment_method, enforcement, and violation schemas`~~ --- @@ -555,7 +555,7 @@ Wave 0 (all parallel): ✅ ALL DONE Wave 1 (all parallel): 1A Bug fixes (curb_event, vehicle_type, time_spans) 1B Align external_reference schema ✅ DONE - 1C Create shared schemas (custom_attrs, linestring, [Partial] + 1C Create shared schemas (custom_attrs, linestring, ✅ DONE payment, enforcement) 1D Update event_type enum 1E Update vehicle_type enum @@ -602,7 +602,7 @@ Wave 4 (all parallel, then 4C last): | 0D | Project documentation | AGENTS.MD, CLAUDE.md, etc. | 0 | ✅ Done | | 1A | Fix bugs (event_type ref, bicyle, days_of_week) | curb_event, vehicle_type, time_spans | 1 | ✅ Done | | 1B | Align external_reference schema | external_reference.yaml | 1 | ✅ Done | -| 1C | Create shared schemas | 6 files (2 exist, 4 new) | 1 | Partial | +| 1C | Create shared schemas | 6 files (2 exist, 4 new) | 1 | ✅ Done | | 1D | New event types | event_type.yaml | 1 | TODO | | 1E | New vehicle types (MDS) | vehicle_type.yaml | 1 | TODO | | 1F | New propulsion types (MDS) | propulsion_type.yaml | 1 | TODO | diff --git a/toc.json b/toc.json index 41faf5f..474573a 100644 --- a/toc.json +++ b/toc.json @@ -83,6 +83,11 @@ "title": "Data Source Type", "uri": "/Shared Data Models/data_source_type.yaml" }, + { + "type": "item", + "title": "Enforcement", + "uri": "/Shared Data Models/enforcement.yaml" + }, { "type": "item", "title": "Event Purpose", @@ -133,6 +138,16 @@ "title": "Operator ID", "uri": "/Shared Data Models/operator_id.yaml" }, + { + "type": "item", + "title": "Payment Channel", + "uri": "/Shared Data Models/payment_channel.yaml" + }, + { + "type": "item", + "title": "Payment Method", + "uri": "/Shared Data Models/payment_method.yaml" + }, { "type": "item", "title": "Propulsion Type", @@ -168,6 +183,11 @@ "title": "Vehicle Type", "uri": "/Shared Data Models/vehicle_type.yaml" }, + { + "type": "item", + "title": "Violation", + "uri": "/Shared Data Models/violation.yaml" + }, { "type": "divider", "title": "Shared API Responses" @@ -203,4 +223,4 @@ "uri": "/Rest Responses/rest_response_last_updated.yaml" } ] -} \ No newline at end of file +} From 7d7d227e90dc63c1f2727da864f39aba23508c23 Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 14:48:57 -0400 Subject: [PATCH 20/26] feat(shared): add enforcement and detection event types --- Shared Data Models/event_type.yaml | 4 ++++ V1.1_IMPLEMENTATION_PLAN.md | 16 ++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Shared Data Models/event_type.yaml b/Shared Data Models/event_type.yaml index 4c241aa..a538cfa 100644 --- a/Shared Data Models/event_type.yaml +++ b/Shared Data Models/event_type.yaml @@ -11,4 +11,8 @@ enum: - scheduled_report - enter_area - exit_area + - vehicle_detected + - vehicle_violation_start + - vehicle_violation_end + - citation_issued description: A description of what event occurred. diff --git a/V1.1_IMPLEMENTATION_PLAN.md b/V1.1_IMPLEMENTATION_PLAN.md index f3a057b..2e75d2c 100644 --- a/V1.1_IMPLEMENTATION_PLAN.md +++ b/V1.1_IMPLEMENTATION_PLAN.md @@ -158,19 +158,19 @@ All steps in this wave are independent and can run in parallel. --- -### Step 1D – Update event_type.yaml — TODO +### ~~Step 1D – Update event_type.yaml~~ -Add new event types: +~~Add new event types:~~ - `vehicle_detected` - `vehicle_violation_start` - `vehicle_violation_end` - `citation_issued` -- **Depends on:** nothing -- **Files:** `Shared Data Models/event_type.yaml` -- **Upstream ref:** `events/README.md` (event types table) -- **Commit:** `feat(shared): add enforcement and detection event types` +- ~~**Depends on:** nothing~~ +- ~~**Files:** `Shared Data Models/event_type.yaml`~~ +- ~~**Upstream ref:** `events/README.md` (event types table)~~ +- ~~**Commit:** `feat(shared): add enforcement and detection event types`~~ --- @@ -557,7 +557,7 @@ Wave 1 (all parallel): 1B Align external_reference schema ✅ DONE 1C Create shared schemas (custom_attrs, linestring, ✅ DONE payment, enforcement) - 1D Update event_type enum + 1D Update event_type enum ✅ DONE 1E Update vehicle_type enum 1F Update propulsion_type enum 1G Update rules (user_classes, name, description) @@ -603,7 +603,7 @@ Wave 4 (all parallel, then 4C last): | 1A | Fix bugs (event_type ref, bicyle, days_of_week) | curb_event, vehicle_type, time_spans | 1 | ✅ Done | | 1B | Align external_reference schema | external_reference.yaml | 1 | ✅ Done | | 1C | Create shared schemas | 6 files (2 exist, 4 new) | 1 | ✅ Done | -| 1D | New event types | event_type.yaml | 1 | TODO | +| 1D | New event types | event_type.yaml | 1 | ✅ Done | | 1E | New vehicle types (MDS) | vehicle_type.yaml | 1 | TODO | | 1F | New propulsion types (MDS) | propulsion_type.yaml | 1 | TODO | | 1G | Rules: user_classes, name, description | rules.yaml | 1 | TODO | From e5762033be49d358e76116822602426f3831b173 Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 15:09:11 -0400 Subject: [PATCH 21/26] feat(shared): complete Wave 1 shared schema updates Complete implementation plan steps 1E through 1I. Updates included: - add upstream CDS 1.1 vehicle types to vehicle_type.yaml - add upstream CDS 1.1 propulsion types and align propulsion descriptions to upstream CDS text - update rules.yaml with name, description, revised user_classes, user_classes_except, and purposes - update curb_policy.yaml with name, description, upstream priority wording, and policy_color - add weeks_of_month and update designated_period_except wording in time_spans.yaml - mark steps 1E through 1I complete in V1.1_IMPLEMENTATION_PLAN.md --- Shared Data Models/curb_policy.yaml | 73 +++++++++++++++++- Shared Data Models/propulsion_type.yaml | 31 ++++++-- Shared Data Models/rules.yaml | 76 ++++++++++++++++++- Shared Data Models/time_spans.yaml | 20 ++++- Shared Data Models/vehicle_type.yaml | 4 + V1.1_IMPLEMENTATION_PLAN.md | 98 ++++++++++++------------- 6 files changed, 243 insertions(+), 59 deletions(-) diff --git a/Shared Data Models/curb_policy.yaml b/Shared Data Models/curb_policy.yaml index b694647..6b4c740 100644 --- a/Shared Data Models/curb_policy.yaml +++ b/Shared Data Models/curb_policy.yaml @@ -13,6 +13,12 @@ properties: A `curb_policy_id` MUST NOT be reused. Once created, it must continue to refer to the identical policy forever. type: string + name: + type: string + description: User friendly name of policy. + description: + type: string + description: Detailed description of policy. published_date: $ref: ./timestamp.yaml description: |- @@ -21,7 +27,13 @@ properties: An integer representing a number of milliseconds since midnight, January 1st, 1970 UTC (the UNIX epoch) priority: type: integer - description: 'Specifies which other policies this one takes precedence over. If two Policies on the same Curb Zone have overlapping Time Spans and apply to the same user class, the one that applies at a given time is the one with the lowest priority.' + description: >- + Specifies which other policies this one takes precedence over. If two Policies + on the same Curb Zone have overlapping Time Spans and apply to the same user + class, the one that applies at a given time is the one with the lowest priority. + E.g., a priority of `1` takes precedence over a priority of `3`. Two Policies + that apply to the same Curb Zone with overlapping Time Spans MAY ONLY have the + same priority if the policies apply to disjoint User Classes or disjoint Activities. rules: type: array description: The rule(s) that this policy applies. @@ -42,6 +54,65 @@ properties: **Values are defined globally and are managed by the Open Mobility Foundation and operators may register [here](https://github.com/openmobilityfoundation/curb-data-specification/wiki/Adding-a-CDS-Data-Source-Operator-ID)** items: $ref: ./operator_id.yaml + policy_color: + type: object + description: > + A JSON object that defines the official colors used to represent this specific + policy, used in physical curb paint, digital and print maps, etc. + properties: + primary_color: + type: string + description: > + The 6 digit hexidecimal triplet value of the primary curb color, in standard + capitalized RRGGBB format, without a leading hash symbol ("#"). E.g. "839D8F", + "FF00FF". + primary_pattern_type: + type: string + description: > + One of `solid`, `long_dash`, `short_dash`, `dot`, `dot_dash`, `diagonal` + to define the type of pattern on the `primary_color`. + enum: + - solid + - long_dash + - short_dash + - dot + - dot_dash + - diagonal + primary_border_color: + type: string + description: The 6 digit hex value of the `primary_color` border color. + primary_border_pattern_type: + type: string + description: > + One of `solid`, `long_dash`, `short_dash`, `dot`, `dot_dash`, `diagonal` + to define the type of pattern on the `primary_border_color`. + enum: + - solid + - long_dash + - short_dash + - dot + - dot_dash + - diagonal + secondary_color: + type: string + description: The 6 digit hex value of the secondary curb color. + secondary_border_color: + type: string + description: The 6 digit hex value of the `secondary_color` border color. + secondary_border_pattern_type: + type: string + description: > + One of `solid`, `long_dash`, `short_dash`, `dot`, `dot_dash`, `diagonal` + to define the type of pattern on the `secondary_border_color`. + enum: + - solid + - long_dash + - short_dash + - dot + - dot_dash + - diagonal + required: + - primary_color external_references: type: array description: External references that link this policy to other systems or specifications. diff --git a/Shared Data Models/propulsion_type.yaml b/Shared Data Models/propulsion_type.yaml index a144bc8..3d55a64 100644 --- a/Shared Data Models/propulsion_type.yaml +++ b/Shared Data Models/propulsion_type.yaml @@ -3,14 +3,31 @@ x-stoplight: id: exicw88mt3m4k type: array description: |- - The method of propulsion for a vehicle. Vehicles that may have one or more values from the enumeration can be represented as an array with multiple types. + Propulsion type `vehicle_propulsion_types` of the vehicle, similar to + `propulsion_type` in MDS. For this CDS release the list will be developed + independently here to accommodate CDS and MDS use cases, while still aligning + to the MDS design principles. In the next major MDS 2.0 release and next CDS + release, alignment between CDS and MDS propulsion types can occur. |Value|Description| |--------------|-----| - | human | Pedal or foot propulsion | - | electric_assist | Provides power only alongside human propulsion | - | electric | Contains throttle mode with a battery-powered motor | - | combustion | Contains throttle mode with a gas engine-powered motor | + | human | Pedal or foot propulsion | + | electric_assist | Provides electric motor assist only in combination with human propulsion - no throttle mode | + | electric | Powered by battery-powered electric motor with throttle mode | + | combustion | Powered by gasoline combustion engine | + | combustion_diesel | Powered by diesel combustion engine | + | hybrid | Powered by combined combustion engine and battery-powered motor | + | hydrogen_fuel_cell | Powered by hydrogen fuel cell powered electric motor | + | plug_in_hybrid | Powered by combined combustion engine and battery-powered motor with plug-in charging | + + A vehicle may have one or more values from the `vehicle_propulsion_types`, + depending on the number of modes of operation. For example, a scooter that can + be powered by foot or by electric motor would have the + `vehicle_propulsion_types` represented by the array `["human", "electric"]`. + A bicycle with pedal-assist would have the `vehicle_propulsion_types` + represented by the array `["human", "electric_assist"]` if it can also be + operated as a traditional bicycle. A hybrid vehicle may use + `["combustion", "electric"]`. items: x-stoplight: id: 1vps8ofx4ythw @@ -19,3 +36,7 @@ items: - electric_assist - electric - combustion + - combustion_diesel + - hybrid + - hydrogen_fuel_cell + - plug_in_hybrid diff --git a/Shared Data Models/rules.yaml b/Shared Data Models/rules.yaml index 924c9d0..9e22f86 100644 --- a/Shared Data Models/rules.yaml +++ b/Shared Data Models/rules.yaml @@ -4,6 +4,12 @@ x-stoplight: type: object description: 'A rule defines who is allowed to do what, and for how long, on a curb, per the policy.' properties: + name: + type: string + description: User friendly name of rule. + description: + type: string + description: Detailed description of rule. activity: type: string enum: @@ -31,7 +37,48 @@ properties: description: The Unit of Time associated with the no_return value. user_classes: type: array - description: A user class represents any class of vehicles that is regulated by a city with respect to curb space. + description: > + User classes describe which vehicles, vehicle properties, or permit types + a rule applies to. All values in the array must match for the rule to apply. + items: + type: string + enum: + - bicycle + - bus + - cargo_bicycle + - car + - moped + - motorcycle + - scooter + - shuttle + - truck + - van + - accessible + - human + - electric_assist + - electric + - combustion + - autonomous + - construction + - delivery + - disabled_parking_permit + - emergency_use + - freight + - parking + - permit + - rideshare + - school + - service_vehicles + - special_events + - taxi + - utilities + - vending + - waste_management + user_classes_except: + type: array + description: > + If specified, the rule applies only to users who do not match any of the + listed user classes. items: type: string enum: @@ -42,9 +89,10 @@ properties: - moped - motorcycle - scooter + - shuttle - truck - van - - handicap-accessible + - accessible - human - electric_assist - electric @@ -52,6 +100,30 @@ properties: - autonomous - construction - delivery + - disabled_parking_permit + - emergency_use + - freight + - parking + - permit + - rideshare + - school + - service_vehicles + - special_events + - taxi + - utilities + - vending + - waste_management + purposes: + type: array + description: > + The permitted purposes associated with this rule. If specified, the rule + applies only when one of the listed purposes matches. + items: + type: string + enum: + - construction + - delivery + - disabled_parking_permit - emergency_use - freight - parking diff --git a/Shared Data Models/time_spans.yaml b/Shared Data Models/time_spans.yaml index 756ebb3..02ef5fc 100644 --- a/Shared Data Models/time_spans.yaml +++ b/Shared Data Models/time_spans.yaml @@ -38,6 +38,16 @@ properties: type: integer minimum: 1 maximum: 31 + weeks_of_month: + type: array + description: > + An array of weeks of the month when this time span applies, specified as + integers (1-5), to represent ordinal weeks. E.g. "2" would be the 2nd + week of the month. + items: + type: integer + minimum: 1 + maximum: 5 months: type: array description: 'If specified, this time span applies only during these months (1=January, 12=December).' @@ -58,7 +68,13 @@ properties: - holidays - school days - game days - description: 'A string representing an arbitrarily-named, externally-defined period of time.' + description: > + A string representing an arbitrarily-named, externally-defined period of + time. Any values MAY be specified but the following known values SHOULD be + used when possible. designated_period_except: type: boolean - description: 'If specified and true, this time span applies at all times not matching the named designated period.' \ No newline at end of file + description: > + If specified and true, all fields in this Time Span are describing a + period in which the associated rule does not apply. This field takes + precedence over designated_period when present. diff --git a/Shared Data Models/vehicle_type.yaml b/Shared Data Models/vehicle_type.yaml index 16d7ce3..76f7202 100644 --- a/Shared Data Models/vehicle_type.yaml +++ b/Shared Data Models/vehicle_type.yaml @@ -4,9 +4,13 @@ x-stoplight: type: string enum: - bicycle + - bus - cargo_bicycle - car + - delivery_robot - scooter + - scooter_standing + - scooter_seated - moped - motorcycle - truck diff --git a/V1.1_IMPLEMENTATION_PLAN.md b/V1.1_IMPLEMENTATION_PLAN.md index 2e75d2c..abcf427 100644 --- a/V1.1_IMPLEMENTATION_PLAN.md +++ b/V1.1_IMPLEMENTATION_PLAN.md @@ -174,86 +174,86 @@ All steps in this wave are independent and can run in parallel. --- -### Step 1E – Update vehicle_type.yaml (new values) — TODO +### ~~Step 1E – Update vehicle_type.yaml (new values)~~ -Add MDS-aligned vehicle types (the `bicycle` typo fix is in Step 1A): +~~Add MDS-aligned vehicle types (the `bicycle` typo fix is in Step 1A):~~ - `bus` - `delivery_robot` - `scooter_standing` - `scooter_seated` -- **Depends on:** nothing -- **Files:** `Shared Data Models/vehicle_type.yaml` -- **Upstream ref:** `general-information.md` (vehicle types), MDS spec alignment notes -- **Commit:** `feat(shared): add MDS-aligned vehicle types` +- ~~**Depends on:** nothing~~ +- ~~**Files:** `Shared Data Models/vehicle_type.yaml`~~ +- ~~**Upstream ref:** `general-information.md` (vehicle types), MDS spec alignment notes~~ +- ~~**Commit:** `feat(shared): add MDS-aligned vehicle types`~~ --- -### Step 1F – Update propulsion_type.yaml — TODO +### ~~Step 1F – Update propulsion_type.yaml~~ -Add MDS-aligned propulsion types: +~~Add MDS-aligned propulsion types:~~ - `combustion_diesel` - `hybrid` - `hydrogen_fuel_cell` - `plug_in_hybrid` -Update the description table to include new values. +~~Update the description table to include new values.~~ -- **Depends on:** nothing -- **Files:** `Shared Data Models/propulsion_type.yaml` -- **Upstream ref:** `general-information.md` (propulsion types) -- **Commit:** `feat(shared): add MDS-aligned propulsion types` +- ~~**Depends on:** nothing~~ +- ~~**Files:** `Shared Data Models/propulsion_type.yaml`~~ +- ~~**Upstream ref:** `general-information.md` (propulsion types)~~ +- ~~**Commit:** `feat(shared): add MDS-aligned propulsion types`~~ --- -### Step 1G – Update rules.yaml (user_classes + name/description) — TODO +### ~~Step 1G – Update rules.yaml (user_classes + name/description)~~ -Enum updates: +~~Enum updates:~~ - Replace `handicap-accessible` with `accessible` - Add `disabled_parking_permit` -New fields: +~~New fields:~~ - Add `name` (string, Optional) - Add `description` (string, Optional) -- **Depends on:** nothing -- **Files:** `Shared Data Models/rules.yaml` -- **Upstream ref:** `curbs/README.md` (rules table, user classes) -- **Commit:** `feat(shared): update rules with name, description, and accessibility user_classes` +- ~~**Depends on:** nothing~~ +- ~~**Files:** `Shared Data Models/rules.yaml`~~ +- ~~**Upstream ref:** `curbs/README.md` (rules table, user classes)~~ +- ~~**Commit:** `feat(shared): update rules with name, description, and accessibility user_classes`~~ --- -### Step 1H – Update curb_policy.yaml (name, description, color) — Partial +### ~~Step 1H – Update curb_policy.yaml (name, description, color)~~ -**What exists (uncommitted):** `external_references` and `custom_attributes` fields already added. -**Remaining:** -- Add `name` (string, Optional) -- Add `description` (string, Optional) -- Add `primary_color` (string, Optional — hex RGB for visual representation) -- Update `priority` description: "Two overlapping policies MAY have the same priority IF they apply to disjoint User Classes OR disjoint Activities." +~~**What exists (uncommitted):** `external_references` and `custom_attributes` fields already added.~~ +~~**Remaining:**~~ +- ~~Add `name` (string, Optional)~~ +- ~~Add `description` (string, Optional)~~ +- ~~Add `primary_color` (string, Optional — hex RGB for visual representation)~~ +- ~~Update `priority` description: "Two overlapping policies MAY have the same priority IF they apply to disjoint User Classes OR disjoint Activities."~~ -- **Depends on:** nothing -- **Files:** `Shared Data Models/curb_policy.yaml` -- **Upstream ref:** `curbs/README.md` (policies table) -- **Commit:** `feat(shared): add name, description, primary_color to curb_policy` +- ~~**Depends on:** nothing~~ +- ~~**Files:** `Shared Data Models/curb_policy.yaml`~~ +- ~~**Upstream ref:** `curbs/README.md` (policies table)~~ +- ~~**Commit:** `feat(shared): add name, description, primary_color to curb_policy`~~ --- -### Step 1I – Update time_spans.yaml (weeks_of_month) — TODO +### ~~Step 1I – Update time_spans.yaml (weeks_of_month)~~ -After the `days_of_week` array fix in Step 1A: +~~After the `days_of_week` array fix in Step 1A:~~ - Add `weeks_of_month` (array of integers 1–5) - Review and update `designated_period_except` description per v1.1 -Note: This step edits the same file as 1A. If running in parallel, coordinate -or combine into a single step. +~~Note: This step edits the same file as 1A. If running in parallel, coordinate +or combine into a single step.~~ -- **Depends on:** Step 1A (same file) -- **Files:** `Shared Data Models/time_spans.yaml` -- **Upstream ref:** `curbs/README.md` (time spans table) -- **Commit:** `feat(shared): add weeks_of_month to time_spans` +- ~~**Depends on:** Step 1A (same file)~~ +- ~~**Files:** `Shared Data Models/time_spans.yaml`~~ +- ~~**Upstream ref:** `curbs/README.md` (time spans table)~~ +- ~~**Commit:** `feat(shared): add weeks_of_month to time_spans`~~ --- @@ -558,11 +558,11 @@ Wave 1 (all parallel): 1C Create shared schemas (custom_attrs, linestring, ✅ DONE payment, enforcement) 1D Update event_type enum ✅ DONE - 1E Update vehicle_type enum - 1F Update propulsion_type enum - 1G Update rules (user_classes, name, description) - 1H Update curb_policy (name, description, color) [Partial] - 1I Update time_spans (weeks_of_month) [coordinate with 1A] + 1E Update vehicle_type enum ✅ DONE + 1F Update propulsion_type enum ✅ DONE + 1G Update rules (user_classes, name, description) ✅ DONE + 1H Update curb_policy (name, description, color) ✅ DONE + 1I Update time_spans (weeks_of_month) ✅ DONE │ ▼ Wave 2 (all parallel): @@ -604,11 +604,11 @@ Wave 4 (all parallel, then 4C last): | 1B | Align external_reference schema | external_reference.yaml | 1 | ✅ Done | | 1C | Create shared schemas | 6 files (2 exist, 4 new) | 1 | ✅ Done | | 1D | New event types | event_type.yaml | 1 | ✅ Done | -| 1E | New vehicle types (MDS) | vehicle_type.yaml | 1 | TODO | -| 1F | New propulsion types (MDS) | propulsion_type.yaml | 1 | TODO | -| 1G | Rules: user_classes, name, description | rules.yaml | 1 | TODO | -| 1H | Policy: name, description, color | curb_policy.yaml | 1 | Partial | -| 1I | Time spans: weeks_of_month | time_spans.yaml | 1 | TODO | +| 1E | New vehicle types (MDS) | vehicle_type.yaml | 1 | ✅ Done | +| 1F | New propulsion types (MDS) | propulsion_type.yaml | 1 | ✅ Done | +| 1G | Rules: user_classes, name, description | rules.yaml | 1 | ✅ Done | +| 1H | Policy: name, description, color | curb_policy.yaml | 1 | ✅ Done | +| 1I | Time spans: weeks_of_month | time_spans.yaml | 1 | ✅ Done | | 2A | Zone: LineString, objects, context, refs | curb_zone.yaml | 2 | TODO | | 2B | Area: external refs, custom attrs | curb_area.yaml | 2 | TODO | | 2C | Space: object IDs, refs, attrs | curb_space.yaml | 2 | TODO | From 57f61713fc73e9c8bb26efff0c45f2f3c6ffb90e Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 16:22:54 -0400 Subject: [PATCH 22/26] docs: simplify and re-baseline v1.1 implementation plan --- V1.1_IMPLEMENTATION_PLAN.md | 724 ++++++++---------------------------- 1 file changed, 164 insertions(+), 560 deletions(-) diff --git a/V1.1_IMPLEMENTATION_PLAN.md b/V1.1_IMPLEMENTATION_PLAN.md index abcf427..949a64a 100644 --- a/V1.1_IMPLEMENTATION_PLAN.md +++ b/V1.1_IMPLEMENTATION_PLAN.md @@ -1,627 +1,231 @@ # CDS OpenAPI v1.1 – Implementation Plan -This plan captures every change required to bring the `cds-openapi` project from -CDS 1.0 to full alignment with the -[CDS 1.1.0 specification](https://github.com/openmobilityfoundation/curb-data-specification) -as finalized in Q4 2025 via -[PR #201](https://github.com/openmobilityfoundation/curb-data-specification/pull/201). +## Goal ---- - -## How to Use This Plan - -Steps are grouped into **waves**. Within a wave, every step is independent and can be -worked on in parallel by separate agents or contributors. A wave cannot start until all -steps in the previous wave are complete. - -Each step lists: -- **Status:** Done, Partial, or TODO -- **Depends on:** which prior steps must be done first (if any beyond the wave gate) -- **Files:** exactly which files to create or edit -- **Upstream ref:** where to look in the CDS spec for authoritative field definitions -- **Commit:** suggested commit message - -Before starting any step, read `AGENTS.MD` for style conventions and the upstream spec -section listed in **Upstream ref** for exact field names, types, and descriptions. - -### Status Legend - -- ~~Strikethrough~~ = Done (committed) -- **Partial** = some sub-tasks complete (committed or uncommitted), remainder noted -- Unmarked = TODO - ---- - -## Pre-Existing Bugs (v1.0 branch) - -These bugs exist on the v1.0 branch and are addressed in Wave 1. - -| ID | File | Bug | Fix | -|----|------|-----|-----| -| B1 | `curb_event.yaml` line 14 | `event_type` `$ref` points to `./event_purpose.yaml` | Change to `$ref: ./event_type.yaml` | -| B2 | `vehicle_type.yaml` | Enum value `bicyle` is misspelled | Change to `bicycle` | -| B3 | `time_spans.yaml` | `days_of_week` typed as `string` instead of `array` | Change to `type: array` with `items: { type: string, enum: [sun, mon, ...] }` | -| B4 | `APIs/Curbs API.yaml` | Query parameter schema references `#/components/schemas/uuid`, but no such component exists | Replace with the repository's standard UUID schema pattern (`type: string`, `format: uuid`) | -| B5 | `APIs/Curbs API.yaml` | Query parameter schema references `#/components/schemas/timestamp`, but no such component exists | Replace with a file ref to `../Shared Data Models/timestamp.yaml` | -| B6 | `APIs/Curbs API.yaml` | `ids` query parameter for `/curbs/policies` is modeled as a single `string` with `format: uuid`, but CDS 1.1 defines it as a comma-separated list of UUIDs | Model the parameter as an array of UUIDs serialized as comma-separated query values (`style: form`, `explode: false`) and update the example accordingly | -| B7 | `Shared Data Models/coordinates.yaml` | Array schema is missing an `items` definition, making the schema invalid OpenAPI | Add the appropriate `items` schema so the coordinate array is structurally valid and matches upstream CDS intent | - ---- - -## Wave 0 – Housekeeping and Setup - -All steps in this wave are independent. - -### ~~Step 0A – Version bump and README~~ ✅ - -~~Bump `info.version` to `1.1` in all three API YAMLs. Update `README.md` with draft-v1.1 status and date.~~ - -- ~~**Files:** `APIs/Curbs API.yaml`, `APIs/Events API.yaml`, `APIs/Metrics API.yaml`, `README.md`~~ -- ~~**Commit:** `8e52a11` — `API version bump, README date, fix last_updated refs`~~ - ---- - -### ~~Step 0B – Fix last_updated ref typo~~ ✅ - -~~Replace `rest_response_last-updated.yaml` with `rest_response_last_updated.yaml` in all API files.~~ - -- ~~**Files:** `APIs/Curbs API.yaml`, `APIs/Events API.yaml`~~ -- ~~**Commit:** `8e52a11` (combined with 0A)~~ - ---- - -### ~~Step 0C – Remove unused legacy file~~ ✅ - -~~Delete `original_CDS APIs.yaml`.~~ - -- ~~**Files:** `original_CDS APIs.yaml` (deleted)~~ -- ~~**Commit:** `e982398` — `Removes old file that isn't in use`~~ - ---- - -### ~~Step 0D – Project documentation~~ ✅ - -~~Create `AGENTS.MD`, `CLAUDE.md`, `CHANGELOG.MD`, and `V1.1_IMPLEMENTATION_PLAN.md`.~~ - -- ~~**Files:** `AGENTS.MD`, `CLAUDE.md`, `CHANGELOG.MD`, `V1.1_IMPLEMENTATION_PLAN.md`~~ -- ~~**Commits:** `901f305`, `52418c2`, `94fbcdf`~~ - ---- - -## Wave 1 – Bug Fixes, New Schemas, and Enum Updates +Bring the local `draft-v1.1` branch of `cds-openapi` into parity with the +upstream `main` branch of +[`openmobilityfoundation/curb-data-specification`](https://github.com/openmobilityfoundation/curb-data-specification), +using the upstream specification as of April 7, 2026 as the source of truth. -All steps in this wave are independent and can run in parallel. - -### ~~Step 1A – Fix pre-existing bugs~~ ✅ - -~~Fix bugs B1, B2, B3, B4, B5, B6, and B7 listed above.~~ - -- ~~**Depends on:** nothing~~ -- ~~**Files:** `Shared Data Models/curb_event.yaml`, `Shared Data Models/vehicle_type.yaml`, `Shared Data Models/time_spans.yaml`, `Shared Data Models/coordinates.yaml`, `APIs/Curbs API.yaml`~~ -- ~~**Upstream ref:** `events/README.md` (event fields table), `general-information.md` (vehicle types), `curbs/README.md` (time spans and Query Curb Policies parameter table), plus repository conventions in `AGENTS.MD` for UUID and timestamp schema usage~~ -- ~~**Commit:** `fix: correct refs and schema bugs in shared models and Curbs API`~~ +Read `AGENTS.MD` for workflow rules and upstream-reference guidance before +editing the spec. --- -### ~~Step 1B – Align external_reference.yaml with v1.1 spec~~ - -~~The v1.1 spec defines external references with these fields:~~ - -- `reference_url` (string/uri, **Required**) — URL to external data feed -- `name` (string, Optional) — feed identifier -- `public` (boolean, Optional) — feed accessibility -- `identifier_name` (string, Optional) — external field name -- `ids` (array of string, Optional) — IDs from external source +## Outstanding Decisions -~~The file exists (uncommitted) but uses a different structure (`reference_type`, `id`, -`url`, `description`). Reconcile with the upstream spec.~~ - -~~**What exists (uncommitted):** `external_reference.yaml` file created with v1.0-era schema.~~ -~~**Remaining:** Update field names and structure to match v1.1 spec.~~ - -- ~~**Depends on:** nothing~~ -- ~~**Files:** `Shared Data Models/external_reference.yaml`~~ -- ~~**Upstream ref:** `data-types.md` (external references section), `curbs/README.md`~~ -- ~~**Commit:** `fix(shared): align external_reference schema with v1.1 spec`~~ +- `custom_attributes_dictionary` vs `custom_attribute_dictionary` + Upstream docs use both names. Pick one local representation and keep it + consistent. +- JSON media-type examples still show `version=1.0` + Treat this as upstream text drift and keep it explicit in final review notes. +- Metrics is a CSV API + Local OpenAPI modeling can vary, but the wire contract must remain CSV. --- -### ~~Step 1C – Create shared schemas (custom_attributes, geoJSON_linestring, payment, enforcement)~~ - -~~**custom_attributes.yaml** — Done (uncommitted). File exists with `additionalProperties: true`.~~ - -~~**geoJSON_linestring.yaml** — Done (uncommitted). File exists with LineString geometry definition.~~ - -~~**payment_channel.yaml** — TODO. String enum:~~ -`meter`, `mobile_app`, `website`, `sms`, `telephone` +## Active Path Forward -~~**payment_method.yaml** — TODO. String enum:~~ -`cash`, `credit_card`, `debit_card`, `digital_wallet`, `transit_card`, `prepaid_account`, `permit` +Strike through completed items as work lands on `draft-v1.1`. -~~**enforcement.yaml** — TODO. Object:~~ -- `enforcement_id` (UUID, Required) -- `citation_id` (string, Optional) -- `is_warning` (boolean, Optional) -- `action_taken` (string enum: `citation_registered`, `citation_posted`, `citation_served`, `citation_emailed`, Optional) -- `citation_cost` (string, Optional) -- `violations` (array of `$ref: ./violation.yaml`, Optional) +### Phase 1 – Foundation And Early Alignment -~~**violation.yaml** — TODO. Object:~~ -- `violation_code` (string) -- `violation_name` (string) -- `violation_cost` (string) - -- ~~**Depends on:** nothing~~ -- ~~**Files:** New files in `Shared Data Models/`~~ -- ~~**Upstream ref:** `events/README.md` (payment fields, enforcement object), `data-types.md`~~ -- ~~**Commit:** `feat(shared): add payment_channel, payment_method, enforcement, and violation schemas`~~ +- ~~Bump the API specs to v1.1 and clean up baseline metadata and lint issues.~~ +- ~~Fix the pre-existing schema defects in `curb_event.yaml`, + `vehicle_type.yaml`, `time_spans.yaml`, `coordinates.yaml`, and + `APIs/Curbs API.yaml`.~~ +- ~~Align `external_reference.yaml` with the upstream CDS data-types table.~~ +- ~~Add the foundational shared schemas: + `custom_attributes.yaml`, `geoJSON_linestring.yaml`, + `payment_channel.yaml`, `payment_method.yaml`, `enforcement.yaml`, and + `violation.yaml`.~~ +- ~~Update the foundational shared value lists and policy fields used across the spec: + `event_type.yaml`, `vehicle_type.yaml`, `propulsion_type.yaml`, + `rules.yaml`, `curb_policy.yaml`, and `time_spans.yaml`.~~ --- -### ~~Step 1D – Update event_type.yaml~~ - -~~Add new event types:~~ - -- `vehicle_detected` -- `vehicle_violation_start` -- `vehicle_violation_end` -- `citation_issued` - -- ~~**Depends on:** nothing~~ -- ~~**Files:** `Shared Data Models/event_type.yaml`~~ -- ~~**Upstream ref:** `events/README.md` (event types table)~~ -- ~~**Commit:** `feat(shared): add enforcement and detection event types`~~ - ---- - -### ~~Step 1E – Update vehicle_type.yaml (new values)~~ - -~~Add MDS-aligned vehicle types (the `bicycle` typo fix is in Step 1A):~~ - -- `bus` -- `delivery_robot` -- `scooter_standing` -- `scooter_seated` - -- ~~**Depends on:** nothing~~ -- ~~**Files:** `Shared Data Models/vehicle_type.yaml`~~ -- ~~**Upstream ref:** `general-information.md` (vehicle types), MDS spec alignment notes~~ -- ~~**Commit:** `feat(shared): add MDS-aligned vehicle types`~~ - ---- - -### ~~Step 1F – Update propulsion_type.yaml~~ - -~~Add MDS-aligned propulsion types:~~ - -- `combustion_diesel` -- `hybrid` -- `hydrogen_fuel_cell` -- `plug_in_hybrid` - -~~Update the description table to include new values.~~ - -- ~~**Depends on:** nothing~~ -- ~~**Files:** `Shared Data Models/propulsion_type.yaml`~~ -- ~~**Upstream ref:** `general-information.md` (propulsion types)~~ -- ~~**Commit:** `feat(shared): add MDS-aligned propulsion types`~~ - ---- - -### ~~Step 1G – Update rules.yaml (user_classes + name/description)~~ - -~~Enum updates:~~ -- Replace `handicap-accessible` with `accessible` -- Add `disabled_parking_permit` - -~~New fields:~~ -- Add `name` (string, Optional) -- Add `description` (string, Optional) - -- ~~**Depends on:** nothing~~ -- ~~**Files:** `Shared Data Models/rules.yaml`~~ -- ~~**Upstream ref:** `curbs/README.md` (rules table, user classes)~~ -- ~~**Commit:** `feat(shared): update rules with name, description, and accessibility user_classes`~~ - ---- - -### ~~Step 1H – Update curb_policy.yaml (name, description, color)~~ - -~~**What exists (uncommitted):** `external_references` and `custom_attributes` fields already added.~~ -~~**Remaining:**~~ -- ~~Add `name` (string, Optional)~~ -- ~~Add `description` (string, Optional)~~ -- ~~Add `primary_color` (string, Optional — hex RGB for visual representation)~~ -- ~~Update `priority` description: "Two overlapping policies MAY have the same priority IF they apply to disjoint User Classes OR disjoint Activities."~~ +### Phase 2 – Shared Model Parity -- ~~**Depends on:** nothing~~ -- ~~**Files:** `Shared Data Models/curb_policy.yaml`~~ -- ~~**Upstream ref:** `curbs/README.md` (policies table)~~ -- ~~**Commit:** `feat(shared): add name, description, primary_color to curb_policy`~~ +#### 2A. `curb_zone.yaml` — TODO ---- - -### ~~Step 1I – Update time_spans.yaml (weeks_of_month)~~ - -~~After the `days_of_week` array fix in Step 1A:~~ -- Add `weeks_of_month` (array of integers 1–5) -- Review and update `designated_period_except` description per v1.1 - -~~Note: This step edits the same file as 1A. If running in parallel, coordinate -or combine into a single step.~~ - -- ~~**Depends on:** Step 1A (same file)~~ -- ~~**Files:** `Shared Data Models/time_spans.yaml`~~ -- ~~**Upstream ref:** `curbs/README.md` (time spans table)~~ -- ~~**Commit:** `feat(shared): add weeks_of_month to time_spans`~~ - ---- - -## Wave 2 – Model Wiring and New Models - -All steps in this wave are independent and can run in parallel. -Requires Wave 1 to be complete (schemas they reference must exist). - -### Step 2A – Wire shared schemas into curb_zone.yaml — TODO - -Add: -- `external_references` (array of `$ref: ./external_reference.yaml`) -- `custom_attributes` (`$ref: ./custom_attributes.yaml`) -- `geometry_line` (`$ref: ./geoJSON_linestring.yaml`) — optional LineString geometry -- `curb_object_ids` (array of UUID strings) -- `description` (string, Optional) -- `jurisdiction_type` (string enum: `public`, `private`, `other`) -- `owner_name` (string, Optional) -- `address_number` (string, Optional) - -- **Depends on:** Step 1B (external_reference alignment) - **Files:** `Shared Data Models/curb_zone.yaml` -- **Upstream ref:** `curbs/README.md` (curb zones table) -- **Commit:** `feat(shared): add LineString, objects, jurisdiction, context, external refs to curb_zone` - ---- - -### Step 2B – Wire shared schemas into curb_area.yaml — TODO +- **Done when:** + - `geometry` supports both Polygon and LineString in the same field + - no separate `geometry_line` field is introduced + - missing upstream fields are added: + `description`, `jurisdiction_type`, `owner_name`, `address_number`, + `available`, `available_spaces`, `custom_attributes`, `curb_object_ids`, + `external_references` + - UUID typing is corrected where needed for policy ID arrays -Add: -- `external_references` (array of `$ref: ./external_reference.yaml`) -- `custom_attributes` (`$ref: ./custom_attributes.yaml`) +#### 2B. `curb_area.yaml` — TODO -- **Depends on:** Step 1B (external_reference alignment) - **Files:** `Shared Data Models/curb_area.yaml` -- **Upstream ref:** `curbs/README.md` (curb areas table) -- **Commit:** `feat(shared): add external_references and custom_attributes to curb_area` +- **Done when:** + - `custom_attributes` is added + - `external_references` is added + - the required fields still match the upstream table ---- - -### Step 2C – Wire shared schemas into curb_space.yaml — TODO +#### 2C. `curb_space.yaml` — TODO -Add: -- `external_references` (array of `$ref: ./external_reference.yaml`) -- `custom_attributes` (`$ref: ./custom_attributes.yaml`) -- `curb_object_ids` (array of UUID strings, conditionally required) - -- **Depends on:** Step 1B (external_reference alignment) - **Files:** `Shared Data Models/curb_space.yaml` -- **Upstream ref:** `curbs/README.md` (curb spaces table) -- **Commit:** `feat(shared): add external_references, custom_attributes, curb_object_ids to curb_space` +- **Done when:** + - `curb_object_ids`, `custom_attributes`, and `external_references` are added + - the required fields match upstream: + `curb_space_id`, `geometry`, `published_date`, `last_updated_date`, + `curb_zone_id`, `length` + - `curb_object_ids` is described as conditionally required only in prose, not + made globally required in schema + +#### 2D. `curb_object.yaml` — TODO + +- **Files:** `Shared Data Models/curb_object.yaml` +- **Done when:** + - a new `curb_object.yaml` model exists + - required fields match upstream: + `curb_object_id`, `geometry`, `object_type`, `name`, `published_date`, + `last_updated_date` + - conditional relationship requirement between `curb_zone_id` and + `curb_space_id` is documented + - optional fields from the current upstream table are represented, including + `lane_type`, shape or line geometry, dimensions, `custom_attributes`, and + `external_references` + +#### 2E. `curb_event.yaml` — Partial ---- - -### Step 2D – Create curb_object.yaml — TODO - -New shared data model for physical curb infrastructure. - -**Required fields:** -- `curb_object_id` (UUID) -- `curb_object_type` (string — well-known values: bollard, planter, camera, pay_station, locker, etc.) -- `geometry` (`$ref: ./geoJSON_point.yaml`) -- `published_date` (`$ref: ./timestamp.yaml`) -- `last_updated_date` (`$ref: ./timestamp.yaml`) - -**Optional fields:** -- `name`, `description`, `owner`, `operator` (strings) -- `external_object_id` (string) -- `external_url` (string, format: uri) -- `curb_policy_id` (string, format: uuid) -- `geometry_line` (`$ref: ./geoJSON_linestring.yaml`) -- `external_references` (array of `$ref: ./external_reference.yaml`) -- `custom_attributes` (`$ref: ./custom_attributes.yaml`) - -- **Depends on:** Step 1B (external_reference alignment) -- **Files:** New `Shared Data Models/curb_object.yaml` -- **Upstream ref:** `curbs/README.md` (curb objects section) -- **Commit:** `feat(shared): add Curb Object data model` - ---- - -### Step 2E – Extend curb_event.yaml — Partial - -**What exists (uncommitted):** `external_references` and `custom_attributes` fields already added. -**Remaining:** - -**Payment:** -- `payment_channel` (`$ref: ./payment_channel.yaml`) -- `payment_method` (`$ref: ./payment_method.yaml`) -- `payment_transaction_id` (string) - -**Enforcement:** -- `enforcement` (`$ref: ./enforcement.yaml`) - -**Object reference:** -- `curb_object_id` (string, format: uuid) - -**Vehicle / Computer Vision:** -- `vehicle_type` (`$ref: ./vehicle_type.yaml`) -- `vehicle_license_plate_jurisdiction` (string) -- `vehicle_license_plate_confidence` (number, 0–1) -- `vehicle_type_confidence` (number, 0–1) -- `vehicle_color` (string) -- `vehicle_color_confidence` (number, 0–1) -- `vehicle_company_name` (string) -- `vehicle_company_name_confidence` (number, 0–1) -- `vehicle_run_id` (string) -- `vehicle_run_id_confidence` (number, 0–1) - -**Data source:** -- `data_source_device_name` (string) - -**Required fields:** review whether `event_publication_time` should be added to `required`. - -- **Depends on:** Steps 1A (bug fix), 1C (payment/enforcement schemas) - **Files:** `Shared Data Models/curb_event.yaml` -- **Upstream ref:** `events/README.md` (event fields table) -- **Commit:** `feat(shared): add payment, enforcement, vehicle CV, and object ref to curb_event` - ---- +- **Done when:** + - ~~`external_references` and `custom_attributes` are already present~~ + - missing upstream fields are added: + `curb_object_id`, `data_source_device_name`, `payment_channel`, + `payment_method`, `payment_transaction_id`, `enforcement`, `vehicle_type`, + `vehicle_license_plate_jurisdiction`, `vehicle_license_plate_confidence`, + `vehicle_type_confidence`, `vehicle_color`, `vehicle_color_confidence`, + `vehicle_company_name`, `vehicle_company_name_confidence`, + `vehicle_run_id`, `vehicle_run_id_confidence` + - confidence fields are modeled as integers from `1` to `100` + - `event_publication_time` is required + - `event_location` is no longer globally required + - UUID typing and field descriptions match the upstream table + +#### 2F. `session.yaml` — Partial -### Step 2F – Update session.yaml — Partial - -**What exists (uncommitted):** `custom_attributes` field already added. -**Remaining:** -- Add `curb_object_id` (string, format: uuid, Optional) - -- **Depends on:** nothing beyond Wave 1 gate - **Files:** `Shared Data Models/session.yaml` -- **Upstream ref:** `metrics/README.md` (sessions table) -- **Commit:** `feat(shared): add curb_object_id to session model` - ---- - -### Step 2G – Update aggregate.yaml — Partial +- **Done when:** + - `curb_object_id` is added + - the local-only `custom_attributes` field is removed or explicitly reverted + - field types and descriptions match the current Metrics session columns -**What exists (uncommitted):** `custom_attributes` field already added. -**Remaining:** -- Add enforcement-related metric types to the `metric_type` enum (e.g., `total_events`, `total_enforcement_events`) -- Review v1.1 spec for any additional aggregate fields +#### 2G. `aggregate.yaml` — Partial -- **Depends on:** nothing beyond Wave 1 gate - **Files:** `Shared Data Models/aggregate.yaml` -- **Upstream ref:** `metrics/README.md` (aggregates table, methodology) -- **Commit:** `feat(shared): add enforcement metric types to aggregate model` - ---- - -## Wave 3 – API Endpoint Changes - -All steps in this wave are independent and can run in parallel. -Requires Wave 2 to be complete (models they reference must exist). - -### Step 3A – Curbs API: Add object endpoints — TODO - -Add to `Curbs API.yaml`: - -- `GET /curbs/objects` — list curb objects - - Query params: `area` (UUID), `zone` (UUID), `space` (UUID), `time` (timestamp), `operator` (string), plus existing geo params (bbox and lat/lng/radius) - - Response: standard wrapper with `data.objects` array of `$ref: curb_object.yaml` - - 501 response for optional endpoint -- `GET /curbs/objects/{id}` — get single curb object - - Path param: `id` (required) - - Response: standard wrapper with `data` as single `$ref: curb_object.yaml` - - 501 response - -- **Depends on:** Step 2D (curb_object model) -- **Files:** `APIs/Curbs API.yaml` -- **Upstream ref:** `curbs/README.md` (query curb objects endpoint) -- **Commit:** `feat(curbs): add GET /curbs/objects and GET /curbs/objects/{id} endpoints` +- **Done when:** + - `curb_space_type` is renamed to `curb_place_type` + - `curb_place_type` includes `object` + - `metric_type` includes `total_events` + - `total_enforcement_events` is not introduced + - scalar types match upstream: + `date` is `date`, `hour` is `integer`, `value` is `number` + - the local-only `custom_attributes` field is removed or explicitly reverted --- -### Step 3B – Curbs API: Add operator query parameter — TODO +### Phase 3 – API Parity -Add `operator` query parameter to zones, spaces, and areas endpoints. +#### 3A. `Curbs API.yaml` — TODO -- **Depends on:** nothing beyond Wave 2 gate - **Files:** `APIs/Curbs API.yaml` -- **Upstream ref:** `curbs/README.md` (query parameters) -- **Commit:** `feat(curbs): add operator query parameter to zone/space/area endpoints` +- **Done when:** + - an `objects` tag is added + - `GET /curbs/objects` is added with only `time`, `zone`, and `space` + - `GET /curbs/objects/{id}` is added with optional `time` + - both object endpoints include `501 Not Implemented` + - list responses use the same `data.` wrapper pattern consistently ---- - -### Step 3C – Events API: Add event_time query parameter — TODO +#### 3B. `Events API.yaml` GET parity — TODO -Add `event_time` to `GET /events/events`: -- Type: string -- Format: ISO 8601 UTC hour (`YYYY-MM-DDTHH`) -- Description: returns all events within that hour; default when omitted is last 60 minutes - -- **Depends on:** nothing beyond Wave 2 gate - **Files:** `APIs/Events API.yaml` -- **Upstream ref:** `events/README.md` (query events endpoint) -- **Commit:** `feat(events): add event_time query parameter` - ---- - -### Step 3D – Events API: Add POST /events/events endpoint — TODO +- **Done when:** + - `event_time` is added to `GET /events/events` + - `curb_object_id` is added to `GET /events/events` + - `curb_object_id` is added to `GET /events/status` -Add optional push endpoint: -- Request body: array of curb_event objects -- Responses: 201 Created, 501 Not Implemented -- Description: optional real-time push endpoint +#### 3C. `Events API.yaml` POST parity — TODO -- **Depends on:** Step 2E (curb_event updates) - **Files:** `APIs/Events API.yaml` -- **Upstream ref:** `events/README.md` (POST events section) -- **Commit:** `feat(events): add POST /events/events endpoint for real-time push` - ---- - -### Step 3E – Events API: Authorization guidance — TODO +- **Done when:** + - `POST /events/event` is added at the correct singular path + - deduplication guidance by `event_id` is documented + - response coverage matches upstream: + `200`, `201`, `400`, `401`, `404`, `406`, `409`, `500`, `501` -Update endpoint descriptions to note: -- Event endpoints may require authorization by the public agency -- Authorization is **recommended** but not strictly required +#### 3D. `Events API.yaml` auth and error guidance — TODO -- **Depends on:** nothing beyond Wave 2 gate - **Files:** `APIs/Events API.yaml` -- **Upstream ref:** `events/README.md` (authorization section) -- **Commit:** `docs(events): add authorization guidance` +- **Done when:** + - GET endpoints are marked as authorization recommended + - POST `/events/event` is marked as authorization required + - event ordering guidance is aligned with `general-information.md#event-times` + - error and bulk-response descriptions align with upstream general information ---- +#### 3E. `Metrics API.yaml` contract parity — TODO -### Step 3F – Metrics API: Update responses — TODO +- **Files:** `APIs/Metrics API.yaml` +- **Done when:** + - Metrics is represented as a CSV API, not a JSON wrapper API + - the media type reflects + `application/vnd.cds+csv;version=1.1` + - `curb_place_type` includes `object` + - `metric_type` includes `total_events` + - `start_time` is documented as inclusive + - `end_time` is documented as exclusive -Ensure session and aggregate response schemas reflect updated models. Add 501 -descriptions consistently on optional endpoints. +#### 3F. `Metrics API.yaml` descriptive parity — TODO -- **Depends on:** Steps 2F, 2G (session/aggregate updates) - **Files:** `APIs/Metrics API.yaml` -- **Upstream ref:** `metrics/README.md` -- **Commit:** `feat(metrics): update response schemas for v1.1 model changes` +- **Done when:** + - recommended authorization guidance is added + - representative-sample-data and update-frequency guidance are reflected where useful + - `501 Not Implemented` wording is consistent + - any row examples or schema surrogates match the updated shared models --- -## Wave 4 – Cross-Cutting Polish - -All steps in this wave are independent and can run in parallel. -Requires Wave 3 to be complete. - -### Step 4A – Authorization and 501 standardization (all APIs) — TODO +### Phase 4 – Validation And Cleanup -Across all three API YAMLs: -- Ensure Curbs API includes authorization descriptions where specified -- Confirm 501 Not Implemented is consistent on all optional endpoints -- Add pagination preference language from the spec if applicable +#### 4A. JSON wrapper and auth cleanup — TODO -- **Depends on:** nothing beyond Wave 3 gate -- **Files:** All three API YAMLs -- **Upstream ref:** `general-information.md` (authorization, pagination) -- **Commit:** `docs: standardize authorization and 501 behavior across all APIs` +- **Files:** `APIs/Curbs API.yaml`, `APIs/Events API.yaml` +- **Done when:** + - JSON endpoint wrappers match `general-information.md#rest-endpoints` + - the conditional custom attribute dictionary field is handled consistently + - optional endpoints consistently document `501 Not Implemented` + - bearer-auth documentation is aligned where auth is required or recommended ---- - -### Step 4B – Update toc.json — Partial - -**What exists (uncommitted):** Entries added for Custom Attributes, External Reference, GeoJSON LineString. -**Remaining:** Add entries for: +#### 4B. `toc.json` — Partial -- Curb Object (under "CDS Models") -- Enforcement (under "Shared Models") -- Violation (under "Shared Models") -- Payment Channel (under "Shared Models") -- Payment Method (under "Shared Models") - -- **Depends on:** nothing beyond Wave 3 gate - **Files:** `toc.json` -- **Upstream ref:** n/a -- **Commit:** `chore: add new v1.1 models to toc.json` +- **Done when:** + - ~~entries exist for `Custom Attributes`, `External Reference`, + `GeoJSON LineString`, `Enforcement`, `Violation`, `Payment Channel`, and + `Payment Method`~~ + - `Curb Object` is added under `CDS Models` ---- +#### 4C. Lint and final upstream review — TODO -### Step 4C – Validate and lint — TODO - -- Run `.spectral.mjs` linter and fix any errors -- Spot-check all `$ref` targets for correct paths -- Verify every new field has a `description` -- Verify `required` arrays are correct for all updated models -- Cross-check every field against the upstream v1.1 spec for completeness - -- **Depends on:** Steps 4A, 4B -- **Files:** All files -- **Commit:** `chore: lint, validate, and polish OpenAPI spec for v1.1` +- **Files:** full repo +- **Done when:** + - `.spectral.mjs` passes + - all `$ref` paths are valid + - every new or changed field has a description + - required arrays match upstream tables + - a final field-by-field re-check against the source-of-truth docs is complete + - known ambiguities are either resolved locally or explicitly documented --- -## Dependency Graph - -``` -Wave 0 (all parallel): ✅ ALL DONE - 0A Version bump + README - 0B Fix last_updated ref typo - 0C Remove legacy file - 0D Project documentation - │ - ▼ -Wave 1 (all parallel): - 1A Bug fixes (curb_event, vehicle_type, time_spans) - 1B Align external_reference schema ✅ DONE - 1C Create shared schemas (custom_attrs, linestring, ✅ DONE - payment, enforcement) - 1D Update event_type enum ✅ DONE - 1E Update vehicle_type enum ✅ DONE - 1F Update propulsion_type enum ✅ DONE - 1G Update rules (user_classes, name, description) ✅ DONE - 1H Update curb_policy (name, description, color) ✅ DONE - 1I Update time_spans (weeks_of_month) ✅ DONE - │ - ▼ -Wave 2 (all parallel): - 2A Wire curb_zone (depends on 1B) - 2B Wire curb_area (depends on 1B) - 2C Wire curb_space (depends on 1B) - 2D Create curb_object model (depends on 1B) - 2E Extend curb_event (depends on 1A, 1C) [Partial] - 2F Update session [Partial] - 2G Update aggregate [Partial] - │ - ▼ -Wave 3 (all parallel): - 3A Curbs API: object endpoints (depends on 2D) - 3B Curbs API: operator param - 3C Events API: event_time param - 3D Events API: POST endpoint (depends on 2E) - 3E Events API: auth guidance - 3F Metrics API: responses (depends on 2F, 2G) - │ - ▼ -Wave 4 (all parallel, then 4C last): - 4A Auth + 501 standardization - 4B Update toc.json [Partial] - 4C Lint and validate (depends on 4A, 4B) -``` - ---- +## Definition Of Done -## Summary Table - -| Step | Focus | Key Files | Wave | Status | -|------|-------|-----------|------|--------| -| 0A | Version bump + README | All 3 API YAMLs, README | 0 | ✅ Done | -| 0B | Fix last_updated ref typo | API YAMLs | 0 | ✅ Done | -| 0C | Remove legacy file | original_CDS APIs.yaml | 0 | ✅ Done | -| 0D | Project documentation | AGENTS.MD, CLAUDE.md, etc. | 0 | ✅ Done | -| 1A | Fix bugs (event_type ref, bicyle, days_of_week) | curb_event, vehicle_type, time_spans | 1 | ✅ Done | -| 1B | Align external_reference schema | external_reference.yaml | 1 | ✅ Done | -| 1C | Create shared schemas | 6 files (2 exist, 4 new) | 1 | ✅ Done | -| 1D | New event types | event_type.yaml | 1 | ✅ Done | -| 1E | New vehicle types (MDS) | vehicle_type.yaml | 1 | ✅ Done | -| 1F | New propulsion types (MDS) | propulsion_type.yaml | 1 | ✅ Done | -| 1G | Rules: user_classes, name, description | rules.yaml | 1 | ✅ Done | -| 1H | Policy: name, description, color | curb_policy.yaml | 1 | ✅ Done | -| 1I | Time spans: weeks_of_month | time_spans.yaml | 1 | ✅ Done | -| 2A | Zone: LineString, objects, context, refs | curb_zone.yaml | 2 | TODO | -| 2B | Area: external refs, custom attrs | curb_area.yaml | 2 | TODO | -| 2C | Space: object IDs, refs, attrs | curb_space.yaml | 2 | TODO | -| 2D | Curb Object model (new) | curb_object.yaml | 2 | TODO | -| 2E | Event: payment, enforcement, CV | curb_event.yaml | 2 | Partial | -| 2F | Session: object ref | session.yaml | 2 | Partial | -| 2G | Aggregate: enforcement metrics | aggregate.yaml | 2 | Partial | -| 3A | Curbs API: object endpoints | Curbs API.yaml | 3 | TODO | -| 3B | Curbs API: operator param | Curbs API.yaml | 3 | TODO | -| 3C | Events API: event_time param | Events API.yaml | 3 | TODO | -| 3D | Events API: POST endpoint | Events API.yaml | 3 | TODO | -| 3E | Events API: auth guidance | Events API.yaml | 3 | TODO | -| 3F | Metrics API: responses | Metrics API.yaml | 3 | TODO | -| 4A | Auth + 501 across all APIs | All 3 API YAMLs | 4 | TODO | -| 4B | Update toc.json | toc.json | 4 | Partial | -| 4C | Lint and validate | All files | 4 | TODO | +The `draft-v1.1` branch is a current representation of the CDS API +specification as it appears on the upstream `main` branch. From dd9c07d3a6b0280e6504712fd724eaf09cab77ac Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 20:28:55 -0400 Subject: [PATCH 23/26] feat(api): use custom_attributes_dictionary in JSON responses --- APIs/Curbs API.yaml | 16 ++++++++++++++++ APIs/Events API.yaml | 2 ++ ..._response_custom_attributes_dictionary.yaml | 13 +++++++++++++ Rest Responses/rest_response_version.yaml | 2 +- V1.1_IMPLEMENTATION_PLAN.md | 18 ++++++++++++------ 5 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 Rest Responses/rest_response_custom_attributes_dictionary.yaml diff --git a/APIs/Curbs API.yaml b/APIs/Curbs API.yaml index 06a79da..23f57f8 100644 --- a/APIs/Curbs API.yaml +++ b/APIs/Curbs API.yaml @@ -45,6 +45,8 @@ paths: $ref: ../Rest Responses/rest_response_last_updated.yaml currency: $ref: ../Rest Responses/rest_response_currency.yaml + custom_attributes_dictionary: + $ref: ../Rest Responses/rest_response_custom_attributes_dictionary.yaml author: $ref: ../Rest Responses/rest_response_author.yaml license_url: @@ -103,6 +105,8 @@ paths: $ref: ../Rest Responses/rest_response_last_updated.yaml currency: $ref: ../Rest Responses/rest_response_currency.yaml + custom_attributes_dictionary: + $ref: ../Rest Responses/rest_response_custom_attributes_dictionary.yaml author: $ref: ../Rest Responses/rest_response_author.yaml license_url: @@ -152,6 +156,8 @@ paths: $ref: ../Rest Responses/rest_response_last_updated.yaml currency: $ref: ../Rest Responses/rest_response_currency.yaml + custom_attributes_dictionary: + $ref: ../Rest Responses/rest_response_custom_attributes_dictionary.yaml author: $ref: ../Rest Responses/rest_response_author.yaml license_url: @@ -211,6 +217,8 @@ paths: $ref: ../Rest Responses/rest_response_last_updated.yaml currency: $ref: ../Rest Responses/rest_response_currency.yaml + custom_attributes_dictionary: + $ref: ../Rest Responses/rest_response_custom_attributes_dictionary.yaml author: $ref: ../Rest Responses/rest_response_author.yaml license_url: @@ -266,6 +274,8 @@ paths: $ref: ../Rest Responses/rest_response_last_updated.yaml currency: $ref: ../Rest Responses/rest_response_currency.yaml + custom_attributes_dictionary: + $ref: ../Rest Responses/rest_response_custom_attributes_dictionary.yaml author: $ref: ../Rest Responses/rest_response_author.yaml license_url: @@ -317,6 +327,8 @@ paths: $ref: ../Rest Responses/rest_response_last_updated.yaml currency: $ref: ../Rest Responses/rest_response_currency.yaml + custom_attributes_dictionary: + $ref: ../Rest Responses/rest_response_custom_attributes_dictionary.yaml author: $ref: ../Rest Responses/rest_response_author.yaml license_url: @@ -358,6 +370,8 @@ paths: $ref: ../Rest Responses/rest_response_last_updated.yaml currency: $ref: ../Rest Responses/rest_response_currency.yaml + custom_attributes_dictionary: + $ref: ../Rest Responses/rest_response_custom_attributes_dictionary.yaml author: $ref: ../Rest Responses/rest_response_author.yaml license_url: @@ -404,6 +418,8 @@ paths: $ref: ../Rest Responses/rest_response_last_updated.yaml currency: $ref: ../Rest Responses/rest_response_currency.yaml + custom_attributes_dictionary: + $ref: ../Rest Responses/rest_response_custom_attributes_dictionary.yaml author: $ref: ../Rest Responses/rest_response_author.yaml license_url: diff --git a/APIs/Events API.yaml b/APIs/Events API.yaml index 65bb45c..dd0dfe0 100644 --- a/APIs/Events API.yaml +++ b/APIs/Events API.yaml @@ -50,6 +50,8 @@ paths: $ref: ../Rest Responses/rest_response_currency.yaml x-stoplight: id: ez7o2wgpfn60b + custom_attributes_dictionary: + $ref: ../Rest Responses/rest_response_custom_attributes_dictionary.yaml author: $ref: ../Rest Responses/rest_response_author.yaml x-stoplight: diff --git a/Rest Responses/rest_response_custom_attributes_dictionary.yaml b/Rest Responses/rest_response_custom_attributes_dictionary.yaml new file mode 100644 index 0000000..238c09e --- /dev/null +++ b/Rest Responses/rest_response_custom_attributes_dictionary.yaml @@ -0,0 +1,13 @@ +title: Custom Attributes Dictionary +x-stoplight: + id: nq1l5v6u0c6hp +type: string +format: uri +description: |- + This is a standard value that may be returned in the body of JSON API endpoints. + + A URL to the data dictionary containing information about the fields and values + used in `custom_attributes`. This should describe the attribute name, data + type, associated CDS element if applicable, and what the attribute represents. + Use `custom_attributes_dictionary` consistently when this field is included. +example: https://city.example.gov/cds/custom-attributes-dictionary.json diff --git a/Rest Responses/rest_response_version.yaml b/Rest Responses/rest_response_version.yaml index fbe48dd..cf0445b 100644 --- a/Rest Responses/rest_response_version.yaml +++ b/Rest Responses/rest_response_version.yaml @@ -6,4 +6,4 @@ description: |- This is a standard value that must be returned in the body of all API endpoints. The version of CDS that the API conforms to. -example: 1.0.0 +example: 1.1.0 diff --git a/V1.1_IMPLEMENTATION_PLAN.md b/V1.1_IMPLEMENTATION_PLAN.md index 949a64a..7e148f0 100644 --- a/V1.1_IMPLEMENTATION_PLAN.md +++ b/V1.1_IMPLEMENTATION_PLAN.md @@ -12,15 +12,21 @@ editing the spec. --- -## Outstanding Decisions +## Highlights / Outstanding Decisions - `custom_attributes_dictionary` vs `custom_attribute_dictionary` - Upstream docs use both names. Pick one local representation and keep it - consistent. + + - `general-information.md` file uses `custom_attributes_dictionary` - https://github.com/openmobilityfoundation/curb-data-specification/blob/release-1.1.0/general-information.md?plain=1 + + - `data-types.md` file uses `custom_attribute_dictionary` - https://github.com/openmobilityfoundation/curb-data-specification/blob/release-1.1.0/data-types.md?plain=1#L17 + + - **Going with `custom_attributes_dictionary` as that is what we have primarily documented.** + - JSON media-type examples still show `version=1.0` - Treat this as upstream text drift and keep it explicit in final review notes. + - Treat this as upstream text drift and keep it explicit in final review notes. + - Metrics is a CSV API - Local OpenAPI modeling can vary, but the wire contract must remain CSV. + - Local OpenAPI modeling can vary, but the wire contract must remain CSV. --- @@ -199,7 +205,7 @@ Strike through completed items as work lands on `draft-v1.1`. - **Files:** `APIs/Curbs API.yaml`, `APIs/Events API.yaml` - **Done when:** - JSON endpoint wrappers match `general-information.md#rest-endpoints` - - the conditional custom attribute dictionary field is handled consistently + - the conditional `custom_attributes_dictionary` field is handled consistently - optional endpoints consistently document `501 Not Implemented` - bearer-auth documentation is aligned where auth is required or recommended From 798d6de32b25b9518c4e6bae0bf4111cd4a2210a Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 21:14:00 -0400 Subject: [PATCH 24/26] feat: complete phase 2 shared model parity --- AGENTS.MD | 7 +- Shared Data Models/aggregate.yaml | 28 +++-- Shared Data Models/curb_area.yaml | 8 ++ Shared Data Models/curb_event.yaml | 180 ++++++++++++++++++++-------- Shared Data Models/curb_object.yaml | 135 +++++++++++++++++++++ Shared Data Models/curb_space.yaml | 27 ++++- Shared Data Models/curb_zone.yaml | 44 ++++++- Shared Data Models/session.yaml | 19 +-- V1.1_IMPLEMENTATION_PLAN.md | 40 +++---- toc.json | 5 + 10 files changed, 393 insertions(+), 100 deletions(-) create mode 100644 Shared Data Models/curb_object.yaml diff --git a/AGENTS.MD b/AGENTS.MD index d0f1124..91e15f3 100644 --- a/AGENTS.MD +++ b/AGENTS.MD @@ -54,8 +54,8 @@ The authoritative CDS spec lives at https://github.com/openmobilityfoundation/cu - Include `description` on every field - Use `x-stoplight: id:` annotations if present on surrounding fields (Stoplight generates these) - Keep `required` arrays up to date -4. **Validate** — run the Spectral linter (`.spectral.mjs`) after changes -5. **Update the plan** — mark the completed step as done in `V1.1_IMPLEMENTATION_PLAN.md` +4. **Validate** — run the Spectral linter (`.spectral.mjs`) after all changes for the task. `spectral` is currently available in this environment, so validation should be performed before wrapping up work. +5. **Update the plan** — use strikethrough in `V1.1_IMPLEMENTATION_PLAN.md` to mark completed items as work lands. Do not add status suffixes like `DONE`, `TODO`, or `Partial` to implementation-step headings. 6. **Commit** — use the commit message suggested in the plan step, or a similar conventional-commit style message ### Style conventions @@ -79,11 +79,10 @@ The authoritative CDS spec lives at https://github.com/openmobilityfoundation/cu The project uses Spectral for OpenAPI linting: ```bash -# If spectral is installed globally spectral lint "APIs/*.yaml" ``` -The `.spectral.mjs` config extends a remote Stoplight ruleset. After making changes, always lint and fix any errors before committing. +The `.spectral.mjs` config extends a remote Stoplight ruleset. `spectral` is currently available in this workspace and should be run after all changes are made. Always lint and fix any errors before committing. When a task adds or edits standalone shared model files, lint those touched files as well in addition to the API documents. ## Key Gotchas diff --git a/Shared Data Models/aggregate.yaml b/Shared Data Models/aggregate.yaml index c495f50..5ae7e9e 100644 --- a/Shared Data Models/aggregate.yaml +++ b/Shared Data Models/aggregate.yaml @@ -4,13 +4,15 @@ x-stoplight: type: object description: 'Aggregates are historic pre-computed counts and metrics of Events occurring in curb places, aggregated to the hour. All Aggregates can be calculated from the data defined in the **Session** object which is returned in the `metrics/sessions` endpoint of the CDS Metrics API.' properties: - curb_space_type: + curb_place_type: + type: string x-stoplight: id: gukvs97dshqqd enum: - area - zone - space + - object description: The type of curb place this aggregate applies to from the Curbs API. curb_place_id: type: string @@ -19,10 +21,12 @@ properties: format: uuid description: The id of the curb place identified in `curb_place_type`. metric_type: + type: string x-stoplight: id: i3l816hllui51 enum: - total_sessions + - total_events - turnover - average_dwell_time - occupancy_percent @@ -32,20 +36,24 @@ properties: x-stoplight: id: lh6zrzck6teja description: 'The date the event occured in ISO 8601 format, local timezone.' - example: '2023-04-31' + format: date + example: '2023-04-30' hour: - type: string + type: integer x-stoplight: id: a3vtd4y1ex7kj - example: '23' + example: 23 description: 'The hour of the day the event occured in ISO 8601 format, local timezone.' value: - type: string + type: number x-stoplight: id: 2r1yhfhxhsuym description: 'The results of the calculations for this metric from the [Methodology](https://github.com/openmobilityfoundation/curb-data-specification/tree/main/metrics#methodology). Note that "-1" means that the sensor/source was offline for the majority of the time.' - custom_attributes: - $ref: ./custom_attributes.yaml - x-stoplight: - id: 3n7k2b5h9qx4z - description: Implementation-specific custom attributes for this aggregate record. + example: 100 +required: + - curb_place_type + - curb_place_id + - metric_type + - date + - hour + - value diff --git a/Shared Data Models/curb_area.yaml b/Shared Data Models/curb_area.yaml index dd4624f..f9407d3 100644 --- a/Shared Data Models/curb_area.yaml +++ b/Shared Data Models/curb_area.yaml @@ -39,6 +39,14 @@ properties: type: string format: uuid example: '0eaa505d-c9e1-49ad-9fac-3c745ba58676' + custom_attributes: + $ref: ./custom_attributes.yaml + description: Additional attributes (fields and data) to include in this endpoint. + external_references: + type: array + description: One or more references to external data feeds impacting this Curb Area. References external data that is relevant to this Area now. If the external reference is temporary, it should be added, then removed when no longer relevant. + items: + $ref: ./external_reference.yaml required: - curb_area_id - geometry diff --git a/Shared Data Models/curb_event.yaml b/Shared Data Models/curb_event.yaml index 7dce5a1..34bff88 100644 --- a/Shared Data Models/curb_event.yaml +++ b/Shared Data Models/curb_event.yaml @@ -6,24 +6,25 @@ description: A Curb Event is a record of activity that happens within the geogra properties: event_id: type: string + format: uuid x-stoplight: id: xjwh3uor9vtj9 description: The globally unique identifier of the event that occurred. - format: uuid event_type: $ref: ./event_type.yaml x-stoplight: id: e9o3ixjkgnvna - description: General activity type that occurred during the event. Required for sources capable of determining activity type for relevant events. + description: The event_type that happened for this event. event_purpose: $ref: ./event_purpose.yaml x-stoplight: id: 3iqfxzhwqegxd + description: General curb usage purpose that the vehicle performed during the event. Required for sources capable of determining activity type for relevant event_types. event_location: $ref: ./geoJSON_point.yaml x-stoplight: id: y19vh7p0lzd87 - description: 'The geo-location of where the event occurred, represented as a GeoJSON Point.' + description: The geographic point location where the event occurred. All efforts should be made to provide this field, even if slightly imprecise. But there may be times when a location is impossible or irrelevant. event_time: $ref: ./timestamp.yaml x-stoplight: @@ -33,33 +34,24 @@ properties: $ref: ./timestamp.yaml x-stoplight: id: 4v1u5gy1laa9l - description: Time at which the event became available for consumption from this API. + description: Time at which the event became available for consumption by this API. event_session_id: type: string + format: uuid x-stoplight: id: jj9z3s5o99fi3 - format: uuid - description: |- - May be provided to tie known connected park_start and park_end event types together by a unique session ID. - - If **not** confident of being able to determine a park_end event at some time after park_start is recorded (i.e., you cannot detect when a vehicle departs), then do **not** use session_id. + description: May be provided to tie known connected `park_start` and `park_end` event types together by a unique session ID. If not confident of being able to determine a `park_end` event at some time after `park_start` is recorded, do not use `event_session_id`. curb_zone_id: type: string + format: uuid x-stoplight: id: 68921cndpqjax - format: uuid - description: |- - Unique ID of the Curb Zone where the event occurred. - - Required for events that occurred at a known Curb Zone for **ALL** event_types. + description: Unique ID of the Curb Zone where the event occurred. Required for events that occurred in a known Curb Zone, if known and used, for ALL event_types. curb_area_ids: type: array x-stoplight: id: f49dygxi2plhh - description: |- - Unique IDs of the Curb Area where the event occurred. Since Curb Areas can overlap, an event may happen in more than one. - - Unique IDs of the Curb Area where the event occurred. Since Curb Areas can overlap, an event may happen in more than one. Required for events that occurred in a known Curb Area, if known and used, for these event_types: `enter_area`, `exit_area`, `park_start`, `park_end` + description: Unique IDs of the Curb Area where the event occurred. Since Curb Areas can overlap, an event may happen in more than one. Required for events that occurred in a known Curb Area, if known and used, for ALL event_types. items: x-stoplight: id: h4hgjz0397qtp @@ -67,37 +59,42 @@ properties: format: uuid curb_space_id: type: string + format: uuid x-stoplight: id: i1hz5ebvpvyp6 + description: Unique ID of the Curb Space where the event occurred. Required for events that occurred at a known Curb Space, if known and used, for ALL event_types. + curb_object_id: + type: string format: uuid - description: |- - Unique ID of the Curb Space where the event occurred. - - Required for events that occurred at a known Curb Space, if known and used, for these event_types: `park_start`, `park_end`, `enter_area`, `exit_area` + x-stoplight: + id: z2v5n8p1r6cqh + description: Unique ID of the Curb Object where the event occurred. Required for events that occurred at a known Curb Object, if known and used, for ALL event_types. data_source_type: $ref: ./data_source_type.yaml x-stoplight: id: tow3anlr8tr36 + description: General category of the source creating the event. data_source_operator_id: $ref: ./operator_id.yaml x-stoplight: id: b7peq7v42kjls + description: Unique identifier of the entity responsible for operating the event data source. IDs can identify the fleet operator sending a data feed, or the organization operating the sensor or other source. data_source_operator_name: type: string x-stoplight: id: swh4u6hrdy0ey - description: |- - Name of the provider responsible for operating the vehicle, device, or sensor at the time of the event. - - May be sent along with `data_source_operator_id` or on its own for small operators at the discretion of the city. + description: Name of the provider responsible for operating the vehicle, device, or sensor at the time of the event. May be sent along with `data_source_operator_id` or on its own for small operators at the discretion of the city. data_source_device_id: type: string + format: uuid x-stoplight: id: w91m0qzkjjacq - description: |- - Unique identifier of this event source, whether sensor, vehicle, camera, etc. Allows agencies to connect related Events as they are recorded by the same source. If coming from a provider, this is a generated UUID they use and not the same as the external `vehicle_id`. - - If this field is needed for your use cases, review our [Privacy Guidance](https://github.com/openmobilityfoundation/curb-data-specification/blob/main/README.md#data-privacy). + description: Unique identifier of this event source, whether sensor, vehicle, camera, or similar device. Allows agencies to connect related Events as they are recorded by the same source. If coming from a provider, this is a generated UUID they use and not the same as the external `vehicle_id`. + data_source_device_name: + type: string + x-stoplight: + id: r7c1m0f2l4pda + description: Unique name of this event source, whether sensor, vehicle, camera, or similar device. Must be provided if the device has a visible or shared identifier. data_source_manufacturer: type: string x-stoplight: @@ -112,72 +109,155 @@ properties: type: boolean x-stoplight: id: 3i9prwaz5klwz - description: 'If a sensor was used to capture this event, the commissioned status at the time that the event was reported. Indicates whether the sensor is currently in a state where it should be reporting data.' + description: If a sensor was used to capture this event, the commissioned status at the time that the event was reported. Indicates whether the sensor is currently in a state where it should be reporting data. sensor_status_is_online: type: boolean x-stoplight: id: 9ra1u95oix354 - description: 'If a sensor was used to capture this event, the online status at the time that the event was reported. Indicates whether the sensor is currently online and reporting data.' + description: If a sensor was used to capture this event, the online status at the time that the event was reported. Indicates whether the sensor is currently online and reporting data. vehicle_id: type: string x-stoplight: id: 3lyyz739s4ew1 - description: 'A vehicle identifier visible externally on the vehicle itself. If this field is needed for your use cases, review our [Privacy Guidance](https://github.com/openmobilityfoundation/curb-data-specification/blob/main/README.md#data-privacy).' + description: A vehicle identifier visible externally on the vehicle itself. If this field is needed for your use cases, review the CDS privacy guidance. vehicle_license_plate: type: string x-stoplight: id: cckfib2gi29uh - description: 'The consistently placed vehicle license plate, usable by ALPR systems, when required for curb use. This field is potentially sensitive (depending on local, state, and national laws) and a data privacy framework is recommended for collecting, retention, deletion, obfuscation, and security. If this field is needed for your use cases, review our [Privacy Guidance](https://github.com/openmobilityfoundation/curb-data-specification/blob/main/README.md#data-privacy).' + description: The consistently placed vehicle license plate, usable by ALPR systems, when required for curb use. This field may be sensitive depending on jurisdiction. + vehicle_license_plate_jurisdiction: + type: string + x-stoplight: + id: p1x9v0r6m2qsa + description: Jurisdiction or state in which the `vehicle_license_plate` is registered. + vehicle_license_plate_confidence: + type: integer + minimum: 1 + maximum: 100 + x-stoplight: + id: n6c4t7h2z8bpa + description: Value from 1 to 100 specifying the recognition confidence level for `vehicle_license_plate`. vehicle_permit_number: type: string x-stoplight: id: p9m8k37j17kcv - description: 'If applicable, the assigned permit number for this vehicle from the city agency.' + description: If applicable, the assigned permit number for this vehicle from the city agency. vehicle_length: - type: number + type: integer x-stoplight: id: zax8xvn9afq87 - description: |- - Approximate length of the vehicle that performed the event, measured in centimeters. - - Required for sources capable of determining vehicle length. + description: Approximate length of the vehicle that performed the event, measured in centimeters. Required for sources capable of determining vehicle length. + vehicle_type: + $ref: ./vehicle_type.yaml + x-stoplight: + id: d4w1o9q7e2lks + description: Type of the vehicle that performed the event. Required for sources capable of determining vehicle type. + vehicle_type_confidence: + type: integer + minimum: 1 + maximum: 100 + x-stoplight: + id: q3j8f0k1w5rns + description: Value from 1 to 100 specifying the recognition confidence level for `vehicle_type`. + vehicle_color: + type: string + x-stoplight: + id: t8m2c5d9y1vhr + description: Color of the vehicle that performed the event. + vehicle_color_confidence: + type: integer + minimum: 1 + maximum: 100 + x-stoplight: + id: b7n4p3k6s8ywd + description: Value from 1 to 100 specifying the recognition confidence level for `vehicle_color`. + vehicle_company_name: + type: string + x-stoplight: + id: v0m3q7n5x2lzc + description: Company or courier name of the vehicle that performed the event. + vehicle_company_name_confidence: + type: integer + minimum: 1 + maximum: 100 + x-stoplight: + id: h5c2r8j1p9dsa + description: Value from 1 to 100 specifying the recognition confidence level for `vehicle_company_name`. + vehicle_run_id: + type: string + x-stoplight: + id: k9d4s1f7m6qla + description: Run identifier from an external runs table containing information about the make, model, and/or year of the vehicle that performed the event. + vehicle_run_id_confidence: + type: integer + minimum: 1 + maximum: 100 + x-stoplight: + id: m1p8x3w6c4znt + description: Value from 1 to 100 specifying the recognition confidence level for `vehicle_run_id`. vehicle_propulsion_types: $ref: ./propulsion_type.yaml x-stoplight: id: yzh8z7wdjj72a + description: List of propulsion types used by the vehicle that performed the event. Required for sources capable of determining vehicle propulsion type. vehicle_blocked_lane_types: - $ref: ./lane_type.yaml + type: array x-stoplight: id: z3n62d0081u5j + description: Type(s) of lane blocked by the vehicle performing the event. If no lanes are blocked by the vehicle performing the event, the array should be empty. Required for sources capable of determining it for `park_start` events. + items: + $ref: ./lane_type.yaml curb_occupants: type: array x-stoplight: id: jysyhxqxkjnnz + description: Current occupants of the Curb Zone. If the source can determine linear location, the array should be ordered by the start of each occupant's linear reference. items: $ref: ./curb_occupant.yaml x-stoplight: id: bzse7o4oic8h4 actual_cost: type: integer + example: 100 x-stoplight: id: aifcs5wjnhqhu - description: 'If available from the source, the actual cost, in the currency defined in currency, paid by the curb user for this event. The currency type is sent in with the REST Endpoints JSON object. All costs should be given as integers in the currency''s smallest unit. As an example, to represent $1 USD, specify an amount of 100 (for 100 cents).' - example: 100 + description: If available from the source, the actual cost paid by the curb user for this event, in the currency's smallest unit. + enforcement: + $ref: ./enforcement.yaml + x-stoplight: + id: a8q2v6n1t5mzr + description: Enforcement information related to this Curb Event, relevant to the moment in time the event happens. Only used for enforcement related events such as `vehicle_detected`, `vehicle_violation_start`, `vehicle_violation_end`, and `citation_issued`. + payment_channel: + $ref: ./payment_channel.yaml + x-stoplight: + id: c6h1n4j8q5sza + description: If available from the source, the medium by which a user submitted payment. + payment_method: + $ref: ./payment_method.yaml + x-stoplight: + id: e4p9d2k7m1rxc + description: If available from the source, the method used to pay for this event. + payment_transaction_id: + type: string + x-stoplight: + id: u3k8m1x5q7vbd + description: The transaction ID of the payment if available from the source and different from the `event_id`. + custom_attributes: + $ref: ./custom_attributes.yaml + x-stoplight: + id: 4l3d9m8q2sk1v + description: Additional attributes (fields and data) to include in this endpoint. external_references: type: array x-stoplight: id: 7p2n4a8k3jw0c - description: External references that link this event to other systems or specifications. + description: One or more references to external data sources impacting this Curb Event. The external reference is relevant to the moment in time the event happens. items: $ref: ./external_reference.yaml - custom_attributes: - $ref: ./custom_attributes.yaml - x-stoplight: - id: 4l3d9m8q2sk1v - description: Implementation-specific custom attributes for this event. required: - event_id - - event_location + - event_type - event_time + - event_publication_time - data_source_type - data_source_device_id diff --git a/Shared Data Models/curb_object.yaml b/Shared Data Models/curb_object.yaml new file mode 100644 index 0000000..6500150 --- /dev/null +++ b/Shared Data Models/curb_object.yaml @@ -0,0 +1,135 @@ +title: Curb Object +x-stoplight: + id: 2b5f7d9n1c4qa +type: object +description: | + Defines individual assets located adjacent to, overlapping, within, or associated with a Curb Space or Curb Zone. Important notes about Curb Objects: + * Curb Objects can be located anywhere within, beside, or overlapping with Curb Zones, Spaces, or other Curb Objects + * Curb Objects must be associated with either a Curb Space or Curb Zone + * Curb Objects do not typically have Curb Policies linked directly to them (unless the object is directly aligned with a single Policy, using the optional `curb_policy_id` field). Associated Curb Policies can be found by looking at the related Curb Zone, either directly or through the Curb Space. + * Unlike Zones and similar to Spaces, Objects may be updated as needed, with a new `curb_object_id` being optionally assigned by the city +properties: + curb_object_id: + type: string + format: uuid + x-stoplight: + id: 6z1d4q8m2v7hy + description: The ID of the curb object. + geometry: + $ref: ./geoJSON_point.yaml + x-stoplight: + id: 1f8k3s9b6t2wr + description: The spatial location of this curb object location. This can represent the approximate center of the object, or the centroid location of the object, depending on its size and shape. + curb_zone_id: + type: string + format: uuid + x-stoplight: + id: 3v8m1q5d9n2kc + description: The ID of the Curb Zone this object is physically in or closest to. The geometry of the specified Curb Zone does not need to directly relate to the geometry of this object. Either a Zone or Space ID is required for an Object. + curb_space_id: + type: string + format: uuid + x-stoplight: + id: 5d1n8k3p7v2qx + description: The ID of the Curb Space this object is physically in or closest to. The geometry of the specified Curb Space does not need to directly relate to the geometry of this object. Either a Zone or Space ID is required for an Object. + curb_policy_id: + type: string + format: uuid + x-stoplight: + id: 2x7c4m9v1n6qd + description: ID of Policy object that is directly associated with this curb object. For example, `signage` or `paint` that relates to a single policy. + lane_type: + $ref: ./lane_type.yaml + x-stoplight: + id: 6q1v8n3c5m7px + description: The type of lane the curb object is primarily located in. + object_type: + type: string + x-stoplight: + id: 8c2m5n1p7v4qx + description: The category of the curb object. CDS includes well-known recommended values such as `signage`, `bus_stop`, `bike_rack`, `scooter_parking`, `ev_charging`, `ramp`, `meter`, `pay_station`, `paint`, `lighting`, `signal_cabinet`, `utility_box`, `fire_hydrant`, `surveillance_camera`, `barrier`, `bollard`, `street_trees`, `planter`, `drinking_fountain`, `toilet`, `bench`, `sculpture`, `art`, `fountain`, `solid_waste_bins`, `post_box`, `locker`, and `food_vendor`. The upstream list is intentionally not exhaustive, so additional local object types may be used when needed. + example: signage + name: + type: string + x-stoplight: + id: 4h9p2v6c1x8nd + description: A short name of this curb object for reference. + description: + type: string + x-stoplight: + id: 1c6m9q4v8n2px + description: A more detailed description of the object if needed. + owner: + type: string + x-stoplight: + id: 8m2q5v1n7c4pd + description: The name of the agency, department, or similar entity responsible for maintaining this object. + operator: + type: string + x-stoplight: + id: 4n8c1q7v5m2px + description: The name of the agency, department, or similar entity responsible for operating this object. + object_shape: + $ref: ./geoJSON_polygon.yaml + x-stoplight: + id: 7p1m4v8c2n6qd + description: A simplified geometric outline of this object. Recommended for objects that are an unusual shape and may affect curb activities. + object_line: + $ref: ./geoJSON_linestring.yaml + x-stoplight: + id: 3q6v1n8m5c2px + description: A simplified geometric line defining this object. Recommended for objects that may affect or help define curb activities, like `paint`. + linear_distance: + type: integer + x-stoplight: + id: 9n2c5m1q7v4pd + description: Parallel distance from the side of the object to the linear referencing start point of the curb, in centimeters. + perpendicular_distance: + type: integer + x-stoplight: + id: 2p7m4v8n1c6qx + description: Perpendicular distance from the front of the object to the curb edge start or end, in centimeters. This distance can be negative or positive, with the positive direction being from the curb toward the sidewalk. + max_length: + type: integer + x-stoplight: + id: 5m1q8v3n7c2pd + description: Maximum, bounding-box length of the object parallel to the curb, in centimeters. + max_depth: + type: integer + x-stoplight: + id: 1v6c9m4q8n2px + description: Maximum, bounding-box depth of the object perpendicular to the curb, in centimeters. + max_height: + type: integer + x-stoplight: + id: 8q3n5m1v7c2pd + description: Maximum, bounding-box height of the object from the sidewalk or street surface, in centimeters. + published_date: + $ref: ./timestamp.yaml + x-stoplight: + id: 7r3j6d1f8m2qp + description: The date/time that this curb object was first published in this data feed. + last_updated_date: + $ref: ./timestamp.yaml + x-stoplight: + id: 9k5n2b7t4c1sx + description: The date/time that the properties of this curb object were last updated. This helps consumers know that some fields may have changed. + custom_attributes: + $ref: ./custom_attributes.yaml + x-stoplight: + id: 4m7q1n8v3c2px + description: Additional attributes (fields and data) to include in this endpoint. + external_references: + type: array + x-stoplight: + id: 6n2c5m1q8v4pd + description: One or more references to external data feeds impacting this Curb Object. References external data that is relevant to this Object now. If the external reference is temporary, it should be added, then removed when no longer relevant. + items: + $ref: ./external_reference.yaml +required: + - curb_object_id + - geometry + - object_type + - name + - published_date + - last_updated_date diff --git a/Shared Data Models/curb_space.yaml b/Shared Data Models/curb_space.yaml index 184a623..ac7ab3e 100644 --- a/Shared Data Models/curb_space.yaml +++ b/Shared Data Models/curb_space.yaml @@ -18,13 +18,13 @@ properties: x-stoplight: id: r3a5hu6diex3r description: |- - The date/time that this curb area was first published in this data feed. + The date/time that this curb space was first published in this data feed. An integer representing a number of milliseconds since midnight, January 1st, 1970 UTC (the UNIX epoch) last_updated_date: $ref: ./timestamp.yaml description: |- - The date/time that the properties of ths curb area were last updated. This helps consumers know that some fields may have changed. + The date/time that the properties of this curb space were last updated. This helps consumers know that some fields may have changed. An integer representing a number of milliseconds since midnight, January 1st, 1970 UTC (the UNIX epoch) curb_zone_id: @@ -32,6 +32,12 @@ properties: type: string format: uuid example: '0eaa505d-c9e1-49ad-9fac-3c745ba58676' + curb_object_ids: + type: array + description: The ID(s) of the Curb Objects that this Curb Space is related to, in particular what Objects are in the Space's areas of influence. For example, a meter being used for two paid parking spaces, a locker for a commercial loading space, or a camera monitoring several spaces. If specified, the objects identified MUST be retrievable through a Curbs API Objects endpoint. + items: + type: string + format: uuid space_number: type: integer description: 'The sequence number of this space within its Zone. If specified, two spaces within the same Curb Zone MUST NOT share a space number, and space numbers SHOULD be consecutive positive integers starting at 1.' @@ -51,8 +57,23 @@ properties: If availability information is present, the most recent time that availability was computed for this space. An integer representing a number of milliseconds since midnight, January 1st, 1970 UTC (the UNIX epoch) + custom_attributes: + $ref: ./custom_attributes.yaml + description: Additional attributes (fields and data) to include in this endpoint. + external_references: + type: array + description: One or more references to external data feeds impacting this Curb Space. References external data that is relevant to this Space now. If the external reference is temporary, it should be added, then removed when no longer relevant. + items: + $ref: ./external_reference.yaml description: | Defines individual demarcated spaces within a Curb Zone. Important notes about Curb Spaces: * Curb Spaces may NOT overlap with other Curb Spaces - * Curb Spaces must be wholly contained within a single Curb Zone + * Curb Spaces must be wholly contained within or associated with a single Curb Zone * Unlike Zones, Spaces may be updated as needed, with a new `curb_space_id` being optionally assigned by the city +required: + - curb_space_id + - geometry + - published_date + - last_updated_date + - curb_zone_id + - length diff --git a/Shared Data Models/curb_zone.yaml b/Shared Data Models/curb_zone.yaml index 235d31b..6c9227a 100644 --- a/Shared Data Models/curb_zone.yaml +++ b/Shared Data Models/curb_zone.yaml @@ -21,13 +21,19 @@ properties: format: uuid example: '0eaa505d-c9e1-49ad-9fac-3c745ba58676' geometry: - $ref: ./geoJSON_polygon.yaml - description: The spatial extent of this Curb Zone. + oneOf: + - $ref: ./geoJSON_polygon.yaml + - $ref: ./geoJSON_linestring.yaml + description: The spatial extent of this curb zone. A new `curb_zone_id` is required if this geometry changes. A two-dimensional polygon is preferred, but a LineString is acceptable. Include the `width` field if known. + description: + type: string + description: A more detailed description of the zone if needed. curb_policy_ids: type: array description: 'An array of IDs of Policy objects. Together, these define the regulations of this Curb Zone.' items: type: string + format: uuid published_date: $ref: ./timestamp.yaml prev_policies: @@ -52,6 +58,25 @@ properties: - end_date last_updated_date: $ref: ./timestamp.yaml + available: + type: boolean + description: If `true`, the curb zone is open for normal use. If `false`, the curb zone is closed and vehicles should look for alternative zones. Do not provide this field if the availability is unknown. + available_spaces: + type: integer + description: The number of curb spaces that are open for use within the Zone. Spaces could be a count of available marked or delineated spaces, or a count of the available CDS Curb Spaces. + jurisdiction_type: + type: string + enum: + - public + - private + - other + description: Describes the type of entity that has jurisdiction of this curb zone. Use `public` for curb zones maintained by a public sector agency, `private` for curb zones maintained by a private entity, or `other` for other cases. + owner_name: + type: string + description: Identifies the name of the owner of the curb zone. + address_number: + type: string + description: The address number, street number, civic number, or number range of the street that this Curb Zone is on. prev_curb_zone_ids: type: array description: An array of IDs of previous curb zone objects. They are listed in order with the most recent ones first. @@ -161,12 +186,27 @@ properties: items: type: string format: uuid + curb_object_ids: + type: array + description: |- + The ID(s) of the Curb Objects that this Curb Zone is related to, in particular what Objects are in the Zone's areas of influence. For example, a pay station being used for multiple paid parking zones, a locker for a commercial loading zone, or a camera monitoring several zones. If specified, the objects identified MUST be retrievable through a Curbs API Objects endpoint. + items: + type: string + format: uuid curb_space_ids: type: array description: The ID(s) of the Curb Spaces that this Curb Zone contains. items: type: string format: uuid + custom_attributes: + $ref: ./custom_attributes.yaml + description: Additional attributes (fields and data) to include in this endpoint. + external_references: + type: array + description: One or more references to external data feeds impacting this Curb Zone. The external reference should be relevant to this Zone now and may be updated without requiring a new `curb_zone_id`. + items: + $ref: ./external_reference.yaml required: - curb_zone_id - geometry diff --git a/Shared Data Models/session.yaml b/Shared Data Models/session.yaml index cc0d220..9b0092b 100644 --- a/Shared Data Models/session.yaml +++ b/Shared Data Models/session.yaml @@ -5,6 +5,7 @@ type: object description: 'Sessions are a historic subset of curb events, with some rows combined, some columns removed for clarity and privacy, and for only some curb event types. Sessions are meant to provide a granular view of parking and area sessions happening around the curb places, so consumers can do their own analysis.' properties: session_type: + type: string x-stoplight: id: 49itgyeou9waj description: The type of session that happened for this event. @@ -15,17 +16,20 @@ properties: type: string x-stoplight: id: 8lcen78qfos5h - description: Used to tie two events together. Will be the `event_session_id` from two events that are related. + description: If known and recorded to tie two Events together, include the `event_session_id` from the Curb Event. + format: uuid event_id_start: type: string x-stoplight: id: fvcmaudenb031 description: The globally unique identifier of the start/enter event that occurred. + format: uuid event_id_end: type: string x-stoplight: id: wlxhekqn5l8bh description: The globally unique identifier of the end/exit event that occurred. + format: uuid event_location_start_latitude: $ref: ./latitude.yaml x-stoplight: @@ -50,7 +54,7 @@ properties: $ref: ./timestamp.yaml x-stoplight: id: 8pxac87n00v0s - description: Timestamp (date/time) at which the event started with the `park_start` or `enter_area`` event types. + description: Timestamp (date/time) at which the event started with the `park_start` or `enter_area` event types. event_time_end: $ref: ./timestamp.yaml x-stoplight: @@ -78,6 +82,12 @@ properties: id: a1ikxiabeqv97 description: 'Unique ID of the Curb Space where the event occurred. Conditionally required for events that occurred at a known Curb Space for these event_types: park_start, park_end, enter_area, exit_area' format: uuid + curb_object_id: + type: string + x-stoplight: + id: 0f6kq2w1z9hmc + description: 'Unique ID of the Curb Object where the event occurred. Conditionally required for events that occurred at a known Curb Object for these event_types: `park_start`, `park_end`, `enter_area`, `exit_area`.' + format: uuid vehicle_length: type: integer x-stoplight: @@ -88,10 +98,5 @@ properties: x-stoplight: id: 07zos4ceswa84 description: Type of the vehicle that performed the event. Conditionally required for sources capable of determining vehicle type. - custom_attributes: - $ref: ./custom_attributes.yaml - x-stoplight: - id: 1c9m4p7s2vd8k - description: Implementation-specific custom attributes for this session. required: - session_type diff --git a/V1.1_IMPLEMENTATION_PLAN.md b/V1.1_IMPLEMENTATION_PLAN.md index 7e148f0..5b1ac81 100644 --- a/V1.1_IMPLEMENTATION_PLAN.md +++ b/V1.1_IMPLEMENTATION_PLAN.md @@ -22,18 +22,10 @@ editing the spec. - **Going with `custom_attributes_dictionary` as that is what we have primarily documented.** -- JSON media-type examples still show `version=1.0` - - Treat this as upstream text drift and keep it explicit in final review notes. - -- Metrics is a CSV API - - Local OpenAPI modeling can vary, but the wire contract must remain CSV. - --- ## Active Path Forward -Strike through completed items as work lands on `draft-v1.1`. - ### Phase 1 – Foundation And Early Alignment - ~~Bump the API specs to v1.1 and clean up baseline metadata and lint issues.~~ @@ -53,7 +45,7 @@ Strike through completed items as work lands on `draft-v1.1`. ### Phase 2 – Shared Model Parity -#### 2A. `curb_zone.yaml` — TODO +#### ~~2A. `curb_zone.yaml`~~ - **Files:** `Shared Data Models/curb_zone.yaml` - **Done when:** @@ -65,7 +57,7 @@ Strike through completed items as work lands on `draft-v1.1`. `external_references` - UUID typing is corrected where needed for policy ID arrays -#### 2B. `curb_area.yaml` — TODO +#### ~~2B. `curb_area.yaml`~~ - **Files:** `Shared Data Models/curb_area.yaml` - **Done when:** @@ -73,7 +65,7 @@ Strike through completed items as work lands on `draft-v1.1`. - `external_references` is added - the required fields still match the upstream table -#### 2C. `curb_space.yaml` — TODO +#### ~~2C. `curb_space.yaml`~~ - **Files:** `Shared Data Models/curb_space.yaml` - **Done when:** @@ -84,7 +76,7 @@ Strike through completed items as work lands on `draft-v1.1`. - `curb_object_ids` is described as conditionally required only in prose, not made globally required in schema -#### 2D. `curb_object.yaml` — TODO +#### ~~2D. `curb_object.yaml`~~ - **Files:** `Shared Data Models/curb_object.yaml` - **Done when:** @@ -98,7 +90,7 @@ Strike through completed items as work lands on `draft-v1.1`. `lane_type`, shape or line geometry, dimensions, `custom_attributes`, and `external_references` -#### 2E. `curb_event.yaml` — Partial +#### ~~2E. `curb_event.yaml`~~ - **Files:** `Shared Data Models/curb_event.yaml` - **Done when:** @@ -115,7 +107,7 @@ Strike through completed items as work lands on `draft-v1.1`. - `event_location` is no longer globally required - UUID typing and field descriptions match the upstream table -#### 2F. `session.yaml` — Partial +#### ~~2F. `session.yaml`~~ - **Files:** `Shared Data Models/session.yaml` - **Done when:** @@ -123,7 +115,7 @@ Strike through completed items as work lands on `draft-v1.1`. - the local-only `custom_attributes` field is removed or explicitly reverted - field types and descriptions match the current Metrics session columns -#### 2G. `aggregate.yaml` — Partial +#### ~~2G. `aggregate.yaml`~~ - **Files:** `Shared Data Models/aggregate.yaml` - **Done when:** @@ -139,7 +131,7 @@ Strike through completed items as work lands on `draft-v1.1`. ### Phase 3 – API Parity -#### 3A. `Curbs API.yaml` — TODO +#### 3A. `Curbs API.yaml` - **Files:** `APIs/Curbs API.yaml` - **Done when:** @@ -149,7 +141,7 @@ Strike through completed items as work lands on `draft-v1.1`. - both object endpoints include `501 Not Implemented` - list responses use the same `data.` wrapper pattern consistently -#### 3B. `Events API.yaml` GET parity — TODO +#### 3B. `Events API.yaml` GET parity - **Files:** `APIs/Events API.yaml` - **Done when:** @@ -157,7 +149,7 @@ Strike through completed items as work lands on `draft-v1.1`. - `curb_object_id` is added to `GET /events/events` - `curb_object_id` is added to `GET /events/status` -#### 3C. `Events API.yaml` POST parity — TODO +#### 3C. `Events API.yaml` POST parity - **Files:** `APIs/Events API.yaml` - **Done when:** @@ -166,7 +158,7 @@ Strike through completed items as work lands on `draft-v1.1`. - response coverage matches upstream: `200`, `201`, `400`, `401`, `404`, `406`, `409`, `500`, `501` -#### 3D. `Events API.yaml` auth and error guidance — TODO +#### 3D. `Events API.yaml` auth and error guidance - **Files:** `APIs/Events API.yaml` - **Done when:** @@ -175,7 +167,7 @@ Strike through completed items as work lands on `draft-v1.1`. - event ordering guidance is aligned with `general-information.md#event-times` - error and bulk-response descriptions align with upstream general information -#### 3E. `Metrics API.yaml` contract parity — TODO +#### 3E. `Metrics API.yaml` contract parity - **Files:** `APIs/Metrics API.yaml` - **Done when:** @@ -187,7 +179,7 @@ Strike through completed items as work lands on `draft-v1.1`. - `start_time` is documented as inclusive - `end_time` is documented as exclusive -#### 3F. `Metrics API.yaml` descriptive parity — TODO +#### 3F. `Metrics API.yaml` descriptive parity - **Files:** `APIs/Metrics API.yaml` - **Done when:** @@ -200,7 +192,7 @@ Strike through completed items as work lands on `draft-v1.1`. ### Phase 4 – Validation And Cleanup -#### 4A. JSON wrapper and auth cleanup — TODO +#### 4A. JSON wrapper and auth cleanup - **Files:** `APIs/Curbs API.yaml`, `APIs/Events API.yaml` - **Done when:** @@ -209,7 +201,7 @@ Strike through completed items as work lands on `draft-v1.1`. - optional endpoints consistently document `501 Not Implemented` - bearer-auth documentation is aligned where auth is required or recommended -#### 4B. `toc.json` — Partial +#### 4B. `toc.json` - **Files:** `toc.json` - **Done when:** @@ -218,7 +210,7 @@ Strike through completed items as work lands on `draft-v1.1`. `Payment Method`~~ - `Curb Object` is added under `CDS Models` -#### 4C. Lint and final upstream review — TODO +#### 4C. Lint and final upstream review - **Files:** full repo - **Done when:** diff --git a/toc.json b/toc.json index 474573a..3f5bd05 100644 --- a/toc.json +++ b/toc.json @@ -39,6 +39,11 @@ "title": "Curb Space", "uri": "/Shared Data Models/curb_space.yaml" }, + { + "type": "item", + "title": "Curb Object", + "uri": "/Shared Data Models/curb_object.yaml" + }, { "type": "item", "title": "Curb Policy", From 4ac0ef21b81ddaca0c307c35abc8bd5603cab73b Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Tue, 7 Apr 2026 21:31:01 -0400 Subject: [PATCH 25/26] feat: implement phase 3 API parity --- APIs/Curbs API.yaml | 165 ++++++++++++++++++++++++++++------- APIs/Events API.yaml | 166 +++++++++++++++++++++++++++++++++++- APIs/Metrics API.yaml | 126 ++++++++++----------------- V1.1_IMPLEMENTATION_PLAN.md | 12 +-- 4 files changed, 346 insertions(+), 123 deletions(-) diff --git a/APIs/Curbs API.yaml b/APIs/Curbs API.yaml index 23f57f8..7a34ff3 100644 --- a/APIs/Curbs API.yaml +++ b/APIs/Curbs API.yaml @@ -15,6 +15,8 @@ info: tags: - name: areas description: Query and fetch curb areas. + - name: objects + description: Query and fetch curb objects. - name: policies description: Query and fetch curb policies. - name: spaces @@ -52,14 +54,12 @@ paths: license_url: $ref: ../Rest Responses/rest_response_license_url.yaml data: - type: array - items: - type: object - properties: - zones: - type: array - items: - $ref: ../Shared Data Models/curb_zone.yaml + type: object + properties: + zones: + type: array + items: + $ref: ../Shared Data Models/curb_zone.yaml operationId: get-curbs-zones description: 'This required endpoint must be implemented by every Curbs API server. If attaching policies to curb zones, the Query Curb Policies endpoint is also required.' parameters: @@ -112,14 +112,12 @@ paths: license_url: $ref: ../Rest Responses/rest_response_license_url.yaml data: - type: array - items: - type: object - properties: - areas: - type: array - items: - $ref: ../Shared Data Models/curb_area.yaml + type: object + properties: + areas: + type: array + items: + $ref: ../Shared Data Models/curb_area.yaml '501': description: NOT_IMPLEMENTED operationId: get-curbs-areas @@ -163,14 +161,12 @@ paths: license_url: $ref: ../Rest Responses/rest_response_license_url.yaml data: - type: array - items: - type: object - properties: - spaces: - type: array - items: - $ref: ../Shared Data Models/curb_space.yaml + type: object + properties: + spaces: + type: array + items: + $ref: ../Shared Data Models/curb_space.yaml '501': description: NOT_IMPLEMENTED operationId: get-curbs-spaces @@ -196,6 +192,113 @@ paths: security: [] x-stoplight: id: p8unincykkub4 + /curbs/objects: + get: + summary: Get Curb Objects + tags: + - objects + responses: + '200': + description: Objects Found + content: + application/json: + schema: + type: object + properties: + version: + $ref: ../Rest Responses/rest_response_version.yaml + time_zone: + $ref: ../Rest Responses/rest_response_time_zone.yaml + last_updated: + $ref: ../Rest Responses/rest_response_last_updated.yaml + currency: + $ref: ../Rest Responses/rest_response_currency.yaml + custom_attributes_dictionary: + $ref: ../Rest Responses/rest_response_custom_attributes_dictionary.yaml + author: + $ref: ../Rest Responses/rest_response_author.yaml + license_url: + $ref: ../Rest Responses/rest_response_license_url.yaml + data: + type: object + properties: + objects: + type: array + items: + $ref: ../Shared Data Models/curb_object.yaml + '501': + description: NOT_IMPLEMENTED + operationId: get-curbs-objects + description: 'Optional endpoint. If not implemented, the server should reply with 501 Not Implemented.' + parameters: + - schema: + $ref: ../Shared Data Models/timestamp.yaml + in: query + name: time + description: Only the most recently updated objects as of this time will be returned. + - schema: + type: string + format: uuid + in: query + name: zone + description: The ID of a Curb Zone. If specified, only return Curb Objects associated within this zone. + - schema: + type: string + format: uuid + in: query + name: space + description: The ID of a Curb Space. If specified, only return Curb Objects associated within this space. + security: [] + x-stoplight: + id: 5vgp4n8h2j1qd + '/curbs/objects/{id}': + get: + summary: Get a single Curb Object + tags: + - objects + responses: + '200': + description: Object Found + content: + application/json: + schema: + type: object + properties: + version: + $ref: ../Rest Responses/rest_response_version.yaml + time_zone: + $ref: ../Rest Responses/rest_response_time_zone.yaml + last_updated: + $ref: ../Rest Responses/rest_response_last_updated.yaml + currency: + $ref: ../Rest Responses/rest_response_currency.yaml + custom_attributes_dictionary: + $ref: ../Rest Responses/rest_response_custom_attributes_dictionary.yaml + author: + $ref: ../Rest Responses/rest_response_author.yaml + license_url: + $ref: ../Rest Responses/rest_response_license_url.yaml + data: + $ref: ../Shared Data Models/curb_object.yaml + '501': + description: NOT_IMPLEMENTED + operationId: get-curbs-object + description: 'Fetch a single Curb Object by ID. Optional endpoint. If not implemented, the server should reply with 501 Not Implemented.' + parameters: + - schema: + type: string + name: id + in: path + required: true + description: Curb Object ID + - schema: + $ref: ../Shared Data Models/timestamp.yaml + in: query + name: time + description: Availability data (if supplied) will be returned as of this time. + security: [] + x-stoplight: + id: 2q7b9n4d5k1mv /curbs/policies: get: summary: Get Curb Policies @@ -224,14 +327,12 @@ paths: license_url: $ref: ../Rest Responses/rest_response_license_url.yaml data: - type: array - items: - type: object - properties: - policies: - type: array - items: - $ref: ../Shared Data Models/curb_policy.yaml + type: object + properties: + policies: + type: array + items: + $ref: ../Shared Data Models/curb_policy.yaml '501': description: NOT_IMPLEMENTED operationId: get-curbs-policies diff --git a/APIs/Events API.yaml b/APIs/Events API.yaml index dd0dfe0..b421ef3 100644 --- a/APIs/Events API.yaml +++ b/APIs/Events API.yaml @@ -77,10 +77,18 @@ paths: x-stoplight: id: er2r1dx6pype6 parameters: + - schema: + type: string + in: query + name: event_time + description: An ISO 8601 extended datetime representing a UTC hour between 00 and 23 in `YYYY-MM-DDTHH` format. If omitted, return all events from the last 60 minutes. For example, `event_time=2019-10-01T07` returns events where `2019-10-01T07:00:00 <= event.event_time < 2019-10-01T08:00:00` UTC. - $ref: '#/components/parameters/curb_area_id' - $ref: '#/components/parameters/curb_zone_id' - $ref: '#/components/parameters/curb_space_id' - description: This required endpoint must be implemented by every Events API server. Enables the querying of CDS events. + - $ref: '#/components/parameters/curb_object_id' + description: |- + Authorization is recommended for this endpoint. + This required endpoint must be implemented by every Events API server. It returns CDS events ordered by `event_time`, with the newest events first, following the Event Times guidance. /events/status: get: summary: Get Status @@ -140,10 +148,162 @@ paths: - $ref: '#/components/parameters/curb_area_id' - $ref: '#/components/parameters/curb_zone_id' - $ref: '#/components/parameters/curb_space_id' - description: Optional endpoint. Enables the querying of statuses for various sensors monitoring curb places. + - $ref: '#/components/parameters/curb_object_id' + description: |- + Authorization is recommended for this endpoint. + Optional endpoint. Enables the querying of statuses for various sensors monitoring curb places. If not implemented, the server should reply with `501 Not Implemented`. + /events/event: + post: + summary: Push Event + tags: + - events + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/event_batch_request' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/event_bulk_response' + '201': + description: Created + content: + application/json: + schema: + $ref: '#/components/schemas/event_bulk_response' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/event_error_response' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/event_error_response' + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/event_error_response' + '406': + description: Not Acceptable + content: + application/json: + schema: + $ref: '#/components/schemas/event_error_response' + '409': + description: Conflict + content: + application/json: + schema: + $ref: '#/components/schemas/event_error_response' + '500': + description: Internal Server Error. The response may contain a plain-text error message for troubleshooting. + content: + application/json: + schema: + $ref: '#/components/schemas/event_error_response' + text/plain: + schema: + type: string + '501': + description: Not Implemented + operationId: post-events-event + description: |- + Authorization is required for this endpoint. + Optional endpoint. Enables submission of CDS events. If not implemented, the server should reply with `501 Not Implemented`. Servers implementing this endpoint should deduplicate events from a publisher based on the `event_id` field, and should expect repeated submissions after interrupted network or system connections. components: - schemas: {} + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: Bearer token authentication. JWT is recommended as the token format. + schemas: + event_batch_request: + type: array + description: Array of Curb Event objects to submit to `POST /events/event`. + items: + $ref: ../Shared Data Models/curb_event.yaml + event_bulk_failure: + type: object + description: Details for one failed item in a bulk event submission response. + required: + - item + - error + - error_description + properties: + item: + $ref: ../Shared Data Models/curb_event.yaml + description: The submitted event that failed processing. + error: + type: string + description: Short machine-readable error code for the failed item. + error_description: + type: string + description: Human-readable explanation of why the item failed. + error_details: + type: array + description: Array of field names or other details that explain the failure. + items: + type: string + event_bulk_response: + type: object + description: Bulk response for multi-record `POST /events/event` submissions. `success` counts written records, `total` counts submitted records, and `failures` lists item-level errors. + required: + - success + - total + - failures + properties: + success: + type: integer + description: Number of successfully written records. + total: + type: integer + description: Total number of records submitted. + failures: + type: array + description: Array of failed records. The array is empty when every item succeeds. + items: + $ref: '#/components/schemas/event_bulk_failure' + event_error_response: + type: object + description: Generic Events API error response object. The `error` field is a machine-readable error code, `error_description` is a human-readable explanation, and `error_details` provides additional troubleshooting context. + required: + - error + - error_description + properties: + error: + type: string + description: Error code such as `bad_param` or `missing_param`. + error_description: + type: string + description: Human-readable error description. This may be localized. + error_details: + type: array + description: Array of error details, such as missing parameters or field names. + items: + type: string parameters: + curb_object_id: + name: curb_object_id + in: query + required: false + schema: + type: string + format: uuid + description: The ID of a Curb Object. If specified, only return events or statuses occurring at this object. curb_area_id: name: curb_area_id in: query diff --git a/APIs/Metrics API.yaml b/APIs/Metrics API.yaml index 8f01181..8918d72 100644 --- a/APIs/Metrics API.yaml +++ b/APIs/Metrics API.yaml @@ -4,7 +4,7 @@ x-stoplight: info: title: Metrics API version: '1.1' - description: 'The Metrics API is a REST API allowing historic metrics calculations based on Event activity that happened at defined Curb places. Defines common calculation methodologies to measure historic dwell time, occupancy, usage and other aggregated statistics.' + description: 'The Metrics API is a REST API allowing historic metrics calculations based on Event activity that happened at defined Curb places. Metrics data should be viewed as representative sample data, not necessarily a 100% accurate picture of what happens at every defined curb space, and agencies may update the data monthly or more frequently depending on their implementation. Defines common calculation methodologies to measure historic dwell time, occupancy, usage and other aggregated statistics.' contact: name: Open Mobility Foundation url: 'https://www.openmobilityfoundation.org' @@ -25,39 +25,24 @@ paths: summary: Get Metric Sessions tags: - sessions + description: |- + Optional endpoint. Authorization is recommended for all Metrics endpoints, since depending on implementation, use cases, fields required, local laws, and audience it may contain information only city transportation agencies should have access to. + + Metrics data should be viewed as representative sample data, not necessarily a 100% accurate picture of what happens at every defined curb space. Agencies may choose how frequently they update the data; at least monthly is recommended, but weekly, daily, hourly, or more frequent updates are acceptable. + + Agencies wishing to publicly release Metrics data are encouraged to limit releases to static Aggregate data that has been reviewed for potential privacy risks. + + An agency may choose to make this endpoint static (and return all the available data at once in a single file) or dynamic (and allow the use of any or all of the query parameters below to filter the data). If dynamic, all query parameters are optional. If not implemented, a server should reply with 501 Not Implemented. responses: '200': - description: OK + description: CSV file content: - application/json: + application/vnd.cds+csv;version=1.1: schema: - type: object - properties: - version: - $ref: ../Rest Responses/rest_response_version.yaml - time_zone: - $ref: ../Rest Responses/rest_response_time_zone.yaml - last_updated: - $ref: ../Rest Responses/rest_response_last_updated.yaml - currency: - $ref: ../Rest Responses/rest_response_currency.yaml - author: - $ref: ../Rest Responses/rest_response_author.yaml - license_url: - $ref: ../Rest Responses/rest_response_license_url.yaml - data: - type: object - x-stoplight: - id: 6hdpx6vi33ec9 - properties: - sessions: - type: array - x-stoplight: - id: je2bc2zsqo5jk - items: - $ref: ../Shared Data Models/session.yaml - x-stoplight: - id: 69aplzzla18wd + type: string + description: CSV rows corresponding to the Session data object. + '406': + description: Not Acceptable '501': description: Not Implemented operationId: get-metrics-sessions @@ -75,48 +60,29 @@ paths: - $ref: '#/components/parameters/radius' - $ref: '#/components/parameters/start_time' - $ref: '#/components/parameters/end_time' - description: |- - Optional endpoint. Enables the querying of sessions from an agency. - - An agency may choose to make this endpoint static (and return all the available data at once in a single file) or dynamic (and allow the use of any or all of the query parameters below to filter the data). If dynamic, all query parameters are optional. /metrics/aggregates: get: summary: Get Metric Aggregates tags: - aggregates + description: |- + Optional endpoint. Authorization is recommended for all Metrics endpoints, since depending on implementation, use cases, fields required, local laws, and audience it may contain information only city transportation agencies should have access to. + + Metrics data should be viewed as representative sample data, not necessarily a 100% accurate picture of what happens at every defined curb space. Agencies may choose how frequently they update the data; at least monthly is recommended, but weekly, daily, hourly, or more frequent updates are acceptable. + + Agencies wishing to publicly release Metrics data are encouraged to limit releases to static Aggregate data that has been reviewed for potential privacy risks. + + An agency may choose to make this endpoint static (and return all the available data at once in a single file) or dynamic (and allow the use of any or all of the query parameters below to filter the data). If dynamic, all query parameters are optional. If not implemented, a server should reply with 501 Not Implemented. responses: '200': - description: OK + description: CSV file content: - application/json: + application/vnd.cds+csv;version=1.1: schema: - type: object - properties: - version: - $ref: ../Rest Responses/rest_response_version.yaml - time_zone: - $ref: ../Rest Responses/rest_response_time_zone.yaml - last_updated: - $ref: ../Rest Responses/rest_response_last_updated.yaml - currency: - $ref: ../Rest Responses/rest_response_currency.yaml - author: - $ref: ../Rest Responses/rest_response_author.yaml - license_url: - $ref: ../Rest Responses/rest_response_license_url.yaml - data: - type: object - x-stoplight: - id: zb3228zd0gb2t - properties: - aggregate: - type: array - x-stoplight: - id: 3b1l6aypuq5sc - items: - $ref: ../Shared Data Models/aggregate.yaml - x-stoplight: - id: w9zfx39ouf3ti + type: string + description: CSV rows corresponding to the Aggregate data object. + '406': + description: Not Acceptable '501': description: Not Implemented operationId: get-metrics-aggregates @@ -135,12 +101,6 @@ paths: - $ref: '#/components/parameters/radius' - $ref: '#/components/parameters/start_time' - $ref: '#/components/parameters/end_time' - requestBody: - content: {} - description: |- - Optional endpoint. Enables the querying of aggregates from an agency. - - An agency may choose to make this endpoint static (and return all the available data at once in a single file) or dynamic (and allow the use of any or all of the query parameters below to filter the data). If dynamic, all query parameters are optional. components: schemas: {} parameters: @@ -157,56 +117,56 @@ components: required: false schema: type: number - description: 'Used to specify a latitude and longitude bounding box. Must be used together with `min_lat`, `max_lat`, and `max_lng`. If specified only return locations that intersect the supplied bounding box. This parameter is incompatible with `lat`, `lng`, and `radius`.' + description: 'Used to specify a latitude and longitude bounding box. Must be used together with `min_lat`, `max_lat`, and `max_lng`. If specified only return locations that intersect the supplied bounding box. This parameter is incompatible with `lat`, `lng`, and `radius`.' max_lat: name: max_lat in: query required: false schema: type: number - description: 'Used to specify a latitude and longitude bounding box. Must be used together with `min_lat`, `min_lng`, and `max_lng`. If specified only return locations that intersect the supplied bounding box. This parameter is incompatible with `lat`, `lng`, and `radius`.' + description: 'Used to specify a latitude and longitude bounding box. Must be used together with `min_lat`, `min_lng`, and `max_lng`. If specified only return locations that intersect the supplied bounding box. This parameter is incompatible with `lat`, `lng`, and `radius`.' max_lng: name: max_lng in: query required: false schema: type: number - description: 'Used to specify a latitude and longitude bounding box. Must be used together with `min_lat`, `min_lng`, and `max_lat`. If specified only return locations that intersect the supplied bounding box. This parameter is incompatible with `lat`, `lng`, and `radius`.' + description: 'Used to specify a latitude and longitude bounding box. Must be used together with `min_lat`, `min_lng`, and `max_lat`. If specified only return locations that intersect the supplied bounding box. This parameter is incompatible with `lat`, `lng`, and `radius`.' lat: name: lat in: query required: false schema: type: number - description: 'Specifies a latitude and longitude bounding point and a radius away from that point. Must be used together with `lng` and `radius`. Returns only locations that are within radius centimeters of the point identified by lat/lng. Curb Zones in the response MUST be ordered ascending by distance from the center point. This parameter is incompatible with `min_lat`, `min_lng`, `max_lat`, and `max_lng`' + description: 'Specifies a latitude and longitude bounding point and a radius away from that point. Must be used together with `lng` and `radius`. Returns only locations that are within radius centimeters of the point identified by lat/lng. Curb Zones in the response MUST be ordered ascending by distance from the center point. This parameter is incompatible with `min_lat`, `min_lng`, `max_lat`, and `max_lng`.' lng: name: lng in: query required: false schema: type: number - description: 'Specifies a latitude and longitude bounding point and a radius away from that point. Must be used together with `lat` and `radius`. Returns only locations that are within radius centimeters of the point identified by lat/lng. Curb Zones in the response MUST be ordered ascending by distance from the center point. This parameter is incompatible with `min_lat`, `min_lng`, `max_lat`, and `max_lng`' + description: 'Specifies a latitude and longitude bounding point and a radius away from that point. Must be used together with `lat` and `radius`. Returns only locations that are within radius centimeters of the point identified by lat/lng. Curb Zones in the response MUST be ordered ascending by distance from the center point. This parameter is incompatible with `min_lat`, `min_lng`, `max_lat`, and `max_lng`.' radius: name: radius in: query required: false schema: type: number - description: 'Specifies a latitude and longitude bounding point and a radius away from that point. Must be used together with `lng` and `lng`. Returns only locations that are within radius centimeters of the point identified by lat/lng. Curb Zones in the response MUST be ordered ascending by distance from the center point. This parameter is incompatible with `min_lat`, `min_lng`, `max_lat`, and `max_lng`' + description: 'Specifies a latitude and longitude bounding point and a radius away from that point. Must be used together with `lng` and `lng`. Returns only locations that are within radius centimeters of the point identified by lat/lng. Curb Zones in the response MUST be ordered ascending by distance from the center point. This parameter is incompatible with `min_lat`, `min_lng`, `max_lat`, and `max_lng`.' start_time: name: start_time in: query required: false schema: - type: string - description: The start of the time period to return data where the value is inclusive. + $ref: ../Shared Data Models/timestamp.yaml + description: The start of the time period to return data; the value is inclusive. end_time: name: end_time in: query required: false schema: - type: string - description: The end of the time period to return data where the value is inclusive. + $ref: ../Shared Data Models/timestamp.yaml + description: The end of the time period to return data; the value is exclusive. curb_place_type: name: curb_place_type in: query @@ -217,7 +177,8 @@ components: - area - zone - space - description: The type of curb place this aggregate applies to from the Curbs API. + - object + description: The type of curb place this aggregate applies to from the Curbs API. Required with `curb_place_id`. curb_place_id: name: curb_place_id in: query @@ -225,7 +186,7 @@ components: schema: type: string format: uuid - description: 'The ID of this single curb place. If specified, only return data contained within this area. Required with `curb_place_type`.' + description: The ID of this single curb place. If specified, only return data contained within this curb place. Required with `curb_place_type`. metric_type: name: metric_type in: query @@ -234,7 +195,8 @@ components: type: string enum: - total_sessions + - total_events - turnover - average_dwell_time - occupancy_percent - description: 'The single metric to return from the [Methodology](https://github.com/openmobilityfoundation/curb-data-specification/tree/main/metrics#methodology)' + description: 'The single metric to return from the [Methodology](https://github.com/openmobilityfoundation/curb-data-specification/tree/main/metrics#methodology).' diff --git a/V1.1_IMPLEMENTATION_PLAN.md b/V1.1_IMPLEMENTATION_PLAN.md index 5b1ac81..2ce56fb 100644 --- a/V1.1_IMPLEMENTATION_PLAN.md +++ b/V1.1_IMPLEMENTATION_PLAN.md @@ -131,7 +131,7 @@ editing the spec. ### Phase 3 – API Parity -#### 3A. `Curbs API.yaml` +#### ~~3A. `Curbs API.yaml`~~ - **Files:** `APIs/Curbs API.yaml` - **Done when:** @@ -141,7 +141,7 @@ editing the spec. - both object endpoints include `501 Not Implemented` - list responses use the same `data.` wrapper pattern consistently -#### 3B. `Events API.yaml` GET parity +#### ~~3B. `Events API.yaml` GET parity~~ - **Files:** `APIs/Events API.yaml` - **Done when:** @@ -149,7 +149,7 @@ editing the spec. - `curb_object_id` is added to `GET /events/events` - `curb_object_id` is added to `GET /events/status` -#### 3C. `Events API.yaml` POST parity +#### ~~3C. `Events API.yaml` POST parity~~ - **Files:** `APIs/Events API.yaml` - **Done when:** @@ -158,7 +158,7 @@ editing the spec. - response coverage matches upstream: `200`, `201`, `400`, `401`, `404`, `406`, `409`, `500`, `501` -#### 3D. `Events API.yaml` auth and error guidance +#### ~~3D. `Events API.yaml` auth and error guidance~~ - **Files:** `APIs/Events API.yaml` - **Done when:** @@ -167,7 +167,7 @@ editing the spec. - event ordering guidance is aligned with `general-information.md#event-times` - error and bulk-response descriptions align with upstream general information -#### 3E. `Metrics API.yaml` contract parity +#### ~~3E. `Metrics API.yaml` contract parity~~ - **Files:** `APIs/Metrics API.yaml` - **Done when:** @@ -179,7 +179,7 @@ editing the spec. - `start_time` is documented as inclusive - `end_time` is documented as exclusive -#### 3F. `Metrics API.yaml` descriptive parity +#### ~~3F. `Metrics API.yaml` descriptive parity~~ - **Files:** `APIs/Metrics API.yaml` - **Done when:** From 3c3a04f7a8541bd605481ac73bbc4cdd05faa025 Mon Sep 17 00:00:00 2001 From: Michael Danko Date: Fri, 10 Apr 2026 09:45:05 -0400 Subject: [PATCH 26/26] fix: align v1.1 OpenAPI schemas with CDS spec --- APIs/Curbs API.yaml | 82 +++++++++++++++++-- APIs/Events API.yaml | 22 +++++ APIs/Metrics API.yaml | 2 +- Rest Responses/rest_response_currency.yaml | 2 +- Shared Data Models/curb_object.yaml | 5 ++ Shared Data Models/curb_policy.yaml | 4 +- Shared Data Models/curb_status.yaml | 21 ++++- Shared Data Models/custom_attributes.yaml | 12 +-- Shared Data Models/event_purpose.yaml | 31 +------ Shared Data Models/lane_type.yaml | 4 +- Shared Data Models/rules.yaml | 95 +++------------------- Shared Data Models/time_spans.yaml | 5 -- Shared Data Models/timestamp.yaml | 2 +- 13 files changed, 148 insertions(+), 139 deletions(-) diff --git a/APIs/Curbs API.yaml b/APIs/Curbs API.yaml index 7a34ff3..76cd15b 100644 --- a/APIs/Curbs API.yaml +++ b/APIs/Curbs API.yaml @@ -4,7 +4,7 @@ x-stoplight: info: title: Curb API version: '1.1' - description: 'The Curbs API is a REST API allowing cities to specify areas of interest along the curb along with the rules for using them: who is allowed to park, load, unload, pick up, drop off, etc., for how long, for what price (if any), at what times, and on which days. Locations defined in the Curbs API can be connected to event and metrics data, and can be shared with companies and the public, for purposes such as routing, finding legal parking, loading, and pick-up/drop-off spots, or analyzing curb utilization over time.' + description: 'The Curbs API is a REST API allowing cities to specify areas of interest along and around the curb, objects in and near the curb or on sidewalks or travel lanes, and off street parking areas. Included are the rules and policies for using these areas: who is allowed to park, load, unload, pick up, drop off, etc., for how long, for what price (if any), at what times, and on which days. Locations defined in the Curbs API can be connected to event and metrics data, and can be shared with companies and the public, for purposes such as routing, finding legal parking, loading, and pick-up/drop-off spots, enforcement, compliance, and analyzing curb utilization over time.' contact: name: Open Mobility Foundation url: 'https://www.openmobilityfoundation.org' @@ -38,6 +38,12 @@ paths: application/json: schema: type: object + required: + - version + - time_zone + - last_updated + - currency + - data properties: version: $ref: ../Rest Responses/rest_response_version.yaml @@ -55,6 +61,8 @@ paths: $ref: ../Rest Responses/rest_response_license_url.yaml data: type: object + required: + - zones properties: zones: type: array @@ -78,7 +86,7 @@ paths: - $ref: '#/components/parameters/radius' - $ref: '#/components/parameters/include_geometry' - schema: - type: string + $ref: ../Shared Data Models/timestamp.yaml in: query name: time description: Only Curb Zone objects whose validity period includes this time will be returned; availability data (if supplied) will be returned as of this time @@ -96,6 +104,12 @@ paths: application/json: schema: type: object + required: + - version + - time_zone + - last_updated + - currency + - data properties: version: $ref: ../Rest Responses/rest_response_version.yaml @@ -113,6 +127,8 @@ paths: $ref: ../Rest Responses/rest_response_license_url.yaml data: type: object + required: + - areas properties: areas: type: array @@ -145,6 +161,12 @@ paths: application/json: schema: type: object + required: + - version + - time_zone + - last_updated + - currency + - data properties: version: $ref: ../Rest Responses/rest_response_version.yaml @@ -162,6 +184,8 @@ paths: $ref: ../Rest Responses/rest_response_license_url.yaml data: type: object + required: + - spaces properties: spaces: type: array @@ -185,7 +209,7 @@ paths: - $ref: '#/components/parameters/lng' - $ref: '#/components/parameters/radius' - schema: - type: string + $ref: ../Shared Data Models/timestamp.yaml in: query name: time description: Availability data (if supplied) will be returned as of this time. @@ -204,6 +228,12 @@ paths: application/json: schema: type: object + required: + - version + - time_zone + - last_updated + - currency + - data properties: version: $ref: ../Rest Responses/rest_response_version.yaml @@ -221,6 +251,8 @@ paths: $ref: ../Rest Responses/rest_response_license_url.yaml data: type: object + required: + - objects properties: objects: type: array @@ -263,6 +295,12 @@ paths: application/json: schema: type: object + required: + - version + - time_zone + - last_updated + - currency + - data properties: version: $ref: ../Rest Responses/rest_response_version.yaml @@ -311,6 +349,12 @@ paths: application/json: schema: type: object + required: + - version + - time_zone + - last_updated + - currency + - data properties: version: $ref: ../Rest Responses/rest_response_version.yaml @@ -328,6 +372,8 @@ paths: $ref: ../Rest Responses/rest_response_license_url.yaml data: type: object + required: + - policies properties: policies: type: array @@ -366,6 +412,12 @@ paths: application/json: schema: type: object + required: + - version + - time_zone + - last_updated + - currency + - data properties: version: $ref: ../Rest Responses/rest_response_version.yaml @@ -419,6 +471,12 @@ paths: application/json: schema: type: object + required: + - version + - time_zone + - last_updated + - currency + - data properties: version: $ref: ../Rest Responses/rest_response_version.yaml @@ -462,6 +520,12 @@ paths: application/json: schema: type: object + required: + - version + - time_zone + - last_updated + - currency + - data properties: version: $ref: ../Rest Responses/rest_response_version.yaml @@ -510,6 +574,12 @@ paths: application/json: schema: type: object + required: + - version + - time_zone + - last_updated + - currency + - data properties: version: $ref: ../Rest Responses/rest_response_version.yaml @@ -528,9 +598,9 @@ paths: data: $ref: ../Shared Data Models/curb_policy.yaml '501': - description: Not Implemented + description: NOT_IMPLEMENTED operationId: get-curbs-policy - description: Fetch a single Curb Policy by ID. + description: 'Fetch a single Curb Policy by ID. Optional endpoint. If not implemented, the server should reply with 501 Not Implemented.' parameters: - schema: type: string @@ -592,7 +662,7 @@ components: required: false schema: type: number - description: 'Specifies a latitude and longitude bounding point and a radius away from that point. Must be used together with `lng` and `lng`. Returns only locations that are within radius centimeters of the point identified by lat/lng. Curb Zones in the response MUST be ordered ascending by distance from the center point. This parameter is incompatible with `min_lat`, `min_lng`, `max_lat`, and `max_lng`' + description: 'Specifies a latitude and longitude bounding point and a radius away from that point. Must be used together with `lat` and `lng`. Returns only locations that are within radius centimeters of the point identified by lat/lng. Curb Zones in the response MUST be ordered ascending by distance from the center point. This parameter is incompatible with `min_lat`, `min_lng`, `max_lat`, and `max_lng`' include_geometry: name: include_geometry in: query diff --git a/APIs/Events API.yaml b/APIs/Events API.yaml index b421ef3..99c35ce 100644 --- a/APIs/Events API.yaml +++ b/APIs/Events API.yaml @@ -33,6 +33,12 @@ paths: application/json: schema: type: object + required: + - version + - time_zone + - last_updated + - currency + - data properties: version: $ref: ../Rest Responses/rest_response_version.yaml @@ -64,6 +70,8 @@ paths: type: object x-stoplight: id: fhxkczml8n5d5 + required: + - events properties: events: type: array @@ -86,6 +94,9 @@ paths: - $ref: '#/components/parameters/curb_zone_id' - $ref: '#/components/parameters/curb_space_id' - $ref: '#/components/parameters/curb_object_id' + security: + - bearerAuth: [] + - {} description: |- Authorization is recommended for this endpoint. This required endpoint must be implemented by every Events API server. It returns CDS events ordered by `event_time`, with the newest events first, following the Event Times guidance. @@ -101,6 +112,12 @@ paths: application/json: schema: type: object + required: + - version + - time_zone + - last_updated + - currency + - data properties: version: $ref: ../Rest Responses/rest_response_version.yaml @@ -130,6 +147,8 @@ paths: type: object x-stoplight: id: e1wkbj37xvkz9 + required: + - status properties: status: type: array @@ -149,6 +168,9 @@ paths: - $ref: '#/components/parameters/curb_zone_id' - $ref: '#/components/parameters/curb_space_id' - $ref: '#/components/parameters/curb_object_id' + security: + - bearerAuth: [] + - {} description: |- Authorization is recommended for this endpoint. Optional endpoint. Enables the querying of statuses for various sensors monitoring curb places. If not implemented, the server should reply with `501 Not Implemented`. diff --git a/APIs/Metrics API.yaml b/APIs/Metrics API.yaml index 8918d72..582178f 100644 --- a/APIs/Metrics API.yaml +++ b/APIs/Metrics API.yaml @@ -152,7 +152,7 @@ components: required: false schema: type: number - description: 'Specifies a latitude and longitude bounding point and a radius away from that point. Must be used together with `lng` and `lng`. Returns only locations that are within radius centimeters of the point identified by lat/lng. Curb Zones in the response MUST be ordered ascending by distance from the center point. This parameter is incompatible with `min_lat`, `min_lng`, `max_lat`, and `max_lng`.' + description: 'Specifies a latitude and longitude bounding point and a radius away from that point. Must be used together with `lat` and `lng`. Returns only locations that are within radius centimeters of the point identified by lat/lng. Curb Zones in the response MUST be ordered ascending by distance from the center point. This parameter is incompatible with `min_lat`, `min_lng`, `max_lat`, and `max_lng`.' start_time: name: start_time in: query diff --git a/Rest Responses/rest_response_currency.yaml b/Rest Responses/rest_response_currency.yaml index 0cb1e1a..965d7ad 100644 --- a/Rest Responses/rest_response_currency.yaml +++ b/Rest Responses/rest_response_currency.yaml @@ -6,4 +6,4 @@ description: |- This is a standard value that must be returned in the body of all API endpoints. The ISO 4217 3-letter code for the currency in which rates for curb usage are denominated. All costs should be given as integers in the currency's smallest unit. As an example, to represent `$1 USD`, specify an amount of `100` (for 100 cents). -example: '100' +example: USD diff --git a/Shared Data Models/curb_object.yaml b/Shared Data Models/curb_object.yaml index 6500150..d58d71d 100644 --- a/Shared Data Models/curb_object.yaml +++ b/Shared Data Models/curb_object.yaml @@ -133,3 +133,8 @@ required: - name - published_date - last_updated_date +anyOf: + - required: + - curb_zone_id + - required: + - curb_space_id diff --git a/Shared Data Models/curb_policy.yaml b/Shared Data Models/curb_policy.yaml index 6b4c740..cde92b3 100644 --- a/Shared Data Models/curb_policy.yaml +++ b/Shared Data Models/curb_policy.yaml @@ -13,6 +13,7 @@ properties: A `curb_policy_id` MUST NOT be reused. Once created, it must continue to refer to the identical policy forever. type: string + format: uuid name: type: string description: User friendly name of policy. @@ -118,9 +119,6 @@ properties: description: External references that link this policy to other systems or specifications. items: $ref: ./external_reference.yaml - custom_attributes: - $ref: ./custom_attributes.yaml - description: Implementation-specific custom attributes for this policy. required: - curb_policy_id - published_date diff --git a/Shared Data Models/curb_status.yaml b/Shared Data Models/curb_status.yaml index 7658ead..340118a 100644 --- a/Shared Data Models/curb_status.yaml +++ b/Shared Data Models/curb_status.yaml @@ -2,9 +2,26 @@ title: Curb Status x-stoplight: id: hvscebeq5ob4d type: object +description: The Curb Status is the current status of sensors that are monitoring curb places. properties: - id: + data_source_device_id: type: string + format: uuid x-stoplight: id: adhs12kh3sx96 -description: The Curb Status is the current status of sensors that are monitoring curb places. + description: Unique identifier of this event source, whether sensor, vehicle, camera, or similar device. + data_source_type: + $ref: ./data_source_type.yaml + description: General category of the source creating the event. + data_source_operator_id: + $ref: ./operator_id.yaml + description: Unique identifier of the entity responsible for operating the event data source. Can be global from `data_source_operators.csv` or defined per city. + sensor_status_is_commissioned: + type: boolean + description: Indicates whether the sensor is currently in a state where it should be reporting data. + sensor_status_is_online: + type: boolean + description: Indicates whether the sensor is currently online and reporting data. +required: + - data_source_device_id + - data_source_type diff --git a/Shared Data Models/custom_attributes.yaml b/Shared Data Models/custom_attributes.yaml index 5fa8e7d..f4f572f 100644 --- a/Shared Data Models/custom_attributes.yaml +++ b/Shared Data Models/custom_attributes.yaml @@ -2,14 +2,10 @@ title: Custom Attributes type: object description: > Implementation-specific custom attributes for this object. - Publishers MAY use any keys and value structures appropriate to their use case. - A simple `"key": "value"` structure is provided as an example only and does NOT - restrict the allowed shapes of `custom_attributes`. -additionalProperties: true + Custom attributes are one or more JSON name/value pairs, and the values must be + strings. +additionalProperties: + type: string examples: - color: "blue" kiosk_id: "K-1234" - internal_tag: - source: "legacy_system" - code: 42 - diff --git a/Shared Data Models/event_purpose.yaml b/Shared Data Models/event_purpose.yaml index befe645..7839e10 100644 --- a/Shared Data Models/event_purpose.yaml +++ b/Shared Data Models/event_purpose.yaml @@ -2,38 +2,11 @@ title: Event Purpose x-stoplight: id: 6uj3ohchgx8f9 type: string -enum: - - construction - - delivery - - emergency_use - - parking - - passenger_transport - - special_events - - waste_management - - device_maintenance - - autonomous - - ems - - fire - - food_delivery - - parcel_delivery - - police - - public_transit - - ride_hail - - road_maintenance - - service_vehicles - - taxi - - utility_work - - vehicle_charging - - vehicle_parking - - vending - - unspecified description: |- General event purpose that the vehicle performed during its event, discernible by observation, sensors, or self-reported in company data feeds. New event purposes MAY be generated to reflect local curb uses, but when possible, the following well-known recommended values should be used. It may not always be knowable, but where it is possible this information should be conveyed. - If multiple purposes apply, then use the more descriptive/specific value. For a description of each value in the enumeration, the OMF's documentation can be found [here](https://github.com/openmobilityfoundation/curb-data-specification/tree/main/events#event-purpose). + If multiple purposes apply, then use the more descriptive or specific value. - |Value|Description| - |--------------|-----| - | value | description | + Well-known recommended values include `construction`, `delivery`, `emergency_use`, `parking`, `passenger_transport`, `special_events`, `waste_management`, `device_maintenance`, `autonomous`, `ems`, `fire`, `food_delivery`, `parcel_delivery`, `police`, `public_transit`, `ride_hail`, `road_maintenance`, `service_vehicles`, `taxi`, `utility_work`, `vehicle_charging`, `vehicle_parking`, `vending`, and `unspecified`. diff --git a/Shared Data Models/lane_type.yaml b/Shared Data Models/lane_type.yaml index 0d0b164..262c59c 100644 --- a/Shared Data Models/lane_type.yaml +++ b/Shared Data Models/lane_type.yaml @@ -5,6 +5,7 @@ type: string enum: - travel_lane - turn_lane + - center_turn_lane - bike_lane - bus_lane - parking @@ -17,8 +18,9 @@ description: |- |Value|Description| |--------------|-----| - | human | A standard vehicle travel lane. | + | travel_lane | A standard vehicle travel lane. | | turn_lane | A dedicated turn lane. | + | center_turn_lane | A center lane available for turns in both directions. Sometimes used for courier parking for loading activity. | | bike_lane | A lane dedicated for usage by cyclists. | | bus_lane | A lane dedicated for usage by buses. | | parking | A lane used for parking, not allowed for travel. | diff --git a/Shared Data Models/rules.yaml b/Shared Data Models/rules.yaml index 9e22f86..abbea92 100644 --- a/Shared Data Models/rules.yaml +++ b/Shared Data Models/rules.yaml @@ -40,102 +40,30 @@ properties: description: > User classes describe which vehicles, vehicle properties, or permit types a rule applies to. All values in the array must match for the rule to apply. + Locally generated user classes are allowed. Well-known recommended values + include bicycle, bus, cargo_bicycle, car, moped, motorcycle, scooter, + shuttle, truck, van, accessible, autonomous, combustion, electric, + electric_assist, and human. items: type: string - enum: - - bicycle - - bus - - cargo_bicycle - - car - - moped - - motorcycle - - scooter - - shuttle - - truck - - van - - accessible - - human - - electric_assist - - electric - - combustion - - autonomous - - construction - - delivery - - disabled_parking_permit - - emergency_use - - freight - - parking - - permit - - rideshare - - school - - service_vehicles - - special_events - - taxi - - utilities - - vending - - waste_management user_classes_except: type: array description: > If specified, the rule applies only to users who do not match any of the - listed user classes. + listed user classes. Locally generated user classes are allowed. items: type: string - enum: - - bicycle - - bus - - cargo_bicycle - - car - - moped - - motorcycle - - scooter - - shuttle - - truck - - van - - accessible - - human - - electric_assist - - electric - - combustion - - autonomous - - construction - - delivery - - disabled_parking_permit - - emergency_use - - freight - - parking - - permit - - rideshare - - school - - service_vehicles - - special_events - - taxi - - utilities - - vending - - waste_management purposes: type: array description: > The permitted purposes associated with this rule. If specified, the rule - applies only when one of the listed purposes matches. + applies only when one of the listed purposes matches. Locally generated + purpose values are allowed. Well-known recommended values include + construction, delivery, disabled_parking_permit, emergency_use, freight, + parking, permit, rideshare, carshare, school, service_vehicles, + special_events, taxi, utilities, valet, vending, and waste_management. items: type: string - enum: - - construction - - delivery - - disabled_parking_permit - - emergency_use - - freight - - parking - - permit - - rideshare - - school - - service_vehicles - - special_events - - taxi - - utilities - - vending - - waste_management rate: type: array description: The cost of using this Curb Zone when this regulation applies. @@ -167,6 +95,9 @@ properties: end_duration: type: integer description: The number of rate_units after which the rate stops applying. + maximum_fee: + type: integer + description: The maximum amount in cents a user of the curb can pay for a particular parking event. required: - rate - rate_unit diff --git a/Shared Data Models/time_spans.yaml b/Shared Data Models/time_spans.yaml index 02ef5fc..3c60845 100644 --- a/Shared Data Models/time_spans.yaml +++ b/Shared Data Models/time_spans.yaml @@ -63,11 +63,6 @@ properties: description: 'The local time that this time span stops applying, as 24-hour "HH:MM".' designated_period: type: string - enum: - - snow emergency - - holidays - - school days - - game days description: > A string representing an arbitrarily-named, externally-defined period of time. Any values MAY be specified but the following known values SHOULD be diff --git a/Shared Data Models/timestamp.yaml b/Shared Data Models/timestamp.yaml index d311af6..b878688 100644 --- a/Shared Data Models/timestamp.yaml +++ b/Shared Data Models/timestamp.yaml @@ -1,6 +1,6 @@ title: Timestamp x-stoplight: id: wts331n59lxqu -type: number +type: integer description: 'An integer representing a number of milliseconds since midnight, January 1st, 1970 UTC (the UNIX epoch)' example: 1643130000000