[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M69BCNFGJTKKASKWDB0MM668":3,"skill-same:01M69BCNFGJTKKASKWDB0MM668":46},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":21,"frontMatter":22,"refName":23,"commitSha":24,"skillDirectory":25,"forksCount":26,"repositoryHtmlUrl":27,"repositoryHomepageUrl":6,"ownerHtmlUrl":28,"ownerBlogUrl":29,"ownerDescription":30,"updatedAt":31,"install":32,"resources":45},"01M69BCNFGJTKKASKWDB0MM668","azd-seed-hook",null,"microsoft\u002Ffsi-banking-payments-dispute-resolution-accelerator","microsoft","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6154722?v=4","01M698VYDXZ6JW5WH3CRV650R8","Microsoft","01M69BAVFSPPF5KP50DCBC2R4Q",0,"MIT","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ffsi-banking-payments-dispute-resolution-accelerator\u002Fblob\u002Fmain\u002F.squad\u002Fskills\u002Fazd-seed-hook\u002FSKILL.md",[],[18],"azd seed hook","2026-11-10 10:12:30",false,"# Skill: AZD Postdeploy Seed Hook (Python + Cosmos)\n\n**Category:** DevOps \u002F Infrastructure  \n**Author:** Fenster  \n**Date:** 2026-07-07\n\n> **Update (2026-07-09):** The top-level `postdeploy` hook described below was removed from\n> `azure.yaml` — it fired after every `azd deploy \u003Cservice>`, including `web`\u002F`portal` on the\n> GitHub-hosted runner, breaking CD once Cosmos went private. Seeding is now handled solely by\n> the `deploy-api` job in `.github\u002Fworkflows\u002Fcd.yml` (self-hosted, in-VNet runner). The pattern\n> below (seed script design, upsert idempotency, env var reconciliation) is still accurate for\n> the script itself — only the \"wire to `azure.yaml`\" hook step and the \"double-run is harmless\"\n> claim are now obsolete.\n\n---\n\n## Pattern: Idempotent Cosmos seed wired as an azd postdeploy hook\n\n### Problem\n\nAfter `azd up`, a freshly provisioned Cosmos container is empty. You want synthetic \u002F seed data automatically inserted on every clean deploy, without re-seeding breaking anything on subsequent runs.\n\n### Solution\n\n1. **Add `upsert_item` helper to `cosmos_client.py`** — one function, always idempotent:\n   ```python\n   def upsert_dispute(dispute: dict) -> dict:\n       return _get_container(\"disputes\").upsert_item(body=dispute)\n   ```\n\n2. **Write a seed script** at `src\u002Fapi\u002Fscripts\u002Fseed_cosmos.py`:\n   - Soft-fail if endpoint is absent (check both `COSMOS_ENDPOINT` and `AZURE_COSMOS_ENDPOINT`)\n   - Defer `import cosmos_client` until after env vars are set (preserves testability)\n   - Build documents per contract: `doc = {**case, \"id\": caseId, \"disputeId\": caseId, \"networkCode\": cardNetwork}`\n   - Call `upsert_dispute(doc)` per item\n\n3. **Reconcile env var names** — azd exposes Bicep outputs with AZURE_ prefix; the client may expect shorter names. In `_resolve_env()`:\n   ```python\n   endpoint = os.environ.get(\"COSMOS_ENDPOINT\") or os.environ.get(\"AZURE_COSMOS_ENDPOINT\", \"\")\n   os.environ[\"COSMOS_ENDPOINT\"] = endpoint  # set before deferred import\n   ```\n\n4. **Wire to `azure.yaml`** with posix\u002Fwindows split. Use `continueOnError: false` once runner deps are installed (see Key rules):\n   ```yaml\n   hooks:\n     postdeploy:\n       posix:\n         shell: sh\n         run: cd src\u002Fapi && PYTHONPATH=. python3 scripts\u002Fseed_cosmos.py\n         continueOnError: false\n       windows:\n         shell: pwsh\n         run: |\n           $env:PYTHONPATH = \"src\\api\"\n           python src\\api\\scripts\\seed_cosmos.py\n         continueOnError: false\n   ```\n\n### Key rules\n\n| Rule | Why |\n|------|-----|\n| **Prefer an explicit CD step over the azd postdeploy hook in CI** | The postdeploy hook output is invisible in CD runs (no log capture), and `continueOnError: true` silently swallows failures. An explicit `- name: Seed Cosmos DB` step after AZD Deploy shows output in the job log and fails loudly on non-zero exit. Keep the azure.yaml hook only for local `azd up` convenience — the double-run is harmless (upsert idempotency). |\n| Install app deps on CI runner before azd deploy | The postdeploy hook runs on the same runner; `ubuntu-latest` does NOT have `azure-cosmos` \u002F `azure-identity`. Add `actions\u002Fsetup-python@v5` + `pip install -r requirements.txt` steps before the AZD Deploy step in `cd.yml`. Without this, imports fail silently. |\n| Use `python3` in posix hooks, not `python` | Bare `python` is absent on `ubuntu-latest`; `python3` is the reliable alias. |\n| `continueOnError: false` once deps are installed | With runner deps in place, a non-zero exit means a real problem. Use `false` so genuine failures (import errors, RBAC 403, upsert exception) fail the deploy visibly. The script's own `sys.exit(0)` on missing endpoint handles the soft-fail case — no need to swallow all errors at the hook level. |\n| `continueOnError: true` only during bootstrap | Acceptable temporarily if RBAC propagation is known to lag and you have no retry wrapper. Remove as soon as possible. |\n| Defer `import cosmos_client` | Avoids `DefaultAzureCredential` construction at import time — critical for test mocking |\n| Soft-fail on missing endpoint | Enables running `azd up` from repo without Cosmos (e.g. local-only dev) |\n| Upsert not insert | Re-running the hook is always safe; no duplicate-key errors |\n\n### Testing\n\nMock `cosmos_client` before import with `sys.modules`:\n```python\nstub = MagicMock()\nmonkeypatch.setitem(sys.modules, \"cosmos_client\", stub)\nimport scripts.seed_cosmos as seed_mod\nmonkeypatch.setattr(seed_mod, \"cosmos_client\", stub, raising=False)\n```\n\nAssert `stub.upsert_dispute.call_count == len(cases)` and verify document shape.\n\nSoft-fail test: confirm `SystemExit(0)` is raised (not an exception) when endpoint unset.\n",{},"main","5494201db9619bc6c0de7999caa5ae7ae315e6be",".squad\u002Fskills\u002Fazd-seed-hook",3,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ffsi-banking-payments-dispute-resolution-accelerator","https:\u002F\u002Fgithub.com\u002Fmicrosoft","https:\u002F\u002Fopensource.microsoft.com","Open source projects and samples from Microsoft","2026-10-27 01:08:00",{"source":7,"skillName":5,"commands":33,"prompt":43,"zipAvailable":44},[34,37,40],{"tool":35,"command":36},"npx","npx skills add microsoft\u002Ffsi-banking-payments-dispute-resolution-accelerator --skill azd-seed-hook",{"tool":38,"command":39},"bunx","bunx skills add microsoft\u002Ffsi-banking-payments-dispute-resolution-accelerator --skill azd-seed-hook",{"tool":41,"command":42},"pnpm","pnpm dlx skills add microsoft\u002Ffsi-banking-payments-dispute-resolution-accelerator --skill azd-seed-hook","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ffsi-banking-payments-dispute-resolution-accelerator\u002Fblob\u002Fmain\u002F.squad\u002Fskills\u002Fazd-seed-hook\u002FSKILL.md",true,[],[47,62,68,82,96,106],{"skillNo":48,"name":49,"nameZh":6,"description":50,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":51,"categories":52,"keywords":58,"publishedAt":19},"01M69BCQ2R27TFYA8Q03XFNFWM","{skill-name}","{what this skill teaches agents}","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ffsi-banking-payments-dispute-resolution-accelerator\u002Fblob\u002Fmain\u002F.squad\u002Ftemplates\u002Fskill.md",[53],{"categoryNo":54,"categoryName":55,"taxonomyType":56,"taxonomyTypeDesc":57},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[59,60,61],"{skill name}","testing","design",{"skillNo":63,"name":49,"nameZh":6,"description":50,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":64,"categories":65,"keywords":67,"publishedAt":19},"01M69BCDES6M5FRTCMY7214B94","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ffsi-banking-payments-dispute-resolution-accelerator\u002Fblob\u002Fmain\u002F.squad\u002Fskill.md",[66],{"categoryNo":54,"categoryName":55,"taxonomyType":56,"taxonomyTypeDesc":57},[59,60,61],{"skillNo":69,"name":70,"nameZh":6,"description":71,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":72,"categories":73,"keywords":78,"publishedAt":19},"01M69BCBAGT24AD5SBDP876P09","tiered-memory","Three-tier agent memory model (hot\u002Fcold\u002Fwiki) for context reduction per spawn","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ffsi-banking-payments-dispute-resolution-accelerator\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Ftiered-memory\u002FSKILL.md",[74,77],{"categoryNo":75,"categoryName":76,"taxonomyType":56,"taxonomyTypeDesc":57},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":54,"categoryName":55,"taxonomyType":56,"taxonomyTypeDesc":57},[79,80,61,81],"tiered memory","memory","performance",{"skillNo":83,"name":84,"nameZh":6,"description":85,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":86,"categories":87,"keywords":91,"publishedAt":19},"01M69BC9SWSK7ZX02AKTVT16E9","test-discipline","Update tests when changing APIs — no exceptions","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ffsi-banking-payments-dispute-resolution-accelerator\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Ftest-discipline\u002FSKILL.md",[88],{"categoryNo":89,"categoryName":90,"taxonomyType":56,"taxonomyTypeDesc":57},"01JBSEEDCAT000000000000008","测试与质量",[92,93,94,95],"test discipline","test","sync","update",{"skillNo":97,"name":98,"nameZh":6,"description":99,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":100,"categories":101,"keywords":102,"publishedAt":19},"01M69BC7MR12QTSH97DCQYB81N","squad","Squad's command catalog and interactive menu. Invoke via \u002Fsquad (slash command) or natural language (\"squad commands\", \"what can squad do\", \"show me squad options\"). Presents categorized operations (Install & Upgrade, Team Management, Issues & PRs, Plugins & Skills, Model & Cost, Sessions & State) as an interactive picker. Routes to the right squad CLI command or the Squad coordinator agent.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ffsi-banking-payments-dispute-resolution-accelerator\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fsquad\u002FSKILL.md",[],[98,103,104,105],"squad commands","sessions","cli",{"skillNo":107,"name":108,"nameZh":6,"description":109,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":110,"categories":111,"keywords":112,"publishedAt":19},"01M69BC68975N2E3G78YR9X4MD","squad-version-check","Internals of how @bradygaster\u002Fsquad-cli stamps its version, how `squad upgrade` works (what it preserves vs overwrites), and how to probe the npm registry for the latest version from a coordinator prompt.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ffsi-banking-payments-dispute-resolution-accelerator\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fsquad-version-check\u002FSKILL.md",[],[113,98,105],"squad version check"]