From 48a3e91f3ceba633fe835a5174186965a91059de Mon Sep 17 00:00:00 2001 From: yaniswav Date: Thu, 21 May 2026 17:48:08 +0200 Subject: [PATCH] chore: post-v0.5.0 cruft sweep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Eight stale-doc and minor-dedup items found in a second-pass review post-v0.5.0. Zero behavior change, zero schema change, 399 tests still green, ruff clean. .env.example (item 1): - TOPICFORGE_DDS_BACKEND backend list and `auto` chain were stuck at v0.3.0 (Fast > Cyclone > Mock). Refreshed to the v0.4.0 Phase 1.5 8-vendor list and the rti > opensplice > coredx > intercom (Pro) > opendds > fast > cyclone > dust > mock auto chain. MAX_SAMPLE_COUNT consolidation (items 2-3): - Two adapters had silently re-declared `_MAX_SAMPLE_COUNT = 50` locally (`ros2_mock/adapter.py`, `bag_service.py`) — exactly the smell audit item A4 set out to prevent. - A4's home `services/constants.py` was layer-misplaced — importing it from `adapters/` triggered a circular load through `services/__init__.py`. Moved the canonical home up to `topicforge/constants.py` (root-level, layer-neutral). - Dropped the `MAX_SAMPLE_COUNT` re-export from `services/inspector.py` `__all__` ; updated `tests/test_health.py` to import from the new canonical location (the only remaining caller — the "v0.1.x backcompat" comment was misleading). DDS adapter docstring + comment refresh (items 4-6): - `dds_cyclone/adapter.py` module docstring still listed a "v0.3.0 scope" with `peek_dds_samples` raising on user topics. Phase 1.5 shipped the XTypes pipeline ; refreshed to the v0.4.0+ scope listing full participant_events, topic_metrics, and the user-topic `_decode_status` annotation behavior. - `dds_cyclone/adapter.py:100` `_BUILTIN_DCPS_TOPICS` comment likewise refreshed. - `dds_fast/adapter.py` module docstring mirrored — Fast's user-topic path returns raw-annotated samples (not raises), `participant_events` is listener-callback-native, `topic_metrics` is opportunistic. Doc references retired (items 7-8): - `DDS_QUICKSTART.md §3`: extended-QoS coverage tagged "v0.3.x patches" → "v0.5.x patches" (consistent with §6 already updated). - `mcp-02-spec.md`: four "v0.3.x patch/candidate" callouts revisited. Two pointed at things already shipped (FastDdsAdapter v0.3.0, IDL/ XTypes Cyclone v0.4.0 Phase 1.5) — restated as past tense with pointers. One QoS callout retagged v0.5.x. Section heading "resolve before v0.3.x patches" → "v0.5.x patches". Out of scope : - The four remaining "v0.3.x roadmap" strings in `MIGRATION_v0.3_to_v0.4.md`, `audit-post-v0.4.0.md`, and `archive/launch-posts-v0.3.0/` are legitimate historical references (quoting the v0.3.0 error message, audit narrative, frozen marketing post) — kept as-is. --- .env.example | 23 +++++-- docs/DDS_QUICKSTART.md | 2 +- docs/projet-file/mcp-02-spec.md | 46 ++++++++------ .../adapters/dds_cyclone/adapter.py | 63 +++++++++++-------- src/topicforge/adapters/dds_fast/adapter.py | 34 +++++++--- src/topicforge/adapters/ros2_mock/adapter.py | 7 +-- src/topicforge/constants.py | 28 +++++++++ src/topicforge/services/bag_service.py | 5 +- src/topicforge/services/constants.py | 21 ------- src/topicforge/services/health.py | 2 +- src/topicforge/services/inspector.py | 8 +-- tests/test_health.py | 2 +- 12 files changed, 145 insertions(+), 96 deletions(-) create mode 100644 src/topicforge/constants.py delete mode 100644 src/topicforge/services/constants.py diff --git a/.env.example b/.env.example index adc08c0..d999d37 100644 --- a/.env.example +++ b/.env.example @@ -20,12 +20,23 @@ TOPICFORGE_ROS2_BIN=ros2 # bag paths, no environment data — see the README "Telemetry" section. TOPICFORGE_TELEMETRY=off -# DDS module backend: mock | cyclone | fast | rti | auto (default: mock). -# mock — deterministic fixtures, no SDK required (default) -# cyclone — Eclipse CycloneDDS, requires `pip install topicforge[dds-cyclone]` -# fast — eProsima Fast DDS, requires `pip install topicforge[dds-fast]` -# rti — RTI Connext (Pro tier, v0.4.0+ roadmap, BYO license) -# auto — pick best available: Fast > Cyclone > Mock +# DDS module backend: one of mock | cyclone | fast | rti | opensplice | +# coredx | intercom | opendds | dust | auto (default: mock). +# v0.4.0 Phase 1.5 widened the Literal — Pro tier vendors (rti, opensplice, +# coredx, intercom) are probed against the topicforge_pro package, not the +# upstream SDK ; the OSS core never imports a commercial binding directly. +# +# mock — deterministic fixtures, no SDK required (default) +# cyclone — Eclipse CycloneDDS, requires `pip install topicforge[dds-cyclone]` +# fast — eProsima Fast DDS, requires `pip install topicforge[dds-fast]` +# opendds — OpenDDS (OSS, awaiting upstream Python binding ; `topicforge[dds-opendds]`) +# dust — Dust DDS Rust (OSS, awaiting Python binding ; `topicforge[dds-dust]`) +# rti — RTI Connext (Pro tier, BYO license) +# opensplice — Vortex OpenSplice legacy (Pro tier, BYO license) +# coredx — Twin Oaks CoreDX (Pro tier, BYO license) +# intercom — TechSoft InterCOM (Pro tier, BYO license) +# auto — auto-detect chain: rti > opensplice > coredx > intercom (Pro +# tier, if installed) > opendds > fast > cyclone > dust > mock # Both Cyclone and Fast are OMG DDS-RTPS conformant ; they observe every # vendor on the bus — see docs/dds-interop-matrix.md for the canonical # multi-vendor positioning and the OMG May 2025 interop reference. diff --git a/docs/DDS_QUICKSTART.md b/docs/DDS_QUICKSTART.md index 466df51..399c6b7 100644 --- a/docs/DDS_QUICKSTART.md +++ b/docs/DDS_QUICKSTART.md @@ -95,7 +95,7 @@ Both real backends and the mock fixtures encode the canonical "subscriber doesn' An LLM reading this output has enough information to suggest a concrete fix ("the writer is BEST_EFFORT but the reader requires RELIABLE — either relax the reader or upgrade the writer"). That is the diagnostic loop the DDS module is designed to support — and it works identically regardless of which backend produced the discovery samples, because the vendor-neutral pure analyzer at `src/topicforge/adapters/common/qos_analyzer.py` operates on canonical `QosProfile` Pydantic models. -The analyzer covers the four MVP policies — **Reliability**, **Durability**, **History**, **Deadline** — that explain the bulk of real-world mismatch cases. Liveliness, Ownership, Partition, TimeBasedFilter, and LatencyBudget are v0.3.x patches. +The analyzer covers the four MVP policies — **Reliability**, **Durability**, **History**, **Deadline** — that explain the bulk of real-world mismatch cases. Liveliness, Ownership, Partition, TimeBasedFilter, and LatencyBudget are v0.5.x patches. --- diff --git a/docs/projet-file/mcp-02-spec.md b/docs/projet-file/mcp-02-spec.md index 1e48c23..6172b9b 100644 --- a/docs/projet-file/mcp-02-spec.md +++ b/docs/projet-file/mcp-02-spec.md @@ -332,11 +332,11 @@ ships in TopicForge releases, in this order: Soft-breaking schema changes to `TopicInfo` and `HealthReport` (additive optional fields). The 8-tool ceiling activates. - **TopicForge v0.3.0 — real Cyclone + real Fast DDS + multi-vendor - framing (current sprint).** Replace the v0.2.0 Cyclone stub with the + framing (shipped).** Replaced the v0.2.0 Cyclone stub with the actual CycloneDDS discovery (builtin DCPS readers for participants and endpoints, QoS pair extraction, typed reader for samples on - builtin DCPS topics first ; arbitrary user-topic peek defers to a - v0.3.x patch). Ship `FastDdsAdapter` as a parallel OSS backend on + builtin DCPS topics first ; arbitrary user-topic peek deferred to + v0.4.0 Phase 1.5 — see entry below). Shipped `FastDdsAdapter` as a parallel OSS backend on `fastdds` (listener-driven discovery via `DomainParticipantListener`, RLock-protected state, bounded discovery startup delay). Pyproject extras refactor: @@ -346,15 +346,25 @@ ships in TopicForge releases, in this order: statement and the OMG May 2025 interop reference. Same 3 MCP tools ; vendor-neutral wire contract. Soft-breaking `ParticipantInfo.vendor` Literal expansion to include `"fast"`. -- **TopicForge v0.3.x patches.** IDL/XTypes discovery to extend - `peek_dds_samples` to arbitrary user topics (today: builtin DCPS - topics only). Extended QoS coverage (Liveliness, Ownership, - Partition, TimeBasedFilter, LatencyBudget). Composite adapter - delegating per-tool category so users can run ROS2 + DDS surfaces - simultaneously. -- **TopicForge v0.4.0+ — `RtiConnextAdapter` in Pro tier.** Same - `_try_register_pro(mcp)` pattern. BYO RTI license. No new MCP - tools ; same 3 DDS tools, new backend. +- **TopicForge v0.4.0 — observability + composite + XTypes (shipped).** + IDL/XTypes discovery extended `peek_dds_samples` to arbitrary user + topics via best-effort Cyclone `cyclonedds.dynamic` decode (Phase 1.5) + with a `_decode_status` annotation (`"full"` / `"partial"` / `"raw"`). + `CompositeAdapter` (Phase 1) lifts the v0.3.0 single-adapter + limitation — ROS2 and DDS surfaces work simultaneously, routed by + tool category. 8-vendor auto-detect chain (Phase 1.5). Three new + observability tools shipped (`participant_events`, `topic_metrics`, + `peek_bag_samples`) — the v0.3.0 8-tool ceiling explicitly broken + three times, acknowledged inline. +- **TopicForge v0.5.x patches (planned).** Fast DDS `TypeObjectFactory` + binding completion to lift `_decode_status="raw"` → `"full"` on Fast + user topics. Extended QoS coverage (Liveliness, Ownership, Partition, + TimeBasedFilter, LatencyBudget). Real-bus validation of the v0.4.0 + Phase 1.5 Cyclone XTypes pipeline against multi-vendor publishers. +- **TopicForge v0.4.0+ Pro tier — real `RtiConnextAdapter`.** Same + `_try_register_pro(mcp)` pattern. BYO RTI license. The v0.4.0 Phase 1.5 + scaffold lives in `pro/` (gitignored) ; production binding pending + Pro tier launch. No new MCP tools ; same surface, new backend. The earlier v0.2.0 / v0.3.0 split (proposed before the mono-MCP pivot of 2026-05-14 and further refined by the multi-vendor framing when @@ -508,11 +518,11 @@ decisions. - **`cyclonedds` version pin.** Determined at v0.3.0 implementation kickoff after a smoke test against the current bindings on Windows + Linux. -- **QoS mismatch taxonomy.** Lock the policies checked at v0.3.0 - (recommended: Reliability, Durability, History, Deadline — the four - that explain > 80 % of real-world *"subscriber doesn't receive"* - cases). Defer Liveliness, Ownership, Partition, TimeBasedFilter, - LatencyBudget to v0.3.x patches. +- **QoS mismatch taxonomy.** Resolved at v0.3.0 : Reliability, + Durability, History, Deadline ship — the four that explain > 80 % + of real-world *"subscriber doesn't receive"* cases. Liveliness, + Ownership, Partition, TimeBasedFilter, LatencyBudget deferred to + v0.5.x patches (still pending as of v0.5.0). - **`peek_dds_samples` vs `sample_messages` description ergonomics.** Verify in a Claude session that the LLM picks the right tool when asked *"show me recent samples on topic /foo"* in a mixed ROS2+DDS @@ -544,7 +554,7 @@ Resolved by the multi-vendor framing of 2026-05-14 importable on their host ; the new ordering only matters when both SDKs are installed. -Newly open (resolve before v0.3.x patches) : +Newly open (resolve before v0.5.x patches) : - ~~**XTypes / IDL discovery feasibility.**~~ **Resolved in v0.4.0 Phase 1 (2026-05-14).** `peek_dds_samples` on user-defined topics no diff --git a/src/topicforge/adapters/dds_cyclone/adapter.py b/src/topicforge/adapters/dds_cyclone/adapter.py index 3edd302..8511dee 100644 --- a/src/topicforge/adapters/dds_cyclone/adapter.py +++ b/src/topicforge/adapters/dds_cyclone/adapter.py @@ -1,26 +1,37 @@ -"""Cyclone DDS adapter — real implementation (v0.3.0). - -Replaces the v0.2.0 stub with actual CycloneDDS discovery via the -`cyclonedds.builtin` builtin data readers. Joins the bus as a read-only -DDS-RTPS participant on the configured domain and observes every -conformant vendor on the wire — see `docs/dds-interop-matrix.md` for -the canonical multi-vendor positioning. - -The 3 DDS methods (`list_participants`, `detect_qos_mismatches`, -`peek_dds_samples`) call into the CycloneDDS Python bindings ; the 4 -ROS2 methods raise `AdapterError(DDS_ONLY_ERROR_MSG)` (this adapter is -DDS-only). The factory only loads this module when -`TOPICFORGE_DDS_BACKEND=cyclone` (or `auto` resolving to cyclone) — see -`services/factory.py`. - -v0.3.0 scope: - * `list_participants` — full DCPSParticipant discovery via builtin reader - * `detect_qos_mismatches` — DCPSSubscription + DCPSPublication paired - by topic, run through the vendor-neutral pure analyzer in - `adapters/common/qos_analyzer.py` - * `peek_dds_samples` — works on the 4 builtin DCPS topics. Arbitrary - user topics require IDL/XTypes discovery and raise an `AdapterError` - pointing at the v0.3.x roadmap. +"""Cyclone DDS adapter — real implementation (v0.3.0+). + +Joins the bus as a read-only DDS-RTPS participant on the configured +domain via the `cyclonedds.builtin` builtin data readers, and observes +every conformant vendor on the wire — see `docs/dds-interop-matrix.md` +for the canonical multi-vendor positioning. + +The DDS / observability methods call into the CycloneDDS Python +bindings ; the ROS2 graph methods raise `AdapterError(DDS_ONLY_ERROR_MSG)` +(this adapter is DDS-only — pair with `Ros2CliAdapter` via the +v0.4.0 `CompositeAdapter` to get both surfaces simultaneously). The +factory only loads this module when `TOPICFORGE_DDS_BACKEND=cyclone` +(or `auto` resolving to cyclone) — see `services/factory.py`. + +Current scope (v0.4.0+): + +* `list_participants` — DCPSParticipant discovery via builtin reader, + enriched with `LifecycleBuffer` reconciliation (first/last seen, + status, seen_count). +* `detect_qos_mismatches` — DCPSSubscription + DCPSPublication paired by + topic, run through the vendor-neutral pure analyzer in + `adapters/common/qos_analyzer.py`. +* `peek_dds_samples` — full-fidelity on the 4 builtin DCPS topics ; + arbitrary user topics go through `_peek_user_topic` with best-effort + `cyclonedds.dynamic` XTypes decode (Phase 1.5). Each sample carries a + `_decode_status` annotation (`"full"` / `"partial"` / `"raw"`) so the + LLM caller knows whether to trust the payload or fall back on + `_raw_bytes_hex`. +* `participant_events` — `discovered` / `lost` events from the + `LifecycleBuffer`. Caveat : Cyclone updates the buffer only on + `list_participants` poll calls (no native at-discovery callbacks). +* `topic_metrics` — opportunistic frequency / sequence-gap / latency + metrics buffered as `peek_dds_samples` surfaces samples (no native + at-sample-receive callback in cyclonedds 2.6.x Python). Sample-introspection helpers below are defensive against binding-version shape variations — they read attributes via `getattr` with fallbacks and @@ -96,8 +107,10 @@ _MAX_PARTICIPANTS = 256 _MAX_ENDPOINTS = 1024 -# Builtin DCPS topics that `peek_dds_samples` can serve in v0.3.0. -# Arbitrary user topics require IDL/XTypes discovery (v0.3.x roadmap). +# Builtin DCPS topics that `peek_dds_samples` serves with full-fidelity +# structured payloads. Arbitrary user topics route through +# `_peek_user_topic` (Phase 1.5 best-effort XTypes decode → annotated +# raw-bytes fallback). _BUILTIN_DCPS_TOPICS: dict[str, Any] = { "DCPSParticipant": BuiltinTopicDcpsParticipant, "DCPSSubscription": BuiltinTopicDcpsSubscription, diff --git a/src/topicforge/adapters/dds_fast/adapter.py b/src/topicforge/adapters/dds_fast/adapter.py index 1213a58..b6e9777 100644 --- a/src/topicforge/adapters/dds_fast/adapter.py +++ b/src/topicforge/adapters/dds_fast/adapter.py @@ -1,21 +1,35 @@ -"""eProsima Fast DDS adapter — listener-driven discovery (v0.3.0). +"""eProsima Fast DDS adapter — listener-driven discovery (v0.3.0+). Joins the bus as a read-only DDS-RTPS participant via the eProsima Fast DDS Python bindings. A duck-typed listener accumulates discovery state under an RLock so the public API methods read consistent -snapshots without racing the discovery callbacks. +snapshots without racing the discovery callbacks. Lifecycle events +(`discovered` / `lost`) are captured natively in +`on_participant_discovery` — no polling reconciliation needed, +contrasted with the Cyclone polling path. See `docs/dds-interop-matrix.md` for the canonical multi-vendor positioning. The factory only loads this module when `TOPICFORGE_DDS_BACKEND=fast` (or `auto` resolving to fast) — see -`services/factory.py`. - -v0.3.0 scope mirrors `CycloneDdsAdapter`: - * `list_participants` — snapshot of discovered participants - * `detect_qos_mismatches` — paired subs/pubs by topic + pure analyzer - * `peek_dds_samples` — builtin discovery snapshots (DCPSParticipant / - DCPSSubscription / DCPSPublication). Arbitrary user topics raise - `AdapterError` pointing at the v0.3.x XTypes/IDL roadmap. +`services/factory.py`. Pair with `Ros2CliAdapter` via the v0.4.0 +`CompositeAdapter` to serve both ROS2 and DDS surfaces simultaneously. + +Current scope (v0.4.0+) mirrors `CycloneDdsAdapter` : + + * `list_participants` — snapshot of discovered participants enriched + with `LifecycleBuffer` fields (first/last seen, status, seen_count). + * `detect_qos_mismatches` — paired subs/pubs by topic + pure analyzer. + * `peek_dds_samples` — full-fidelity on the 4 builtin DCPS topics ; + arbitrary user topics return best-effort samples with a + `_decode_status` annotation. Fast DDS 2.6.x ships only a partial + dynamic XTypes Python surface, so the `"raw"` fallback with + `_raw_bytes_hex` is the common path on user topics until upstream + binding completion (v0.5.x patch). + * `participant_events` — `discovered` + `lost` from native listener + callbacks ; no polling required. + * `topic_metrics` — opportunistic metrics buffered as `peek_dds_samples` + surfaces samples (same caveat as Cyclone — no at-sample-receive + callback in fastdds 2.6.x Python). Sample-introspection helpers are defensive against binding-version shape variations — same convention as the Cyclone adapter's helpers. diff --git a/src/topicforge/adapters/ros2_mock/adapter.py b/src/topicforge/adapters/ros2_mock/adapter.py index 7a8a3d4..e2ae22e 100644 --- a/src/topicforge/adapters/ros2_mock/adapter.py +++ b/src/topicforge/adapters/ros2_mock/adapter.py @@ -10,6 +10,7 @@ from topicforge.adapters.base import AdapterError, AdapterName, EffectiveMode from topicforge.adapters.ros2_mock import fixtures +from topicforge.constants import MAX_SAMPLE_COUNT from topicforge.models import ( BagAnalysis, MessageSample, @@ -27,8 +28,6 @@ # first ROS2 install. _BAG_EXTENSIONS: frozenset[str] = frozenset({".mcap", ".db3", ".bag"}) -_MAX_SAMPLE_COUNT = 50 - class MockAdapter: name: AdapterName = "mock" @@ -66,7 +65,7 @@ def peek_bag_samples(self, path: str, topic: str, count: int) -> SampleResult: if count < 0: raise AdapterError("count must be >= 0") _reject_non_bag_path(path) - clamped = min(count, _MAX_SAMPLE_COUNT) + clamped = min(count, MAX_SAMPLE_COUNT) samples = fixtures.mock_bag_samples_for(topic, clamped) return SampleResult( topic=topic, @@ -98,7 +97,7 @@ def peek_dds_samples(self, topic: str, count: int) -> SampleResult: f"Unknown DDS topic: {topic!r}. Known mock DDS topics: " f"{list(fixtures.MOCK_DDS_TOPICS)}" ) - clamped = min(count, _MAX_SAMPLE_COUNT) + clamped = min(count, MAX_SAMPLE_COUNT) return fixtures.mock_dds_samples_for(topic, clamped) def participant_events( diff --git a/src/topicforge/constants.py b/src/topicforge/constants.py new file mode 100644 index 0000000..6ee46a3 --- /dev/null +++ b/src/topicforge/constants.py @@ -0,0 +1,28 @@ +"""Package-wide constants shared across tools, services, and adapters. + +Layer-neutral home for values consumed across the whole package : tool +handlers cap arguments here, services read them when constructing +clamped slices, adapters reference them when sizing their internal +buffers. Keeping the home at the root level (rather than under +`services/`) avoids reverse-layer imports (`adapters/` would otherwise +have to reach into `services.constants` and trigger a circular load +through `services/__init__.py`). + +History : v0.2.0 moved `MAX_SAMPLE_COUNT` out of `services.inspector` +into `services.constants` (audit-2026-05-14 item A4). v0.5.x relocated +to this root module so the mock adapter and bag service can reuse it +without crossing the services→adapters layer in reverse. + +If a constant graduates to runtime-configurable, move it onto +`Settings` and update callers accordingly. +""" + +from __future__ import annotations + +# Server-side cap on `sample_messages`, `peek_dds_samples`, and +# `peek_bag_samples` count parameters. Surfaced to clients via +# `HealthReport.max_sample_count`. Requests above this value are +# silently clamped to keep tool output bounded ; clients sizing their +# requests proactively should read the cap from the health endpoint +# rather than hardcoding it. +MAX_SAMPLE_COUNT = 50 diff --git a/src/topicforge/services/bag_service.py b/src/topicforge/services/bag_service.py index 703dd9c..b4c38c0 100644 --- a/src/topicforge/services/bag_service.py +++ b/src/topicforge/services/bag_service.py @@ -31,6 +31,7 @@ annotate_partial, annotate_raw, ) +from topicforge.constants import MAX_SAMPLE_COUNT from topicforge.models import ( BagAnalysis, BagTopicStats, @@ -45,8 +46,6 @@ ".db3": "db3", ".bag": "bag", } - -_MAX_SAMPLE_COUNT = 50 _ROSBAGS_REQUIRED_MSG = ( "Bag analysis with full sample decode requires the `rosbags` library. " "Install via `pip install topicforge[bags]` and retry. " @@ -154,7 +153,7 @@ def peek_samples( if not resolved.exists(): raise AdapterError(f"bag path does not exist: {path!r}") - clamped = min(count, _MAX_SAMPLE_COUNT) + clamped = min(count, MAX_SAMPLE_COUNT) try: samples = _peek_with_rosbags(resolved, topic, clamped) except AdapterError: diff --git a/src/topicforge/services/constants.py b/src/topicforge/services/constants.py deleted file mode 100644 index 59f5551..0000000 --- a/src/topicforge/services/constants.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Domain constants shared across services and tools. - -This module hosts values that multiple service-layer modules read but -that don't conceptually belong to any single one. Keeping them here -avoids cross-service imports (e.g. `services.health` reaching into -`services.inspector` for `MAX_SAMPLE_COUNT`) — flagged by the v0.1.2 -architecture audit as a smell that compounds once DDS adds its own -per-tool caps. - -If a constant graduates to runtime-configurable, move it onto `Settings` -and update callers accordingly. -""" - -from __future__ import annotations - -# Server-side cap on `sample_messages` and `peek_dds_samples` count -# parameters. Surfaced to clients via `HealthReport.max_sample_count`. -# Requests above this value are silently clamped to keep tool output -# bounded ; clients sizing their requests proactively should read the -# cap from the health endpoint rather than hardcoding it. -MAX_SAMPLE_COUNT = 50 diff --git a/src/topicforge/services/health.py b/src/topicforge/services/health.py index 59c0e66..3ec9c73 100644 --- a/src/topicforge/services/health.py +++ b/src/topicforge/services/health.py @@ -24,8 +24,8 @@ # `topicforge-pro` package still sees `middleware_available=True` — # they just need the Pro package to actually use it. from topicforge.config.settings import _DDS_BACKEND_MODULES +from topicforge.constants import MAX_SAMPLE_COUNT from topicforge.models import HealthReport -from topicforge.services.constants import MAX_SAMPLE_COUNT _HEALTH_FALLBACK_MODULES: dict[str, str] = { "rti": "rti.connextdds", diff --git a/src/topicforge/services/inspector.py b/src/topicforge/services/inspector.py index 9788b57..b68726d 100644 --- a/src/topicforge/services/inspector.py +++ b/src/topicforge/services/inspector.py @@ -10,6 +10,7 @@ from pathlib import Path from topicforge.adapters.base import AdapterError, AdapterName, MiddlewareAdapter +from topicforge.constants import MAX_SAMPLE_COUNT from topicforge.models import ( BagAnalysis, MismatchReport, @@ -19,7 +20,6 @@ TopicInfo, TopicMetrics, ) -from topicforge.services.constants import MAX_SAMPLE_COUNT DEFAULT_SAMPLE_COUNT = 5 DEFAULT_LOOKBACK_SECONDS = 300 @@ -31,11 +31,7 @@ _WINDOW_MIN = 1 _WINDOW_MAX = 3600 -# Re-export for backward-compatibility with v0.1.x code that imports -# `MAX_SAMPLE_COUNT` from `topicforge.services.inspector`. The canonical -# home is now `topicforge.services.constants` ; new code should import -# from there. -__all__ = ["DEFAULT_SAMPLE_COUNT", "MAX_SAMPLE_COUNT", "Inspector"] +__all__ = ["DEFAULT_SAMPLE_COUNT", "Inspector"] # Strict allowlist mirroring ROS2 topic-name conventions: # * must start with `/` diff --git a/tests/test_health.py b/tests/test_health.py index 44bc772..db8d7db 100644 --- a/tests/test_health.py +++ b/tests/test_health.py @@ -3,8 +3,8 @@ from __future__ import annotations from topicforge.config import Settings +from topicforge.constants import MAX_SAMPLE_COUNT from topicforge.services import HealthService -from topicforge.services.inspector import MAX_SAMPLE_COUNT def test_health_report_in_mock_mode() -> None: