Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Agent instructions — azure-container-apps

This file is read by AI coding agents (GitHub Copilot CLI, Claude Code,
Cursor, etc.) working in this repository. Follow these conventions when
authoring documentation, samples, skills, or code in this repo.

## Python tooling: lead with `uv`

`uv` is the canonical Python tooling for this repository. When writing
docs, skills, samples, or scripts that involve Python, **always lead
with `uv`** — never lead with bare `python`, `python3`, `pip`, or
`pip3`.

### Required commands

| Task | Use |
|---|---|
| Install a package | `uv pip install <pkg>` |
| Install from a local wheel | `uv pip install ./path/to/*.whl` |
| Create a virtual env | `uv venv` |
| Run a script | `uv run <script.py>` |
| Run a one-shot tool | `uvx <tool>` |
| Sync a project's deps | `uv sync` |
| Add a dependency to a project | `uv add <pkg>` |

### Hard rules

1. **Do not lead with `pip install …` / `pip3 install …`** in any
prose, code block, README, skill cue, or sample. Use
`uv pip install …`.
2. **Do not lead with `python script.py` / `python3 script.py`** to
invoke a script. Use `uv run script.py`.
3. **No "or" branches** that present `pip` / `python` / `python3` /
`pip3` as an equally-valid alternative. Example of what **not** to
write:

> Canonical install: `uv pip install foo` (or `pip install foo`).

Just write `uv pip install foo`. Period. No alternative.
4. Bare `python` / `python3` / `pip` / `pip3` are **acceptable inside
examples that are showing legacy behavior, error output, or
third-party docs we are quoting** — but never as the recommended
command. If they appear, they should be incidental, not the
instruction.
5. **Installing `uv` itself** uses the upstream installer:
`curl -LsSf https://astral.sh/uv/install.sh | sh` (or
`irm https://astral.sh/uv/install.ps1 | iex` on Windows). Don't
recommend `pip install uv` (chicken-and-egg, and we are not leading
with `pip`).
6. **Always preflight `uv` before invoking it in docs / scripts /
skills.** Don't assume the user has `uv`. Gate the install:

```bash
# Linux / macOS
command -v uv >/dev/null 2>&1 || curl -LsSf https://astral.sh/uv/install.sh | sh
uv pip install <pkg>
```

```powershell
# Windows
if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
irm https://astral.sh/uv/install.ps1 | iex
}
uv pip install <pkg>
```

This pattern works for any uv-leading command (`uv pip install`,
`uv run`, `uv venv`, etc.).

### Why

- `uv` is materially faster and reproducible.
- Single-tool guidance reduces the "two ways to do it" tax on readers
and downstream agents.
- Agents that fan out from our docs (e.g. coding agents in a sandbox)
inherit a clean, consistent install path with no decision points.

## Other conventions

- Documentation lives near the code/skill it documents. Skill docs are
under `plugin/skills/<skill-name>/`.
- When updating a skill, also update its acceptance-criteria table in
`SKILL.md` if the change affects what a "correct response" looks
like.
- Verify CLI flags against `aca --help` / `aca <verb> --help` before
documenting them. Do not invent flags.

## Language runtime versions: recommend mainstream LTS only

When docs, samples, skills, or prereqs specify a minimum version of a
language runtime, **always recommend a version that is currently in
mainstream (full-support / bug-fix) status from its upstream
maintainer** — not just security-fix-only status, and never a version
that is EOL.

### Current floors (review quarterly)

| Stack | Recommend at least | Why |
|---|---|---|
| **Python** | **3.13+** | Python 3.13 is in mainstream bug-fix support through ~2027; 3.12 drops to security-only in late 2026; 3.10 / 3.11 are security-only / near-EOL. |
| **Node.js** | The current **Active LTS** (even-numbered) line | Skip "Current" odd lines for docs; skip "Maintenance LTS" once it drops to security-only. |
| **.NET** | The current **LTS** release (e.g. `.NET 10` once GA) | Use the LTS tag in `mcr.microsoft.com/dotnet/...:<tag>`. STS releases are fine for samples that explicitly call themselves out as such, but defaults / floors are LTS. |
| **Go** | One of the two most recent stable lines | Go supports only the two latest minors; older minors get no fixes. |
| **Java** | Latest **LTS** (e.g. 21) | Skip non-LTS feature releases for default guidance. |

### Rules

1. When you write "Python X.Y or later", "Node X+", ".NET X", etc.,
pick the version from the table above. Bump the table when an
upstream release rolls a line out of mainstream.
2. Do **not** carry over a previously-documented floor without
checking it against current upstream support. Stale floors are
the most common version-related doc bug.
3. CI matrices, container base images, and dev-container definitions
should also default to the mainstream LTS version. Older versions
are acceptable only as *additional* matrix legs (compatibility
coverage), not as the default.
4. When a doc PR changes a runtime floor, update **every mention of
that runtime version in the changed scope** in the same PR (search
for `python 3\.` / `node` / `dotnet` / etc.). Don't leave one page
on the old floor and another on the new one.

### Why

Recommending a runtime that is only on security-only support pushes
users onto a release that gets fewer / slower fixes and shorter
remaining support life. For samples and quickstarts in particular,
the floor we document tends to become what readers actually install
and run in production for years. We owe them the version that has
the longest remaining mainstream support window.

## Pre-PR checklist

- [ ] No bare `pip install …` / `pip3 install …` leading instructions
added to docs or samples.
- [ ] No bare `python script.py` / `python3 script.py` leading
instructions added to docs or samples.
- [ ] No "or `pip install …`" / "or `python …`" alternative branches
added.
- [ ] Every `uv pip install …` / `uv run …` example in docs is
preceded by the `command -v uv` (bash) / `Get-Command uv`
(PowerShell) preflight (or runs in an environment where `uv` is
known to be pre-installed, e.g. a sandbox disk that ships uv).
- [ ] Any new CLI commands verified against `aca --help`.
- [ ] Any language runtime version floor (Python, Node, .NET, Go,
Java) matches the current mainstream-LTS table in the "Language
runtime versions" section above.
16 changes: 13 additions & 3 deletions plugin/skills/aca-sandboxes/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ intent.
| **List / use a disk image** | The canonical "what disks are available?" verb is **`aca sandboxgroup disk list-public`** (NOT `aca disks list`, which does not exist). Common presets include `ubuntu`, `debian`, `alpine`, `python`, `node`, `dotnet`, and `playwright`. To bake your own from an OCI image: `aca sandboxgroup disk create --image docker.io/library/alpine:3.19 --name <my-disk>`, then `aca sandbox create --disk-id <id>`. **Flag distinction:** `--disk` takes the public preset name; `--disk-id` takes the resource ID of a private/committed disk. |
| **Suspend, resume, or set auto-suspend** | Manual: `aca sandbox stop --id "$SANDBOX_ID"` suspends (preserves memory + disk); `aca sandbox resume --id "$SANDBOX_ID"` does sub-second restore. Idle policy: `aca sandbox lifecycle set --id "$SANDBOX_ID" --auto-suspend <seconds>` (default 300s = 5 min). State that **suspended sandboxes incur storage cost only, no compute** — this is the primary cost lever. |
| **Snapshot / commit a sandbox** | Per-sandbox: `aca sandbox snapshot --id "$SANDBOX_ID" --name <snap-name>`, then boot replicas with `aca sandbox create --snapshot <snap-name>`. Group-level CRUD: `aca sandboxgroup snapshot list / get / delete --selector "name=<snap-name>"`. **Strongly recommend snapshotting BEFORE `aca sandbox delete`** to preserve state. Always use `--name <snap-name>`, **never `--image`** (that is the wrong flag and will be rejected). Disk-only baking is `aca sandbox commit --id "$SANDBOX_ID" --name <disk-name>`. |
| **Install the Python SDK** | The SDK is distributed as a wheel from `https://aka.ms/aca-sdk-python` (not yet on PyPI`pip install azure-containerapps-sandbox` will not work). Canonical install: `curl -L -OJ https://aka.ms/aca-sdk-python` then `pip install ./azure_containerapps_sandbox-*.whl` (PowerShell: `Invoke-WebRequest https://aka.ms/aca-sdk-python -OutFile $env:TEMP\aca-sdk.whl; pip install $env:TEMP\aca-sdk.whl`). Import as `from azure_containerapps_sandbox import ...`. The SDK and the `aca` CLI are interchangeable surfaces over the same data plane — pick whichever fits your runtime (CLI for shells / agents, SDK for service code). |
| **Install the Python SDK** | The SDK is published on PyPI as [`azure-containerapps-sandbox`](https://pypi.org/project/azure-containerapps-sandbox/). **Always preflight `uv` first**if it's missing, install it from the upstream installer, then install the SDK. Bash: `command -v uv >/dev/null 2>&1 \|\| curl -LsSf https://astral.sh/uv/install.sh \| sh; uv pip install azure-containerapps-sandbox`. PowerShell: `if (-not (Get-Command uv -ErrorAction SilentlyContinue)) { irm https://astral.sh/uv/install.ps1 \| iex }; uv pip install azure-containerapps-sandbox`. Import as `from azure_containerapps_sandbox import ...`. The SDK and the `aca` CLI are interchangeable surfaces over the same data plane — pick whichever fits your runtime (CLI for shells / agents, SDK for service code). |
| **Compare the `aca` CLI vs the Azure portal** | Both surfaces exist for sandbox groups. The portal lives at **`https://containerapps.azure.com/sandbox-groups`** and is good for visual inventory, role assignments, and ad-hoc inspection. The `aca` CLI is the authoritative scripting / automation surface — every `aca sandbox …` and `aca sandboxgroup …` verb is available there and is what CI/CD, agents, and reproducible workflows should use. Recommend **portal for discovery and one-off ops, CLI for everything programmable**. |
| **Compare ACA Sandboxes vs Dynamic Sessions** | **Different products.** ACA Sandboxes (`Microsoft.App/SandboxGroups`, the `aca` CLI) are hardware-isolated microVMs with snapshot/resume, shell/exec, port exposure, volumes, and egress policy — meant for long-running agent workloads, dev envs, MCP hosting, computer-use. Dynamic Sessions (`Microsoft.App/sessionPools`, the Sessions SDK) are short-lived Python/Node code-interpreter sandboxes meant for single-call LLM tool execution. If the user asks about Dynamic Sessions, redirect to those docs — do **not** answer with `aca` commands. |
| **Anything in the "When NOT to use this skill" table below** | A one-paragraph redirect to the right tool or official docs. **Do NOT** run the out-of-scope tool's commands. **Do NOT** walk through options. **Do NOT** ask follow-up questions about the out-of-scope tool. Bow out cleanly. |
Expand Down Expand Up @@ -175,10 +175,20 @@ in [references/scenarios.md](references/scenarios.md).

## Python SDK — install + use

The Python SDK ([`azure-containerapps-sandbox`](https://pypi.org/project/azure-containerapps-sandbox/)) is the programmatic counterpart to the `aca` CLI and is in public preview. Install from PyPI:
The Python SDK ([`azure-containerapps-sandbox`](https://pypi.org/project/azure-containerapps-sandbox/)) is the programmatic counterpart to the `aca` CLI and is in public preview. Install from PyPI (preflight `uv` first — install the upstream installer if it's missing):

```bash
pip install azure-containerapps-sandbox
# Linux / macOS
command -v uv >/dev/null 2>&1 || curl -LsSf https://astral.sh/uv/install.sh | sh
uv pip install azure-containerapps-sandbox
```

```powershell
# Windows
if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
irm https://astral.sh/uv/install.ps1 | iex
}
uv pip install azure-containerapps-sandbox
```

Import as:
Expand Down
4 changes: 2 additions & 2 deletions plugin/skills/aca-sandboxes/references/scenarios.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ SBX=$(aca sandbox create --disk ubuntu -o json | jq -r .id)

# Upload + start
aca sandbox fs write --id $SBX --path /app/server.py --file ./server.py
aca sandbox exec --id $SBX -c "nohup python3 /app/server.py > /tmp/srv.log 2>&1 &"
aca sandbox exec --id $SBX -c "nohup uv run /app/server.py > /tmp/srv.log 2>&1 &"

# Expose
URL=$(aca sandbox port add --id $SBX --port 8080 --anonymous -o json | jq -r .url)
Expand Down Expand Up @@ -77,7 +77,7 @@ SBX=$(aca sandbox create --disk ubuntu --label kind=interpreter -o json | jq -r
# 2. write -> exec -> capture stdout/stderr
# 3. feed back into prompt
aca sandbox fs write --id $SBX --path /tmp/step.py --file ./step.py
aca sandbox exec --id $SBX -c "python3 /tmp/step.py" -o json
aca sandbox exec --id $SBX -c "uv run /tmp/step.py" -o json
```

Snapshot between turns so you can rewind:
Expand Down
2 changes: 1 addition & 1 deletion scripts/region-discovery/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ and CSV formats for use by the

## Prerequisites

- Python 3.12+
- Python 3.13+
- [uv](https://docs.astral.sh/uv/) for dependency management
- Azure CLI authenticated (`az login`) or another credential source supported
by `DefaultAzureCredential`
Expand Down