| name | adl |
| description | Author and maintain projects built with ADL (Agent Definition Language) - the YAML manifest format from `inference-gateway/adl` that `adl-cli` turns into a full A2A agent scaffold (Go/Rust/TypeScript). Use when working inside a generated agent project (presence of `agent.yaml` + `.adl-ignore`), when editing the manifest, when implementing custom tools and skills against the generated scaffold, or when planning the schema-first / domain modelling for a new agent.
|
| license | Apache-2.0 |
ADL (Agent Definition Language) Expert
Use this skill when working in any project whose root contains an agent.yaml
manifest with apiVersion: adl.inference-gateway.com/v1 (or v2+ when it
ships) together with an .adl-ignore file. That pair is the fingerprint of a
project generated by adl-cli
from a schema in inference-gateway/adl.
Also use it when the user is authoring a fresh manifest (adl init) or
planning the domain model for a new agent.
Mental model
ADL is "OpenAPI for AI agents". A single declarative manifest names everything
the agent needs - capabilities, the AI provider, services, function-call
tools, markdown skills, server/auth, language runtime, sandbox, deployment -
and adl-cli turns it into an enterprise-ready, A2A-compatible project. You
own the manifest and the bodies of TODO placeholders; the CLI owns everything
else and will regenerate it on demand.
The contract has two halves you must keep straight:
| Concept | Where it lives | Who owns it | Regenerated on adl generate? |
|---|
Manifest (agent.yaml) | repo root | you | no - source of truth |
Scaffolding (main.go, config/, internal/<service>/, Dockerfile, CI, ...) | various | the generator | yes |
Custom tool bodies (tools/<name>.{go,rs,ts}) | tools/ | you, after a TODO scaffold | no - protected by .adl-ignore |
Custom service impls (internal/<service>/*.go) | internal/ | you, after a TODO scaffold | no - protected by .adl-ignore |
Skills (skills/<id>/SKILL.md) | skills/ | you (bare) or upstream (sourced) | no - whole dir protected |
If you forget which half a file belongs to, cat .adl-ignore - anything
matched there survives adl generate --overwrite.
Spec at a glance. Every spec.* top-level field the v1 schema defines:
spec.* field | Required? | Purpose |
|---|
capabilities | yes | A2A feature flags - streaming, pushNotifications, stateTransitionHistory (all three booleans) |
server | yes | port (1-65535), optional scheme, debug, auth.enabled |
language | yes | At least one of go, typescript, rust (each with its own required pair, e.g. module+version) |
agent | no | LLM provider/model/systemPrompt/maxTokens/temperature |
card | no | Static A2A agent-card metadata served at .well-known/agent-card.json |
services | no | Domain services declared as ports (type, interface, factory, description) |
config | no | Arbitrary per-section config maps; one section per service (env-mapped) |
tools | no | Function-call entrypoints - reserved built-in ids and user tools |
skills | no | Markdown playbooks - registry / GitHub source: / bare: true |
acronyms | no | String list the generator preserves in generated identifier casing |
artifacts | no | enabled: true to generate an artifacts server (filesystem or MinIO backend) |
hooks | no | post: [...] commands the CLI runs after each adl generate |
scm | no | provider, url, github_app, issue_templates, dependabot, ci, cd |
development | no | sandbox.{flox,devcontainer,dockerCompose} + ai.{claudecode,codex,gemini,opencode,infer} |
deployment | no | type: kubernetes | cloudrun plus the matching block |
The sections below cover each in turn. Anything not in this table is not in
v1 - if you see it in an existing manifest, treat it as a CLI extension and
verify against adl-cli's changelog.
Schema-first / domain-first workflow
ADL is schema-driven by design: the manifest is the domain model. Sequence
work so the manifest leads and the code follows.
- Model the domain in YAML before touching any source file.
- Name the bounded context in
metadata.name (lowercase, hyphenated) and
pin metadata.version (semver - ^\d+\.\d+\.\d+$).
- Declare the required spec frame first:
spec.capabilities (all
three booleans - streaming, pushNotifications,
stateTransitionHistory), spec.server.port, and at least one
spec.language.{go|typescript|rust} target. Almost always set
spec.agent (provider + model) and spec.card (A2A discovery) too -
they're optional in the schema but the agent is useless without them.
- Enumerate services (
spec.services.*) as ports: each gets a
type (service/repository/client/middleware), an interface,
a factory, and a description. One service per responsibility - don't
conflate database and cache.
- Enumerate config sections (
spec.config.*) as value objects. Use
dotted-name injection (config.database) to give a tool only the
subsection it needs.
- Enumerate tools (
spec.tools[]) as commands - the verbs the model
can call. Each one declares its JSON Schema (schema:) and the services
it depends on (inject:).
- Enumerate skills (
spec.skills[]) as the procedural knowledge - the
"how" and "when" for using the tools. Skills are markdown, not code.
- Validate the manifest before generating.
adl validate agent.yaml
checks shape against the pinned schema and rejects typos in reserved
namespaces (e.g. spec.config.tools.bash.tymeout_seconds).
- Generate.
adl generate --file agent.yaml --output . writes the
scaffold. The first run creates .adl-ignore with every file containing a
TODO marked for protection. Re-run with --overwrite to refresh
non-protected scaffolding after a manifest edit.
- Implement TODO bodies. Each generated
tools/<name>.{go,rs,ts} and
each internal/<service>/*.{go,rs,ts} ships with // TODO: markers
spelling out what to replace. The factory signature and the handler
signature are locked - don't rename them, or you'll desync from the
generator's expectations on the next regeneration of main.go.
- Test what you implemented (see "Testing custom tools" - this is the
non-negotiable part).
- Run.
task build && task run (Go/Rust) or the equivalent in the chosen
language. The generated Taskfile.yml is the entry point.
Capabilities, the LLM, and the agent card
Three top-of-spec blocks shape what the agent advertises, which model it
talks to, and how clients discover it. Set them before anything domain-
specific.
spec.capabilities (required). All three booleans must be present -
the validator rejects the manifest if any is missing:
capabilities:
streaming: true
pushNotifications: false
stateTransitionHistory: true
spec.agent (optional but near-universal). The LLM the generated agent
defers to. Provider is a fixed enum: openai, anthropic, ollama,
deepseek, google, mistral, groq, or "" for "configure at runtime
via env vars only". Temperature is bounded to 0-2; maxTokens must be ≥1.
agent:
provider: deepseek
model: deepseek-v4-flash
systemPrompt: |
You are a helpful A2A agent. Use the AVAILABLE SKILLS playbooks for
workflows; call tools for deterministic actions.
maxTokens: 4096
temperature: 0.3
spec.card (optional). Static fields for the A2A agent card served at
/.well-known/agent-card.json. Used by other agents and the Inference
Gateway registry for discovery. Fields are all free-form strings/arrays
(no enum constraints in v1):
card:
protocolVersion: "0.3.0"
preferredTransport: JSONRPC
defaultInputModes: [text, voice]
defaultOutputModes: [text, audio]
url: "https://my-agent.example.com:8443"
documentationUrl: "https://github.com/company/my-agent/docs"
iconUrl: "https://github.com/company/my-agent/icon.png"
Server, auth, and language targets
spec.server (required). Only port is mandatory. scheme (http/
https), debug, and auth.enabled are optional. v1 only models the auth
on/off toggle - specific providers (OIDC, JWT, ...) live in the generated
code and config sections, not the schema:
server:
port: 8443
scheme: https
debug: false
auth:
enabled: true
spec.language (required). At least one of three children; the
generator emits the corresponding scaffold. Required pairs per language:
| Language | Required fields | Optional |
|---|
go | module, version | - |
typescript | packageName, nodeVersion | - |
rust | packageName, version, edition | features[] |
language:
go:
module: github.com/example/my-agent
version: "1.26.2"
vendor.{deps,devdeps} (CLI extension, not in v1 schema). adl-cli
reads spec.language.<lang>.vendor.deps[] (runtime) and vendor.devdeps[]
(code generators / dev tools) for Go and Rust to pre-populate go.mod /
Cargo.toml. The v1 JSON Schema doesn't validate these keys, but it permits
additional properties so they round-trip cleanly:
language:
go:
module: github.com/example/my-agent
version: "1.26.2"
vendor:
deps:
- github.com/stretchr/testify@v1.10.0
devdeps:
- golang.org/x/tools/cmd/stringer@v0.20.0
Verify against the latest adl-cli changelog before relying on it; treat
schema-validated fields as load-bearing and extensions as best-effort.
Tools vs Skills (the often-confused distinction)
ADL distinguishes two complementary surfaces, and conflating them produces
unmaintainable agents. Apply the rule first, then write the entry.
Use a tool (spec.tools[]) when | Use a skill (spec.skills[]) when |
|---|
| The agent must invoke a deterministic operation (DB query, HTTP call, file write) | The agent must learn a workflow, policy, or response pattern |
| Inputs and outputs are structured (JSON Schema fits) | The instructions are prose |
| Implemented in code | Authored as markdown |
| Registered with the toolbox at startup | Loaded into the system prompt at startup via the AVAILABLE SKILLS: manifest |
A skill that needs to read files (e.g. its own SKILL.md body, or bundled
templates) requires - id: read in spec.tools and
spec.config.tools.read.enabled: true. The validator enforces this; don't
disable it.
Reserved built-in tools
spec.tools[] recognises five reserved ids that map to framework-supplied
implementations. They ship with their own unit tests (see
builtin/*_test.go.tmpl in the CLI templates), so you do not need to
write tests for them. You activate them - that's all:
| Reserved id | Purpose | Activation namespace |
|---|
read | Read a file (file_path, optional offset/limit) | spec.config.tools.read |
bash | Execute a whitelisted shell command with a timeout | spec.config.tools.bash |
write | Write content to a file (creates parent dirs) | spec.config.tools.write |
edit | Replace a unique old_string with new_string in a file | spec.config.tools.edit |
fetch | GET/HEAD an http(s) URL (host whitelist, byte cap) | spec.config.tools.fetch |
All five default to enabled: false. Opt in by listing the id alone (no
name, description, or schema - the generator owns those) and setting
enabled: true in the matching spec.config.tools.<id> block. The reserved
config block accepts only the typed keys the generator knows; typos like
tymeout_seconds fail validation by design.
Resolution precedence at runtime is env > compile-time literal > built-in
default (disabled). The kill-switch envs (A2A_BASH_DISABLED=1,
A2A_FETCH_DISABLED=1, etc.) override the compile-time enabled: true.
Each built-in accepts its own typed config keys under
spec.config.tools.<id>. Anything not on this list fails validation:
| Tool | Config keys |
|---|
read | enabled, max_lines (default file slice), allowed_roots[] (empty = project-wide) |
bash | enabled, whitelist[] (allowed commands), timeout_seconds |
write | enabled |
edit | enabled |
fetch | enabled, allowed_domains[] (entries starting with . match any subdomain), max_bytes, timeout_seconds, allow_downloads, download_dir |
A representative configuration that opts in the three tools with the
richest config surfaces:
config:
tools:
read:
enabled: true
max_lines: 2000
bash:
enabled: true
whitelist: [ls, cat, grep, find, rg, jq, wc, head, tail, git, go]
timeout_seconds: 30
fetch:
enabled: true
allowed_domains:
- pkg.go.dev
- .rust-lang.org
- raw.githubusercontent.com
max_bytes: 5242880
timeout_seconds: 20
allow_downloads: true
download_dir: /tmp/adl-fetch-cache
tools:
- id: read
- id: bash
- id: fetch
Custom tool implementation
A user tool is a full spec.tools[] entry - id, name, description,
optional tags, inject, and a JSON Schema for schema. The generator
produces tools/<name>.{go,rs,ts} with:
- a struct holding the injected dependencies (
logger, services, optional
config or config.<section> subsections),
- a constructor
New<PascalName>Tool(...) server.Tool whose signature mirrors
inject: in declaration order, and
- a handler method
<PascalName>Handler(ctx, args) (string, error) whose body
is a single // TODO: Implement <name> logic comment plus a placeholder
return.
You replace the handler body. Do not change:
- the struct name or its field order (regeneration of
main.go wires fields
positionally),
- the constructor signature,
- the handler method name.
Injection patterns you can use in inject::
inject:
- logger
- config
- config.database
- cache
Always prefer config.<section> over config - it keeps the tool's blast
radius small, makes unit tests trivial to set up, and the validator catches
mismatches between inject: and spec.config.* at generate time.
Testing custom tools (mandatory)
The built-in reserved tools (read, bash, write, edit, fetch) ship
with generated unit tests. Custom tools do NOT. This is the single most
common gap in ADL projects.
Verification step for every PR that adds or modifies spec.tools[]:
- For each entry whose
id is not in the reserved set above, confirm a
sibling test file exists - tools/<name>_test.go (Go), tools/<name>.rs
tests block (Rust), tools/<name>.test.ts (TypeScript) - and the file is
listed in .adl-ignore so regeneration won't clobber it.
- The test file MUST exercise the handler against a service mock for every
dependency in
inject:. Use the service's interface (declared in
spec.services.*.interface) - that's why it exists.
- Use table-driven tests (Go) / parameterised tests (Rust) /
describe+it
blocks (TypeScript) covering at least: happy path, invalid args, service
error.
- Run the language-native test command from the generated Taskfile:
task test (Go and Rust) or the equivalent target. CI must pass.
A minimal Go template for a custom tool test (adapt to the actual struct
name and interface):
package tools
import (
"context"
"errors"
"testing"
"go.uber.org/zap"
"github.com/stretchr/testify/require"
)
type stubDatabase struct{ err error; out string }
func (s *stubDatabase) Query(ctx context.Context, sql string) (string, error) {
return s.out, s.err
}
func TestQueryDatabaseTool_Handler(t *testing.T) {
cases := []struct {
name string
args map[string]any
db *stubDatabase
wantErr bool
}{
{"happy path", map[string]any{"query": "SELECT 1", "table": "t"}, &stubDatabase{out: `{"rows":1}`}, false},
{"missing query", map[string]any{"table": "t"}, &stubDatabase{}, true},
{"backend error", map[string]any{"query": "SELECT 1", "table": "t"}, &stubDatabase{err: errors.New("boom")}, true},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
tool := &QueryDatabaseTool{logger: zap.NewNop(), database: tc.db}
_, err := tool.QueryDatabaseHandler(context.Background(), tc.args)
if tc.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}
Skip this and the agent ships untested business logic - the schema can't
catch a misread args["query"].(string) or a swallowed service error.
The .adl-ignore contract
adl-cli writes this file on first generation. It works like .gitignore
but applies to the generator - anything matched is preserved across
adl generate --overwrite. The generator automatically adds:
- every custom tool file (
tools/<name>.<ext>),
- every custom service implementation (
internal/<service>/*),
- every
bare: true skill directory (skills/<id>/).
You can extend it to protect anything else you've hand-edited:
# .adl-ignore
Dockerfile # custom container build
k8s/ # using ArgoCD overlays elsewhere
Taskfile.yml # extended with project-specific targets
README.md # written by hand
Patterns: # for comments, trailing / for directories, * wildcards,
exact paths.
When a regeneration goes wrong: check .adl-ignore first. A missing
entry there is the usual cause of "my custom code disappeared after I edited
the manifest."
Services and configuration
Services are first-class - declare them in spec.services with type,
interface, factory, and description, and the generator creates
internal/<service>/<service>.{go,rs} with an interface stub and a factory
that takes (*zap.Logger, *config.Config).
type is a closed enum - pick the one that best names the role so the
generator can emit idiomatic scaffolding:
type | Use it for |
|---|
service | Domain logic / orchestration (default choice when in doubt) |
repository | Persistence and data-access ports - DBs, object stores |
client | Outbound HTTP / gRPC / RPC clients to another system |
middleware | Request-pipeline behaviour - auth, logging, rate limits |
interface and factory must match ^[a-zA-Z][a-zA-Z0-9_]*$ - they
become real identifiers in the generated code. Three rules keep this
maintainable:
- One interface per responsibility. Don't ship a
UtilService. Split
database and cache even if they share a backend.
- Inject the interface, never the implementation. Tools depend on
database.DatabaseService, not on *database.databaseService. This is
what makes the unit-test stubs above trivial.
- Configuration mirrors services. A service named
googleCalendar
reads its settings from spec.config.googleCalendar -> generated as
GoogleCalendarConfig with GOOGLE_CALENDAR_* env prefix. Don't
hand-wire env vars; let the prefix mapping do it.
Skills inside an ADL agent
spec.skills[] accepts three entry shapes, resolved by adl-cli:
| Shape | Behaviour |
|---|
id: <name> (and optional version: <semver>) | Fetched from https://registry.inference-gateway.com/skills/. Override with ADL_SKILLS_REGISTRY. |
id: <name> + source: <shorthand-or-URL> | The whole GitHub directory (SKILL.md + any bundled assets) is pulled into skills/<id>/. |
id: <name> + bare: true (+ name, description, tags, optional license) | Scaffolded locally as a TODO. Author it by hand. |
source: shorthand:
- id: skill-creator
source: skill-creator
- id: skill-creator
source: skill-creator@v1.0
- id: pdf
source: anthropics/skills/pdf
- id: pdf
source: anthropics/skills/pdf@abc1234
- id: custom
source: https://github.com/my-org/my-repo/tree/release/path/to/skill
At runtime, the generated agent walks first-level subdirectories under
skills/, parses each <id>/SKILL.md's frontmatter, and appends an
AVAILABLE SKILLS: block to the system prompt - the bodies are not
inlined. The model loads them on demand via the read tool, so a
skills-using agent must opt read in (see "Reserved built-in tools").
license: on a skill entry must be one of the SPDX identifiers the schema
accepts (MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, GPL-2.0,
GPL-3.0, LGPL-2.1, LGPL-3.0, MPL-2.0, ISC, CC0-1.0,
CC-BY-4.0, CC-BY-SA-4.0, Unlicense) or the literal Proprietary. SPDX
expressions like MIT OR Apache-2.0 are not currently accepted.
Local development: sandbox and AI assistants
spec.development configures the experience of working on the agent
locally - reproducible dev environments and AI-assistant onboarding files.
Two independent subsections, both optional.
spec.development.sandbox. Three alternative packagings - pick any
combination; each declares its own enabled boolean. Generated artefacts:
| Sub-block | enabled: true generates |
|---|
flox | .flox/env/manifest.toml (Flox/Nix-backed reproducible env) |
devcontainer | .devcontainer/devcontainer.json (VS Code Dev Containers) |
dockerCompose | docker-compose.yaml (with the artifacts server wired in when spec.artifacts.enabled: true) |
spec.development.ai. Per-agent toggles for the coding assistants the
project is meant to be edited with. Each is independent; all default off.
The CLI generates each agent's onboarding file the first time it's enabled,
and keeps it in sync on subsequent adl generate --overwrite runs unless
you list it in .adl-ignore:
| Sub-block | Generated file |
|---|
claudecode | CLAUDE.md (Anthropic Claude Code) |
gemini | GEMINI.md (Google Gemini) |
codex | shared AGENTS.md (OpenAI Codex) |
opencode | shared AGENTS.md |
infer | shared AGENTS.md (Inference Gateway infer) |
A small example combining sandbox + AI toggles:
development:
sandbox:
flox:
enabled: true
devcontainer:
enabled: false
dockerCompose:
enabled: true
ai:
claudecode:
enabled: true
codex:
enabled: true
spec.development.deps[] (CLI extension, not in v1 schema).
adl-cli v0.38.0+ reads a cross-cutting list of sandbox-level tool
dependencies to install across every enabled sandbox flavour. Treat it as
a CLI convention - useful, but not schema-validated.
SCM, CI/CD, and deployment
Two related blocks that together drive everything outside main.go: where
the code lives, how it ships, and where it runs.
spec.scm. All fields optional; the provider enum is closed:
| Field | Effect |
|---|
provider | github | gitlab | bitbucket - selects the workflow templates |
url | Repository URL (used in generated README.md, agent card, etc.) |
github_app | Generate a GitHub App / token configuration in CI |
issue_templates | Write .github/ISSUE_TEMPLATE/*.md |
dependabot | Write .github/dependabot.yml |
ci | Write .github/workflows/ci.yml |
cd | Write .github/workflows/cd.yml and .releaserc.yaml (semantic-release) |
spec.deployment. Choose type: kubernetes or type: cloudrun; the
matching sub-block carries the detail. Both share an image shape with
registry, repository, tag, optional useCloudBuild.
type: kubernetes generates k8s/deployment.yaml:
deployment:
type: kubernetes
kubernetes:
image:
registry: ghcr.io
repository: example/my-agent
tag: v1.0.0
type: cloudrun generates cloudrun/ helpers plus a deploy target in
the Taskfile.yml. Fields map 1:1 to Cloud Run concepts:
deployment:
type: cloudrun
cloudrun:
image:
registry: gcr.io
repository: my-agent
tag: v1.0.0
useCloudBuild: true
resources:
cpu: "1"
memory: 512Mi
scaling:
minInstances: 0
maxInstances: 100
concurrency: 1000
service:
timeout: 3600
allowUnauthenticated: true
serviceAccount: my-agent@PROJECT_ID.iam.gserviceaccount.com
executionEnvironment: gen2
environment:
LOG_LEVEL: info
ENVIRONMENT: production
adl generate exposes equivalent CLI flags (--ci, --cd,
--deployment kubernetes|cloudrun, --flox, --devcontainer) that OR
with the manifest values. Prefer the manifest for anything that needs to
be reproducible across machines and CI runs - treat the flags as
one-off escape hatches.
Artifacts and post-generate hooks
Two small spec blocks that round out the manifest.
spec.artifacts.enabled. Set true to generate an artifacts server -
a small HTTP service for storing task outputs - and to wire the matching
create_artifact / read_artifact / list_artifacts helpers into the
generated code. The backend (filesystem vs MinIO) is chosen at runtime via
env vars on the generated binary, not via the manifest. When
spec.development.sandbox.dockerCompose.enabled: true, the MinIO instance
is wired into the generated docker-compose.yaml (adl-cli v0.39.0+).
artifacts:
enabled: true
spec.hooks.post. A list of shell commands the CLI runs at the end of
every adl generate. Use for the things a generator can't reasonably
infer:
hooks:
post:
- go mod tidy
- go generate ./...
Common commands
adl init is for bootstrapping a fresh agent.yaml; adl validate /
adl generate are the steady-state loop. Run from the repo root:
adl init [name]
adl init [name] --defaults
adl validate [file]
adl generate -f agent.yaml -o .
adl generate -f agent.yaml -o . --overwrite
adl generate ... --offline
adl generate ... --ci
adl generate ... --cd
adl generate ... --deployment kubernetes
adl generate ... --deployment cloudrun
adl generate ... --flox
adl generate ... --devcontainer
adl generate ... -t minimal
Once generated:
task build
task test
task run
task deploy
Validate the manifest in CI on every PR. Add a job step that runs
adl validate agent.yaml before any code build - it's faster and surfaces
schema drift early. Keep the manifest as the source of truth - prefer
spec.scm.ci: true over --ci, spec.deployment.type: cloudrun over
--deployment cloudrun, and so on. The flags exist for one-off enabling;
they are not a substitute for declarative state.
Cross-repo awareness
ADL touches several repos in the inference-gateway org. When a change in
one of them might ripple, surface it explicitly:
inference-gateway/adl (schema) -> consumers must bump the pinned tag in
their Taskfile.yml/internal/schema/. Breaking changes go to v2/, not
on top of v1/.
inference-gateway/adl-cli (generator) -> template edits land here, not
in the schema. A generator change without a schema change means existing
manifests keep working.
inference-gateway/skills (this catalog) -> if a spec.skills[] entry
uses source: <id> without an owner, it resolves here. Pin via @<tag>
for reproducibility.
For ecosystem-wide concerns (release flow, conventional commits,
cross-repo checklists), consult inference-gateway/.github (org-level
CLAUDE.md / README.md) and each repo's own CLAUDE.md.
What NOT to do
- Don't hand-edit generated files that are not in
.adl-ignore - they're
overwritten on the next adl generate --overwrite. If you need them
custom, add them to .adl-ignore (and accept that you've forked them
from the generator).
- Don't rename a custom tool's struct, constructor, or handler. Rename via
the manifest (
spec.tools[].name) and regenerate.
- Don't duplicate a reserved built-in id (
read, bash, write, edit,
fetch) as a custom tool. The generator owns those.
- Don't inject
config when you only need one section. Use
config.<section> - smaller blast radius, simpler tests.
- Don't make schema-breaking changes in
schema/v1/. They go to a new
major (schema/v2/) per the additive contract.
- Don't ship a custom tool without a
<name>_test.go (or language
equivalent). The reserved built-ins are tested upstream; yours are not.
- Don't conflate ADL with ADK. ADL is the manifest format; the ADK
(
inference-gateway/adk) is the Go runtime the generated main.go
imports.
- Don't omit
spec.capabilities. All three booleans (streaming,
pushNotifications, stateTransitionHistory) are required by the v1
schema; adl validate rejects the manifest if any are missing.
- Don't hand-edit generated
CLAUDE.md, AGENTS.md, or GEMINI.md. They're
re-emitted by the spec.development.ai.* generators on every
adl generate --overwrite unless you add them to .adl-ignore. If you
want the AI-onboarding docs to stay hand-written, ignore the file and
accept that you've forked it from the generator.