Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONFORMANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ A conformant implementation SHOULD:
- Emit events for budget-state changes (reservation.*, budget.*, quota.*) matching the `EventType` enum. Implementations MAY sample or filter which events they emit, but emitted events MUST follow the schema.
- Propagate `X-Cycles-Trace-Id` and W3C `traceparent` headers per `cycles-protocol-v0.yaml` §CORRELATION AND TRACING. Trace correlation is central to multi-service debugging.
- Implement `POST /v1/decide` — marked OPTIONAL in the v0 spec, but agent frameworks need soft-landing signals for graceful degradation.
- Implement `GET /v1/reservations` (**listReservations**) — marked OPTIONAL in v0; useful for reservation recovery (re-discover a lost `reservation_id` via `idempotency_key`) and for identifying stuck `ACTIVE` reservations.
- Implement `GET /v1/reservations` (**listReservations**) — marked OPTIONAL in v0; useful for reservation recovery (re-discover a lost `reservation_id` via `idempotency_key`), for identifying stuck `ACTIVE` reservations, and for time-window queries via the additive `from`/`to` parameters (v0.1.25, revision 2026-05-21).
- Implement `GET /v1/reservations/{reservation_id}` (**getReservation**) — marked "optional, for debugging" in v0; valuable for support / monitoring of long-running reservations.
- Implement `POST /v1/events` (**createEvent**) — marked OPTIONAL in v0; the post-only accounting path for cases where pre-estimation is unavailable (bills-later providers, receipt ingestion).

Expand Down
52 changes: 52 additions & 0 deletions changelogs/cycles-protocol-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,58 @@ New entries are added directly to this file. See `scripts/validate_changelogs.py

---

## v0.1.25 — 2026-05-21

_(revision 2026-05-21 — `from`/`to` created-time range filters on listReservations)_

- Adds two optional query parameters to `listReservations`
(`GET /v1/reservations`):
* `from`: ISO 8601 date-time. Inclusive lower bound on
reservation `created_at_ms`. May be supplied alone (no
upper bound) or paired with `to`.
* `to`: ISO 8601 date-time. Inclusive upper bound on
reservation `created_at_ms`. May be supplied alone (no
lower bound) or paired with `from`.
- Closes a real client-side cost: today, fetching "last 24h
of reservations" requires sort-by-`created_at_ms` + a
page-size escalation loop until the oldest item falls
outside the window. For high-volume agent clusters this
scans far more rows than the caller actually needs. With
`from`/`to`, the server boundaries the scan to the
requested window and pagination over that window remains
cursor-stable.
- Both parameters bind to `created_at_ms` regardless of
`sort_by`. A client sorting by `expires_at_ms` while
filtering by `from`/`to` gets the expected behavior:
results in the requested window, ordered by expiry. This
keeps the contract predictable across sort keys (no
per-key filter semantics to memorize).
- Validation:
* Servers MUST reject `from > to` with HTTP 400
INVALID_REQUEST.
* Either parameter alone is valid; absent parameter
means "no bound on that side."
* Malformed date-time values MUST be rejected with HTTP
400 INVALID_REQUEST (consistent with other ISO 8601
query parameters in the spec family).
- Additive-parameter guarantee: servers that don't recognize
`from`/`to` MUST ignore without error and return the
unfiltered set. Older clients that never send them get the
pre-revision wire behavior byte-for-byte.
- Naming and wire-type rationale: matches the family-wide
`from`/`to` + `format: date-time` convention already in
use on `listAuditLogs`, `listEvents`,
`listWebhookDeliveries`, `listTenantEvents`, and
`listTenantWebhookDeliveries` in the governance-admin
spec. Bespoke `created_after`/`created_before` names or
Unix-epoch wire types would split the convention for
clients and codegen.
- Backward compatible: purely additive. No request or
response schema changes. Both ApiKeyAuth and AdminKeyAuth
callers see the new parameters.

---

## v0.1.25 — 2026-04-18

_(revision 2026-04-18 — trace_id cross-surface correlation, W3C Trace Context-compatible)_
Expand Down
51 changes: 51 additions & 0 deletions cycles-protocol-v0.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -421,12 +421,12 @@
$ref: '#/components/schemas/ErrorResponse'

schemas:
IdempotencyKey:

Check warning on line 424 in cycles-protocol-v0.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: string
minLength: 1
maxLength: 256

ErrorCode:

Check warning on line 429 in cycles-protocol-v0.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: string
enum:
- INVALID_REQUEST
Expand All @@ -445,7 +445,7 @@
- MAX_EXTENSIONS_EXCEEDED
- INTERNAL_ERROR

ErrorResponse:

Check warning on line 448 in cycles-protocol-v0.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: object
required: [error, message, request_id]
additionalProperties: false
Expand Down Expand Up @@ -480,7 +480,7 @@
type: object
additionalProperties: true

DecisionEnum:

Check warning on line 483 in cycles-protocol-v0.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: string
enum: [ALLOW, ALLOW_WITH_CAPS, DENY]

Expand Down Expand Up @@ -573,7 +573,7 @@
the companion-specs publication model does not use cross-spec
$ref.

Amount:

Check warning on line 576 in cycles-protocol-v0.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: object
required: [unit, amount]
additionalProperties: false
Expand Down Expand Up @@ -655,7 +655,7 @@
maxLength: 256
maxProperties: 16

Action:

Check warning on line 658 in cycles-protocol-v0.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: object
required: [kind, name]
additionalProperties: false
Expand Down Expand Up @@ -713,7 +713,7 @@
minimum: 0

# ---- Decide (optional) ----
DecisionRequest:

Check warning on line 716 in cycles-protocol-v0.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: object
required: [idempotency_key, subject, action, estimate]
additionalProperties: false
Expand All @@ -730,7 +730,7 @@
type: object
additionalProperties: true

DecisionResponse:

Check warning on line 733 in cycles-protocol-v0.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: object
required: [decision]
additionalProperties: false
Expand Down Expand Up @@ -798,7 +798,7 @@
description: Reservation lifecycle state (v1+ may add additional terminal states).
enum: [ACTIVE, COMMITTED, RELEASED, EXPIRED]

ReservationCreateRequest:

Check warning on line 801 in cycles-protocol-v0.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: object
required: [idempotency_key, subject, action, estimate]
additionalProperties: false
Expand Down Expand Up @@ -955,7 +955,7 @@
until reconciled (debt repaid below overdraft_limit, or is_over_limit cleared by operator).
Defaults to false when absent.

ReservationCreateResponse:

Check warning on line 958 in cycles-protocol-v0.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: object
required: [decision, affected_scopes]
additionalProperties: false
Expand Down Expand Up @@ -1470,6 +1470,20 @@
- Query parameters tenant/workspace/app/workflow/agent/toolset filter on the canonical Subject fields.
- Filtering on Subject.dimensions is out of scope for v0 unless explicitly implemented by the server.

TIME-RANGE FILTERS (NORMATIVE, ADDITIVE):
- Query parameters `from` and `to` (ISO 8601 date-time strings)
bound the `created_at_ms` of returned reservations, inclusive
on both ends.
- The filter is fixed to `created_at_ms` regardless of `sort_by`;
sorting by `expires_at_ms` while filtering by `from`/`to` is
valid and well-defined.
- Either may be supplied alone (open interval) or together
(closed window). `from > to` MUST return 400 INVALID_REQUEST.
- Both are additive parameters: servers that don't recognize
them MUST ignore without error and return the unfiltered set.
This matches the family-wide `from`/`to` convention on
`listAuditLogs`, `listEvents`, and `listWebhookDeliveries`.

TENANCY (NORMATIVE):
- Under ApiKeyAuth: the server MUST scope results to the effective
tenant derived from auth. If the tenant query parameter is
Expand Down Expand Up @@ -1524,6 +1538,43 @@
in: query
required: false
schema: {type: string}
- name: from
in: query
required: false
description: >-
Inclusive lower bound on reservation creation time. ISO 8601
date-time. When set, the server MUST return only reservations
whose `created_at_ms` is greater than or equal to this
timestamp. The filter ALWAYS binds to `created_at_ms`,
independent of `sort_by`. May be supplied alone (no upper
bound) or paired with `to`. Servers MUST reject `from > to`
with HTTP 400 INVALID_REQUEST.

Additive parameter — servers that don't recognize it MUST
ignore without error (additive-parameter guarantee). Matches
the `from` / `to` convention on `listAuditLogs`,
`listEvents`, and `listWebhookDeliveries` in the governance
spec family.
schema:
type: string
format: date-time
- name: to
in: query
required: false
description: >-
Inclusive upper bound on reservation creation time. ISO 8601
date-time. When set, the server MUST return only reservations
whose `created_at_ms` is less than or equal to this
timestamp. The filter ALWAYS binds to `created_at_ms`,
independent of `sort_by`. May be supplied alone (no lower
bound) or paired with `from`. Servers MUST reject `from > to`
with HTTP 400 INVALID_REQUEST.

Additive parameter — servers that don't recognize it MUST
ignore without error.
schema:
type: string
format: date-time
- name: sort_by
in: query
required: false
Expand Down
32 changes: 32 additions & 0 deletions merged/cycles-openapi-protocol-merged.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@
in: header
name: X-Admin-API-Key
schemas:
IdempotencyKey:

Check warning on line 74 in merged/cycles-openapi-protocol-merged.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: string
minLength: 1
maxLength: 256
ErrorCode:

Check warning on line 78 in merged/cycles-openapi-protocol-merged.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: string
enum:
- INVALID_REQUEST
Expand All @@ -96,7 +96,7 @@
- ACTION_QUOTA_EXCEEDED
- ACTION_KIND_NOT_ALLOWED
- ACTION_KIND_DENIED
ErrorResponse:

Check warning on line 99 in merged/cycles-openapi-protocol-merged.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: object
required:
- error
Expand Down Expand Up @@ -129,7 +129,7 @@
details:
type: object
additionalProperties: true
DecisionEnum:

Check warning on line 132 in merged/cycles-openapi-protocol-merged.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: string
enum:
- ALLOW
Expand Down Expand Up @@ -210,7 +210,7 @@
servers MUST default to "desc". Servers that do not recognize the parameter MUST ignore it without
error (additive-parameter guarantee). This schema is carried inline in each spec of the family
(protocol + governance); the companion-specs publication model does not use cross-spec $ref.
Amount:

Check warning on line 213 in merged/cycles-openapi-protocol-merged.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: object
required:
- unit
Expand Down Expand Up @@ -293,7 +293,7 @@
type: string
maxLength: 256
maxProperties: 16
Action:

Check warning on line 296 in merged/cycles-openapi-protocol-merged.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: object
required:
- kind
Expand Down Expand Up @@ -342,7 +342,7 @@
cooldown_ms:
type: integer
minimum: 0
DecisionRequest:

Check warning on line 345 in merged/cycles-openapi-protocol-merged.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: object
required:
- idempotency_key
Expand All @@ -362,7 +362,7 @@
metadata:
type: object
additionalProperties: true
DecisionResponse:

Check warning on line 365 in merged/cycles-openapi-protocol-merged.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: object
required:
- decision
Expand Down Expand Up @@ -423,7 +423,7 @@
- COMMITTED
- RELEASED
- EXPIRED
ReservationCreateRequest:

Check warning on line 426 in merged/cycles-openapi-protocol-merged.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: object
required:
- idempotency_key
Expand Down Expand Up @@ -570,7 +570,7 @@
capped the charge. When true, ALL new reservations against this scope are blocked with 409
OVERDRAFT_LIMIT_EXCEEDED until reconciled (debt repaid below overdraft_limit, or is_over_limit
cleared by operator). Defaults to false when absent.
ReservationCreateResponse:

Check warning on line 573 in merged/cycles-openapi-protocol-merged.yaml

View workflow job for this annotation

GitHub Actions / Lint sources, check merge drift, lint merged artifacts

schema-must-have-description Top-level schemas in components should have a description
type: object
required:
- decision
Expand Down Expand Up @@ -2186,6 +2186,20 @@
- Query parameters tenant/workspace/app/workflow/agent/toolset filter on the canonical Subject fields.
- Filtering on Subject.dimensions is out of scope for v0 unless explicitly implemented by the server.

TIME-RANGE FILTERS (NORMATIVE, ADDITIVE):
- Query parameters `from` and `to` (ISO 8601 date-time strings)
bound the `created_at_ms` of returned reservations, inclusive
on both ends.
- The filter is fixed to `created_at_ms` regardless of `sort_by`;
sorting by `expires_at_ms` while filtering by `from`/`to` is
valid and well-defined.
- Either may be supplied alone (open interval) or together
(closed window). `from > to` MUST return 400 INVALID_REQUEST.
- Both are additive parameters: servers that don't recognize
them MUST ignore without error and return the unfiltered set.
This matches the family-wide `from`/`to` convention on
`listAuditLogs`, `listEvents`, and `listWebhookDeliveries`.

TENANCY (NORMATIVE):
- Under ApiKeyAuth: the server MUST scope results to the effective
tenant derived from auth. If the tenant query parameter is
Expand Down Expand Up @@ -2246,6 +2260,24 @@
required: false
schema:
type: string
- name: from
in: query
required: false
description: |-
Inclusive lower bound on reservation creation time. ISO 8601 date-time. When set, the server MUST return only reservations whose `created_at_ms` is greater than or equal to this timestamp. The filter ALWAYS binds to `created_at_ms`, independent of `sort_by`. May be supplied alone (no upper bound) or paired with `to`. Servers MUST reject `from > to` with HTTP 400 INVALID_REQUEST.
Additive parameter — servers that don't recognize it MUST ignore without error (additive-parameter guarantee). Matches the `from` / `to` convention on `listAuditLogs`, `listEvents`, and `listWebhookDeliveries` in the governance spec family.
schema:
type: string
format: date-time
- name: to
in: query
required: false
description: |-
Inclusive upper bound on reservation creation time. ISO 8601 date-time. When set, the server MUST return only reservations whose `created_at_ms` is less than or equal to this timestamp. The filter ALWAYS binds to `created_at_ms`, independent of `sort_by`. May be supplied alone (no lower bound) or paired with `from`. Servers MUST reject `from > to` with HTTP 400 INVALID_REQUEST.
Additive parameter — servers that don't recognize it MUST ignore without error.
schema:
type: string
format: date-time
- name: sort_by
in: query
required: false
Expand Down