Skip to content

Move live device resolve to direct_control; drop config-service's outbound call - #112

Merged
Anthony Sligar (sligara7) merged 7 commits into
NSLS2:mainfrom
sligara7:refactor/resolve-in-direct-control
Aug 3, 2026
Merged

Move live device resolve to direct_control; drop config-service's outbound call#112
Anthony Sligar (sligara7) merged 7 commits into
NSLS2:mainfrom
sligara7:refactor/resolve-in-direct-control

Conversation

@sligara7

Copy link
Copy Markdown
Collaborator

Each backend service must run standalone as well as composed. The resolver enrichment callback was the one place a dependency arrow reversed: configuration_service called direct_control's /devices/enrich, while everything else calls configuration_service — the only service-to-service cycle in the system.

  • direct_control: new POST /api/v1/devices/resolve resolves dotted device addresses to PV names by live introspection, via its registry provider (config-service in http mode, local file in standalone mode) and the existing ophyd-cache walk. Handles the ophyd FormattedComponent runtime placeholders static resolution cannot. Replaces /devices/enrich (its only consumer was config-service).
  • configuration_service: /devices/resolve is now static-only; needs_enrichment is a terminal outcome pointing at direct_control's resolve. The direct-control client, CONFIG_DIRECT_CONTROL_URL, the enrichment cache, and the enrichment_unavailable outcome are removed. The service makes no outbound HTTP calls at all.
  • OpenAPI schemas regenerated (config: enum-only change; direct_control: enrich out, resolve in). Orientation docs updated — direct_control is now the client on every inter-service path.
  • Test-infra: the caproto test IOC port is overridable via DIRECT_CONTROL_TEST_IOC_PORT (a foreign IOC on 5064 otherwise poisons the suite).
  • Dev lockfiles bumped to ophyd-async 0.21.0; both suites pass against it.

No deployed environment sets CONFIG_DIRECT_CONTROL_URL, and nothing calls the old enriched resolve, so no consumer breaks.

Tests: configuration_service 438 passed; direct_control 343 passed (incl. 13 new resolve-endpoint tests with a live FormattedComponent case); ruff clean on both.

A foreign IOC bound to 5064 (another project's simulator) was silently
reused as the test IOC, hanging every EPICS test on connect timeouts.
DIRECT_CONTROL_TEST_IOC_PORT now picks a free port for both the caproto
server and the CA clients.
…tbound HTTP

Each backend service must run standalone as well as composed, and the
resolver enrichment callback was the one place a dependency arrow
reversed: configuration_service called direct_control's /devices/enrich,
while direct_control (and everything else) calls configuration_service -
the only service-to-service cycle in the system.

- direct_control: new POST /api/v1/devices/resolve resolves dotted
  device addresses to PV names by live introspection, using its registry
  provider (config-service in http mode, local file in standalone mode)
  and the existing ophyd-cache walk. Handles the ophyd FormattedComponent
  runtime placeholders static resolution cannot. Replaces the
  config-facing /devices/enrich endpoint (its only consumer).
- configuration_service: /devices/resolve is now static-only;
  needs_enrichment is a terminal outcome pointing at direct_control's
  resolve. The direct-control client, CONFIG_DIRECT_CONTROL_URL, the
  enrichment cache, and the enrichment_unavailable outcome are removed.
  The service makes no outbound HTTP calls at all.
- OpenAPI schemas regenerated; docs and orientation pages updated.
Both suites pass against 0.21.0 (config 438, direct-control 343);
pyproject constraints were already open so CI resolves latest.
The guard pinned config-service's enrich client models against
direct_control's published schema. With the enrich client removed and
live resolve moved to direct_control, there is no cross-service enrich
wire left to pin; the resolve endpoint's request/response shapes are
covered by direct_control's own tests and the OpenAPI drift gate.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR removes the last configuration_service → direct_control dependency cycle by moving the “live” (runtime-placeholder-capable) device address resolution into direct_control as POST /api/v1/devices/resolve, while making configuration_service’s /devices/resolve explicitly static-only and terminal on needs_enrichment.

Changes:

  • Added direct_control POST /api/v1/devices/resolve that resolves dotted device addresses to PV names via live device instantiation + ophyd-cache walk, using the configured registry provider for specs/PV maps.
  • Removed the old direct_control /devices/enrich endpoint and eliminated configuration_service’s outbound direct-control client, cache, and enrichment_unavailable outcome.
  • Updated OpenAPI schemas and orientation/integration docs to reflect the new split (static resolve in configuration_service; live resolve in direct_control) and the “configuration never calls out” convention; updated direct_control test IOC port configurability.

Reviewed changes

Copilot reviewed 24 out of 27 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
shared-schema/direct_control.openapi.json Replaces /devices/enrich with /devices/resolve and adds new resolve request/response/outcome schemas.
shared-schema/configuration_service.openapi.json Updates /devices/resolve description and removes enrichment_unavailable from outcome enum.
docs/orientation/side-a.html Updates the architecture diagram and narrative to reflect direct_control as client on all paths and adds live resolve description.
docs/orientation/direct-control-service.html Updates endpoint list to document POST /api/v1/devices/resolve instead of enrich.
docs/orientation/configuration-service.html Updates key endpoints section to describe static-only resolve and the handoff to direct_control for live resolution.
docs/frontend-backend-integration.html Updates outcome semantics for needs_enrichment and removes enrichment_unavailable guidance.
backend/direct_control_service/tests/test_resolve_endpoint.py Adds new integration tests for the new live resolve endpoint, including a FormattedComponent placeholder case.
backend/direct_control_service/tests/test_enrich_endpoint.py Removes tests for the deleted enrich endpoint.
backend/direct_control_service/tests/test_enrich_config_integration.py Removes integration tests for configuration_service’s removed direct-control enrich client.
backend/direct_control_service/tests/conftest.py Makes the caproto test IOC port overridable and updates registry stub shape for new resolve needs.
backend/direct_control_service/src/direct_control/models.py Introduces resolve request/response/outcome models and makes ok a computed field derived from outcome.
backend/direct_control_service/src/direct_control/main.py Implements POST /api/v1/devices/resolve and removes the old enrich route.
backend/direct_control_service/README.md Documents the new resolve endpoint.
backend/direct_control_service/.gitignore Stops ignoring uv.lock in this service directory.
backend/configuration_service/uv.lock Bumps ophyd-async to 0.21.0.
backend/configuration_service/tests/test_resolver_loader_store_fixes.py Removes tests covering now-deleted direct-control client failure behavior.
backend/configuration_service/tests/test_enrichment_fallback.py Removes resolver enrichment fallback tests since config no longer calls direct_control.
backend/configuration_service/tests/test_direct_control_client.py Removes direct-control client unit tests since the client is deleted.
backend/configuration_service/tests/test_classes.py Removes test-only ophyd classes used solely by enrichment fallback tests.
backend/configuration_service/tests/init.py Updates package rationale docstring after removing the dedicated test_classes module.
backend/configuration_service/src/configuration_service/path_resolver.py Removes ENRICHMENT_UNAVAILABLE and redefines NEEDS_ENRICHMENT as terminal in config-service.
backend/configuration_service/src/configuration_service/main.py Removes direct-control client wiring + enrichment pass; resolve is now single-pass static-only.
backend/configuration_service/src/configuration_service/direct_control_client.py Deletes the direct-control enrichment HTTP client.
backend/configuration_service/src/configuration_service/config.py Removes settings for CONFIG_DIRECT_CONTROL_URL/timeouts used by the deleted client.
backend/configuration_service/docs/reference/api.md Updates API reference to describe needs_enrichment as terminal and point to direct_control resolve.
.gitignore Adds .reflow2/ to ignores.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/direct_control_service/src/direct_control/main.py
@sligara7
Anthony Sligar (sligara7) force-pushed the refactor/resolve-in-direct-control branch from 7f03873 to 1a2c861 Compare August 3, 2026 15:25
…ups per device

A batch addressing the same device repeatedly now hits the registry once
per lookup kind instead of once per address (get_device_pvs has no
client-side cache, so a 200-address single-device batch could cost up to
400 sequential HTTP calls). Failed lookups are memoized too. Regression
test asserts the call counts.
@sligara7
Anthony Sligar (sligara7) merged commit 745f619 into NSLS2:main Aug 3, 2026
24 of 25 checks passed
@sligara7
Anthony Sligar (sligara7) deleted the refactor/resolve-in-direct-control branch August 3, 2026 16:17
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.

2 participants