We'd love to accept your patches and contributions to this project.
- How to contribute
- Branches
- Multi-Module Development
- Experimental features
- Before you begin
- Contribution workflow
- AI-assisted development
- ADK Web
ADK Go uses two long-lived branches:
main— the actively developed 2.x line. This is the default branch and the base for new pull requests.v1— the maintenance branch for the 1.x line. Target this branch only for fixes that need to ship to 1.x.
The v1 branch is a snapshot of the 1.x line, branched from main before the
2.0 work landed. main then continued forward as the 2.x line (the 2.0 release
was merged into it), so its history is unbroken — old clones fast-forward
cleanly. There is no need to re-sync or rename anything locally:
git switch main
git pull # fast-forwards onto the 2.x lineTo work on a 1.x fix, base your branch on v1:
git switch -c my-fix origin/v1Three labels are in play, and only one of them does anything:
| Label | Meaning |
|---|---|
v1-needed |
This change still owes a 1.x equivalent. Drives the queue. |
v2 |
Informational: the PR targets main. |
v1 |
Informational: the PR targets the v1 branch. |
Add v1-needed while the context is fresh — at review time, not later. It is
the only signal the automation reads, so a fix that should reach 1.x without it
is a fix nobody backports.
Merging a v1-needed PR opens its backport by itself: the
Backport to v1 workflow replays the squash
commit onto a branch cut from v1 and opens a pull request against it. One
backport PR per original PR — a change that conflicts costs its own backport
and nothing else. The workflow works off the label queue rather than the merge
event, so a PR labelled after it merged is picked up by the nightly run, and
re-running on one that is already backported is a no-op rather than an error.
The workflow runs scripts/backport.sh, which is also usable directly — to
check the queue, or to work through a conflict the automation could not:
scripts/backport.sh --list # what is pending?
scripts/backport.sh 1301 # replay one PR into a scratch worktree
scripts/backport.sh --pr # replay the whole queue and open the PRsIt replays each commit on a detached HEAD in a scratch worktree under $TMPDIR,
so your working tree and your branches are left alone — it does add and remove
its own worktree in your clone, and prunes stale worktree registrations while
doing so. Nothing leaves the machine without --pr.
The script rewrites the module path (google.golang.org/adk/v2 →
google.golang.org/adk) in each patch before applying it. That difference is
otherwise the main source of cherry-pick conflicts, because it puts every Go
file's import block out of sync between the two branches.
A PR leaves the queue when its change reaches v1 — matched on the
(cherry picked from commit <sha>) trailer the script writes, not on anything
parsed out of a title — or while a backport pull request for it is open. Close
that pull request and delete its backport/v1/pr-<n> branch and the PR is
queued again, which is how you regenerate a backport that went stale. A branch
left behind by a run that died does not suppress anything: the next run replays
it.
Backport PRs get the usual CI, because go.yml runs on every pull request
whatever its base branch, and apidiff.yml lists v1 among the base branches
it filters on — but the runs start held. A pull request opened by
github-actions[bot] gets its workflows in an approval-required state, so open
the backport PR and click Approve workflows to run in the merge box. Anyone
with write access can.
That is what running on the built-in GITHUB_TOKEN costs, and it is worth
paying: no long-lived credential lives in the repository, and the click lands on
a pull request someone has to review anyway. The one repository setting it needs
is "Allow GitHub Actions to create and approve pull requests", under
Settings → Actions → General.
Authorship follows the same mechanism as today, with one visible change. Each
replayed commit keeps its original author, and squash-merging the backport PR
makes the PR owner the author of the commit that lands on v1, recording the
original author as a Co-authored-by: trailer. Because these pull requests are
opened by github-actions[bot], that owner is now the bot: git blame on v1
will point at it, and the human author is the trailer.
Two things the tooling cannot do for you:
- A clean apply is not a correct backport.
v1may lack a helper or a refactor thatmainalready had, so a patch can apply and still not compile. Build and test the backport branch before merging it. - Dependency changes need judgement. The 1.x dependency set differs from
main's, so
go.modhunks often reject. Re-run with--skip-gomodandgo mod tidythe module instead.
When a patch conflicts, a local run applies what it can and leaves .rej files
in the worktree to work from. The unattended run does not push a half-applied
tree: it comments on the original PR asking for a manual backport, once, and
leaves the PR queued so a later v1 change can still let it land on its own.
Policy: New integrations with heavy or optional dependencies must be created as separate Go modules.
Local Development: Contributors should use go work init && go work use -r . to set up their local workspaces.
Steps to Add a New Module (e.g., plugin/myplugin):
- Navigate into the directory:
cd <module_directory_path> - Initialize the module:
go mod init google.golang.org/adk/<module_directory_path> - Add your Go code, dependencies, and tests.
- Tidy the module:
go mod tidy - Return to the repo root.
- Tidy the root module:
go mod tidy - Add the module to your workspace:
go work use ./<module_directory_path> - Verify everything builds and tests from the root:
go build work && go test work. The CI will automatically pick up the new module on the PR.
Release Tagging:
- Core Module: Tags remain
vX.Y.Z(e.g.,v2.1.0). - Submodules: Tags are prefixed with the full module path directory, e.g.,
plugin/agentanalytics/v0.1.0. This is the standard Go way to version modules not at the repo root. - go get / go install: Consumers will use:
go get google.golang.org/adk/v2@v2.1.0go get google.golang.org/adk/plugin/agentanalytics@v0.1.0
- Version Coupling: Each submodule's
go.modwill specify the minimum version ofgoogle.golang.org/adk/v2it depends on. Submodules can be released independently of the core module and each other. - go.work Impact:
go.workis for local development only and does not affect how modules are versioned, tagged, or fetched by consumers.
A feature whose API or behavior is not settled yet lives under exp/, in a
package of its own, such as exp/appinfo. Anything there may change or be
removed in a later version without notice, and exp/doc.go tells
users so.
Use exp/ when a feature has to ship before its design is final: it serves an
outside contract that is still being agreed, or its API needs real use before
anyone can commit to it. A feature ready for the compatibility promise goes
straight into the stable packages, and a helper only ADK itself uses goes in
internal/.
A package under exp/:
- Is part of the root module.
exp/<feature>is a package ofgoogle.golang.org/adk/v2, not a separate module. It is released with everything else and can use the module'sinternal/packages. - Adds what it needs elsewhere as stable API. An accessor the feature needs in a stable package is ordinary API there, under the normal compatibility rules, and it stays when the experiment goes. Add only what is useful on its own.
- Has no
ExperimentalorXin its names. The import path already says it. Name things as they will be named once stable, so that graduating is a move rather than a rename. - Is listed in
exp/doc.go, and its own package doc says it is experimental.
apidiff checks exp/ like every other package, so an incompatible change
there still fails it. Label the PR breaking-change: under exp/ the break is
allowed, and the label records it rather than asking for an exception. Title it
feat(exp): … or fix(exp): … so it reaches the release notes, which are how
users of the feature find out. Do not mark it with ! or a BREAKING CHANGE:
footer, because release-please reads either as a new major version.
A feature that proves itself moves out of exp/ into the stable packages, and
the compatibility rules apply to it from then on. One that does not may be
removed.
All submissions to this project need to follow Google’s Contributor License Agreement (CLA), which covers any original work of authorship included in the submission. This doesn't prohibit the use of coding assistance tools, including tool-, AI-, or machine-generated code, as long as these submissions abide by the CLA's requirements.
You (or your employer) retain the copyright to your contribution; this simply gives us permission to use and redistribute your contributions as part of the project.
If you or your current employer have already signed the Google CLA (even if it was for a different project), you probably don't need to do it again.
Visit https://cla.developers.google.com/ to see your current agreements or to sign a new one.
This project follows Google's Open Source Community Guidelines.
All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Consult GitHub Help for more information on using pull requests.
- Browse issues labeled
good first issue(newcomer-friendly) orhelp wanted(general contributions). - For other issues, please kindly ask before contributing to avoid duplication.
- Code must follow Google Go Style Guide.
- All PRs, other than small documentation or typo fixes, must have an Issue
associated. If a relevant issue doesn't exist, please create one first, then
reference it from the PR description with
Closes: #<number>orRelated: #<number>. Describe the problem and the solution in the PR description as well, following the structure of our issue templates. - Small, focused PRs. Keep changes minimal—one concern per PR.
- Library code must not write diagnostics to stdout, stderr, the standard
logpackage's logger orslog's default logger, andgolangci-lintrejects it. See Logging and error messages inAGENTS.mdfor the levels to use and what to do instead. - Use Conventional Commits —
feat:,fix:,docs:and so on, optionally scoped asfix(runner):— in the PR title, and in the commit subject too on a single-commit PR, where that is what lands instead. Release tooling reads the landed subject to pick the next version and build the release notes, and silently skips anything with no recognized type. - If the change alters behavior for anyone already on the current release, say so in the description: what differs, and who is affected. This includes bug fixes, since a fix that alters an observable result is still a behavior change. If nothing changes for existing users, say that instead.
- For bug fixes or features, please provide logs or screenshots after the fix is applied to help reviewers better understand the fix.
- Please include a
testing plansection in your PR to talk about how you will test. This will save time for PR review. SeeTesting Requirementssection for more details.
Run the same checks CI runs, in every module. The repository is multi-module, so a run from the repository root covers only the root module.
First install the golangci-lint version CI pins. A newer release reports
findings CI does not, which reads as a failure you did not cause:
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.3.1Then set up the Go workspace and run every module:
test -f go.work || go work init
go work use -r .
for m in $(find . -name go.mod -not -path './.git/*' -exec dirname {} \;); do
( cd "$m" \
&& go mod tidy -diff \
&& go build -mod=readonly ./... \
&& go test -race -mod=readonly -count=1 -shuffle=on ./... \
&& golangci-lint run ) || echo "FAILED: $m"
donegolangci-lint run reports formatting problems too, as gofumpt and
goimports findings, but it does not fix them. Run golangci-lint fmt to
rewrite the files, or golangci-lint fmt --diff to see what it would change
without writing anything.
Then confirm each new test fails when your source change is reverted, and work
out what your change means for someone already on the current release. Both are
described in the Before you open a PR section
of AGENTS.md, which applies to human contributors just as much as to agents.
For substantial features or architectural revisions:
- Open an Issue First: Outline your proposal, including design considerations and impact.
- Gather Feedback: Discuss with maintainers and the community to ensure alignment and avoid duplicate work.
To maintain code quality and prevent regressions, all code changes must include comprehensive tests and verifiable end-to-end (E2E) evidence.
Please add or update unit tests for your change.
Requirements for unit tests:
- Cover new features, edge cases, error conditions, and typical use cases.
- Fail without your change. Revert the source change, keep the test, and run the package: it must go red. A test that passes either way is not testing your change. Name the failing test in your testing plan.
- Free of external dependencies (use mocks or fixtures as needed). Tests run
offline — LLM traffic is replayed from
testdata/*.httprr— so never add a live model or network call. - Named so that a failure message identifies what broke without opening the file.
- Fast and isolated, with comments for complex scenarios.
Manual E2E tests ensure integrated flows work as intended. Your tests should cover all scenarios. Sometimes, it's also good to ensure relevant functionality is not impacted.
Depending on your change:
-
ADK Web:
- Capture and attach relevant screenshots demonstrating the UI/UX changes or outputs.
- Label screenshots clearly in your PR description.
-
Runner:
- Provide testing setup. For example, the agent definition, and the runner setup.
- Include the command used and console output showing test results.
- Highlight sections of the log that directly relate to your change.
If your pull request is waiting on you, you have two weeks to respond. After that, a maintainer may take the change over and finish it. Waiting on you means review comments unanswered, CI red, or a question left hanging.
The two weeks run from the last thing that needed an answer from you. Any reply stops the clock, and a comment saying you need another week counts. Come back after a takeover has started and the pull request goes back to you, unless a replacement has already merged.
Your commits keep your name on them either way. The point is to get the change to users, not to take the work off you.
This repo ships skills for AI coding agents (Antigravity, Gemini CLI, Claude
Code, and others) in .agents/skills/. Compatible tools load them on their own;
otherwise read the relevant SKILL.md before starting that kind of work.
adk-go-self-review— reviewing a change before opening a PR, or before any later push that changes code: the fresh-context pass, the five lenses, and the mutation check that proves your tests pin the change.adk-sample-creator— authoring or reworking a runnable example underexamples/: directory layout,main.goanatomy, the README template with its diagram and transcript, and the checks to run before opening the PR.
AGENTS.md carries the rest of the project context an agent needs.
The web UI ships as a prebuilt Angular bundle, committed under
cmd/launcher/web/webui/distr/ and embedded into the server binary. It is built
from a pinned revision of adk-web, not from
the latest main.
- Run
./scripts/adk-web/update-adk-web.shto rebuild the bundle from the pinned revision. It reports the previous and the new upstream commit, and a URL comparing the two. - The committed bundle predates the pin and carries no provenance file, so
the pin does not yet describe what is in
distr/. The first refresh will therefore move the UI forward to the pinned revision rather than reproduce the current bundle. Review that refresh as a version bump. It reads the previous commit out of that missing file, so it reports the previous commit as unknown and prints no compare URL. Every refresh after it prints both. - Run
docker run -it adk-web-builder:latest sh -c "<COMMAND>"to start the container and debug the build, e.g.:docker run -it adk-web-builder:latest sh -c "ls -alh dist/agent_framework_web/browser"to view the built files.docker run -it adk-web-builder:latest sh -c "npm run build"to debug the build output.
Please leave the revision pinned. It used to track main, so one refresh
silently pulled in an upstream change that moved 30 API endpoints, and most of
the UI returned 404 for two months.
See scripts/adk-web/README.md for how to bump the pin deliberately, and for the checklist to apply when reviewing a refresh. A refresh also needs the UI-to-server route contract test to pass:
go test ./server/adkrest/ -run TestUIContractFor any changes that impact user-facing documentation (guides, API reference, tutorials), please open a PR in the adk-docs repository to update the relevant parts before or alongside your code PR.
We lean on adk-python for being the source of truth and one should refer to adk-python for validation.