Skip to content

Add the public recommender scorecard read to the curation desk gateway - #96

Merged
feruzm merged 3 commits into
mainfrom
feature/curation-recommender-route
Sep 5, 2026
Merged

Add the public recommender scorecard read to the curation desk gateway#96
feruzm merged 3 commits into
mainfrom
feature/curation-recommender-route

Conversation

@feruzm

@feruzm feruzm commented Sep 5, 2026

Copy link
Copy Markdown
Member

Adds GET /private-api/curation-desk/recommender/{username}, a sixth public curation desk read, piping to the desk backend's recommender scorecard ({username, window_days, recommended, curated, dismissed, withdrawn, precision, trusted, computed_at}).

Why: the web client shows each recommender's 90-day scorecard and a trusted chip next to recommender names, which needs a public read of its own.

It goes through the same path as the other public reads: shared secret on the upstream call, byte memo with single flight and last-good fallback, public payload fence. The unconfigured 503 is decided before the route value is read, the username is validated against the Hive name grammar and escaped before the URL is built, and the route takes no query parameters, so one name is one memo entry. Policy public, max-age=0, s-maxage=60.

Test plan: dotnet build and dotnet test green (414 tests). New cases cover the path grammar, the 503 while unconfigured, the 400 before any upstream call, the piped call with its token header, Cache-Control on a 200 JSON body only, a memo hit offering the remaining window with no Age header, name-only cache keying and the payload fence on served and memoized bytes. Parity KNOWN_DIVERGENCES gained the new route.

Summary by CodeRabbit

  • New Features

    • Added a curation-desk recommender endpoint for retrieving username-based scorecards.
    • Added validation for Hive account names to reject malformed usernames.
    • Added Docker Compose configuration for enabling curation-desk routes and setting memo capacity.
  • Performance

    • Recommender responses are publicly cacheable for up to 60 seconds.

Sixth public curation desk read: GET /private-api/curation-desk/recommender/{username}
pipes to curation/desk/recommenders/<username> through the same path as the other
public reads, so the shared secret, the byte memo, the single flight and the public
payload fence all apply to it unchanged.

The unconfigured 503 is decided before the route value is read, so a dark desk
answers every route alike instead of reporting on this one's name grammar. The name
is validated against the Hive name grammar and escaped before the URL is built; the
route takes no query parameters, so one name is one memo entry and one shared-cache
key. Its own policy holds a scorecard for a minute: the numbers behind it are a
rolling 90-day count recomputed in the background.
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: efdcd9c0-e410-40c5-946c-ecdec20ea56f

📥 Commits

Reviewing files that changed from the base of the PR and between 06530cb and b424406.

📒 Files selected for processing (12)
  • dotnet/EcencyApi.Tests/CachePolicyTests.cs
  • dotnet/EcencyApi.Tests/CurationDeskAuthTests.cs
  • dotnet/EcencyApi.Tests/CurationDeskPayloadTests.cs
  • dotnet/EcencyApi.Tests/CurationDeskPublicPayloadTests.cs
  • dotnet/EcencyApi.Tests/CurationDeskTestSupport.cs
  • dotnet/EcencyApi.Tests/HiveNamesTests.cs
  • dotnet/EcencyApi/Handlers/PrivateApi.CurationDesk.cs
  • dotnet/EcencyApi/Handlers/Routes.cs
  • dotnet/EcencyApi/Infrastructure/CachePolicy.cs
  • dotnet/EcencyApi/Infrastructure/HiveNames.cs
  • dotnet/docker-compose.yml
  • dotnet/parity/driver.py

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Changes

Curation desk recommender

Layer / File(s) Summary
Account validation and path construction
dotnet/EcencyApi/Infrastructure/HiveNames.cs, dotnet/EcencyApi/Handlers/PrivateApi.CurationDesk.cs, dotnet/EcencyApi.Tests/*
Adds Hive account-name validation and uses it to construct safe recommender paths.
Route handling and cache policy
dotnet/EcencyApi/Handlers/*, dotnet/EcencyApi/Infrastructure/CachePolicy.cs, dotnet/docker-compose.yml, dotnet/parity/driver.py
Adds the recommender route, desk configuration, a 60-second shared-cache policy, and parity metadata.
Route behavior and regression coverage
dotnet/EcencyApi.Tests/*
Tests authentication, caching, memo keys, payload filtering, malformed names, and public-read enumeration.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to b4244

This adds a validated, cached recommender scorecard endpoint with authenticated upstream access and filtered public responses. Current coverage supports the intended behavior, with no concrete merge-blocking risk identified.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant CurationDeskRecommender
  participant CurationDeskRecommenderPath
  participant ServeDeskRead
  participant Upstream
  Client->>CurationDeskRecommender: GET /private-api/curation-desk/recommender/{username}
  CurationDeskRecommender->>CurationDeskRecommenderPath: Validate username and build upstream path
  CurationDeskRecommender->>ServeDeskRead: Serve read with 60-second cache policy
  ServeDeskRead->>Upstream: Request scorecard with desk token
  Upstream-->>ServeDeskRead: Return scorecard JSON
Loading

Poem

A rabbit checks each name with care
Then sends scorecards through the air
A minute’s cache keeps hops in line
Safe fields stay, secrets decline
The recommender route now shines fine

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.59% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 29 functions across 11 files. (1 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding a public recommender scorecard read to the curation desk gateway.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 27.59% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 29 functions across 11 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/curation-recommender-route

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Expose recommender scorecards through the curation desk gateway

✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Adds a public gateway read for per-recommender 90-day scorecards.
• Reuses authenticated upstream piping, payload stripping, memoization, and shared-cache controls.
• Validates Hive usernames and expands route, parity, cache, and security coverage.
Diagram

sequenceDiagram
    actor Client
    participant Routes as Route Map
    participant Handler as Recommender Handler
    participant Pipeline as Read Pipeline
    participant Memo as Byte Memo
    participant Backend as Desk Backend
    Client->>Routes: GET scorecard
    Routes->>Handler: Route username
    Handler->>Handler: Check token then name
    Handler->>Pipeline: Validated endpoint
    Pipeline->>Memo: Find fresh bytes
    alt Fresh memo
        Memo-->>Pipeline: Public scorecard
    else Cache miss
        Pipeline->>Backend: Tokenized GET
        Backend-->>Pipeline: Scorecard JSON
        Pipeline->>Pipeline: Strip private fields
        Pipeline->>Memo: Store 60 seconds
    end
    Pipeline-->>Client: JSON and cache policy
Loading
High-Level Assessment

Reusing the established curation desk read pipeline is the preferred approach because it preserves shared-secret authentication, single-flight memoization, last-good fallback, payload filtering, and cache semantics without duplicating security-sensitive behavior. A dedicated proxy pipeline or generic unrestricted passthrough would add duplication or expose unnecessary upstream surface.

Files changed (9) +234 / -5

Enhancement (2) +38 / -2
PrivateApi.CurationDesk.csImplement the recommender scorecard gateway handler +37/-2

Implement the recommender scorecard gateway handler

• Adds the recommender read handler with configuration-first failure behavior and Hive username validation. Builds an escaped upstream path and delegates serving to the existing secured, memoized, payload-filtered read pipeline.

dotnet/EcencyApi/Handlers/PrivateApi.CurationDesk.cs

Routes.csMap the recommender scorecard route +1/-0

Map the recommender scorecard route

• Registers GET '/private-api/curation-desk/recommender/{username}' with the new handler.

dotnet/EcencyApi/Handlers/Routes.cs

Tests (6) +192 / -2
CachePolicyTests.csCover the recommender cache policy +2/-0

Cover the recommender cache policy

• Adds the recommender policy to general cache-policy tests and verifies its 60-second shared maximum age.

dotnet/EcencyApi.Tests/CachePolicyTests.cs

CurationDeskAuthTests.csExercise recommender gateway security and caching +141/-0

Exercise recommender gateway security and caching

• Adds end-to-end handler tests for authenticated upstream forwarding, 60-second memo behavior, query-independent keying, validation ordering, unconfigured responses, and payload filtering. It also verifies that memoized bytes remain sanitized.

dotnet/EcencyApi.Tests/CurationDeskAuthTests.cs

CurationDeskPayloadTests.csValidate recommender path construction +43/-0

Validate recommender path construction

• Tests valid Hive names, escaping boundaries, dot segments, structural URL characters, grammar violations, and username length limits for the upstream path helper.

dotnet/EcencyApi.Tests/CurationDeskPayloadTests.cs

CurationDeskPublicPayloadTests.csMake public-read assertions route-count aware +1/-1

Make public-read assertions route-count aware

• Replaces the fixed upstream call count with the registered public-read count so payload-fence coverage automatically includes the new route.

dotnet/EcencyApi.Tests/CurationDeskPublicPayloadTests.cs

CurationDeskTestSupport.csRegister the recommender public read in shared tests +3/-0

Register the recommender public read in shared tests

• Adds the recommender handler, request factory, and cache policy to the common public-read test enumeration.

dotnet/EcencyApi.Tests/CurationDeskTestSupport.cs

driver.pyRecord the recommender route as a known parity divergence +2/-1

Record the recommender route as a known parity divergence

• Adds the new GET route to generated curation desk divergence cases and updates the documented public-read count.

dotnet/parity/driver.py

Other (1) +4 / -1
CachePolicy.csDefine a one-minute recommender cache policy +4/-1

Define a one-minute recommender cache policy

• Introduces 'public, max-age=0, s-maxage=60' for rolling recommender scorecards and documents why a minute-long shared window is appropriate.

dotnet/EcencyApi/Infrastructure/CachePolicy.cs

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Sep 5, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Incomplete Hive name validation ✓ Resolved 🐞 Bug ≡ Correctness
Description
CurationDeskRecommenderPath accepts malformed Hive names such as -ab, abc-, a..b, or ...
because it checks only length and allowed characters. These requests are forwarded upstream instead
of receiving the route's intended 400 response, producing inconsistent validation and unnecessary
authenticated backend calls.
Code

dotnet/EcencyApi/Handlers/PrivateApi.CurationDesk.cs[R183-185]

+    public static string? CurationDeskRecommenderPath(string username) =>
+        username is "." or ".." || !DeskAuthorPattern.IsMatch(username)
+            ? null
Evidence
The shared regex permits any arrangement of lowercase letters, digits, dots, and hyphens, while the
newly added path function treats every matching value except exactly . and .. as valid and
builds an upstream endpoint. The route's tests explicitly expect names outside the Hive grammar to
produce no path, but do not cover malformed arrangements composed solely of allowed characters.

dotnet/EcencyApi/Handlers/PrivateApi.CurationDesk.cs[146-150]
dotnet/EcencyApi/Handlers/PrivateApi.CurationDesk.cs[174-186]
dotnet/EcencyApi.Tests/CurationDeskPayloadTests.cs[377-405]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The recommender route's username validation accepts strings that use allowed characters but violate Hive account-name structure, such as leading/trailing hyphens or dots and consecutive dots.
## Issue Context
`DeskAuthorPattern` only enforces `[a-z0-9.-]{3,16}`. The new route relies on this pattern to decide whether to return 400 or make an authenticated upstream request.
## Fix Focus Areas
- dotnet/EcencyApi/Handlers/PrivateApi.CurationDesk.cs[146-150]
- dotnet/EcencyApi/Handlers/PrivateApi.CurationDesk.cs[183-186]
- dotnet/EcencyApi.Tests/CurationDeskPayloadTests.cs[377-405]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can keep summaries lean with Finding overflow, which tucks the rest behind 'View more'

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread dotnet/EcencyApi/Handlers/PrivateApi.CurationDesk.cs
A `[a-z0-9.-]{3,16}` class accepts `-ab`, `abc-`, `a..b`, `ab.cdef` and
`...`, none of which is a Hive account name, so those requests went
upstream with the desk token instead of answering 400. `HiveNames`
walks the name the way the chain's `is_valid_account_name` does: labels
of three or more characters, each starting with a letter and ending with
a letter or digit; a trailing dot is rejected as well. Both desk path
helpers use it. Regression cases cover each rejected shape and the
valid edges (`a-b`, multi-label names).
… file

The service only receives the variables the stack file names, so a
`docker stack deploy` from this file left the desk routes at 503 whatever
the host exported. Names only; values stay on the host.
@feruzm
feruzm merged commit c226cd6 into main Sep 5, 2026
4 checks passed
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