[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6E5TG3MPXT1SVT4FTJZ4JNT":3,"skill-same:01M6E5TG3MPXT1SVT4FTJZ4JNT":64},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":38,"linkOnly":39,"markdownContent":40,"markdownBody":41,"frontMatter":42,"refName":44,"commitSha":45,"skillDirectory":46,"forksCount":13,"repositoryHtmlUrl":47,"repositoryHomepageUrl":6,"ownerHtmlUrl":48,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":49,"install":50,"resources":63},"01M6E5TG3MPXT1SVT4FTJZ4JNT","watch-deploy",null,"Monitor (and, within bounds, remediate) a deploy triggered by a merge — staging automatically post-merge, production only after the owner approves the queued deploy. Polls the deploy run to a terminal state, classifies failures, attempts bounded fix-forward, and escalates with a precise diagnosis.","parametrization\u002F2real-team-framework","parametrization","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F1111097?v=4","01M6E5MC117M94CEXKX3C96PPX","01M6E5MC7YWG5N3VV4792VFK85",0,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fparametrization\u002F2real-team-framework\u002Fblob\u002Fmain\u002Fintake\u002Fnoorinalabs-2026-06\u002Fcandidates\u002Fskills\u002Fwatch-deploy\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000011","运维与部署","SCENARIO","使用场景",[23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"deploy","watch","watch deploy","terminal","merge","monitor","run","state","Production","fix","only","env","id","diagnosis","post","2026-11-10 10:12:30",false,"---\nname: watch-deploy\ndescription: Monitor (and, within bounds, remediate) a deploy triggered by a merge — staging automatically post-merge, production only after the owner approves the queued deploy. Polls the deploy run to a terminal state, classifies failures, attempts bounded fix-forward, and escalates with a precise diagnosis.\nargs: env (stg|prod), optional triggering sha, optional run-id\n---\n\nWatch a deploy that a merge to `main` triggered, confirm it reaches a healthy terminal state, and fix-forward or escalate when it does not. This is the active counterpart to `\u002Fwave-wrapup` Step 11.6's passive \"latest run is green\" check: it follows the *specific* deploy a merge kicked off, rather than inspecting only the most-recent run.\n\nMotivation: deploy#418 — a user-service merge silently broke the staging deploy at the image-pull step and only self-healed because an isnad-graph push happened to follow. Nothing watched the dispatched run or remediated. See also main#623 (this skill's tracking issue) and the P3W14-retro session-start \"red default-branch publish\" check, which is passive and does not follow a fresh deploy.\n\n> Note: all repo paths in bash blocks below are rooted at `$REPO_ROOT` to avoid cwd drift when the skill is invoked from a worktree or child-repo subdirectory (#149).\n\n## When to use\n\n- **Automatically after a merge that triggers a staging deploy** — any merge to `main` in a fan-in repo (`noorinalabs-isnad-graph`, `noorinalabs-user-service`) fires a `repository_dispatch` to `noorinalabs-deploy\u002F.github\u002Fworkflows\u002Fdeploy-stg.yml`. Call `\u002Fwatch-deploy stg \u003Csha>` to follow it.\n- **From `\u002Fwave-wrapup` Step 11.6** — once per wave→main merge in a deploy-triggering repo, so a wave is not declared green until *its* triggered deploys are confirmed healthy (not just the latest run on the board).\n- **For production — only after the owner has approved the queued prod deploy.** Prod deploys wait on a manual approval gate (owner directive 2026-06-09). This skill MUST NOT approve, trigger, or otherwise advance a prod deploy. Once the owner approves, call `\u002Fwatch-deploy prod \u003Csha>` to monitor the approved run.\n\n## Environment contract\n\n| env | Workflow (in `noorinalabs-deploy`) | Trigger | This skill may act before owner? |\n|-----|------------------------------------|---------|----------------------------------|\n| `stg` | `deploy-stg.yml` | auto — `repository_dispatch` from a service repo's `ghcr-publish.yml` after both images publish | **Yes** — staging is auto-deploy; bounded remediation is allowed |\n| `prod` | `deploy-prod.yml` | `workflow_dispatch` from `promote.yml`, **gated on owner approval** | **No** — never approve\u002Ftrigger; monitor only after approval, never auto-remediate prod |\n\n## Instructions\n\n### 1. Resolve the run to watch\n\n```bash\nREPO_ROOT=\"$(git rev-parse --show-toplevel)\"\nDEPLOY_REPO=\"noorinalabs\u002Fnoorinalabs-deploy\"\nENV=\"{stg|prod}\"                     # first arg\nSHA=\"{triggering sha or empty}\"      # optional second arg\nRUN_ID=\"{run-id or empty}\"           # optional third arg\n\ncase \"$ENV\" in\n  stg)  WORKFLOW=\"deploy-stg.yml\";  RUN_NAME=\"Deploy to staging\" ;;\n  prod) WORKFLOW=\"deploy-prod.yml\"; RUN_NAME=\"Deploy to production\" ;;\n  *) echo \"ERROR: env must be stg or prod\"; exit 1 ;;\nesac\n```\n\nFind the run. Note two facts that make naive matching wrong:\n\n- A `$WORKFLOW` run's `headSha` is the **deploy repo's** sha, and its `displayTitle` is the **event-type** (`deploy-noorinalabs-\u003Cservice>`) — neither is the triggering **service** sha. So you cannot filter `gh run list` by the service sha.\n- The service sha lives in the dispatch `client_payload`, which is not exposed by `gh run list`.\n\nCorrelate by **time** instead (deploys are serialized — `deploy-stg.yml` uses `concurrency: deploy-staging, cancel-in-progress: false`): the run a merge triggered is the **earliest `$WORKFLOW` run whose `createdAt` is at\u002Fafter the merge commit's timestamp**.\n\n```bash\nif [ -n \"$RUN_ID\" ]; then\n  RUN=$(gh run view \"$RUN_ID\" --repo \"$DEPLOY_REPO\" --json databaseId,status,conclusion,url,headSha,createdAt)\nelif [ -n \"$SHA\" ]; then\n  # Resolve the service commit's timestamp. We don't know which fan-in repo the\n  # sha belongs to, so probe them (the sha is unique across repos in practice).\n  MERGE_TS=\"\"\n  for r in noorinalabs-isnad-graph noorinalabs-user-service; do\n    ts=$(gh api \"repos\u002Fnoorinalabs\u002F$r\u002Fcommits\u002F$SHA\" --jq '.commit.committer.date' 2>\u002Fdev\u002Fnull || true)\n    [ -n \"$ts\" ] && { MERGE_TS=\"$ts\"; SRC_REPO=\"$r\"; break; }\n  done\n  if [ -z \"$MERGE_TS\" ]; then\n    echo \"Could not resolve $SHA in any fan-in repo — falling back to latest $WORKFLOW run.\"\n    RUN=$(gh run list --repo \"$DEPLOY_REPO\" --workflow \"$WORKFLOW\" --limit 1 \\\n      --json databaseId,status,conclusion,url,headSha,createdAt --jq '.[0] \u002F\u002F empty')\n  else\n    echo \"Merge $SHA in $SRC_REPO at $MERGE_TS — selecting earliest $WORKFLOW run at\u002Fafter that.\"\n    RUN=$(gh run list --repo \"$DEPLOY_REPO\" --workflow \"$WORKFLOW\" --limit 30 \\\n      --json databaseId,status,conclusion,url,headSha,createdAt \\\n      --jq \"[.[] | select(.createdAt >= \\\"$MERGE_TS\\\")] | sort_by(.createdAt) | .[0] \u002F\u002F empty\")\n  fi\nelse\n  # No sha — watch the latest run, and say so (not a specific merge's deploy).\n  echo \"No sha given — watching the latest $WORKFLOW run.\"\n  RUN=$(gh run list --repo \"$DEPLOY_REPO\" --workflow \"$WORKFLOW\" --limit 1 \\\n    --json databaseId,status,conclusion,url,headSha,createdAt --jq '.[0] \u002F\u002F empty')\nfi\n[ -z \"$RUN\" ] && { echo \"No $WORKFLOW run found to watch (env=$ENV, sha=${SHA:-none}).\"; exit 0; }\nRUN_DB=$(echo \"$RUN\" | jq -r '.databaseId'); RUN_URL=$(echo \"$RUN\" | jq -r '.url')\necho \"Watching: $RUN_URL\"\n```\n\nFor **prod**, the `$WORKFLOW` is dispatched by `promote.yml` (a `workflow_dispatch`, not a service-repo push), so the sha-correlation above does not apply — pass the `RUN_ID` of the approved prod run, or omit the sha to watch the latest prod run.\n\n### 2. Production approval guard (prod only — HARD STOP)\n\nFor `prod`, before watching, confirm the run is **past** the owner-approval gate. If the run is `waiting`\u002F`action_required` (pending approval), **STOP and report** — do not poll, do not nudge, do not approve. Surface the approval URL and wait for the owner.\n\n```bash\nif [ \"$ENV\" = \"prod\" ]; then\n  ST=$(echo \"$RUN\" | jq -r '.status')\n  if [ \"$ST\" = \"waiting\" ] || [ \"$ST\" = \"action_required\" ]; then\n    echo \"PROD DEPLOY AWAITING OWNER APPROVAL — $RUN_URL\"\n    echo \"This skill will NOT approve or advance it. Re-invoke \u002Fwatch-deploy prod once you have approved.\"\n    exit 0\n  fi\nfi\n```\n\n### 3. Poll to a terminal state\n\nPoll the run until `status == completed` (bounded wait; deploys are minutes, not hours). Prefer `gh run watch` where available; otherwise poll.\n\n```bash\ngh run watch \"$RUN_DB\" --repo \"$DEPLOY_REPO\" --exit-status >\u002Fdev\u002Fnull 2>&1 || true\nRUN=$(gh run view \"$RUN_DB\" --repo \"$DEPLOY_REPO\" --json status,conclusion,url,databaseId)\nCONCLUSION=$(echo \"$RUN\" | jq -r '.conclusion')\necho \"Terminal conclusion: $CONCLUSION — $RUN_URL\"\n```\n\nIf `CONCLUSION == success`: for stg, confirm the post-deploy verifier is also green (the `Verify Deployment` \u002F health-check run). Report success and stop.\n\n### 4. Classify the failure\n\nIf `CONCLUSION != success`, pull the failed step log and classify:\n\n```bash\nLOG=$(gh run view \"$RUN_DB\" --repo \"$DEPLOY_REPO\" --log-failed 2>\u002Fdev\u002Fnull || true)\n```\n\n| Class | Signal in failed log | Meaning |\n|-------|----------------------|---------|\n| **image-not-found** | `failed to resolve reference`, `: not found`, `manifest unknown`, or a `MISS ` line from the deploy-stg pre-flight | A resolved image tag is absent in GHCR (deploy#418 class) |\n| **health-check** | `health check failed`, `kafka` healthcheck flap | Container came up but a healthcheck did not pass (often the pipeline-only kafka flap — deploy#393 class, non-blocking) |\n| **migrate-gate** | `alembic`, `upgrade head`, advisory-lock contention | Pre-deploy DB migration failed |\n| **infra\u002Fssh** | `ssh`, `connection refused`, `Permission denied`, runner\u002Fnetwork error | Transient or host-level infra problem |\n| **other** | none of the above | Unknown — escalate with the raw failing step |\n\n### 5. Remediate (bounded) or escalate\n\n**Staging** — bounded fix-forward is allowed:\n\n| Class | Action |\n|-------|--------|\n| **image-not-found** | Re-dispatch the last-good stg images: `gh workflow run deploy-stg.yml --repo \"$DEPLOY_REPO\"` (defaults to `stg-latest` for every service). Then re-watch ONCE. If the original was a per-sha deploy whose image never published, also surface the upstream publish run so the real gap is filed. (Post-deploy#418 this class should be caught by the pre-flight with a readable `MISS`; if it still reaches a deploy, that is itself worth a follow-up.) |\n| **health-check** | If it is the known kafka pipeline-only flap (deploy#393 class), note it as non-blocking and do **not** count it as a wave-blocking failure; confirm the app containers (api, user-service) are healthy. Otherwise re-watch ONCE; if still red, escalate. |\n| **migrate-gate** | Do **not** auto-retry migrations. Escalate with the alembic error and the failing revision. |\n| **infra\u002Fssh** | Re-watch \u002F re-dispatch ONCE (likely transient). If it recurs, escalate. |\n| **other** | Escalate with the raw failing step. |\n\nAfter **one** remediation attempt, re-resolve and re-poll (Steps 1–3) exactly once. Do **not** loop indefinitely — a second failure escalates.\n\n**Production** — NO auto-remediation. On any prod failure: report the class + failing step, and recommend the owner-gated path (`rollback.yml` to the prior `prod-\u003Cshort>`, or fix-forward + re-promote via `promote.yml`). Never trigger a prod redeploy or rollback yourself.\n\n### 6. Report\n\n```\n**watch-deploy: {env} — {merge sha or \"latest\"}**\n\n- Run: {url}\n- Terminal: {success | \u003Cclass> failure}\n- Verifier (stg): {green | red | n\u002Fa}\n- Remediation: {none | re-dispatched stg-latest, re-watched → green | escalated}\n- Escalation: {none | \u003Cwhat the owner\u002Foperator must do>}\n```\n\nIf a real defect surfaced (a publish gap, a recurring health failure, an infra problem), file it per `\u002Ffile-bug` and link it. Do not silently swallow a failure that self-healed on retry — record it so the underlying flake is tracked.\n\n## What this skill does NOT do\n\n- It never approves, triggers, or advances a **production** deploy — that gate is the owner's. It only monitors after approval.\n- It does not loop more than one remediation attempt — a second failure is an escalation, not another retry.\n- It does not edit code or workflows — fixes to the deploy mechanics themselves are filed as issues (e.g. deploy#418) and go through the normal PR path.\n- It does not roll back production — it recommends `rollback.yml`; the owner runs it.\n","Watch a deploy that a merge to `main` triggered, confirm it reaches a healthy terminal state, and fix-forward or escalate when it does not. This is the active counterpart to `\u002Fwave-wrapup` Step 11.6's passive \"latest run is green\" check: it follows the *specific* deploy a merge kicked off, rather than inspecting only the most-recent run.\n\nMotivation: deploy#418 — a user-service merge silently broke the staging deploy at the image-pull step and only self-healed because an isnad-graph push happened to follow. Nothing watched the dispatched run or remediated. See also main#623 (this skill's tracking issue) and the P3W14-retro session-start \"red default-branch publish\" check, which is passive and does not follow a fresh deploy.\n\n> Note: all repo paths in bash blocks below are rooted at `$REPO_ROOT` to avoid cwd drift when the skill is invoked from a worktree or child-repo subdirectory (#149).\n\n## When to use\n\n- **Automatically after a merge that triggers a staging deploy** — any merge to `main` in a fan-in repo (`noorinalabs-isnad-graph`, `noorinalabs-user-service`) fires a `repository_dispatch` to `noorinalabs-deploy\u002F.github\u002Fworkflows\u002Fdeploy-stg.yml`. Call `\u002Fwatch-deploy stg \u003Csha>` to follow it.\n- **From `\u002Fwave-wrapup` Step 11.6** — once per wave→main merge in a deploy-triggering repo, so a wave is not declared green until *its* triggered deploys are confirmed healthy (not just the latest run on the board).\n- **For production — only after the owner has approved the queued prod deploy.** Prod deploys wait on a manual approval gate (owner directive 2026-06-09). This skill MUST NOT approve, trigger, or otherwise advance a prod deploy. Once the owner approves, call `\u002Fwatch-deploy prod \u003Csha>` to monitor the approved run.\n\n## Environment contract\n\n| env | Workflow (in `noorinalabs-deploy`) | Trigger | This skill may act before owner? |\n|-----|------------------------------------|---------|----------------------------------|\n| `stg` | `deploy-stg.yml` | auto — `repository_dispatch` from a service repo's `ghcr-publish.yml` after both images publish | **Yes** — staging is auto-deploy; bounded remediation is allowed |\n| `prod` | `deploy-prod.yml` | `workflow_dispatch` from `promote.yml`, **gated on owner approval** | **No** — never approve\u002Ftrigger; monitor only after approval, never auto-remediate prod |\n\n## Instructions\n\n### 1. Resolve the run to watch\n\n```bash\nREPO_ROOT=\"$(git rev-parse --show-toplevel)\"\nDEPLOY_REPO=\"noorinalabs\u002Fnoorinalabs-deploy\"\nENV=\"{stg|prod}\"                     # first arg\nSHA=\"{triggering sha or empty}\"      # optional second arg\nRUN_ID=\"{run-id or empty}\"           # optional third arg\n\ncase \"$ENV\" in\n  stg)  WORKFLOW=\"deploy-stg.yml\";  RUN_NAME=\"Deploy to staging\" ;;\n  prod) WORKFLOW=\"deploy-prod.yml\"; RUN_NAME=\"Deploy to production\" ;;\n  *) echo \"ERROR: env must be stg or prod\"; exit 1 ;;\nesac\n```\n\nFind the run. Note two facts that make naive matching wrong:\n\n- A `$WORKFLOW` run's `headSha` is the **deploy repo's** sha, and its `displayTitle` is the **event-type** (`deploy-noorinalabs-\u003Cservice>`) — neither is the triggering **service** sha. So you cannot filter `gh run list` by the service sha.\n- The service sha lives in the dispatch `client_payload`, which is not exposed by `gh run list`.\n\nCorrelate by **time** instead (deploys are serialized — `deploy-stg.yml` uses `concurrency: deploy-staging, cancel-in-progress: false`): the run a merge triggered is the **earliest `$WORKFLOW` run whose `createdAt` is at\u002Fafter the merge commit's timestamp**.\n\n```bash\nif [ -n \"$RUN_ID\" ]; then\n  RUN=$(gh run view \"$RUN_ID\" --repo \"$DEPLOY_REPO\" --json databaseId,status,conclusion,url,headSha,createdAt)\nelif [ -n \"$SHA\" ]; then\n  # Resolve the service commit's timestamp. We don't know which fan-in repo the\n  # sha belongs to, so probe them (the sha is unique across repos in practice).\n  MERGE_TS=\"\"\n  for r in noorinalabs-isnad-graph noorinalabs-user-service; do\n    ts=$(gh api \"repos\u002Fnoorinalabs\u002F$r\u002Fcommits\u002F$SHA\" --jq '.commit.committer.date' 2>\u002Fdev\u002Fnull || true)\n    [ -n \"$ts\" ] && { MERGE_TS=\"$ts\"; SRC_REPO=\"$r\"; break; }\n  done\n  if [ -z \"$MERGE_TS\" ]; then\n    echo \"Could not resolve $SHA in any fan-in repo — falling back to latest $WORKFLOW run.\"\n    RUN=$(gh run list --repo \"$DEPLOY_REPO\" --workflow \"$WORKFLOW\" --limit 1 \\\n      --json databaseId,status,conclusion,url,headSha,createdAt --jq '.[0] \u002F\u002F empty')\n  else\n    echo \"Merge $SHA in $SRC_REPO at $MERGE_TS — selecting earliest $WORKFLOW run at\u002Fafter that.\"\n    RUN=$(gh run list --repo \"$DEPLOY_REPO\" --workflow \"$WORKFLOW\" --limit 30 \\\n      --json databaseId,status,conclusion,url,headSha,createdAt \\\n      --jq \"[.[] | select(.createdAt >= \\\"$MERGE_TS\\\")] | sort_by(.createdAt) | .[0] \u002F\u002F empty\")\n  fi\nelse\n  # No sha — watch the latest run, and say so (not a specific merge's deploy).\n  echo \"No sha given — watching the latest $WORKFLOW run.\"\n  RUN=$(gh run list --repo \"$DEPLOY_REPO\" --workflow \"$WORKFLOW\" --limit 1 \\\n    --json databaseId,status,conclusion,url,headSha,createdAt --jq '.[0] \u002F\u002F empty')\nfi\n[ -z \"$RUN\" ] && { echo \"No $WORKFLOW run found to watch (env=$ENV, sha=${SHA:-none}).\"; exit 0; }\nRUN_DB=$(echo \"$RUN\" | jq -r '.databaseId'); RUN_URL=$(echo \"$RUN\" | jq -r '.url')\necho \"Watching: $RUN_URL\"\n```\n\nFor **prod**, the `$WORKFLOW` is dispatched by `promote.yml` (a `workflow_dispatch`, not a service-repo push), so the sha-correlation above does not apply — pass the `RUN_ID` of the approved prod run, or omit the sha to watch the latest prod run.\n\n### 2. Production approval guard (prod only — HARD STOP)\n\nFor `prod`, before watching, confirm the run is **past** the owner-approval gate. If the run is `waiting`\u002F`action_required` (pending approval), **STOP and report** — do not poll, do not nudge, do not approve. Surface the approval URL and wait for the owner.\n\n```bash\nif [ \"$ENV\" = \"prod\" ]; then\n  ST=$(echo \"$RUN\" | jq -r '.status')\n  if [ \"$ST\" = \"waiting\" ] || [ \"$ST\" = \"action_required\" ]; then\n    echo \"PROD DEPLOY AWAITING OWNER APPROVAL — $RUN_URL\"\n    echo \"This skill will NOT approve or advance it. Re-invoke \u002Fwatch-deploy prod once you have approved.\"\n    exit 0\n  fi\nfi\n```\n\n### 3. Poll to a terminal state\n\nPoll the run until `status == completed` (bounded wait; deploys are minutes, not hours). Prefer `gh run watch` where available; otherwise poll.\n\n```bash\ngh run watch \"$RUN_DB\" --repo \"$DEPLOY_REPO\" --exit-status >\u002Fdev\u002Fnull 2>&1 || true\nRUN=$(gh run view \"$RUN_DB\" --repo \"$DEPLOY_REPO\" --json status,conclusion,url,databaseId)\nCONCLUSION=$(echo \"$RUN\" | jq -r '.conclusion')\necho \"Terminal conclusion: $CONCLUSION — $RUN_URL\"\n```\n\nIf `CONCLUSION == success`: for stg, confirm the post-deploy verifier is also green (the `Verify Deployment` \u002F health-check run). Report success and stop.\n\n### 4. Classify the failure\n\nIf `CONCLUSION != success`, pull the failed step log and classify:\n\n```bash\nLOG=$(gh run view \"$RUN_DB\" --repo \"$DEPLOY_REPO\" --log-failed 2>\u002Fdev\u002Fnull || true)\n```\n\n| Class | Signal in failed log | Meaning |\n|-------|----------------------|---------|\n| **image-not-found** | `failed to resolve reference`, `: not found`, `manifest unknown`, or a `MISS ` line from the deploy-stg pre-flight | A resolved image tag is absent in GHCR (deploy#418 class) |\n| **health-check** | `health check failed`, `kafka` healthcheck flap | Container came up but a healthcheck did not pass (often the pipeline-only kafka flap — deploy#393 class, non-blocking) |\n| **migrate-gate** | `alembic`, `upgrade head`, advisory-lock contention | Pre-deploy DB migration failed |\n| **infra\u002Fssh** | `ssh`, `connection refused`, `Permission denied`, runner\u002Fnetwork error | Transient or host-level infra problem |\n| **other** | none of the above | Unknown — escalate with the raw failing step |\n\n### 5. Remediate (bounded) or escalate\n\n**Staging** — bounded fix-forward is allowed:\n\n| Class | Action |\n|-------|--------|\n| **image-not-found** | Re-dispatch the last-good stg images: `gh workflow run deploy-stg.yml --repo \"$DEPLOY_REPO\"` (defaults to `stg-latest` for every service). Then re-watch ONCE. If the original was a per-sha deploy whose image never published, also surface the upstream publish run so the real gap is filed. (Post-deploy#418 this class should be caught by the pre-flight with a readable `MISS`; if it still reaches a deploy, that is itself worth a follow-up.) |\n| **health-check** | If it is the known kafka pipeline-only flap (deploy#393 class), note it as non-blocking and do **not** count it as a wave-blocking failure; confirm the app containers (api, user-service) are healthy. Otherwise re-watch ONCE; if still red, escalate. |\n| **migrate-gate** | Do **not** auto-retry migrations. Escalate with the alembic error and the failing revision. |\n| **infra\u002Fssh** | Re-watch \u002F re-dispatch ONCE (likely transient). If it recurs, escalate. |\n| **other** | Escalate with the raw failing step. |\n\nAfter **one** remediation attempt, re-resolve and re-poll (Steps 1–3) exactly once. Do **not** loop indefinitely — a second failure escalates.\n\n**Production** — NO auto-remediation. On any prod failure: report the class + failing step, and recommend the owner-gated path (`rollback.yml` to the prior `prod-\u003Cshort>`, or fix-forward + re-promote via `promote.yml`). Never trigger a prod redeploy or rollback yourself.\n\n### 6. Report\n\n```\n**watch-deploy: {env} — {merge sha or \"latest\"}**\n\n- Run: {url}\n- Terminal: {success | \u003Cclass> failure}\n- Verifier (stg): {green | red | n\u002Fa}\n- Remediation: {none | re-dispatched stg-latest, re-watched → green | escalated}\n- Escalation: {none | \u003Cwhat the owner\u002Foperator must do>}\n```\n\nIf a real defect surfaced (a publish gap, a recurring health failure, an infra problem), file it per `\u002Ffile-bug` and link it. Do not silently swallow a failure that self-healed on retry — record it so the underlying flake is tracked.\n\n## What this skill does NOT do\n\n- It never approves, triggers, or advances a **production** deploy — that gate is the owner's. It only monitors after approval.\n- It does not loop more than one remediation attempt — a second failure is an escalation, not another retry.\n- It does not edit code or workflows — fixes to the deploy mechanics themselves are filed as issues (e.g. deploy#418) and go through the normal PR path.\n- It does not roll back production — it recommends `rollback.yml`; the owner runs it.\n",{"args":43,"name":5,"description":7},"env (stg|prod), optional triggering sha, optional run-id","main","53ec1032b93d41533a48603e8fae7a92f56cc66c","intake\u002Fnoorinalabs-2026-06\u002Fcandidates\u002Fskills\u002Fwatch-deploy","https:\u002F\u002Fgithub.com\u002Fparametrization\u002F2real-team-framework","https:\u002F\u002Fgithub.com\u002Fparametrization","2026-09-21 01:06:30",{"source":8,"skillName":5,"commands":51,"prompt":61,"zipAvailable":62},[52,55,58],{"tool":53,"command":54},"npx","npx skills add parametrization\u002F2real-team-framework --skill watch-deploy",{"tool":56,"command":57},"bunx","bunx skills add parametrization\u002F2real-team-framework --skill watch-deploy",{"tool":59,"command":60},"pnpm","pnpm dlx skills add parametrization\u002F2real-team-framework --skill watch-deploy","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fparametrization\u002F2real-team-framework\u002Fblob\u002Fmain\u002Fintake\u002Fnoorinalabs-2026-06\u002Fcandidates\u002Fskills\u002Fwatch-deploy\u002FSKILL.md",true,[],[65,86,103,119,134,147],{"skillNo":66,"name":67,"nameZh":6,"description":68,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":69,"categories":70,"keywords":71,"publishedAt":38},"01M6E5VG1R7FF1025BZAV6BDTS","wave-wrapup","Finalize a wave — PR review, merge sequencing, issue cleanup, worktree cleanup, and handoff to retro","https:\u002F\u002Fgithub.com\u002Fparametrization\u002F2real-team-framework\u002Fblob\u002Fmain\u002Fintake\u002Fnoorinalabs-2026-06\u002Fcandidates\u002Fskills\u002Fwave-wrapup\u002FSKILL.md",[],[72,73,74,75,27,76,77,78,79,80,81,82,83,84,85],"wave wrapup","wrapup","review","handoff","pr review","pr","cleanup","worktree","retro","team","issue","worktree cleanup","name","sequencing",{"skillNo":87,"name":88,"nameZh":6,"description":89,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":90,"categories":91,"keywords":92,"publishedAt":38},"01M6E5V9GDF75GV4YD2WPSH912","wave-start","Initialize a new wave — worktree cleanup, branch creation, label setup, status file update","https:\u002F\u002Fgithub.com\u002Fparametrization\u002F2real-team-framework\u002Fblob\u002Fmain\u002Fintake\u002Fnoorinalabs-2026-06\u002Fcandidates\u002Fskills\u002Fwave-start\u002FSKILL.md",[],[93,94,95,78,96,79,97,81,98,99,100,83,101,84,102],"wave start","start","setup","update","file","branch","file update","initialize","creation","status",{"skillNo":104,"name":105,"nameZh":6,"description":106,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":107,"categories":108,"keywords":109,"publishedAt":38},"01M6E5V4VXEJ39V87VHN1A3AW9","wave-scope","Reconcile declared-vs-labeled wave scope between \u002Fwave-retro and \u002Fwave-kickoff — collect dispositions, fold in retro carry-forwards and memory must-includes, and refresh the meta-issue","https:\u002F\u002Fgithub.com\u002Fparametrization\u002F2real-team-framework\u002Fblob\u002Fmain\u002Fintake\u002Fnoorinalabs-2026-06\u002Fcandidates\u002Fskills\u002Fwave-scope\u002FSKILL.md",[],[110,111,112,113,114,80,115,116,117,82,118],"wave scope","scope","memory","collect","Meta","refresh","reconcile","Fold","kickoff",{"skillNo":120,"name":121,"nameZh":6,"description":122,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":123,"categories":124,"keywords":125,"publishedAt":38},"01M6E5TZNPGG5EXZEN1FKJJT6K","wave-retro","Automated wave retrospective — PR analysis, assessments, trust matrix updates, feedback log, charter change proposals","https:\u002F\u002Fgithub.com\u002Fparametrization\u002F2real-team-framework\u002Fblob\u002Fmain\u002Fintake\u002Fnoorinalabs-2026-06\u002Fcandidates\u002Fskills\u002Fwave-retro\u002FSKILL.md",[],[126,80,127,128,77,129,130,81,131,132,133,84],"wave retro","log","change","retrospective","analysis","feedback","proposals","trust",{"skillNo":135,"name":136,"nameZh":6,"description":137,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":138,"categories":139,"keywords":140,"publishedAt":38},"01M6E5TR8XB3Z10A9SD4ZYNV45","wave-kickoff","Automated wave planning — branch creation, label management, issue labeling, kickoff comments, and execution plan","https:\u002F\u002Fgithub.com\u002Fparametrization\u002F2real-team-framework\u002Fblob\u002Fmain\u002Fintake\u002Fnoorinalabs-2026-06\u002Fcandidates\u002Fskills\u002Fwave-kickoff\u002FSKILL.md",[],[141,118,142,82,101,84,143,81,144,98,145,146],"wave kickoff","management","plan","execution","planning","execution plan",{"skillNo":148,"name":149,"nameZh":6,"description":150,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":151,"categories":152,"keywords":153,"publishedAt":38},"01M6E5TMZAFB6MTVYT750FZDVY","wave-audit","Audit open issues against merged PRs — find and close orphaned issues with proper comments","https:\u002F\u002Fgithub.com\u002Fparametrization\u002F2real-team-framework\u002Fblob\u002Fmain\u002Fintake\u002Fnoorinalabs-2026-06\u002Fcandidates\u002Fskills\u002Fwave-audit\u002FSKILL.md",[],[154,155,156,157,158,159],"audit","wave audit","close","find","open","issues"]