feat: provision local ClickHouse for Intake - #1113
Conversation
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
|
@coderabbitai review |
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughIntake now provisions and reuses local ClickHouse containers when no external URL is configured. Startup applies the provisioned URL and continues with recovery guidance when provisioning fails. Reset and teardown remove managed containers before deleting bind-mounted data. ChangesLocal ClickHouse lifecycle
Sequence Diagram(s)sequenceDiagram
participant IntakeService
participant LocalClickHouse
participant Docker
participant ClickHouse
IntakeService->>LocalClickHouse: request local provisioning
LocalClickHouse->>Docker: validate or create managed container
Docker->>ClickHouse: start container and expose loopback port
LocalClickHouse->>ClickHouse: wait for readiness
LocalClickHouse-->>IntakeService: return effective ClickHouse URL
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (5)
services/intake/tests/test_local_clickhouse.py (1)
139-143: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd coverage for the already-reachable short circuit.
_patch_provisioningforces_can_connecttoFalse, and every provisioning test uses it. The first branch of_provision_local_clickhouse, which returnssettings.urlwithout touching Docker, is therefore never exercised. That branch is the reuse path this PR advertises, and it must not create a container.Add one test with
_can_connectreturningTrueand assertrun_calls == []and thatdocker.from_envis not called.🤖 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 `@services/intake/tests/test_local_clickhouse.py` around lines 139 - 143, Add a focused test for _provision_local_clickhouse where _can_connect returns True, asserting it returns the existing settings.url without invoking Docker provisioning. Ensure the test verifies the fake client’s run_calls remains empty and docker.from_env is not called, while keeping _patch_provisioning unchanged for the existing tests.services/intake/src/nmp/intake/config.py (1)
16-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRead the ClickHouse version from
.clickhouse-version.
local_clickhouse.CLICKHOUSE_VERSIONaliases this hardcoded constant, whilerun_clickhouse.shusesservices/intake/.clickhouse-version. A version bump can leave the Python defaults at26.3. Load the default from the version file or testDEFAULT_CLICKHOUSE_VERSIONagainst it.🤖 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 `@services/intake/src/nmp/intake/config.py` around lines 16 - 19, Update DEFAULT_CLICKHOUSE_VERSION in the configuration constants and the derived DEFAULT_CLICKHOUSE_IMAGE to use the version declared in services/intake/.clickhouse-version instead of maintaining the hardcoded 26.3 value. Ensure the existing local_clickhouse.CLICKHOUSE_VERSION alias continues to reflect the file-based version used by run_clickhouse.sh.services/intake/src/nmp/intake/local_clickhouse.py (2)
96-98: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winImplicit adoption of an unmanaged ClickHouse at the default URL.
should_provision_local_clickhouseis true only when no external URL is set, sosettings.urlhere is the defaulthttp://localhost:8123. If an unrelated ClickHouse listens there, Intake adopts it, skips container provisioning, and writes Intake data into a database it does not own. The integration test comment atservices/intake/tests/integration/test_local_clickhouse_provisioning.pyLine 55 acknowledges this case on developer machines.Consider restricting this fast path to an explicitly configured URL, and always managing the container otherwise.
🤖 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 `@services/intake/src/nmp/intake/local_clickhouse.py` around lines 96 - 98, Update the _can_connect fast path in should_provision_local_clickhouse so an already-reachable ClickHouse is reused only when the URL was explicitly configured as an external endpoint; when using the default localhost URL, continue with local container provisioning instead of adopting the unmanaged service.
110-112: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winValidate the legacy container mount before adoption. The fallback passes
data_dir=Noneto_validate_container, so a same-named container with the expected image but no/var/lib/clickhousemount is accepted for Intake writes. Require theCLICKHOUSE_DATA_PATHmount while preserving compatibility with legacy labels.🤖 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 `@services/intake/src/nmp/intake/local_clickhouse.py` around lines 110 - 112, Update the legacy fallback around _get_container so the selected container is validated with CLICKHOUSE_DATA_PATH mounted before setting legacy and adopting it. Preserve compatibility with legacy labels while rejecting same-named containers that lack the required ClickHouse data mount.services/intake/tests/test_clickhouse_startup.py (1)
60-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReuse the local
IntakeConfigin the assertion.
Service.service_configis typed asTConfig | None. Direct access toservice.service_config.clickhouse_configcan failtychecking.🤖 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 `@services/intake/tests/test_clickhouse_startup.py` around lines 60 - 63, Update the assertion in the startup test to reuse the local IntakeConfig instance when obtaining the expected ClickHouse image and data directory, instead of accessing the optional service.service_config value. Keep the existing provision.await_args.kwargs expectations unchanged.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.
Inline comments:
In `@packages/nemo_platform_ext/src/nemo_platform_ext/skills/inference/SKILL.md`:
- Around line 526-534: Add blank lines immediately before and after the fenced
bash block in the reset local platform state section, without changing the
commands or surrounding content.
- Around line 528-537: Update the teardown script around the ClickHouse cleanup
command to mirror the guard used in nemo-teardown: stop immediately when `python
-m nmp.intake.local_clickhouse --remove` fails, before executing `rm -rf
"$DATA_DIR"`. Also add the same refusal for an unsafe or explicitly configured
`DATA_DIR`, while preserving cleanup of an external `CLICKHOUSE_DATA_DIR` and
deletion only after successful removal.
In `@services/intake/scripts/spans/run_clickhouse.sh`:
- Around line 19-20: Update the compatibility path around the local ClickHouse
launcher in nmp.intake.local_clickhouse so it preserves the previous script’s
permission handling: apply chmod 755 to the configured
NMP_INTAKE_CLICKHOUSE_DATA_DIR and its tmp directory before starting ClickHouse,
while retaining manage_permissions=False for the data_dir flow.
In `@services/intake/src/nmp/intake/local_clickhouse.py`:
- Around line 166-179: Update remove_local_clickhouse to check both
_managed_container_name(resolved_data_dir) and LEGACY_CONTAINER_NAME, removing
any matching container after applying _validate_cleanup_target with the same
data-directory mount validation. Preserve the existing return and logging
behavior while ensuring legacy containers created by main or run_clickhouse.sh
are removed before cleanup proceeds.
In `@SETUP.md`:
- Around line 62-74: Update the cleanup snippet in SETUP.md to gate rm -rf
"$DATA_DIR" on successful completion of the local_clickhouse --remove command.
Use the document’s established uv run command form, and ensure a Docker cleanup
failure exits or otherwise prevents deletion so the user must start Docker and
retry.
---
Nitpick comments:
In `@services/intake/src/nmp/intake/config.py`:
- Around line 16-19: Update DEFAULT_CLICKHOUSE_VERSION in the configuration
constants and the derived DEFAULT_CLICKHOUSE_IMAGE to use the version declared
in services/intake/.clickhouse-version instead of maintaining the hardcoded 26.3
value. Ensure the existing local_clickhouse.CLICKHOUSE_VERSION alias continues
to reflect the file-based version used by run_clickhouse.sh.
In `@services/intake/src/nmp/intake/local_clickhouse.py`:
- Around line 96-98: Update the _can_connect fast path in
should_provision_local_clickhouse so an already-reachable ClickHouse is reused
only when the URL was explicitly configured as an external endpoint; when using
the default localhost URL, continue with local container provisioning instead of
adopting the unmanaged service.
- Around line 110-112: Update the legacy fallback around _get_container so the
selected container is validated with CLICKHOUSE_DATA_PATH mounted before setting
legacy and adopting it. Preserve compatibility with legacy labels while
rejecting same-named containers that lack the required ClickHouse data mount.
In `@services/intake/tests/test_clickhouse_startup.py`:
- Around line 60-63: Update the assertion in the startup test to reuse the local
IntakeConfig instance when obtaining the expected ClickHouse image and data
directory, instead of accessing the optional service.service_config value. Keep
the existing provision.await_args.kwargs expectations unchanged.
In `@services/intake/tests/test_local_clickhouse.py`:
- Around line 139-143: Add a focused test for _provision_local_clickhouse where
_can_connect returns True, asserting it returns the existing settings.url
without invoking Docker provisioning. Ensure the test verifies the fake client’s
run_calls remains empty and docker.from_env is not called, while keeping
_patch_provisioning unchanged for the existing tests.
🪄 Autofix
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 4bbf51ba-56a5-4b6d-a4cf-9513eabcb2d3
⛔ Files ignored due to path filters (4)
sdk/python/nemo-platform/src/nemo_platform/skills/inference/SKILL.mdis excluded by!sdk/**sdk/python/nemo-platform/src/nemo_platform/skills/nemo-intake/SKILL.mdis excluded by!sdk/**sdk/python/nemo-platform/src/nemo_platform/skills/nemo-teardown/SKILL.mdis excluded by!sdk/**uv.lockis excluded by!**/*.lock
📒 Files selected for processing (21)
README.mdSETUP.mddocs/agents/observability.mdxdocs/evaluator/experiments.mdxdocs/get-started/example-agent.mdxpackages/nemo_platform/pyproject.tomlpackages/nemo_platform_ext/src/nemo_platform_ext/skills/inference/SKILL.mdpackages/nemo_platform_ext/src/nemo_platform_ext/skills/nemo-intake/SKILL.mdpackages/nemo_platform_ext/src/nemo_platform_ext/skills/nemo-teardown/SKILL.mdplugins/nemo-agents/examples/nemo-agent-config/README.mdplugins/nemo-insights/examples/research-agent/README.mdservices/intake/README.mdservices/intake/pyproject.tomlservices/intake/scripts/spans/run_clickhouse.shservices/intake/src/nmp/intake/config.pyservices/intake/src/nmp/intake/local_clickhouse.pyservices/intake/src/nmp/intake/service.pyservices/intake/tests/integration/test_local_clickhouse_provisioning.pyservices/intake/tests/test_clickhouse_architecture.pyservices/intake/tests/test_clickhouse_startup.pyservices/intake/tests/test_local_clickhouse.py
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
|
@coderabbitai review Addressed the review in e8f3e39. The default local path no longer adopts an arbitrary ClickHouse already listening on localhost:8123; explicit NMP_INTAKE_CLICKHOUSE_URL remains the external-service bypass, so the suggested reachable-short-circuit test is no longer applicable. Legacy reuse now requires the exact expected data mount. The startup test now uses its concrete IntakeConfig and narrows await_args for ty. DEFAULT_CLICKHOUSE_VERSION remains a package-safe constant because the service version file is not available from every installed-package layout. Existing test_clickhouse_image_version_matches_service_pin already asserts exact equality with services/intake/.clickhouse-version, preventing drift in CI. Validation: 240 Intake unit tests, focused Ruff and ty, repository pre-commit hooks, and the real Docker create/reuse/remove integration all pass. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
git show --stat --oneline --decorate --no-renames e8f3e3904a
git show --format=fuller --no-ext-diff --unified=80 --no-renames e8f3e3904a -- \
services/intake/src/nmp/intake/local_clickhouse.py \
services/intake/src/nmp/intake/config.py \
services/intake/src/nmp/intake/service.py \
services/intake/tests/test_local_clickhouse.py \
services/intake/tests/test_clickhouse_startup.py \
services/intake/tests/test_clickhouse_image_version.py \
services/intake/testsLength of output: 50478 🏁 Script executed: #!/bin/bash
set -euo pipefail
ast-grep outline services/intake/src/nmp/intake/local_clickhouse.py --items all --type function,class
ast-grep outline services/intake/tests/test_local_clickhouse.py --items all --type function,class
sed -n '1,145p' services/intake/tests/test_local_clickhouse.py
sed -n '1,130p' services/intake/scripts/spans/run_clickhouse.sh
sed -n '1,240p' services/intake/src/nmp/intake/local_clickhouse.py
rg -n -C 5 '\bremove_local_clickhouse\s*\(|--remove\b|nmp\.intake\.local_clickhouse' \
services/intake SETUP.md README.md packages/nemo_platform_ext \
-g '*.py' -g '*.sh' -g '*.md' -g '*.mdx'Length of output: 41843
As a result, a pre-existing legacy container can start successfully, but documented Make legacy cleanup accept the same verified legacy ownership criteria as legacy reuse, or migrate the legacy container to the managed labels. Add a test for a label-less, same-mount legacy container that is reused and then removed. ✏️ Learnings added
You are interacting with an AI system. |
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
|
Fixed the remaining CodeRabbit finding in ca71c2b. Cleanup now accepts a label-less historical container only under the legacy name with the exact resolved ClickHouse data mount; partial or conflicting ownership labels still fail closed. The regression test now reuses that label-less legacy container and removes it through the documented cleanup path. All 240 Intake unit tests, focused Ruff and ty, and repository pre-commit hooks pass. |
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
|
Addressed the Python e2e tests (prebuilt image) CI failure in c99eced. The tests themselves completed; artifact upload failed while traversing a container-owned ClickHouse access directory under E2E_SERVICES_LOG_DIR. E2E persistent state now lives in a sibling data directory, keeping generated configs and service logs collectable without archiving platform state. Validation: all 58 nmp_testing package tests, focused Ruff and ty, and repository pre-commit hooks pass. |
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
|
@coderabbitai review Addressed the Linux Docker integration failure in d0e7e26. Docker SDK encoded the tuple with a None host port as an empty HostPort; the Linux daemon produced an occupied binding. The managed path now passes host port 0 explicitly, preserving loopback-only ephemeral allocation. Focused unit, Ruff, ty, repository pre-commit, and the real Docker create/reuse/remove integration pass. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
git show --format=fuller --stat --no-ext-diff --no-renames d0e7e26b27
git show --format= --unified=100 --no-ext-diff --no-renames d0e7e26b27 -- \
services/intake/src/nmp/intake/local_clickhouse.py \
services/intake/tests/test_local_clickhouse.py \
services/intake/tests/integration/test_local_clickhouse_provisioning.py
ast-grep outline services/intake/src/nmp/intake/local_clickhouse.py --items all --type function,class
rg -n -C 6 'ports|HostPort|127\.0\.0\.1|provision_local_clickhouse' \
services/intake/src/nmp/intake/local_clickhouse.py \
services/intake/tests/test_local_clickhouse.py \
services/intake/tests/integration/test_local_clickhouse_provisioning.pyLength of output: 55799
The managed path now requests You are interacting with an AI system. |
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
|
@coderabbitai review The latest commit serializes the new local ClickHouse provisioning integration test with the existing evaluator integration tests via their shared xdist resource group. Local xdist verification placed all three tests on one worker in sequence. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== PR head =='
gh pr view 1113 --repo NVIDIA-NeMo/nemo-platform --json headRefOid --jq '.headRefOid'
echo '== Latest commit =='
git log -1 --format='%H%n%s%n%b'
echo '== Latest commit changed files and diff =='
git show --stat --oneline --no-renames HEAD
git show --format= --no-ext-diff --unified=80 --no-renames HEAD
echo '== Integration test structure =='
ast-grep outline services/intake/tests/integration/test_local_clickhouse_provisioning.py --items all --type function,class
echo '== Resource-group configuration and usages =='
rg -n -C 5 --glob '*.py' --glob '*.toml' --glob '*.ini' --glob '*.cfg' \
'xdist_group|resource[_ -]?group|pytest\.mark\.xdist' \
services e2e packages pyproject.toml pytest.ini setup.cfg tox.ini 2>/dev/null || trueLength of output: 7145 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== Candidate evaluator and ClickHouse integration tests =='
fd -t f . | rg '(evaluator|clickhouse).*test.*\.py$|test.*(evaluator|clickhouse).*\.py$' || true
echo '== All xdist groups and ClickHouse test references =='
rg -n -i -C 4 \
'xdist_group|nmp_intake_clickhouse|clickhouse' \
services e2e packages .github pyproject.toml pytest.ini setup.cfg tox.ini \
-g '*.py' -g '*.toml' -g '*.yml' -g '*.yaml' -g '*.ini' -g '*.cfg' 2>/dev/null || true
echo '== pytest-xdist distribution configuration and CI invocations =='
rg -n -C 4 -- \
'--dist(=|\s+)(loadgroup|load|worksteal|each)|-n\s*(auto|[0-9]+)|pytest-xdist|xdist' \
.github services e2e packages pyproject.toml pytest.ini setup.cfg tox.ini \
-g '*.py' -g '*.toml' -g '*.yml' -g '*.yaml' -g '*.ini' -g '*.cfg' -g '*.sh' 2>/dev/null || trueLength of output: 201366 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== Shared resource-group markers =='
git grep -n -C 3 'nmp_intake_clickhouse' -- '*.py' '*.toml' '*.ini' '*.cfg' '*.yml' '*.yaml' '*.md' '*.sh' || true
echo '== Every xdist_group marker in Intake tests =='
git grep -n -C 3 'xdist_group' -- 'services/intake/**' || true
echo '== Intake test execution definitions =='
git grep -n -i -C 4 -E \
'services/intake/tests|test_local_clickhouse_provisioning|test_evaluator|pytest.*integration|integration.*pytest' \
-- '.github/**' 'services/intake/**' 'Makefile' 'Taskfile*' 'justfile' '*.toml' '*.yaml' '*.yml' '*.md' '*.sh' 2>/dev/null || true
echo '== Group-aware xdist scheduler configuration =='
git grep -n -C 4 -E -- '--dist[=[:space:]]*loadgroup|dist[[:space:]]*=[[:space:]]*"loadgroup"|dist[[:space:]]*=[[:space:]]*loadgroup' \
-- ':!uv.lock' ':!**/*.lock' || trueLength of output: 19943 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== Evaluator integration conftest map =='
ast-grep outline plugins/nemo-evaluator/tests/integration/conftest.py --items all --type function,class
echo '== Shared ClickHouse group implementation =='
rg -n -C 12 \
'CLICKHOUSE_XDIST_GROUP|CLICKHOUSE_XDIST_FIXTURE|pytest_collection_modifyitems|xdist_group|def _clickhouse' \
plugins/nemo-evaluator/tests/integration/conftest.py
echo '== Makefile pytest distribution expansion =='
sed -n '285,350p' MakefileLength of output: 7529
The provisioning test uses You are interacting with an AI system. |
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
|
@coderabbitai review The latest commit fixes Linux reuse after the ClickHouse image changes ownership of the bind mount. The marker remains mode 0600; provisioning now derives identity from stable filesystem metadata instead of reading owner-only contents. Focused unit, type, lint, and real-Docker create/reuse/remove tests pass. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== PR head and latest commit =='
gh pr view 1113 --repo NVIDIA-NeMo/nemo-platform --json headRefOid --jq '.headRefOid'
git log -1 --format='%H%n%s%n%b'
echo '== Latest commit diff =='
git show --stat --oneline --no-renames HEAD
git show --format= --no-ext-diff --unified=100 --no-renames HEAD -- \
services/intake/src/nmp/intake/local_clickhouse.py \
services/intake/tests/test_local_clickhouse.py \
services/intake/tests/integration/test_local_clickhouse_provisioning.py
echo '== Implementation and test map =='
ast-grep outline services/intake/src/nmp/intake/local_clickhouse.py --items all --type function,class
ast-grep outline services/intake/tests/test_local_clickhouse.py --items all --type function,class
echo '== Identity marker, filesystem metadata, and reuse references =='
rg -n -C 12 \
'marker|identity|stat\(|st_(dev|ino|uid|gid|mode)|chmod|0600|data_dir|reuse|mount' \
services/intake/src/nmp/intake/local_clickhouse.py \
services/intake/tests/test_local_clickhouse.py \
services/intake/tests/integration/test_local_clickhouse_provisioning.pyLength of output: 112366
Use an identity source that remains readable after ClickHouse changes ownership and is unique per marker creation. Add a test that forces identical marker metadata across two marker incarnations and verifies that the stale container is replaced. You are interacting with an AI system. |
|
@coderabbitai review Added the guarded |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== Commit and changed files =='
git show --stat --oneline --no-renames a4c7e85457
git show --format= --no-ext-diff --unified=100 --no-renames a4c7e85457
echo '== Candidate reset and teardown files =='
fd -t f . | rg '(reset|teardown|service|clickhouse|SKILL|SETUP).*($|\.py$|\.md$|\.mdx$)' | head -300
echo '== Reset-data definitions, callers, and tests =='
rg -n -C 8 --glob '*.py' --glob '*.md' --glob '*.mdx' --glob '*.sh' \
'reset-data|reset_data|remove_local_clickhouse|ownership restoration|chown|Docker.*unavailable' \
. 2>/dev/null || trueLength of output: 213566 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== CLI reset helpers with line numbers =='
nl -ba packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py | sed -n '55,130p;410,510p'
echo '== Port bindability and instance enumeration =='
rg -n -C 10 --glob '*.py' \
'def is_port_bindable|def list_instances|def nmp_user_data_dir|class ClickHouseConfig|def _restore_data_dir_ownership|def remove_local_clickhouse' \
packages/nemo_platform_ext packages/nemo_platform_plugin services/intake
echo '== Reset-data test coverage and CLI copy parity =='
rg -n -C 4 --glob '*.py' \
'reset-data|reset_services_data|is_port_bindable|restore_data_ownership|Could not restore' \
packages/nemo_platform_ext/tests sdk/python/nemo-platform/tests services/intake/tests
echo '== Copy parity =='
cmp -s \
packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py \
sdk/python/nemo-platform/src/nemo_platform/cli/commands/services/cli.py \
&& echo 'CLI copies match' || echo 'CLI copies differ'Length of output: 1409 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== CLI reset helpers =='
sed -n '55,130p;410,510p' packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py
echo '== Port bindability and instance enumeration =='
rg -n -C 10 --glob '*.py' \
'def is_port_bindable|def list_instances|def nmp_user_data_dir|class ClickHouseConfig|def _restore_data_dir_ownership|def remove_local_clickhouse' \
packages/nemo_platform_ext packages/nemo_platform_plugin services/intake || true
echo '== Reset-data test coverage =='
rg -n -C 4 --glob '*.py' \
'reset-data|reset_services_data|is_port_bindable|restore_data_ownership|Could not restore' \
packages/nemo_platform_ext/tests sdk/python/nemo-platform/tests services/intake/tests || true
echo '== CLI copy parity =='
cmp -s \
packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py \
sdk/python/nemo-platform/src/nemo_platform/cli/commands/services/cli.py \
&& echo 'CLI copies match' || echo 'CLI copies differ'Length of output: 38333 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== Data-directory propagation =='
rg -n -C 8 --glob '*.py' \
'NMP_DATA_DIR|local_services\.data_dir|data_dir.*local_services|nmp_user_data_dir\(' \
packages/nemo_platform_ext sdk/python/nemo-platform packages/nemo_platform_plugin packages/nmp_common \
services 2>/dev/null || true
echo '== Config and runner symbols =='
ast-grep outline packages/nemo_platform_ext/src --items all --type function,class --match 'Config|PlatformAppConfig|run_platform'
ast-grep outline packages/nmp_common/src --items all --type function,class --match 'Config|PlatformAppConfig|run_platform'
echo '== Canonical and vendored CLI checksums =='
sha256sum \
packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py \
sdk/python/nemo-platform/src/nemo_platform/cli/commands/services/cli.pyLength of output: 71640
You are interacting with an AI system. |
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
|
@coderabbitai review Removed the newly introduced |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== PR head and target commit =='
gh pr view 1113 --repo NVIDIA-NeMo/nemo-platform --json headRefOid --jq '.headRefOid'
git show --format='%H%n%s%n%b' --no-patch 4abb23e893
echo '== Changed files and patch =='
git show --stat --oneline --no-renames 4abb23e893
git show --format= --no-ext-diff --unified=100 --no-renames 4abb23e893
echo '== Relevant source structure =='
ast-grep outline services/intake/src/nmp/intake/local_clickhouse.py --items all --type function,class
ast-grep outline packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py --items all --type function,class
ast-grep outline sdk/python/nemo-platform/src/nemo_platform/cli/commands/services/cli.py --items all --type function,class
echo '== Cleanup, ownership, and removed-command references =='
rg -n -C 8 --glob '*.py' --glob '*.md' --glob '*.mdx' --glob '*.sh' \
'reset-data|reset_data|remove_local_clickhouse|restore.*ownership|chown|NMP_DATA_DIR|data_dir' \
services/intake packages/nemo_platform_ext sdk/python/nemo-platform SETUP.md README.md
echo '== Relevant test structure and coverage =='
ast-grep outline services/intake/tests/test_local_clickhouse.py --items all --type function,class
rg -n -C 6 --glob '*.py' \
'remove_local_clickhouse|restore.*ownership|ownership|external.*data|data_dir' \
services/intake/tests packages/nemo_platform_ext/tests sdk/python/nemo-platform/tests 2>/dev/null || true
echo '== Canonical/vendored CLI parity =='
cmp -s \
packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py \
sdk/python/nemo-platform/src/nemo_platform/cli/commands/services/cli.py \
&& echo 'CLI copies match' || echo 'CLI copies differ'Length of output: 202366 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== Commit files =='
git diff-tree --no-commit-id --name-status -r 4abb23e893
echo '== Target patch only =='
git show --format= --no-ext-diff --unified=50 --no-renames 4abb23e893 -- \
services/intake/src/nmp/intake/local_clickhouse.py \
services/intake/tests/test_local_clickhouse.py \
packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py \
packages/nemo_platform_ext/tests \
README.md SETUP.md
echo '== Current relevant implementation =='
sed -n '160,207p;344,395p;532,567p' services/intake/src/nmp/intake/local_clickhouse.py
sed -n '480,555p' services/intake/tests/test_local_clickhouse.py
sed -n '730,825p' packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py
echo '== All reset-data references, excluding Git internals =='
rg -n -i -C 3 --glob '!**/.git/**' \
'reset-data|reset_data|reset services data|reset services-data' . || true
echo '== Discover possible duplicate service CLI implementations =='
fd -t f 'cli.py' . | rg '/cli/commands/services/cli\.py$|/commands/services/cli\.py$' || true
echo '== Test files changed by target commit =='
git diff-tree --no-commit-id --name-only -r 4abb23e893 | rg '(^|/)(test|tests)(/|_)|test_.*\.py$' || trueLength of output: 75592
The Intake You are interacting with an AI system. |
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
|
@coderabbitai review Addressed the compatibility review in 44f7fc0. The legacy wrapper now forwards arguments and selects legacy mode explicitly; direct module execution defaults to managed naming. Script-backed platform callers pin NMP_INTAKE_CLICKHOUSE_URL, wrapper cleanup targets its actual data directory, unmanaged credential diagnostics are accurate, and Insights resolves managed containers by label without guessing across multiple instances. Validation: 340 broader unit tests plus 14 eval-stack tests and 1 real-Docker provisioning integration pass; focused Ruff, ty, shell syntax, and all applicable pre-commit hooks pass. |
🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== PR head and target commit =='
gh pr view 1113 --repo NVIDIA-NeMo/nemo-platform --json headRefOid --jq '.headRefOid'
git show --no-patch --format='%H%n%s%n%b' 44f7fc0475
echo '== Changed files and patch =='
git show --stat --oneline --no-renames 44f7fc0475
git show --format= --no-ext-diff --unified=100 --no-renames 44f7fc0475
echo '== Relevant implementation/test maps =='
fd -t f . | rg '(local_clickhouse|run_clickhouse|insights|clickhouse).*\.py$|run_clickhouse\.sh$' | sort
ast-grep outline services/intake/src/nmp/intake/local_clickhouse.py --items all --type function,class
ast-grep outline services/intake/tests/test_local_clickhouse.py --items all --type function,class
echo '== Compatibility call sites, environment pins, labels, and cleanup =='
rg -n -C 8 --glob '*.py' --glob '*.sh' --glob '*.md' \
'run_clickhouse|local_clickhouse|NMP_INTAKE_CLICKHOUSE_URL|--legacy|legacy.*mode|remove_local_clickhouse|credential|label' \
services plugins packages e2e 2>/dev/null || trueLength of output: 201525 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== Files changed by 44f7fc0475 =='
git diff-tree --no-commit-id --name-status -r 44f7fc0475
echo '== Focused patch =='
git show --format= --no-ext-diff --unified=60 --no-renames 44f7fc0475 -- \
services/intake/src/nmp/intake/local_clickhouse.py \
services/intake/scripts/spans/run_clickhouse.sh \
services/intake/tests/test_local_clickhouse.py \
services/intake/tests/integration/test_local_clickhouse_provisioning.py \
plugins/nemo-insights \
packages/nemo_platform_ext \
sdk/python/nemo-platform
echo '== Current local ClickHouse symbols =='
rg -n --glob '*.py' \
'^(async )?def |^class |^(LEGACY|MANAGED|CLICKHOUSE)_[A-Z_]+\s*=' \
services/intake/src/nmp/intake/local_clickhouse.py
echo '== Current compatibility call sites and tests =='
rg -n -C 5 --glob '*.py' --glob '*.sh' \
'run_clickhouse|NMP_INTAKE_CLICKHOUSE_URL|legacy_mode|legacy.*name|remove_local_clickhouse|managed.*label|containers\.list' \
services/intake plugins/nemo-insights packages/nemo_platform_ext sdk/python/nemo-platform \
-g '!**/.git/**' 2>/dev/null || trueLength of output: 139976 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== Provisioning and removal implementation =='
sed -n '72,210p' services/intake/src/nmp/intake/local_clickhouse.py
sed -n '535,575p' services/intake/src/nmp/intake/local_clickhouse.py
echo '== All wrapper callers =='
rg -n -C 10 --glob '*.py' --glob '*.sh' \
'scripts/spans/run_clickhouse\.sh|run_clickhouse\.sh' \
services plugins packages e2e .github 2>/dev/null || true
echo '== Changed evaluator integration test =='
git show --format= --no-ext-diff --unified=80 44f7fc0475 -- \
plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py
echo '== Current Insights resolver and cleanup tests =='
sed -n '475,555p' plugins/nemo-insights/testbed/reingest.py
sed -n '1215,1275p' plugins/nemo-insights/tests/testbed/test_reingest.py
echo '== Current wrapper documentation =='
rg -n -C 6 --glob '*.md' --glob '*.sh' \
'legacy-script-mode|run_clickhouse\.sh|--remove|--data-dir' \
services/intake/README.md services/intake/scripts/spans/run_clickhouse.shLength of output: 38866
The wrapper forwards arguments and uses legacy mode. Direct module execution uses managed mode. Script-backed platform callers pin Insights resolves one managed container by label and rejects multiple matches. The focused tests cover these paths. ✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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.
Inline comments:
In `@plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py`:
- Around line 101-115: Update the ClickHouse fixture setup around _wait_for_tcp
so the startup subprocess.run is inside the try block, ensuring teardown
executes even when provisioning or readiness fails. Change the cleanup
subprocess.run invocation to check=True so teardown failures propagate instead
of being suppressed.
In `@plugins/nemo-insights/testbed/reingest.py`:
- Line 513: Update the cleanup logic using running_names and
_LEGACY_CLICKHOUSE_CONTAINER to validate that the matched container has the
expected data mount or durable Intake identity before removing it; if validation
fails, preserve all state. Add a test covering an unrelated same-name running
container and verify it is not cleaned up.
- Around line 530-531: Bind the TTL command at lines 530-531 and cleanup at
lines 872-874 in reingest.py to an explicit verified target proving Intake uses
the managed local ClickHouse container, rather than relying on loopback
base_url; when binding is unknown or NMP_INTAKE_CLICKHOUSE_URL points
externally, skip both operations and preserve rows and workspace records. Add a
test covering loopback Intake with an external ClickHouse target.
🪄 Autofix
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 6586ebb4-9075-4eb8-9c68-97a3cb2c717f
⛔ Files ignored due to path filters (1)
sdk/python/nemo-platform/src/nemo_platform/skills/inference/SKILL.mdis excluded by!sdk/**
📒 Files selected for processing (12)
SETUP.mdpackages/nemo_platform_ext/src/nemo_platform_ext/skills/inference/SKILL.mdplugins/nemo-evaluator/tests/integration/test_publish_to_intake.pyplugins/nemo-insights/scripts/insights_demo.pyplugins/nemo-insights/testbed/eval/stack.pyplugins/nemo-insights/testbed/reingest.pyplugins/nemo-insights/tests/testbed/test_reingest.pyservices/intake/README.mdservices/intake/scripts/spans/run_clickhouse.shservices/intake/src/nmp/intake/local_clickhouse.pyservices/intake/tests/integration/test_local_clickhouse_provisioning.pyservices/intake/tests/test_local_clickhouse.py
🚧 Files skipped from review as they are similar to previous changes (4)
- services/intake/README.md
- services/intake/tests/integration/test_local_clickhouse_provisioning.py
- SETUP.md
- services/intake/src/nmp/intake/local_clickhouse.py
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
|
@coderabbitai review Addressed all three findings in 49d4b1c. Evaluator teardown is unconditional and loud. Insights Docker mutations now require an explicit local ClickHouse URL bound to exactly one Intake-labeled container on the matching host port; missing, external, ambiguous, and same-name/unlabeled targets preserve all state. Validation: all 95 reingest tests, focused Ruff, ty, and format checks pass. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
commit="49d4b1c633"
echo '== PR head and target commit =='
gh pr view 1113 --repo NVIDIA-NeMo/nemo-platform --json headRefOid --jq '.headRefOid'
git show --no-patch --format='%H%n%s%n%b' "$commit"
echo '== Changed files =='
git diff-tree --no-commit-id --name-status -r "$commit"
echo '== Target patch =='
git show --format= --no-ext-diff --unified=100 --no-renames "$commit"
echo '== Candidate files =='
fd -t f . | rg '(^|/)(reingest|test_reingest|test_publish_to_intake|run_clickhouse|local_clickhouse).*\.py$' | sort
echo '== Source and test symbol map =='
ast-grep outline plugins/nemo-insights/testbed/reingest.py --items all --type function,class
ast-grep outline plugins/nemo-insights/tests/testbed/test_reingest.py --items all --type function,class
ast-grep outline plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py --items all --type function,class
echo '== Docker mutations, URL parsing, container selection, and teardown =='
rg -n -C 10 --glob '*.py' \
'docker|containers\.(get|list)|exec_run|remove\(|stop\(|start\(|kill\(|NMP_INTAKE_CLICKHOUSE_URL|clickhouse.*url|url.*clickhouse|hostname|host.*port|port.*host|label|teardown|finalizer' \
plugins/nemo-insights/testbed/reingest.py \
plugins/nemo-insights/tests/testbed/test_reingest.py \
plugins/nemo-evaluator/tests/integration/test_publish_to_intake.pyLength of output: 139289 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== All Docker command construction in Insights testbed =='
rg -n -C 6 --glob '*.py' \
'["'\'']docker["'\'']|docker\s+(exec|ps|rm|stop|start)|subprocess\.(run|Popen)\(' \
plugins/nemo-insights/testbed plugins/nemo-insights/tests/testbed
echo '== Guarded resolver and cleanup call sites =='
rg -n -C 6 --glob '*.py' \
'\b_local_clickhouse_container\s*\(|\b_stop_local_ttl_merges\s*\(|\bcleanup_scratch\s*\(' \
plugins/nemo-insights
echo '== Removed container parameters or legacy-name references =='
rg -n -C 4 --glob '*.py' \
'cleanup_scratch\([^)]*container|_stop_local_ttl_merges\([^)]*container|_LEGACY_CLICKHOUSE_CONTAINER|nmp-intake-clickhouse' \
plugins/nemo-insights
echo '== Exact current changed regions =='
sed -n '485,558p;850,920p' plugins/nemo-insights/testbed/reingest.py
sed -n '90,118p' plugins/nemo-evaluator/tests/integration/test_publish_to_intake.pyLength of output: 63596
["docker", "exec", "nmp-intake-clickhouse", "clickhouse-client", "--query", "SYSTEM STOP TTL MERGES"]This bypasses the new target verification in Use the guarded resolver, or verify the stack-owned container with the same URL, label, and port criteria before You are interacting with an AI system. |
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
|
@coderabbitai review Fixed the remaining stack bypass in 1c94706. The stack now resolves the ClickHouse container using its configured local URL, Intake ownership labels, and published host port before issuing SYSTEM STOP TTL MERGES. It no longer executes against the historical name. Coverage verifies a same-name unlabeled container is excluded. Validation: 111 combined Insights tests, focused Ruff, ty, and format checks pass. |
🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
commit="1c94706c2f"
echo '== Commit and patch =='
git show --no-patch --format='%H%n%s%n%b' "$commit"
git diff-tree --no-commit-id --name-status -r "$commit"
git show --format= --no-ext-diff --unified=100 --no-renames "$commit"
echo '== Relevant source/test maps =='
ast-grep outline plugins/nemo-insights/testbed/eval/stack.py --items all --type function,class
ast-grep outline plugins/nemo-insights/testbed/reingest.py --items all --type function,class
ast-grep outline plugins/nemo-insights/tests/testbed/test_reingest.py --items all --type function,class
fd -t f . plugins/nemo-insights | rg '(^|/)(test_.*stack|stack.*test|test_reingest).*\.py$' || true
echo '== Resolver, Docker mutation, URL, label, and port references =='
rg -n -C 10 --glob '*.py' \
'SYSTEM STOP TTL MERGES|exec_run|docker.*exec|_local_clickhouse_container|NMP_INTAKE_CLICKHOUSE_URL|clickhouse.*url|url.*clickhouse|containers\.(get|list)|label|host.*port|port.*host|nmp-intake-clickhouse' \
plugins/nemo-insights/testbed \
plugins/nemo-insights/tests
echo '== Targeted current source regions =='
sed -n '1,150p' plugins/nemo-insights/testbed/eval/stack.py
sed -n '470,570p' plugins/nemo-insights/testbed/reingest.pyLength of output: 120686
A same-name unlabeled container does not match the resolver. The regression test covers this case. ✅ Action performedReview finished.
|
|
@coderabbitai review |
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
plugins/nemo-insights/tests/testbed/test_eval_scripts.py (1)
15-47: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExercise the configured-port and rejection paths.
The success test calls
_intake_clickhouse_container()withoutclickhouse_url, so it verifies only the default port8123. It does not verify the new URL-to-port conversion. Add focused cases for a non-default local URL, a non-local URL, and multiple matching containers.Example success-case adjustment
- assert stack._intake_clickhouse_container() == "nmp-intake-clickhouse-managed" + assert stack._intake_clickhouse_container("http://localhost:55123") == "nmp-intake-clickhouse-managed" ... - assert "publish=8123" in commands[0] + assert "publish=55123" in commands[0]🤖 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 `@plugins/nemo-insights/tests/testbed/test_eval_scripts.py` around lines 15 - 47, Extend the tests for _intake_clickhouse_container to pass a non-default local clickhouse_url and assert the Docker filter uses its converted port, add a non-local URL case that verifies the expected rejection or fallback behavior, and add a multiple-matching-containers case to verify the selection behavior. Preserve the existing label assertions and unlabeled-container rejection coverage.
🤖 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.
Inline comments:
In `@plugins/nemo-insights/testbed/eval/stack.py`:
- Around line 108-112: Update the subprocess environment setup around the
ClickHouse startup invocation and its delegated provisioner so both subprocesses
receive NMP_INTAKE_CLICKHOUSE_URL set to CLICKHOUSE_URL. Ensure
run_clickhouse.sh is pinned to the local ClickHouse endpoint rather than relying
on legacy-script-mode behavior.
---
Nitpick comments:
In `@plugins/nemo-insights/tests/testbed/test_eval_scripts.py`:
- Around line 15-47: Extend the tests for _intake_clickhouse_container to pass a
non-default local clickhouse_url and assert the Docker filter uses its converted
port, add a non-local URL case that verifies the expected rejection or fallback
behavior, and add a multiple-matching-containers case to verify the selection
behavior. Preserve the existing label assertions and unlabeled-container
rejection coverage.
🪄 Autofix
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 995b20bb-6b68-4e5c-bca5-7d51eab14b17
📒 Files selected for processing (5)
plugins/nemo-evaluator/tests/integration/test_publish_to_intake.pyplugins/nemo-insights/testbed/eval/stack.pyplugins/nemo-insights/testbed/reingest.pyplugins/nemo-insights/tests/testbed/test_eval_scripts.pyplugins/nemo-insights/tests/testbed/test_reingest.py
🚧 Files skipped from review as they are similar to previous changes (3)
- plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py
- plugins/nemo-insights/tests/testbed/test_reingest.py
- plugins/nemo-insights/testbed/reingest.py
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
|
Manual local E2E verification: # From an existing nemo-platform clone. Run make bootstrap-python first if needed.
gh pr checkout 1113
docker info >/dev/null
uv run nemo services start \
--services auth,entities,intake \
--instance clickhouse-pr-e2e
# Writes an OTLP trace and polls Intake until it reads sample-session back.
uv run services/intake/examples/send_otel_sample.py \
--endpoint http://127.0.0.1:8080/apis/intake/v2/workspaces/default/ingest/otlp/v1/traces
# Show the two persisted ClickHouse-backed rows explicitly.
curl -fsSG \
--data-urlencode 'filter[session_id]=sample-session' \
http://127.0.0.1:8080/apis/intake/v2/workspaces/default/spans \
| jq '.data[] | {name, session_id, source}'
uv run nemo services stop --instance clickhouse-pr-e2eExpected readback includes I ran this exact sequence against the current PR head. The smoke script completed its built-in readback, the explicit query returned both rows, and graceful service shutdown stopped the managed ClickHouse container without deleting its data. |
ironcommit
left a comment
There was a problem hiding this comment.
LGTM, would like further clean up at some point.
Summary
Intake now provisions an isolated, data-directory-owned ClickHouse container when no external URL is configured, while preserving explicit external ClickHouse ownership. Managed containers use dynamic loopback ports, stop without data deletion on graceful platform shutdown, and restart on the next startup.
Changes
503responses when Docker or ClickHouse provisioning fails, with actionable recovery guidance.clickhouse-connectconnection warning during readiness polling while retaining final timeout failures and unrelated warnings.sourcefilter.Type of Change
Quality Gates
Verification
Signed-off-by:traileruv run pre-commit run -apasses, or any blocked checks are identified belowTargeted validation:
uv run --frozen pytest services/intake/tests/test_local_clickhouse.py services/intake/tests/test_clickhouse_startup.py plugins/nemo-insights/tests/testbed/test_eval_scripts.py -q— 55 passed.uv run --frozen pytest services/intake/tests/integration/test_local_clickhouse_provisioning.py -v --log-cli-level=WARNING— 1 real-Docker create/reuse/dynamic-port/restart-policy/removal test passed using temporary data.uv run --frozen ruff check ...anduv run --frozen ruff format --check ...— passed.uv run --frozen ty check services/intake/src/nmp/intake/local_clickhouse.py services/intake/src/nmp/intake/service.py plugins/nemo-insights/testbed/eval/stack.py— passed.make vendor-nemo-platform-ext— passed; the generated teardown skill mirror is byte-identical to its package source.nemo services startprovisioned ClickHouse,send_otel_sample.pywrote and read backsample-chainandsample-llm, the spans API returned both rows, andnemo services stopstopped the managed container.uv run pre-commit run -a— all backend/config hooks passed.studio-lint-stagedwas blocked by the local unrelated Studio environment: Node v24.18.0 does not satisfy>=22.23.2 <23, andlint-stagedis not installed. Noweb/files changed.uv run --frozen pytest services/intake/tests -qwas stopped after 125 passing tests in 5m34s when it entered the broad real-ClickHouse integration matrix; no failure was observed. The directly affected unit and isolated Docker lifecycle tests above completed successfully.origin/main..HEADcontain a matchingSigned-off-by:trailer.