Skip to content

Commit dc34406

Browse files
authored
Merge pull request #66 from runcycles/feat/list-reservations-from-to
feat(list_reservations): lock in from/to ISO-8601 window passthrough (0.4.2)
2 parents 3407b42 + 6f6335d commit dc34406

4 files changed

Lines changed: 69 additions & 4 deletions

File tree

AUDIT.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
# Cycles Protocol v0.1.23 — Client (Python) Audit
1+
# Cycles Protocol v0.1.25 — Client (Python) Audit
22

3-
**Date:** 2026-03-14
4-
**Spec:** `cycles-protocol-v0.yaml` (OpenAPI 3.1.0, v0.1.23)
3+
**Date:** 2026-05-21 (v0.4.2 — `from` / `to` ISO-8601 window-filter passthrough on `list_reservations` per `cycles-protocol-v0.yaml` revision 2026-05-21; closes the client side of runcycles/cycles-server#159. No code change — the existing `**query_params` signature already forwards arbitrary kwargs to the URL query string. Added sync + async regression tests that lock the passthrough in (using the `**{"from": ..., "to": ...}` dict-unpack form because `from` is a Python reserved keyword). 391 tests pass at 100% coverage.),
4+
2026-03-14
5+
**Spec:** `cycles-protocol-v0.yaml` (OpenAPI 3.1.0, v0.1.25)
56
**Client:** `runcycles` (Python 3.10+ / httpx / Pydantic v2)
67
**Server audit:** See `cycles-server/AUDIT.md` (all passing)
78

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.4.2] - 2026-05-21
9+
10+
Wire-passthrough verification for the new `from` / `to` query params on `list_reservations`. Implements `cycles-protocol-v0.yaml` revision 2026-05-21 ([runcycles/cycles-protocol#97](https://github.com/runcycles/cycles-protocol/pull/97)) on the client side; runcycles/cycles-server#160 ships the server impl.
11+
12+
### Added
13+
14+
- Sync + async regression tests confirming `list_reservations` forwards `from` / `to` ISO-8601 date-time values to the URL query string byte-exactly. The client's `**query_params` signature already accepted these — the tests lock that in so future tightening cannot drop the params silently.
15+
16+
### Notes
17+
18+
- **`from` is a Python reserved keyword.** Callers cannot write `client.list_reservations(from="...", to="...")` directly. The supported pattern is the dict-unpack form:
19+
```python
20+
client.list_reservations(**{"from": "2026-05-21T00:00:00Z", "to": "2026-05-22T00:00:00Z"})
21+
```
22+
The wire format is identical; only the Python call-site syntax differs.
23+
- No protocol or wire-format change. Servers older than v0.1.25.20 will silently ignore the params per the additive-parameter guarantee in `cycles-protocol-v0.yaml`.
24+
- 391 tests pass at 100% coverage (gate ≥95%).
25+
826
## [0.4.1] - 2026-05-08
927

1028
PyPI metadata refresh for category-search discovery. No code changes; package wire format and API are identical to 0.4.0.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "runcycles"
7-
version = "0.4.1"
7+
version = "0.4.2"
88
description = "Python AI agent budget control — enforce LLM cost limits, tool permissions, and multi-tenant policies before agent actions execute."
99
readme = "README.md"
1010
license = "Apache-2.0"

tests/test_client.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,30 @@ def test_list_reservations(self, config: CyclesConfig, httpx_mock) -> None: # t
154154

155155
assert response.is_success
156156

157+
def test_list_reservations_with_from_to_window(self, config: CyclesConfig, httpx_mock) -> None: # type: ignore[no-untyped-def]
158+
"""`from`/`to` ISO-8601 window filter passthrough (cycles-protocol v0.1.25 revision 2026-05-21).
159+
160+
`from` is a Python reserved keyword so callers must use the dict-unpack form
161+
`**{"from": ..., "to": ...}` rather than a direct kwarg. The client forwards
162+
the params byte-exactly to the URL query string."""
163+
httpx_mock.add_response(
164+
method="GET",
165+
url="http://localhost:7878/v1/reservations?tenant=acme&from=2026-05-21T00%3A00%3A00Z&to=2026-05-22T00%3A00%3A00Z",
166+
json={"reservations": [], "has_more": False},
167+
status_code=200,
168+
)
169+
170+
with CyclesClient(config) as client:
171+
response = client.list_reservations(
172+
**{
173+
"tenant": "acme",
174+
"from": "2026-05-21T00:00:00Z",
175+
"to": "2026-05-22T00:00:00Z",
176+
}
177+
)
178+
179+
assert response.is_success
180+
157181
def test_get_reservation(self, config: CyclesConfig, httpx_mock) -> None: # type: ignore[no-untyped-def]
158182
httpx_mock.add_response(
159183
method="GET",
@@ -397,6 +421,28 @@ async def test_list_reservations(self, config: CyclesConfig, httpx_mock) -> None
397421

398422
assert response.is_success
399423

424+
async def test_list_reservations_with_from_to_window(self, config: CyclesConfig, httpx_mock) -> None: # type: ignore[no-untyped-def]
425+
"""Async parity for the `from`/`to` window-filter passthrough.
426+
427+
See sync sibling for the reserved-keyword caveat on `from`."""
428+
httpx_mock.add_response(
429+
method="GET",
430+
url="http://localhost:7878/v1/reservations?tenant=acme&from=2026-05-21T00%3A00%3A00Z&to=2026-05-22T00%3A00%3A00Z",
431+
json={"reservations": [], "has_more": False},
432+
status_code=200,
433+
)
434+
435+
async with AsyncCyclesClient(config) as client:
436+
response = await client.list_reservations(
437+
**{
438+
"tenant": "acme",
439+
"from": "2026-05-21T00:00:00Z",
440+
"to": "2026-05-22T00:00:00Z",
441+
}
442+
)
443+
444+
assert response.is_success
445+
400446
async def test_get_reservation(self, config: CyclesConfig, httpx_mock) -> None: # type: ignore[no-untyped-def]
401447
httpx_mock.add_response(
402448
method="GET",

0 commit comments

Comments
 (0)