Skip to content

feat(plugin): add EntityUpdateClientProtocol and parent-aware get - #1064

Open
SandyChapman wants to merge 1 commit into
mainfrom
entity-store-client-protocol/schapman
Open

feat(plugin): add EntityUpdateClientProtocol and parent-aware get#1064
SandyChapman wants to merge 1 commit into
mainfrom
entity-store-client-protocol/schapman

Conversation

@SandyChapman

@SandyChapman SandyChapman commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Problem

The entity-client protocols in nemo_platform_plugin describe a narrower surface than EntityClient actually has, so a plugin needing more must declare a private protocol or type against the concrete class. Two capabilities are missing:

  • update — the read-modify-write against the db_version optimistic lock.
  • get(..., parent=...) — needed to address a child entity, which is unique within (workspace, entity_type, parent, name) rather than by name alone.

Approach

update becomes its own protocol, composed with the existing ones rather than folded into them:

class Store(EntityClientProtocol[T], EntityUpdateClientProtocol[T], Protocol[T]):
    ...

Folding it into EntityClientProtocol would be wrong: protocols are structural, so a new member silently invalidates every existing implementer — including every test double — even for services that never call it. MetricService only creates and reads; it shouldn't be typed against update, and its fakes shouldn't grow methods to satisfy a type.

parent is added to EntityGetterProtocol.get rather than given its own protocol, because a second protocol declaring a conflicting get couldn't compose with the first. It's optional, so fetching a root entity is unchanged.

That does require the existing test doubles to accept it — 10 fakes, one argument each. That's a correctness fix rather than churn: each stands in for a client that already accepts parent, so their signature was simply inaccurate.

Drift guard

The tests include a static conformance assertion:

def _static_conformance(client: EntityClient) -> _ReadWriteStore[_Entity]:
    return client

ty fails here if the client and protocols ever diverge. Verified it actually catches drift — renaming update in the protocol produces:

info: type `EntityClient` is not assignable to protocol `_ReadWriteStore[_Entity]`
info: └── protocol member `update_renamed` is not defined on type `EntityClient`

_ReadWriteStore is itself the composition shown above, so the test doubles as proof that the pieces compose.

Verification

  • 1575 tests pass (nemo_platform_plugin + nemo-evaluator)
  • tools/lint/lint-all.sh — 13/13
  • ty across both packages: 130 diagnostics before, 128 after — two fewer, none new

Context

Prompted by review feedback on #1023, which had to declare a private EntityStoreProtocol to publish entity revisions. That PR will drop its private copy and compose these instead. No evaluator-specific behavior is included here — the only evaluator changes are the test-double signatures the protocol change requires.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Entity retrieval now supports optional parent context, enabling reliable access to nested or child entities.
    • Added a dedicated update capability with optimistic-locking support for clients that require entity updates.
    • Newly available entity client protocols are exposed through the public API.
  • Tests

    • Expanded protocol and compatibility coverage for parent-aware retrieval and update-capable clients.

@github-actions github-actions Bot added the feat label Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 30261/38277 79.1% 63.7%
Integration Tests 17902/36946 48.4% 20.9%

The entity-client protocols describe a narrower surface than `EntityClient`
actually has, so a plugin needing more either declares a private protocol or
types against the concrete class. Two capabilities are missing:

  - `update`, the read-modify-write against the `db_version` optimistic lock
  - `get(..., parent=...)`, needed to address a child entity, which is unique
    within (workspace, entity_type, parent, name) rather than by name alone

Add `EntityUpdateClientProtocol` as its own protocol rather than folding
`update` into `EntityClientProtocol`. Protocols are structural, so a new member
silently invalidates every existing implementer — including every test double —
even for services that never call it. Most services only create and read; they
keep their narrow surface, and a service needing both composes:

    class Store(EntityClientProtocol[T], EntityUpdateClientProtocol[T], Protocol[T]):
        ...

`parent` is added to `EntityGetterProtocol.get` instead, because a second
protocol declaring a conflicting `get` could not compose with the first. It is
optional, so fetching a root entity is unchanged. The existing test doubles gain
the argument: each stands in for a client that already accepts it, so their
signature was simply inaccurate.

A static conformance assertion in the tests fails type-checking if the client and
the protocols ever drift apart, which is the situation this change exists to fix.

Signed-off-by: Sandy Chapman <schapman@nvidia.com>
@SandyChapman
SandyChapman force-pushed the entity-store-client-protocol/schapman branch from 9e5b4c1 to 29eabaf Compare August 4, 2026 14:53
@SandyChapman SandyChapman changed the title feat(plugin): add EntityStoreClientProtocol for update and child access feat(plugin): add EntityUpdateClientProtocol and parent-aware get Aug 4, 2026
@SandyChapman
SandyChapman marked this pull request as ready for review August 4, 2026 14:59
@SandyChapman
SandyChapman requested review from a team as code owners August 4, 2026 14:59
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The entity client contracts now support optional parent context for retrieval and a separate optimistic-locking update protocol. Public exports, protocol tests, and evaluator test fakes were updated to match the revised interfaces.

Changes

Entity client contract alignment

Layer / File(s) Summary
Entity retrieval and update contracts
packages/nemo_platform_plugin/src/nemo_platform_plugin/entities/base.py
EntityGetterProtocol.get now accepts an optional parent. EntityUpdateClientProtocol defines asynchronous updates with optional original_name.
Public exports and protocol validation
packages/nemo_platform_plugin/src/nemo_platform_plugin/entities/__init__.py, packages/nemo_platform_plugin/src/nemo_platform_plugin/entity_client.py, packages/nemo_platform_plugin/tests/entities/test_client_protocols.py
The update protocol is publicly exported. Tests validate protocol composition, update separation, method parameters, and parent-aware retrieval.
Evaluator test client alignment
plugins/nemo-evaluator/tests/api/service/*, plugins/nemo-evaluator/tests/api/v2/*, plugins/nemo-evaluator/tests/test_*_refs.py
Test fakes accept the optional parent argument while retaining their existing lookup behavior.

Suggested reviewers: mikeknep

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. 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 identifies both primary changes: the new update protocol and parent-aware entity retrieval.
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.
✨ 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 entity-store-client-protocol/schapman

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/nemo_platform_plugin/tests/entities/test_client_protocols.py (1)

11-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove postponed annotation evaluation.

from __future__ import annotations makes annotations string-based. All referenced types have normal imports. Remove this import.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/nemo_platform_plugin/tests/entities/test_client_protocols.py` at
line 11, Remove the `from __future__ import annotations` import statement at the
top of the test_client_protocols.py file. Since all type annotations in the
module use properly imported types rather than requiring string-based deferred
evaluation, this future import is unnecessary and should be deleted.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/nemo_platform_plugin/tests/entities/test_client_protocols.py`:
- Line 11: Remove the `from __future__ import annotations` import statement at
the top of the test_client_protocols.py file. Since all type annotations in the
module use properly imported types rather than requiring string-based deferred
evaluation, this future import is unnecessary and should be deleted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 582832db-b1dd-4daf-8832-f8c52aa12bdd

📥 Commits

Reviewing files that changed from the base of the PR and between 2eb952f and 29eabaf.

📒 Files selected for processing (14)
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/entities/__init__.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/entities/base.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/entity_client.py
  • packages/nemo_platform_plugin/tests/entities/test_client_protocols.py
  • plugins/nemo-evaluator/tests/api/service/test_metric_service.py
  • plugins/nemo-evaluator/tests/api/service/test_result_service.py
  • plugins/nemo-evaluator/tests/api/service/test_task_service.py
  • plugins/nemo-evaluator/tests/api/service/test_taskset_service.py
  • plugins/nemo-evaluator/tests/api/v2/test_metrics_routes.py
  • plugins/nemo-evaluator/tests/api/v2/test_results_routes.py
  • plugins/nemo-evaluator/tests/api/v2/test_tasks_routes.py
  • plugins/nemo-evaluator/tests/api/v2/test_tasksets_routes.py
  • plugins/nemo-evaluator/tests/test_metric_refs.py
  • plugins/nemo-evaluator/tests/test_task_refs.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants