Files
agent-harness-spec/docs/deployment.md
Jonathan Boniface e6f10ba8c9 feat: initial harness spec v1.0.0 + reference template
Adds the normative contract every platform agent must conform to, plus a
working reference template under template/ that ticks every box out of
the box.

Spec is derived from the production agents shipped in
cjot-backstage-az/agents/ on branch sandbox/jonathan:
  - agent-registry, agent-factory, decomposer, discovery-agent,
    golden-path, modernization-factory, modernization-factory-v2,
    policy-transformer, scaffold-agent, support-intake-agent, the-watcher

Contents:
  - SPEC.md          normative contract (13 sections + conformance checklist)
  - CHANGELOG.md     spec versioning (1.0.0)
  - docs/
      registration.md   self-registration with the agent-gateway
      observability.md  OTel logs/metrics/traces wiring
      manifest.md       /.well-known/agent.json schema + AgentSkill
                        serialisation (pydantic vs protobuf)
      kubernetes.md     k8s deployment shape, mandatory cross-refs,
                        per-namespace agents, resource sizing
      deployment.md     .image-version, ACR build, deploy scripts, rollback
      deviations.md     tracked debts against the spec
  - template/
      .env.local.example, .gitignore, .image-version (0.1.0),
      Dockerfile (python:3.12-slim, non-root UID 1001, HEALTHCHECK),
      requirements.txt (harness floor + optional LLM stack),
      app/agent.py (Starlette entry, lifespan + self-registration,
                    defensive _skill_dict for pydantic vs protobuf),
      app/logging_setup.py (canonical OTel log bridge — copy verbatim),
      app/metrics.py (meter + example counter/histogram),
      app/skills.py (AGENT_CONFIG + AgentSkill list),
      k8s/configmap.yaml + deployment.yaml (Deployment + Service,
                                            OTel annotations + 6-var env block,
                                            agents-sa + agents-kv-spc bindings),
      scripts/deploy.sh (auto-bump + az acr build + apply + rollout),
      scripts/full-deploy.sh (preflight + deploy + post-deploy smoke),
      scripts/deploy-local.sh (docker/podman + .env.local)
2026-06-09 15:54:18 +01:00

134 lines
4.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Build & deployment
> Normative requirements: [SPEC.md §10](../SPEC.md#10-build--deployment-must)
Every agent ships three deploy scripts under `scripts/` that wrap the same build → push → apply → roll-out pipeline at three layers of fidelity.
## `.image-version`
A single-line file at the agent root:
```
1.0.3
```
Conventions:
- Semver only.
- **MUST** match `OTEL_RESOURCE_ATTRIBUTES`' `service.version` value at all times.
- Bumped whenever a code change ships. The patch number is auto-incremented by `scripts/deploy.sh` when no `--tag` is given.
- Pure image-version-only commits are reserved for *redeploy reruns* (e.g. rebuilding without code changes to refresh dependencies). They **SHOULD** be rare.
## ACR
All images are pushed to:
```
bstagecjotdevacr.azurecr.io/<agent-name>:<X.Y.Z>
bstagecjotdevacr.azurecr.io/<agent-name>:latest
```
Both tags are pushed every build so that pinning to `:latest` works for sandbox/dev clusters and pinning to `:<version>` works for prod clusters.
## `scripts/deploy.sh`
The fast path. Used for routine redeploys.
```bash
#!/bin/bash
set -euo pipefail
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
ACR_NAME="bstagecjotdevacr"
APP_NAME="<agent-name>"
NAMESPACE="agents"
VERSION_FILE="${SCRIPT_DIR}/../.image-version"
TAG=""
while [[ "$#" -gt 0 ]]; do
case $1 in
--tag) TAG="$2"; shift ;;
*) echo "unknown arg: $1"; exit 2 ;;
esac
shift
done
if [ -z "$TAG" ]; then
CURRENT=$(cat "$VERSION_FILE" 2>/dev/null || echo "1.0.0")
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
PATCH=$((PATCH + 1))
TAG="${MAJOR}.${MINOR}.${PATCH}"
echo "$TAG" > "$VERSION_FILE"
fi
IMAGE="${ACR_NAME}.azurecr.io/${APP_NAME}:${TAG}"
echo "Building ${IMAGE} ..."
az acr build --registry "$ACR_NAME" \
--image "${APP_NAME}:${TAG}" \
--image "${APP_NAME}:latest" \
"${SCRIPT_DIR}/.."
echo "Applying manifests ..."
kubectl apply -f "${SCRIPT_DIR}/../k8s/configmap.yaml"
kubectl apply -f "${SCRIPT_DIR}/../k8s/deployment.yaml"
kubectl -n "$NAMESPACE" rollout status "deployment/${APP_NAME}" --timeout=180s
```
Behaviour:
- Auto-bumps the patch on `.image-version` if `--tag` is not given.
- Rebuilds and re-pushes both `:<tag>` and `:latest`.
- Applies `configmap.yaml` and `deployment.yaml` in order.
- Blocks until the rollout completes (or fails) within 180 s.
## `scripts/full-deploy.sh`
The "first time" path. Adds preflight + post-deploy smoke.
Preflight (refuse to continue if any check fails):
- `kubectl` available and pointed at a cluster.
- `az` available and logged in.
- Target namespace exists.
- ServiceAccount `agents-sa` exists in the namespace.
- SecretProviderClass `agents-kv-spc` exists in the namespace.
After `rollout status` succeeds:
- `kubectl exec deployment/<agent> -- curl -fs http://localhost:<port>/health`
- `kubectl exec deployment/<agent> -- curl -fs http://localhost:<port>/.well-known/agent.json | jq -e '.name'`
Failures abort with a non-zero exit code so CI can block.
## `scripts/deploy-local.sh`
For dev loop on a workstation without cluster access.
- Builds the image locally with `docker` or `podman`.
- Reads `.env.local` (sibling of `.env.local.example`).
- Runs the container with `-p <port>:<port>` and `--env-file`.
Useful for iterating on prompt changes without touching the cluster.
## CI integration
The platform's GitHub Actions / Gitea Actions runners invoke `scripts/deploy.sh --tag $GITHUB_SHA` (or similar) when an agent's directory changes on the deploy branch. Pull requests trigger a build-only run that pushes a `:<sha>` tag without applying manifests.
## Rolling back
There is no automated rollback. To roll back:
```bash
kubectl -n agents set image deployment/<agent-name> \
<agent-name>=bstagecjotdevacr.azurecr.io/<agent-name>:<previous-version>
kubectl -n agents rollout status deployment/<agent-name> --timeout=180s
```
If the rollback ships a different `service.version`, also revert `OTEL_RESOURCE_ATTRIBUTES` in the ConfigMap or the dashboards will misattribute. Prefer a forward-fix (new patch version) over a rollback whenever possible.
## Re-registration after redeploy
The agent re-registers with the gateway on every pod start. No manual step is required after a rollout. If the gateway pod is also restarting at the same time, expect 12 `WARNING`s in the agent log before registration succeeds — see [registration.md](registration.md#failure-modes--resolution).