- name
- threadlight-deploy
- description
- Take a designed agent project (from threadlight-design or hand-crafted) and generate all deployment artifacts for Microsoft Foundry Hosted Agents. Reads specs/SPEC.md, AGENTS.md, and skills to produce container.py, Dockerfile, pyproject.toml, an azd project, and deploy-notes.md for one-command `azd up`. Also runs in Kratos-export mode: handed a Kratos-exported project (src/hosted-agent/ + use-cases/<x>/) it enriches/validates only and backfills the missing evals/ directory. USE FOR: deploy to Foundry, make this deployable, generate deployment files, Foundry hosted agent, containerize agent, package agent, deploy agent, azd deploy, azd up, Kratos export, foundry-agent.zip, backfill evals. DO NOT USE FOR: designing the process (use threadlight-design), running evals (use foundry-evals), Teams bot (use foundry-teams-bot), MCP server deployment (use foundry-mcp-aca), standalone/non-Threadlight GHCP runtime customization (use ghcp-hosted-agents), azd tenant isolation (use azure-tenant-isolation).
- metadata
- {"version":"1.9.0"}
# Foundry Hosted Agent Deploy
## Presenter-ready selected consumer
For an incumbent project, follow the [adoption map](references/presenter-adoption.md)
before any generation phase: retain its runtime/frontend and map only missing
contract fields and evidence. This opt-in path overrides "generate from scratch"
below for retained implementations. Use the narrow
[built web artifact smoke](references/web-artifact-smoke.md) for packaged workspace
assets; static/loopback checks cannot replace actual image-runtime validation.
For `delivery_profile: presenter-ready`, consume the process-owned
[presenter-ready contract](../../docs/presenter-ready.md) and the same
[`presenter-deployment-pin.json`](../_shared/presenter-deployment-pin.json)
as Design and Safe Check. Read the selected upstream guide/templates at its exact
commit. Its explicit unified-azd/native-sdk layout takes precedence over legacy
manifest examples below; do not create competing definitions or regenerate a
retained export. Keep Foundation/runtime-policy and selected governance cohorts.
Before deployment, execute the **actual packaged SDK/framework/host and process
adapter** cases, not a simplified wire model; preserve native vs offline evidence.
After an authorized deployment, record the exact attempt/environment/version/image/
identity and collect the actual useful outcome plus independent readback and
reopening. A running container or setup-ready preflight is not backend-verified.
Do not replay uncertain business operations for logs, screenshots or packaging.
Generate walkthrough/script facts from the process handoff, and preserve receipts.
> ⚠️ **Azure Tenant Isolation (mandatory).** Before running any Phase that
> touches Azure (`azd up`, `az deployment`, `az acr build`), verify tenant
> isolation per the [`azure-tenant-isolation`](https://github.com/aiappsgbb/awesome-gbb/tree/main/skills/azure-tenant-isolation/)
> skill: set `AZURE_CONFIG_DIR` + `AZD_CONFIG_DIR`, assert tenant +
> subscription with `az account show`, then proceed. Check token validity
> first — only prompt `az login` if `az account show` fails.
Take a project folder (containing AGENTS.md, `src/agent/skills/`, config/, etc.) and enrich
it with all files needed to deploy as a **Microsoft Foundry Hosted Agent**.
## Selected governance — before choosing a container template
Read the explicit governance contract first. **Off means no changes and no
governance infrastructure.** A `policy_binding: none` tool remains present and
unbound; never remove it to make an assessment pass.
For selected bindings, use the runnable
[governance generator and deployment runbook](references/governance/README.md):
For the bounded real-business VM-first vertical, use the
[returns/MCP source package and demo runbook](references/governance/returns-mcp-demo.md).
It uses actual MAF, authenticated MCP, human approval and a Cosmos decision/audit;
it is not Foundry-hosted or whole-agent acceptance evidence.
```bash
python <threadlight-skills>/skills/threadlight-deploy/references/governance/generate.py \
generate --project <pilot> --contract <contract.json> --configuration <package.json>
```
- Keep the operator's runtime choice. MAF local hooks use the **whole portable
provider**, native Task6 validator, signed Task8 envelope, real Key Vault
verification, HTTP approval client and authenticated Task8 audit delivery. The
`create_governed_agent` result is the agent actually passed to
`ResponsesHostServer`; application middleware stays inside its native hooks.
Required output mediation buffers before delivery and forces `store=False`.
Required audit uses remote receipt ACK **before effects**, with bounded worker
replay and shutdown; the local retry spool is not persistent hosted storage.
CP outage or local spool failure is unhealthy and blocks required effects.
- MAF can also use the explicit **gateway-only** action path through
`maf-gateway-container.py`: real native MAF functions call the authenticated
MCP gateway, which runs ACS and the existing approval/audit protocol before
the downstream action. This path has no mixed local/gateway bindings or
lifecycle bindings; application-local tools must be exactly the declared
unbound reads. Arbitrary request-level tool/middleware/provider overrides
are rejected. OBO is not provided by this app-only path. Human decisions
remain separate delegated approval-service calls, not SDK auto-approval.
Generation and local native integration do not establish Azure deployment
or business-binding live proof. An explicitly signed deferred approval mode
returns `pending_approval` without keeping HTTP open. Resume the same action
with its `governance_operation_id`; policy, arguments, trusted facts, identity,
expiry and one-use approval are rechecked before the effect. This does not
add OBO or automatically deploy a review frontend.
- GHCP uses `CopilotClient` + Invocations, **not** invented local hooks. Only
bound MCP tools go through the authenticated gateway; mixed servers retain
their explicit unbound tool filters, original URL and auth. The pinned pre-MCP
hook supports metadata, not HTTP headers: the generated loopback adapter
supplies fresh gateway-audience auth and Task9 idempotency headers.
- **Selected governance pins override all legacy/default pins below and stale
installed companions.** Use `../_shared/governance-upstream-pin.json` exactly
(AGT 5, ACS 0.3.1b0, Hooks 0.1.0a5, MAF 1.14/Foundry 1.11/hosting b260813).
- Missing/unsigned/expired policy, approval or audit dependencies must not
produce healthy readiness. A diagnostic fallback is **not** a healthy agent.
Deadline pressure or an already-built legacy image does not waive wiring.
- Separate control-plane, gateway, downstream and publisher identities.
Binding must cross-check packaged service scopes/URLs against the observed
deployment audiences/endpoints before writing artifacts; no auth drift.
Never reuse the agent identity or a shared ancillary admin identity; the
agent receives no approval-store write or direct downstream permissions.
Private-required means supplied, verified Foundry VNet injection and reachable
private dependencies, not external ingress plus a TODO.
- Follow **foundation → package/build → bind** in the runbook. Images and
deployment identity must be resolved before service revisions; no
hello-world placeholder images. Packaging, assessments and health are not
live runtime/effect-closure proof. GHCP is only action-governed after the
Task9/11 effect-closure probes pass; never claim full output gating.
For a prepared exact-pin Linux environment, the bounded inner loop is
`python scripts/ci/run-governance-pin-tests.py --mcp-prepared`. It verifies
published runtime bytes and runs only the MAF/MCP client, host and generation cases with a
120-second ceiling. Missing/skipped cases fail. It never installs packages,
deploys, runs the whole pipeline or replaces the full native/CTK acceptance gate.
**Canonical runtime policy:**
`../threadlight-design/references/runtime-policy.json`. The locked default
route remains `github-copilot-sdk` + `agent` + `invocations`
(`policy_route: default-agent`). Exception routes are
`deterministic-workflow` → MAF workflow + Responses,
`maf-agent-capabilities` → MAF agent + Responses, and
`explicit-supported-choice` for operator overrides listed in
`compatible_combinations` **and** free of that route's `blocked_when`
capability signals. Canonical default tuple: `github-copilot-sdk` +
`agent` + `invocations` (`policy_route: default-agent`). `specs/foundation.md`
is the selector authority (see Phase 0 Runtime-policy pre-flight below); SPEC
only supplies capability signals, never a competing selector.
Uses the **`azd ai agent` extension** for declarative deployment — `azure.yaml` defines
agent configuration, model deployments, and container resources; `azd up` handles everything.
## When to Use
- User has a designed agent project and wants to deploy it to Foundry
- User asks to "make this deployable" or "package for Foundry"
- User wants to containerize their agent for hosted deployment
- User asks for Dockerfile, container runtime, or deployment files
- User asks about MCP tools in Foundry hosted agents
- User hands you a **Kratos-exported project** and wants to harden it for production
## Input Modes
This skill accepts **two starting points**. Detect which one you're in **before
Phase 1** and branch accordingly — they are additive, with no change to the
existing design-driven path.
| Mode | Detection signal | What this skill does |
|------|------------------|----------------------|
| **threadlight-design mode** (default) | `AGENTS.md` + `src/agent/skills/` present; no `src/hosted-agent/` | Generate all deploy artifacts from scratch (Phases 0–7, unchanged). |
| **Kratos-export mode** | `src/hosted-agent/` **and** `use-cases/<x>/` both present | **Enrich/validate only.** The runtime (`Dockerfile`, `main.py`, `pyproject.toml`, `agent.yaml`, `azure.yaml`) already exists — do **not** regenerate it. Backfill `evals/`, validate, and hand off to the production-hardening skills. |
> [!IMPORTANT]
> **Kratos-export mode skips Phase 2 (Generate Deployment Artifacts).** Kratos's
> exporter already ships a deployable full-clone project; re-running the
> generators would clobber `src/hosted-agent/main.py`, `Dockerfile`, and
> `azure.yaml`. In this mode the skill enriches and validates in place. See the
> full runbook in [`references/kratos-export-mode.md`](references/kratos-export-mode.md)
> and the bridge overview in [`docs/KRATOS-BRIDGE.md`](../../docs/KRATOS-BRIDGE.md).
**Skills root.** Kratos puts skills under `use-cases/<x>/skills/`;
`threadlight-design` puts them under `src/agent/skills/`. Resolve the skills root
with this precedence: (1) explicit `--skills-root <path>` override, (2)
`use-cases/<x>/skills/` if present (Kratos-export mode), (3) `src/agent/skills/`
(design mode). No symlinks, no moving files.
## Why Hosted Agents (not Prompt/Declarative Agents)
Foundry offers simpler agent types (`PromptAgentDefinition`, `DeclarativeAgentDefinition`)
that run on Foundry's servers with no custom container. However, these **cannot** support:
- **SkillsProvider** — progressive skill discovery and on-demand loading
- **Custom tools** — `@tool(approval_mode="never_require")` Python functions
- **Complex orchestration** — multi-step workflows, custom error handling
- **Custom telemetry** — OpenTelemetry instrumentation with Azure Monitor
- **Instruction injection** — runtime `COSMOS_DATABASE` substitution, tool-use discipline
For any agent that uses **skills, custom middleware, or complex logic**, you MUST use
`HostedAgentDefinition` with a custom container.
## Prerequisites
> **Kratos-export mode (alternative input).** When starting from a Kratos export,
> the inputs below live under `use-cases/<x>/` instead: `SYSTEM_PROMPT.md` (in
> place of `AGENTS.md`), `use-cases/<x>/skills/*/SKILL.md`, `apm.yml`, and
> `.mcp.json`. The hosted-agent runtime is under `src/hosted-agent/`. See
> [`references/kratos-export-mode.md`](references/kratos-export-mode.md).
The input folder MUST have:
- `AGENTS.md` — agent identity, skills, tools, behavioral guidelines
- `src/agent/skills/*/SKILL.md` — one or more skill definitions
Recommended (from `threadlight-design`):
- `specs/SPEC.md` — SpecKit specification (business rules, data models, integrations, compliance)
- `specs/manifest.json` — checkpoint metadata (process name, phase, status)
- `specs/sample-data/*.json` — mock data for inaccessible systems
- `specs/manifest.json` — machine-readable deployment contract
- `src/agent/config/*.json` — process configuration
> [!IMPORTANT]
> **Dependency skills.** This skill references content from other skills instead of
> duplicating it. Check that companion skills are available:
>
> | Skill | When Needed |
> |-------|------------|
> | `foundry-hosted-agents` | **Always** — RBAC, identity model, agent.yaml schema, dependency versions, troubleshooting |
> | `threadlight-design` | **Always** — produces SPEC.md sections this skill consumes (§ 5b, § 7b, § 8, § 8b, § 9, § 10b, § 11b, § 11c, § 11d) |
> | `azd-patterns` | **Always** — Bicep module library that Phase 6 (Module Composer) reads from |
> | `foundry-iq` | **Default for every process** — provisions the Knowledge Agent + AI Search index for SPEC § 7 knowledge sources |
> | `foundry-teams-bot` | If Teams integration is needed |
> | `foundry-mcp-aca` | If deploying custom MCP servers as ACA or Azure Functions |
> | `threadlight-workspace-ui` | If SPEC § 8b specifies an operator workspace |
> | `threadlight-hitl-patterns` | If SPEC § 8 declares any human action gate (approve/edit-and-approve/reject/escalate/signoff/audit-view/request-info) |
> | `threadlight-event-triggers` | If SPEC § 10b declares any event-driven, scheduled, or webhook trigger |
> | `threadlight-demo-data-factory` | If SPEC § 5 marks any system as `mock` (almost always true for pilots) |
> | `foundry-doc-vision-speech` | If SPEC § 7b selects any vision / DocIntel / Speech model |
> | `foundry-evals` | For post-deployment evaluation AND continuous evaluation: **Plan A** (default) — Foundry's built-in scheduled evaluations (no extra infra). **Plan B** (fallback) — ACA Job cron eval that reads from App Insights and writes to Workbook (use only when Plan A doesn't yet support hosted-agent eval kinds you need). Phase 6 includes the ACA Job ONLY when SPEC § 9 sets `continuous_eval.plan: "B"` |
> | `citadel-spoke-onboarding` | **Phase 7 (opt-in)** — runs ONLY when SPEC § 11b sets `governance_hub.required: yes` |
> | `threadlight-workflow` | **Phase 2 alternative** — runs ONLY when SPEC § 11e sets `workflow_model: "workflow"` **AND the skill is installed** (`/skills list`). Generates a MAF Workflow container instead of an Agent container, then Phase 5-6 pick it up. **If it is not installed, do NOT block or hunt for it — fall back to the Phase 2 agent container path (see the Workflow model gate below).** |
>
> Use `/skills list` to check availability. If missing, install from `aiappsgbb/awesome-gbb`.
## Workflow
```
Phase 0 → Phase 1 → Phase 2 → Phase 3 → Phase 4 → Phase 5 → Phase 6 → Phase 6.5 → Phase 6.7 → Phase 7
Poly-repo Analyze Generate Validate Teams Bot azd Module Demo data Prep-guide Citadel
guard SPEC + runtime scaffold (optional) project composer seed (when live walkthrough handoff
AGENTS.md files scaffold (Bicep) mocks exist) back-fill (opt-in)
```
---
## Phase 0: Poly-Repo Guard (mandatory pre-flight)
**Rule**: each threadlight process gets ONE repo. ONE repo = ONE process = ONE
`azd up`. **Never multi-process repos.**
### Why
We learned this the hard way in older multi-process repos. Multi-process
repos:
- Inflate Bicep into one giant template with 70% `if` blocks
- Force unrelated processes to share azd env, breaking iteration
- Make customer hand-off awkward (they only want one process; they get all 13)
- Concentrate blast radius — one botched deploy takes down siblings
### Pre-flight checklist (run this FIRST, before Phase 1)
Inspect the input folder. These are **repo-shape signals only**. If ANY of
these are true, **stop and ask the user to split the repo before
proceeding**:
- More than one `specs/SPEC.md` exists at any depth
- More than one `AGENTS.md` exists at any depth
- The folder name contains a plural / catalog noun (`processes/`, `catalog/`, `pilots/`)
- The folder contains nested `specs/<process-slug>/SPEC.md` siblings
- A previous run produced an `azure.yaml` with multiple `services:` entries that
point to different agent containers
A runtime-policy mismatch is **never** one of these signals and is **never**
remediated by splitting the repo — see the mandatory **Runtime-policy
pre-flight** subsection below, which runs independently of this checklist.
### How to split
```
Before (rejected): After (each is its own azd up):
<multi-process-repo>/ <your-process-repo>/
├── <process-a>/ ├── specs/
│ ├── specs/ │ └── SPEC.md
│ └── src/ ├── src/
├── <process-b>/ └── azure.yaml
│ ├── specs/
│ └── src/ retail-pim-enrichment/
└── azure.yaml (← shared) ├── specs/
│ └── SPEC.md
├── src/
└── azure.yaml
```
The `threadlight-design` skill respects this by default — it generates one
self-contained subtree per process. This skill enforces it.
### Runtime-policy pre-flight (mandatory — separate from the Poly-Repo checklist above)
`specs/foundation.md` is the **selector authority** for the runtime tuple
(`framework` + `runtime_shape` + `protocol` + `policy_route`) — this is a
cross-skill contract check, not a repo-shape signal, and it is **never**
remediated by asking the user to split the repo.
1. **Missing dependency — HARD STOP, never fall back to a remembered
default.** If
`../threadlight-design/references/runtime-policy.json` cannot be read
(the skill isn't installed/enabled), stop immediately and tell the
operator to install or enable **`threadlight-design`** (`/skills list`; if
missing, install from `aiappsgbb/awesome-gbb`). `threadlight-deploy`
already declares `threadlight-design` an **Always** dependency (see
Prerequisites above), so this file is expected to exist whenever
`threadlight-design` is present — its absence is a broken installation,
not a signal to guess.
**Kratos-export mode gate:** after this dependency check, if
GitHubで見る