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 pinmetadata.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 onespec.language.{go|typescript|rust}target. Almost always setspec.agent(provider + model) andspec.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 atype(service/repository/client/middleware), aninterface, afactory, and a description. One service per responsibility - don't conflatedatabaseandcache. - 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.
- Name the bounded context in
- Validate the manifest before generating.
adl validate agent.yamlchecks 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-ignorewith every file containing a TODO marked for protection. Re-run with--overwriteto refresh non-protected scaffolding after a manifest edit. - Implement TODO bodies. Each generated
tools/<name>.{go,rs,ts}and eachinternal/<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 ofmain.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 generatedTaskfile.ymlis 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 # SSE-based streaming responses
pushNotifications: false # webhook callbacks on long-running tasks
stateTransitionHistory: true # record task state transitions for replay
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: # CLI extension
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 # any subdomain of rust-lang.org
- raw.githubusercontent.com
max_bytes: 5242880 # 5 MiB
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, optionalconfigorconfig.<section>subsections), - a constructor
New<PascalName>Tool(...) server.Toolwhose signature mirrorsinject:in declaration order, and - a handler method
<PascalName>Handler(ctx, args) (string, error)whose body is a single// TODO: Implement <name> logiccomment plus a placeholder return.
You replace the handler body. Do not change:
- the struct name or its field order (regeneration of
main.gowires fields positionally), - the constructor signature,
- the handler method name.
Injection patterns you can use in inject::
inject:
- logger # always available, *zap.Logger (Go)
- config # the whole *config.Config
- config.database # only *config.DatabaseConfig - principle of least privilege
- cache # any name declared in spec.services
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
idis not in the reserved set above, confirm a sibling test file exists -tools/<name>_test.go(Go),tools/<name>.rstests block (Rust),tools/<name>.test.ts(TypeScript) - and the file is listed in.adl-ignoreso 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 inspec.services.*.interface) - that's why it exists. - Use table-driven tests (Go) / parameterised tests (Rust) /
describe+itblocks (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: trueskill 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. Splitdatabaseandcacheeven 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
googleCalendarreads its settings fromspec.config.googleCalendar-> generated asGoogleCalendarConfigwithGOOGLE_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 # inference-gateway/skills, main
- id: skill-creator
source: skill-creator@v1.0 # pinned tag
- id: pdf
source: anthropics/skills/pdf # different repo
- id: pdf
source: anthropics/skills/pdf@abc1234 # pinned commit SHA
- 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] # interactive wizard for a new agent.yaml
adl init [name] --defaults # non-interactive; accept the defaults
adl validate [file] # shape check; default file is agent.yaml
adl generate -f agent.yaml -o . # initial scaffold
adl generate -f agent.yaml -o . --overwrite # refresh after manifest edit (respects .adl-ignore)
adl generate ... --offline # skip skills registry; require local cache (~/.adl/skills-cache/)
adl generate ... --ci # also write .github/workflows/ci.yml
adl generate ... --cd # also write cd.yml + .releaserc.yaml
adl generate ... --deployment kubernetes # also write k8s/deployment.yaml
adl generate ... --deployment cloudrun # also write cloudrun/ helpers + Taskfile deploy target
adl generate ... --flox # enable Flox sandbox (OR with spec.development.sandbox.flox.enabled)
adl generate ... --devcontainer # enable DevContainer sandbox (OR with spec.development.sandbox.devcontainer.enabled)
adl generate ... -t minimal # template selector (currently only "minimal" ships)
Once generated:
task build # compile
task test # run unit tests - INCLUDING custom tool tests
task run # start the agent on spec.server.port
task deploy # only present when spec.deployment is set
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 theirTaskfile.yml/internal/schema/. Breaking changes go tov2/, not on top ofv1/.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 aspec.skills[]entry usessource: <id>without an owner, it resolves here. Pin via@<tag>for reproducibility.
For ecosystem-wide concerns (release flow, conventional commits,
cross-repo checklists), defer to the maintainer skill rather than
restating it.
What NOT to do
- Don't hand-edit generated files that are not in
.adl-ignore- they're overwritten on the nextadl 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
configwhen you only need one section. Useconfig.<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 generatedmain.goimports. - Don't omit
spec.capabilities. All three booleans (streaming,pushNotifications,stateTransitionHistory) are required by the v1 schema;adl validaterejects the manifest if any are missing. - Don't hand-edit generated
CLAUDE.md,AGENTS.md, orGEMINI.md. They're re-emitted by thespec.development.ai.*generators on everyadl generate --overwriteunless 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.
Source: inference-gateway/skills — distributed by TomeVault.