Skip to content

ts: mesh.jobs cancel/status/wait facades (TS parity for #1074) - #1079

Merged
dhyansraj merged 1 commit into
mainfrom
feature/1078-ts-mesh-jobs-facades
May 22, 2026
Merged

ts: mesh.jobs cancel/status/wait facades (TS parity for #1074)#1079
dhyansraj merged 1 commit into
mainfrom
feature/1078-ts-mesh-jobs-facades

Conversation

@dhyansraj

@dhyansraj dhyansraj commented May 22, 2026

Copy link
Copy Markdown
Owner

Summary

TypeScript parity for the Python mesh.jobs.cancel/status/wait facades shipped in #1077. Three new module-level async functions mirror the existing postEvent / subscribeEvents DDDI-clean pattern — callers with only a jobId no longer need to construct new JobProxy(jobId, registryUrl) and pass MCP_MESH_REGISTRY_URL explicitly.

export async function cancel(jobId: string, reason?: string): Promise<void>
export async function status(jobId: string): Promise<JobStatus>
export async function wait(jobId: string, timeoutSecs?: number): Promise<unknown>

Each: resolveRegistryUrl()_getOrCreateProxy() (LRU-cached) → napi dispatch → translateJobError() for typed exception re-classification. Underlying napi binding (JsJobProxy.cancel/status/wait) was already exposed — no Rust changes.

JobStatus interface added alongside JobEvent / JobEventReceipt, exported from mesh.jobs. Fields mirror job_to_json in jobs_napi.rs: required fields typed T, Option<T> fields emitted as T | null (every key always present; only null-checks needed, no key-presence checks).

MeshJobsNamespace extended with cancel/status/wait alphabetically; public exports added.

Bonus generalization

resolveRegistryUrl's error message prefix generalized from "mesh.jobs.postEvent:""mesh.jobs:". Now accurate for all four facades — mirrors the same generalization the Python BLOCKER fix made in #1077.

Out of scope

Review Notes

Independent review: 0 BLOCKER, 1 WARNING (addressed in amended commit), 5 INFOs (skipped as cosmetic).

  • WARNING fixed: JobStatus interface used field?: T for nullable fields, but job_to_json always emits every field with Value::Null fallback. Tightened to field: T | null where appropriate, and field: T (no optional, no null) for fields the Rust binding emits unconditionally. Added one test pinning the every-key-present contract via Object.keys comparison.

INFOs skipped: Promise<unknown> vs Any (the stricter shape is correct), repeated try/catch boilerplate matches postEvent's style, empty toHaveBeenCalledWith() matcher is intentional for nullary calls, JSDoc null-vs-undefined nuance, doc table layout.

Test plan

  • TS unit tests: 39 pass (was 23, +16 new — 15 facade tests + 1 contract test)
  • uc22_meshjob_ts integration: 24/24 pass (222.8s)
  • npm run build clean (no TS compile errors after type tightening)
  • Vitest hoisting workaround confined to test file (import { cancel as cancelFacade }); SDK exports stay canonical — import { cancel } from "@mcpmesh/sdk" works unaliased for end users
  • Cross-runtime scope: no edits to Python / Java / Rust / jobs_java.md / Python jobs.md variant
  • Public-artifact framing: no naming downstream consumers; structural language throughout

Closes #1078

Summary by CodeRabbit

Release Notes

  • New Features

    • Added job lifecycle management capabilities: cancel jobs, retrieve job status, and wait for job completion.
    • Enhanced job operation documentation with TypeScript implementation examples and per-runtime behavior specifications.
  • Tests

    • Expanded test coverage for new job control operations and error handling scenarios.

Review Change Stack

TypeScript parity for the Python facades shipped in #1077. Three new
module-level async functions on mesh.jobs mirror the existing
postEvent / subscribeEvents DDDI-clean shape — callers with only a
jobId no longer need to construct a JobProxy or pass MCP_MESH_REGISTRY_URL
explicitly:

  async cancel(jobId: string, reason?: string): Promise<void>
  async status(jobId: string): Promise<JobStatus>
  async wait(jobId: string, timeoutSecs?: number): Promise<unknown>

Each: resolveRegistryUrl() → _getOrCreateProxy() (LRU-cached) → napi
dispatch → translateJobError() for typed exception re-classification.
Signatures verified against JsJobProxy in src/runtime/core/src/jobs_napi.rs;
underlying napi binding already exposes cancel/status/wait so no Rust
changes were needed.

JobStatus interface added alongside JobEvent / JobEventReceipt,
exported from mesh.jobs. Fields mirror job_to_json in jobs_napi.rs:
id, capability, owner_instance_id, status, progress, progress_message,
result, error, submitted_payload, attempt_count, max_retries,
max_duration, total_deadline, lease_expires_at, last_heartbeat_at,
submitted_at, submitted_by. Only id and status are required.

MeshJobsNamespace in src/runtime/typescript/src/index.ts extended with
cancel/status/wait alongside postEvent/subscribeEvents. Public exports
added.

Bonus generalization: resolveRegistryUrl()'s error message prefix
generalized from "mesh.jobs.postEvent:" → "mesh.jobs:". Now accurate
for all four facades (mirrors the same generalization the Python
BLOCKER fix made in #1077).

Tests (jobs.spec.ts): 23 → 38 (+15 new). For each new facade: happy
path, MCP_MESH_REGISTRY_URL missing, JobNotFoundError translation,
JobTerminalError translation (cancel/wait). Plus one shared-cache test
asserting cancel → status → wait reuse one cached proxy.

Wait-timeout: napi maps JobError::Timeout to message "timeout: wait
timed out after ...". translateJobError doesn't dispatch on this; the
facade re-raises as plain Error. Documented in the wait JSDoc; a
proper TimeoutError class with substring dispatch is a follow-up if
usage warrants.

Docs: docs/concepts/jobs.md "Lifecycle facades by jobId" section now
has Python | TypeScript tabs. src/core/cli/man/content/jobs_typescript.md
mirrors the Python variant's facade documentation.

Out of scope (separate work):
- Java parity (MeshJobs.cancel/status/wait) — separate PR per the
  polyglot cadence (mirrors #1041#1043#1045 trilogy)
- TimeoutError class + dispatch — follow-up if needed

Closes #1078

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR adds three new module-level async facades (cancel, status, wait) to the TypeScript mesh.jobs namespace, mirroring Python parity. Each resolves the registry URL internally and caches a JobProxy, eliminating the need for callers to pass MCP_MESH_REGISTRY_URL explicitly. Includes JobStatus type contract, comprehensive unit tests, API wiring, and user documentation.

Changes

TypeScript Job Lifecycle Facades

Layer / File(s) Summary
JobStatus contract and facade implementations
src/runtime/typescript/src/jobs.ts
New JobStatus interface models the job snapshot shape with required fields and explicit nullability for Rust Option<T> properties. Three new exported async functions (cancel, status, wait) resolve the registry URL, obtain a cached JobProxy, dispatch to N-API bindings, and translate errors via translateJobError. Error message prefix in resolveRegistryUrl() updated.
Public API surface and namespace wiring
src/runtime/typescript/src/index.ts
mesh.jobs import expanded to include cancel, status, wait. MeshJobsNamespace interface and jobs constant extended with the three new methods. Public re-exports broadened to include the new functions and JobStatus type.
Comprehensive test infrastructure and facade tests
src/runtime/typescript/src/__tests__/jobs.spec.ts
JobProxy mock expanded with cancel, status, wait method fields and corresponding mock functions. Test harness imports and resets new mocks. New end-to-end test suites validate registry URL resolution, argument forwarding (including null cases), error re-classification (JobNotFoundError, JobTerminalError, timeout Error with "timeout:" prefix), and proxy cache sharing across all four facades.
User-facing documentation for facades
docs/concepts/jobs.md, src/core/cli/man/content/jobs_typescript.md
concepts/jobs.md operations table updated with explicit per-runtime return types for each facade; new TypeScript example section demonstrates abort_workflow, check_progress, run_to_completion. Language-specific timeout behavior documented: Python raises TimeoutError, TypeScript rejects with Error message starting with "timeout:". CLI man page adds lifecycle facades section describing mesh.jobs.cancel/status/wait calls, error behavior, and timeout handling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • #1078: Primary objective issue—TypeScript parity for Python mesh.jobs cancel/status/wait facades.

Possibly related PRs

  • dhyansraj/mcp-mesh#1043: Both PRs involve TypeScript job lifecycle plumbing; main PR adds the mesh.jobs.wait/cancel/status facades that depend on the same JobProxy.wait N-API behavior, error translation, and proxy caching infrastructure.

Poem

🐇 A rabbit hops through jobs with glee,
Now cancel, status, wait run free—
No proxy dance, no registry passed,
Just jobId calls, blazingly fast!
TypeScript and Python, now in sync,
Hop along, the façades wink.

🚥 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 'ts: mesh.jobs cancel/status/wait facades (TS parity for #1074)' clearly and specifically describes the main change: adding three TypeScript job control facades with reference to the Python parity issue.
Linked Issues check ✅ Passed All coding requirements from #1078 are met: three facades (cancel/status/wait) added to mesh.jobs, JobStatus interface exported, MeshJobsNamespace extended, tests added, documentation updated, no Rust changes made.
Out of Scope Changes check ✅ Passed All changes are in-scope: TypeScript implementations, documentation, and tests directly support the facade feature. Out-of-scope items (Java parity, caller authorization, typed TimeoutError) are correctly excluded.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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 feature/1078-ts-mesh-jobs-facades

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 and usage tips.

@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: 1

🤖 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/typescript/src/jobs.ts`:
- Line 84: The JobStatus.status union in jobs.ts currently omits "pending",
causing incorrect exhaustiveness and runtime assumptions; update the type
declaration for JobStatus.status to include "pending" (i.e., add "pending" to
the union alongside "working" | "input_required" | "completed" | "failed" |
"cancelled"), then scan for and update any switch statements, exhaustive
type-narrowing, or handling logic that assumes the old set (look for usages of
JobStatus.status, JobStatus, and any switch/case or exhaustive checks) to
account for the new "pending" state.
🪄 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: 524bed91-8b09-43be-825d-b052cedd36b1

📥 Commits

Reviewing files that changed from the base of the PR and between 01fb311 and dddd8e7.

📒 Files selected for processing (5)
  • docs/concepts/jobs.md
  • src/core/cli/man/content/jobs_typescript.md
  • src/runtime/typescript/src/__tests__/jobs.spec.ts
  • src/runtime/typescript/src/index.ts
  • src/runtime/typescript/src/jobs.ts

/** Instance id of the replica currently holding the lease (if any). */
owner_instance_id: string | null;
/** Lifecycle status. */
status: "working" | "input_required" | "completed" | "failed" | "cancelled";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add "pending" to the JobStatus.status union.

Line [84] excludes "pending", but this surface is documented as the full job-row snapshot. A submitted job can be observable in pending state before claim, so the current type can mislead exhaustive handling.

Proposed fix
-  status: "working" | "input_required" | "completed" | "failed" | "cancelled";
+  status:
+    | "pending"
+    | "working"
+    | "input_required"
+    | "completed"
+    | "failed"
+    | "cancelled";
📝 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
status: "working" | "input_required" | "completed" | "failed" | "cancelled";
status:
| "pending"
| "working"
| "input_required"
| "completed"
| "failed"
| "cancelled";
🤖 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/typescript/src/jobs.ts` at line 84, The JobStatus.status union in
jobs.ts currently omits "pending", causing incorrect exhaustiveness and runtime
assumptions; update the type declaration for JobStatus.status to include
"pending" (i.e., add "pending" to the union alongside "working" |
"input_required" | "completed" | "failed" | "cancelled"), then scan for and
update any switch statements, exhaustive type-narrowing, or handling logic that
assumes the old set (look for usages of JobStatus.status, JobStatus, and any
switch/case or exhaustive checks) to account for the new "pending" state.

@dhyansraj
dhyansraj merged commit fb225c4 into main May 22, 2026
13 checks passed
@dhyansraj
dhyansraj deleted the feature/1078-ts-mesh-jobs-facades branch May 22, 2026 16:09
dhyansraj added a commit that referenced this pull request May 22, 2026
Java parity for the Python facades shipped in #1077 and TypeScript
facades in #1079. Three new static methods (with overloads) on
MeshJobs mirror the existing postEvent / subscribeEvents DDDI-clean
shape — callers with only a jobId no longer need to construct a
JobProxy(jobId, registryUrl) and pass MCP_MESH_REGISTRY_URL explicitly:

  public static void cancel(String jobId, String reason)
  public static void cancel(String jobId)
  public static Map<String, Object> status(String jobId)
  public static Object await(String jobId, double timeoutSecs)
  public static Object await(String jobId)

Each: resolveRegistryUrl() → getOrCreateProxy() (LRU-cached) → dispatch
to the existing JobProxy instance method → substring-match translation
to typed exceptions (JobNotFoundException, JobTerminalException) via a
new package-private translateJobError helper.

Java naming nuance: 'await' (not 'wait') because Object.wait() is
final. Matches the existing JobProxy.await() instance method precedent.

What was already in place (no Rust/JNR/JobProxy changes needed):
- FFI: mesh_job_proxy_cancel/status/wait in jobs_ffi.rs
- JNR: declarations in MeshCore.java:648-670
- JobProxy.cancel/status/await instance methods
- JobNotFoundException, JobTerminalException, MeshException classes
- LRU proxy cache infrastructure

Bonus generalization: resolveRegistryUrl's error message prefix
generalized from 'MeshJobs.postEvent:' → 'MeshJobs:'. Now accurate for
all four facades. Mirrors the same generalization the Python BLOCKER
fix made in #1077 and the TS bonus in #1079 — the trilogy is now
consistent across all three runtimes in this respect.

Tests (MeshJobsTest.java): 14 → 27 (+13 new). For each new facade:
arg validation (null/empty jobId), env resolution (MCP_MESH_REGISTRY_URL
missing), cache hit reuse. Plus the cross-facade shared-cache test
asserting cancel/status/await reuse one cached proxy (mirrors Python
W6 from #1077's review fixes).

await timeout: JobProxy.await contract says timeoutSecs <= 0.0 or
non-finite means 'no timeout'. The MeshJobs.await(jobId) no-arg
overload delegates to await(jobId, -1.0).

Docs: docs/concepts/jobs.md 'Lifecycle facades by jobId' section now
has Python | TypeScript | Java tabs. src/core/cli/man/content/jobs_java.md
mirrors the Python/TS variants' facade documentation.

Out of scope (separate work):
- Strongly-typed JobStatus record — returns Map<String, Object> matching
  the JobProxy.status() shape; a record with typed fields is a follow-up
  if usage warrants
- Caller authorization on lifecycle ops — design discussion at #1076

Closes #1080

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
dhyansraj added a commit that referenced this pull request May 22, 2026
…1081)

## Summary

Java parity for the `mesh.jobs.cancel/status/wait` facades shipped in
Python (#1077) and TypeScript (#1079). Three new static methods (with
overloads) on `MeshJobs` mirror the existing `postEvent` /
`subscribeEvents` DDDI-clean pattern — callers with only a `jobId` no
longer need to construct `new JobProxy(jobId, registryUrl)` and pass
`MCP_MESH_REGISTRY_URL` explicitly.

```java
public static void cancel(String jobId, String reason) throws MeshException
public static void cancel(String jobId) throws MeshException
public static Map<String, Object> status(String jobId) throws MeshException
public static Object await(String jobId, double timeoutSecs) throws MeshException
public static Object await(String jobId) throws MeshException
```

Each: `resolveRegistryUrl()` → `getOrCreateProxy()` (LRU-cached) →
dispatch to the existing `JobProxy` instance method → substring-match
translation to typed exceptions via a new package-private
`translateJobError` helper.

**Java naming nuance**: `await` (not `wait`) to avoid readability
confusion with the inherited `Object.wait()` / `wait(long)` /
`wait(long, int)` overload family, and to match the existing
`JobProxy.await(double)` precedent.

## What was already in place (no work needed)

- FFI: `mesh_job_proxy_cancel/status/wait` in `jobs_ffi.rs`
- JNR: declarations in `MeshCore.java:648-670`
- `JobProxy.cancel/status/await` instance methods
- `JobNotFoundException`, `JobTerminalException`, `MeshException`
classes
- LRU proxy cache infrastructure

## Bonus generalization

`resolveRegistryUrl`'s error message prefix generalized from
`"MeshJobs.postEvent:"` → `"MeshJobs:"`. Now accurate for all four
facades. Mirrors the same generalization the Python BLOCKER fix made in
#1077 and the TS bonus in #1079 — the trilogy is now consistent across
all three runtimes in this respect.

## Trilogy complete

- Python facades (#1077) ✓ merged
- TypeScript facades (#1079) ✓ merged
- **Java facades (this PR)** ← closes the trilogy

## Out of scope

- Strongly-typed `JobStatus` record — returns `Map<String, Object>`
matching the existing `JobProxy.status()` shape. A record with typed
fields is a follow-up if usage warrants.
- Caller authorization on lifecycle ops — design discussion at
**#1076**.

## Review Notes

Independent review: 0 BLOCKER, 1 WARNING (addressed), 4 INFOs (3
addressed, 1 skipped as docs cosmetic).

- **W1 — Triplicated try/catch boilerplate fixed**: each facade's
translation block collapsed from 6 lines to a one-liner `throw
translateJobError(exc);`. The previous indirection (`if (translated !=
exc) throw translated; throw exc;`) was dead-equivalent on Java because
`translateJobError` chains the original exception as `cause` via the
typed constructor. Net -12 lines on the facade region.
- **I1 — `await` naming Javadoc accuracy**: the previous text claimed
"overloading would be a compile error" (wrong — `wait(double)` is
overloading, legal). Updated wording across 3 surfaces (`MeshJobs.java`,
`JobProxy.java`, `jobs_java.md`) to cite the real reason: readability
confusion with the inherited `Object.wait()` overload family + the
existing `JobProxy.await(double)` precedent.
- **I2 — Test message symmetry**: `JobTerminalException` translation
test now asserts message preservation matching the
`JobNotFoundException` test.
- **I3 — `@DisabledIfEnvironmentVariable`**: the three
`*_failsCleanlyWhenRegistryUrlUnset` tests now use JUnit 5's declarative
annotation instead of silent `return` — skips are visible in test
reports, no green-test illusion in CI shells that export
`MCP_MESH_REGISTRY_URL`.

I4 (4-column docs table) skipped as cosmetic.

Closes #1080

## Test plan

- [x] `mvn install -pl mcp-mesh-core,mcp-mesh-sdk -am`: BUILD SUCCESS
- [x] `mvn test -pl mcp-mesh-sdk`: 98 tests pass (`MeshJobsTest` 27/27)
- [x] `@DisabledIfEnvironmentVariable` verified visible in report when
env set
- [x] `tsuite uc23_meshjob_java --parallel 4`: 27/27
- [x] `mkdocs build` clean
- [x] Cross-runtime scope: no edits to Python / TS / Rust / FFI / JNR /
`JobProxy.cancel/status/await` instance methods / Python `jobs.md` / TS
`jobs_typescript.md`
- [x] Public-artifact framing: no naming downstream consumers;
structural language throughout


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added convenience static methods to manage job lifecycle—cancel, check
status, and await completion—using only a job ID, without requiring a
JobProxy instance.

* **Documentation**
* Enhanced job lifecycle documentation with comprehensive examples,
detailed timeout semantics, and cross-runtime operation guidance.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/dhyansraj/mcp-mesh/pull/1081?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
dhyansraj added a commit that referenced this pull request May 23, 2026
## Summary

**v2.3.0 — Lifecycle facades across the polyglot trilogy + unified
dependency-injection contract.**

v2.2 introduced the MeshJob substrate. v2.3 completes the lifecycle
surface so callers that hold only a `job_id` can drive `cancel` /
`status` / `wait` through DDDI-clean module-level facades — the same
shape `post_event` and `subscribe_events` already had. The DI rules for
`McpMeshTool` and `MeshJob` parameters are unified under a single
positional contract, eliminating a silent wrong-proxy footgun when both
types appeared in the same tool.

## What ships

### Lifecycle facades by `job_id` (Python #1077, TS #1079, Java #1081)

| Operation | Python | TypeScript | Java |
| ------------------------ |
-------------------------------------------------- |
------------------------------------------------ |
--------------------------------------------- |
| Cancel a running job | `await mesh.jobs.cancel(job_id, reason=None)` |
`await mesh.jobs.cancel(jobId, reason?)` | `MeshJobs.cancel(jobId[,
reason])` |
| Read latest job state | `await mesh.jobs.status(job_id)` | `await
mesh.jobs.status(jobId)` | `MeshJobs.status(jobId)` |
| Wait for terminal state | `await mesh.jobs.wait(job_id,
timeout_secs=None)` | `await mesh.jobs.wait(jobId, timeoutSecs?)` |
`MeshJobs.await(jobId[, timeoutSecs])` |

Underlying `JobProxy.cancel/status/wait` was already shipped in v2.2;
this adds the module-level wrappers that resolve the registry URL
internally — no more `JobProxy(jobId, registryUrl)` plumbing in user
code. Typed errors (`JobNotFoundError` / `JobTerminalError`) translate
consistently across all three runtimes. TS adds a typed `JobStatus`
interface exported from `mesh.jobs`.

### Unified positional dependency injection (#1082, Python)

`McpMeshTool` and `MeshJob` parameters now share a **single positional
`dep_index` namespace** in parameter declaration order. Each
`dependencies[i]` strictly pairs with one parameter position; the slot's
type determines what gets constructed. Previously, the two types had
inconsistent injection rules (positional for `McpMeshTool`, by-name for
`MeshJob`), producing wrong-proxy injection when both appeared in the
same tool with the `MeshJob` capability listed first in
`dependencies[]`.

**Behavior change to call out**: users who deliberately wrote `MeshJob`
params out-of-order with their `dependencies[]` array (relying on the
previous by-name resolution) now need to put params in the same order as
deps. The natural same-order case continues to work unchanged.

TypeScript and Java SDK DI paths still follow the orthogonal injection
contract — their port to the unified positional rule is tracked
separately.

### `health_check_ttl` refresh on the user loop (#1073)

`@mesh.agent(health_check=fn, health_check_ttl=N)` now actually
refreshes every N seconds. Previously, the result was stored exactly
once at startup and served forever — a failed check during startup
cached as unhealthy and permanently failed k8s readiness probes. The
refresh loop runs on the user loop (same loop as `lifespan` and tools)
so health checks touching loop-bound resources work without cross-loop
errors. A lifespan-ready signal gates the refresh start so iterations
don't fire during user `__aenter__`.

### FastMCP lifespan documentation correction (#1073)

The v2.2.4 "Loop topology" docs incorrectly showed a FastAPI-style
`app.state.pool` example — FastMCP's lifespan receives a server
instance, not a FastAPI app. Three doc surfaces rewritten to the
canonical module-level globals pattern.

## Mechanical bundle

- `scripts/bump_version.py 2.2.4 → 2.3.0` (419 files updated across 36
categories)
- `helm dependency update helm/mcp-mesh-core`
- `cargo generate-lockfile` (src/runtime/core)
- `RELEASE_NOTES.md` — new v2.3.0 entry with full narrative

## Post-merge

Per the established v2.2.3 / v2.2.4 publish pattern:

1. `/dev` reset to pull the merged main
2. `gh release create v2.3.0 --target main --title "v2.3.0" --notes-file
<v2.3.0 section> --latest` (one-shot)
3. Wait for all 4 publish jobs to land (npm × 7 packages, PyPI, Maven
Central, crates.io)
4. Verify `npm view @mcpmesh/core version` returns `2.3.0`

## Test plan

- [x] `mvn install` BUILD SUCCESS for Java
- [x] Python unit suite: 1010/1010
- [x] uc02_agent_lifecycle: 23/23
- [x] uc21_meshjob (Python): 21/21
- [x] uc22_meshjob_ts: 24/24
- [x] uc23_meshjob_java: 27/27
- [x] `mkdocs build` clean
- [ ] Release workflow fires cleanly across all 4 registries

Co-authored-by: Claude Opus 4.7 <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.

ts: mesh.jobs cancel/status/wait facades (TypeScript parity for #1074)

1 participant