Skip to content
Open
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
199 changes: 199 additions & 0 deletions .agents/skills/release/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
---
name: release
description: Guide maintainers through the multi-step release process — version bump, CI verification, tagging, Maven Central deployment, SNAPSHOT bump, and versioned documentation.
compatibility: Requires gh CLI, mvn, and git
allowed-tools: Bash(gh:*) Bash(mvn:*) Bash(git:*) Bash(./update-version.sh:*) Read Edit Write Glob Grep
---

# Release Process

Guide the full release lifecycle. Proceed autonomously through mechanical steps (running scripts, polling CI, creating PRs) and pause only for genuine decisions, failures, or destructive actions.

## Phase 0: Determine Release Parameters

1. Read the current version from the root `pom.xml` — the `-SNAPSHOT` suffix indicates the current dev version.
2. Ask the user which version to release if not already specified.
3. Apply the **Final suffix convention**: if the user specifies a plain version like `1.2.0`, the release version is `1.2.0.Final`. Pre-release qualifiers (`Alpha1`, `Beta1`, `CR1`) are used as-is.
4. Suggest a sensible next SNAPSHOT version and confirm with the user:
- Final: `1.1.0.Final` → `1.1.1.Final-SNAPSHOT`
- Pre-release: `1.1.0.Alpha1` → `1.1.0.Alpha2-SNAPSHOT`
5. Determine the documentation plan:
- **Skip** for micro/patch releases (X.Y.Z where Z > 0)
- **Ask** for pre-releases (Alpha/Beta/CR)
- **Yes** for major/minor Final releases (X.Y.0.Final)

## Phase 1: Pre-Release Verification

1. Verify clean working tree:
```bash
git status
```
If there are uncommitted changes or we're not on `main`, stop and ask.

2. Check latest CI status on main:
```bash
gh run list --branch main --limit 5
```
If CI is failing, alert the user and stop.

3. Confirm the current SNAPSHOT version in `pom.xml` matches expectations.

## Phase 2: Version Bump & Release PR

1. Preview version changes:
```bash
./update-version.sh <current-SNAPSHOT> <release-version> --dry-run
```

2. Apply version update:
```bash
./update-version.sh <current-SNAPSHOT> <release-version>
```

3. Verify the build compiles (tests will run in CI):
```bash
mvn clean install -DskipTests
```
If the build fails, stop and report.

4. Create the release PR:
```bash
git checkout -b release/<version>
git add -A
git commit -m "chore: release <version>"
git push origin release/<version>
gh pr create --title "chore: release <version>" --body "Release <version>"
```

5. Wait for CI:
```bash
gh pr checks --watch
```
If there are flaky failures, rerun with `gh run rerun <run-id> --failed` and watch again.

6. **Ask the user for confirmation before merging.** Then merge:
```bash
gh pr merge --squash
```

## Phase 3: Tag & Deploy

1. Update local main:
```bash
git checkout main
git pull origin main
```

2. Create annotated tag:
```bash
git tag -a v<version> -m "Release <version>"
```

3. **Ask the user for confirmation before pushing the tag** — this is irreversible and triggers Maven Central deployment.

4. Push the tag:
```bash
git push origin v<version>
```
This triggers `release-to-maven-central.yml` and `create-github-release.yml`.

## Phase 4: Documentation (conditional)

Documentation is created before the SNAPSHOT bump so that Javadoc generation uses release version strings.

**Decision rules:**
- **Skip entirely** for micro/patch releases
- **Ask the user** for pre-releases (Alpha/Beta/CR)
- **Always do** for major/minor Final releases

When applicable:

1. Copy dev docs to the new version:
```bash
cp -r docs/content/dev docs/content/<version>
```

2. Create the version data file by copying `dev.yml` (it has the most up-to-date menu):
```bash
cp docs/data/versions/dev.yml docs/data/versions/<version>.yml
```

3. Edit `docs/data/versions/<version>.yml`:
- Set `label` to `"<version>"`
- Set `path` to `"<version>"`
- Set `sortOrder` to the next value — scan existing ymls for max `sortOrder` **excluding** `dev.yml` (which uses 999 as a sentinel), then increment by 1
- Set `defaultVersion` to `true` only for Final releases
- Set `devVersion` to `false`

4. For Final releases: set the previous default version's `defaultVersion` to `false`.

5. For pre-releases superseding a prior pre-release in the same X.Y.Z series: remove the old pre-release's content folder (`docs/content/<old-version>`), version yml (`docs/data/versions/<old-version>.yml`), and apidocs folder (`docs/public/<old-version>/apidocs/`).

6. Generate Javadoc:
```bash
mvn javadoc:aggregate -Psite-javadoc
mkdir -p docs/public/<version>/apidocs
cp -r target/reports/apidocs/* docs/public/<version>/apidocs/
```
If the `site-javadoc` profile doesn't exist, note it and skip.

7. Add Javadoc menu entry to the version yml if not already present.

8. Create and merge a docs PR:
```bash
git checkout -b docs/release-<version>
git add -A
git commit -m "docs: <version> release"
git push origin docs/release-<version>
gh pr create --title "docs: <version> release" --body "Versioned documentation for <version>"
gh pr checks --watch
```
If there are flaky failures, rerun with `gh run rerun <run-id> --failed` and watch again.
Once CI passes, merge:
```bash
gh pr merge --squash
```

## Phase 5: Bump to Next SNAPSHOT

1. Update local main:
```bash
git checkout main
git pull origin main
```

2. Bump to next SNAPSHOT:
```bash
./update-version.sh <release-version> <next-SNAPSHOT>
```

3. Create the SNAPSHOT PR:
```bash
git checkout -b chore/bump-to-<next-SNAPSHOT>
git add -A
git commit -m "chore: bump version to <next-SNAPSHOT>"
git push origin chore/bump-to-<next-SNAPSHOT>
gh pr create --title "chore: bump version to <next-SNAPSHOT>" --body "Bump version to <next-SNAPSHOT>"
gh pr checks --watch
```
If there are flaky failures, rerun with `gh run rerun <run-id> --failed` and watch again.

4. Check that the Maven Central deployment workflow completed successfully:
```bash
gh run list --workflow=release-to-maven-central.yml --limit 5
```
If the release workflow failed, stop and guide troubleshooting (check logs — common causes: expired tokens, javadoc issues). May need to delete the tag and retag.

5. Merge the SNAPSHOT PR once everything is green:
```bash
gh pr merge --squash
```

## Phase 6: Verify Deployment

Print the following URLs for the maintainer to check:

- **Maven Central**: `https://central.sonatype.com/artifact/org.a2aproject.sdk/a2a-java-sdk-parent/<version>`
- **GitHub Release**: `https://github.com/a2aproject/a2a-java/releases/tag/v<version>`

Note that Maven Central propagation can take up to 2 hours.
15 changes: 14 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,19 @@ mvn clean install

### Documentation Site

The docs site (`docs/`) uses versioned content folders (`docs/content/1.0.0.Final/`, `docs/content/1.1.0.Final/`, `docs/content/dev/`). When updating documentation to reflect code changes, only edit pages under `docs/content/dev/` — released version folders are frozen snapshots and must not be modified. New versioned folders are created at release time (see RELEASE.md).
The docs site (`docs/`) is built with [Roq](https://docs.quarkiverse.io/quarkus-roq/dev/index.html) (a Quarkus-based static site generator). Content is organized into versioned folders under `docs/content/<version>/` (e.g. `docs/content/1.1.0.Final/`, `docs/content/dev/`).

**Editing rules:**
- Only edit pages under `docs/content/dev/` — released version folders are frozen snapshots and must not be modified
- New versioned folders are created at release time (see RELEASE.md step 9)

**Version metadata:** Each version has a YAML file in `docs/data/versions/` (e.g. `dev.yml`, `1.1.0.Final.yml`) that defines the label, URL path, sort order, default/dev flags, and sidebar menu. When adding or removing a documentation page, update the `menu` list in `docs/data/versions/dev.yml` accordingly.

**Running the docs site locally:**
```bash
cd docs
mvn quarkus:dev
```

### PR instructions
- Follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) for the commit title and message
Expand All @@ -86,6 +98,7 @@ The docs site (`docs/`) uses versioned content folders (`docs/content/1.0.0.Fina

- [update-a2a-proto](.agents/skills/update-a2a-proto/SKILL.md) — Update the gRPC proto file `a2a.proto` from upstream and regenerate Java sources
- [fix-tck-issue](.agents/skills/fix-tck-issue/SKILL.md) — Analyze and fix A2A TCK compatibility issues across transports
- [release](.agents/skills/release/SKILL.md) — Guide the full release process: version bump, CI, tagging, Maven Central deploy, docs, SNAPSHOT bump

### Commands

Expand Down
9 changes: 5 additions & 4 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@ Wait for all CI checks to pass before proceeding.
### 5. Merge Release PR

Once all checks pass and the PR is approved:
- Merge the PR to `main` branch
- **Do NOT squash** - keep the release commit message intact for changelog
- Merge the PR to `main` branch (squash merge — enforced by repo settings)

### 6. Tag and Push

Expand Down Expand Up @@ -179,7 +178,7 @@ Edit `docs/data/versions/X.Y.Z.Final.yml`:
- Set `sortOrder` to the next number (higher than the previous release)
- Set `defaultVersion` to `true`
- Set `devVersion` to `false`
- Adjust the `menu` list if the new version adds or removes pages
- Verify the `menu` list matches the pages in the new version's content folder (add/remove entries if pages were added or removed since the previous release)

Update the previous default version's data file (e.g., `docs/data/versions/OLD_VERSION.yml`):
- Set `defaultVersion` to `false`
Expand All @@ -204,6 +203,8 @@ git commit -m "docs: add Javadoc for X.Y.Z.Final"

The Javadoc menu entry structure matches `dev.yml` — copy it into the new version's data file when creating version files.

**Validation**: The docs site enforces that exactly one version has `defaultVersion: true` and all `sortOrder` values are unique (see `docs/src/main/java/org/a2aproject/docs/Versions.java`). Run the docs site locally (`cd docs && mvn quarkus:dev`) to verify the new version renders correctly.

### 10. Increment to Next SNAPSHOT

Prepare repository for next development cycle:
Expand Down Expand Up @@ -293,7 +294,7 @@ Follow semantic versioning with qualifiers:
- **Major.Minor.Patch** - Standard releases (e.g., `1.0.0`)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Don't we use Final there ?

- **Major.Minor.Patch.AlphaN** - Alpha releases (e.g., `0.4.0.Alpha1`)
- **Major.Minor.Patch.BetaN** - Beta releases (e.g., `0.3.0.Beta1`)
- **Major.Minor.Patch.RCN** - Release candidates (e.g., `1.0.0.RC1`)
- **Major.Minor.Patch.CRN** - Candidate releases (e.g., `1.0.0.CR1`)
- **-SNAPSHOT** - Development versions (e.g., `0.4.0.Alpha2-SNAPSHOT`)

## Workflows Reference
Expand Down
2 changes: 1 addition & 1 deletion docs/content/dev/authorization.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ The SDK discovers the bean via CDI automatically — no additional wiring needed

Authorization decisions rely on `context.getUser()` returning the authenticated user. How the user is populated depends on the transport:

- **JSON-RPC and REST**: The Quarkus route handler extracts the user from the Vert.x routing context (`rc.userContext()`) and sets it on `ServerCallContext` directly.
- **JSON-RPC and REST**: The Quarkus route handler extracts the user from the Vert.x routing context (`rc.user()`) and sets it on `ServerCallContext` directly.
- **gRPC**: The reference server includes a `QuarkusCallContextFactory` CDI bean that injects the Quarkus `SecurityIdentity` and maps it to the `ServerCallContext` `User`. This happens automatically when using the reference gRPC module. If you provide your own `CallContextFactory`, you are responsible for populating the user.

## Authorization Checks
Expand Down
17 changes: 7 additions & 10 deletions docs/content/dev/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,28 +96,25 @@ Task task = client.getTask(new TaskQueryParams("task-1234"));
Task task = client.getTask(new TaskQueryParams("task-1234", 10)); // with history limit

// Cancel a task
Task cancelled = client.cancelTask(new TaskIdParams("task-1234"));
Task cancelled = client.cancelTask(new CancelTaskParams("task-1234"));

// Subscribe to an ongoing task
client.subscribeToTask(new TaskIdParams("task-1234"));
client.subscribeToTask(taskIdParams, customConsumers, customErrorHandler);

// Retrieve the server agent card
AgentCard serverCard = client.getAgentCard();
AgentCard serverCard = client.getExtendedAgentCard();
```

## Push Notifications

```java
// Set a push notification configuration
PushNotificationConfig pushConfig = PushNotificationConfig.builder()
.url("https://example.com/callback")
.authenticationInfo(new AuthenticationInfo(List.of("jwt"), null))
.build();

TaskPushNotificationConfig taskConfig = TaskPushNotificationConfig.builder()
.id("config-4567")
.taskId("task-1234")
.pushNotificationConfig(pushConfig)
.url("https://example.com/callback")
.authentication(new AuthenticationInfo("bearer", "my-token"))
.build();

client.createTaskPushNotificationConfiguration(taskConfig);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe show the returned value

Expand All @@ -127,9 +124,9 @@ TaskPushNotificationConfig config = client.getTaskPushNotificationConfiguration(
new GetTaskPushNotificationConfigParams("task-1234", "config-4567"));

// List configurations
List<TaskPushNotificationConfig> configs =
ListTaskPushNotificationConfigsResult result =
client.listTaskPushNotificationConfigurations(
new ListTaskPushNotificationConfigParams("task-1234"));
new ListTaskPushNotificationConfigsParams("task-1234"));

// Delete a configuration
client.deleteTaskPushNotificationConfigurations(
Expand Down
15 changes: 5 additions & 10 deletions docs/content/dev/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ AgentCard card = AgentCard.builder()
.name("My Agent")
// ... other v1.0 fields ...
.supportedInterfaces(List.of(
new AgentInterface("jsonrpc", "http://localhost:9999")))
new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:9999")))
// v0.3 backward-compatibility fields:
.url("http://localhost:9999")
.preferredTransport("jsonrpc")
.preferredTransport(TransportProtocol.JSONRPC.asString())
.additionalInterfaces(List.of(
new Legacy_0_3_AgentInterface("jsonrpc", "http://localhost:9999")))
new Legacy_0_3_AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:9999")))
.build();
```

Expand Down Expand Up @@ -109,14 +109,9 @@ gRPC and REST transports are also available:
- `a2a-java-sdk-compat-0.3-client-transport-rest`

```java
AgentCard card = A2ACardResolver.builder().baseUrl("http://localhost:1234")
.build().getAgentCard();
AgentCard_v0_3 agentCard = A2A_v0_3.getAgentCard("http://localhost:1234");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe add a comment that says that 0.3 version makes this methid handle discovery


AgentInterface v03Interface = card.supportedInterfaces().stream()
.filter(i -> A2AProtocol_v0_3.PROTOCOL_VERSION.equals(i.protocolVersion()))
.findFirst().orElseThrow();

Client_v0_3 client = ClientBuilder_v0_3.forUrl(v03Interface.url())
Client_v0_3 client = Client_v0_3.builder(agentCard)
.withTransport(JSONRPCTransport_v0_3.class, new JSONRPCTransportConfigBuilder_v0_3())
.build();
```
Expand Down
11 changes: 11 additions & 0 deletions docs/content/dev/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ a2a.executor.max-pool-size=50

# Thread keep-alive time in seconds (default: 60)
a2a.executor.keep-alive-seconds=60

# Queue capacity for pending tasks (default: 100)
# When the queue is full, new threads are created up to max-pool-size
a2a.executor.queue-capacity=100
```

### Blocking Call Timeouts
Expand All @@ -45,6 +49,13 @@ a2a.blocking.consumption.timeout.seconds=5
a2a.blocking.reconciliation.timeout.seconds=1
```

### Agent Card Caching

```properties
# HTTP Cache-Control max-age for Agent Card responses in seconds (default: 3600)
a2a.agent-card.cache.max-age=3600
```

### Tuning Guidelines

- **Streaming Performance**: The executor handles streaming subscriptions. Too few threads can cause timeouts under concurrent load.
Expand Down
2 changes: 1 addition & 1 deletion docs/content/dev/extras.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Import the extras BOM to manage versions:
<dependencies>
<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-extras-bom</artifactId>
<artifactId>a2a-java-sdk-extras-bom</artifactId>
<version>$\{org.a2aproject.sdk.version}</version>
<type>pom</type>
<scope>import</scope>
Expand Down
Loading
Loading