Skip to content

refactor(sessions): one name per session lifecycle concept before release - #5083

Merged
ananthsub merged 7 commits into
mainfrom
ananthsub/session-lifecycle-names
Nov 5, 2026
Merged

ananthsub merged 7 commits into
mainfrom
ananthsub/session-lifecycle-names

Conversation

@ananthsub

@ananthsub ananthsub commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Gives every resources session and agent session lifecycle concept one name, used the same way in code, docstrings, error messages, tests, config descriptions and the docs, as #4997 did for partial-rollout checkpoints.

The session contract grew across several PRs, and the same concept ended up with several names:

  • ResourcesSessionFence did two jobs. It held the per-session lock that keeps a seed and a close from interleaving, and it kept the record that refuses a seed after the close. Servers therefore wrote self._fence.locked(...), which reads as a lock and a fence at once.
  • That record was called a closed record (ClosedResourcesSessionRecords.remember), a closed mark (mark_closed, closed_episode, raise_if_closed), a close receipt, and a tombstone, depending on the file.
  • How long the record lasts was the close window, the retry window, the retention window, window_seconds and close_window_seconds, although the released config field already calls it session_close_retry_window_seconds.
  • The record running out was "expire", "forgotten" and "the close receipt has expired".
  • A session an environment server seeds was a typed session, a typed seed or an Environment Server session, and a seed from an agent's own /run was an untyped seed.
  • A refusal the environment server does not retry was both "final" and "terminal".
  • Session routing called the worker that holds a session's state its claimant, while the checkpoint stack uses claim for a replacement attempt taking a restored record. Freeing state was both "free" and "release".
  • Agent servers refused conflicting session requests with a plain HTTPException(409), while resources servers had ResourcesSessionConflictError.

Nothing renamed here has shipped as a public API, so every rename is a plain rename with no aliases.

One term per concept.

Concept Name now Earlier names
Running a session's seed and close one at a time lock; ResourcesSessionLocks.locked() ResourcesSessionFence.locked, _FenceLock
The record a close leaves, which refuses a seed that arrives after the close fence; ResourcesSessionFences[T] with fence(), get(), raise_if_fenced(), clear() ClosedResourcesSessionRecords.remember, ResourcesSessionFence.mark_closed, closed_episode, raise_if_closed, "remembers the close", "close receipt", "tombstone"
How long a fence lasts close retry window; close_retry_window_seconds window_seconds, close_window_seconds, "close window", "retry window", "retention window"
A fence or failed seed running out at the end of that window expire; expire_agent_session_records() "forgotten", "close receipt has expired"
A retried close answered from the first close's response saved reply (the term #4997 settled) "close receipt", "retained close response"
Freeing a session's or an episode's state release; _release_retired_session(), on_session_released, SessionReleased _free_session, on_session_freed, SessionFreed, "free", "unfreed"
The worker that holds a session's state and serves its requests owner; SessionOwners, owner file in session-owners claim, claimant, SessionClaims, claim file in session-claims, routing claim
A replacement attempt taking a restored checkpoint record claim, unclaimed (unchanged, checkpoint sense only)
A request for a session the server does not hold lost; "No seeded <server> session for this <request>" "Unknown oracle agent session; seed it first"
A refusal the environment server does not retry terminal "final"
The state a resources server holds for an episode resources session "Resources Server session"
The session an environment server seeds, and its seed episode session, episode seed "typed session", "typed seed", "typed close", "Environment Server session"
A seed sent by an agent's own /run agent-run seed "untyped seed"
A 409 refusal of an agent session request AgentSessionConflictError HTTPException(409, ...)

"Expire" keeps one meaning in session code: a timed lifetime ran out. A fence expires, a checkpoint lease expires, and an open session whose lifetime runs out verifies as session_expired. The subject of the sentence tells them apart. SandboxSession.expire(), which ends a harness as if its time limit had passed, is listed in the glossary with the other words that other features use in their own senses.

Lifecycle.

flowchart TD
    seed["Episode seed, holding the session's lock"] --> open["Open session"]
    open --> work["Tool calls and verify"]
    work --> close["Close, holding the session's lock"]
    close --> release["Release the session's state"]
    release --> fence["Fence the session for the close retry window"]
    fence -->|"a seed arrives late"| refused["409 conflict, terminal"]
    fence -->|"the close is retried"| saved["Same answer; an agent returns the saved reply"]
    fence -->|"the close retry window passes"| expired_fence["The fence expires and the ID is new again"]
    work -->|"the server no longer holds the session"| lost["Lost: 404 on a tool call, masked verify with session_lost"]
    open -->|"its lifetime runs out (planned)"| expired_session["Expired: masked verify with session_expired"]
Loading

Locks and fences are now separate classes. ResourcesSessionFence is split in two. ResourcesSessionLocks holds the per-session lock, and ResourcesSessionFences[T] replaces both the fence half of ResourcesSessionFence and ClosedResourcesSessionRecords[T], which were the same structure with different method names. A fence holds a record of the server's choice: example_single_tool_call, enterpriseops_gym and gdpval store the closing episode, partner_if stores an optional episode, and the NOOA counter server stores the close request. A server that used ResourcesSessionFence now holds both:

_locks: ResourcesSessionLocks = PrivateAttr(default_factory=ResourcesSessionLocks)
_fences: ResourcesSessionFences[EpisodeId] = PrivateAttr()

async with self._locks.locked(resources_session_id):
    self._fences.raise_if_fenced(resources_session_id)

raise_if_fenced() refuses any fenced ID, including one whose record is None, as partner_if stores for a retired session. The old raise_if_closed() refused only when the stored episode was not None. No server's behavior changes: every server that calls it stores an episode, and partner_if tests membership itself. get() returns None both for an ID that is not fenced and for a None record, so its docstring tells such callers to test in. partner_if's SWE adapter keeps its fences in _adapter_fences, apart from the Scale-SWE _fences it inherits, as the two stores were apart before the rename.

Agent sessions. SimpleResponsesAPIAgent raises AgentSessionConflictError for every 409 it returned before, with the same status and, apart from the two messages below, the same detail. Like ResourcesSessionConflictError, it is also a ValueError for direct callers. A seed or close whose cookie names a session the server no longer holds is refused with "Agent session cookie names a session that is no longer available" and "Agent session is no longer available" instead of "Agent session cookie has expired" and "Agent close receipt has expired". Missing state does not by itself establish that a fence expired. The Codex agent's stale-cookie refusal follows the same wording. Agent subclasses and resources servers that raise a plain 409 HTTPException keep doing so; moving them onto the conflict errors is left for a follow-up.

Routing says owner. Session routing records which uvicorn worker holds each session and forwards the session's requests there. It already called that worker the owner in the x-ng-session-owner header and in routes_sessions_to_owner, and now says owner everywhere: SessionOwners with owner() and acquire(), owner files in a session-owners directory, and owned_session_routing(). Claim keeps only its checkpoint meaning. The rollout-stop routing #5145 added names its key /rollouts/<rollout id> with ROLLOUT_KEY_PREFIX and SessionKind.path_key. The checkpoint stack says release for freeing state, as the session code does.

Docs. The glossary page (About → Key Terminology) gains a Sessions section: one row per term with the names it replaces, and a note on the words other features use in their own senses (receipts, tombstones, token capture and launch fences, MCP token claims and the launch claim file, the checkpoint terminal step, and SandboxSession.expire()). Its Conflict row names the shared session code that raises the conflict errors and asks new session code to do the same, since many existing servers still raise a plain 409. Release also covers a worker releasing its ownership of a session ID, the opposite of acquiring it. AGENTS.md points assistants at that section in one line, so the table is not loaded into every session. The resources server API guide describes episode sessions, ResourcesSessionLocks and ResourcesSessionFences. The harness guide, the troubleshooting guide, the NeMo user simulator rollout pages and the Codex, Hermes, OpenClaw, OpenCode, NeMo user simulator and SWE-Together READMEs use fence, saved reply, close retry window and resources session.

Unchanged by decision:

  • session_close_retry_window_seconds already names the close retry window.
  • session_lost and session_expired already name lost and expired sessions.
  • _locked_agent_session and terminal_bench_2_1's _locked_resources_session already say lock.
  • The checkpoint names refactor(checkpoint): one name per checkpoint concept before release #4997 settled stay: retire and forget, mark_retired_sessions (a retired session is marked, not fenced, and its mark ends at forget), unclaimed, and ResourcesSessionLifetimes.retain and forget.
  • Token capture's delete stays; ResourcesSessionFences.clear() is documented as deleting every fence, in the same sense.
  • Other features' receipts and tombstones keep their names: the process supervisor's termination and cleanup receipts, export receipts, capture receipts, SWE-Together's cleanup receipts and judge_receipt_* gap codes, gdpval's journal tombstones and genrm_compare's cohort tombstones.
  • "Environment Server" as the name of the component that drives an episode keeps its capital letters; only "Environment Server session" moves to episode session.
  • "Legacy" for an agent's own /run stays, except where a sentence paired it with "typed": there it becomes agent-run seed.
  • An adapter's termination receipt, which records whether a harness confirmed it stopped, is a different concept from a close's saved reply and keeps its name.

Servers merged with the earlier names move onto the settled ones here too:

  • swebench_pro, interactive_browser and rna_design (fix(resources): move swebench_pro and interactive_browser onto the session contract #4692, feat(rna_design): RNA design environment #4931) move onto ResourcesSessionLocks and ResourcesSessionFences.
  • Merged since this PR opened: agentic_if, officeenv, partner_if's SWE adapter and scale_swe move from ClosedResourcesSessionRecords to ResourcesSessionFences. browsecomp_advanced_harness and the shared SWE sandbox-session base in swebench, which r2e_gym and swe_gym use, move from ResourcesSessionFence to both classes. Their prose, test names and attributes drop "typed", "Environment Server session" and "remembers the close" (for example _typed_sessions → _episode_sessions).
  • terminal_bench_4 keeps its own fence bookkeeping, now named for fences: _fenced_resources, _fence_expiry, _start_fence_expiry() and _expire_fences() instead of closed records and a retention window.
  • nemo_user_sim's closed_resources_session_ids → fenced_resources_session_ids.
  • Merged since the last rebase: feat(swebench): seed typed resources sessions for environment server episodes #4277 gave swebench's own seed_session an agent-run seed path that still called ResourcesSessionFence.closed_episode(), which this PR removes. It now tests membership in the shared base's _task_session_fences and refuses with "An agent-run seed cannot replace an episode session", as r2e_gym and swe_gym do. Its docstrings and test names say episode seed and agent-run seed, and the refusal test checks the message.

Renamed public names

For open PRs and downstream servers:

  • ResourcesSessionFence(window_seconds) → ResourcesSessionLocks() plus ResourcesSessionFences[EpisodeId](close_retry_window_seconds)
  • ResourcesSessionFence.locked() → ResourcesSessionLocks.locked()
  • ResourcesSessionFence.mark_closed() → ResourcesSessionFences.fence()
  • ResourcesSessionFence.closed_episode() → ResourcesSessionFences.get()
  • ResourcesSessionFence.raise_if_closed() → ResourcesSessionFences.raise_if_fenced()
  • ClosedResourcesSessionRecords[T](window_seconds) → ResourcesSessionFences[T](close_retry_window_seconds), and remember() → fence()
  • install_session_routing(close_window_seconds=...) and the claimed_session_routing() key close_window_seconds → close_retry_window_seconds
  • ResourcesSessionLifetimes(window_seconds) and its window_seconds attribute → close_retry_window_seconds
  • SimpleResponsesAPIAgent._free_session() → _release_retired_session(), which subclasses call from retire_agent_session
  • SimpleResponsesAPIAgent._closed_session_records → _fenced_session_records, which agent tests read
  • New: AgentSessionConflictError in nemo_gym.agents.base_responses_api_agent, exported from nemo_gym.agents
  • SessionClaims → SessionOwners; its claimant() → owner() and claim() → acquire()
  • SESSION_CLAIMS_DIR ("session-claims") → SESSION_OWNERS_DIR ("session-owners"), and SESSION_CLAIM_SCOPE_KEY → SESSION_ACQUIRE_SCOPE_KEY
  • app.state.nemo_gym_session_claims → app.state.nemo_gym_session_owners
  • claimed_session_routing() → owned_session_routing(), and SessionRoutingMiddleware(claims=...) → SessionRoutingMiddleware(owners=...)
  • AgentSessionHolder.use_agent_session_claims() → use_agent_session_owners(); SimpleResponsesAPIAgent._agent_session_claims → _agent_session_owners and _release_agent_session_claim() → _release_agent_session_ownership()
  • From feat(agent): let a caller stop a running rollout and still grade it #5145: ROLLOUT_CLAIM_PREFIX → ROLLOUT_KEY_PREFIX, SessionKind.path_claim (group claim) → path_key (group key), _claim_rollout() → _acquire_rollout_ownership() and _release_rollout_claim() → _release_rollout_ownership()
  • CheckpointParticipant.on_session_freed → on_session_released, and SessionFreed → SessionReleased

The import-surface fixture that guards old module paths records the new names in place of the removed ones for the six old paths that served them, as #5248 did for its rename.

Issue

No issue. The names came up while reviewing #4692, which moves swebench_pro and interactive_browser onto the resources session contract; most of the confusing terms there come from names already on main. Settling them before release keeps the open session PRs from spreading them further.

Open PRs that rebase onto this

These open PRs add servers or routing code with the earlier names; each needs only the renames above:

The sandbox checkpointing stack has the largest overlap, and the merge order matters. In merge order the stack is #4220 → #4221 → #4222 → #4576 → #4274, with drafts #4451 → #4553 → #4778 → #4779 → #4936 above it. Its branches still carry the multi-worker session routing commits from before #3909 was squashed, so #4220's diff against main adds about 130 lines with SessionClaims, claim/claimed, close_window_seconds and ClosedResourcesSessionRecords, and #4221 and #4222 add one more each. The stack needs a rebase onto main regardless. If this PR merges first, that rebase picks up the owner, release and fence names in the same pass. If the stack merges first, this PR has to rename the sandbox checkpointing code as well.

#4893 also edits the glossary page; whichever of the two merges second needs a small rebase.

Validation

After rebasing onto main at b5f8da9:

  • Core unit tests: pytest -n 16 tests/unit_tests in a venv synced with CI's extras. The failures are those listed below, which fail on main too.
  • swebench's server tests in its own venv, for the agent-run seed path this rebase moved onto the settled names.
  • The rebase's one conflict was in the multi-worker session routing end-to-end test, which main had rewritten; the resolution keeps main's version with owner() in place of claimant(). That test needs the end-to-end environment, so CI runs it.
  • pre-commit run --files on the changed files.

Before that, after rebasing onto main at af77ecc:

  • Core unit tests: pytest -n 16 tests/unit_tests in a venv synced with CI's extras. The import-surface checks pass with the updated fixture.
  • Server tests, in the root venv, for the servers this rebase changed whose dependencies the root extras cover: agentic_if, officeenv, partner_if, scale_swe, r2e_gym, swe_gym, gymnasium, example_session_state_mgmt, simple_agent, single_agent_turn and swe_together.
  • Server tests in each server's own venv, built from its pyproject.toml, for the servers whose packages the root extras lack: browsecomp_advanced_harness, swebench, terminal_bench_4, interactive_browser, nemo_user_sim (both the resources server and the environment server) and nooa_agent. The resources servers among them run check_resources_session_contract.
  • Before the rebase, the same server tests ran for every server the earlier commits touched: example_single_tool_call, enterpriseops_gym, gdpval, terminal_bench_2_1, codex_agent, harbor_oracle_agent, opencode_agent, hermes_agent, openclaw_agent, miniswe_sandboxed_agent, claude_code_agent, swebench_pro and rna_design.
  • pre-commit run --files on every file the branch changes.

Unit tests that fail locally fail on main at the same commit too: the benchmark-listing test, because this host's name resolves to 127.0.1.1, and the Harbor run-summary tests. Two Docker PTY tests failed once under the full parallel run and pass on their own; this branch does not touch the sandbox code.

haystack_agent and langgraph_agent change only a comment each, so they were not run.

Checkpoint stack, at f7d5ab2c7a, in a venv synced with CI's extras so that no checkpoint test skips for a missing package:

  • Every checkpoint unit test (pytest -n 16 tests/unit_tests -k checkpoint), across server runtime, models and coordination. Only two unrelated tests skip, for the optional comet and nemo.lens packages.
  • The checkpoint end-to-end suite with real server processes and the fake backend: NEMO_GYM_CHECKPOINT_E2E=1 pytest tests/e2e/checkpoint -k "not real_model".
  • Its multi-worker stress test with 64 rollouts in flight (NEMO_GYM_CHECKPOINT_MULTIWORKER_STRESS=64), which restores sessions across workers through the renamed owner, release and fence code.

The split of ResourcesSessionFence and ClosedResourcesSessionRecords into ResourcesSessionLocks and ResourcesSessionFences is the one refactor here that could change checkpoint behavior; these runs show none.

After the review follow-up commit:

  • pytest -n 16 tests/unit_tests/agents tests/unit_tests/server_runtime.
  • Server tests for every server that commit touches: codex_agent, openclaw_agent, opencode_agent, simple_agent, partner_if, r2e_gym, swe_gym, terminal_bench_2_1, agentic_if, swebench_pro, enterpriseops_gym and harbor in the root venv, and swebench, terminal_bench_4, nemo_user_sim (both servers), interactive_browser and nooa_agent in their own venvs.
  • pi_agent's tests pass except its pinned-runtime tests, which fail on this host because its Node runtime lacks webidl.util.markAsUncloneable; that commit only renames one pi_agent test.
  • hermes_agent's tests need packages neither venv has; that commit only renames one test name there.
  • officeenv's tests skip without the OfficeEnv package, so its renamed session code has not run here.
  • The new Codex test fails when the stale-cookie refusal is removed.
  • pre-commit run --files on the changed files.

Rollouts

N/A: the change renames classes, methods, parameters and messages and splits one class into two with the same behavior. No rollout, verifier or session routing behavior changes, and the session conformance checks cover every server whose session code changed.

Compatibility

  • Breaking for unreleased APIs only. ResourcesSessionFence, ClosedResourcesSessionRecords and the routing parameters have not been released.
  • Two agent 409 messages and the Codex agent's stale-cookie message change wording; their status codes do not change.
  • A request refused because another worker took ownership of the same new ID now reads "This session's ID is already owned by another worker. Retry it." instead of "... is already claimed", and session routing's refusal of a new session reads "This new session's ID is already owned by another worker. Retry it." instead of "This new session's ID is already claimed. Retry it.", and a forget refused while a retire is unfinished says "releasing" instead of "freeing". Status codes and error codes do not change.
  • r2e_gym, swe_gym and swebench refuse an agent-run seed over an episode session with "An agent-run seed cannot replace an episode session" instead of "A legacy seed cannot replace a typed resources session"; the status does not change.
  • Owner files move from session-claims to session-owners inside each server's runtime directory, which every server process creates fresh, so nothing on disk needs migrating.
  • A server that overrides claimed_session_routing() must rename it to owned_session_routing().
  • NeMo RL needs no change. Its main (at 9477426609) imports none of the renamed names. Its code imports the checkpoint stack's coordination module and AdmissionClosedError, and its tests also use ResourcesParticipant and RestartOnlyAgentParticipant, whose constructors and hooks keep their names, and reads the unclaimed status, which is unchanged. It handles a refused forget by its failures, not by the message text that now says "releasing".
  • The harbor_oracle_agent 404 for a request without a seeded session now reads "No seeded oracle agent session for this request".
  • check_agent_seed_rejected reports an unsupported seed that did not fail with "a terminal 422" instead of "a final 422"; a test that matches that assertion text needs the new word.

Checklist

  • I have read the contributing guidelines.
  • The change is focused; unrelated "drive-by" edits are tracked as separate issues/PRs.
  • Tests added or updated and pass locally.
  • Documentation added or updated.
  • Pre-commit checks pass locally (pre-commit run --files on the changed files).
  • New source files include the required Apache-2.0 SPDX header.
  • All commits have DCO sign-off (git commit -s).

@copy-pr-bot

copy-pr-bot Bot commented Oct 26, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@svcnvidia-nemo-ci svcnvidia-nemo-ci added area:core Shared APIs, servers, telemetry, health, and registries feature New capabilities, enhancements, or enablement work labels Oct 26, 2026
@ananthsub
ananthsub requested a review from yaoyu-33 October 26, 2026 16:20
@ananthsub
ananthsub marked this pull request as ready for review October 26, 2026 16:20
@nemo-automation-bot
nemo-automation-bot Bot requested review from a team October 26, 2026 16:21
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the needs-review PR is ready for code review and waiting on a reviewer label Oct 26, 2026
@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

@svcnvidia-nemo-ci svcnvidia-nemo-ci left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

SHIP

LGTM — no reliability concerns.

Reviewed all 50 changed files and traced affected seed, close, retire, routing, and exception-handling paths. The resources helper split preserves locking and non-renewing fence expiry. The agent migrations retain failed-cleanup retries and stale-cookie rejection, and the renamed routing keyword propagates consistently through server startup. No material defects found.

Static review only; tests were inspected, not executed.

Note: formal approval is not available for this request, so this review is posted as a comment only.

Review generated by codex. This was a full review. Request a full review with /review reset=true.

@copy-pr-bot

copy-pr-bot Bot commented Oct 26, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

yaoyu-33
yaoyu-33 previously approved these changes Oct 26, 2026

@yaoyu-33 yaoyu-33 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM for the naming/terminology scope. Fixed agent-run seed wording and unavailable-session messages without changing lifecycle behavior. Validation: 311 targeted tests passed, 6 Linux-only skips; pre-commit --all-files passed. Rollout N/A (wording-only fixes).

@yaoyu-33 yaoyu-33 added ready-to-merge PR is approved, current, and waiting for final CI or merge and removed needs-review PR is ready for code review and waiting on a reviewer labels Oct 26, 2026
@ananthsub

Copy link
Copy Markdown
Contributor Author

/ok to test d2ef399

@nemo-automation-bot
nemo-automation-bot Bot requested a review from a team October 26, 2026 21:01

Copy link
Copy Markdown
Contributor

💬 The author’s new head, f7d5ab2c, is mergeable, so my unpublished conflict resolution is no longer needed. CI is running with no failures reported so far. I’ll keep watching for the results and merge.

Copy link
Copy Markdown
Contributor

✅ CI is green on NVIDIA-NeMo/Gym PR #5083 at f7d5ab2c7a08 — still watching.

@ananthsub
ananthsub added this pull request to the merge queue Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

🔄 Merge queue validation started! You can track the progress here: https://github.com/NVIDIA-NeMo/Gym/actions/runs/42107460119

@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

🔄 Merge queue validation started! You can track the progress here: https://github.com/NVIDIA-NeMo/Gym/actions/runs/42108052103

@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

🔄 Merge queue validation started! You can track the progress here: https://github.com/NVIDIA-NeMo/Gym/actions/runs/42108265571

Copy link
Copy Markdown
Contributor

💬 All reported checks on f7d5ab2c have finished: 55 succeeded and 13 were skipped, with none failing or pending. The PR is still open; I’ll keep watching for the merge.

Merged via the queue into main with commit 0f64fca Nov 5, 2026
68 checks passed

Copy link
Copy Markdown
Contributor

The PR is no longer an open ready-to-merge PR targeting main.

@ananthsub
ananthsub deleted the ananthsub/session-lifecycle-names branch November 5, 2026 21:13
yfw added a commit that referenced this pull request Nov 6, 2026
Main split ResourcesSessionFence into ResourcesSessionLocks and ResourcesSessionFences (#5083).
A server holds the session's ResourcesSessionLocks lock around lend() and release().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>

This branch was previously deployed

1 inactive deployment
public — f7d5ab2c Deployed Nov 5, 2026 by copy-pr-bot[bot] via release / finalize / notify #10475
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core Shared APIs, servers, telemetry, health, and registries feature New capabilities, enhancements, or enablement work ready-to-merge PR is approved, current, and waiting for final CI or merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants