Skip to content

feat(jobs): per-filter event cursors, multi-replica tests, lease docs (closes #1252) - #1254

Merged
dhyansraj merged 2 commits into
mainfrom
feat/1252-cursors-tests-docs
Jul 3, 2026
Merged

feat(jobs): per-filter event cursors, multi-replica tests, lease docs (closes #1252)#1254
dhyansraj merged 2 commits into
mainfrom
feat/1252-cursors-tests-docs

Conversation

@dhyansraj

@dhyansraj dhyansraj commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Summary

Completes #1252 (Phases 3-5; Phases 1+2 landed in #1253).

  • Per-filter event cursors (Phase 3): JobController tracks an independent cursor per canonical type-filter (sorted/deduped, trimmed, empty types dropped to match registry semantics; unfiltered is its own stream). Exactly-once within a stream, documented at-least-once across streams; interleaved gates with different filters can no longer permanently skip each other's events. Per-filter locks replace the global recv lock, so a 60s long-poll on one type doesn't block another — while same-filter calls stay serialized and monotonic. The epoch/supersession logic from feat(jobs): claim-epoch fencing and poll-liveness (Phases 1+2 of #1252) #1253 is untouched (verified byte-intact in review). The mock-vs-registry page contract is pinned by a dual-backend scenario table with exact path/query matching (the registry's own behavior is pinned Go-side), and the in-process mock now applies the registry's trim/drop-empties filter rule.
  • Multi-replica integration tests (Phase 4, new uc33_meshjob_replicas): two 2-replica scenarios from the field report — tc01 proves a quietly-gating handler polling recvEvent through 2× its lease window keeps a single claim (poll-liveness), and tc02 proves a genuinely-wedged handler is re-claimed with the stale owner fenced (epochs [1,2], exactly one surviving owner, job completes once). Assertions run against the registry job API with the epoch/attempt/transition evidence; timing margins sized for CI load.
  • Harness hygiene: the nine cold-JVM-sensitive tests (uc10 java-consumer LLM suite, uc06 java tracing, uc20 committee) now poll-until-registered with a 240s deadline instead of sleeping a fixed 25s; test-level budgets raised where the waits stack. These were the recurring registration flakes in full-suite runs — all nine pass under parallel-8 in this PR's gate run.
  • Docs (Phase 5): jobs man pages (base + TS/Java variants), environment.md, and docs/concepts/jobs.md now document the lease window derivation (max_duration, 300s default), what renews a lease (progress deltas AND executor recvEvent polls), multi-replica claiming and epoch fencing, per-runtime supersession surfaces, per-filter cursor semantics with checkpoint guidance, claimEpoch accessors, and the MCP_MESH_JOB_STALE_TIMEOUT reap-ceiling vs lease-window distinction. Every claim was verified against the implementation before writing.

Review Notes

Zero-context review applied (0 HIGH); all findings fixed: contract-test circularity (now matches after/types per scenario), tc02 lease-margin hardening (2s finish-gate rounds), retention raised to 60s with the 10s sweep tick kept, fail-fast re-claim wait, filter-key input hygiene + honest edge-case docs. Backlog note from testing: MCP_MESH_AGENT_NAME seeds only the instance-id prefix — there is no env knob for a shared registered name across replicas.

Closes #1252

Test plan

  • Rust core 468 tests (incl. the shared-cursor defect pin, canonicalization, concurrency, dual-backend contract table)
  • tsuite run --suite-path tests/src-tests — 12/12
  • uc33 scoped — 2/2, plus green in the full run
  • Full integration — 507 passed + 15 API-credit-exhaustion failures, all 15 green on re-run after top-up (522/522 accounted); uc21/uc22 jobs suites and all nine harness-fixed tests pass under parallel-8

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Improved recv_event to treat each event type filter as its own independent stream with separate cursors, preventing cross-filter skipping.
    • Added multi-replica execution fencing with a per-claim epoch to prevent stale executions from winning.
  • Bug Fixes

    • Refined lease renewal/reaping behavior for quiet and long-running jobs to rely on accepted non-terminal events and active event polling.
    • Clarified supersession and cancellation propagation so superseded work can’t overwrite newer results.
  • Documentation

    • Updated job, environment, and JavaScript/Java semantics for event cursors, lease recovery, and fencing.
  • Tests

    • Added UC33 replica scenarios and fixtures; extended timeouts and unified agent readiness polling.

…, and lease-semantics docs

Each distinct recvEvent type-filter now tracks its own cursor —
independent streams, exactly-once within a stream — so interleaved
gates can no longer skip each other's events; per-filter locks let a
long-poll on one type run concurrently with another. The mock and
registry share one verified page contract. New uc33 integration tests
prove poll-liveness (a quietly-gating job is never re-claimed) and
supersession fencing (exactly one owner survives a genuine reclaim) on
2-replica consumers. Registration waits in the cold-JVM-sensitive
suites poll until registered instead of sleeping a fixed 25s. Jobs man
pages document lease/liveness rules, multi-replica fencing, cursor
semantics, and the stale-timeout-vs-lease distinction.

Closes #1252

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Updates job event cursoring and lease/fencing docs, adds UC33 multi-replica integration fixtures and scenarios, and replaces several integration-test waits with a shared agent-registration routine.

Changes

Per-filter cursors, fencing docs, and UC33 tests

Layer / File(s) Summary
Per-filter cursor implementation in JobController
src/runtime/core/src/jobs.rs
Replaces the shared event cursor with per-filter cursor maps and per-filter serialization locks in JobController, updating filter-key canonicalization, cursor lookup, and cursor advancement logic in recv_event.
Rust core test coverage for cursor semantics
src/runtime/core/src/jobs.rs
Adds and expands Rust tests covering unfiltered-vs-empty-string filter equivalence, per-filter cursor independence, filter-key canonicalization, and a mock-vs-registry contract test for pagination parameters and responses; also updates the mock backend's list_job_events to canonicalize filters like the registry.
Concept docs: fencing, poll-liveness, and per-filter cursors
docs/concepts/jobs.md
Updates docs/concepts/jobs.md to describe multi-replica fencing and poll-liveness lease renewal, per-controller-per-filter cursor advancement in the event-injection diagram, and expanded recv_event cursor semantics.
CLI man-page docs across languages
src/core/cli/man/content/environment.md, src/core/cli/man/content/jobs.md, src/core/cli/man/content/jobs_java.md, src/core/cli/man/content/jobs_typescript.md
Updates man-page content to document the stale-timeout reaping ceiling, per-filter event stream semantics, lease renewal via non-terminal deltas and polling, and new multi-replica execution and fencing sections with claimEpoch examples.
UC33 gate-driver and worker fixtures plus registry routine
tests/integration/suites/uc33_meshjob_replicas/artifacts/*, tests/integration/suites/uc33_meshjob_replicas/fixtures/*, tests/integration/suites/uc33_meshjob_replicas/routines.yaml
Adds symlinked UC33 artifacts, a submit-only gate-driver fixture, two gated-worker fixtures with quiet-gate and wedged-owner scenarios, and registry fast-sweep/stop_all routines.
UC33 integration test scenarios
tests/integration/suites/uc33_meshjob_replicas/tc01_quiet_gate_single_owner/test.yaml, tests/integration/suites/uc33_meshjob_replicas/tc02_supersession_fences_stale_owner/test.yaml
Adds two integration tests that orchestrate multi-replica worker/driver startup, job submission, gated event posting, mid-test checks, and end-state assertions on transitions, epochs, and claim counts.

Estimated code review effort: 5 (Critical) | ~120 minutes

Registration wait routine and test timeout updates

Layer / File(s) Summary
Shared agent registration wait routine
tests/integration/global/routines.yaml
Adds a global routine that polls meshctl list until named agents appear, using case-insensitive matching and timeout diagnostics with recent logs on failure.
Timeout and polling adjustments across test suites
tests/integration/suites/uc06_observability/tc03_java_llm_tracing/test.yaml, tests/integration/suites/uc10_toolcalls/tc19_consumer_java_provider_claude_py_tool_py/test.yaml, tests/integration/suites/uc10_toolcalls/tc20_consumer_java_provider_claude_py_tool_ts/test.yaml, tests/integration/suites/uc10_toolcalls/tc22_consumer_java_provider_openai_py_tool_py/test.yaml, tests/integration/suites/uc10_toolcalls/tc23_consumer_java_provider_openai_py_tool_ts/test.yaml, tests/integration/suites/uc10_toolcalls/tc25_consumer_java_provider_gemini_py_tool_py/test.yaml, tests/integration/suites/uc10_toolcalls/tc26_consumer_java_provider_gemini_py_tool_ts/test.yaml, tests/integration/suites/uc10_toolcalls/tc46_consumer_java_provider_claude_ts_tool_py/test.yaml, tests/integration/suites/uc20_tutorial/tc07_day07_committee/test.yaml
Increases top-level and step-level timeouts and replaces fixed-duration waits with agent-registration routine calls in uc06 observability, uc10 toolcalls, and uc20 tutorial suites.

Possibly related PRs

  • dhyansraj/mcp-mesh#1047: Also changes MeshJob event-log cursor semantics in src/runtime/core/src/jobs.rs and depends on the same pagination/cursor behavior.
  • dhyansraj/mcp-mesh#1049: Related because it uses the event-log cursor and pagination behavior that this PR changes for per-types filtering.
  • dhyansraj/mcp-mesh#1053: Documents the same recv_event cursor and filter semantics that this PR implements in the runtime.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is specific and matches the main changes: per-filter cursors, multi-replica tests, and lease/documentation updates.
Linked Issues check ✅ Passed The diff covers per-filter cursors, poll-liveness docs, claim-epoch fencing scenarios, and the required integration and contract tests for #1252.
Out of Scope Changes check ✅ Passed The added test timeouts, shared wait helper, fixtures, and docs all support the jobs integrity work and do not appear unrelated.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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 feat/1252-cursors-tests-docs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 2

🧹 Nitpick comments (3)
src/runtime/core/src/jobs.rs (1)

3714-3781: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Force the concurrency tests to overlap.

These tests can pass without proving the lock behavior: the spawned receiver is not synchronized to enter recv_event, and MockBackend::list_job_events returns immediately. Use a small blocking/Notify test backend so the first call is known to be pending before the peer call starts; then assert different filters proceed while same-filter calls serialize.

🤖 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 `@src/runtime/core/src/jobs.rs` around lines 3714 - 3781, The concurrency tests
in recv_event_different_filters_do_not_block_each_other and
recv_event_same_filter_monotonic_under_concurrency do not guarantee the calls
actually overlap. Update them to use a small blocking/Notify-based test backend
or synchronization point so the first recv_event call is definitely pending
before starting the second, and then assert that different filters can proceed
concurrently while same-filter calls serialize and observe distinct seq values.
tests/integration/suites/uc06_observability/tc03_java_llm_tracing/test.yaml (1)

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

Good dependency-aware readiness check.

Unlike the sibling uc10 consumer-wait blocks (tc19/tc20/tc46), this analyst-wait loop verifies actual dependency resolution (analyst.*1/1 / analyst.*healthy), not just registration presence — this is the more robust pattern.

Separately, this exact polling idiom (seq/sleep/grep/error-tail) is now duplicated verbatim across this file (x2), tc19/tc20/tc46, and uc20 tc07 (x2) — 7 occurrences total. Consider extracting it into a shared routine (the repo already uses routines.yaml for reusable setup elsewhere) to avoid drift as timeouts/messages get tuned independently over time.

🤖 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 `@tests/integration/suites/uc06_observability/tc03_java_llm_tracing/test.yaml`
around lines 77 - 124, This analyst readiness loop is correct, but the same
polling pattern is duplicated in multiple test steps, which will drift over
time. Extract the repeated seq/sleep/grep/error-tail logic into a shared
reusable routine (following the existing routines.yaml pattern) and have the
analyst check in this test call that shared helper while keeping the
dependency-aware 1/1 or healthy condition intact. Reference the wait_provider
and wait_analyst shell handlers, plus the similar tc19/tc20/tc46 and uc20 tc07
waits, when consolidating the logic.
tests/integration/suites/uc10_toolcalls/tc22_consumer_java_provider_openai_py_tool_py/test.yaml (1)

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

Polling-based registration wait looks correct.

Loop bound (120×2s=240s) matches the comment and stays within the 260s step timeout; failure path dumps logs before exiting non-zero, which aids debugging.

Note: this identical polling block is duplicated verbatim across many test.yaml files in this cohort (tc19, tc20, tc22, tc23, tc25, tc26, tc46, uc06/tc03, uc20/tc07). Consider extracting it into a shared routine (similar to existing global.setup_for_python_agent routines) parameterized by agent name, to avoid maintaining N copies of the same wait logic.

🤖 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
`@tests/integration/suites/uc10_toolcalls/tc22_consumer_java_provider_openai_py_tool_py/test.yaml`
around lines 94 - 114, The polling logic for waiting on consumer registration is
duplicated verbatim across multiple test.yaml suites, which will be hard to
maintain. Extract the repeated shell-based wait loop from this scenario into a
shared routine like the existing global.setup_for_python_agent helpers,
parameterized by the agent name (for example, the consumer identifier used by
the wait step). Update the current step to call that shared helper instead of
inlining the registration polling and log-dump logic.
🤖 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 `@src/runtime/core/src/jobs.rs`:
- Around line 756-766: The per-filter lock acquisition in jobs.rs currently
waits on filter_lock.lock() before the timeout budget starts, so queued calls
can exceed their intended limit. Update the locking flow around
Self::filter_key, recv_locks, and the filter_lock/_guard section so the deadline
is computed before awaiting the mutex, then acquire the lock by racing it
against the remaining timeout budget and fail early if the budget is exhausted.
Ensure the timeout used for the poll accounts for time spent waiting on the
same-filter lock.

In
`@tests/integration/suites/uc10_toolcalls/tc19_consumer_java_provider_claude_py_tool_py/test.yaml`:
- Around line 94-114: The wait step in the consumer registration shell command
only checks that analyst-java appears in meshctl list, which is insufficient for
readiness. Update the wait logic to use the same provider-ready signal as tc03,
and keep polling until analyst-java reports a healthy/ready state such as 1/1
rather than just registration. Use the existing wait-for-consumer step and
meshctl list/logs checks to locate the change, and ensure the analyze step only
runs after full readiness is confirmed.

---

Nitpick comments:
In `@src/runtime/core/src/jobs.rs`:
- Around line 3714-3781: The concurrency tests in
recv_event_different_filters_do_not_block_each_other and
recv_event_same_filter_monotonic_under_concurrency do not guarantee the calls
actually overlap. Update them to use a small blocking/Notify-based test backend
or synchronization point so the first recv_event call is definitely pending
before starting the second, and then assert that different filters can proceed
concurrently while same-filter calls serialize and observe distinct seq values.

In `@tests/integration/suites/uc06_observability/tc03_java_llm_tracing/test.yaml`:
- Around line 77-124: This analyst readiness loop is correct, but the same
polling pattern is duplicated in multiple test steps, which will drift over
time. Extract the repeated seq/sleep/grep/error-tail logic into a shared
reusable routine (following the existing routines.yaml pattern) and have the
analyst check in this test call that shared helper while keeping the
dependency-aware 1/1 or healthy condition intact. Reference the wait_provider
and wait_analyst shell handlers, plus the similar tc19/tc20/tc46 and uc20 tc07
waits, when consolidating the logic.

In
`@tests/integration/suites/uc10_toolcalls/tc22_consumer_java_provider_openai_py_tool_py/test.yaml`:
- Around line 94-114: The polling logic for waiting on consumer registration is
duplicated verbatim across multiple test.yaml suites, which will be hard to
maintain. Extract the repeated shell-based wait loop from this scenario into a
shared routine like the existing global.setup_for_python_agent helpers,
parameterized by the agent name (for example, the consumer identifier used by
the wait step). Update the current step to call that shared helper instead of
inlining the registration polling and log-dump logic.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8a02bc62-fbd9-487b-a605-27d9a8ddc31d

📥 Commits

Reviewing files that changed from the base of the PR and between 564464e and 98e8046.

📒 Files selected for processing (24)
  • docs/concepts/jobs.md
  • src/core/cli/man/content/environment.md
  • src/core/cli/man/content/jobs.md
  • src/core/cli/man/content/jobs_java.md
  • src/core/cli/man/content/jobs_typescript.md
  • src/runtime/core/src/jobs.rs
  • tests/integration/suites/uc06_observability/tc03_java_llm_tracing/test.yaml
  • tests/integration/suites/uc10_toolcalls/tc19_consumer_java_provider_claude_py_tool_py/test.yaml
  • tests/integration/suites/uc10_toolcalls/tc20_consumer_java_provider_claude_py_tool_ts/test.yaml
  • tests/integration/suites/uc10_toolcalls/tc22_consumer_java_provider_openai_py_tool_py/test.yaml
  • tests/integration/suites/uc10_toolcalls/tc23_consumer_java_provider_openai_py_tool_ts/test.yaml
  • tests/integration/suites/uc10_toolcalls/tc25_consumer_java_provider_gemini_py_tool_py/test.yaml
  • tests/integration/suites/uc10_toolcalls/tc26_consumer_java_provider_gemini_py_tool_ts/test.yaml
  • tests/integration/suites/uc10_toolcalls/tc46_consumer_java_provider_claude_ts_tool_py/test.yaml
  • tests/integration/suites/uc20_tutorial/tc07_day07_committee/test.yaml
  • tests/integration/suites/uc33_meshjob_replicas/artifacts/gate-driver
  • tests/integration/suites/uc33_meshjob_replicas/artifacts/gated-worker-a
  • tests/integration/suites/uc33_meshjob_replicas/artifacts/gated-worker-b
  • tests/integration/suites/uc33_meshjob_replicas/fixtures/gate-driver/main.py
  • tests/integration/suites/uc33_meshjob_replicas/fixtures/gated-worker-a/main.py
  • tests/integration/suites/uc33_meshjob_replicas/fixtures/gated-worker-b/main.py
  • tests/integration/suites/uc33_meshjob_replicas/routines.yaml
  • tests/integration/suites/uc33_meshjob_replicas/tc01_quiet_gate_single_owner/test.yaml
  • tests/integration/suites/uc33_meshjob_replicas/tc02_supersession_fences_stale_owner/test.yaml

Comment thread src/runtime/core/src/jobs.rs
Comment on lines +94 to +114
# Liveness only: poll until the Java consumer REGISTERS instead of a fixed
# sleep — cold-JVM starts (mvn compile + Spring Boot startup) routinely
# outran the old 25s wait. Generous 240s deadline; dependency resolution
# stays covered by the runtime's settle window (no dep-resolution wait).
- name: "Wait for consumer registration"
handler: shell
workdir: /workspace
command: |
echo "Waiting for analyst-java to register..."
for i in $(seq 1 120); do
if meshctl list 2>/dev/null | grep -q "analyst-java"; then
echo "analyst-java registered after ~$((i*2))s"
exit 0
fi
sleep 2
done
echo "ERROR: analyst-java did not register within 240s"
meshctl logs analyst-java 2>/dev/null | tail -50 || true
exit 1
capture: wait_consumer_registration
timeout: 260

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the target test and the analogous uc06 tc03 check.
git ls-files 'tests/integration/suites/uc10_toolcalls/tc19_consumer_java_provider_claude_py_tool_py/test.yaml' \
             'tests/integration/suites/uc06_toolcalls/tc03_*/test.yaml' \
             'tests/integration/suites/**/test.yaml' | sed -n '1,120p'

echo '--- target file excerpt ---'
cat -n tests/integration/suites/uc10_toolcalls/tc19_consumer_java_provider_claude_py_tool_py/test.yaml | sed -n '70,140p'

echo '--- search for registration checks and meshctl list usage ---'
rg -n --hidden -S 'meshctl list|1/1|healthy|analyst-java|claude-provider-py|Non-tool query|analyze' tests/integration/suites

echo '--- analogous tc03 file excerpt if found ---'
python3 - <<'PY'
import pathlib, re
matches = list(pathlib.Path('tests/integration/suites').rglob('test.yaml'))
for p in matches:
    txt = p.read_text(errors='ignore')
    if '1/1' in txt and 'healthy' in txt and 'meshctl list' in txt:
        print(f'FILE: {p}')
        lines = txt.splitlines()
        for i,l in enumerate(lines,1):
            if any(k in l for k in ['meshctl list','1/1','healthy','analyst','claude-provider-py','analyze','Non-tool query']):
                start=max(1,i-3); end=min(len(lines),i+5)
                print(f'--- lines {start}-{end} ---')
                for j in range(start,end+1):
                    print(f'{j}: {lines[j-1]}')
                print()
        break
PY

Repository: dhyansraj/mcp-mesh

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- target file: tc19 excerpt ---'
cat -n tests/integration/suites/uc10_toolcalls/tc19_consumer_java_provider_claude_py_tool_py/test.yaml | sed -n '1,220p'

echo '--- similar Java-consumer toolcall tests with registration polling ---'
for f in \
  tests/integration/suites/uc10_toolcalls/tc23_consumer_java_provider_openai_py_tool_ts/test.yaml \
  tests/integration/suites/uc10_toolcalls/tc27_consumer_java_provider_gemini_py_tool_java/test.yaml \
  tests/integration/suites/uc10_toolcalls/tc02_consumer_py_provider_claude_py_tool_ts/test.yaml \
  tests/integration/suites/uc10_toolcalls/tc04_consumer_py_provider_openai_py_tool_py/test.yaml
do
  if [ -f "$f" ]; then
    echo "FILE: $f"
    cat -n "$f" | sed -n '80,180p'
    echo
  fi
done

echo '--- search for any explicit "healthy" or dependency-resolution assertion near analyze ---'
rg -n -C 3 -S 'analyze|healthy|1/1|register|meshctl list' \
  tests/integration/suites/uc10_toolcalls/tc19_consumer_java_provider_claude_py_tool_py/test.yaml \
  tests/integration/suites/uc10_toolcalls/tc23_consumer_java_provider_openai_py_tool_ts/test.yaml \
  tests/integration/suites/uc10_toolcalls/tc27_consumer_java_provider_gemini_py_tool_java/test.yaml \
  tests/integration/suites/uc10_toolcalls/tc02_consumer_py_provider_claude_py_tool_ts/test.yaml \
  tests/integration/suites/uc10_toolcalls/tc04_consumer_py_provider_openai_py_tool_py/test.yaml

Repository: dhyansraj/mcp-mesh

Length of output: 46340


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- find meshctl list implementation / docs ---'
rg -n -S 'meshctl list|list agents|healthy|1/1|status' . \
  -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**' -g '!**/.git/**' \
  | sed -n '1,250p'

echo '--- locate likely CLI source files ---'
git ls-files | rg -n '(^|/)(meshctl|cli|cmd|commands?).*\.(go|ts|js|py)$|(^|/)main\.(go|ts|js|py)$'

echo '--- inspect files with meshctl list formatting, if found ---'
python3 - <<'PY'
import pathlib, re
cands=[]
for p in pathlib.Path('.').rglob('*'):
    if p.is_file() and p.suffix in {'.go','.ts','.js','.py'}:
        try:
            txt=p.read_text(errors='ignore')
        except Exception:
            continue
        if 'meshctl' in txt and ('list' in txt or 'healthy' in txt or '1/1' in txt):
            cands.append(str(p))
for p in cands[:20]:
    print(p)
PY

Repository: dhyansraj/mcp-mesh

Length of output: 50374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- src/core/cli/list.go ---'
cat -n src/core/cli/list.go | sed -n '1,260p'

echo '--- src/core/cli/status.go ---'
cat -n src/core/cli/status.go | sed -n '1,260p'

echo '--- src/core/cli/list_test.go excerpts ---'
rg -n -C 3 -S 'healthy|unresolved|dependency|meshctl list|superseded|down' src/core/cli/list_test.go

echo '--- docs/meshctl-cli.md list semantics excerpt ---'
rg -n -C 3 -S 'meshctl list|healthy agents by default|unhealthy|dependencies|status' docs/meshctl-cli.md

Repository: dhyansraj/mcp-mesh

Length of output: 49715


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- tc19 artifacts / wiring references ---'
rg -n -S 'analyst-java|claude-provider-py|weather-tool-py|dependency_resolutions|Mesh provider not resolved|`@MeshLlm`|analyze' \
  tests/integration/suites/uc10_toolcalls/tc19_consumer_java_provider_claude_py_tool_py \
  examples/toolcalls \
  tests/integration/suites/uc10_toolcalls/tc23_consumer_java_provider_openai_py_tool_ts \
  tests/integration/suites/uc10_toolcalls/tc27_consumer_java_provider_gemini_py_tool_java \
  src/core/cli \
  docs \
  | sed -n '1,260p'

echo '--- inspect any tc19 artifact/main files if present ---'
git ls-files 'tests/integration/suites/uc10_toolcalls/tc19_consumer_java_provider_claude_py_tool_py/**' 'examples/toolcalls/**' | sed -n '1,200p'

Repository: dhyansraj/mcp-mesh

Length of output: 34759


Wait for provider readiness, not just registration.
meshctl list | grep -q "analyst-java" only proves the consumer name appeared. A consumer can still be healthy while its dependency_resolutions are unresolved, so this can race the following analyze call and flake under cold JVM startup. Match the same readiness condition used in tc03 (e.g. 1/1/healthy) before proceeding.

🤖 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
`@tests/integration/suites/uc10_toolcalls/tc19_consumer_java_provider_claude_py_tool_py/test.yaml`
around lines 94 - 114, The wait step in the consumer registration shell command
only checks that analyst-java appears in meshctl list, which is insufficient for
readiness. Update the wait logic to use the same provider-ready signal as tc03,
and keep polling until analyst-java reports a healthy/ready state such as 1/1
rather than just registration. Use the existing wait-for-consumer step and
meshctl list/logs checks to locate the change, and ensure the analyze step only
runs after full readiness is confirmed.

… routines

recv_event computes its deadline before queueing on the per-filter
lock and races acquisition against the budget and this execution's
cancel token, so queued calls honor their own timeout and cancelled
executions never park on the lock. Concurrency tests now prove overlap
via a gated mock backend. The nine registration waits share one global
liveness-only routine parameterized by agent list; the single
dep-aware wait condition is normalized to liveness-only per the
settle-grace convention.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dhyansraj

Copy link
Copy Markdown
Owner Author

Review comments addressed in afc1a27:

  • recv_event per-filter lock vs timeout budget — valid, fixed. The deadline is now computed before queueing; lock acquisition races the remaining budget (timeout semantics preserved: Ok(None)) and this execution's cancel token (a cancelled/superseded execution no longer parks on the lock behind a 60s long-poll). The poll loop's per-iteration budget derives from the same deadline, so lock-wait time is subtracted automatically. Epoch/supersession logic untouched.
  • Concurrency tests don't prove overlap — valid, reworked. A gated mock backend (Notify + park) makes call 1 provably in-flight before call 2 starts; different-filter concurrency and same-filter serialization are now asserted under proven overlap, plus two new tests pinning the fixes above (queued call times out at its own budget; cancel-while-queued returns promptly). Core suite: 470 passed.
  • Registration wait duplication — valid, extracted. New global routine wait_for_agents_registered (parameterized by agent list, liveness-only rule documented in its header) replaces the eleven inlined loops across the nine tests; behavior identical, validated by dry-run + scoped run + a full-suite run in which all nine pass through the routine.
  • Dep-aware readiness ("wait for 1/1 like tc03") — skipped, with the inverse consistency fix. Per the settle-grace design (runtime: settling-window grace for dependency injection — bounded wait for resolution during agent startup #1193), registration waits are liveness-only: dependency resolution is covered at call time by the settle window, and all nine tests pass under parallel-8 on that basis. tc03's loop was actually the outlier carrying a dep-aware grep — it's now normalized to liveness-only rather than spreading the condition.

Full suite on the refactored tree: 520 passed; the 2 failures are pre-existing uc04 structured-output tests in an untouched use case (re-run in progress to confirm).

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

Caution

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

⚠️ Outside diff range comments (3)
tests/integration/suites/uc10_toolcalls/tc19_consumer_java_provider_claude_py_tool_py/test.yaml (1)

18-18: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Raise the suite timeout

tests/integration/config.yaml treats execution.timeout as the per-test wall-clock cap, so this suite can still hit the 600s limit: maven-install alone allows 600s, wait_for_agents_registered runs up to ~240s, and the two analyze calls add 240s more. tc03_java_llm_tracing already uses 1200 for the same stacked-wait pattern.

🤖 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
`@tests/integration/suites/uc10_toolcalls/tc19_consumer_java_provider_claude_py_tool_py/test.yaml`
at line 18, Increase the suite timeout for this test so it can accommodate the
combined wall-clock time of `maven-install`, `wait_for_agents_registered`, and
both `analyze` calls under `execution.timeout`; update the `timeout` value in
`tc19_consumer_java_provider_claude_py_tool_py/test.yaml` to match the higher
limit already used by `tc03_java_llm_tracing` for the same stacked-wait pattern.
tests/integration/suites/uc10_toolcalls/tc20_consumer_java_provider_claude_py_tool_ts/test.yaml (1)

19-19: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Raise the TC20 suite timeout
timeout: 600 leaves no room for the rest of this flow: the Java consumer install is capped at 600s, then the suite still runs 15s/20s waits, the 260s registration poll, and two 120s analyze calls. TC20 also adds TypeScript agent setup, so this path needs a larger suite budget than tc19.

🤖 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
`@tests/integration/suites/uc10_toolcalls/tc20_consumer_java_provider_claude_py_tool_ts/test.yaml`
at line 19, The TC20 suite timeout is too low for the full flow in this test;
update the suite budget in the test configuration for tc20 so it accounts for
the Java consumer install cap, the registration polling, the two analyze calls,
and the added TypeScript agent setup. Locate the timeout setting in the TC20
test YAML and increase it to a value larger than the current 600s so the suite
can complete reliably.
tests/integration/suites/uc10_toolcalls/tc22_consumer_java_provider_openai_py_tool_py/test.yaml (1)

18-18: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Increase the suite timeout above the 600s Maven install budget
timeout: 600 at the suite level leaves no room for the later waits and 120s query calls if maven-install uses most of its own 600s budget. Raise the suite timeout so the whole scenario has headroom.

🤖 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
`@tests/integration/suites/uc10_toolcalls/tc22_consumer_java_provider_openai_py_tool_py/test.yaml`
at line 18, The suite-level timeout in the test configuration is too tight
relative to the 600s Maven install budget. Update the timeout in the
uc10_toolcalls tc22 consumer/provider YAML so the full scenario, including later
waits and query calls, has extra headroom; use the suite timeout setting in the
test definition as the target to increase.
🧹 Nitpick comments (1)
tests/integration/global/routines.yaml (1)

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

Diagnostic dump on timeout doesn't scale with agent count vs. remaining time budget.

The loop consumes up to 240s (120 × 2s) of the step's 260s timeout, leaving only ~20s for the failure path to run meshctl list plus one meshctl logs "$agent" | tail -30 per expected agent (Line 315-318). Callers like tc07_day07_committee pass 12 agent names in a single call — if registration genuinely times out, the ~20s remainder is unlikely to be enough to dump logs for all 12 agents before the step's own 260s timeout kills the process, truncating exactly the diagnostics needed to debug the failure.

Consider reserving diagnostic time proportional to the agent count (e.g., scale the loop bound/timeout by TOTAL, or cap/parallelize the log dumps).

♻️ Possible adjustment
-    steps:
-      - handler: shell
-        workdir: /workspace
-        command: |
-          EXPECTED="${params.agents}"
-          TOTAL=$(echo "$EXPECTED" | wc -w | tr -d ' ')
-          echo "Waiting for $TOTAL agent(s) to register: $EXPECTED"
-          for i in $(seq 1 120); do
+    steps:
+      - handler: shell
+        workdir: /workspace
+        command: |
+          EXPECTED="${params.agents}"
+          TOTAL=$(echo "$EXPECTED" | wc -w | tr -d ' ')
+          echo "Waiting for $TOTAL agent(s) to register: $EXPECTED"
+          for i in $(seq 1 110); do

Trim the poll loop bound (or bump the step timeout) to leave headroom that grows with TOTAL for the diagnostics block.

🤖 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 `@tests/integration/global/routines.yaml` around lines 293 - 320, The timeout
handling in the shell step that waits for agents to register does not leave
enough room for the diagnostic dump when many agents are expected. In the wait
loop inside the wait_agents_registered handler, reduce the polling budget or
otherwise make it scale with params.agents/TOTAL so the failure path still has
time to run meshctl list and the per-agent meshctl logs checks. Keep the
existing diagnostics, but ensure the remaining time budget grows with the number
of expected agents.
🤖 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.

Outside diff comments:
In
`@tests/integration/suites/uc10_toolcalls/tc19_consumer_java_provider_claude_py_tool_py/test.yaml`:
- Line 18: Increase the suite timeout for this test so it can accommodate the
combined wall-clock time of `maven-install`, `wait_for_agents_registered`, and
both `analyze` calls under `execution.timeout`; update the `timeout` value in
`tc19_consumer_java_provider_claude_py_tool_py/test.yaml` to match the higher
limit already used by `tc03_java_llm_tracing` for the same stacked-wait pattern.

In
`@tests/integration/suites/uc10_toolcalls/tc20_consumer_java_provider_claude_py_tool_ts/test.yaml`:
- Line 19: The TC20 suite timeout is too low for the full flow in this test;
update the suite budget in the test configuration for tc20 so it accounts for
the Java consumer install cap, the registration polling, the two analyze calls,
and the added TypeScript agent setup. Locate the timeout setting in the TC20
test YAML and increase it to a value larger than the current 600s so the suite
can complete reliably.

In
`@tests/integration/suites/uc10_toolcalls/tc22_consumer_java_provider_openai_py_tool_py/test.yaml`:
- Line 18: The suite-level timeout in the test configuration is too tight
relative to the 600s Maven install budget. Update the timeout in the
uc10_toolcalls tc22 consumer/provider YAML so the full scenario, including later
waits and query calls, has extra headroom; use the suite timeout setting in the
test definition as the target to increase.

---

Nitpick comments:
In `@tests/integration/global/routines.yaml`:
- Around line 293-320: The timeout handling in the shell step that waits for
agents to register does not leave enough room for the diagnostic dump when many
agents are expected. In the wait loop inside the wait_agents_registered handler,
reduce the polling budget or otherwise make it scale with params.agents/TOTAL so
the failure path still has time to run meshctl list and the per-agent meshctl
logs checks. Keep the existing diagnostics, but ensure the remaining time budget
grows with the number of expected agents.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9729fd5c-2047-48b4-9cb1-d340e63bbb07

📥 Commits

Reviewing files that changed from the base of the PR and between 98e8046 and afc1a27.

📒 Files selected for processing (11)
  • src/runtime/core/src/jobs.rs
  • tests/integration/global/routines.yaml
  • tests/integration/suites/uc06_observability/tc03_java_llm_tracing/test.yaml
  • tests/integration/suites/uc10_toolcalls/tc19_consumer_java_provider_claude_py_tool_py/test.yaml
  • tests/integration/suites/uc10_toolcalls/tc20_consumer_java_provider_claude_py_tool_ts/test.yaml
  • tests/integration/suites/uc10_toolcalls/tc22_consumer_java_provider_openai_py_tool_py/test.yaml
  • tests/integration/suites/uc10_toolcalls/tc23_consumer_java_provider_openai_py_tool_ts/test.yaml
  • tests/integration/suites/uc10_toolcalls/tc25_consumer_java_provider_gemini_py_tool_py/test.yaml
  • tests/integration/suites/uc10_toolcalls/tc26_consumer_java_provider_gemini_py_tool_ts/test.yaml
  • tests/integration/suites/uc10_toolcalls/tc46_consumer_java_provider_claude_ts_tool_py/test.yaml
  • tests/integration/suites/uc20_tutorial/tc07_day07_committee/test.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/runtime/core/src/jobs.rs

@dhyansraj
dhyansraj merged commit 48999eb into main Jul 3, 2026
13 checks passed
@dhyansraj
dhyansraj deleted the feat/1252-cursors-tests-docs branch July 3, 2026 14:33
@dhyansraj dhyansraj mentioned this pull request Jul 3, 2026
dhyansraj added a commit that referenced this pull request Jul 3, 2026
## v2.8.0 release

### Bump scope
- `scripts/bump_version.py 2.7.0 2.8.0` — 387 files across runtimes,
examples, tests, docs
- Helm: all nine `Chart.yaml` (chart version + mesh-tracking appVersions
+ core subchart pins) — done by hand; script gap backlogged on #1260 —
with `Chart.lock` regenerated via `helm dependency update`
- `Cargo.lock` regenerated for the core crate version

### Release highlights (see RELEASE_NOTES.md)
- **`required=true`** — availability-aware dependency graph: transitive
capability availability with full constraint matching, route 503
perimeters in all three runtimes, cycle rejection, meshctl/UI
observability, job claim-gating (#1249: #1255/#1257/#1258; #1256
lost-event fix)
- **MeshJob execution integrity** — claim-epoch fencing, poll-liveness,
per-filter event cursors, multi-replica integration suite (#1252:
#1253/#1254)
- **Data fidelity** — empty tool returns round-trip exactly across
runtimes (#1251); LLM reply envelope always carries the answer as a
string (#1248)
- Breaking/behavior changes and mixed-version upgrade notes are called
out in RELEASE_NOTES.md

### Validation
- Full release gate (src-tests + integration at 2.8.0) running; result
will be posted as a comment before merge.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

design(jobs): claim epochs, poll-liveness, and per-filter event cursors — multi-replica execution integrity

1 participant