Skip to content

Implement pagination crate foundation with tests and workspace wiring#327

Merged
leynos merged 3 commits into
mainfrom
implement-pagination-crate-1zphgx
Mar 23, 2026
Merged

Implement pagination crate foundation with tests and workspace wiring#327
leynos merged 3 commits into
mainfrom
implement-pagination-crate-1zphgx

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Mar 22, 2026

Summary

  • Implemented a standalone pagination crate foundation at backend/crates/pagination, wired as an explicit workspace member.
  • Exposes opaque cursor handling via Cursor<K>, normalized query parameters via PageParams, and envelope types Paginated<T> with PaginationLinks for navigation.
  • Adds unit tests (rstest) and behavioural tests (rstest-bdd), plus a feature file to cover crate-level expectations.
  • Updates and documents the architecture and execution plan to reflect the pagination crate foundation and its workspace wiring.
  • Aligns workspace discovery and tooling with explicit crate path in the root manifest.

Changes

  • Workspace:
    • Add backend/crates/pagination as an explicit member in the root Cargo.toml to ensure workspace discovery.
  • Crate scaffold:
    • Create backend/crates/pagination with Cargo.toml, src/lib.rs, and modules such as cursor.rs, params.rs, envelope.rs, and (public) items exposed by lib.rs.
    • Provide crate-level and public-item Rustdoc comments with usage examples where helpful.
  • Tests:
    • Add rstest unit test scaffolds to define public contracts (cursor round-tripping, invalid token decoding, defaulting/normalization).
    • Add rstest-bdd behavioural test scaffolds to exercise happy/unhappy/edge scenarios at the crate boundary.
    • Add a Gherkin-style feature file at backend/crates/pagination/tests/features/pagination.feature.
    • Include behavioural tests at backend/crates/pagination/tests/pagination_bdd.rs.
  • Documentation:
    • Add ExecPlan for 4.1.1 at docs/execplans/backend-4-1-1-pagination-crate.md.
    • Update architecture docs to describe the shared pagination crate boundary at backend/crates/pagination and how it fits hexagonal boundaries.
    • Update backend roadmap to mark 4.1.1 as completed.
  • Architecture / Docs:
    • Documentation updates reflect the crate boundary, scope, and workspace wiring.

Milestones / Progress plan

  • Milestone 0 – Create crate seam and red-name the API
    • Created directory tree and manifest at backend/crates/pagination.
    • Wired explicit workspace membership.
  • Milestone 1 – Implement cursor primitives
    • Implemented Cursor<K> with opaque base64url JSON encoding/decoding and error handling.
  • Milestone 2 – Implement PageParams and Paginated<T> envelopes
    • Implemented PageParams, DEFAULT_LIMIT, MAX_LIMIT, and Paginated<T> with PaginationLinks.
  • Milestone 3 – Behavioural coverage and docs
    • Added unit and behavioural tests, crate docs, and example usage snippets.
  • Milestone 4 – Architecture alignment and gates
    • Updated architecture docs and ExecPlan; updated workspace wiring and gate-related docs.

Acceptance criteria

  1. cargo test -p pagination passes, including rstest unit tests.
  2. cargo test -p pagination --test pagination_foundation_bdd passes for BDD scenarios.
  3. cargo doc --workspace --no-deps succeeds with the new crate present.
  4. make check-fmt, make lint, and make test pass with logs captured.
  5. Roadmap item 4.1.1 checkbox updated only after gates are green.

Test plan

  • Local verification steps:
    • cargo test -p pagination
    • cargo test -p pagination --test pagination_foundation_bdd
    • cargo doc --workspace --no-deps
    • make check-fmt
    • make lint
    • make test (gate-based workspace flow with embedded PG as configured in the repo)

Rationale / design notes

  • The crate remains transport- and persistence-agnostic to support hexagonal boundaries between inbound adapters and outbound repositories.
  • Public API is intentionally small and human-readable, with distinct modules for cursor, params, envelope, and error (cursor error surfaced via CursorError).
  • Behavioural tests and documentation are crate-scoped to avoid coupling to persistence/backend specifics.

Risks & mitigations

  • Workspace wiring risk: explicitly declare backend/crates/pagination in the root manifest to avoid invisibility to tooling.
  • API surface risk: keep the minimal seam needed for opaque cursor round-tripping and link generation; defer more advanced traversal/persistence details to later milestones.

Ownership / collaboration

  • Crate design: responsible for layout and public API surface.
  • Test agent: unit and behavioural test coverage.
  • Documentation: crate docs and architecture/docs updates.
  • Gate agent: ensure gates and logs are captured for the final verification step.

Task

📎 Task: https://www.devboxer.com/task/5a151c60-1799-446e-89c1-84fed29af235

Summary by Sourcery

Introduce a shared pagination crate and wire it into the workspace, providing reusable cursor, parameter, and envelope primitives with accompanying tests and documentation updates.

New Features:

  • Add a new backend/crates/pagination crate exposing opaque cursor encoding, normalized PageParams, and Paginated<T>/PaginationLinks response envelopes for shared pagination support.

Enhancements:

  • Wire the new pagination crate into the root Cargo workspace and autodiscovery globs for consistent tooling visibility.
  • Document the shared pagination crate boundary, responsibilities, and constraints in the backend architecture guide.
  • Record the execution plan for roadmap item 4.1.1 and update the backend roadmap to mark the pagination foundation as complete.

Documentation:

  • Add an ExecPlan document detailing the implementation plan, constraints, risks, and outcomes for the pagination crate foundation roadmap item.

Tests:

  • Add rstest unit tests and rstest-bdd behavioural tests, including a Gherkin feature, to verify cursor round-tripping, parameter normalization, and link generation in the pagination crate.

📎 Task: https://www.devboxer.com/task/e6b2733d-a5de-4317-b1e2-2fd8aa605430

….1.1

Introduce the execution plan document for roadmap item 4.1.1, outlining the design, constraints, progress, risks, and milestones for implementing the backend pagination crate. This living document ensures clarity on the scope and approach before implementation begins.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Mar 22, 2026

Reviewer's Guide

Introduces a new workspace-local pagination crate under backend/crates/pagination that provides opaque cursor handling, normalized page parameters, and paginated response envelopes with navigation links, together with BDD/unit tests and architecture/roadmap/docs updates wiring the crate into the Cargo workspace autodiscovery.

Sequence diagram for cursor encode/decode flow

sequenceDiagram
    actor Developer
    participant BackendService
    participant PaginationCursor as "Cursor<UserKey>"

    Developer->>BackendService: call handler with last_seen_user
    BackendService->>PaginationCursor: new(last_seen_user_key)
    PaginationCursor-->>BackendService: Cursor<UserKey>
    BackendService->>PaginationCursor: encode()
    PaginationCursor-->>BackendService: Result<String, CursorError>
    BackendService-->>Developer: opaque cursor token in response

    Developer->>BackendService: follow next page with cursor token
    BackendService->>PaginationCursor: decode(cursor_token)
    PaginationCursor-->>BackendService: Result<Cursor<UserKey>, CursorError>
    BackendService-->>Developer: next page data or error mapped from CursorError
Loading

Sequence diagram for building paginated response with links

sequenceDiagram
    actor Client
    participant HttpHandler
    participant Repo as Repository
    participant PageParamsType as PageParams
    participant PaginationLinksType as PaginationLinks
    participant PaginatedType as Paginated~UserDto~

    Client->>HttpHandler: GET /users?cursor=c1&limit=200
    HttpHandler->>PageParamsType: deserialize query into PageParams
    PageParamsType-->>HttpHandler: PageParams{ cursor: c1, limit: 100 }

    HttpHandler->>Repo: fetch_page(cursor=c1, limit=100)
    Repo-->>HttpHandler: users, next_cursor, prev_cursor

    HttpHandler->>PaginationLinksType: from_request(request_url, &PageParams, next_cursor, prev_cursor)
    PaginationLinksType-->>HttpHandler: PaginationLinks

    HttpHandler->>PaginatedType: new(users, PageParams.limit(), PaginationLinks)
    PaginatedType-->>HttpHandler: Paginated<UserDto>

    HttpHandler-->>Client: 200 OK with data, limit, links
Loading

Class diagram for new pagination crate types

classDiagram
    class Cursor~Key~ {
        -Key key
        +new(key: Key) Cursor~Key~
        +key() &Key
        +into_inner() Key
        +encode() Result<String, CursorError>
    }

    class CursorError {
        <<enum>>
        +Serialize message: String
        +InvalidBase64 message: String
        +Deserialize message: String
    }

    class PageParams {
        -Option<String> cursor
        -usize limit
        +new(cursor: Option<String>, limit: Option<usize>) Result<PageParams, PageParamsError>
        +cursor() Option<&str>
        +limit() usize
    }

    class PageParamsError {
        <<enum>>
        +InvalidLimit
    }

    class PaginationLinks {
        +String self_
        +Option<String> next
        +Option<String> prev
        +new(self_: String, next: Option<String>, prev: Option<String>) PaginationLinks
        +from_request(request_url: Url, params: &PageParams, next_cursor: Option<&str>, prev_cursor: Option<&str>) PaginationLinks
    }

    class Paginated~T~ {
        +Vec<T> data
        +usize limit
        +PaginationLinks links
        +new(data: Vec<T>, limit: usize, links: PaginationLinks) Paginated~T~
    }

    class Constants {
        +DEFAULT_LIMIT: usize
        +MAX_LIMIT: usize
    }

    Cursor~Key~ --> CursorError
    PageParams --> PageParamsError
    PaginationLinks --> PageParams
    Paginated~T~ --> PaginationLinks
    PageParams ..> Constants
Loading

File-Level Changes

Change Details Files
Add and wire a new pagination utility crate into the Cargo workspace
  • Create backend/crates/pagination crate manifest with dependencies on base64, serde, serde_json, thiserror, and url plus rstest/rstest-bdd for tests
  • Expose the crate via src/lib.rs re-exporting cursor, params, and envelope modules and enforcing workspace lint configuration
  • Add the new crate as an explicit member in the root workspace and extend workspace autodiscovery globs to include backend/crates/* so sync scripts retain it
backend/crates/pagination/Cargo.toml
backend/crates/pagination/src/lib.rs
Cargo.toml
Cargo.lock
Implement opaque cursor encoding/decoding with typed errors
  • Define generic Cursor<Key> wrapper with new, key, into_inner, encode, and decode helpers using base64url JSON representation
  • Introduce CursorError enum for serialize, base64, and deserialize failures with thiserror messages
  • Add unit tests covering successful round-trip, invalid base64, and structurally invalid JSON cases
backend/crates/pagination/src/cursor.rs
Implement normalized page parameters with shared defaults and validation
  • Define PageParams struct with cursor and normalized limit plus accessors and constructor new that applies default (20), max cap (100), and rejects zero
  • Provide PageParamsError and internal RawPageParams plus custom Deserialize impl that normalizes on deserialization
  • Add constants DEFAULT_LIMIT and MAX_LIMIT and unit tests for defaulting, capping, zero rejection, and JSON deserialization behavior
backend/crates/pagination/src/params.rs
Implement paginated response envelope and hypermedia link generation
  • Define PaginationLinks with self_, optional next, and optional prev plus new and from_request constructors
  • Implement internal build_page_link that clones a Url, strips existing limit/cursor, preserves other query params, and rewrites limit and cursor
  • Define generic Paginated<T> envelope with data, limit, links and new constructor, plus unit tests verifying query-parameter preservation and generated links
backend/crates/pagination/src/envelope.rs
Add BDD-style behavioural tests and Gherkin feature for the pagination crate
  • Create pagination_bdd.rs scenario harness using rstest-bdd with a World state carrying keys, tokens, params, URLs, and envelopes
  • Define Given/When/Then steps to exercise cursor round-trip, malformed token failure, limit normalization, and link generation semantics
  • Add tests/features/pagination.feature Gherkin file describing scenarios for cursor behaviour, parameter limits, and envelope link building
backend/crates/pagination/tests/pagination_bdd.rs
backend/crates/pagination/tests/features/pagination.feature
Update architecture, roadmap, and execution-plan documentation for the new crate
  • Extend backend architecture doc with a subsection describing the shared pagination foundation crate, its narrow boundary, and hexagonal rules
  • Mark roadmap item 4.1.1 as completed in the backend roadmap document
  • Add a detailed execution plan document for roadmap 4.1.1 capturing constraints, risks, decisions, progress, and gate evidence
docs/wildside-backend-architecture.md
docs/backend-roadmap.md
docs/execplans/backend-4-1-1-pagination-crate.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 22, 2026

Walkthrough

Introduce a new workspace crate backend/crates/pagination providing opaque cursor encoding/decoding (Cursor<Key>), pagination parameter normalisation (PageParams), and transport-neutral paginated envelopes (Paginated<T> / PaginationLinks). Wire the crate into the workspace and add unit/BDD tests plus documentation and an exec plan.

Changes

Cohort / File(s) Summary
Workspace Configuration
Cargo.toml
Add backend/crates/pagination to workspace members and extend autodiscovery globs to include backend/crates/*.
Pagination Crate Manifest
backend/crates/pagination/Cargo.toml
Add new crate manifest declaring dependencies (serde, base64, serde_json, thiserror, url) and dev-dependencies for rstest BDD.
Cursor primitives
backend/crates/pagination/src/cursor.rs
Add Cursor<Key> with encode/decode using JSON + base64url, and CursorError variants; include unit tests for round-trip and failure cases.
Envelope & Links
backend/crates/pagination/src/envelope.rs
Add PaginationLinks and Paginated<T>; implement from_request to build self/next/prev preserving non-pagination query params; include unit test for query preservation.
Page parameters
backend/crates/pagination/src/params.rs
Add PageParams with DEFAULT_LIMIT = 20, MAX_LIMIT = 100; normalise limits (default, clamp, reject zero); implement Deserialize and tests.
Crate root exports
backend/crates/pagination/src/lib.rs
Wire modules and re-export public types/constants: Cursor, CursorError, Paginated, PaginationLinks, PageParams, DEFAULT_LIMIT, MAX_LIMIT, PageParamsError.
Behavioural tests
backend/crates/pagination/tests/features/pagination.feature, backend/crates/pagination/tests/pagination_bdd.rs
Add BDD feature and step implementations to validate cursor encoding/decoding, param normalisation, and link generation/omission.
Documentation & Plans
docs/backend-roadmap.md, docs/execplans/backend-4-1-1-pagination-crate.md, docs/wildside-backend-architecture.md
Mark roadmap task complete; add detailed exec plan and architecture subsection describing crate responsibilities and hexagonal boundaries.

Sequence Diagrams

sequenceDiagram
    participant Client
    participant PageParams as PageParams\n(Normaliser)
    participant Cursor as Cursor<K>\n(Encoder/Decoder)
    participant Store as DataStore
    participant Envelope as Paginated<T>\n+ PaginationLinks

    rect rgba(100,150,200,0.5)
        Note over Client,PageParams: Inbound request with pagination query
        Client->>PageParams: cursor=abc123&limit=50
        PageParams->>PageParams: Normalise limit (default/clamp/validate)
        PageParams-->>Client: PageParams{cursor, limit}
    end

    rect rgba(150,200,100,0.5)
        Note over Client,Cursor: Decode cursor token (if present)
        Client->>Cursor: decode("abc123")
        Cursor->>Cursor: base64url-decode → JSON → Cursor<K>
        Cursor-->>Client: Cursor{key}
    end

    rect rgba(200,150,100,0.5)
        Note over Client,Store: Fetch and assemble response
        Client->>Store: Query with normalised limit & key
        Store-->>Client: Vec<T>
        Client->>Envelope: new(data, limit, links)
        Envelope->>Envelope: PaginationLinks::from_request(request_url, params, next, prev)
        Envelope->>Envelope: Preserve existing filters, set limit and cursor qs
        Envelope-->>Client: Paginated{data, limit, links{self, next, prev}}
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Poem

"Opaque cursor tucked in base64's shawl,
PageParams steady, limits heed the call,
Links stitch filters into every route,
Paginated cargo sets the page about,
Crate births a flow — a tidy pagination ball."

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title directly and precisely summarizes the main change: implementing a pagination crate foundation with tests and workspace wiring, which aligns with all file additions and modifications.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, covering all major components (crate scaffold, tests, workspace wiring, documentation) with clear objectives, milestones, and acceptance criteria.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch implement-pagination-crate-1zphgx

Comment @coderabbitai help to get the list of available commands and usage tips.

codescene-delta-analysis[bot]

This comment was marked as outdated.

- Introduce `backend/crates/pagination` workspace crate providing:
  - `Cursor<K>` for opaque base64url JSON cursor encoding/decoding.
  - `PageParams` for normalized pagination query parameters with defaults and caps.
  - `Paginated<T>` and `PaginationLinks` for paginated response envelopes with hypermedia links.
- Add unit and behavioral tests covering cursor round-trip, parameter normalization, and envelope link generation.
- Update workspace configuration to include backend crates directory for discovery.
- Document the crate design and integration in backend architecture and roadmap.

This crate is generic and infrastructure-neutral, serving as a shared pagination foundation.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
@leynos leynos changed the title Implement pagination crate foundation with tests Add pagination crate foundation and workspace wiring with tests Mar 22, 2026
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos leynos marked this pull request as ready for review March 22, 2026 20:46
sourcery-ai[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7a2415032d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/crates/pagination/tests/pagination_bdd.rs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/crates/pagination/src/cursor.rs`:
- Around line 110-117: The decode function currently uses URL_SAFE_NO_PAD which
rejects padded base64url strings; modify the imports to bring in both
URL_SAFE_NO_PAD and URL_SAFE from base64::engine::general_purpose, then change
Cursor::decode so it first attempts URL_SAFE_NO_PAD.decode(value) and, on error,
falls back to URL_SAFE.decode(value) before mapping the error into
CursorError::InvalidBase64 (preserving the existing error.message handling) so
both padded and unpadded tokens are accepted.

In `@backend/crates/pagination/src/envelope.rs`:
- Around line 23-65: Add Rustdoc examples for the public constructors/builders:
PaginationLinks::new, PaginationLinks::from_request, and Paginated::new. For
each function comment, include a short, executable example block (/// ```rust)
that constructs realistic inputs (e.g., a Url and PageParams or sample cursors),
calls the function, and asserts or prints the resulting link strings or
Paginated<T> shape to show expected output; ensure examples compile by using
simple types for T (like i32 or &str) and include use statements necessary for
Url/PageParams or create minimal test helpers, so the doc tests validate the
demonstrated behavior.

In `@backend/crates/pagination/tests/pagination_bdd.rs`:
- Around line 3-6: Remove the crate-level attribute
#![expect(clippy::expect_used, ...)] from the top of the test module and instead
add a function-level #[expect(clippy::expect_used, reason = "...")] on each BDD
step function that intentionally calls expect (for example, annotate
pagination_parameters_without_a_limit which calls
PageParams::new(...).expect(...), and any other step functions that use expect).
Ensure no file-level allow/expect attributes remain and verify with the provided
rg command to confirm there are no remaining crate-wide suppressions.

In `@docs/execplans/backend-4-1-1-pagination-crate.md`:
- Around line 460-469: The code block lines exceed 120 columns; introduce a
variable (e.g., LOG_DIR) and use mkdir -p "${LOG_DIR}" then replace repeated
long paths with "${LOG_DIR}/<name>.log" for each command (make
prepare-pg-worker, make fmt, make markdownlint, make nixie, make check-fmt, make
lint, make test) so every shell line (including set -o pipefail && ... | tee
...) stays under 120 characters while preserving behavior and filenames.
- Line 58: Replace all absolute local filesystem links that start with
/home/user/project in docs/execplans/backend-4-1-1-pagination-crate.md with
repository-relative paths; for example change the link target for "root
[Cargo.toml]" from (/home/user/project/Cargo.toml) to a relative path like
(../../Cargo.toml) and update other occurrences such as
"[docs/wildside-backend-architecture.md]" and the self-link
"[docs/execplans/backend-4-1-1-pagination-crate.md]" to repository-relative
forms (e.g., (../wildside-backend-architecture.md) and
(backend-4-1-1-pagination-crate.md)); apply the same replacement pattern to the
other reported locations (lines ~70 and ~145) so no link targets point to the
local filesystem.

In `@docs/wildside-backend-architecture.md`:
- Around line 1953-1957: The sentence claiming the root workspace autodiscovery
does not include backend/crates/* is stale; update the paragraph about "Roadmap
4.1.1" and the `backend/crates/pagination` crate to state that the root Cargo
workspace now includes `backend/crates/*` (as updated in the root Cargo.toml),
so the crate will remain present during workspace-member sync — replace the old
sentence with wording that reflects the current state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c9c5ce7e-d21d-4017-af8a-38f28d230bc5

📥 Commits

Reviewing files that changed from the base of the PR and between b810523 and 7a24150.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • Cargo.toml
  • backend/crates/pagination/Cargo.toml
  • backend/crates/pagination/src/cursor.rs
  • backend/crates/pagination/src/envelope.rs
  • backend/crates/pagination/src/lib.rs
  • backend/crates/pagination/src/params.rs
  • backend/crates/pagination/tests/features/pagination.feature
  • backend/crates/pagination/tests/pagination_bdd.rs
  • docs/backend-roadmap.md
  • docs/execplans/backend-4-1-1-pagination-crate.md
  • docs/wildside-backend-architecture.md

Comment thread backend/crates/pagination/src/cursor.rs
Comment thread backend/crates/pagination/src/envelope.rs
Comment thread backend/crates/pagination/tests/pagination_bdd.rs Outdated
Comment thread docs/execplans/backend-4-1-1-pagination-crate.md Outdated
Comment thread docs/execplans/backend-4-1-1-pagination-crate.md
Comment thread docs/wildside-backend-architecture.md
…n links API

- Cursor decoding now accepts both unpadded and padded Base64 URL-safe cursors.
- Added examples and expanded API for PaginationLinks and Paginated envelope construction.
- Extended BDD tests to verify correct inclusion and omission of pagination links based on cursor availability.
- Updated documentation links and fixed relative paths in docs.

These enhancements improve flexibility and usability of the pagination crate's core components.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
docs/execplans/backend-4-1-1-pagination-crate.md (2)

460-469: ⚠️ Potential issue | 🟡 Minor

Wrap verification command lines to stay within 120 columns.

Shorten Line 462 to Line 468 by introducing a log directory variable and reusing it.

🛠️ Proposed fix
 ```bash
-mkdir -p /tmp/backend-4-1-1
-set -o pipefail && make prepare-pg-worker 2>&1 | tee /tmp/backend-4-1-1/prepare-pg-worker.log
-set -o pipefail && make fmt 2>&1 | tee /tmp/backend-4-1-1/fmt.log
-set -o pipefail && make markdownlint 2>&1 | tee /tmp/backend-4-1-1/markdownlint.log
-set -o pipefail && make nixie 2>&1 | tee /tmp/backend-4-1-1/nixie.log
-set -o pipefail && make check-fmt 2>&1 | tee /tmp/backend-4-1-1/check-fmt.log
-set -o pipefail && make lint 2>&1 | tee /tmp/backend-4-1-1/lint.log
-set -o pipefail && make test 2>&1 | tee /tmp/backend-4-1-1/test.log
+LOG_DIR=/tmp/backend-4-1-1
+mkdir -p "${LOG_DIR}"
+set -o pipefail && make prepare-pg-worker 2>&1 | tee "${LOG_DIR}/prepare-pg-worker.log"
+set -o pipefail && make fmt 2>&1 | tee "${LOG_DIR}/fmt.log"
+set -o pipefail && make markdownlint 2>&1 | tee "${LOG_DIR}/markdownlint.log"
+set -o pipefail && make nixie 2>&1 | tee "${LOG_DIR}/nixie.log"
+set -o pipefail && make check-fmt 2>&1 | tee "${LOG_DIR}/check-fmt.log"
+set -o pipefail && make lint 2>&1 | tee "${LOG_DIR}/lint.log"
+set -o pipefail && make test 2>&1 | tee "${LOG_DIR}/test.log"
</details>



As per coding guidelines: "Code blocks in Markdown must be wrapped at 120 columns."

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @docs/execplans/backend-4-1-1-pagination-crate.md around lines 460 - 469, The
verification commands exceed the 120-column Markdown wrap rule; introduce a
reusable log directory variable (e.g., LOG_DIR) and use it for mkdir -p and all
tee targets so each line is shorter: set LOG_DIR=/tmp/backend-4-1-1, mkdir -p
"${LOG_DIR}", then replace explicit /tmp/backend-4-1-1/... paths in the make
commands (prepare-pg-worker, fmt, markdownlint, nixie, check-fmt, lint, test)
with "${LOG_DIR}/.log" to keep lines under 120 columns.


</details>

---

`56-58`: _⚠️ Potential issue_ | _🟡 Minor_

**Synchronize the workspace wiring constraint with the implemented decision.**

Update Line 56 to Line 58 so this section reflects the final autodiscovery-glob approach already recorded later in the same document; remove the stale “explicit workspace member” directive.

<details>
<summary>📝 Proposed fix</summary>

```diff
-- Because the root workspace currently auto-discovers `crates/*` rather than
-- `backend/crates/*`, add the new crate as an explicit workspace member in the
-- root [Cargo.toml](../../Cargo.toml).
+- Ensure the root workspace autodiscovery globs include `backend/crates/*` so
+- `backend/crates/pagination` remains durable during workspace-member sync in
+- root [Cargo.toml](../../Cargo.toml).

As per coding guidelines: "Use markdown files within the docs/ directory as the knowledge base and source of truth for project requirements, dependencies, and architectural decisions."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/execplans/backend-4-1-1-pagination-crate.md` around lines 56 - 58,
Replace the stale instruction sentence that reads "Because the root workspace
currently auto-discovers `crates/*` rather than `backend/crates/*`, add the new
crate as an explicit workspace member in the root
[Cargo.toml](../../Cargo.toml)." with wording that reflects the implemented
autodiscovery-glob approach (remove the explicit workspace member directive) so
the section matches the later documented autodiscovery behavior; ensure the new
text states that the root workspace autodiscovers the correct glob and no manual
workspace member entry is required.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@docs/execplans/backend-4-1-1-pagination-crate.md`:
- Around line 460-469: The verification commands exceed the 120-column Markdown
wrap rule; introduce a reusable log directory variable (e.g., LOG_DIR) and use
it for mkdir -p and all tee targets so each line is shorter: set
LOG_DIR=/tmp/backend-4-1-1, mkdir -p "${LOG_DIR}", then replace explicit
/tmp/backend-4-1-1/... paths in the make commands (prepare-pg-worker, fmt,
markdownlint, nixie, check-fmt, lint, test) with "${LOG_DIR}/<name>.log" to keep
lines under 120 columns.
- Around line 56-58: Replace the stale instruction sentence that reads "Because
the root workspace currently auto-discovers `crates/*` rather than
`backend/crates/*`, add the new crate as an explicit workspace member in the
root [Cargo.toml](../../Cargo.toml)." with wording that reflects the implemented
autodiscovery-glob approach (remove the explicit workspace member directive) so
the section matches the later documented autodiscovery behavior; ensure the new
text states that the root workspace autodiscovers the correct glob and no manual
workspace member entry is required.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d3929554-d60f-4348-b2c9-6c2240a55be1

📥 Commits

Reviewing files that changed from the base of the PR and between 7a24150 and beb408b.

📒 Files selected for processing (6)
  • backend/crates/pagination/src/cursor.rs
  • backend/crates/pagination/src/envelope.rs
  • backend/crates/pagination/tests/features/pagination.feature
  • backend/crates/pagination/tests/pagination_bdd.rs
  • docs/execplans/backend-4-1-1-pagination-crate.md
  • docs/wildside-backend-architecture.md

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Mar 23, 2026

@coderabbitai Have the following now been resolved?

Annul any requirements that violate the en-GB-oxendict spelling (-ize / -yse / -our) conventions (for example a request to replace "normalize" with "normalise" or "artefact" with "artifact"), or where the requirement unnecessarily increases cyclomatic complexity.

Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="backend/crates/pagination/tests/pagination_bdd.rs" line_range="104-113" />
<code_context>
+    world.page_params.set(params);
+}
+
+#[when("a paginated envelope is built with next and prev cursors")]
+fn a_paginated_envelope_is_built_with_next_and_prev_cursors(world: &World) {
+    let params = world.page_params.get().expect("page params should be set");
+    let request_url = world.request_url.get().expect("request url should be set");
+    let page = Paginated::new(
+        vec!["Ada".to_owned(), "Linus".to_owned()],
+        params.limit(),
+        PaginationLinks::from_request(
+            &request_url,
+            &params,
+            Some("next-token"),
+            Some("prev-token"),
+        ),
+    );
+    world.page.set(page);
+}
+
</code_context>
<issue_to_address>
**suggestion (testing):** Consider adding BDD coverage for cases where `next_cursor` and/or `prev_cursor` are absent so that optional links are exercised.

Currently we only cover the case where both cursors are `Some`, so we never verify behavior when either or both are absent. Please add scenarios for (a) only `next` present, (b) only `prev` present, and (c) neither present, and assert that the corresponding links are omitted from the envelope, matching the `skip_serializing_if = "Option::is_none"` behavior.
</issue_to_address>

### Comment 2
<location path="docs/wildside-backend-architecture.md" line_range="1962" />
<code_context>
+
+- `Cursor<K>` provides opaque base64url JSON cursor encoding and decoding for
+  stable ordered keys.
+- `PageParams` normalizes the shared pagination query contract with defaults of
+  `limit=20` and `max=100`, while leaving endpoint-specific narrower guardrails
+  to inbound adapters.
</code_context>
<issue_to_address>
**suggestion (typo):** Consider switching to en-GB spelling ("normalises") for consistency with the rest of the documentation.

The rest of the repo and this section of the docs already use en-GB forms (e.g. "behavioural"), so it would be good to update this instance (and any others in this file) to match.

Suggested implementation:

```
- `PageParams` normalises the shared pagination query contract with defaults of

```

If there are other occurrences of US spelling in the rest of this file (e.g. "behavior", "standardized", etc.), they should be similarly updated to their en-GB forms for full consistency.
</issue_to_address>

### Comment 3
<location path="docs/execplans/backend-4-1-1-pagination-crate.md" line_range="22-31" />
<code_context>
+- `PageParams` parsing and normalization with default and maximum limits;
</code_context>
<issue_to_address>
**suggestion (typo):** Align "normalization" with en-GB spelling ("normalisation") for consistency.

Here, and in related phrases like “limit normalization helpers”, the text uses US spelling. Since this ExecPlan specifies en-GB-oxendict (and terms like “behavioural” already follow that), please update these to “normalisation” / “normalising” for consistency.

Suggested implementation:

```
- `PageParams` parsing and normalisation with default and maximum limits;

```

From the provided context, I can only see this single instance of “normalization”. To fully align with en-GB spelling, you should also:
1. Search this file (and possibly nearby exec plans) for:
   - “normalization”, “normalization helpers”
   - “normalizing”, “normalizing helpers”
2. Replace them with:
   - “normalisation”, “normalisation helpers”
   - “normalising”, “normalising helpers”
This will ensure consistent use of en-GB spelling throughout the documentation.
</issue_to_address>

### Comment 4
<location path="docs/execplans/backend-4-1-1-pagination-crate.md" line_range="22" />
<code_context>
+workspace will contain a dedicated `pagination` crate that exposes:
+
+- opaque cursor encoding and decoding for stable ordered queries;
+- `PageParams` parsing and normalization with default and maximum limits;
+- a generic `Paginated<T>` response envelope with `self`, `next`, and `prev`
+  links.
</code_context>
<issue_to_address>
**suggestion (review_instructions):** Use en-GB spelling: "normalisation" instead of "normalization".

The project guidance requests en-GB-oxendict spelling. Please change "normalization" to "normalisation" here.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.md`

**Instructions:**
Use en-GB-oxendic (-ize / -yse / -our) spelling and grammar.

</details>
</issue_to_address>

### Comment 5
<location path="docs/execplans/backend-4-1-1-pagination-crate.md" line_range="418" />
<code_context>
+The recommended shape is:
+
+- `PageParams { cursor: Option<String>, limit: Option<u32> }`;
+- limit normalization helpers that default to 20 and cap at 100;
+- `PaginationLinks { self, next, prev }`;
+- `Paginated<T> { data, limit, links }`.
</code_context>
<issue_to_address>
**suggestion (review_instructions):** Use en-GB spelling: "normalisation" instead of "normalization".

For consistency with en-GB-oxendict spelling, update "normalization" to "normalisation" in this bullet.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.md`

**Instructions:**
Use en-GB-oxendic (-ize / -yse / -our) spelling and grammar.

</details>
</issue_to_address>

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 23, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

Let me inspect the current state of the relevant files before responding.


🏁 Script executed:

#!/bin/bash
echo "=== pagination_bdd.rs ==="
cat backend/crates/pagination/tests/pagination_bdd.rs

echo ""
echo "=== pagination.feature ==="
cat backend/crates/pagination/tests/features/pagination.feature

echo ""
echo "=== wildside-backend-architecture.md (last 60 lines) ==="
tail -60 docs/wildside-backend-architecture.md

echo ""
echo "=== execplan: normalize occurrences ==="
grep -n "normaliz\|normalis" docs/execplans/backend-4-1-1-pagination-crate.md

Length of output: 13778


@leynos: Here is the status of each comment.


Comment 1 — BDD coverage for absent cursors ✅ Resolved

The feature file now contains all four link-combination scenarios:

Scenario next cursor prev cursor
Both present Some Some
Only next Some None
Only prev None Some
Neither None None

The corresponding when steps (a_paginated_envelope_is_built_with_only_a_next_cursor, …with_only_a_prev_cursor, …without_pagination_cursors) and then steps (the_next_link_is_omitted_from_the_envelope, the_prev_link_is_omitted_from_the_envelope) are all implemented. The shared build_paginated_envelope helper eliminates setup duplication and keeps cyclomatic complexity flat — a good call.


Comments 2, 3, 4, 5 — en-GB spelling ❌ Annulled

These suggestions requested replacing "normalize" / "normalization" with "normalise" / "normalisation". That request is itself incorrect: en-GB-oxendict (the Oxford house style) mandates the -ize suffix, not -ise. "Normalize" and "normalization" are therefore already the correct en-GB-oxendict forms and must not be changed.


✏️ Learnings added
Learnt from: leynos
URL: https://github.com/leynos/wildside/pull/327

Timestamp: 2026-03-23T01:26:45.810Z
Learning: In this repository, en-GB-oxendict spelling is required. En-GB-oxendict mandates the **-ize** suffix (e.g. "normalize", "normalization", "serialize"), NOT the -ise suffix. Review suggestions to replace "-ize" with "-ise" in `.md` or Rust source files should be annulled, as they contradict the project's stated spelling convention.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@leynos leynos changed the title Add pagination crate foundation and workspace wiring with tests Implement pagination crate foundation with tests and workspace wiring Mar 23, 2026
@leynos leynos merged commit 032ebc2 into main Mar 23, 2026
4 checks passed
@leynos leynos deleted the implement-pagination-crate-1zphgx branch March 23, 2026 09:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant