diff --git a/.github/skills/create-package-skill/SKILL.md b/.github/skills/create-package-skill/SKILL.md new file mode 100644 index 000000000000..aff78ada323d --- /dev/null +++ b/.github/skills/create-package-skill/SKILL.md @@ -0,0 +1,81 @@ +--- +name: create-package-skill +description: 'Interactive wizard that walks service teams through creating a package-specific skill for their Azure SDK package. Scans the package, detects customization patterns, scaffolds a SKILL.md with references, validates with vally lint, and registers in find-package-skill. WHEN: create package skill; add service skill; bootstrap skill for package; new package skill; skill for my SDK package; write skill for search; write skill for cosmos.' +--- + +# Create Package Skill Wizard + +> **Minimal beats comprehensive. Human-written beats auto-generated. Scaffold and iterate.** + +> Skills encode tribal knowledge — the "I wish someone had told me" stuff that's hard to learn from just reading code. Focus on what's non-obvious and package-specific. + +## Interaction Protocols + +**CONFIRM Protocol** (asset-producing steps — creating files): +1. PRESENT the proposed assets and explain why. +2. ASK exactly one question: "Create now (recommended), edit first, or skip?" +3. ACT immediately. Create → write files this turn. Edit → refine, re-ask. Skip → move on. + +**DECIDE Protocol** (informational/correction steps — no files created): +1. PRESENT the information or findings. +2. ASK one specific question appropriate to the decision. +3. PROCEED based on the answer. + +One question at a time. Respect "skip" — never re-ask or defer. + +## Wizard Flow + +Run each phase in order. **Progressive loading:** Read only the current phase file. + +| Phase | Description | Instructions | +|---|---|---| +| **Phase 0** | 🧭 Scan Package — detect architecture, customizations, key files | [phases/00-scan-package.md](phases/00-scan-package.md) | +| **Phase 1** | 📝 Scaffold SKILL.md — generate skill with step-by-step post-regen workflow (Option A) or reference-manual structure (Option B) | [phases/01-scaffold-skill.md](phases/01-scaffold-skill.md) | +| **Phase 2** | 📚 Generate References — create customizations.md (required) and optionally architecture.md | [phases/02-generate-references.md](phases/02-generate-references.md) | +| **Phase 3** | Validate -- run vally lint | [phases/03-validate.md](phases/03-validate.md) | +| **Phase 4** | 📋 Register — add to find-package-skill table | [phases/04-register.md](phases/04-register.md) | + +## Guardrails + +**Content:** +- Every line must be non-obvious and package-specific. No generic Python/SDK boilerplate. +- SKILL.md should be under 500 tokens (soft limit). Move details to references/. +- References under 1000 tokens each. Split if larger. +- Never duplicate what's already in `.github/copilot-instructions.md` or shared skills. + +**Relationship to existing SDK tools:** +- Package skills **complement** the Azure SDK MCP tools (`azsdk_package_generate_code`, `azsdk_package_build_code`, `azsdk_customized_code_update`, etc.) — they do NOT replace them. +- MCP tools handle deterministic operations (generate, build, test). Package skills provide the reasoning context an agent needs to use those tools correctly for a specific package, plus the package-specific verification commands the tools do not know about. +- Reference the existing tools for the overall workflow (e.g., "Run `tsp-client update` or `azsdk_package_generate_code`"), but DO include the copy-pasteable verification commands an agent needs: import smoke tests (`python -c "from ... import ..."`), `ApiVersion` reconciliation (`grep`, `python -c "import json; ..."`), diff commands (`git diff --name-only | grep ...`), and the package validation invocation (`azsdk_package_run_check with checkType="All"`). +- Never paraphrase an MCP tool's entire contract. Do include the one-line invocations the agent will run. + +**Structure:** +- Skill directory: `sdk///.github/skills//` +- Directory name MUST match `name` field in frontmatter (vally lint enforces this). +- `name` is the distribution package name, e.g. `azure-search-documents`, `azure-ai-projects`, `azure-mgmt-appconfiguration`. +- Use semicolons to separate trigger phrases in description (YAML-safe). + +**Security:** +- Never embed secrets or credentials in skill content. +- Never instruct agents to bypass CI, pylint, mypy, pyright, or other gating tools. +- Never instruct agents to edit files inside `_generated/` (or any file with the `"Code generated by Microsoft (R) Python Code Generator"` header) — always route through `_patch.py` or sibling hand-written modules. + +## Key Principles (from eval data) + +Our eval showed that skill **structure** matters more than **volume**: + +| Pattern | Impact | +|---|---| +| Numbered step-by-step post-regen workflow (Option A) | Agent executes verification top-to-bottom without inventing steps | +| Copy-pasteable verification commands (import smoke tests, `ApiVersion` reconciliation, git diff of generated operations/models) | Agent actually runs the checks instead of guessing whether customizations still work | +| "Expose new generated methods through `_patch.py`" step (mixin inheritance / override / polymorphic / create-or-update / list / `__all__` re-export) | Agent wires new generated operations into the public surface instead of leaving them unreachable | +| "Check `_patch.py` FIRST" directives | Changes agent default from "fix the error location" to "check the customization layer" | +| Per-file `Depends On` / `Defines` / `After Regeneration, Verify` inventory in `customizations.md` | Agent can pinpoint exactly which generated symbol a breakage maps to | +| Generated-file detection guidance (directory OR header comment) | Agent correctly identifies auto-generated files under both `_generated/` and inline layouts | +| Async parity checklist | Agent remembers to mirror sync changes into `aio/` | +| CHANGELOG / README update step | Prevents "silent" releases that ship new operations without docs | + +## References (load on demand) + +- [references/skill-template.md](references/skill-template.md) -- SKILL.md template with required sections +- [references/validation-tools.md](references/validation-tools.md) -- vally lint, CI workflow setup diff --git a/.github/skills/create-package-skill/phases/00-scan-package.md b/.github/skills/create-package-skill/phases/00-scan-package.md new file mode 100644 index 000000000000..f3ca568d502b --- /dev/null +++ b/.github/skills/create-package-skill/phases/00-scan-package.md @@ -0,0 +1,93 @@ +# Phase 0: Scan Package 🧭 + +> 📍 **Phase 0 — Scan Package** | Detect the package's architecture, customization patterns, and key files. + +## Step 1 — Identify the Package + +Ask the user: "Which SDK package should I create a skill for?" + +The user should provide either: +- A package name (e.g., `azure-search-documents`, `azure-ai-projects`, `azure-mgmt-appconfiguration`) +- A path (e.g., `sdk/search/azure-search-documents`, `sdk/ai/azure-ai-projects`) + +Resolve to the package root directory. Verify it exists and contains at least one of: `pyproject.toml`, `setup.py`, `tsp-location.yaml`, or a `swagger/README.md` (legacy autorest). + +## Step 2 — Scan the Package + +Scan the package using the checklist below. Use file_search / grep_search / read_file tools. + +### Scan Checklist + +1. **Code generation toolchain**: + - `tsp-location.yaml` → TypeSpec-generated package. Note the spec directory and commit SHA. + - `_metadata.json` → if present, record that `apiVersion` lives here (the skill will reconcile it against the hand-maintained `ApiVersion` enum). + - `swagger/README.md` → legacy autorest-generated (mgmt packages typically). + - Neither → hand-written package (rare). + +2. **Namespace root**: Derive from the package name by replacing `-` with `/` (e.g., `azure-ai-projects` → `/azure/ai/projects`). Verify the directory exists. + +3. **Generated layout detection**: + - If `/_generated/` exists → **nested layout**. Generated files are confined to `_generated/`; everything else is hand-written/customization. + - If `_generated/` is absent → **inline layout** (newer pattern). Generated files are identified by the header comment `"Code generated by Microsoft (R) Python Code Generator"` at the top of each file. + +4. **Customizations** (`_patch.py` at each level): + - `/_patch.py` — top-level client customization + - `/models/_patch.py` — model customization + - `/operations/_patch.py` — sync operations customization + - `/aio/_patch.py` — async client customization + - `/aio/operations/_patch.py` — async operations customization + + For each, check if it is **empty** (only copyright header, default docstring, empty `__all__`, empty `patch_sdk()`) or **non-empty** (defines classes/functions, or `__all__` has entries). Record the non-empty patches. + +5. **Hand-written utility modules**: Glob all `.py` files under `` excluding `_generated/`, `__init__.py`, `_version.py`, `_patch.py`. Classify each as generated (has the header) or custom. For custom utility modules, read the first few lines to get a one-line purpose. + +6. **Public API surface**: Read each `__init__.py` (top-level, `models/`, `operations/`, `aio/`, `aio/operations/`). Note re-exports from `_patch.py` and any renames/omissions vs `_generated/__init__.py` (when nested layout). + +7. **Async parity**: For each non-empty sync customization file, check whether the async counterpart exists under `aio/`. Record `identical`, `equivalent`, `divergent`, `sync-only`, or `async-only`. + +8. **Service API versions**: Search for an `ApiVersion` enum or `api_version` constants under `` (often inside `_patch.py` itself, not `_generated/`). Record the source file path, but do **not** hard-code the list of versions into the skill — point to the source file. + +9. **Hand-authored named classes**: Grep for `class .*Buffered|class .*Batching|class .*Sender|class .*Paged|class .*PageIterator` under ``. These are hand-authored utilities that the skill must name in its Customization Patterns Reference. + +10. **Top public symbols (for import smoke tests)**: Read the top-level `__init__.py` and `aio/__init__.py`. Record up to 3–5 most important public names (clients, senders, key models/enums) so Phase 1 can generate accurate `python -c "from import "` commands. + +11. **Tests**: Check `tests/` directory. Look for `conftest.py`, `*test_base*.py`, `assets.json` (recorded tests via Test Proxy), `testpreparer*.py`. Note whether tests are live, recorded, or both. + +12. **Documentation surfaces**: Check for `CHANGELOG.md` and `README.md` at the package root. Both are near-universal; the skill's Step 7 (Update Documentation and Samples) will reference them. + +13. **Management vs data plane**: If package name starts with `azure-mgmt-` → management-plane rules apply (see copilot-instructions). Otherwise data-plane. + +## Step 3 — Present Package Profile + +Print a concise summary: + +📋 **Package Profile** + +| Field | Value | +|---|---| +| Package | `` (e.g., `azure-ai-projects`) | +| Path | `sdk//` | +| Plane | data / mgmt | +| Generated | TypeSpec (`tsp-location.yaml`) / autorest (`swagger/README.md`) / hand-written | +| Metadata | `_metadata.json` present: Y/N | +| Layout | nested (`_generated/`) / inline (header-based) | +| Non-empty `_patch.py` files | List which levels have customizations (top / models / operations / aio / aio/operations / indexes…) | +| Custom utility modules | N hand-written `.py` files outside `_patch.py` | +| Hand-authored named classes | e.g., `SearchIndexingBufferedSender`, `SearchItemPaged` (or `none`) | +| Top public symbols | List of 3–5 names used for Phase 1 import smoke tests | +| Public API surface | Notes on re-exports, renames, omissions | +| Async parity | summary (identical / divergent / sync-only / async-only) | +| `ApiVersion` enum | source file + present Y/N | +| Tests | Unit: Y, Live: Y/N, Recorded (assets.json): Y/N | +| Docs | `CHANGELOG.md`: Y/N, `README.md`: Y/N | +| Suggested structure | Option A (step-by-step) / Option B (reference manual) | + +## Step 4 — DECIDE + +Question: "Does this profile look right? Anything to add or correct?" + +📍 **Phase 0 complete** | Package scanned | Next: Phase 1 + +--- +## → Next: Phase 1 — Scaffold SKILL.md +Read [01-scaffold-skill.md](01-scaffold-skill.md) and begin immediately. diff --git a/.github/skills/create-package-skill/phases/01-scaffold-skill.md b/.github/skills/create-package-skill/phases/01-scaffold-skill.md new file mode 100644 index 000000000000..f19d47d2d66f --- /dev/null +++ b/.github/skills/create-package-skill/phases/01-scaffold-skill.md @@ -0,0 +1,221 @@ +# Phase 1: Scaffold SKILL.md 📝 + +> 📍 **Phase 1 — Scaffold SKILL.md** | Generate the main skill file. + +> 📖 Read `references/skill-template.md` for the full template and the two allowed structure options. + +Using the package profile from Phase 0, generate a `SKILL.md` at: +``` +sdk///.github/skills//SKILL.md +``` + +The skill directory name MUST match the Python distribution package name +(e.g., `azure-search-documents`, `azure-ai-projects`, `azure-mgmt-appconfiguration`). +This is both the `name` in frontmatter and the directory name (vally lint enforces the match). + +## Structure decision + +From the Phase 0 profile, pick the structure option that fits: + +- **Option A — step-by-step workflow** (preferred when the package has multiple non-empty `_patch.py` files, hand-authored utility modules, or a hand-maintained `ApiVersion` enum). This is how `azure-search-documents` is structured. +- **Option B — reference manual** (simpler packages with few customizations; use when Option A would feel padded). + +Ask the user to confirm the choice, defaulting to A when any of these are true: >=2 non-empty `_patch.py`; `ApiVersion`/`DEFAULT_VERSION` present in `_patch.py`; hand-authored `Buffered`/`Batching`/`Paged` class detected. + +## Content principles (recap) + +- **Keep it static** regarding release state (no version numbers, no current default API version) — but **do** include copy-pasteable commands the agent will need. +- **Point to source** for mutable things (API version enum → point at `_patch.py`; generator target → point at `_metadata.json`). +- **Prefer TypeSpec over `_patch.py`** when the customization is expressible in the spec — note this in the customization patterns section. +- **Include MCP tool invocations** where appropriate (`azsdk_package_generate_code`, `azsdk_package_run_check`, etc.). For validation, use `azsdk_package_run_check with checkType="All"`. Include the direct `tsp-client update` CLI fallback for generation since agents routinely run it. + +## Required sections — Option A (step-by-step) + +### Frontmatter + +```yaml +--- +name: +description: '. WHEN: regenerate ; modify ; fix bug; add feature; tsp-client update; edit _patch.py.' +--- +``` + +Semicolons separate triggers (YAML-safe). Every trigger should include the distribution package name. + +### Intro (2–4 sentences) + +State the core premise in plain language: +- The generator never touches `_patch.py`, so customizations survive regeneration. +- But `_patch.py` imports from generated modules, which DO change. +- A rename / new parameter / removed enum value silently breaks `_patch.py`. +- The following steps catch those breaks. + +### Step 1 — Run Regeneration + +Minimal shell block with `cd sdk//`, then `tsp-client update` +(or `azsdk_package_generate_code`). Mention updating `tsp-location.yaml` with the new +spec commit SHA, and `_metadata.json` if the API version changed. + +### Step 2 — Verify Imports in All `_patch.py` Files + +Provide import smoke-test commands for the top public symbols (fill from Phase 0 scan): + +```bash +python -c "from import " +python -c "from .aio import " +python -c "from . import , " +``` + +List every non-empty `_patch.py` file in a tree diagram with its role (one line each). +Tell the agent: if any import fails, a generated class/enum was renamed or removed — +check `references/customizations.md` for the exact import each `_patch.py` depends on. + +### Step 3 — Check ApiVersion (only if `ApiVersion` enum exists in a `_patch.py`) + +Concrete commands to reconcile `_metadata.json` with the hand-maintained enum: + +```bash +python -c "import json; print(json.load(open('_metadata.json'))['apiVersion'])" +grep -A 20 'class ApiVersion' +grep 'DEFAULT_VERSION' +``` + +If the generated API version is not in the enum: add the member, update `DEFAULT_VERSION`, +update the docstring. Include a round-trip verification: + +```bash +python -c "from ._patch import ApiVersion, DEFAULT_VERSION; print(DEFAULT_VERSION.value)" +``` + +### Step 4 — Expose New Generated Methods Through `_patch.py` + +New generated operations do NOT automatically appear on the public surface in the +shape users expect. For each new method, decide which exposure path applies: + +```bash +# What new operations did regeneration add? +git diff --name-only | grep "_operations\.py" | grep -v _patch +git diff | grep -E '^\+\s+(async\s+)?def ' +``` + +Exposure paths (pick the one that applies to each new method): + +1. **Pass-through via generated mixin inheritance** — if the generated signature is + already user-friendly, the `_OperationsMixin` subclass in `_patch.py` + inherits it automatically. Only verify the method name does NOT collide with an + existing `_patch.py` override. + +2. **Override in the operations mixin** — if the method needs custom paging, custom + request building, response conversion, or error handling, add the override to + the `_OperationsMixin` class in the appropriate `_operations/_patch.py` + (and the async mirror). Typical triggers: custom `ItemPaged`, request-body + rewriting, `@search.*` field extraction, 413 batch splitting. + +3. **Polymorphic str-or-model wrapper** — for `delete_*` operations on a named + resource. Accept either a `str` name or the resource model; forward `e_tag` / + `match_condition` when the caller passes a model. + +4. **Create-or-update wrapper** — for `create_or_update_*` operations. Forward + `prefer="return=representation"`, `match_condition`, and `etag`; plus any + package-specific flags (e.g., `allow_index_downtime`, `skip_indexer_reset`, + `skip/disable cache`). + +5. **List projection wrapper** — for `list_*` operations that need a `select` + parameter, a name-only projection via `cls` callback, or a conversion from a + generated projection type back to the canonical model + (e.g., `_convert__response`). + +6. **Re-export via `__all__`** — any NEW public symbol defined or overridden in + `_patch.py` MUST be added to that file's `__all__`. Without this, `patch_sdk()` + will not surface it and the symbol will be missing from `from import *` + and from the `__init__.py` public API. Verify with: + ```bash + python -c "import ; print(sorted(.__all__))" + ``` + +Reminder: every sync exposure/wrapper needs a matching async mirror under `aio/`. +Skip the categories above that do not apply to this package. + +### Step 5 — Check for Model/Enum Changes That Affect Customizations + +```bash +git diff +``` + +List what to watch for, tailored to the package (from Phase 0 scan): +- Renamed enum values → backward-compat aliases in `_patch.py` reference old names +- Changed model constructors → subclassed models in `_patch.py` may break +- New fields on response models → extractor helpers need to be updated +- Changed request models → builder helpers need to wire new parameters through + +### Step 6 — Run package validation + +Run the full validation suite in one shot: + +``` +azsdk_package_run_check with checkType="All" +``` + +Mention what the package- and repo-level config already ignore (typically generated internals, `_vendor/`, `tests/`, `samples/`) so the agent knows errors will originate in `_patch.py` customizations. + +### Step 7 — Update Documentation and Samples + +**CHANGELOG**: +- Find the topmost `## (Unreleased)` section in `CHANGELOG.md`. +- Add entries under `### Features Added`, `### Breaking Changes`, or `### Bugs Fixed`. +- If no `(Unreleased)` section exists, create one above the latest release with the next version from `_version.py`. +- Include a small example entry block. + +**README**: +- Update usage examples for new client classes or operations. +- Update Key Concepts for new resource types. +- Update listed API version in Getting Started if it changed. + +### Customization Patterns Reference (inline) + +For each hand-written pattern that actually exists in the package (from Phase 0 scan), +add a 2–5 sentence subsection naming the pattern and describing *what it does* and +*what would break it*. Typical patterns: + +- Constructor reorder on a public client subclass +- Custom paging (standard `ItemPaged` replaced with a custom iterator) +- Hand-authored batching / buffered sender +- Polymorphic delete / create-or-update pattern +- Backward-compatible enum aliases (camelCase → UPPER_CASE) +- Field builders +- Monkey-patched staticmethod helpers +- Wire-format encoding helpers (e.g., pipe-delimited parameters) + +Each subsection should point to the specific `_patch.py` it lives in and, where relevant, +note: "Use `_patch.py` when TypeSpec cannot express the behavior, or when the behavior +is Python-specific. For TypeSpec-level customizations (preferred when possible), see +[typespec-python emitter docs](https://github.com/Azure/autorest.python/blob/main/packages/typespec-python/README.md)." + +### Pointer to `references/customizations.md` + +Single line / short paragraph telling the agent to read `references/customizations.md` +for the exhaustive file-by-file inventory. + +## Required sections — Option B (reference manual) + +See `references/skill-template.md` *Option B* for the section list. Apply the same +required content (tree of non-empty `_patch.py`, async parity reminder, import smoke +tests, `ApiVersion` reconciliation, CHANGELOG/README step, per-pattern reference). + +## Step 1 — Present + +Generate the full SKILL.md content and print it. Fill in package-specific details from +the Phase 0 profile. Mark anything you could not confirm with `` or +``. + +## Step 2 — CONFIRM + +Question: "Create this SKILL.md now (recommended), edit first, or skip?" + +If confirmed, create the skill directory and SKILL.md file. + +📍 **Phase 1 complete** | Created: SKILL.md | Next: Phase 2 + +--- +## → Next: Phase 2 — Generate References +Read [02-generate-references.md](02-generate-references.md) and begin immediately. diff --git a/.github/skills/create-package-skill/phases/02-generate-references.md b/.github/skills/create-package-skill/phases/02-generate-references.md new file mode 100644 index 000000000000..ef1e194c89cd --- /dev/null +++ b/.github/skills/create-package-skill/phases/02-generate-references.md @@ -0,0 +1,99 @@ +# Phase 2: Generate References 📚 + +> 📍 **Phase 2 — Generate References** | Create supporting reference documents. + +Only two reference files are ever considered, and only one is required: + +| File | Required? | Purpose | +|---|---|---| +| `references/customizations.md` | **Required** if any `_patch.py` file is non-empty, or hand-written utility modules exist | File-by-file inventory of every non-empty `_patch.py` | +| `references/architecture.md` | **Optional** | Only create if the layout is complex enough that a tree diagram inline in SKILL.md is insufficient | + +Packages like `azure-search-documents` intentionally ship with `customizations.md` only — the layout fits comfortably inline in SKILL.md. + +## references/customizations.md (canonical format) + +Use one **section per non-empty `_patch.py` file**, in this structure: + +``` +## File: `/_patch.py` + +### Depends On (from generated code) +- `` as `` (base class / direct import) +- ... + +### Defines +| Symbol | Type | What It Does | +|--------|------|-------------| +| `` | class / function / constant | | +| ... | ... | ... | + +### After Regeneration, Verify +- [ ] +- [ ] +- [ ] ... +``` + +If the file imports shared helpers from the sync partner instead of duplicating them, +add an **Imports From Sync (NOT Duplicated)** subsection listing the symbols. + +For each polymorphic-wrapper mixin or multi-method class, use a nested table of +`Method | Customization`. + +End the file with a section listing **Empty `_patch.py` Files (No Customizations)** — +just file paths, to tell the agent "no action needed after regeneration" for these. + +### Extraction rules + +- Read the actual `_patch.py` files. Extract imports, class names, function names, + method names, and monkey-patched assignments (e.g., `X.Foo = Y.FOO`). +- Use real snippets — do not fabricate code. +- For each `Defines` row, the "What It Does" cell MUST be grounded in the actual source. +- For each `After Regeneration, Verify` checkbox, pick the specific generated symbol + whose change would break this `_patch.py` — list that symbol by name. +- Do NOT include version numbers, current default API version values, or release-specific info. + +### Patterns worth calling out (when they exist) + +If any of these patterns appear in the package, each MUST be documented: + +- Constructor reorder / parameter swap on a public client subclass +- Custom paging (replacement of `ItemPaged` / `AsyncItemPaged`) +- Hand-authored batching / buffered sender (not generated at all) +- Polymorphic delete / create-or-update taking str or model object +- Backward-compatible enum aliases (assignment statements like `E.Foo = E.FOO`) +- Monkey-patched staticmethod (e.g., `E.Collection = staticmethod(Collection)`) +- Field builders (e.g., `SimpleField`, `SearchableField`, `ComplexField`) +- Response-model conversion helpers (e.g., `_convert_*_response`) +- Request-model builder helpers (e.g., `_build_*_request`) +- Continuation-token pack / unpack helpers + +## references/architecture.md (optional) + +Only create this file when the layout is complex enough that a tree diagram inline in SKILL.md is insufficient. + +If you do create it, include: + +- **Repository layout** — directory tree with generated/hand-written annotations +- **Namespace layout** — tree under `/` +- **Code generation** — toolchain (TypeSpec `@azure-tools/typespec-python` or autorest), `tsp-location.yaml` format, `_metadata.json` +- **Generated vs custom** — table with how to identify each +- **Public client types** — sync/async client classes and file locations +- **API version management** — where the version enum lives, how `_patch.py` interacts with it +- **Key supporting files** — policies, helpers, `mypy.ini`, `assets.json`, etc. +- **Dependencies** — key runtime / dev / test deps +- **Build / test / lint** — MCP tools (`azsdk_package_run_check with checkType="All"`) and any package-specific test entry points (not full command re-documentation) + +## Step 1 — Present + +Print the proposed reference files content. + +## Step 2 — CONFIRM + +Question: "Create these reference files now (recommended), edit first, or skip?" + +📍 **Phase 2 complete** | Created: references/ | Next: Phase 3 + +--- +## → Next: Phase 3 — Validate +Read [03-validate.md](03-validate.md) and begin immediately. diff --git a/.github/skills/create-package-skill/phases/03-validate.md b/.github/skills/create-package-skill/phases/03-validate.md new file mode 100644 index 000000000000..4c4d160b55ec --- /dev/null +++ b/.github/skills/create-package-skill/phases/03-validate.md @@ -0,0 +1,46 @@ +# Phase 3: Validate + +> 📍 **Phase 3 — Validate** | Run structural validation on the new skill. + +> 📖 Read `references/validation-tools.md` for tool details. + +## Step 1 — Structural Validation (vally lint) + +Run vally lint against the skill: + +```powershell +# If vally is installed (npm package from microsoft/evaluate) +vally lint sdk///.github/skills/ + +# If vally is not installed, check manually: +# - SKILL.md exists with valid YAML frontmatter +# - name field matches directory name (= distribution package name, e.g. azure-ai-projects) +# - All markdown links in SKILL.md resolve to existing files +# - No orphaned files in references/ (every file linked from SKILL.md) +``` + +Expected: 3/3 checks pass (spec-compliance, valid-refs, orphan-files). + +**Common failure**: `name-directory-mismatch` — the `name` in frontmatter doesn't match the directory name. Fix by renaming the directory to match the distribution package name (e.g., `azure-search-documents`, not `search-documents` or `com.azure.search-documents`). + +## Step 2 — Token Budget Check + +Verify token budgets: +- SKILL.md: under 500 tokens (soft), under 5000 (hard) +- Each reference file: under 1000 tokens + +If over budget, split content into additional reference files (for example, break `customizations.md` into `customizations-client.md`, `customizations-models.md`, `customizations-operations.md`). + +## Step 3 — DECIDE + +Present validation results. If all pass: +Question: "Validation passed. Proceed to register the skill?" + +If failures exist, present them and ask: +Question: "These issues need fixing. Fix now, or skip validation?" + +📍 **Phase 3 complete** | Validation: pass/fail | Next: Phase 4 + +--- +## → Next: Phase 4 — Register +Read [04-register.md](04-register.md) and begin immediately. diff --git a/.github/skills/create-package-skill/phases/04-register.md b/.github/skills/create-package-skill/phases/04-register.md new file mode 100644 index 000000000000..c9c36103a2dd --- /dev/null +++ b/.github/skills/create-package-skill/phases/04-register.md @@ -0,0 +1,48 @@ +# Phase 4: Register 📋 + +> 📍 **Phase 4 — Register** | Add the skill to the find-package-skill discovery table. + +## Step 1 — Update find-package-skill + +Add a row to `.github/skills/find-package-skill/SKILL.md` in the **Package Skills** table: + +```markdown +| `` | `sdk///.github/skills//SKILL.md` | +``` + +Example: + +```markdown +| `azure-ai-projects` | `sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects/SKILL.md` | +``` + +## Step 2 — Summary + +Print a summary of everything created: + +📋 **Package Skill Created** + +| Item | Path | Status | +|---|---|---| +| SKILL.md | `sdk///.github/skills//SKILL.md` | Created | +| architecture.md | `...references/architecture.md` | Created/Skipped | +| customizations.md | `...references/customizations.md` | Created/Skipped | +| find-package-skill | `.github/skills/find-package-skill/SKILL.md` | Updated | +| vally lint | 3/3 checks | Passed | + +**Next steps for the service team:** +1. Fill in any `` sections with domain-specific knowledge. +2. Test the skill by asking an agent to regenerate your package (`azsdk_package_generate_code`) and apply a small TypeSpec change — the agent should consult the skill, not guess. +3. Iterate: agent gets something wrong → update skill → test again. +4. Submit a PR. + +**Maintaining your skill:** +- When your package's `_patch.py` files or utility modules change, update the skill. +- Keep content static — no version numbers, no current API version values, no release-specific info. Point to source files for things that change. +- When adding a new sync customization, make sure the skill reminds the agent about the async counterpart under `aio/`. + +## Step 3 — CONFIRM + +Question: "Register the skill in find-package-skill now (recommended), or skip?" + +📍 **Phase 4 complete** | Skill registered | Wizard done 🎉 diff --git a/.github/skills/create-package-skill/references/skill-template.md b/.github/skills/create-package-skill/references/skill-template.md new file mode 100644 index 000000000000..ee416a2bcfb0 --- /dev/null +++ b/.github/skills/create-package-skill/references/skill-template.md @@ -0,0 +1,111 @@ +# SKILL.md Template for Azure SDK for Python Package Skills + +Use this template when creating a new package skill. Replace placeholders with package-specific content. + +```yaml +--- +name: +description: '. WHEN: regenerate ; modify ; fix bug; add feature; tsp-client update.' +--- +``` + +The `name` field and directory name MUST match the Python distribution package name (e.g., `azure-search-documents`, `azure-ai-projects`, `azure-mgmt-appconfiguration`). + +## Content Principles + +- **Workflow-first.** The primary audience is an agent running post-regeneration cleanup. Structure the skill so the agent can execute it top-to-bottom: regenerate → verify imports → check API version → check for new operations → check for model/enum changes → lint/type-check → update docs. See *Structure Options* below. +- **Include the commands the agent needs to copy-paste.** `tsp-client update`, `python -c "from import X"` import smoke tests, `grep -A 20 'class ApiVersion' `, `git diff --name-only | grep _operations\.py`, `azsdk_package_run_check with checkType="All"`, etc. Do not force the agent to invent commands. "Don't re-document MCP tools" means don't paraphrase an entire MCP tool contract — it does **not** mean omit the one-line invocation an agent is expected to run. +- **Point to source for things that change.** Never hardcode API version strings, release numbers, or the current `DEFAULT_VERSION` value. Point at `_patch.py`, `_metadata.json`, or `_version.py`. +- **Prefer TypeSpec-level customizations over `_patch.py`.** Note when a customization could be expressed by a TypeSpec decorator or emitter option instead. +- **Focus on the convenience layer.** What does the agent need to know to write/maintain `_patch.py`, hand-written utilities, and async parity correctly. + +## Structure Options + +Pick the one that fits how the package is maintained. + +### Option A — Step-by-step workflow (recommended when non-empty `_patch.py` files exist) + +Numbered steps the agent executes in order. Canonical skeleton: + +1. Run Regeneration (`tsp-client update` or `azsdk_package_generate_code`) +2. Verify Imports in All `_patch.py` Files (import smoke tests) +3. Check ApiVersion (reconcile `_metadata.json` with the hand-maintained `ApiVersion` enum) +4. Check for New Operations That Need Wrappers (git diff generated operations) +5. Check for Model/Enum Changes That Affect Customizations (git diff models/enums) +6. Run package validation (`azsdk_package_run_check with checkType="All"`) +7. Update Documentation and Samples (CHANGELOG, README) + +Followed by a *Customization Patterns Reference* section that names and briefly describes each hand-written pattern in the codebase (constructor reorder, polymorphic delete, enum aliases, hand-authored batching sender, custom paging, etc.), each with 2–5 sentences on *what it does* and *what would break it*. Exhaustive file-by-file detail goes to `references/customizations.md`. + +### Option B — Reference-manual (simpler packages, or when the workflow is shared at a higher level) + +Sections in this order: +1. **Common Pitfalls** — 3–5 most dangerous mistakes, FIRST. Always include: never edit generated files; check `_patch.py` FIRST; maintain async parity. +2. **Architecture** — namespace layout, generated vs hand-written, non-empty `_patch.py` files. +3. **After Regeneration** — error categorization table, API version management, breaking-change detection. +4. **Post-Regeneration Customizations** — per-pattern notes with async-counterpart guidance. +5. **Testing Notes** — optional. +6. **References** — table linking to `references/*.md`. + +## Required Content (both options) + +Regardless of structure, the skill MUST include: + +- Frontmatter with `name` == directory name == distribution package name, and a `WHEN:` trigger list that includes the package name. +- A list or tree of the non-empty `_patch.py` files in the package (the agent's map of "where customizations live"). Example: + ``` + azure// + ├── _patch.py # + ├── _operations/_patch.py # + ├── models/_patch.py # + └── aio/… # async mirror + ``` +- A reminder that `_patch.py` files are never overwritten by regeneration, but their imports from generated modules CAN break silently. +- Guidance that every sync customization must have an async mirror under `aio/`, and that shared helpers are **imported** (not duplicated) from sync into async. +- Explicit import smoke-test commands for the top public symbols. Example: + ```bash + python -c "from import " + python -c "from .aio import " + python -c "from . import , " + ``` +- An `ApiVersion` management note: where the hand-maintained enum and `DEFAULT_VERSION` live, and how to reconcile them with `_metadata.json`'s `apiVersion` after regeneration. Include the exact reconciliation commands, e.g.: + ```bash + python -c "import json; print(json.load(open('_metadata.json'))['apiVersion'])" + grep -A 20 'class ApiVersion' + ``` +- A **"Expose New Generated Methods Through `_patch.py`"** step with a diff command and the exposure paths that apply to the package: + - Pass-through via generated mixin inheritance (just verify no collision) + - Override in the operations mixin (custom paging, request rewriting, response conversion, error handling) + - Polymorphic str-or-model `delete_*` wrapper (forward `e_tag` / `match_condition`) + - `create_or_update_*` wrapper (forward `prefer="return=representation"`, `match_condition`, `etag`, plus package-specific flags like `allow_index_downtime`, `skip_indexer_reset`) + - List projection wrapper (`select`, name-only via `cls`, generated-projection-to-canonical-model conversion) + - **`__all__` re-export** — any new public symbol added/overridden in `_patch.py` MUST be appended to that file's `__all__`, otherwise `patch_sdk()` will not expose it. Include the verification command `python -c "import ; print(sorted(.__all__))"`. + Skip categories that do not apply. +- A "Check for Model/Enum Changes" step listing what a diff of the generated models/enums would break in the customizations (renamed enum values → aliases; changed model constructors → subclass constructors; new fields on response models → extractor helpers; new parameters on request models → builder helpers). +- A linting / type-checking step that runs `azsdk_package_run_check with checkType="All"` (covers mypy, pylint, and the rest of the validation suite in one invocation). Mention what the repo-level / package-level config already ignores so the agent knows where errors will come from. +- A **Documentation and Samples** step covering: + - CHANGELOG: find the topmost `## (Unreleased)` section; add entries under `### Features Added` / `### Breaking Changes` / `### Bugs Fixed`; create the section above the latest release if missing. Include a small example block. + - README: when to update usage examples, Key Concepts, and the listed API version. +- A per-pattern *Customization Patterns Reference* naming each hand-written pattern that actually exists in the codebase (NOT a generic list). + +## Optional Content + +- **Architecture reference file** — only create `references/architecture.md` if the layout is complex enough that readers need a separate map. Most packages are fine describing layout inline with a tree diagram. +- **Testing Notes** — include if the package has non-default test setup (Test Proxy `assets.json`, service-specific fixtures, required env vars). +- **References table** — useful if there are multiple reference files. + +## Structural Rules + +| Rule | Enforced By | +|---|---| +| `name` matches directory name (= distribution package name) | `vally lint` | +| All markdown links resolve | `vally lint` | +| No orphaned reference files | `vally lint` | +| Code references still exist in codebase | Manual review | +| SKILL.md under 5000 tokens | `vally lint` | +| No version numbers or release-specific info | Manual review | +| Trigger phrases include distribution package name | Manual review | +| No cross-language content | Manual review | +| Async parity reminder present | Manual review | +| Import smoke-test commands present | Manual review | +| CHANGELOG / README update step present | Manual review | diff --git a/.github/skills/create-package-skill/references/validation-tools.md b/.github/skills/create-package-skill/references/validation-tools.md new file mode 100644 index 000000000000..514c1298941f --- /dev/null +++ b/.github/skills/create-package-skill/references/validation-tools.md @@ -0,0 +1,24 @@ +# Validation Tools for Package Skills + +## vally lint (Structural Validation) + +**Source**: `microsoft/evaluate` (currently private, will be public) + +```bash +# Install (once published) +npm install -g @microsoft/vally-cli + +# Lint a skill +vally lint sdk///.github/skills/ + +# Lint all skills in repo +vally lint .github/skills +``` + +### What it checks + +| Check | What it does | Common failure | +|---|---|---| +| `spec-compliance` | Validates frontmatter (name, description), name-directory match | Directory name doesn't match `name` field (name must equal the distribution package name, e.g. `azure-ai-projects`) | +| `valid-refs` | All markdown links resolve to existing files | Broken link to reference file | +| `orphan-files` | No unreferenced files in references/ | File in references/ not linked from SKILL.md | diff --git a/.vscode/cspell.json b/.vscode/cspell.json index 4204a4d75848..4c9e7a14dfcc 100644 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -156,6 +156,9 @@ "sdk/ai/azure-ai-voicelive/samples/**" ], "words": [ + "vally", + "regen", + "pylintrc", "qnas", "msedge", "spinup",