Skip to content

refactor(studio): consolidate four delete modals into generic BulkDeleteModal (ASTD-232)#439

Open
aray12 wants to merge 15 commits into
mainfrom
astd-232-merge-near-identical-delete-modals-into-one-generic
Open

refactor(studio): consolidate four delete modals into generic BulkDeleteModal (ASTD-232)#439
aray12 wants to merge 15 commits into
mainfrom
astd-232-merge-near-identical-delete-modals-into-one-generic

Conversation

@aray12

@aray12 aray12 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Replaces four structurally-identical delete modals (DataDesignerJobsDataView/DeleteJobModal, SafeSynthesizerJobsDataView/DeleteJobModal, FilesetListRoute/DatasetBulkDeleteModal, FilesetFileExplorer/BulkDeleteModal) with a single generic BulkDeleteModal<T> at components/BulkDeleteModal/.
  • Generic accepts items, open, onDelete: (items: T[]) => Promise<void>, title: string | ((count: number) => string), and onClose.
  • Each caller retains its own mutation hook and query-cache invalidation logic as an onDelete callback — modal handles pending state, error display, and the confirmation UI via DeleteConfirmationModal.
  • BulkDeleteModal/utils.ts (extractFilePathsFromDirectory) relocated to FilesetFileExplorer/extractFilePathsFromDirectory.ts.
  • Net: -1,508 lines (258 added, 1,766 deleted across 15 files).

Test plan

  • pnpm --filter nemo-studio-ui typecheck — passes
  • pnpm --filter nemo-studio-ui lint — passes
  • pnpm --filter nemo-studio-ui test src/components/DatasetsTable src/routes/FilesetListRoute src/components/filesets/FilesetFileExplorer src/components/dataViews/DataDesignerJobsDataView src/components/dataViews/SafeSynthesizerJobsDataView — 136 tests pass

Summary by CodeRabbit

  • New Features

    • Added local-subprocess E2E support and new job auth tests for workspace-aware access, admin listing, and principal propagation.
    • Studio now shows job artifacts in Claude Code chat history and supports a top-bar chat popout with shared session state.
    • Added bulk-delete support across datasets, filesets, and job views using a unified confirmation dialog.
  • Bug Fixes

    • Improved job scheduling/reconciliation diagnostics and workspace-scoped task listing.
    • Updated Docker base images and dependency metadata for newer, more current builds.

aray12 and others added 14 commits June 22, 2026 15:57
…generic BulkDeleteModal

Replaces DataDesignerJobsDataView/DeleteJobModal, SafeSynthesizerJobsDataView/DeleteJobModal,
FilesetListRoute/DatasetBulkDeleteModal, and FilesetFileExplorer/BulkDeleteModal with a single
generic BulkDeleteModal<T> that accepts items, open, onDelete, title, and onClose.

Each caller now owns its own mutation hook and passes onDelete as a callback, keeping
delete logic co-located with its query-cache invalidation while the modal handles
pending state, error display, and confirmation UI via DeleteConfirmationModal.

Signed-off-by: Alex Ray <alray@nvidia.com>
…re (#398)

Add a new E2E test suite (`test_jobs_auth.py`) that validates workspace
isolation and principal propagation under an auth-enabled platform config.
Introduce a reusable `diagnostics.py` module in the jobs controller layer
to collect and log structured job/step/task state on errors, and wire it
into the reconciler and scheduler for automatic debug-level diagnostics
when steps transition to ERROR or encounter unexpected exceptions.

Refactor `e2e/conftest.py` to support multiple running-services instances
keyed by config hash, enabling per-test-module platform configs (e.g.,
`local-subprocess.yaml` with auth enabled) to coexist in a single session.
Add a `local-subprocess.yaml` E2E config and extend `nmp_testing` utilities
with `grant_workspace_role`, `unique_email`, and `TEST_ADMIN_EMAIL` helpers
needed by the auth test scenarios.

Signed-off-by: Ryan S <267728323+ironcommit@users.noreply.github.com>
* feat: surface experiment metadata as dynamic columns

Union of metadata keys across loaded rows, sorted alphabetically,
inserted after the Models column. Each key becomes a hideable column
via the existing EditColumnsMenu. Cell rendering: null/undefined → '-',
object/array → JSON.stringify, primitives → String(). Values over 50
chars truncate with a tooltip showing the full string.

Signed-off-by: Nathan Walston <nwalston@nvidia.com>

* fix: normalize metadata keys to lowercase to collapse case variants

'status' and 'Status' now produce a single column instead of two
identically-labelled headers. The accessor finds the first key that
lowercases to a match, so the value is still retrieved correctly
regardless of how the producer cased the key.

Signed-off-by: Nathan Walston <nwalston@nvidia.com>

* style: fix prettier formatting in ExperimentGroupDataView

Signed-off-by: Nathan Walston <nwalston@nvidia.com>

---------

Signed-off-by: Nathan Walston <nwalston@nvidia.com>
Signed-off-by: Swarom Muley <smuley@nvidia.com>
…usages (#385)

* fix(studio): agent rule for correct usage of KUI select API, global CSS patch

Signed-off-by: Octavian Drulea <odrulea@nvidia.com>

* fix(studio): add agent rule to prevent future bugs

Signed-off-by: Octavian Drulea <odrulea@nvidia.com>

* fix(studio): apply SelectListbox in 2 call sites that were modified

Signed-off-by: Octavian Drulea <odrulea@nvidia.com>

* fix(studio): fix breaking test

Signed-off-by: Octavian Drulea <odrulea@nvidia.com>

---------

Signed-off-by: Octavian Drulea <odrulea@nvidia.com>
…356)

Signed-off-by: Henrique Tolentino <htolentino@nvidia.com>
* Add a pop out window for the Studio Code Agent

Signed-off-by: Henrique Tolentino <htolentino@nvidia.com>

* Small fixes

Signed-off-by: Henrique Tolentino <htolentino@nvidia.com>

* Fix tests and format

Signed-off-by: Henrique Tolentino <htolentino@nvidia.com>

* Fix unrelated tests

Signed-off-by: Henrique Tolentino <htolentino@nvidia.com>

---------

Signed-off-by: Henrique Tolentino <htolentino@nvidia.com>
Signed-off-by: Matt Kornfield <mkornfield@nvidia.com>
Signed-off-by: Matt Kornfield <mkornfield@nvidia.com>
* feat(studio): row pinning for ExperimentGroupDataView

Signed-off-by: Nicholas Kolean <nakolean@gmail.com>

* change from local storage to two query approach with pinned status saved in be

Signed-off-by: Nicholas Kolean <nakolean@gmail.com>

* rebase fix

Signed-off-by: Nicholas Kolean <nakolean@gmail.com>

---------

Signed-off-by: Nicholas Kolean <nakolean@gmail.com>
Signed-off-by: Matt Kornfield <mkornfield@nvidia.com>
nemoguardrails 0.21.0 depends on LangChain but does not declare it as a
direct dependency, leaving consumers exposed to vulnerable transitive
versions. Explicitly add langchain, langchain-community, langchain-core,
and langchain-openai with minimum version bounds that include the CVE
fixes, and update uv.lock accordingly.

Also document the vendored Switchyard snapshot in NOTICE and include
NOTICE in the root pyproject.toml license-files list.

Signed-off-by: Ryan S <267728323+ironcommit@users.noreply.github.com>
)

* chore: upgrade the python base container used in some other builds

Signed-off-by: Brooke Storm <brookes@nvidia.com>

* chore: update another image

Signed-off-by: Brooke Storm <brookes@nvidia.com>

* chore: correct typo

Signed-off-by: Brooke Storm <brookes@nvidia.com>

* chore: target the current vuln surface

Signed-off-by: Brooke Storm <brookes@nvidia.com>

---------

Signed-off-by: Brooke Storm <brookes@nvidia.com>
…227) (#366)

* refactor(studio): replace deprecated ReadOnlyField with KVPair (ASTD-227)

Migrate all usages of the deprecated ReadOnlyField component to KVPair
from @nemo/common and delete the deprecated file.

Signed-off-by: Alex Ray <alray@nvidia.com>

* fix(studio): use nullish checks for metric params to preserve falsy values (ASTD-227)

Signed-off-by: Alex Ray <alray@nvidia.com>

---------

Signed-off-by: Alex Ray <alray@nvidia.com>
@aray12 aray12 requested review from a team as code owners June 24, 2026 16:59
…dentical-delete-modals-into-one-generic

Signed-off-by: Alex Ray <alray@nvidia.com>
@aray12 aray12 requested a review from a team as a code owner June 24, 2026 18:20
@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 20908/27478 76.1% 61.2%
Integration Tests 12108/26247 46.1% 19.5%

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds job controller diagnostics (scheduler/reconciler), fixes workspace-scoped task listing, and introduces a subprocess backend grace window. Refactors the E2E harness to a config-hash pooled services model with auth support. Refactors the Studio Claude Code chat page to a shared ClaudeCodeChatProvider with a top-bar pop-out, job artifact tracking, and localStorage session persistence. Consolidates bulk-delete UI into a generic modal, fixes KUI v1 SelectListbox wrapping, migrates evaluation panels from ReadOnlyField to KVPair, and adds a pinned/unpinned experiment hook. Also renames vendored switchyard, bumps Docker base images, removes stale CI workflows, and adds numerous spec/plan documents.

Changes

Jobs Service: Diagnostics, Scheduler Conflicts, Subprocess Grace Window

Layer / File(s) Summary
Jobs config field and diagnostics module
services/core/jobs/src/nmp/core/jobs/config.py, services/core/jobs/src/nmp/core/jobs/controllers/diagnostics.py, docs/set-up/config-reference.mdx, services/core/jobs/tests/controllers/test_diagnostics.py
Adds include_job_logs_in_diagnostics bool field (default False) and a new diagnostics module with collect_job_diagnostics/log_job_diagnostics_if_debug; tests cover conditional log inclusion.
Workspace-scoped task listing
services/core/jobs/src/nmp/core/jobs/app/dispatcher.py, services/core/jobs/src/nmp/core/jobs/api/v2/jobs/endpoints.py, services/core/jobs/tests/test_dispatcher_cross_workspace.py
list_tasks now requires and filters by workspace; regression tests added for default and custom workspace isolation.
Reconciler diagnostics and typed status params
services/core/jobs/src/nmp/core/jobs/controllers/reconciler.py, services/core/jobs/tests/controllers/test_reconciler.py
Wires log_job_diagnostics_if_debug into error transitions and unexpected exceptions; get_steps_for_reconciliation now uses typed filter params.
Scheduler: conflict handling and diagnostics
services/core/jobs/src/nmp/core/jobs/controllers/scheduler.py, services/core/jobs/tests/controllers/test_scheduler.py
Adds _should_ignore_conflicting_pending_update to handle stale 409 PENDING writes; wires diagnostics into error paths; adds a step_spec assertion.
Subprocess backend grace window
services/core/jobs/src/nmp/core/jobs/controllers/backends/subprocess.py, services/core/jobs/tests/controllers/test_subprocess_backend.py, services/core/jobs/tests/test_timestamp_contracts.py
5-second grace window for missing-metadata PENDING steps; adds task fallback and staleness helpers; removes string-to-datetime conversion in cleanup.
Task auth runtime integration refactor
services/core/jobs/tests/integration/test_task_auth_runtime.py
Replaces dynamic ModuleType helper with a typed Protocol-based implementation returning secret value directly.

E2E Harness: Config-Hash Pooling and Auth Job Tests

Layer / File(s) Summary
Services pool data models
e2e/services_pool.py, pytest.ini
Defines ServicesPoolKey, RunningServices, ModuleConfigState, admin_headers, and constants; registers e2e_config marker.
E2EServicesPool lifecycle and config hashing
e2e/services_pool.py
Implements acquire/release reference counting, deterministic config normalization/hashing, per-instance path rewriting, and service startup with health/auth readiness polling.
conftest.py fixture rewiring
e2e/conftest.py
Replaces direct subprocess harness with pooling fixtures; updates pytest_collection_modifyitems and pytest_runtest_makereport; removes nemo_run fixture.
Subprocess E2E config and run_nemo_local injection
e2e/configs/local-subprocess.yaml, packages/nmp_testing/src/nmp/testing/utils.py, packages/nmp_testing/src/nmp/testing/__init__.py, packages/nmp_testing/tests/unit/test_e2e_harness.py
Adds local subprocess E2E config; extends run_nemo_local with base_url/workspace injection; adds unit tests for pool helpers.
Auth job E2E tests
e2e/test_jobs_auth.py, e2e/test_jobs.py, e2e/test_data_designer.py
Adds three auth-gated job tests (principal propagation, unauthorized workspace denial, admin all-workspace listing); wires test_jobs to subprocess config; migrates test_data_designer to run_nemo_local.
Developer scripts
script/run-e2e-linux.sh, script/run-hello-world-jobs.sh, script/submit-hello-world-job.sh
New scripts for Docker-based E2E runs, hello-world service startup (with optional auth), and hello-world job submission.

Studio: Shared ClaudeCodeChatProvider, Top-Bar Pop-Out, and Job Artifacts

Layer / File(s) Summary
Job artifact types, localStorage key, active session storage
web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/types.ts, web/packages/studio/src/util/localStorage.ts, web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/activeSessionStorage.ts
Adds ClaudeCodeChatJobArtifact, jobs field on ClaudeCodeChatArtifacts, CLAUDE_CODE_ACTIVE_SESSION_KEY_PREFIX, and guarded localStorage read/write helpers.
useClaudeCodeChatRuntime: loadSession, onSessionIdChange, studioPathname
web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/useClaudeCodeChatRuntime.ts, useCustomAssistantChatRuntime.ts, useClaudeCodeChatRuntime.test.ts
Adds replaceMessages, loadSession, onSessionIdChange, studioPathname, and the ClaudeCodeChatRuntime type alias.
ClaudeCodeChatContext and ClaudeCodeChatProvider
web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/context/...
Shared context owns the runtime per workspace; implements session loading with stale-guard, startNewChat, toast errors, and localStorage hydration on mount.
ClaudeCodeChatRoute refactor and ClaudeCodeChatThread
web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/index.tsx, ClaudeCodeChatThread.tsx, ClaudeCodeLayout.tsx, DashboardLandingRoute/...
Route driven by useClaudeCodeChatContext; extracts ClaudeCodeChatThread with scroll-to-bottom; adds onNewChat to layout; clears active session on dashboard prompt submit.
ClaudeCodeTopBarChat pop-out, GlobalNav, PageLayout
web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/ClaudeCodeTopBarChat.tsx, web/packages/studio/src/components/Layouts/GlobalNav/..., web/packages/studio/src/routes/PageLayout/...
Pop-out chat with unread badge and thinking indicator; conditionally mounted in GlobalNav; PageLayout wraps with ClaudeCodeChatProvider when coding agent enabled.
Job artifact tracking: artifacts, api, history panel, backend
web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/artifacts.ts, api.ts, historyPanel/..., services/studio/src/nmp/studio/coding_agent_artifacts.py, coding_agent_mcp_tools.py, coding_agents.py, tests
Records job artifacts from studio_link (destination=job) and job_progress tool events through the full stack; renders them as links in the artifacts pane; updates Studio system prompts to prefer Studio UI over CLI.

Studio UI: BulkDeleteModal, SelectListbox, KVPair migration, ExperimentGroup pinning

Layer / File(s) Summary
Generic BulkDeleteModal and call-site migrations
web/packages/studio/src/components/BulkDeleteModal/..., SafeSynthesizerJobsDataView/..., DataDesignerJobsDataView/..., FilesetListRoute/DatasetBulkDeleteModal/..., FilesetFileExplorer/BulkDeleteModal/..., tests
Introduces shared BulkDeleteModal<T>; migrates all four call sites; removes per-view DeleteJobModal files.
SelectListbox wrapping fix
web/packages/common/src/components/form/ControlledSearchableSelect/index.tsx, web/packages/studio/src/components/evaluation/EvaluationModelSelect.tsx, web/packages/studio/src/index.css, web/packages/studio/AGENTS.md
Wraps dropdown content in SelectListbox for KUI v1; adds CSS hack for transparent popover background; documents pattern in AGENTS.md.
ReadOnlyField → KVPair migration
web/packages/studio/src/components/evaluation/Configurations/..., web/packages/studio/src/components/common/ReadOnlyField.tsx
Replaces ReadOnlyField with KVPair in four evaluation config panels; removes the ReadOnlyField component; changes absent-value rendering to undefined.
useExperimentGroupExperiments hook and ExperimentGroupDataView
web/packages/studio/src/components/dataViews/ExperimentGroupDataView/useExperimentGroupExperiments.ts, index.tsx, useExperimentGroupExperiments.test.ts
New hook fetches split pinned/unpinned queries with deduplication, scoped invalidation, and in-flight guard; view updated with case-insensitive metadata columns and column pinning.

Packaging, Docker, Dependency Updates, Specs, and Docs

Layer / File(s) Summary
switchyard-vendored rename and langchain deps
plugins/nemo-switchyard/..., packages/nemo_platform/pyproject.toml, plugins/nemo-guardrails/pyproject.toml, pyproject.toml, NOTICE, third_party/...
Renames vendored package to switchyard-vendored across all wiring; adds langchain deps to guardrails; removes diskcache; updates NOTICE and license files.
Docker base image bumps
docker-bake.hcl, docker/Dockerfile.bake, docker/base/Dockerfile.nmp-python-base
Bumps DISTROLESS_BASE to 3.11-v4.0.8 and Python slim to 3.11.15-slim-bookworm.
Release notes, config reference, and process docs
docs/about/release-notes/current-release.mdx, docs/set-up/config-reference.mdx, process.md, public-images.md, ghcr-mirroring-issue.md, container.md
Updates v0.2.0 release notes; adds config-reference entry for include_job_logs_in_diagnostics; adds process/image-distribution notes.
Implementation plans
plans/2026-06-03-*.md
Adds Helm chart kube E2E, jobs pause/resume, and jobs persistent storage implementation plans.
Architecture and auth spec documents
spec/*.md
Adds specs for jobs local/remote unification, subprocess execution resolution, runtime availability, multi-IdP, machine auth, NVAPI gateway, OIDC scope mapping, plugin service authz, core role grants, and related follow-on specs.

Possibly related PRs

  • NVIDIA-NeMo/nemo-platform#398: Implements the same E2E config-hash pooling, e2e_config marker, services_pool.py, auth-aware job tests, jobs diagnostics module, and workspace-scoped task listing fixes as this PR.
  • NVIDIA-NeMo/nemo-platform#422: Implements the Studio Code Agent pop-out top-bar chat, shared ClaudeCodeChatProvider/runtime flow, ClaudeCodeTopBarChat in GlobalNav, and PageLayout provider wiring that this PR includes.
  • NVIDIA-NeMo/nemo-platform#434: Renames the vendored switchyard package to switchyard-vendored and updates the same pyproject.toml bundle/dependency wiring changed here.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.33% 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 Title accurately summarizes the main change: consolidating four Studio delete modals into a generic BulkDeleteModal.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch astd-232-merge-near-identical-delete-modals-into-one-generic

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

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/artifacts.ts (1)

429-435: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

studio_linkjob detection diverges from the backend.

The live (TS) path only checks input.destination, but buildStudioLinkHrefFromInput above and the Python history parser (coding_agent_artifacts.py) also fall back to page/resource_type. A studio_link carrying page: "job" records a job artifact in history but not in the live stream, so the artifacts pane differs before vs. after reload.

Align with the destination resolution used elsewhere
   if ((toolName === 'studio_link' || toolName.endsWith('__studio_link')) && isRecord(input)) {
-    const destination = getString(input.destination);
+    const destination =
+      getString(input.destination) ?? getString(input.page) ?? getString(input.resource_type);
     const label = getString(input.label) ?? destination;
     const href = buildStudioLinkHrefFromInput(input, artifacts.workspace);
     if (label) appendLink(artifacts, { label, destination, href });
     if (destination === 'job') recordJobArtifact(artifacts, input);
   }
🤖 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 `@web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/artifacts.ts`
around lines 429 - 435, The live artifact handling in artifacts.ts is only
checking input.destination for `studio_link` job detection, which diverges from
the shared destination resolution used by buildStudioLinkHrefFromInput and the
Python history parser. Update the `studio_link` branch to resolve the target
with the same fallback order as the existing href logic (including `page` and
`resource_type`) before deciding whether to call `recordJobArtifact`, so live
stream and history produce the same job artifact behavior.
🟡 Minor comments (20)
web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/context/ClaudeCodeChatProvider.tsx-83-86 (1)

83-86: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

startNewChat leaves loadStatus stuck on 'loading'.

If a session load is in flight (loadStatus === 'loading'), startNewChat nulls requestedSessionIdRef so the pending fetch hits the stale guard (Line 64/73) and returns before setLoadStatus('idle'). The status never clears, so any consumer spinner stays up.

Proposed fix
 const startNewChat = useCallback(() => {
   requestedSessionIdRef.current = null;
+  setLoadStatus('idle');
   handleReset();
 }, [handleReset]);
🤖 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
`@web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/context/ClaudeCodeChatProvider.tsx`
around lines 83 - 86, `startNewChat` in `ClaudeCodeChatProvider` can leave
`loadStatus` stuck on `'loading'` when it clears `requestedSessionIdRef` before
an in-flight load finishes. Update `startNewChat` and the related load/reset
flow so any pending request still drives `setLoadStatus('idle')` on cancellation
or stale-guard exit, and make sure the status is cleared even when `handleReset`
is called while a session fetch is active.
services/core/jobs/tests/controllers/test_subprocess_backend.py-314-333 (1)

314-333: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Leaked subprocess. _schedule_without_otel_export starts a real sleep 10, then pop(key) drops metadata without terminating it. Process lingers post-test. Terminate before pop, or use a short-lived command.

🤖 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/core/jobs/tests/controllers/test_subprocess_backend.py` around lines
314 - 333, The test in
test_sync_uses_persisted_task_when_local_metadata_is_missing leaks a real
subprocess because _schedule_without_otel_export starts the sleep command and
_process_registry.pop removes tracking without stopping it. Update the test to
terminate the subprocess before removing the registry entry, or replace the
long-running command with a short-lived one; use _subprocess_backend,
_schedule_without_otel_export, and SubprocessProcessKey to locate the affected
setup.
services/core/jobs/src/nmp/core/jobs/controllers/backends/subprocess.py-374-374 (1)

374-374: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Mixed tz-aware/naive comparison can raise TypeError.

datetime.datetime.min is naive. If any task lacks both timestamps while others carry tz-aware ones, max compares aware vs naive and throws. Use a tz-aware sentinel.

Proposed fix
-        latest_task = max(tasks.data, key=lambda task: task.updated_at or task.created_at or datetime.datetime.min)
+        latest_task = max(
+            tasks.data,
+            key=lambda task: task.updated_at
+            or task.created_at
+            or datetime.datetime.min.replace(tzinfo=datetime.timezone.utc),
+        )
🤖 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/core/jobs/src/nmp/core/jobs/controllers/backends/subprocess.py` at
line 374, The latest_task selection in subprocess.py can raise a TypeError when
max() compares tz-aware timestamps against the naive datetime.datetime.min
fallback. Update the lambda used in the latest_task assignment to use a
timezone-aware sentinel that matches the timestamp types being compared, so
task.updated_at and task.created_at always fall back to a comparable aware
datetime.
web/packages/studio/src/components/dataViews/SafeSynthesizerJobsDataView/index.tsx-72-85 (1)

72-85: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Invalid jobs are silently dropped and partial failures resolve as success.

valid discards jobs missing workspace/name. If every selected job is invalid, Promise.all([]) resolves, the modal reports success and clears selection without deleting anything. Also, Promise.all rejects on the first failure while other in-flight deletes keep running, so only one error surfaces and successfully-deleted rows are silently removed.

Consider Promise.allSettled (as the dataset flow already does via useMutateMany) and aggregating errors.

🤖 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
`@web/packages/studio/src/components/dataViews/SafeSynthesizerJobsDataView/index.tsx`
around lines 72 - 85, The bulk delete flow in handleDeleteJobs is treating
missing workspace/name entries as a silent success and letting Promise.all
short-circuit on the first delete failure. Update handleDeleteJobs in
SafeSynthesizerJobsDataView to explicitly handle invalid jobs instead of
dropping them, and switch the mutation fan-out to Promise.allSettled so every
delete attempt completes and failures can be aggregated. Then surface a combined
error from deleteJobMutation.mutateAsync results rather than resolving success
when nothing was deleted.
e2e/services_pool.py-25-25 (1)

25-25: 📐 Maintainability & Code Quality | 🟡 Minor

Avoid _pytest.nodes.Node Use pytest.Module here; the only caller passes a module, so the internal pytest type is unnecessary.

🤖 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 `@e2e/services_pool.py` at line 25, The type annotation in the services pool
helper is using the internal pytest symbol `_pytest.nodes.Node` unnecessarily.
Update the relevant import and any related type hints in `services_pool` to use
`pytest.Module` instead, since the only caller passes a module and this keeps
the API on public pytest types.
plans/2026-06-03-jobs-persistent-storage-plan.md-16-21 (1)

16-21: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use repo-relative links instead of local filesystem paths.

These /Users/... references won’t work outside your machine and make the plan harder to share. Replace them with repo-relative links.

🤖 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 `@plans/2026-06-03-jobs-persistent-storage-plan.md` around lines 16 - 21, The
plan currently uses local filesystem links in the referenced files, which makes
it hard to share and navigate. Update the linked items in this section to use
repo-relative paths instead of /Users/... locations, keeping the same target
files and tests such as test_jobs.py, docker.py, endpoints.py, schemas.py,
test_docker_backend.py, and test_jobs_persistent_storage.py.
spec/jobs-local-remote-unification-spec.md-35-37 (1)

35-37: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use repo-relative links instead of local filesystem paths.

These /Users/... source links are machine-specific and won’t resolve for other contributors. Replace them with repo-relative paths.

🤖 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 `@spec/jobs-local-remote-unification-spec.md` around lines 35 - 37, The
document currently uses machine-specific source links, so update the related
entries to use repo-relative paths instead of local filesystem paths. Adjust the
affected markdown in the spec so the references for local/remote target
selection, subprocess inheritance, and task/job storage point to paths within
the repository, using the existing link targets in this spec as the guide.
spec/subprocess-first-class-execution-resolution-spec.md-33-37 (1)

33-37: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use repo-relative links instead of local filesystem paths.

These /Users/... source links are machine-specific and won’t resolve for other contributors. Replace them with repo-relative paths.

🤖 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 `@spec/subprocess-first-class-execution-resolution-spec.md` around lines 33 -
37, The affected links are using machine-specific absolute paths, so update the
references in the spec to repo-relative paths instead. Replace the /Users/...
style link with the corresponding repository-relative location for the jobs
endpoint code, and keep the reference tied to the relevant symbols around the
Jobs API ingress rewrite and the run_local/submit_remote execution paths so the
link works for all contributors.
plans/2026-06-03-helm-chart-kube-e2e-plan.md-3-9 (1)

3-9: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use repo-relative links instead of local filesystem paths.

These /Users/... references are machine-specific and won’t resolve for other contributors. Switch them to repo-relative paths.

🤖 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 `@plans/2026-06-03-helm-chart-kube-e2e-plan.md` around lines 3 - 9, The plan
text uses machine-specific /Users/... links that will not work for other
contributors. Replace every local filesystem reference with repo-relative links,
especially the mentions of Makefile, e2e/conftest.py, docker-bake.hcl, and the
archived Platform-Deploy chart path, so the document is portable and points to
locations within this repository or clearly relative external references.
plans/2026-06-03-jobs-pause-resume-plan.md-16-21 (1)

16-21: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use repo-relative links instead of local filesystem paths.

These /Users/... references won’t work outside your machine and make the plan harder to share. Replace them with repo-relative links.

🤖 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 `@plans/2026-06-03-jobs-pause-resume-plan.md` around lines 16 - 21, The plan
currently uses local /Users/... links, which are not portable or shareable.
Update the referenced items to use repo-relative links instead, preserving the
same targets in the jobs pause/resume plan and its test/backend/dispatcher
references so they work for everyone across the repo.
container.md-1-1 (1)

1-1: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Turn this into a structured doc page.

This reads like internal brainstorming, not publishable documentation: no heading, no sectioning, and lots of first-person narration. Split it into a proper explanation/reference page or keep it in a plan.

As per coding guidelines, **/*.{md,rst} content should fit one Diataxis quadrant; this doesn't yet map cleanly.

🤖 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 `@container.md` at line 1, The current content is unstructured brainstorming
and should be converted into a proper documentation page with a clear Diataxis
fit instead of remaining as a freeform note. Refactor the text into a single-doc
structure with a title, short introduction, and distinct sections that explain
the concepts and relationships among jobs service, backend, executor, profile,
provider, and platform config, using neutral third-person language instead of
first-person narration. If it cannot be made into a publishable
explanation/reference page, move it into a plan or internal note instead of
keeping it in the main docs.

Source: Coding guidelines

spec/jobs-backed-local-run-ux-and-progressive-start-spec.md-14-14 (1)

14-14: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Remove the machine-local path from the doc.

That link only works on your machine and leaks local environment details. Replace it with a repo-relative reference or plain code text.

🤖 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 `@spec/jobs-backed-local-run-ux-and-progressive-start-spec.md` at line 14, The
doc currently includes a machine-specific link to run_local(...) in
scheduler.py, which leaks local environment details and won’t work for others.
Update the reference in the spec to use either a repo-relative path or plain
code text, and keep the mention anchored to the run_local(...) path in the
scheduler module without any absolute local filesystem details.
spec/caller-execution-hints-and-profile-plumbing-spec.md-34-42 (1)

34-42: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Replace the absolute source paths.

These /Users/... links will be broken outside your workstation. Use repo-relative paths or plain code references instead.

🤖 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 `@spec/caller-execution-hints-and-profile-plumbing-spec.md` around lines 34 -
42, The spec currently embeds absolute /Users/... source links that will break
outside your workstation; replace them with repo-relative paths or plain code
references. Update the references in the caller execution hints section to point
to the relevant symbols like scheduler.py, add_job_routes(...), and the Jobs
schema for platform_spec without using machine-specific paths.
spec/jobs-runtime-availability-and-capabilities-spec.md-30-34 (1)

30-34: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Replace the absolute source paths.

These /Users/... links will be broken outside your workstation. Use repo-relative paths or plain code references instead.

🤖 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 `@spec/jobs-runtime-availability-and-capabilities-spec.md` around lines 30 -
34, Replace the absolute source links in the jobs runtime spec with
repo-relative paths or plain symbol references so they work outside your
workstation; update the listed references in the spec text to point at the
relevant symbols like validate_docker_available, the platform config downgrade
logic in config.py, Jobs config/profile construction, default backend config
selection, and the customization contributor instead of /Users/... paths.
spec/nvapi-authentication-gateway-spec.md-450-450 (1)

450-450: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Rename the second Recommendation heading.
This duplicates the earlier section title and will collide in generated anchors / lint checks.

🤖 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 `@spec/nvapi-authentication-gateway-spec.md` at line 450, The second
Recommendation heading duplicates the earlier section title and should be
renamed to a unique section name. Update the repeated markdown heading in the
spec so it has a distinct title, preserving the surrounding section content and
keeping anchor generation and lint checks unambiguous.

Source: Linters/SAST tools

public-images.md-1-1 (1)

1-1: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Start the page with an H1.
This page begins with ##, so markdownlint will flag it and the doc has no top-level title.

🤖 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 `@public-images.md` at line 1, The document currently starts with a
second-level heading instead of a top-level title, which will trigger
markdownlint and leaves the page without a proper title. Update the opening
heading in public-images.md to an H1 and keep the rest of the section hierarchy
unchanged, using the existing “Problem Statement” heading as the page’s
top-level entry point.

Source: Linters/SAST tools

public-images.md-13-26 (1)

13-26: 📐 Maintainability & Code Quality | 🟡 Minor

public-images.md:13-26 — Soften the GHCR limits claim. GHCR public packages are free and support anonymous pulls, but the docs don’t promise “unlimited” rate limits, and GitHub doesn’t publish a specific anonymous-pull quota. Keep the 10 GB per-layer limit only with an official citation.

🤖 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 `@public-images.md` around lines 13 - 26, The GHCR section in public-images.md
overstates the public pull and pricing limits, so soften the language in the
“Optimize GitHub Container Registry (GHCR)” guidance. Keep the documented 10 GB
per-layer constraint, but replace “free and unlimited” and any implied
anonymous-pull quota claims with a more cautious statement that public packages
support anonymous pulls and GitHub does not publish a specific public pull
limit. Ensure the wording in the GHCR bullets stays aligned with the rest of the
section and cites only the confirmed limit details.
spec/permissionless-authenticated-routes-spec.md-50-88 (1)

50-88: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the same caller taxonomy as the main authz spec.

USER/WORKLOAD is not defined anywhere else in this stack, while plugin-service-authz-spec.md uses ANON/PRINCIPAL/SERVICE_PRINCIPAL. Rewriting this option in the same terms will make the tradeoff easier to compare and implement.

🤖 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 `@spec/permissionless-authenticated-routes-spec.md` around lines 50 - 88,
Rewrite the options in this spec to use the same caller taxonomy as the main
authz spec: replace the `USER`/`WORKLOAD` terms in the option descriptions with
`ANON`/`PRINCIPAL`/`SERVICE_PRINCIPAL` so the authenticated-route behavior is
expressed consistently with `plugin-service-authz-spec.md` and easier to compare
across specs.
spec/service-role-visibility-and-bindability-spec.md-9-15 (1)

9-15: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Don't attribute role metadata to the v1 authz spec.

The main authz spec currently stops at permissions and path rules; roles are still a separate follow-up track. Calling roles part of that spec blurs the dependency order and makes the rollout harder to read.

🤖 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 `@spec/service-role-visibility-and-bindability-spec.md` around lines 9 - 15,
The service authz spec should stay scoped to permissions, service-scoped roles,
and path rules, without implying role visibility or bindability metadata is part
of v1. Update the wording in the authz/spec documentation to keep roles clearly
as a separate follow-up track and avoid attributing role metadata to the main
spec, using the spec section that mentions permissions, service-scoped roles,
and path rules as the place to adjust.
spec/plugin-service-authz-spec.md-132-140 (1)

132-140: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clarify the empty-scope representation.

scopes is nullable in the model, but the spec never says whether None and [] mean the same thing. That ambiguity will leak into validation and serialization, so pick one representation or define the difference explicitly.

Also applies to: 239-247

🤖 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 `@spec/plugin-service-authz-spec.md` around lines 132 - 140, Clarify the
meaning of PathRule.scopes by explicitly defining whether None and an empty list
are equivalent or distinct, and update the spec consistently so validation and
serialization use the same representation. Adjust the PathRule model description
and any related authz sections that reference scopes so the behavior is
unambiguous across callers and permissions handling.
🧹 Nitpick comments (5)
web/packages/studio/src/routes/FilesetListRoute/DatasetBulkDeleteModal/index.tsx (1)

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

Avoid any in the cloneElement cast.

Guidelines forbid any. Type the element to its injected prop instead; this also drops the eslint-disable. Note cloneElement overrides any existing onClick on slotTrigger.

♻️ Proposed typing
-  const trigger = isValidElement(slotTrigger) ? (
-    // eslint-disable-next-line `@typescript-eslint/no-explicit-any`
-    cloneElement(slotTrigger as React.ReactElement<any>, { onClick: openTrigger })
+  const trigger = isValidElement(slotTrigger) ? (
+    cloneElement(slotTrigger as React.ReactElement<{ onClick?: React.MouseEventHandler }>, {
+      onClick: openTrigger,
+    })
As per coding guidelines: "Never use `any` type in TypeScript."
🤖 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
`@web/packages/studio/src/routes/FilesetListRoute/DatasetBulkDeleteModal/index.tsx`
around lines 49 - 57, The `DatasetBulkDeleteModal` trigger uses an `any` cast in
the `cloneElement` call, which violates the typing guidelines and forces an
eslint disable. Update the `trigger` logic to type `slotTrigger` as a React
element with the injected `onClick` prop instead of `React.ReactElement<any>`,
and remove the eslint suppression. Keep in mind `cloneElement` will replace any
existing `onClick` on `slotTrigger`, so ensure the intended handler is the one
passed through `openTrigger`.

Source: Coding guidelines

web/packages/studio/src/components/evaluation/Configurations/ConfigurationDetailsPanel.tsx (1)

44-52: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Inconsistent missing-value placeholder.

configName/configId default to '-', but sibling panels (Metric/LLMJudge) pass undefined and rely on KVPair's '—' default. Drop the || '-' so all eval panels render the same placeholder.

♻️ Align with KVPair default
-  const configName = config.name || '-';
-  const configId = config.id || '-';
+  const configName = config.name;
+  const configId = config.id;
🤖 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
`@web/packages/studio/src/components/evaluation/Configurations/ConfigurationDetailsPanel.tsx`
around lines 44 - 52, The ConfigurationDetailsPanel component is overriding
missing values with a different placeholder than the other eval panels. Update
the configName and configId handling in ConfigurationDetailsPanel so it passes
through undefined instead of forcing '-', letting KVPair apply its standard '—'
default for consistency with the Metric and LLMJudge panels.
spec/jobs-runtime-availability-and-capabilities-spec.md (1)

194-203: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Define snapshot freshness before clients cache availability.

“Query Jobs once” is too loose for a dynamic availability model. Specify whether the snapshot is request-scoped, process-scoped, or revalidated before dispatch; otherwise clients will make stale selection decisions.

🤖 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 `@spec/jobs-runtime-availability-and-capabilities-spec.md` around lines 194 -
203, Clarify the freshness semantics for the availability snapshot in the Jobs
flow described in the spec: explicitly state whether the set returned by Jobs is
request-scoped, process-scoped, or must be revalidated before dispatch. Update
the section around the shared deterministic resolver and the Jobs
validation/dispatch steps to define when clients may cache the returned
availability and when they must refresh it, so the resolver behavior is
unambiguous.
process.md (1)

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

Add a top-level heading and split the memo.

This reads like an internal note, not a docs page. Give it a title and sectioned structure so it matches the repo's markdown conventions. As per coding guidelines, markdown content should fit one doc type and stay scannable.

🤖 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 `@process.md` around lines 1 - 25, This memo needs a top-level heading and
clearer sectioning so it reads like a proper docs page instead of an internal
note. Update the markdown in process.md by adding a title and splitting the
current freeform narrative into concise sections that group the core ideas (for
example, delivery intake, timeline/milestones, cycle planning, ownership,
process support, and quality gates) while keeping the tone consistent with the
existing proposal. Use the existing topics in the body as the anchors for the
new structure.

Sources: Coding guidelines, Linters/SAST tools

spec/brian-auth-call.md (1)

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

Move this transcript out of spec/ or rewrite it as a summary.

This is one long transcription dump, so it is hard to scan and doesn't fit the repo's markdown conventions. A titled summary with sections would be far more usable.

🤖 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 `@spec/brian-auth-call.md` at line 1, This file is a raw transcript dump under
spec and should be replaced with a concise, titled summary or moved out of spec
entirely. Rewrite it as a structured markdown note with sections for the auth
goals, current state, gaps, next steps, and follow-ups, using the same
`spec/brian-auth-call` document as the place to locate and update it. Keep only
the actionable takeaways from the conversation and remove the verbatim dialogue
so the document matches the repo’s markdown conventions and is easier to scan.

Sources: Coding guidelines, Linters/SAST tools

🤖 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 `@e2e/configs/local-subprocess.yaml`:
- Line 9: The base_url in the local-subprocess config is set to a
non-client-reachable host, which can break connections. Update the configuration
entry for base_url in local-subprocess.yaml to use a reachable address such as
http://localhost:8080 instead of 0.0.0.0, keeping the change scoped to the
config value.

In `@script/submit-hello-world-job.sh`:
- Around line 33-61: The JSON payload in submit-hello-world-job.sh is being
built by directly interpolating shell variables, which can break when values
contain quotes or newlines. Replace the heredoc-based construction around the
payload_file write with a proper JSON serializer so WORKSPACE, JOB_NAME,
MESSAGE, EXECUTION_PROFILE, and IMAGE are safely escaped. Keep the existing
payload structure and update the script’s payload generation logic accordingly.

In `@spec/options-for-multi-idp.md`:
- Around line 31-39: The spec currently links to absolute local checkout paths
under /Users/rsadler, which makes the references break outside that machine and
exposes a local filesystem path. Update the references in this section to use
repo-relative links or plain code symbols for OIDCConfig, AuthConfig, and the
related fields in base.py so the links work in any clone.

---

Outside diff comments:
In `@web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/artifacts.ts`:
- Around line 429-435: The live artifact handling in artifacts.ts is only
checking input.destination for `studio_link` job detection, which diverges from
the shared destination resolution used by buildStudioLinkHrefFromInput and the
Python history parser. Update the `studio_link` branch to resolve the target
with the same fallback order as the existing href logic (including `page` and
`resource_type`) before deciding whether to call `recordJobArtifact`, so live
stream and history produce the same job artifact behavior.

---

Minor comments:
In `@container.md`:
- Line 1: The current content is unstructured brainstorming and should be
converted into a proper documentation page with a clear Diataxis fit instead of
remaining as a freeform note. Refactor the text into a single-doc structure with
a title, short introduction, and distinct sections that explain the concepts and
relationships among jobs service, backend, executor, profile, provider, and
platform config, using neutral third-person language instead of first-person
narration. If it cannot be made into a publishable explanation/reference page,
move it into a plan or internal note instead of keeping it in the main docs.

In `@e2e/services_pool.py`:
- Line 25: The type annotation in the services pool helper is using the internal
pytest symbol `_pytest.nodes.Node` unnecessarily. Update the relevant import and
any related type hints in `services_pool` to use `pytest.Module` instead, since
the only caller passes a module and this keeps the API on public pytest types.

In `@plans/2026-06-03-helm-chart-kube-e2e-plan.md`:
- Around line 3-9: The plan text uses machine-specific /Users/... links that
will not work for other contributors. Replace every local filesystem reference
with repo-relative links, especially the mentions of Makefile, e2e/conftest.py,
docker-bake.hcl, and the archived Platform-Deploy chart path, so the document is
portable and points to locations within this repository or clearly relative
external references.

In `@plans/2026-06-03-jobs-pause-resume-plan.md`:
- Around line 16-21: The plan currently uses local /Users/... links, which are
not portable or shareable. Update the referenced items to use repo-relative
links instead, preserving the same targets in the jobs pause/resume plan and its
test/backend/dispatcher references so they work for everyone across the repo.

In `@plans/2026-06-03-jobs-persistent-storage-plan.md`:
- Around line 16-21: The plan currently uses local filesystem links in the
referenced files, which makes it hard to share and navigate. Update the linked
items in this section to use repo-relative paths instead of /Users/...
locations, keeping the same target files and tests such as test_jobs.py,
docker.py, endpoints.py, schemas.py, test_docker_backend.py, and
test_jobs_persistent_storage.py.

In `@public-images.md`:
- Line 1: The document currently starts with a second-level heading instead of a
top-level title, which will trigger markdownlint and leaves the page without a
proper title. Update the opening heading in public-images.md to an H1 and keep
the rest of the section hierarchy unchanged, using the existing “Problem
Statement” heading as the page’s top-level entry point.
- Around line 13-26: The GHCR section in public-images.md overstates the public
pull and pricing limits, so soften the language in the “Optimize GitHub
Container Registry (GHCR)” guidance. Keep the documented 10 GB per-layer
constraint, but replace “free and unlimited” and any implied anonymous-pull
quota claims with a more cautious statement that public packages support
anonymous pulls and GitHub does not publish a specific public pull limit. Ensure
the wording in the GHCR bullets stays aligned with the rest of the section and
cites only the confirmed limit details.

In `@services/core/jobs/src/nmp/core/jobs/controllers/backends/subprocess.py`:
- Line 374: The latest_task selection in subprocess.py can raise a TypeError
when max() compares tz-aware timestamps against the naive datetime.datetime.min
fallback. Update the lambda used in the latest_task assignment to use a
timezone-aware sentinel that matches the timestamp types being compared, so
task.updated_at and task.created_at always fall back to a comparable aware
datetime.

In `@services/core/jobs/tests/controllers/test_subprocess_backend.py`:
- Around line 314-333: The test in
test_sync_uses_persisted_task_when_local_metadata_is_missing leaks a real
subprocess because _schedule_without_otel_export starts the sleep command and
_process_registry.pop removes tracking without stopping it. Update the test to
terminate the subprocess before removing the registry entry, or replace the
long-running command with a short-lived one; use _subprocess_backend,
_schedule_without_otel_export, and SubprocessProcessKey to locate the affected
setup.

In `@spec/caller-execution-hints-and-profile-plumbing-spec.md`:
- Around line 34-42: The spec currently embeds absolute /Users/... source links
that will break outside your workstation; replace them with repo-relative paths
or plain code references. Update the references in the caller execution hints
section to point to the relevant symbols like scheduler.py, add_job_routes(...),
and the Jobs schema for platform_spec without using machine-specific paths.

In `@spec/jobs-backed-local-run-ux-and-progressive-start-spec.md`:
- Line 14: The doc currently includes a machine-specific link to run_local(...)
in scheduler.py, which leaks local environment details and won’t work for
others. Update the reference in the spec to use either a repo-relative path or
plain code text, and keep the mention anchored to the run_local(...) path in the
scheduler module without any absolute local filesystem details.

In `@spec/jobs-local-remote-unification-spec.md`:
- Around line 35-37: The document currently uses machine-specific source links,
so update the related entries to use repo-relative paths instead of local
filesystem paths. Adjust the affected markdown in the spec so the references for
local/remote target selection, subprocess inheritance, and task/job storage
point to paths within the repository, using the existing link targets in this
spec as the guide.

In `@spec/jobs-runtime-availability-and-capabilities-spec.md`:
- Around line 30-34: Replace the absolute source links in the jobs runtime spec
with repo-relative paths or plain symbol references so they work outside your
workstation; update the listed references in the spec text to point at the
relevant symbols like validate_docker_available, the platform config downgrade
logic in config.py, Jobs config/profile construction, default backend config
selection, and the customization contributor instead of /Users/... paths.

In `@spec/nvapi-authentication-gateway-spec.md`:
- Line 450: The second Recommendation heading duplicates the earlier section
title and should be renamed to a unique section name. Update the repeated
markdown heading in the spec so it has a distinct title, preserving the
surrounding section content and keeping anchor generation and lint checks
unambiguous.

In `@spec/permissionless-authenticated-routes-spec.md`:
- Around line 50-88: Rewrite the options in this spec to use the same caller
taxonomy as the main authz spec: replace the `USER`/`WORKLOAD` terms in the
option descriptions with `ANON`/`PRINCIPAL`/`SERVICE_PRINCIPAL` so the
authenticated-route behavior is expressed consistently with
`plugin-service-authz-spec.md` and easier to compare across specs.

In `@spec/plugin-service-authz-spec.md`:
- Around line 132-140: Clarify the meaning of PathRule.scopes by explicitly
defining whether None and an empty list are equivalent or distinct, and update
the spec consistently so validation and serialization use the same
representation. Adjust the PathRule model description and any related authz
sections that reference scopes so the behavior is unambiguous across callers and
permissions handling.

In `@spec/service-role-visibility-and-bindability-spec.md`:
- Around line 9-15: The service authz spec should stay scoped to permissions,
service-scoped roles, and path rules, without implying role visibility or
bindability metadata is part of v1. Update the wording in the authz/spec
documentation to keep roles clearly as a separate follow-up track and avoid
attributing role metadata to the main spec, using the spec section that mentions
permissions, service-scoped roles, and path rules as the place to adjust.

In `@spec/subprocess-first-class-execution-resolution-spec.md`:
- Around line 33-37: The affected links are using machine-specific absolute
paths, so update the references in the spec to repo-relative paths instead.
Replace the /Users/... style link with the corresponding repository-relative
location for the jobs endpoint code, and keep the reference tied to the relevant
symbols around the Jobs API ingress rewrite and the run_local/submit_remote
execution paths so the link works for all contributors.

In
`@web/packages/studio/src/components/dataViews/SafeSynthesizerJobsDataView/index.tsx`:
- Around line 72-85: The bulk delete flow in handleDeleteJobs is treating
missing workspace/name entries as a silent success and letting Promise.all
short-circuit on the first delete failure. Update handleDeleteJobs in
SafeSynthesizerJobsDataView to explicitly handle invalid jobs instead of
dropping them, and switch the mutation fan-out to Promise.allSettled so every
delete attempt completes and failures can be aggregated. Then surface a combined
error from deleteJobMutation.mutateAsync results rather than resolving success
when nothing was deleted.

In
`@web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/context/ClaudeCodeChatProvider.tsx`:
- Around line 83-86: `startNewChat` in `ClaudeCodeChatProvider` can leave
`loadStatus` stuck on `'loading'` when it clears `requestedSessionIdRef` before
an in-flight load finishes. Update `startNewChat` and the related load/reset
flow so any pending request still drives `setLoadStatus('idle')` on cancellation
or stale-guard exit, and make sure the status is cleared even when `handleReset`
is called while a session fetch is active.

---

Nitpick comments:
In `@process.md`:
- Around line 1-25: This memo needs a top-level heading and clearer sectioning
so it reads like a proper docs page instead of an internal note. Update the
markdown in process.md by adding a title and splitting the current freeform
narrative into concise sections that group the core ideas (for example, delivery
intake, timeline/milestones, cycle planning, ownership, process support, and
quality gates) while keeping the tone consistent with the existing proposal. Use
the existing topics in the body as the anchors for the new structure.

In `@spec/brian-auth-call.md`:
- Line 1: This file is a raw transcript dump under spec and should be replaced
with a concise, titled summary or moved out of spec entirely. Rewrite it as a
structured markdown note with sections for the auth goals, current state, gaps,
next steps, and follow-ups, using the same `spec/brian-auth-call` document as
the place to locate and update it. Keep only the actionable takeaways from the
conversation and remove the verbatim dialogue so the document matches the repo’s
markdown conventions and is easier to scan.

In `@spec/jobs-runtime-availability-and-capabilities-spec.md`:
- Around line 194-203: Clarify the freshness semantics for the availability
snapshot in the Jobs flow described in the spec: explicitly state whether the
set returned by Jobs is request-scoped, process-scoped, or must be revalidated
before dispatch. Update the section around the shared deterministic resolver and
the Jobs validation/dispatch steps to define when clients may cache the returned
availability and when they must refresh it, so the resolver behavior is
unambiguous.

In
`@web/packages/studio/src/components/evaluation/Configurations/ConfigurationDetailsPanel.tsx`:
- Around line 44-52: The ConfigurationDetailsPanel component is overriding
missing values with a different placeholder than the other eval panels. Update
the configName and configId handling in ConfigurationDetailsPanel so it passes
through undefined instead of forcing '-', letting KVPair apply its standard '—'
default for consistency with the Metric and LLMJudge panels.

In
`@web/packages/studio/src/routes/FilesetListRoute/DatasetBulkDeleteModal/index.tsx`:
- Around line 49-57: The `DatasetBulkDeleteModal` trigger uses an `any` cast in
the `cloneElement` call, which violates the typing guidelines and forces an
eslint disable. Update the `trigger` logic to type `slotTrigger` as a React
element with the injected `onClick` prop instead of `React.ReactElement<any>`,
and remove the eslint suppression. Keep in mind `cloneElement` will replace any
existing `onClick` on `slotTrigger`, so ensure the intended handler is the one
passed through `openTrigger`.
🪄 Autofix (Beta)

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: ba64c6b9-5435-41aa-bd8f-b81c3a63f842

📥 Commits

Reviewing files that changed from the base of the PR and between ed04d87 and b8466a1.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (133)
  • .github/workflows/docker-build-dispatch.yml
  • .github/workflows/docker-build-liveness.yml
  • .github/workflows/docker-build-trigger.yml
  • NOTICE
  • container.md
  • docker-bake.hcl
  • docker/Dockerfile.bake
  • docker/base/Dockerfile.nmp-python-base
  • docs/about/release-notes/current-release.mdx
  • docs/set-up/config-reference.mdx
  • e2e/configs/local-subprocess.yaml
  • e2e/conftest.py
  • e2e/services_pool.py
  • e2e/test_data_designer.py
  • e2e/test_jobs.py
  • e2e/test_jobs_auth.py
  • ghcr-mirroring-issue.md
  • packages/nemo_platform/pyproject.toml
  • packages/nmp_testing/src/nmp/testing/__init__.py
  • packages/nmp_testing/src/nmp/testing/utils.py
  • packages/nmp_testing/tests/unit/test_e2e_harness.py
  • plans/2026-06-03-helm-chart-kube-e2e-plan.md
  • plans/2026-06-03-jobs-pause-resume-plan.md
  • plans/2026-06-03-jobs-persistent-storage-plan.md
  • plugins/nemo-guardrails/pyproject.toml
  • plugins/nemo-switchyard/pyproject.toml
  • plugins/nemo-switchyard/vendor/switchyard/pyproject.toml
  • process.md
  • public-images.md
  • pyproject.toml
  • pytest.ini
  • script/Untitled
  • script/run-e2e-linux.sh
  • script/run-hello-world-jobs.sh
  • script/submit-hello-world-job.sh
  • services/core/jobs/src/nmp/core/jobs/api/v2/jobs/endpoints.py
  • services/core/jobs/src/nmp/core/jobs/app/dispatcher.py
  • services/core/jobs/src/nmp/core/jobs/config.py
  • services/core/jobs/src/nmp/core/jobs/controllers/backends/subprocess.py
  • services/core/jobs/src/nmp/core/jobs/controllers/diagnostics.py
  • services/core/jobs/src/nmp/core/jobs/controllers/reconciler.py
  • services/core/jobs/src/nmp/core/jobs/controllers/scheduler.py
  • services/core/jobs/tests/controllers/test_diagnostics.py
  • services/core/jobs/tests/controllers/test_reconciler.py
  • services/core/jobs/tests/controllers/test_scheduler.py
  • services/core/jobs/tests/controllers/test_subprocess_backend.py
  • services/core/jobs/tests/integration/test_task_auth_runtime.py
  • services/core/jobs/tests/test_dispatcher_cross_workspace.py
  • services/core/jobs/tests/test_timestamp_contracts.py
  • services/studio/src/nmp/studio/coding_agent_artifacts.py
  • services/studio/src/nmp/studio/coding_agent_mcp_tools.py
  • services/studio/src/nmp/studio/coding_agents.py
  • services/studio/tests/unit/test_coding_agents.py
  • spec/brian-auth-call-summary.md
  • spec/brian-auth-call.md
  • spec/caller-execution-hints-and-profile-plumbing-spec.md
  • spec/capability-vs-provider-execution-model-spec.md
  • spec/core-role-default-grants-spec.md
  • spec/daemon-group-local-jobs-follow-on-spec.md
  • spec/first-class-subprocess-provider-slack-draft.md
  • spec/jobs-backed-local-run-ux-and-progressive-start-spec.md
  • spec/jobs-local-remote-unification-spec.md
  • spec/jobs-runtime-availability-and-capabilities-spec.md
  • spec/machine-auth-authentication-and-authorization-spec.md
  • spec/nvapi-authentication-gateway-spec.md
  • spec/oidc-scope-and-claim-mapping-spec.md
  • spec/options-for-multi-idp.md
  • spec/permissionless-authenticated-routes-spec.md
  • spec/plugin-defined-scopes-spec.md
  • spec/plugin-service-authz-spec.md
  • spec/plugin-service-authz-ticket-description.md
  • spec/provider-backend-extensibility-spec.md
  • spec/service-role-visibility-and-bindability-spec.md
  • spec/subprocess-first-class-execution-resolution-spec.md
  • spec/trusted-probes-and-endpoints-spec.md
  • third_party/licenses.jsonl
  • third_party/osv-licenses.json
  • third_party/requirements-main.txt
  • web/packages/common/src/components/form/ControlledSearchableSelect/index.tsx
  • web/packages/studio/AGENTS.md
  • web/packages/studio/src/components/BulkDeleteModal/index.tsx
  • web/packages/studio/src/components/DatasetsTable/index.test.tsx
  • web/packages/studio/src/components/Layouts/GlobalNav/index.test.tsx
  • web/packages/studio/src/components/Layouts/GlobalNav/index.tsx
  • web/packages/studio/src/components/common/ReadOnlyField.tsx
  • web/packages/studio/src/components/dataViews/DataDesignerJobsDataView/DeleteJobModal.tsx
  • web/packages/studio/src/components/dataViews/DataDesignerJobsDataView/index.tsx
  • web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx
  • web/packages/studio/src/components/dataViews/ExperimentGroupDataView/useExperimentGroupExperiments.test.ts
  • web/packages/studio/src/components/dataViews/ExperimentGroupDataView/useExperimentGroupExperiments.ts
  • web/packages/studio/src/components/dataViews/SafeSynthesizerJobsDataView/DeleteJobModal.test.tsx
  • web/packages/studio/src/components/dataViews/SafeSynthesizerJobsDataView/DeleteJobModal.tsx
  • web/packages/studio/src/components/dataViews/SafeSynthesizerJobsDataView/index.tsx
  • web/packages/studio/src/components/evaluation/Configurations/ConfigurationDetailsPanel.tsx
  • web/packages/studio/src/components/evaluation/Configurations/LLMJudgeDisplay.tsx
  • web/packages/studio/src/components/evaluation/Configurations/MetricDisplay.tsx
  • web/packages/studio/src/components/evaluation/Configurations/TaskDisplay.tsx
  • web/packages/studio/src/components/evaluation/EvaluationModelSelect.tsx
  • web/packages/studio/src/components/filesets/FilesetFileExplorer/BulkDeleteModal/index.test.tsx
  • web/packages/studio/src/components/filesets/FilesetFileExplorer/BulkDeleteModal/index.tsx
  • web/packages/studio/src/components/filesets/FilesetFileExplorer/extractFilePathsFromDirectory.ts
  • web/packages/studio/src/index.css
  • web/packages/studio/src/routes/DashboardLandingRoute/index.test.tsx
  • web/packages/studio/src/routes/DashboardLandingRoute/index.tsx
  • web/packages/studio/src/routes/FilesetListRoute/DatasetBulkDeleteModal/index.test.tsx
  • web/packages/studio/src/routes/FilesetListRoute/DatasetBulkDeleteModal/index.tsx
  • web/packages/studio/src/routes/FilesetListRoute/index.test.tsx
  • web/packages/studio/src/routes/PageLayout/index.tsx
  • web/packages/studio/src/routes/SafeSynthesizerJobReportRoute/components/ScorePanels/DataPrivacyPanel.test.tsx
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/ClaudeCodeChatThread.tsx
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/ClaudeCodeHistoryPanel.test.tsx
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/ClaudeCodeLayout.tsx
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/ClaudeCodeTopBarChat.test.tsx
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/ClaudeCodeTopBarChat.tsx
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/activeSessionStorage.ts
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/api.test.ts
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/api.ts
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/artifacts.test.ts
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/artifacts.ts
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/context/ClaudeCodeChatProvider.test.tsx
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/context/ClaudeCodeChatProvider.tsx
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/context/useClaudeCodeChatContext.ts
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/historyPanel/ArtifactSections.tsx
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/historyPanel/ClaudeCodeArtifactsPane.tsx
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/historyPanel/helpers.ts
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/index.test.tsx
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/index.tsx
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/types.ts
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/useClaudeCodeChatRuntime.test.ts
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/useClaudeCodeChatRuntime.ts
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/useCustomAssistantChatRuntime.ts
  • web/packages/studio/src/routes/agents/ClaudeCodeChatRoute/util.test.ts
  • web/packages/studio/src/util/localStorage.ts
💤 Files with no reviewable changes (8)
  • .github/workflows/docker-build-dispatch.yml
  • web/packages/studio/src/components/dataViews/SafeSynthesizerJobsDataView/DeleteJobModal.test.tsx
  • web/packages/studio/src/components/dataViews/SafeSynthesizerJobsDataView/DeleteJobModal.tsx
  • web/packages/studio/src/components/common/ReadOnlyField.tsx
  • .github/workflows/docker-build-liveness.yml
  • .github/workflows/docker-build-trigger.yml
  • web/packages/studio/src/components/dataViews/DataDesignerJobsDataView/DeleteJobModal.tsx
  • third_party/licenses.jsonl


platform:
runtime: "none"
base_url: "http://0.0.0.0:8080"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use a client-reachable base URL.

Line 9 should use http://localhost:8080 (or a concrete host), not 0.0.0.0, to avoid client connection failures.

Proposed fix
-  base_url: "http://0.0.0.0:8080"
+  base_url: "http://localhost:8080"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
base_url: "http://0.0.0.0:8080"
base_url: "http://localhost:8080"
🤖 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 `@e2e/configs/local-subprocess.yaml` at line 9, The base_url in the
local-subprocess config is set to a non-client-reachable host, which can break
connections. Update the configuration entry for base_url in
local-subprocess.yaml to use a reachable address such as http://localhost:8080
instead of 0.0.0.0, keeping the change scoped to the config value.

Comment on lines +33 to +61
cat > "${payload_file}" <<EOF
{
"workspace": "${WORKSPACE}",
"name": "${JOB_NAME}",
"source": "hello-world",
"spec": {
"message": "${MESSAGE}"
},
"platform_spec": {
"steps": [
{
"name": "hello-world",
"executor": {
"provider": "cpu",
"profile": "${EXECUTION_PROFILE}",
"container": {
"image": "${IMAGE}",
"entrypoint": ["nemo-platform"],
"command": ["run", "task", "--task", "nmp.hello_world.tasks.hello_world"]
}
},
"config": {
"message": "${MESSAGE}"
}
}
]
}
}
EOF

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Stop interpolating shell vars directly into JSON.

Lines 33-61 can emit invalid JSON when values contain quotes/newlines. Build JSON with a serializer.

Proposed fix
-payload_file="$(mktemp)"
-trap 'rm -f "${payload_file}"' EXIT
-
-cat > "${payload_file}" <<EOF
-{
-  "workspace": "${WORKSPACE}",
-  "name": "${JOB_NAME}",
-  "source": "hello-world",
-  "spec": {
-    "message": "${MESSAGE}"
-  },
-  "platform_spec": {
-    "steps": [
-      {
-        "name": "hello-world",
-        "executor": {
-          "provider": "cpu",
-          "profile": "${EXECUTION_PROFILE}",
-          "container": {
-            "image": "${IMAGE}",
-            "entrypoint": ["nemo-platform"],
-            "command": ["run", "task", "--task", "nmp.hello_world.tasks.hello_world"]
-          }
-        },
-        "config": {
-          "message": "${MESSAGE}"
-        }
-      }
-    ]
-  }
-}
-EOF
+payload_file="$(mktemp)"
+trap 'rm -f "${payload_file}"' EXIT
+
+python3 - <<'PY' "${payload_file}" "${WORKSPACE}" "${JOB_NAME}" "${MESSAGE}" "${EXECUTION_PROFILE}" "${IMAGE}" "${PROJECT}"
+import json
+import sys
+
+path, workspace, job_name, message, profile, image, project = sys.argv[1:]
+payload = {
+    "workspace": workspace,
+    "name": job_name,
+    "source": "hello-world",
+    "spec": {"message": message},
+    "platform_spec": {
+        "steps": [
+            {
+                "name": "hello-world",
+                "executor": {
+                    "provider": "cpu",
+                    "profile": profile,
+                    "container": {
+                        "image": image,
+                        "entrypoint": ["nemo-platform"],
+                        "command": ["run", "task", "--task", "nmp.hello_world.tasks.hello_world"],
+                    },
+                },
+                "config": {"message": message},
+            }
+        ]
+    },
+}
+if project:
+    payload["project"] = project
+with open(path, "w", encoding="utf-8") as f:
+    json.dump(payload, f)
+PY
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cat > "${payload_file}" <<EOF
{
"workspace": "${WORKSPACE}",
"name": "${JOB_NAME}",
"source": "hello-world",
"spec": {
"message": "${MESSAGE}"
},
"platform_spec": {
"steps": [
{
"name": "hello-world",
"executor": {
"provider": "cpu",
"profile": "${EXECUTION_PROFILE}",
"container": {
"image": "${IMAGE}",
"entrypoint": ["nemo-platform"],
"command": ["run", "task", "--task", "nmp.hello_world.tasks.hello_world"]
}
},
"config": {
"message": "${MESSAGE}"
}
}
]
}
}
EOF
payload_file="$(mktemp)"
trap 'rm -f "${payload_file}"' EXIT
python3 - <<'PY' "${payload_file}" "${WORKSPACE}" "${JOB_NAME}" "${MESSAGE}" "${EXECUTION_PROFILE}" "${IMAGE}" "${PROJECT}"
import json
import sys
path, workspace, job_name, message, profile, image, project = sys.argv[1:]
payload = {
"workspace": workspace,
"name": job_name,
"source": "hello-world",
"spec": {"message": message},
"platform_spec": {
"steps": [
{
"name": "hello-world",
"executor": {
"provider": "cpu",
"profile": profile,
"container": {
"image": image,
"entrypoint": ["nemo-platform"],
"command": ["run", "task", "--task", "nmp.hello_world.tasks.hello_world"],
},
},
"config": {"message": message},
}
]
},
}
if project:
payload["project"] = project
with open(path, "w", encoding="utf-8") as f:
json.dump(payload, f)
PY
🤖 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 `@script/submit-hello-world-job.sh` around lines 33 - 61, The JSON payload in
submit-hello-world-job.sh is being built by directly interpolating shell
variables, which can break when values contain quotes or newlines. Replace the
heredoc-based construction around the payload_file write with a proper JSON
serializer so WORKSPACE, JOB_NAME, MESSAGE, EXECUTION_PROFILE, and IMAGE are
safely escaped. Keep the existing payload structure and update the script’s
payload generation logic accordingly.

Comment on lines +31 to +39
`OIDCConfig` models one provider: one `issuer`, one `client_id`, optional endpoint overrides, and one claim-mapping profile. See [packages/nmp_common/src/nmp/common/config/base.py](/Users/rsadler/src/nemo-platform/packages/nmp_common/src/nmp/common/config/base.py:89).

Relevant indicators:

- single `issuer`: [base.py](/Users/rsadler/src/nemo-platform/packages/nmp_common/src/nmp/common/config/base.py:97)
- single `client_id`: [base.py](/Users/rsadler/src/nemo-platform/packages/nmp_common/src/nmp/common/config/base.py:110)
- single `email_claim`, `groups_claim`, `subject_claim`: [base.py](/Users/rsadler/src/nemo-platform/packages/nmp_common/src/nmp/common/config/base.py:143)
- one nested `auth.oidc` object under `AuthConfig`: [base.py](/Users/rsadler/src/nemo-platform/packages/nmp_common/src/nmp/common/config/base.py:234)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Replace the absolute checkout paths.
These links point at /Users/rsadler/..., so they break in every other clone and leak a local filesystem path. Use repo-relative links or plain code references instead.

Also applies to: 703-718

🤖 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 `@spec/options-for-multi-idp.md` around lines 31 - 39, The spec currently links
to absolute local checkout paths under /Users/rsadler, which makes the
references break outside that machine and exposes a local filesystem path.
Update the references in this section to use repo-relative links or plain code
symbols for OIDCConfig, AuthConfig, and the related fields in base.py so the
links work in any clone.

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.

9 participants