Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions .agents/skills/draft-release-notes/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
name: draft-release-notes
description: Compare NeMo Relay release branches and draft or update the documentation-site release notes. Use when preparing a release-notes update under docs/about-nemo-relay/release-notes, reviewing release-to-release changes, or gathering evidence for a current release summary.
---

# Draft Release Notes

Draft the three documentation-site release-notes pages from verified repository
evidence. Keep complete, PR-by-PR release history in GitHub Releases.

## Gather Evidence

Run the read-only helper with explicit release refs and the target minor version:

```bash
python3 .agents/skills/draft-release-notes/scripts/collect_release_evidence.py \
--previous release/<previous-major>.<previous-minor> \
--current HEAD \
--version <major>.<minor>
```

The report verifies both refs, compares their release-notes trees, identifies
version text currently present in those pages, and groups commits into review
candidates. Treat the groups as an evidence index, not publication-ready copy.

## Workflow

1. Confirm the target release version from the release branch and package
metadata. Preserve unrelated working-tree changes.
2. Run the helper. It reports an absent prior release-notes directory without
failing, which is expected for early release branches.
3. Verify each candidate claim in the changed public docs, API types, command
help, or source before including it. Prioritize breaking changes, migrations,
user-visible features, and ongoing support limitations.
4. Update only these pages unless the release changes their route or entry
points:
- `docs/about-nemo-relay/release-notes/index.mdx`
- `docs/about-nemo-relay/release-notes/highlights.mdx`
- `docs/about-nemo-relay/release-notes/known-issues.mdx`
5. Keep the existing page roles:
- `index.mdx` gives the current-release summary, compatibility notes, scope,
and curated feature links.
- `highlights.mdx` groups notable changes by user-facing theme.
- `known-issues.mdx` records current limitations, compatibility migrations,
current-release fixes, and the complete fixed-item history recorded in
earlier release-note pages. Preserve every prior fixed-item bullet under
release-labelled subsections; do not summarize, deduplicate, or omit it.
GitHub Releases remains the complete PR-by-PR history.
6. Preserve MDX front matter and the JSX SPDX comment. State the full history
is available in GitHub Releases; do not create a changelog or GitHub Release
body from this skill.

## Validate

Run the helper for the target release and for an early branch without release
notes when available. Review public claims, then run:

```bash
git diff --check
just docs
just docs-linkcheck
```

Check product names, commands, package names, support claims, and links against
the current repository before handing off the draft.
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""Collect read-only Git evidence for NeMo Relay documentation release notes."""

from __future__ import annotations

import argparse
import re
import subprocess
from dataclasses import dataclass
from pathlib import Path

RELEASE_NOTES_DIR = "docs/about-nemo-relay/release-notes"
VERSION_PATTERN = re.compile(r"(?:NVIDIA )?NeMo Relay (\d+\.\d+(?:\.\d+)?)")


@dataclass(frozen=True)
class Commit:
"""One non-merge commit and its changed paths."""

sha: str
subject: str
paths: tuple[str, ...]


def run_git(repo: Path, *args: str) -> str:
"""Run Git without a shell and return stdout or raise a readable error."""
result = subprocess.run(
["git", *args],
cwd=repo,
check=False,
capture_output=True,
encoding="utf-8",
)
if result.returncode:
detail = result.stderr.strip() or result.stdout.strip()
raise RuntimeError(f"git {' '.join(args)} failed: {detail}")
return result.stdout


def resolve_ref(repo: Path, ref: str) -> str:
"""Return the commit ID for a user-supplied ref."""
return run_git(repo, "rev-parse", "--verify", "--quiet", f"{ref}^{{commit}}").strip()
Comment thread
willkill07 marked this conversation as resolved.


def note_files(repo: Path, ref: str) -> tuple[str, ...]:
"""List release-note files that exist at a ref."""
output = run_git(repo, "ls-tree", "-r", "--name-only", ref, "--", RELEASE_NOTES_DIR)
return tuple(path for path in output.splitlines() if path)


def note_versions(repo: Path, ref: str, paths: tuple[str, ...]) -> tuple[str, ...]:
"""Return the NeMo Relay versions mentioned in release-note files."""
versions: set[str] = set()
for path in paths:
content = run_git(repo, "show", f"{ref}:{path}")
versions.update(VERSION_PATTERN.findall(content))
return tuple(sorted(versions))


def commits(repo: Path, previous: str, current: str) -> tuple[Commit, ...]:
"""Read non-merge commits and their changed paths, newest first."""
output = run_git(repo, "log", "--no-merges", "--format=%H%x09%s", f"{previous}..{current}")
collected: list[Commit] = []
for line in output.splitlines():
sha, subject = line.split("\t", maxsplit=1)
names = run_git(repo, "diff-tree", "--no-commit-id", "--name-status", "-r", sha)
paths = tuple(entry.rsplit("\t", maxsplit=1)[-1] for entry in names.splitlines() if entry)
collected.append(Commit(sha=sha, subject=subject, paths=paths))
return tuple(collected)


def category(commit: Commit) -> str:
"""Classify a commit for review without treating the result as a release claim."""
subject = commit.subject.lower()
if "!" in commit.subject.split(":", maxsplit=1)[0] or re.search(
r"\b(remove|drop|breaking|deprecat|migration)\b", subject
):
return "Breaking and migration candidates"
if subject.startswith("feat"):
return "Feature candidates"
if subject.startswith(("fix", "perf")):
return "Fix candidates"
return "Documentation and tooling candidates"


def public_paths(commits_to_report: tuple[Commit, ...]) -> tuple[str, ...]:
"""Return changed paths likely to support public release-note claims."""
package_paths = {
"Cargo.toml",
"crates/node/package.json",
"integrations/openclaw/package.json",
"python/plugin/pyproject.toml",
}
return tuple(
sorted(
{
path
for commit in commits_to_report
for path in commit.paths
if path == "README.md"
or path.startswith("docs/")
or path.endswith("/README.md")
or path in package_paths
}
)
)


def print_note_tree(repo: Path, label: str, ref: str) -> None:
"""Print structure and version mentions for one release ref."""
paths = note_files(repo, ref)
print(f"## {label} Release-Notes Tree")
if not paths:
print(f"No files exist under `{RELEASE_NOTES_DIR}` at `{ref}`.\n")
return
print("Files:")
for path in paths:
print(f"- `{path}`")
versions = note_versions(repo, ref, paths)
description = ", ".join(versions) if versions else "no NeMo Relay version text detected"
print(f"Version text: {description}\n")


def main() -> int:
"""Parse arguments and print the evidence report."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--previous", required=True, help="Previous release branch or commit")
parser.add_argument("--current", required=True, help="Current release branch or commit")
parser.add_argument("--version", required=True, help="Target major.minor release version")
parser.add_argument("--repo", default=".", help="Repository root (default: current directory)")
args = parser.parse_args()

repo = Path(args.repo).resolve()
try:
previous_sha = resolve_ref(repo, args.previous)
current_sha = resolve_ref(repo, args.current)
run_git(repo, "merge-base", "--is-ancestor", previous_sha, current_sha)
except RuntimeError as error:
parser.error(str(error))

print(f"# Release-Note Evidence: {args.version}")
print()
print(f"- Previous: `{args.previous}` ({previous_sha[:12]})")
print(f"- Current: `{args.current}` ({current_sha[:12]})")
print(f"- Merge base: `{run_git(repo, 'merge-base', previous_sha, current_sha).strip()[:12]}`")
print()
print_note_tree(repo, "Previous", args.previous)
print_note_tree(repo, "Current", args.current)

changes = commits(repo, previous_sha, current_sha)
grouped: dict[str, list[Commit]] = {
"Breaking and migration candidates": [],
"Feature candidates": [],
"Fix candidates": [],
"Documentation and tooling candidates": [],
}
for commit in changes:
grouped[category(commit)].append(commit)

print("## Commit Candidates")
for heading, entries in grouped.items():
print(f"### {heading}")
if not entries:
print("- None")
for commit in entries:
paths = ", ".join(f"`{path}`" for path in commit.paths[:4])
suffix = f" — {paths}" if paths else ""
print(f"- `{commit.sha[:12]}` {commit.subject}{suffix}")
print()

print("## Changed Public Documentation and Package Surfaces")
paths = public_paths(changes)
if paths:
for path in paths:
print(f"- `{path}`")
else:
print("- None")
print()
print("Review every candidate against current public docs and APIs before drafting prose.")
return 0


if __name__ == "__main__":
raise SystemExit(main())
6 changes: 3 additions & 3 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
# Attribution files require dependency approver review due to change in version(s)
ATTRIBUTIONS-*.md @nvidia/nat-dep-approvers

# Documentation changes must go to technical writers
docs/ @lvojtku
fern/ @lvojtku
# Documentation changes must go to technical writers and NAT developers.
docs/ @lvojtku @nvidia/NAT-developers
fern/ @lvojtku @nvidia/NAT-developers
Comment thread
willkill07 marked this conversation as resolved.
35 changes: 18 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ SPDX-License-Identifier: Apache-2.0

# NVIDIA NeMo Relay

NVIDIA NeMo Relay helps see and control what happens inside agent runs
without rewriting the agent stack already made. It gives coding agents,
NVIDIA NeMo Relay provides visibility into and control over agent runs without
requiring changes to the existing agent stack. It gives coding agents,
applications, framework integrations, middleware, and observability backends a
shared runtime for scopes, policy, plugins, and lifecycle events.

## Where To Start

| Goal | Start With... |
| Goal | Start With |
|---|---|
| Observe Codex, Claude Code, or Hermes locally via CLI | [Quick Start CLI](https://docs.nvidia.com/nemo/relay/nemo-relay-cli/about) |
| Observe Codex, Claude Code, or Hermes locally with the CLI | [Quick Start CLI](https://docs.nvidia.com/nemo/relay/nemo-relay-cli/about) |
| Instrument app-owned LLM or tool calls | [Quick Start Application](https://docs.nvidia.com/nemo/relay/getting-started/quick-start) |
| Use LangChain, LangGraph, Deep Agents, or OpenClaw | [Supported Integrations](https://docs.nvidia.com/nemo/relay/supported-integrations/about) |
| Build a framework or provider integration | [Integrate into Frameworks](https://docs.nvidia.com/nemo/relay/integrate-into-frameworks/about) |
Expand All @@ -36,9 +36,8 @@ shared runtime for scopes, policy, plugins, and lifecycle events.

## Quick Start CLI

A good first step is to record a real agent run on disk. Once Relay is writing raw
events and a trajectory file, there is something concrete to inspect, debug, and
build from.
Start by recording a real agent run on disk. After Relay writes raw events and a
trajectory file, you have concrete data to inspect, debug, and build on.

### Local Agent Trajectory

Expand All @@ -50,6 +49,8 @@ trajectories.

#### 1. Install the CLI

Run the installer for your platform:

```bash
curl -fsSL https://raw.githubusercontent.com/NVIDIA/NeMo-Relay/main/install.sh | sh
```
Expand All @@ -65,7 +66,7 @@ nemo-relay --version
```

The installer supports Linux x86_64/ARM64, macOS Apple Silicon, and Windows
x86_64/ARM64. See the [installation guide](https://docs.nvidia.com/nemo/relay/getting-started/installation)
x86_64/ARM64. Refer to the [installation guide](https://docs.nvidia.com/nemo/relay/getting-started/installation)
for version pinning, custom directories, and source-based installation.

#### 2. Enable Local Observability Output
Expand All @@ -82,13 +83,13 @@ The editor creates or updates the nearest project plugin file at
then configure these sections:

1. Toggle the Observability component on.
2. Open `ATOF`, toggle the section `[on]`
2. Open `ATOF`. Toggle the section `[on]`.

Optionally set:
- `output_directory` to `.nemo-relay/atof`
- `filename` to `events.jsonl`
- `mode` to `overwrite`
3. Open `ATIF`, toggle the section `[on]`
3. Open `ATIF`. Toggle the section `[on]`.

Optionally set:
- `output_directory` to `.nemo-relay/atif`
Expand All @@ -97,12 +98,12 @@ then configure these sections:
5. Press `s` to save.

> [!NOTE]
> Use `nemo-relay plugins edit` _without_ `--project` only if needing to use these
> exporter settings in a user-level Relay config instead of a specific project.
> Run `nemo-relay plugins edit` without `--project` only when you want
> user-level exporter settings that apply across projects.

#### 3. Run Codex or Claude Code Through Relay

Use either host CLI that is installed on a machine. For example:
Run the Relay wrapper for the host CLI installed on your machine. For example:

```bash
nemo-relay codex -- exec "Summarize this repository."
Expand All @@ -119,8 +120,8 @@ and provider settings for that launched process, then shuts the gateway down
when the agent exits.

> [!WARNING]
> Codex users may need to review and activate generated hooks before events
> appear. Using the Codex Desktop App also adds further complications.
> If generated hooks are inactive, Codex users must review and activate them
> before events appear. The Codex Desktop App has additional limitations.
> Refer to the [Codex CLI guide](https://docs.nvidia.com/nemo/relay/nemo-relay-cli/codex) for the
> current hook activation caveat and troubleshooting steps.

Expand Down Expand Up @@ -170,7 +171,7 @@ A successful run creates several outputs to inspect:

#### Next Steps

Go to the full [NeMo Relay CLI](https://docs.nvidia.com/nemo/relay/nemo-relay-cli/about) docs for
Refer to the full [NeMo Relay CLI](https://docs.nvidia.com/nemo/relay/nemo-relay-cli/about) docs for
persistent host plugin installation, gateway configuration, exporter options,
and agent-specific diagnostics.

Expand Down Expand Up @@ -211,7 +212,7 @@ Then run a minimal example workflow for that binding:

## What Relay Adds

Relay is the liaison between agent systems. A production application may
Relay connects agent systems. A production application can
combine NeMo Agent Toolkit, LangChain, LangGraph, provider SDKs, custom harness
code, NeMo Guardrails, tracing systems, and evaluation pipelines. Relay gives
those pieces one runtime contract instead of asking every layer to invent its
Expand Down
2 changes: 1 addition & 1 deletion docs/about-nemo-relay/architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ Two distinctions matter:
- Intercepts affect the real execution path
- Sanitize guardrails affect the emitted observability payload

For the expanded request-to-response runtime path, including streaming and subscriber handoff, see [Middleware](/about-nemo-relay/concepts/middleware#detailed-execution-flow).
For the expanded request-to-response runtime path, including streaming and subscriber handoff, refer to [Middleware](/about-nemo-relay/concepts/middleware#detailed-execution-flow).

## Runtime Layers

Expand Down
Loading
Loading