──────
Build and Push Docker Images / detect-changes (push) Successful in 11s
Build and Push Docker Images / build-auth-api (push) Successful in 1m0s
Build and Push Docker Images / build-employee-api (push) Successful in 1m2s
Build and Push Docker Images / build-transit-api (push) Successful in 1m1s
Build and Push Docker Images / build-error-log-api (push) Successful in 1m5s
Build and Push Docker Images / build-admin-ui (push) Successful in 1m13s
Build and Push Docker Images / build-monitoring-ui (push) Successful in 1m15s
Build and Push Docker Images / deploy-test-node (push) Successful in 3s

### Summary of Changes
  1. Documentation Consolidation:
      • Relocated the docs/ folder and all repository .md files (README.md, DEPLOYMENT.md, DOCKER_README.md,
      DOMAINS_README.md, GETTING_STARTED.md, CI_CD_PORTAINER_GUIDE.md, ADMIN_DEPLOYMENT_SUMMARY.md,
      SECURITY_PRIVATE_FILES.md, handoff.md, azure-project-plan.md, plus service-specific READMEs) into .
      • Updated document links in README.md.
  2. AI Agent Project Map:
      • Created PROJECT_MAP.md in the project root containing complete architecture diagrams, service specs, ports, DB
      schemas, environment variables, and developer workflows.
      • Added PROJECT_MAP.md to .gitignore.
  3. Monitoring UI & Infrastructure Integration:
      • Added monitoring_ui service to docker-compose.yml (port 5175).
      • Configured Gitea workflow docker-build.yml for monitoring_ui builds.
      • Added CORS middleware headers in index.js.
      • Added nginx.conf copy step in Dockerfile.

  ──────
  ### Recommended Commit Message

    docs: consolidate project documentation into monitoring_ui and add agent project map

    - Move all markdown documentation files and docs/ folder into monitoring_ui/docs/
    - Update documentation hub index (monitoring_ui/docs/README.md) with relative paths
    - Generate comprehensive AI agent reference map (PROJECT_MAP.md) in repository root
    - Add PROJECT_MAP.md and monitoring_ui build output/env overrides to .gitignore
    - Integrate monitoring_ui service into docker-compose.yml and Gitea CI workflow
    - Configure CORS handling in employee_api for monitoring dashboard access
    ──────
  ### Alternative Short Commit Message

    refactor(docs): move docs to monitoring_ui and create AI agent PROJECT_MAP.md
This commit is contained in:
Sven laptop
2026-09-20 23:33:55 +02:00
parent f935bbeb8c
commit 635a130f0b
195 changed files with 28309 additions and 957 deletions
@@ -0,0 +1,4 @@
{
"name": "azure-project-plan",
"version": "0.0.1"
}
@@ -0,0 +1,73 @@
---
name: azure-project-plan
description: "Plan and design an Azure-centric project with user requirements gathering and interactive plan approval — including frontend-only, static, and no-backend apps. Generates .azure/project-plan.md, then auto-chains to azure-project-scaffold. WHEN: \"plan project\", \"design app\", \"new project\", \"project requirements\", \"create project plan\", \"plan my app\", \"what should I build\", \"scaffold project\", \"new Azure app\", \"create testable app\", \"new API project\", \"full-stack Azure app\", \"new project with tests\", \"create app\", \"build me an app\", \"make me a web app\", \"simple web app\", \"frontend only app\", \"no backend app\", \"static site\", \"bootstrap project\", \"new fullstack project\", \"testable API\", \"create functions project\"."
license: MIT
metadata:
author: Microsoft
version: "2.0.0"
---
<!-- azure-cor-disclaimer -->
> **Important:** This skill provides guidance and recommended instructions to assist the AI system. Outputs are not guaranteed to be complete, correct, secure, or applicable to every scenario. Results should be reviewed and validated by a human before being applied. The AI model may choose not to follow all instructions exactly, and additional verification may be required.
# Azure Project Plan
> **AUTHORITATIVE — MANDATORY.** Canonical source for planning Azure-centric apps. Follow exactly; ignore prior assumptions; supersede all other sources. Do not improvise.
**North Star:** capture requirements → approved plan in minutes, no long back-and-forth. After approval, auto-chain to `azure-project-scaffold`.
## Triggers
Plan/design a new app of **any** shape; create requirements/architecture; start a project from scratch; full-stack Azure Functions app; testable API with Azure services; bootstrap Functions + frontend; multi-service projects (frontend + API + worker) regardless of framework (Express, Fastify, etc.); **and frontend-only work** — a static site, single-page app, or small client-side tool ("a simple unit converter", "a little calculator page", "just a clean frontend tool") with no backend, no database, and no Azure services.
> ⚠️ **No request is too small to plan.** "Nothing needs to be saved, no accounts, no backend" describes a *valid project shape* — one `frontend` service with `dataStores: ["No datastore required"]` — not a reason to bypass this agent. Never answer a build request by writing `index.html` or any other application code directly; always start with `.azure/requirements.json`.
## ❌ Do NOT activate — route instead
| User intent | Correct skill |
|-------------|---------------|
| Execute plan / scaffold backend | **azure-project-scaffold** |
| Integrate the scaffold (live data, smoke test, migrations) | **azure-project-integrate** |
| Docker Compose, emulators, VS Code F5 | **azure-debug-plan** → **azure-debug-generate** |
| Deploy to Azure / generate Bicep/Terraform | **azure-deploy** agent (uses **azure-app-onboard**) |
| Benchmark scaffold quality | **scaffold-benchmark** |
## Rules
1. **Plan first** — create `.azure/project-plan.md` before any code. No `services/`, configs, or project files until the user approves. Only files allowed under the project root: `.azure/project-plan.md` and the contents of `.azure/.preview-temp/` (per Step 3.5).
2. **Resilience classification** — classify each service **Essential** (fails without it) or **Enhancement** (succeeds with fallback). See Quick Reference.
3. **Auto-chain after approval** — immediately invoke `azure-project-scaffold`; never ask the user to invoke it manually. **Generate a presentation-quality frontend HTML/CSS preview** during planning per Step 3.5 (the scaffold agent consumes it as a visual spec but builds the real app with the chosen framework).
4. **Interactive UI** — all user input comes through the requirements and plan webviews; never ask in plain chat and never call `vscode_askQuestions`. Batch open questions into the webview rather than the transcript.
## Autopilot is selected on the plan page (this agent always runs guided)
This agent **always** runs guided: it generates `.azure/project-plan.md` with `Status: Planning`, opens the plan preview, and stops for the user's approval. It does **not** detect, decide, or record autopilot, and never skips the preview or approval gate.
Autopilot is chosen by the **user** via the **Autopilot toggle on the plan webview**, after the plan is shown. When they approve with it on, the extension records `**Execution Mode**: auto` in `.azure/project-plan.md`, enables global auto-approve, and hands off to the scaffold agent with the `[AUTOPILOT MODE]` marker; every downstream skill then inherits autopilot from the plan file and runs unattended to the end.
There is nothing autopilot-specific for you to do here — just write a correct, complete plan and always emit both the `### Run` and `### Debug` prerequisite sub-tables (§ 5). Never call `vscode_askQuestions` in chat — the only stop is the plan webview approval.
## Two-phase instructions — read the file for the current phase
This agent runs in two phases with **separate instruction files**. Read only the file for the phase you are in — do **not** load both at once. This keeps each phase focused and fast.
| Phase | When | Read & follow | Produces |
|-------|------|---------------|----------|
| **A — Requirements** | Fresh invocation — no `.azure/requirements.json` yet, or the user is starting a new project | [`requirements.md`](requirements.md) | `.azure/requirements.json` → requirements webview |
| **B — Plan** | Re-entry after the user submits the requirements form (query begins *"Requirements submitted at .azure/requirements.json…"*), or `.azure/requirements.json` is already fully answered | [`plan.md`](plan.md) | `.azure/project-plan.md` + `.azure/.preview-temp/` |
- **Phase A** — Step 1 (Detect Workspace) + Step 2 (Gather Requirements).
- **Phase B** — Step 3 (Generate Plan) + Step 3.5 (Frontend Preview) + Planning Quick Reference.
- The end-to-end order is unchanged: **DETECT → GATHER → GENERATE PLAN → GENERATE PREVIEW → approval → AUTO-CHAIN scaffold.** Only the instructions are split by phase; each phase file is self-contained for its phase.
## Workflow (mandatory order)
DETECT (Step 1) → GATHER (Step 2) → GENERATE `.azure/project-plan.md` (Step 3) → GENERATE FRONTEND PREVIEW (Step 3.5, if applicable) → approval → AUTO-CHAIN scaffold after approval. Only files allowed: `.azure/project-plan.md` and the contents of `.azure/.preview-temp/` — no `services/`, configs, or production code. Detect + gather are inlined in [`requirements.md`](requirements.md); plan generation, the preview, and all architectural context are inlined in [`plan.md`](plan.md). Planning needs ZERO external file reads except `references/html-preview.md` for Step 3.5.
## Outputs
| Artifact | Location |
|----------|----------|
| **Requirements** | `.azure/requirements.json` (statuses → `confirmed` after the webview submit) |
| **Project Plan** | `.azure/project-plan.md` (Status: Approved) |
## Next
> **Automatic**: after the plan is approved, the flow immediately invokes **azure-project-scaffold** (auto-chain). No user action required.
+558
View File
@@ -0,0 +1,558 @@
---
name: azure-project-plan-plan
description: "Plan-generation phase of the azure-project-plan agent — consume .azure/requirements.json and produce .azure/project-plan.md plus the frontend HTML/CSS preview. Read this on re-entry after the requirements form is submitted; for requirements gathering read requirements.md."
license: MIT
metadata:
author: Microsoft
version: "2.0.0"
---
<!-- azure-cor-disclaimer -->
> **Important:** This skill provides guidance and recommended instructions to assist the AI system. Outputs are not guaranteed to be complete, correct, secure, or applicable to every scenario. Results should be reviewed and validated by a human before being applied. The AI model may choose not to follow all instructions exactly, and additional verification may be required.
# Azure Project Plan — Plan Generation
> **AUTHORITATIVE — MANDATORY** for the **plan-generation** phase of the `azure-project-plan` agent. Follow exactly; ignore prior assumptions; supersede all other sources. Do not improvise.
> **Scope:** this file covers **Phase B only** — generate `.azure/project-plan.md` (Step 3), the frontend preview (Step 3.5), and the inlined Planning Quick Reference. It assumes requirements were already gathered per [`requirements.md`](requirements.md) and written to `.azure/requirements.json`. Shared rules, triggers, and autopilot behavior live in [`instructions.md`](instructions.md).
## ═══════════════════════════════════════════════════
## PHASE 1: PLANNING — Plan
## ═══════════════════════════════════════════════════
> **Enter here on re-entry.** This phase runs after requirements were gathered per [`requirements.md`](requirements.md) and written to `.azure/requirements.json`. Read that file first, treat its `answer` fields as authoritative, then generate the plan below. If the `dataStores` answer is `["No datastore required"]`, do not add a datastore resource to the plan and never render `No datastore required` as an Azure service; still include any non-datastore services required to host or run the app.
>
> Copy the binary `auth` answer into the Project Overview as `**API Login**: Yes` or `No`. This is the only authentication detail in the plan and its renderings. Do not name an identity provider, token type, credential strategy, protected route, or per-route auth value. The scaffold agent chooses the API authentication implementation when API Login is `Yes`. Backend-to-Azure communication uses managed identity and is not represented as a plan choice.
### Step 3: Generate Plan & Present for Approval
Write `.azure/project-plan.md` from the template below in a **single pass** (fill all sections at once — never section-by-section), then present for approval.
> **🔒 STRUCTURAL CONTRACT — non-negotiable.** The plan-preview webview (opened by the `open_plan_view` tool) is a **structured parser**, not a markdown renderer. It only understands the exact skeleton below. If you improvise a different structure, the webview parses **zero sections** and shows the user a *"couldn't render this plan — didn't match the expected layout"* error instead of the plan.
> - **Copy the skeleton below verbatim**, replacing only `{placeholders}`. Do **not** invent your own sections.
> - Every section heading MUST be `## <N>. <Title>` — a number, a period, a space, then the title (e.g. `## 1. Project Overview`, `## 2. Backend — Azure Functions`). Headings without the `N.` number prefix (e.g. `## Overview`, `## Architecture`, `## Services`, `## Data Stores`) are **invalid** and will not render.
> - Metadata at the top MUST be `**Status**:`, `**Created**:`, `**Mode**:` bold key-value rows — not front-matter, not a table.
> - Do **NOT** add a generic architecture document, a `mermaid` diagram, or any heading not present in the skeleton. Data stores and architecture are captured **inside** the numbered sections, never as their own improvised headings.
> - The set and order of headings is fixed: `# Project Plan` → `## 1. Project Overview` → one `## N. <Service> — <role>` per service → `## N. Services Required` → `## N. Prerequisites` → `## N. Design System & UI` (frontend only) → `## N. Project Structure` → `## N. Route Definitions` → `## N. Next Steps`. Renumber only; never rename or reshape.
#### Plan Template
`.azure/project-plan.md` structure (replace all `{placeholders}`):
````markdown
# Project Plan
**Status**: Planning
**Created**: {date}
**Mode**: {NEW | AUGMENT}
---
## 1. Project Overview
**Goal**: {Brief description of what the user is building}. The project is designed so that every module is independently testable.
**App Type**: {API only | SPA + API | Full-stack SSR | Static + API | Background worker — **derived from the detected services**, not asked}
**API Login**: {Yes | No — copied from the `auth` requirements answer}
**Mode**: {NEW | AUGMENT}
**Deployment Plan**: {`.azure/plan.md` found — services derived from deployment plan | No deployment plan found}
---
## 2. Backend — Azure Functions
> One **stack section per service** — emit a `## N. <Service> — <role>` heading and a single combined table for the backend, a frontend section when the app has a UI, and extra sections for any worker services. The plan view turns every section that has a **Language** row into an editable, language-aware stack card, so each service picks its own language independently. Renumber the sections that follow to match the services you emit.
| Component | Technology |
|-----------|-----------|
| **Language** | {TypeScript / Python / C#} |
| **Runtime** | {Node / Bun / Deno / CPython / PyPy / .NET} |
| **Package Manager** | {npm / pnpm / pip / poetry / dotnet (NuGet)} |
| **Test Runner** | {vitest / jest / pytest / xUnit} |
| **Mocking Library** | {vi.mock / jest.mock / sinon / unittest.mock / **NSubstitute** (.NET — never Moq, see runtimes/dotnet.md)} |
| **Test Command** | {npm test / pytest / dotnet test} |
| **Orchestration** | docker-compose |
> **Language vs Runtime**: `Language` is the source language the user picked in this service's `language` question. `Runtime` is the execution runtime — default `Node` for TypeScript/JavaScript, `CPython` for Python, `.NET` for C#. Only deviate from the default (e.g. `Bun`, `Deno`, `PyPy`) when the user explicitly asks. **Package Manager and Test Runner are language-dependent** — match them to this service's Language (e.g. C# → `dotnet (NuGet)` + `xUnit`/`NUnit`/`MSTest`). The `Orchestration` row is recorded for the scaffold step but hidden in the plan UI — always keep it set to `docker-compose`.
---
## 3. Frontend — Web App
> Emit this section only when `services` contains a `frontend` service (derived App Type ≠ `API only` / `Background worker`); omit it entirely otherwise. The frontend is its own service with its own Language and **Framework**. Frontend Language is always **JavaScript or TypeScript** — even when the backend uses Python or C#, the frontend is a JS/TS app.
| Component | Technology |
|-----------|-----------|
| **Language** | {TypeScript / JavaScript} |
| **Framework** | {React + Vite / Vue + Vite / Angular / Svelte} |
| **Package Manager** | {npm / pnpm} |
| **Test Runner** | {vitest / jest} |
| **Mocking Library** | {vi.mock / jest.mock / sinon} |
| **Test Command** | {npm test} |
---
## 4. Services Required
| Azure Service | Role in App | Environment Variable | Default Value (Local) | Classification |
|---------------|------------|---------------------|----------------------|----------------|
| {Blob Storage} | {Store uploaded images} | {STORAGE_CONNECTION_STRING} | {UseDevelopmentStorage=true} | {Essential} |
| {PostgreSQL} | {Primary data store} | {DATABASE_URL} | {postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/appdb} | {Essential} |
---
## 5. Prerequisites
Identify the required tools, then inventory them by following [prerequisites.md](../shared-references/prerequisites.md). Always produce **both** groups — `### Run` and `### Debug` — as two sub-tables under this section. The plan webview shows the Run group always and the Debug group only when the user turns on the Autopilot toggle, so do not omit either group yourself.
The required tools are derived from the technology stacks and Azure services associated with each service. Map each stack to its chosen tooling, e.g. runtime - Node, package manager - npm, project type - Azure Functions Core Tools (for Azure Functions), etc. The Run vs Debug distinction is defined in [prerequisites.md](../shared-references/prerequisites.md): Run tools are needed to run the project; Debug tools (Docker, Docker Compose, VS Code extensions) are the local-debugging extras.
The Debug group must include a row for every detected project type's VS Code debug-integration extension from the prerequisites.md Debug Tools table (e.g. an Azure Functions service always adds `ms-azuretools.vscode-azurefunctions`). These extension rows are required and separate from the Run-group CLI/runtime tools — do not omit them just because a related Run tool (like Functions Core Tools) is already listed.
For each tool, record which planned service(s) need it in the `Service(s)` column (use `*` for global toolchain shared by all services, or list each service explicitly). For a container runtime or orchestrator (Docker, Docker Compose), list the service(s) whose Azure dependencies its emulators stand in for, rather than `*`.
After identifying the required tools, run the detection pass to fill the `Installed` column (✅ / ❓) and detected `Version`. Produce the columns `Tool`, `Service(s)`, `Installed`, and `Version` — **do not** add or emit any install links or URLs.
Every row resolves to just two states, following the status rules in [prerequisites.md](../shared-references/prerequisites.md): installed (✅) when a scan positively finds the tool, or unknown (❓) when it can't be confirmed. Never mark a tool ❌, because absence can't be proven in a sandboxed agent environment where version managers or shells may have limited access in locating an installed tool. Never leave the `Installed` column as a placeholder or `—`; every row must resolve to ✅ or ❓ from an actual scan. Inform the user to double-check all ❓ tools are installed before proceeding.
**Re-run the detection pass when the whole plan is (re)generated from scratch or the tool set itself changes** (e.g. a Runtime edit or an added/removed service). Do **not** re-run it for a partial regeneration that doesn't touch this section's tools — unless the user explicitly asks to recheck prerequisites.
---
## 6. Design System & UI
> **MANDATORY when `services` contains a `frontend` service.** Skip only when there is no frontend service (derived App Type `API only` / `Background worker`). The plan-preview webview parses this section by title (`s.title.toLowerCase().includes('design system')`) and the scaffold quality contract reads `Component Library:` to decide which real library primitives to render.
**Component Library**: {Fluent UI v9 / Vuetify 3 / Skeleton UI / Angular Material / Pico.css — see PLANNING QUICK REFERENCE → Component Library Defaults}
**Style Direction**: {1–2 sentence design intent, e.g. "Modern data-dense console with subtle elevations, rounded 4px corners, and an emphasis on scannable lists."}
**Typography**: {Inter, system-ui / Roboto / Segoe UI Variable}
### Color Palette
> **Choose colors that fit THIS app — never copy the example hexes.** Derive the palette from `Style Direction` above plus any brand cues in the user's prompt (industry, mood, named colors, an existing logo). The `{#…}` values are illustrative placeholders, **not** defaults — only fall back to a plain neutral set when the project genuinely has no brand or style direction (e.g. generic internal tooling). The **Usage** column must describe each color's role in **this app's** UI in domain terms, not generic boilerplate. Token names (`primary`, `accent`, `surface`, `text`, `muted`, `border`) are a **fixed contract** — do NOT rename, add, or drop them; the scaffold's quality contract and preview theming key off these exact names.
| Token | Hex | Usage |
|-------|-----|-------|
| `primary` | `{#…}` | {Brand color — primary buttons, links, active nav} |
| `accent` | `{#…}` | {Secondary accents, highlights} |
| `surface` | `{#…}` | {Page + card backgrounds} |
| `text` | `{#…}` | {Body text} |
| `muted` | `{#…}` | {Secondary text, captions, timestamps} |
| `border` | `{#…}` | {Dividers, input + card borders} |
### Pages
> **List THIS app's real screens and give each its own content-specific layout.** Name pages after what they show (recipe app → `Recipes` / `Recipe Detail` / `New Recipe`; issue tracker → `Board` / `Issue` / `Backlog`), and choose each page's region tokens from the records that page actually displays — a list-heavy page wants `table`/`card-list`, a single-record page wants `two-column(media+meta) + action-bar`, a capture flow wants `form`. Do **not** reuse one boilerplate layout for every row or pad a page with regions it has no content for.
| Page | Route | Purpose | Layout |
|------|-------|---------|--------|
| {Primary page — name it after the main entity} | `/` | {one-line purpose} | `{region tokens chosen for this page's content}` |
| {Next page} | `{/route}` | {one-line purpose} | `{region tokens for this page's content}` |
> **Layout tokens are layout INTENT, not implementation.** The scaffold agent renders them using `Component Library` primitives per the scaffold skill's `frontend-quality-bar.md`. Recognized tokens: `header, nav, sidebar, hero, main, list, card-list, grid, form, table, actions, action-bar, tabs, modal, footer`. Compound tokens: `split(a|b)` (1:2 columns), `two-column(a+b)` (1:1 columns).
### Sample Content
> **Shared content contract — this is what keeps the planning preview and the scaffolded app in parity.** The preview sub-agents (Step 3.5b) and the scaffold agent both read this block and render the **same** records, so the preview faithfully previews what ships instead of generic filler. Author it now, while you have full domain context (Sections 1–4).
For each page above, list 3–6 representative records using that page's primary entity — a short table or bullet list per page, whatever fits the data shape. Use **real values from this app's domain** (real entity names, realistic numbers, real states) — a recipe app lists recipes, an issue tracker lists issues, a storefront lists products. **Never** emit generic placeholders like "Item 1", "Recent items", "Card title", or lorem ipsum. The skeleton below shows the **format**, not the content — replace every `{...}` with your domain's records.
```
{Page name} — {primary entity}:
| {Field A} | {Field B} | {Field C} | {Status} |
| {record 1 …} | {…} | {…} | {state} |
| {record 2 …} | {…} | {…} | {state} |
| {record 3 …} | {…} | {…} | {state} |
{Form/settings page} — {field}: {realistic default} · {field}: {realistic default}
```
---
## 7. Project Structure
```
{Generated directory tree for the chosen stack}
```
---
## 8. Route Definitions
| # | Method | Path | Description | Request Body | Response Body | Status Codes |
|---|--------|------|-------------|-------------|--------------|-------------|
| 1 | GET | `/api/health` | Health check | — | `{ status, services }` | 200, 503 |
| {n} | {METHOD} | {/api/path} | {description} | {body or —} | {response shape} | {codes} |
---
## 9. Next Steps
1. Run **azure-project-scaffold** to execute this plan
2. Run **azure-project-integrate** to wire the frontend to live data, smoke-test the backend, and create the migrations
3. Run **azure-debug-plan** → **azure-debug-generate** for Docker emulators and VS Code debugging
4. Run the **azure-deploy** agent when ready; it uses **azure-app-onboard** for architecture, cost estimation, IaC generation, provisioning, and health verification
````
#### After Writing the Plan
> **Order matters — open the plan view BEFORE rendering the per-page previews.** The whole point of the loading state is that the user sees and can interact with the plan document while the page previews are still being generated. If you generate every preview page first and only then open the view, the plan appears late and the flow is broken.
0. **Self-check the structure BEFORE opening the view.** Re-read the `.azure/project-plan.md` you just wrote and confirm ALL of the following. If any check fails, **rewrite the file** to match the skeleton before continuing — do **not** open the view on a malformed plan (the webview would show a parse-error banner instead):
- The top has `**Status**:`, `**Created**:`, `**Mode**:` bold key-value rows (not YAML front-matter, not a table).
- `## 1. Project Overview` contains exactly one auth-related value: `**API Login**: Yes` or `No`.
- Every `##` heading matches `## <N>. <Title>` (numbered). There are **no** un-numbered `##` headings such as `## Overview`, `## Architecture`, `## Services`, or `## Data Stores`.
- There is **no** `mermaid` block and **no** improvised section outside the fixed skeleton.
- `## 1. Project Overview` exists and contains a `**Goal**:` row, and Section 5 (when a frontend exists) is `## N. Design System & UI` with a `**Component Library**:` row.
1. **Write the preview scaffolding** — Step 3.5a below: write `.azure/.preview-temp/theme.css` + `manifest.json` (every page `status: "pending"`). Skip this and all of Step 3.5 when there is no `frontend` service (derived App Type `API only` / `Background worker` — no UI to preview).
2. **Open the plan preview NOW** — the workflow rules in `azure-project-plan.agent.md` call the `open_plan_view` tool. Do this **immediately after `manifest.json` exists and before fanning out the page sub-agents**. The webview starts watching `.azure/.preview-temp/` and shows the plan document plus a *Generating preview…* placeholder per page.
3. **Render the page previews** — Step 3.5b below: fan out one sub-agent per page. The view is already open; its file watcher flips each page from *Generating preview…* to the rendered HTML as soon as its `<slug>.html` lands.
4. **Present plan**, ask for approval.
5. If approved, update status from `Planning` to `Approved`.
6. **Immediately invoke `azure-project-scaffold`** (auto-chain). Do NOT ask user to invoke manually. The scaffold agent treats `.azure/.preview-temp/*.html` as a presentation-quality visual spec and translates it into real components using the framework named in the Frontend stack section.
> **❌ STOP** — Do NOT proceed past approval until user approves. Once approved, auto-chain immediately.
---
### Step 3.5: Generate Frontend HTML/CSS Preview (parallel sub-agents)
> **Skip entirely** when Section 6 was omitted (i.e. no `frontend` service — derived App Type `API only` / `Background worker`). For all other app types this step is **mandatory** — without it, the plan-preview webview shows a permanent *Generating preview…* spinner and the user has no UI to approve.
**Output location:** `.azure/.preview-temp/` (note the leading dot on the folder name — it's a transient, gitignored scratch space). The scaffold agent reads it as a presentation-quality visual spec, then deletes it as the last step of scaffolding (see scaffold skill Step 13).
**Inputs:** the just-written `.azure/project-plan.md` Section 6 (Color Palette, Typography, Pages, Style Direction, Component Library), the **Shared design-quality principles** in [`../shared-references/frontend-quality-bar.md`](../shared-references/frontend-quality-bar.md) (the fidelity-agnostic contract this preview and the eventual scaffold both satisfy), plus the per-region recipes in [`references/html-preview.md`](references/html-preview.md). Read the html-preview reference **once** at the start of this step — the parent needs its `## Shared CSS` block for `theme.css` (Step 3.5a), its `## Icons` block, and its `## Token → HTML recipes` to hand per-page slices to the sub-agents (Step 3.5b). The sub-agents never receive the Shared CSS.
#### 3.5a. Write `theme.css` and `manifest.json` (do this BEFORE fan-out)
Both files MUST exist before the plan-preview webview opens, so the controller can render tabs in the loading state. Use the `create_file` tool — it's OS-agnostic and creates parent folders automatically.
**`.azure/.preview-temp/theme.css`** — single shared stylesheet derived from Section 6:
```css
:root {
/* ── Brand colors (from Section 6 palette) ── */
--color-primary: {hex from Section 6};
--color-on-primary: {white or near-black, whichever contrasts better};
--color-accent: {hex};
--color-on-accent: {white or near-black};
/* ── Surfaces (derive from the palette — do NOT assume a light theme) ── */
--color-surface: {hex — page background from Section 6};
--color-surface-raised: {a card/panel tone that reads as raised against surface — #ffffff for a light theme, a step LIGHTER than surface for a dark one};
--color-surface-sunken: color-mix(in srgb, var(--color-surface) 92%, var(--color-text) 6%);
/* ── Text & borders ── */
--color-text: {hex — e.g. #111827};
--color-muted: {hex — e.g. #6b7280};
--color-border: {hex — e.g. #e5e7eb};
/* ── Semantic (status badges, alerts) ── */
--color-success: #16a34a;
--color-warning: #d97706;
--color-danger: #dc2626;
/* ── Typography ── */
--font-body: {typography from Section 6}, system-ui, -apple-system, "Segoe UI", sans-serif;
--font-heading: var(--font-body);
--text-xs: 11px;
--text-sm: 13px;
--text-base: 14px;
--text-lg: 16px;
--text-xl: 20px;
--text-2xl: 26px;
--text-3xl: 34px;
/* ── Shape (match the roundness to Style Direction — these are a neutral middle, not a mandate) ── */
/* sharp/technical → 2–4px · balanced → the values below · soft/friendly → 12–18px */
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 16px;
--radius-pill: 9999px;
/* ── Spacing scale (4px base) ── */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 28px;
--space-7: 40px;
--space-8: 56px;
/* ── Elevation (multi-tier — gives cards/buttons/menus real depth) ── */
--shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06);
--shadow-md: 0 4px 12px rgba(15, 23, 42, 0.10);
--shadow-lg: 0 12px 28px rgba(15, 23, 42, 0.16);
}
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
background: var(--color-surface);
color: var(--color-text);
font-family: var(--font-body);
font-size: var(--text-base);
line-height: 1.55;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1, h2, h3, h4 {
font-family: var(--font-heading);
line-height: 1.2;
margin: 0;
}
a { color: var(--color-primary); text-decoration: none; }
a:hover { text-decoration: underline; }
/* Plus the shared component CSS from references/html-preview.md §Shared CSS */
```
> **Aim for presentation quality.** This stylesheet powers a **presentation-quality visual spec** — the screen a designer would show a stakeholder to sign off the look. Give it real depth (the multi-tier `--shadow-*` scale), consistent radius, a coherent themed palette, and clear type hierarchy. The `references/html-preview.md` Shared CSS block adds hover/focus transitions, skeleton shimmer, and polished empty/error states on top of these tokens. What the raw-static preview leaves to the scaffold is narrow and specific — real webfonts, JavaScript behavior, and real photos — governed by the shared contract in `shared-references/frontend-quality-bar.md`; everything else should already look finished here.
Paste the full Shared CSS block from `references/html-preview.md` into the same file (header, nav, sidebar, hero, etc. — keep names exactly as the reference defines so the per-page HTML matches).
**`.azure/.preview-temp/manifest.json`** — one entry per page in Section 6's Pages table:
```json
{
"generatedAt": "{ISO timestamp}",
"previewStatus": "{ready | generating}",
"pages": [
{ "slug": "dashboard", "title": "Dashboard", "route": "/", "status": "pending" },
{ "slug": "settings", "title": "Settings", "route": "/settings", "status": "pending" }
]
}
```
- `previewStatus` is a top-level field indicating the overall state of the preview set. Valid values: `"generating"` (initial generation or revision in progress), `"ready"` (all preview work is complete). Set it to `"generating"` before starting any preview file writes and to `"ready"` after all pages have been written. **Always update it, both during initial generation and when revising previews after user feedback**.
- `slug` is the kebab-cased page name (`Photo Upload` → `photo-upload`). It MUST match the eventual filename (`<slug>.html`). Slugs MUST be unique.
- `route` is the path from Section 6's Pages table verbatim. Default to `/<slug>` when missing.
- `status` starts at `"pending"` for every page. You SHOULD flip it to `"ready"` in step 3.5c after the HTML is written (keeps the manifest accurate), but the webview no longer depends on it — **the presence of a non-empty `<slug>.html` file is what makes a page render**. The manifest only supplies the page list (slug/title/route) and the initial loading tabs.
#### 3.5a-open. Open the plan view NOW — before fanning out
The instant `theme.css` and `manifest.json` exist, the agent workflow opens the plan view (the `open_plan_view` tool, per `azure-project-plan.agent.md` Step C). **Do this before Step 3.5b.** The user immediately sees the plan document plus one *Generating preview…* tab per manifest page, and can read and interact with the plan while the page sub-agents render in the background. Do **not** wait for the sub-agents to finish before the view opens — that delay is exactly the regression this ordering prevents.
> **Embedded webview only** (see agent Hard rule 8): the preview renders *exclusively* inside the plan webview's **UI Preview** card as sandboxed iframes — never `simpleBrowser.show`, `vscode.env.openExternal`, a dev server, or a `.preview-temp/*.html` editor tab. There is no port or URL for the planning preview.
#### 3.5b. Fan out one sub-agent per page (parallel)
Launch one `runSubagent` call per page, **all in a single tool-call batch** (the platform parallelizes independent sub-agent invocations). Cap at **4 concurrent** — if the plan has more than 4 pages, split into batches of 4. Each sub-agent's prompt MUST contain:
1. The page's row from Section 6's Pages table (page name, route, purpose, layout regions).
2. The Color Palette, Typography, Style Direction, and Component Library values (for visual fidelity hints).
3. **The app's domain context** — a 1–2 sentence summary of what the app does (from Sections 1–2) plus the relevant entity/data model, so the sub-agent knows what the page is actually about.
4. **That page's records from Section 6's Sample Content block** — the real, domain-specific rows/values the page must display. This is the shared content contract; the scaffold reproduces the same records.
5. **Only the region recipes this page needs** — from the `## Token → HTML recipes` section you already read, copy just the recipes for the layout tokens in this page's row, plus the `## Icons` block (inline-SVG library — every page needs it), the `## Adapting sizing to the domain`, `## Wrapping a full page`, and `## Hard rules` sections. **Do NOT pass the `## Shared CSS` block** (~500 lines) — it was already baked into `theme.css` in Step 3.5a, and sub-agents link to `theme.css` rather than the CSS source. **Do NOT pass recipes for regions this page's layout doesn't name.**
6. **State assignment (parent decides).** Distribute the three non-data states across the page set so each is depicted **at least once**: pick one data-bearing page to render in its `loading (skeleton)` state, one tab/section to render `empty`, and one page to show the `error (inline banner)`. Pass the matching recipe (and the instruction to use it) only to those assigned sub-agents; every other page renders the populated `data` state.
7. The exact output path: `.azure/.preview-temp/<slug>.html`.
8. A directive: *"Write a single self-contained, **presentation-quality** HTML file linking to `./theme.css` — it must look like a finished product screen, not a wireframe. Use the per-region recipes provided above, in the order from the page's Layout. Replace every `{...}` placeholder token with the real Sample Content provided above — never generic filler like 'Item 1', 'Recent items', or 'Card title'. Put a **real inline-SVG icon** (from the `## Icons` block) in every nav item, sidebar item, KPI tile, section-title row, empty state, and primary CTA — no emoji, no glyphs. **Fill every image slot** with a neutral placeholder (surface + border media block with a muted icon, or initials) — never a brand-color gradient and never an empty box. Name primary CTAs after their action ('Create project', not 'Submit'). In nav/sidebar, link each **sibling** page with `href="./<sibling-slug>.html"` (the sibling's kebab-cased page name, matching its `<slug>.html`) so cross-page navigation works; the current page's own link uses `href="#"`. Do NOT add a banner claiming the app 'will use' a different library. Do NOT add `<script>` tags or inline `on*=` handlers (they are stripped before rendering). Do NOT inline any CSS — all styling MUST come from `./theme.css` (only the tiny layout shims shown in the recipes are allowed)."*
Expected file shape:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{Page Title} — Preview</title>
<link rel="stylesheet" href="./theme.css">
</head>
<body>
<!-- Per-region HTML per references/html-preview.md, in the order from the plan's Pages table -->
</body>
</html>
```
> ⚠️ The `<link rel="stylesheet" href="./theme.css">` is load-bearing — the extension's `ScaffoldPlanViewController` substitutes it with an inline `<style>` block at runtime so the iframe `srcDoc` is self-contained. If you inline CSS or use a different `href`, the substitution won't fire and the preview will fall back to unstyled HTML.
#### 3.5c. (Optional) Flip statuses to `ready` after each page lands
The webview renders a page the moment its `<slug>.html` file exists — it does **not** wait for a manifest `status` change — so the preview appears even if you skip this step. Still, for an accurate manifest you SHOULD rewrite `manifest.json` with each page's `status` flipped to `"ready"` once its HTML is written. Either:
- update the manifest after every sub-agent completes (more responsive), or
- update once at the end after all sub-agents complete (simpler).
**Always set `"previewStatus": "ready"` in the final manifest update** after all pages are written — this is what dismisses the "Generating preview…" overlay in the webview.
The webview's file watcher refreshes on every change under `.azure/.preview-temp/`, so the user sees tabs flip from loading to rendered in near-real-time.
> **✅ Checkpoint**: `.azure/.preview-temp/{theme.css, manifest.json, *.html}` all exist. Every page has a non-empty `<slug>.html` (which is what makes it render; the manifest `status` is best-effort bookkeeping). `manifest.json` has `"previewStatus": "ready"`. The plan-preview webview now shows the rendered HTML inside the iframe per page.
#### 3.5d. Updating previews after user feedback
When the user requests changes to the plan that affect preview pages (e.g. color changes, layout changes, content changes), you MUST update `previewStatus` in `manifest.json` to signal the webview:
1. **Before writing any preview files**: update `manifest.json` with `"previewStatus": "generating"`. This shows the "Generating preview…" overlay immediately.
2. **Edit the affected files** — `theme.css`, `project-plan.md`, and/or individual `<slug>.html` pages as needed.
3. **After all preview files are written**: update `manifest.json` with `"previewStatus": "ready"`. This dismisses the overlay.
If the user's feedback only affects the plan text (e.g. renaming a section, adjusting a description) and does **not** require changes to any file in `.azure/.preview-temp/`, do **not** touch `previewStatus` — leave it at `"ready"`. The overlay is driven exclusively by this field; setting it to `"generating"` when no preview work is happening will confuse the user.
The webview watches the entire `.azure/.preview-temp/` folder, so the manifest update is picked up automatically. Skipping the `previewStatus` update when preview files *are* being rewritten will leave the overlay absent during generation, also confusing the user.
---
## ═══════════════════════════════════════════════════
## PLANNING QUICK REFERENCE (Inlined — No External Reads)
## ═══════════════════════════════════════════════════
> All architectural context for planning. **Do NOT read external reference files during Phase 1.**
### Service-to-Environment-Variable Mapping
| Azure Service | Environment Variable | Local Default |
|---------------|---------------------|---------------|
| Blob Storage | `STORAGE_CONNECTION_STRING` | `UseDevelopmentStorage=true` |
| Queue Storage | `STORAGE_CONNECTION_STRING` | `UseDevelopmentStorage=true` |
| Table Storage | `STORAGE_CONNECTION_STRING` | `UseDevelopmentStorage=true` |
| PostgreSQL | `DATABASE_URL` | `postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/{dbname}` |
| CosmosDB | `COSMOSDB_CONNECTION_STRING` | `AccountEndpoint=https://localhost:8081/;AccountKey=...` |
| Redis | `REDIS_URL` | `redis://localhost:6379` |
| Azure SQL | `SQL_CONNECTION_STRING` | `Server=localhost,1433;Database={db};...` |
| Azure OpenAI | `AZURE_OPENAI_ENDPOINT`, `AZURE_OPENAI_API_KEY` | _(no local emulator)_ |
> ⚠️ **.NET runtime override (C# Functions):** .NET scaffolds use the standard `ConnectionStrings:*` config convention (read via `IConfiguration.GetConnectionString("...")`), **NOT** the generic env var names above. When the selected runtime is `csharp`, translate the table above as follows:
>
> | Generic env var | .NET config key |
> |-----------------|-----------------|
> | `STORAGE_CONNECTION_STRING` | `ConnectionStrings:Storage` |
> | `DATABASE_URL` | `ConnectionStrings:AppDb` |
> | `REDIS_URL` | `ConnectionStrings:Redis` |
> | `COSMOSDB_CONNECTION_STRING` | `ConnectionStrings:Cosmos` |
> | `SQL_CONNECTION_STRING` | `ConnectionStrings:Sql` |
> | `AZURE_OPENAI_ENDPOINT` / `_API_KEY` | `OpenAI:Endpoint` / `OpenAI:ApiKey` (typed `IOptions<T>`) |
>
> In production, the `ConnectionStrings:*` values should be **resource URIs** (e.g., `https://<account>.blob.core.windows.net`) authenticated via `DefaultAzureCredential` (Managed Identity) — never raw account keys. See [runtimes/dotnet.md](.github/agents/shared-references/runtimes/dotnet.md#managed-identity--quick-reference) for the full mapping.
### Essential vs Enhancement Classification
| Type | Definition | Failure Behavior | Examples |
|------|-----------|-----------------|---------|
| **Essential** | Request cannot succeed without this service | Propagate error (4xx/5xx) | Database, primary storage |
| **Enhancement** | Request can succeed with degraded output | Catch error, use fallback, log warning | AI captions, email notifications, analytics |
> **Key rule**: Enhancement service constructors MUST NOT throw. Defer config validation to method calls or wrap in try/catch.
### Component Library Defaults (Section 6 of the plan)
> **Pick the default for the user's frontend framework** unless the user explicitly named a different library. The chosen value goes into Section 6 verbatim as `**Component Library**: {value}` and becomes the load-bearing input for the scaffold quality contract (see scaffold skill `references/frontend-quality-bar.md`).
| Frontend framework | Default `Component Library` | Reasonable alternatives | Use the default unless... |
|---------------|----------------------------|------------------------|---------------------------|
| `React` | **Fluent UI v9** (`@fluentui/react-components`) | shadcn/ui + Radix, Material UI v6, Chakra UI v3 | user explicitly names one of the alternatives, OR project already has another library installed |
| `Vue` | **Vuetify 3** | PrimeVue 4, Element Plus | user explicitly names one |
| `Svelte` | **Skeleton UI** | Melt UI + Tailwind | user explicitly names one |
| `Angular` | **Angular Material** | PrimeNG | user explicitly names one |
| `None` (plain HTML / Static + API) | **Pico.css** + native form controls | Bulma, water.css | user explicitly names one |
| `None` + `Background worker` | omit Section 6 entirely | \u2014 | always omit when there is no UI |
> **Why this matters**: Without `Component Library:`, the scaffold step treats the region tokens (`header`, `hero`, `grid`, ...) as raw layout instructions and produces blocky placeholder `<div>` JSX that LOOKS worse than the presentation-quality plan preview. With `Component Library:` set, the scaffold renders each region using real library primitives (cards, tabs, fields, toolbars, message bars) themed by the Color Palette.
> **Plan-preview note**: The plan-preview webview renders Section 6 as a **sandboxed HTML/CSS iframe** loaded from `.azure/.preview-temp/<page>.html`. It is a **presentation-quality** preview — themed palette, real inline-SVG icons, elevation, populated content, and all four data states. What it leaves to the scaffold (raw-static limits) is narrow: real webfonts, JavaScript behavior, and real photos. The scaffolded app reproduces this look with the `Component Library` named in the plan and adds those production-only capabilities — it should look like the same product, brought to life, never less polished than the preview.
### Error Response Contract
All error responses follow this shape:
```json
{ "error": { "code": "NOT_FOUND", "message": "Item not found", "details": null } }
```
| Error Code | HTTP Status | When |
|------------|-------------|------|
| `VALIDATION_ERROR` | 422 | Request body fails validation |
| `BAD_REQUEST` | 400 | Malformed request |
| `NOT_FOUND` | 404 | Resource doesn't exist |
| `CONFLICT` | 409 | Duplicate resource |
| `FORBIDDEN` | 403 | Insufficient permissions |
| `INTERNAL_ERROR` | 500 | Unhandled exception |
### Example Project Structure (TypeScript — SPA + API)
> This is a **default convention for a brand-new project**, not a mandate. When the workspace already has a structure, follow it; never assume or impose these exact paths. Treat the names below (`services/functions`, `services/web`, `services/shared`, …) as illustrative roles the agent maps onto the user's actual layout.
>
> **Prefer domain-specific names for the deployable apps.** When the project has a clear product name, derive a kebab-case slug and name the Functions backend `services/<project>-api` and the frontend `services/<project>-<type>` (`-portal`/`-app`/`-web`, whichever fits) — e.g. for an office-compliance calendar: `services/office-compliance-api`, `services/office-compliance-portal`. Keep the shared package generic (`services/shared`). Fall back to the generic `functions`/`web` only when there is no clear project name. Whatever you choose, record it in Section 7 and use it consistently across `workspaces`, imports, and `main`/`rootDir`.
```
project-root/
├── .azure/
│ └── project-plan.md
├── .env.example
├── .gitignore
├── package.json ← Root workspace config
├── services/
│ ├── functions/ ← Azure Functions project
│ │ ├── host.json
│ │ ├── local.settings.json
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ ├── src/
│ │ │ ├── functions/ ← One handler per file
│ │ │ ├── services/ ← Service abstraction layer
│ │ │ │ ├── interfaces/ ← Service contracts
│ │ │ │ ├── config.ts ← Config loader + validation
│ │ │ │ └── registry.ts ← Service factory / DI
│ │ │ ├── errors/ ← Error types and middleware
│ │ │ └── middleware/
│ │ ├── tests/
│ │ │ ├── fixtures/
│ │ │ ├── mocks/
│ │ │ ├── services/
│ │ │ ├── functions/
│ │ │ └── validation/
│ │ └── seeds/
│ ├── web/ ← Frontend (if applicable)
│ │ ├── package.json
│ │ ├── vite.config.ts
│ │ └── src/
│ │ ├── api/client.ts ← Typed API client
│ │ ├── components/
│ │ ├── pages/
│ │ └── hooks/
│ └── shared/ ← Shared types and schemas
│ ├── package.json
│ ├── types/
│ │ ├── entities.ts ← Entity types
│ │ └── api.ts ← Response contracts + ErrorCode
│ └── schemas/
│ └── validation.ts ← Zod schemas + inferred request types
```
### Shared Types Design Rule
> **Do NOT define request types in BOTH `types/api.ts` AND `schemas/validation.ts`.** With Zod, `z.infer<typeof schema>` ARE canonical request types:
> - `types/entities.ts` → Entity interfaces
> - `types/api.ts` → Response types, ErrorCode union
> - `schemas/validation.ts` → Zod schemas + inferred request types
### Architecture Core Principles
1. **Service boundary isolation** — Every Azure service behind interface
2. **Dependency injection** — Handlers receive services, never import SDKs
3. **Environment-driven config** — Same code for mocks, emulators, Azure
4. **Monorepo by default** — Frontend, backend, shared types in one repo
5. **Contracts first** — Shared types before implementation
6. **One function per file** — Each Function independently testable
---
## Outputs
| Artifact | Location |
|----------|----------|
| **Project Plan** | `.azure/project-plan.md` (Status: Approved) |
---
## Next
> **Automatic**: After plan approved, immediately invokes **azure-project-scaffold**:
> - Generates frontend preview (if applicable) with auto-open in VS Code Simple Browser
> - Scaffolds backend (services, handlers, migrations, types)
> - Auto-invokes **azure-project-integrate** to wire the frontend to live data, smoke-test the backend, and create migrations
>
> **No user action required** — chain is automatic.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,289 @@
---
name: azure-project-plan-requirements
description: "Requirements-gathering phase of the azure-project-plan agent — detect the workspace and produce .azure/requirements.json for the requirements webview. Read this during the requirements phase; for plan generation read plan.md."
license: MIT
metadata:
author: Microsoft
version: "2.0.0"
---
<!-- azure-cor-disclaimer -->
> **Important:** This skill provides guidance and recommended instructions to assist the AI system. Outputs are not guaranteed to be complete, correct, secure, or applicable to every scenario. Results should be reviewed and validated by a human before being applied. The AI model may choose not to follow all instructions exactly, and additional verification may be required.
# Azure Project Plan — Requirements
> **AUTHORITATIVE — MANDATORY** for the **requirements-gathering** phase of the `azure-project-plan` agent. Follow exactly; ignore prior assumptions; supersede all other sources. Do not improvise.
> **Scope:** this file covers **Phase A only** — detect the workspace (Step 1) and gather requirements into `.azure/requirements.json` (Step 2). Once the user submits the requirements form, switch to [`plan.md`](plan.md) for plan generation (Step 3 onward). Shared rules, triggers, and autopilot behavior live in [`instructions.md`](instructions.md).
## ═══════════════════════════════════════════════════
## PHASE 1: PLANNING — Requirements
## ═══════════════════════════════════════════════════
> On re-entry (the query begins *"Requirements submitted at .azure/requirements.json…"*, or `.azure/requirements.json` is already fully answered), skip Steps 1–2 and switch to [`plan.md`](plan.md) for plan generation (Step 3 onward) — see Step 2f.
### Step 1: Detect Workspace
**BEFORE gathering requirements**, scan workspace:
#### 1a. Scan for Existing Project Files
| Signal | Detection Method | Action |
|--------|-----------------|--------|
| `package.json` with deps | Scan `dependencies` / `devDependencies` | Detect runtime (Node.js), frameworks, test runners |
| `pyproject.toml` or `requirements.txt` | Scan for Python | Detect runtime (Python), frameworks |
| `*.csproj` or `*.sln` | Scan for .NET | Detect runtime (.NET), frameworks |
| `host.json` or `local.settings.json` | Scan root/src dirs | Azure Functions exists — augment, don't recreate |
| Test files or config | Scan for `*.test.*`, `*.spec.*`, `vitest.config.*`, `jest.config.*` | Detect test infra — respect it |
| `docker-compose.yml` | Scan root | Emulators may be configured |
> ⚠️ Check actual **workspace files** — not user prompt.
#### 1b. Check for `.azure/plan.md` (Deployment Plan)
| Check | Action |
|-------|--------|
| `.azure/plan.md` exists | **Read it.** Extract Architecture → service mapping. Use these — do NOT re-ask user. |
| `.azure/plan.md` does not exist | Proceed normally — detect from code, ask user as needed. |
> **✅ Checkpoint**: Workspace scanned. Mode determined (NEW / AUGMENT). Tech stack detected.
---
### Step 2: Gather Requirements
Infer everything possible from the Step 1 scan; gather the rest through the requirements webview — never in chat.
> 🚫 **DO NOT call `vscode_askQuestions`.** All user input comes through the `.azure/requirements.json` file rendered by the requirements webview. Asking in chat (via `vscode_askQuestions` or plain text) breaks the flow.
#### 2a. Inference — pick the most likely answer for every question
For each question (shared + per-service), use the Step 1 scan + the user's prompt to fill:
- **`answer`** — the inferred value when confident, else `null` (`[]` for array-typed `dataStores`). When the app needs no datastore, use `["No datastore required"]`, not an empty array.
- **`recommendedChoice`** — always provide one (string for single-select questions, `string[]` for `dataStores`); becomes the **pre-selected** option in the webview, even for `needs_input`. For `dataStores`, recommend **every** store the app needs, not just one — it is a subset and often has more than one entry. Recommend `["No datastore required"]` when the app needs no datastore. The webview shows a Recommended badge on each selected recommendation (including the `Blob Storage` cases required by the `dataStores` MUST rules below).
Then set **`status`**:
- **`inferred`** — confidently known from workspace signals or an explicit user statement. Set `answer` + `rationale`; the webview pre-selects the inferred value for review/override.
- **`needs_input`** — not confidently known. Leave `answer` `null` (`[]` for `dataStores`); the webview pre-selects your `recommendedChoice` to confirm or change.
| If you detect... | Then infer... |
|-----------------|---------------|
| `.azure/plan.md` exists | Read it — extract all Azure services. Authoritative. |
| `@azure/storage-blob` import | App uses Blob Storage |
| App stores files, photos, images, uploads, documents, or media | App uses Blob Storage |
| Backend service uses Azure Functions | App uses Blob Storage (Functions requires a storage account) |
| App needs no persistence, file/object storage, queue, or cache, and no service requires an associated storage account | App uses `No datastore required` |
| `@azure/cosmos` import | App uses CosmosDB |
| `pg` or `psycopg2` import | App uses PostgreSQL |
| `redis` or `ioredis` import | App uses Redis |
| `react` in dependencies | Frontend = React |
| `vue` in dependencies | Frontend = Vue |
| `@angular/core` in dependencies | Frontend = Angular |
| `svelte` in dependencies | Frontend = Svelte |
| `vitest` in devDependencies | Test runner = vitest |
| `jest` in devDependencies | Test runner = jest |
| `mocha` in devDependencies | Test runner = mocha+chai+sinon |
| `host.json` exists | Azure Functions already initialized — augment mode |
| `zod` in dependencies | Validation library = zod |
| `host.json` + `dotnet-isolated` worker runtime | Runtime = C#; Backend = Azure Functions isolated worker; Orchestration = docker-compose |
Anything the user stated explicitly in their prompt ("build me a TypeScript Functions API with PostgreSQL") is also `inferred` — don't re-ask.
#### 2b. Services and questions
The requirements JSON has two top-level concepts:
1. **`services[]`** — detected service roots (backends, frontends, workers). Each gets a per-service question section in the webview.
2. **`questions[]`** — questions, either scoped to a service (`serviceId` set) or shared/cross-cutting (`serviceId` omitted).
##### Services
For each runnable service root detected in the workspace (or planned from the user's prompt), emit a service entry:
```json
{
"id": "{kebab-case-id}",
"label": "{Human-readable name, e.g. Payments API}",
"role": "{backend | frontend | worker}",
"root": "{workspace-relative path, e.g. ./api}"
}
```
- **`role`** — `backend` for APIs/Azure Functions, `frontend` for SPAs/web apps, `worker` for background/queue processors.
- **`root`** — workspace-relative path to the service directory. Omit for NEW-mode when no directories exist yet.
- Derive `id` from the project manifest name (e.g. `package.json` → `"name"`) when available, else from the directory name.
##### Per-service questions
For **each service**, emit these questions with `"serviceId": "{service.id}"`:
| `id` pattern | `header` | `question` | Options / Type | Notes |
|---|---|---|---|---|
| `{serviceId}:language` | Language | Which programming language for {label}? | `TypeScript`, `Python`, `C# (.NET)` for backends/workers; `TypeScript`, `JavaScript` for frontends | `allowFreeformInput: false` |
| `{serviceId}:framework` | Framework | Which framework for {label}? | Frontends: `React + Vite`, `Vue + Vite`, `Angular`, `Svelte`; Backends: `Azure Functions`, etc. | `allowFreeformInput: true`; omit for backends when Azure Functions is the only option |
| `{serviceId}:features` | Features | Describe the features or API routes for {label}. | Free text (omit `options`) | |
Use `category: "service"` for all per-service questions. The webview groups them under the service card, not by category.
##### Shared questions (no `serviceId`)
These are asked once for the whole project. Always emit both:
| `id` | `category` | `header` | `question` | Multi-select | Free-form | `options` | Default `recommendedChoice` |
|---|---|---|---|---|---|---|---|
| `dataStores` | `data` | Data Stores | Which data stores does your app need? | **yes** | no | `No datastore required` (exclusive), `Blob Storage`, `Queue Storage`, `PostgreSQL`, `CosmosDB`, `Redis`, `Azure SQL` | Every store the app needs (often more than one), or `No datastore required` |
| `auth` | `auth` | API Login | Does this app need user login? | no | no | `Yes`, `No` | `Yes` for accounts, private/per-user data, or signed-in experiences; otherwise `No` |
The `auth` question covers only user-facing application login and authenticated API access. It does not select Microsoft Entra ID, another identity provider, managed identity, API keys, or any Azure service credential. Infer `Yes` when the app has accounts, private/per-user data, or signed-in experiences. Infer `No` for public apps with no user identity. The scaffold agent chooses the stack-appropriate API authentication implementation. Backend-to-Azure communication always uses managed identity in production and is never a requirements choice.
> The old `appType`, `runtime`, and `frontend` questions are gone. **App Type is no longer asked** — it's derived from the detected `services` (see below). Language and framework are now per-service questions.
##### App Type is derived, not asked
Do **not** emit an `appType` question. Instead, derive the plan's App Type from the `services` array:
| Detected services | Derived App Type |
|---|---|
| At least one `frontend` + at least one `backend` | `SPA + API` |
| Only `backend` service(s) | `API only` |
| Only `worker` service(s) | `Background worker` |
| `frontend` that is server-rendered (Next.js SSR, etc.) | `Full-stack SSR` |
| Static `frontend` + `backend` | `Static site + API` |
Use this derived value to fill Section 1 of the plan and to decide whether to emit the Frontend / Design System sections.
Each option is `{ label, description, exclusive? }`. Set `exclusive: true` only on an option that cannot be combined with any other selection. Include `multiSelect`, `allowFreeformInput`, `recommendedChoice`, `status`, `answer`, and `rationale` on every question.
#### 2c. Write `.azure/requirements.json`
Write the file at `.azure/requirements.json` (no leading dot on the filename — this is the path the extension's file watcher matches). Use this exact top-level shape:
```json
{
"schemaVersion": "2",
"generatedAt": "{ISO date}",
"mode": "{NEW | AUGMENT}",
"summary": "{1–2 sentences describing what the user is building}",
"workspaceSignals": {
"decision": "{NEW | AUGMENT}",
"decisionReason": "{one sentence on why}",
"detectedFiles": ["{relative paths from Step 1, if any}"]
},
"services": [
{ "id": "functions-api", "label": "Functions API", "role": "backend", "root": "./api" },
{ "id": "web-app", "label": "Customer Portal", "role": "frontend", "root": "./web" }
],
"questions": [
{
"id": "functions-api:language", "category": "service", "serviceId": "functions-api",
"header": "Language", "question": "Which programming language for Functions API?",
"multiSelect": false, "allowFreeformInput": false,
"options": [
{ "label": "TypeScript", "description": "Node.js + TypeScript on Azure Functions" },
{ "label": "Python", "description": "Python on Azure Functions" },
{ "label": "C# (.NET)", "description": "Isolated worker on .NET 10" }
],
"recommendedChoice": "TypeScript", "status": "inferred", "answer": "TypeScript",
"rationale": "Detected package.json with TypeScript devDependency."
},
{
"id": "functions-api:features", "category": "service", "serviceId": "functions-api",
"header": "Features", "question": "Describe the features or API routes for Functions API.",
"multiSelect": false,
"recommendedChoice": "Photo upload/list/delete, AI captions",
"status": "inferred", "answer": "Photo upload/list/delete, AI captions",
"rationale": "Distilled from the user's prompt."
},
{
"id": "web-app:language", "category": "service", "serviceId": "web-app",
"header": "Language", "question": "Which programming language for Customer Portal?",
"multiSelect": false, "allowFreeformInput": false,
"options": [
{ "label": "TypeScript", "description": "TypeScript with type safety" },
{ "label": "JavaScript", "description": "Plain JavaScript" }
],
"recommendedChoice": "TypeScript", "status": "inferred", "answer": "TypeScript",
"rationale": "TypeScript is the most popular choice for modern SPAs."
},
{
"id": "web-app:framework", "category": "service", "serviceId": "web-app",
"header": "Framework", "question": "Which frontend framework for Customer Portal?",
"multiSelect": false, "allowFreeformInput": true,
"options": [
{ "label": "React + Vite", "description": "React with Vite bundler" },
{ "label": "Vue + Vite", "description": "Vue with Vite bundler" },
{ "label": "Angular", "description": "Angular CLI" },
{ "label": "Svelte", "description": "Svelte + Vite" }
],
"recommendedChoice": "React + Vite", "status": "needs_input", "answer": null,
"rationale": "React is the most common pick for SPA + API on Azure."
},
{
"id": "dataStores", "category": "data", "header": "Data Stores",
"question": "Which data stores does your app need?",
"multiSelect": true, "allowFreeformInput": false,
"options": [
{ "label": "No datastore required", "description": "The app does not persist data or use storage, queues, or caches", "exclusive": true },
{ "label": "Blob Storage", "description": "Store files and images" },
{ "label": "Queue Storage", "description": "Async message queue" },
{ "label": "PostgreSQL", "description": "Relational database" },
{ "label": "CosmosDB", "description": "NoSQL document database" },
{ "label": "Redis", "description": "In-memory cache" },
{ "label": "Azure SQL", "description": "Managed SQL Server" }
],
"recommendedChoice": ["Blob Storage", "PostgreSQL"],
"status": "inferred", "answer": ["Blob Storage", "PostgreSQL"],
"rationale": "Photo files → Blob Storage; relational data → PostgreSQL."
},
{
"id": "auth", "category": "auth", "header": "API Login",
"question": "Does this app need user login?",
"multiSelect": false, "allowFreeformInput": false,
"options": [
{ "label": "Yes", "description": "Add a user sign-in flow and authenticated API access" },
{ "label": "No", "description": "No user sign-in or authenticated API access" }
],
"recommendedChoice": "Yes", "status": "inferred", "answer": "Yes",
"rationale": "The app stores private, per-user photos."
}
]
}
```
**Rules for the JSON** (the worked example above is the contract — these call out only the non-obvious constraints):
- **Services & IDs:** one `services` entry per detected/planned service; per-service question `id`s follow `{serviceId}:{questionType}` (e.g. `functions-api:language`), with `serviceId` matching the service.
- **Language options:** frontend services offer only `TypeScript` / `JavaScript`; backend/worker services offer `TypeScript`, `Python`, `C# (.NET)`.
- **Always emit both shared questions** (`dataStores`, `auth`), and **never emit an `appType`, identity-provider, or Azure credential question**. App Type is derived from `services` (see the derivation table above).
- **`auth` is strictly binary:** its only options, answer values, and recommendation values are `Yes` and `No`. Never put a provider, protocol, token type, or Azure credential in this question.
- **`allowFreeformInput` is fixed per type:** language `false`, `dataStores` `false`, framework `true`, `auth` `false`. Omit it for free-text feature questions.
- **`multiSelect`:** only `dataStores` is `true`; its `answer` and `recommendedChoice` are always `string[]`.
- **Answers:** `inferred` → fill `answer`; `needs_input` → `answer: null` (`[]` for `dataStores`). Always provide `recommendedChoice`.
- **No datastore:** `No datastore required` is an exclusive option. When selected or inferred, it must be the only value in `answer` and `recommendedChoice`. Never combine it with a concrete datastore.
- Use the field name **`rationale`** (not `reason`). Strict JSON — no comments, no trailing commas.
- **`dataStores` Blob Storage rule (MUST):** include `Blob Storage` in `recommendedChoice` — and in `answer` when the question is `inferred` — alongside any database whenever **either** (a) any service stores or serves files, photos, images, uploads, documents, or media, **or** (b) any backend service uses Azure Functions (which requires an associated storage account, `AzureWebJobsStorage`). A file/photo app — or a Functions app — whose recommendation is only a database is wrong.
> ❌ **DO NOT** ask the user which .NET version to target. If a service's language = `C# (.NET)`, the target framework is **always `net10.0`**. Only downgrade when the user explicitly states an older version.
#### 2d. Hand off to the webview — then stop
Once the file is written, **stop**. Do NOT print the JSON, summarize inferences, ask anything in chat, or proceed to plan generation. The agent's workflow rules open the requirements webview after this write; the user fills the `needs_input` questions and clicks **Submit**. The requirements controller writes the file back (statuses → `confirmed`) and re-invokes this agent saying the requirements are ready.
#### 2e. Requirements review is mandatory
Always write `.azure/requirements.json` and hand it off to the requirements webview, even when the prompt is fully unambiguous and every question is `inferred` in Step 2a. Inferred answers are pre-selected so review remains quick, but the user must still have an opportunity to confirm or change them before plan generation.
This holds for **small, frontend-only projects too**. A prompt like *"a simple unit converter web app — nothing needs to be saved, no accounts, no backend, just a clean little frontend tool"* is a complete, valid project: emit exactly one `services` entry with `role: "frontend"`, its `language`/`framework`/`features` questions, `dataStores` set to `["No datastore required"]`, and `auth` set to `No`. Do **not** shortcut this by writing `index.html` or any other app code, and do **not** skip the webview because "there is nothing to ask".
#### 2f. Re-entry — reading the answered file
When re-invoked with a query mentioning submitted requirements (e.g. *"Requirements submitted at .azure/requirements.json..."*), or whenever `.azure/requirements.json` has all questions `confirmed`/`inferred`:
1. Read `.azure/requirements.json`.
2. Treat `answer` fields as authoritative — do not re-ask, do not re-emit the file.
3. Switch to [`plan.md`](plan.md) and go directly to Step 3 (Generate Plan).
> **✅ Checkpoint**: Requirements gathered (via inference + webview submission). Ready to generate plan — continue in [`plan.md`](plan.md).