From ab47c85f957f803bef2590798e78d7ab57a878f6 Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Wed, 20 May 2026 21:35:26 -0700 Subject: [PATCH] chore(assets): drop cross-repo prose from cursor comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Strip prose references to sibling Go implementations and external ticket IDs from cursor.py, the cursor tests, the keyset integration tests, asset_management's sort-field comment, and the legacy prompt_id alias comment. Pure docstring/comment scrub — no behavior or wire-format changes. x-runtime: [cloud] field annotations in openapi.yaml are unchanged; those are the spec's structural cross-runtime convention, not internal references. --- app/assets/api/routes.py | 2 +- app/assets/services/asset_management.py | 5 ++-- app/assets/services/cursor.py | 21 +++++------------ .../assets_test/services/test_cursor.py | 23 +++++++++---------- tests-unit/assets_test/test_list_cursor.py | 6 ++--- 5 files changed, 23 insertions(+), 34 deletions(-) diff --git a/app/assets/api/routes.py b/app/assets/api/routes.py index 9158f18f1..84ec2dd6e 100644 --- a/app/assets/api/routes.py +++ b/app/assets/api/routes.py @@ -173,7 +173,7 @@ def _build_asset_response(result: schemas.AssetDetailResult | schemas.UploadResu user_metadata=result.ref.user_metadata or {}, metadata=result.ref.system_metadata, job_id=result.ref.job_id, - prompt_id=result.ref.job_id, # deprecated: mirrors job_id for cloud compat + prompt_id=result.ref.job_id, # deprecated alias of job_id, kept for compatibility created_at=result.ref.created_at, updated_at=result.ref.updated_at, last_access_time=result.ref.last_access_time, diff --git a/app/assets/services/asset_management.py b/app/assets/services/asset_management.py index 26beba86f..1072c95fa 100644 --- a/app/assets/services/asset_management.py +++ b/app/assets/services/asset_management.py @@ -253,9 +253,8 @@ def get_asset_by_hash(asset_hash: str) -> AssetData | None: return extract_asset_data(asset) -# Sort fields that support cursor pagination. Mirrors cloud's allowlist -# (created_at, updated_at, name, size). `last_access_time` is OSS-only and -# falls back to offset/limit — no cloud contract to match. +# Sort fields that support cursor pagination. `last_access_time` is not +# in this list — it falls back to offset/limit. _CURSOR_SORT_FIELDS = ("created_at", "updated_at", "name", "size") diff --git a/app/assets/services/cursor.py b/app/assets/services/cursor.py index 4e291a355..47ea77cfc 100644 --- a/app/assets/services/cursor.py +++ b/app/assets/services/cursor.py @@ -1,8 +1,6 @@ """Opaque keyset-pagination cursor for /api/assets. -Wire format aligns with the cloud Go implementation in -`common/pagination/cursor.go` so the frontend sees one contract across -runtimes. Payload JSON uses short keys to keep the encoded length small: +Payload JSON uses short keys to keep the encoded length small: {"s": , "v": , "id": , "o": } @@ -10,14 +8,12 @@ The `o` key binds the cursor to the sort direction it was minted under, so replaying a `desc` cursor against an `asc` request fails with ``INVALID_CURSOR`` rather than silently walking the wrong direction. `o` is mandatory on every payload — a cursor without it is rejected as -malformed. Cloud will land the same field in its mirror PR; until then, -Python and cloud cursors differ by exactly the `o` key, and a cloud- -minted cursor cannot be decoded by this endpoint. +malformed. Encoding is base64url with no padding. JSON serialization escapes `<`, `>`, `&`, U+2028, and U+2029 to match Go's default `json.Marshal` behavior so asset names containing those characters produce -byte-identical cursors across runtimes. +byte-identical cursors across compatible Go implementations. Time values are serialized as Unix microseconds (UTC) — microsecond precision matches PostgreSQL's `timestamp` type, so a cursor minted from @@ -44,12 +40,9 @@ class InvalidCursorError(ValueError): # decode path from oversized allocations and downstream SQL predicates from # unbounded strings. # -# MAX_CURSOR_VALUE_LENGTH is 512 (vs cloud's 256) to fit OSS's -# `AssetReference.name` column max (String(512)) — otherwise a long-named -# asset would mint a cursor the same server then refuses on the next request. -# Cloud's data model has shorter names so its lower cap is fine there; -# cross-runtime byte-identity is unaffected because no real cloud cursor ever -# carries a value > 256. +# MAX_CURSOR_VALUE_LENGTH is 512 to fit the `AssetReference.name` column max +# (`String(512)`) — otherwise a long-named asset would mint a cursor the same +# server then refuses on the next request. MAX_ENCODED_CURSOR_LENGTH = 1024 MAX_CURSOR_VALUE_LENGTH = 512 MAX_CURSOR_ID_LENGTH = 128 @@ -63,8 +56,6 @@ class CursorPayload: order: str -# Order direction tokens. Mirrored on the cloud follow-up so cursors carrying -# `o` are interchangeable between runtimes once both sides ship the field. _VALID_ORDERS = ("asc", "desc") diff --git a/tests-unit/assets_test/services/test_cursor.py b/tests-unit/assets_test/services/test_cursor.py index fb6acefee..c4a1cc1b4 100644 --- a/tests-unit/assets_test/services/test_cursor.py +++ b/tests-unit/assets_test/services/test_cursor.py @@ -1,10 +1,9 @@ """Tests for app.assets.services.cursor. -The wire format must stay byte-identical with the cloud Go implementation -(common/pagination/cursor.go in Comfy-Org/cloud) so the frontend sees one -contract across runtimes. The byte-identity fixture below mirrors the Go -test cases — any drift here means cloud and OSS minted different cursors -for the same triple, which would break FE pagination across backends. +The byte-identity fixtures below pin the wire format so a parallel Go +implementation can mint exchange-compatible cursors for the same triple +(asset name, value, id). Drift here would break frontend pagination +against any compatible backend. """ from __future__ import annotations @@ -187,9 +186,9 @@ class TestEncoderDecoderSymmetry: """ def test_long_name_within_cap_round_trips(self): - """OSS assets allow names up to 512 chars (`String(512)`); cursor must - handle that. Cloud's lower cap is acceptable on its side because the - cloud schema doesn't permit names that long.""" + """Assets allow names up to 512 chars (`String(512)`); the cursor + encoder must round-trip a value at that cap so a freshly minted + cursor never fails decode on the next request.""" long_name = "n" * MAX_CURSOR_VALUE_LENGTH encoded = encode_cursor("name", long_name, "asset-x") payload = decode_cursor(encoded, ALLOWED) @@ -269,10 +268,10 @@ class TestGoCompatJsonEscaping: class TestByteIdentityFixtures: """Pin the wire format so it doesn't drift silently. - NOTE — these fixtures will need updates on the cloud side once cloud - mirrors the `o` (order binding) field. Until then, Python cursors and - cloud cursors differ by exactly that key. The structural format (`s`, - `v`, `id` plus base64url + Go-compat escaping) remains aligned. + These fixtures hold the exact base64url-encoded bytes a given payload + produces. Any change to the encoding (key order, escaping, ordering of + structural fields) will fail these tests loudly rather than diverge + silently from external consumers. """ @pytest.mark.parametrize( diff --git a/tests-unit/assets_test/test_list_cursor.py b/tests-unit/assets_test/test_list_cursor.py index 44ab21037..7f692e8d0 100644 --- a/tests-unit/assets_test/test_list_cursor.py +++ b/tests-unit/assets_test/test_list_cursor.py @@ -1,8 +1,8 @@ """Integration tests for cursor-based pagination on GET /api/assets. -Wire contract is shared with cloud's Go implementation (BE-893). These tests -exercise the handler/service/query path end-to-end; cursor-encoding-level -tests live in tests-unit/assets_test/services/test_cursor.py. +These tests exercise the handler/service/query path end-to-end; +cursor-encoding-level tests live in +tests-unit/assets_test/services/test_cursor.py. """ import time