From 05c65abdb156b0f57680c3222688aa7089893ddc Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 3 Aug 2026 13:29:17 +0100 Subject: [PATCH 1/2] docs: fix inaccuracies in dev docs and improve documentation guidelines - Also add skill to perform releases Co-Authored-By: Claude Opus 4.6 (1M context) --- .agents/skills/release/SKILL.md | 199 ++++++++++++++++++ AGENTS.md | 15 +- RELEASE.md | 9 +- docs/content/dev/authorization.md | 2 +- docs/content/dev/client.md | 17 +- docs/content/dev/compatibility.md | 15 +- docs/content/dev/configuration.md | 11 + docs/content/dev/extras.md | 2 +- .../dev/extras/replicated-queue-manager.md | 17 +- docs/content/dev/server.md | 8 +- 10 files changed, 255 insertions(+), 40 deletions(-) create mode 100644 .agents/skills/release/SKILL.md diff --git a/.agents/skills/release/SKILL.md b/.agents/skills/release/SKILL.md new file mode 100644 index 000000000..22cbacb53 --- /dev/null +++ b/.agents/skills/release/SKILL.md @@ -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 --dry-run + ``` + +2. Apply version update: + ```bash + ./update-version.sh + ``` + +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/ + git add -A + git commit -m "chore: release " + git push origin release/ + gh pr create --title "chore: release " --body "Release " + ``` + +5. Wait for CI: + ```bash + gh pr checks --watch + ``` + If there are flaky failures, rerun with `gh run rerun --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 -m "Release " + ``` + +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 + ``` + 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/ + ``` + +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/.yml + ``` + +3. Edit `docs/data/versions/.yml`: + - Set `label` to `""` + - Set `path` to `""` + - 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/`), version yml (`docs/data/versions/.yml`), and apidocs folder (`docs/public//apidocs/`). + +6. Generate Javadoc: + ```bash + mvn javadoc:aggregate -Psite-javadoc + mkdir -p docs/public//apidocs + cp -r target/reports/apidocs/* docs/public//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- + git add -A + git commit -m "docs: release" + git push origin docs/release- + gh pr create --title "docs: release" --body "Versioned documentation for " + gh pr checks --watch + ``` + If there are flaky failures, rerun with `gh run rerun --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 + ``` + +3. Create the SNAPSHOT PR: + ```bash + git checkout -b chore/bump-to- + git add -A + git commit -m "chore: bump version to " + git push origin chore/bump-to- + gh pr create --title "chore: bump version to " --body "Bump version to " + gh pr checks --watch + ``` + If there are flaky failures, rerun with `gh run rerun --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/` +- **GitHub Release**: `https://github.com/a2aproject/a2a-java/releases/tag/v` + +Note that Maven Central propagation can take up to 2 hours. diff --git a/AGENTS.md b/AGENTS.md index 8475aa3b7..077352df0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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//` (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 @@ -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 diff --git a/RELEASE.md b/RELEASE.md index ef399422d..55ed14349 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -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 @@ -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` @@ -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: @@ -293,7 +294,7 @@ Follow semantic versioning with qualifiers: - **Major.Minor.Patch** - Standard releases (e.g., `1.0.0`) - **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 diff --git a/docs/content/dev/authorization.md b/docs/content/dev/authorization.md index e726cb5ee..f94a0862f 100644 --- a/docs/content/dev/authorization.md +++ b/docs/content/dev/authorization.md @@ -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 diff --git a/docs/content/dev/client.md b/docs/content/dev/client.md index c39bb24f9..acdf7d468 100644 --- a/docs/content/dev/client.md +++ b/docs/content/dev/client.md @@ -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); @@ -127,9 +124,9 @@ TaskPushNotificationConfig config = client.getTaskPushNotificationConfiguration( new GetTaskPushNotificationConfigParams("task-1234", "config-4567")); // List configurations -List configs = +ListTaskPushNotificationConfigsResult result = client.listTaskPushNotificationConfigurations( - new ListTaskPushNotificationConfigParams("task-1234")); + new ListTaskPushNotificationConfigsParams("task-1234")); // Delete a configuration client.deleteTaskPushNotificationConfigurations( diff --git a/docs/content/dev/compatibility.md b/docs/content/dev/compatibility.md index aae404e3e..e135c85cd 100644 --- a/docs/content/dev/compatibility.md +++ b/docs/content/dev/compatibility.md @@ -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(); ``` @@ -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"); -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(); ``` diff --git a/docs/content/dev/configuration.md b/docs/content/dev/configuration.md index 70d337bc8..d38d04de0 100644 --- a/docs/content/dev/configuration.md +++ b/docs/content/dev/configuration.md @@ -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 @@ -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. diff --git a/docs/content/dev/extras.md b/docs/content/dev/extras.md index 2a4478b5e..8b455ff87 100644 --- a/docs/content/dev/extras.md +++ b/docs/content/dev/extras.md @@ -19,7 +19,7 @@ Import the extras BOM to manage versions: org.a2aproject.sdk - a2a-java-extras-bom + a2a-java-sdk-extras-bom $\{org.a2aproject.sdk.version} pom import diff --git a/docs/content/dev/extras/replicated-queue-manager.md b/docs/content/dev/extras/replicated-queue-manager.md index 2d650bd93..b218fcce5 100644 --- a/docs/content/dev/extras/replicated-queue-manager.md +++ b/docs/content/dev/extras/replicated-queue-manager.md @@ -35,20 +35,19 @@ The system replicates these event types while preserving their specific types: - `Task` — complete task objects - `A2AError` — error events -Events are serialized using Jackson with polymorphic type information: +Events are serialized using Gson with member-name wrapping to preserve type information: ```json { "taskId": "task-123", "event": { - "@type": "TaskStatusUpdateEvent", - "taskId": "task-123", - "status": { - "state": "completed", - "timestamp": "2023-09-29T10:30:00Z" - }, - "final": true, - "kind": "status-update" + "statusUpdate": { + "taskId": "task-123", + "status": { + "state": "completed" + }, + "kind": "status-update" + } } } ``` diff --git a/docs/content/dev/server.md b/docs/content/dev/server.md index 39fe727dd..29cce4d11 100644 --- a/docs/content/dev/server.md +++ b/docs/content/dev/server.md @@ -107,7 +107,7 @@ public class WeatherAgentExecutorProducer { } @Override - public void execute(RequestContext context, AgentEmitter agentEmitter) throws JSONRPCError { + public void execute(RequestContext context, AgentEmitter agentEmitter) throws A2AError { if (context.getTask() == null) { agentEmitter.submit(); } @@ -121,14 +121,14 @@ public class WeatherAgentExecutorProducer { } @Override - public void cancel(RequestContext context, AgentEmitter agentEmitter) throws JSONRPCError { + public void cancel(RequestContext context, AgentEmitter agentEmitter) throws A2AError { Task task = context.getTask(); if (task == null) { agentEmitter.cancel(); return; } - if (task.status().state() == TaskState.CANCELED || - task.status().state() == TaskState.COMPLETED) { + if (task.status().state() == TaskState.TASK_STATE_CANCELED || + task.status().state() == TaskState.TASK_STATE_COMPLETED) { throw new TaskNotCancelableError(); } agentEmitter.cancel(); From 84e3d41dee7da255c0d417f63b50719b987357bc Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 4 Aug 2026 10:44:28 +0100 Subject: [PATCH 2/2] Review feedback --- RELEASE.md | 4 ++-- docs/content/dev/client.md | 2 +- docs/content/dev/compatibility.md | 1 + docs/content/dev/extras/replicated-queue-manager.md | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 55ed14349..c113bcc00 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -180,7 +180,7 @@ Edit `docs/data/versions/X.Y.Z.Final.yml`: - Set `devVersion` to `false` - 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`): +Update the previous default version's data file (e.g., `docs/data/versions/.yml`): - Set `defaultVersion` to `false` Review the new version's content for accuracy — ensure all pages reflect features available in this release. @@ -291,7 +291,7 @@ https://github.com/a2aproject/a2a-java/releases/new Follow semantic versioning with qualifiers: -- **Major.Minor.Patch** - Standard releases (e.g., `1.0.0`) +- **Major.Minor.Patch.Final** - Standard releases (e.g., `1.0.0.Final`) - **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.CRN** - Candidate releases (e.g., `1.0.0.CR1`) diff --git a/docs/content/dev/client.md b/docs/content/dev/client.md index acdf7d468..e829e55bf 100644 --- a/docs/content/dev/client.md +++ b/docs/content/dev/client.md @@ -117,7 +117,7 @@ TaskPushNotificationConfig taskConfig = TaskPushNotificationConfig.builder() .authentication(new AuthenticationInfo("bearer", "my-token")) .build(); -client.createTaskPushNotificationConfiguration(taskConfig); +TaskPushNotificationConfig created = client.createTaskPushNotificationConfiguration(taskConfig); // Get a specific configuration TaskPushNotificationConfig config = client.getTaskPushNotificationConfiguration( diff --git a/docs/content/dev/compatibility.md b/docs/content/dev/compatibility.md index e135c85cd..09ec47190 100644 --- a/docs/content/dev/compatibility.md +++ b/docs/content/dev/compatibility.md @@ -109,6 +109,7 @@ gRPC and REST transports are also available: - `a2a-java-sdk-compat-0.3-client-transport-rest` ```java +// getAgentCard() handles agent card discovery internally AgentCard_v0_3 agentCard = A2A_v0_3.getAgentCard("http://localhost:1234"); Client_v0_3 client = Client_v0_3.builder(agentCard) diff --git a/docs/content/dev/extras/replicated-queue-manager.md b/docs/content/dev/extras/replicated-queue-manager.md index b218fcce5..e58c1067e 100644 --- a/docs/content/dev/extras/replicated-queue-manager.md +++ b/docs/content/dev/extras/replicated-queue-manager.md @@ -35,7 +35,7 @@ The system replicates these event types while preserving their specific types: - `Task` — complete task objects - `A2AError` — error events -Events are serialized using Gson with member-name wrapping to preserve type information: +Events are serialized using Gson with member-name wrapping (the event type name becomes the JSON key wrapping the event data) to preserve type information: ```json {