[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CZ5D3G2P0JDVYWHFZV7MN4":3,"skill-same:01M6CZ5D3G2P0JDVYWHFZV7MN4":68},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":35,"linkOnly":36,"markdownContent":37,"markdownBody":38,"frontMatter":39,"refName":45,"commitSha":46,"skillDirectory":47,"forksCount":48,"repositoryHtmlUrl":49,"repositoryHomepageUrl":50,"ownerHtmlUrl":51,"ownerBlogUrl":50,"ownerDescription":52,"updatedAt":53,"install":54,"resources":67},"01M6CZ5D3G2P0JDVYWHFZV7MN4","auto-merge",null,"Automatically merge open PRs that have passing CI, no blocking reviews, and no conflicts","aeonfun\u002Faeon","aeonfun","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F287715280?v=4","01M6CZ23C6MXGMF4N0V1DZ1247","aeon","01M6CZ23GMZP1BR424J8Y8K3DG",793,"MIT","https:\u002F\u002Fgithub.com\u002Faeonfun\u002Faeon\u002Fblob\u002Fmain\u002Fskills\u002Fauto-merge\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34],"merge","auto","auto merge","core","dev","ci","Meta","open","conflicts","reviews","prs","2026-11-10 10:12:30",false,"---\nname: auto-merge\ndescription: Automatically merge open PRs that have passing CI, no blocking reviews, and no conflicts\nmetadata:\n  title: Auto Merge\n  category: core\n  tags:\n    - dev\n    - meta\n  var: \"\"\n---\n\u003C!-- autoresearch: variation C — safety-hardened (author allowlist, size cap, UNKNOWN retry, fork block, dry-run mode) so an autonomous agent with merge credentials cannot accidentally ship a hostile or oversized PR -->\n\n> **${var}** — Repo (owner\u002Frepo) to target. If empty, uses every repo in memory\u002Fwatched-repos.md.\n> Env: `AUTO_MERGE_DRY_RUN=1` logs intent without merging. `MAX_AUTO_MERGE=N` caps merges per run (default 3).\n\nMerge open PRs that are fully green **and** pass an explicit safety policy. The policy exists because this skill runs autonomously with write access — a bug in the gate is a bug that ships to main.\n\nRead memory\u002FMEMORY.md and memory\u002Fwatched-repos.md for repos to target.\nRead the last 2 days of memory\u002Flogs\u002F to avoid re-logging PRs already merged.\n\n## Safety policy\n\nA PR merges only when every one of the following holds:\n\n- **Author allowlist**: `author.login` is one of `dependabot[bot]`, `renovate[bot]`, `github-actions[bot]`, OR appears under a `## Trusted Authors` section in memory\u002Fwatched-repos.md. No allowlist → only the three bot logins are eligible.\n- **Size cap**: `additions + deletions ≤ 500`. Override by applying the label `auto-merge-large` on the PR.\n- **Base branch**: `baseRefName` is `main` or `master`. Refuse any other target.\n- **Not a fork**: `isCrossRepository == false` (fork CI can be tampered with).\n- **Not draft**: `isDraft == false`.\n- **Not already queued**: `autoMergeRequest == null` (avoid fighting GitHub's native auto-merge if a human enabled it) and the PR is not already in a merge queue (`mergeQueueEntry == null`, checked in step 2b).\n- **No opt-out label**: none of {`do-not-merge`, `wip`, `hold`, `needs-review`, `blocked`} present.\n- **Mergeable state**: `mergeStateStatus == \"CLEAN\"` (this is stricter than `mergeable == \"MERGEABLE\"` — CLEAN additionally requires branch-protection gates to be satisfied).\n- **Reviews**: `reviewDecision != \"CHANGES_REQUESTED\"`.\n- **Checks**: every entry in `statusCheckRollup` has `conclusion` in `{SUCCESS, NEUTRAL, SKIPPED}`. Any `FAILURE`, `TIMED_OUT`, `CANCELLED`, `PENDING`, or `null` conclusion disqualifies the PR.\n- **Retry cap**: this PR has been attempted fewer than 3 times. A PR that has hit `MERGE_FAIL` three times across runs is paused — repeated failure on a CLEAN-looking PR usually means something subtle (a required check that didn't surface, branch-protection drift, token scope drift). Surface it and stop looping.\n\n## Steps\n\n0. **Bootstrap state** — per-PR retry counter lives in `memory\u002Ftopics\u002Fauto-merge-state.json`:\n   ```bash\n   mkdir -p memory\u002Ftopics\n   ```\n   Then check for `memory\u002Ftopics\u002Fauto-merge-state.json` with Glob; if it is missing, create it with the Write tool holding `{\"prs\":{},\"last_run\":null}` (a `[ -f ] ||` test and an `echo ... > file` redirect are both refused in this run).\n   Schema:\n   ```json\n   {\n     \"last_run\": \"2026-05-23T08:00:00Z\",\n     \"prs\": {\n       \"owner\u002Frepo#123\": {\n         \"first_seen\": \"2026-05-21T10:00:00Z\",\n         \"last_attempt\": \"2026-05-23T08:00:00Z\",\n         \"attempts\": 2,\n         \"last_outcome\": \"merge_failed\",\n         \"last_error\": \"Pull Request is in unstable state\"\n       }\n     }\n   }\n   ```\n   PR keys are `\u003Cowner>\u002F\u003Crepo>#\u003Cnumber>` so state survives multi-repo runs. Cap to 50 most-recent entries (LRU by `last_attempt`). Validate with `jq empty` after write; restore from `.bak` on failure.\n\n1. **List open PRs** for each watched repo with the full field set:\n   ```bash\n   gh pr list -R owner\u002Frepo --state open --json number,title,author,isDraft,mergeable,mergeStateStatus,reviewDecision,statusCheckRollup,autoMergeRequest,isCrossRepository,labels,additions,deletions,baseRefName\n   ```\n\n2. **Handle UNKNOWN state** — GitHub computes `mergeStateStatus` lazily. If a PR returns `UNKNOWN`, sleep 3 seconds and re-query once:\n   ```bash\n   sleep 3 && gh pr view NUMBER -R owner\u002Frepo --json mergeStateStatus,mergeable,statusCheckRollup\n   ```\n   If still UNKNOWN after the retry, skip the PR with reason `UNKNOWN-persistent` and let the next run retry.\n\n   2b. **Skip PRs already in a merge queue.** A PR this skill queued on an earlier run is still open, so it would be merged again, bump `attempts`, and hit the retry cap while it waits. `gh pr list` does not expose the queue, so ask GraphQL per PR:\n   ```bash\n   gh api graphql -f query='query($o:String!,$r:String!,$n:Int!){repository(owner:$o,name:$r){pullRequest(number:$n){mergeQueueEntry{state}}}}' \\\n     -f o=owner -f r=repo -F n=NUMBER --jq '.data.repository.pullRequest.mergeQueueEntry.state \u002F\u002F \"\"'\n   ```\n   A non-empty state (e.g. `QUEUED`, `AWAITING_CHECKS`, `MERGEABLE`) skips the PR as `SKIP:already-queued:\u003Cstate>`. A failed lookup skips it as `SKIP:merge-queue-unknown`. Neither counts as an attempt: do not touch its `attempts`, and leave its state entry as is.\n\n3. **Apply the safety policy** to each PR. Record a verdict for every PR: either `MERGE` or `SKIP:\u003Cspecific-reason>`. Reasons must name the failing gate — e.g. `SKIP:author-not-allowlisted:contributor123`, `SKIP:size-cap:823-lines`, `SKIP:mergeStateStatus=BEHIND`, `SKIP:label:do-not-merge`, `SKIP:check-failed:lint`, `SKIP:retry-cap:3-attempts`. Vague reasons like `SKIP:not-ready` are not acceptable.\n\n4. **Merge qualifying PRs**, up to MAX_AUTO_MERGE (default 3):\n   - If `AUTO_MERGE_DRY_RUN=1`, log `DRY_RUN:would-merge #N` and continue — **do NOT** invoke merge.\n   - Otherwise:\n     ```bash\n     gh pr merge NUMBER -R owner\u002Frepo --squash --delete-branch\n     ```\n     A zero exit is not proof of a merge: on a repo with a merge queue, `gh pr merge` exits 0 after only adding the PR to the queue. Confirm with GitHub before counting or reporting it:\n     ```bash\n     gh pr view NUMBER -R owner\u002Frepo --json state,mergeCommit --jq '[.state, (.mergeCommit.oid \u002F\u002F \"\")] | @tsv'\n     ```\n     Only `MERGED` with a commit SHA is a merge: report that SHA, never one inferred from the command output. `OPEN` means it was queued: log `QUEUED #N`, record `last_outcome: queued`, and report it as queued, not merged. Any other result, or a failed lookup, is logged as `MERGE_UNCONFIRMED #N` and reported as such. Both count toward `MAX_AUTO_MERGE`: a merge was attempted and GitHub accepted the command, so the cap still bounds how much this run can send to `main`.\n     Increment `state.prs[\"\u003Cowner>\u002F\u003Crepo>#\u003CN>\"].attempts` on every attempt regardless of outcome. Set `first_seen` if absent. Reset to 0 (delete the entry) for PRs that no longer appear in the open list (already merged or closed since the last run).\n     If the merge fails (non-zero exit), capture stderr and log `MERGE_FAIL #N: \u003Cstderr>`. Record `last_outcome: merge_failed` and `last_error: \u003Cstderr ≤200 chars>` on the state entry. A failed merge does NOT count toward the per-run `MAX_AUTO_MERGE` cap — continue to the next qualifying PR. A PR whose `attempts` has reached 3 is filtered out in step 3 with `SKIP:retry-cap:3-attempts`; surface it in step 5b instead of retrying.\n\n5. **Send a notification** only when at least one real (non-dry-run) merge succeeded **or** at least one PR has hit the retry cap (5b below). No merges and no cap hits → no notification, just a log entry.\n\n   5a. **At least one merge succeeded:**\n   ```\n   *Auto Merge - ${today}*\n   Merged N PR(s) on owner\u002Frepo:\n   - #123: PR title (+45\u002F-12, by @author) - squash merged abc1234   (the mergeCommit SHA GitHub returned)\n   Queue cleared. Self-improve cycle unblocked.\n   ```\n\n   5b. **Retry cap reached on ≥1 PR** (`AUTO_MERGE_RETRY_CAP`) — include in the same message if both fire, otherwise stand-alone:\n   ```\n   *Auto Merge - retry cap*\n   Hit retry cap (3 attempts) on:\n   - owner\u002Frepo#40 - last error: \"Pull Request is in unstable state\"\n   Stopping auto-merge attempts on this PR. Investigate manually.\n   ```\n   Dedup: suppress re-notify if the *exact same* set of cap-hit PR keys already notified within the last 24h (grep `memory\u002Flogs\u002F` for prior `AUTO_MERGE_RETRY_CAP` entries).\n\n6. **Persist state** — write the updated `memory\u002Ftopics\u002Fauto-merge-state.json`. Update `last_run` to current timestamp. Validate with `jq empty`; on failure restore from a `.bak` written before this run.\n\n7. **Log to memory\u002Flogs\u002F${today}.md** under an `### auto-merge` heading:\n   - `Mode`: live | dry-run\n   - `Repo(s)`: list\n   - `Merged`: `#N title @author +A-D SHA` per line, confirmed `MERGED` by GitHub\n   - `Queued`: `#N` per line (merge queue accepted it; not merged yet)\n   - `Skipped`: `#N SKIP:\u003Creason>` per line\n   - `Retry-capped`: `owner\u002Frepo#N — \u003Clast_error>` per line (empty if none)\n   - `Totals`: `merged=X queued=Q qualified=Y considered=Z retry_capped=R`\n   - If zero qualified, include a verdict breakdown: `AUTO_MERGE_SKIP: 0\u002FZ qualifying (behind=B blocked=L failing=F draft=D already-queued=U author-blocked=A size-blocked=S retry-capped=R)`\n\n## Network note\n\n`gh` authenticates via the workflow's GITHUB_TOKEN — no curl needed. If `gh pr merge` fails with `Resource not accessible by integration`, the workflow token lacks merge permission on that repo; log once and notify at most once per 7 days (check memory\u002Flogs\u002F for prior notification) to avoid alert spam.\n\n## Constraints\n\n- Never merge a PR whose author is not allowlisted, even if every other gate is green.\n- Never bypass the size cap without the explicit `auto-merge-large` label (set by a human, not a bot).\n- Never auto-retry a `MERGE_FAIL` within the same run — if the first merge attempt fails, log and move on.\n- After 3 failed attempts across runs, stop retrying that PR. Surface it once via the retry-cap notification and let the operator investigate.\n- Do not modify PR state other than merging (no comments, no label edits, no branch updates).\n\n## Running this as an agent-shipping loop\n\nTo close the loop on PRs the agent itself opens (from `feature`, `external-feature`, `self-improve`, etc.), add the agent's GitHub identity under a `## Trusted Authors` section in `memory\u002Fwatched-repos.md`:\n\n```markdown\n## Trusted Authors\n- aeon-bot\n- claude-code[bot]\n```\n\nOnce allowlisted, agent PRs flow through the same safety policy as bot PRs and get auto-merged on green CI. The retry cap protects against runaway behavior on a stuck PR.\n","\u003C!-- autoresearch: variation C — safety-hardened (author allowlist, size cap, UNKNOWN retry, fork block, dry-run mode) so an autonomous agent with merge credentials cannot accidentally ship a hostile or oversized PR -->\n\n> **${var}** — Repo (owner\u002Frepo) to target. If empty, uses every repo in memory\u002Fwatched-repos.md.\n> Env: `AUTO_MERGE_DRY_RUN=1` logs intent without merging. `MAX_AUTO_MERGE=N` caps merges per run (default 3).\n\nMerge open PRs that are fully green **and** pass an explicit safety policy. The policy exists because this skill runs autonomously with write access — a bug in the gate is a bug that ships to main.\n\nRead memory\u002FMEMORY.md and memory\u002Fwatched-repos.md for repos to target.\nRead the last 2 days of memory\u002Flogs\u002F to avoid re-logging PRs already merged.\n\n## Safety policy\n\nA PR merges only when every one of the following holds:\n\n- **Author allowlist**: `author.login` is one of `dependabot[bot]`, `renovate[bot]`, `github-actions[bot]`, OR appears under a `## Trusted Authors` section in memory\u002Fwatched-repos.md. No allowlist → only the three bot logins are eligible.\n- **Size cap**: `additions + deletions ≤ 500`. Override by applying the label `auto-merge-large` on the PR.\n- **Base branch**: `baseRefName` is `main` or `master`. Refuse any other target.\n- **Not a fork**: `isCrossRepository == false` (fork CI can be tampered with).\n- **Not draft**: `isDraft == false`.\n- **Not already queued**: `autoMergeRequest == null` (avoid fighting GitHub's native auto-merge if a human enabled it) and the PR is not already in a merge queue (`mergeQueueEntry == null`, checked in step 2b).\n- **No opt-out label**: none of {`do-not-merge`, `wip`, `hold`, `needs-review`, `blocked`} present.\n- **Mergeable state**: `mergeStateStatus == \"CLEAN\"` (this is stricter than `mergeable == \"MERGEABLE\"` — CLEAN additionally requires branch-protection gates to be satisfied).\n- **Reviews**: `reviewDecision != \"CHANGES_REQUESTED\"`.\n- **Checks**: every entry in `statusCheckRollup` has `conclusion` in `{SUCCESS, NEUTRAL, SKIPPED}`. Any `FAILURE`, `TIMED_OUT`, `CANCELLED`, `PENDING`, or `null` conclusion disqualifies the PR.\n- **Retry cap**: this PR has been attempted fewer than 3 times. A PR that has hit `MERGE_FAIL` three times across runs is paused — repeated failure on a CLEAN-looking PR usually means something subtle (a required check that didn't surface, branch-protection drift, token scope drift). Surface it and stop looping.\n\n## Steps\n\n0. **Bootstrap state** — per-PR retry counter lives in `memory\u002Ftopics\u002Fauto-merge-state.json`:\n   ```bash\n   mkdir -p memory\u002Ftopics\n   ```\n   Then check for `memory\u002Ftopics\u002Fauto-merge-state.json` with Glob; if it is missing, create it with the Write tool holding `{\"prs\":{},\"last_run\":null}` (a `[ -f ] ||` test and an `echo ... > file` redirect are both refused in this run).\n   Schema:\n   ```json\n   {\n     \"last_run\": \"2026-05-23T08:00:00Z\",\n     \"prs\": {\n       \"owner\u002Frepo#123\": {\n         \"first_seen\": \"2026-05-21T10:00:00Z\",\n         \"last_attempt\": \"2026-05-23T08:00:00Z\",\n         \"attempts\": 2,\n         \"last_outcome\": \"merge_failed\",\n         \"last_error\": \"Pull Request is in unstable state\"\n       }\n     }\n   }\n   ```\n   PR keys are `\u003Cowner>\u002F\u003Crepo>#\u003Cnumber>` so state survives multi-repo runs. Cap to 50 most-recent entries (LRU by `last_attempt`). Validate with `jq empty` after write; restore from `.bak` on failure.\n\n1. **List open PRs** for each watched repo with the full field set:\n   ```bash\n   gh pr list -R owner\u002Frepo --state open --json number,title,author,isDraft,mergeable,mergeStateStatus,reviewDecision,statusCheckRollup,autoMergeRequest,isCrossRepository,labels,additions,deletions,baseRefName\n   ```\n\n2. **Handle UNKNOWN state** — GitHub computes `mergeStateStatus` lazily. If a PR returns `UNKNOWN`, sleep 3 seconds and re-query once:\n   ```bash\n   sleep 3 && gh pr view NUMBER -R owner\u002Frepo --json mergeStateStatus,mergeable,statusCheckRollup\n   ```\n   If still UNKNOWN after the retry, skip the PR with reason `UNKNOWN-persistent` and let the next run retry.\n\n   2b. **Skip PRs already in a merge queue.** A PR this skill queued on an earlier run is still open, so it would be merged again, bump `attempts`, and hit the retry cap while it waits. `gh pr list` does not expose the queue, so ask GraphQL per PR:\n   ```bash\n   gh api graphql -f query='query($o:String!,$r:String!,$n:Int!){repository(owner:$o,name:$r){pullRequest(number:$n){mergeQueueEntry{state}}}}' \\\n     -f o=owner -f r=repo -F n=NUMBER --jq '.data.repository.pullRequest.mergeQueueEntry.state \u002F\u002F \"\"'\n   ```\n   A non-empty state (e.g. `QUEUED`, `AWAITING_CHECKS`, `MERGEABLE`) skips the PR as `SKIP:already-queued:\u003Cstate>`. A failed lookup skips it as `SKIP:merge-queue-unknown`. Neither counts as an attempt: do not touch its `attempts`, and leave its state entry as is.\n\n3. **Apply the safety policy** to each PR. Record a verdict for every PR: either `MERGE` or `SKIP:\u003Cspecific-reason>`. Reasons must name the failing gate — e.g. `SKIP:author-not-allowlisted:contributor123`, `SKIP:size-cap:823-lines`, `SKIP:mergeStateStatus=BEHIND`, `SKIP:label:do-not-merge`, `SKIP:check-failed:lint`, `SKIP:retry-cap:3-attempts`. Vague reasons like `SKIP:not-ready` are not acceptable.\n\n4. **Merge qualifying PRs**, up to MAX_AUTO_MERGE (default 3):\n   - If `AUTO_MERGE_DRY_RUN=1`, log `DRY_RUN:would-merge #N` and continue — **do NOT** invoke merge.\n   - Otherwise:\n     ```bash\n     gh pr merge NUMBER -R owner\u002Frepo --squash --delete-branch\n     ```\n     A zero exit is not proof of a merge: on a repo with a merge queue, `gh pr merge` exits 0 after only adding the PR to the queue. Confirm with GitHub before counting or reporting it:\n     ```bash\n     gh pr view NUMBER -R owner\u002Frepo --json state,mergeCommit --jq '[.state, (.mergeCommit.oid \u002F\u002F \"\")] | @tsv'\n     ```\n     Only `MERGED` with a commit SHA is a merge: report that SHA, never one inferred from the command output. `OPEN` means it was queued: log `QUEUED #N`, record `last_outcome: queued`, and report it as queued, not merged. Any other result, or a failed lookup, is logged as `MERGE_UNCONFIRMED #N` and reported as such. Both count toward `MAX_AUTO_MERGE`: a merge was attempted and GitHub accepted the command, so the cap still bounds how much this run can send to `main`.\n     Increment `state.prs[\"\u003Cowner>\u002F\u003Crepo>#\u003CN>\"].attempts` on every attempt regardless of outcome. Set `first_seen` if absent. Reset to 0 (delete the entry) for PRs that no longer appear in the open list (already merged or closed since the last run).\n     If the merge fails (non-zero exit), capture stderr and log `MERGE_FAIL #N: \u003Cstderr>`. Record `last_outcome: merge_failed` and `last_error: \u003Cstderr ≤200 chars>` on the state entry. A failed merge does NOT count toward the per-run `MAX_AUTO_MERGE` cap — continue to the next qualifying PR. A PR whose `attempts` has reached 3 is filtered out in step 3 with `SKIP:retry-cap:3-attempts`; surface it in step 5b instead of retrying.\n\n5. **Send a notification** only when at least one real (non-dry-run) merge succeeded **or** at least one PR has hit the retry cap (5b below). No merges and no cap hits → no notification, just a log entry.\n\n   5a. **At least one merge succeeded:**\n   ```\n   *Auto Merge - ${today}*\n   Merged N PR(s) on owner\u002Frepo:\n   - #123: PR title (+45\u002F-12, by @author) - squash merged abc1234   (the mergeCommit SHA GitHub returned)\n   Queue cleared. Self-improve cycle unblocked.\n   ```\n\n   5b. **Retry cap reached on ≥1 PR** (`AUTO_MERGE_RETRY_CAP`) — include in the same message if both fire, otherwise stand-alone:\n   ```\n   *Auto Merge - retry cap*\n   Hit retry cap (3 attempts) on:\n   - owner\u002Frepo#40 - last error: \"Pull Request is in unstable state\"\n   Stopping auto-merge attempts on this PR. Investigate manually.\n   ```\n   Dedup: suppress re-notify if the *exact same* set of cap-hit PR keys already notified within the last 24h (grep `memory\u002Flogs\u002F` for prior `AUTO_MERGE_RETRY_CAP` entries).\n\n6. **Persist state** — write the updated `memory\u002Ftopics\u002Fauto-merge-state.json`. Update `last_run` to current timestamp. Validate with `jq empty`; on failure restore from a `.bak` written before this run.\n\n7. **Log to memory\u002Flogs\u002F${today}.md** under an `### auto-merge` heading:\n   - `Mode`: live | dry-run\n   - `Repo(s)`: list\n   - `Merged`: `#N title @author +A-D SHA` per line, confirmed `MERGED` by GitHub\n   - `Queued`: `#N` per line (merge queue accepted it; not merged yet)\n   - `Skipped`: `#N SKIP:\u003Creason>` per line\n   - `Retry-capped`: `owner\u002Frepo#N — \u003Clast_error>` per line (empty if none)\n   - `Totals`: `merged=X queued=Q qualified=Y considered=Z retry_capped=R`\n   - If zero qualified, include a verdict breakdown: `AUTO_MERGE_SKIP: 0\u002FZ qualifying (behind=B blocked=L failing=F draft=D already-queued=U author-blocked=A size-blocked=S retry-capped=R)`\n\n## Network note\n\n`gh` authenticates via the workflow's GITHUB_TOKEN — no curl needed. If `gh pr merge` fails with `Resource not accessible by integration`, the workflow token lacks merge permission on that repo; log once and notify at most once per 7 days (check memory\u002Flogs\u002F for prior notification) to avoid alert spam.\n\n## Constraints\n\n- Never merge a PR whose author is not allowlisted, even if every other gate is green.\n- Never bypass the size cap without the explicit `auto-merge-large` label (set by a human, not a bot).\n- Never auto-retry a `MERGE_FAIL` within the same run — if the first merge attempt fails, log and move on.\n- After 3 failed attempts across runs, stop retrying that PR. Surface it once via the retry-cap notification and let the operator investigate.\n- Do not modify PR state other than merging (no comments, no label edits, no branch updates).\n\n## Running this as an agent-shipping loop\n\nTo close the loop on PRs the agent itself opens (from `feature`, `external-feature`, `self-improve`, etc.), add the agent's GitHub identity under a `## Trusted Authors` section in `memory\u002Fwatched-repos.md`:\n\n```markdown\n## Trusted Authors\n- aeon-bot\n- claude-code[bot]\n```\n\nOnce allowlisted, agent PRs flow through the same safety policy as bot PRs and get auto-merged on green CI. The retry cap protects against runaway behavior on a stuck PR.\n",{"name":5,"metadata":40,"description":7},{"var":41,"tags":42,"title":44,"category":27},"",[28,43],"meta","Auto Merge","main","504a21d53c0c9b485f6e90c43db83000825e1d3c","skills\u002Fauto-merge",308,"https:\u002F\u002Fgithub.com\u002Faeonfun\u002Faeon","https:\u002F\u002Fwww.aeon.fun\u002F","https:\u002F\u002Fgithub.com\u002Faeonfun","The most autonomous agent framework.","2026-11-15 03:40:18",{"source":8,"skillName":5,"commands":55,"prompt":65,"zipAvailable":66},[56,59,62],{"tool":57,"command":58},"npx","npx skills add aeonfun\u002Faeon --skill auto-merge",{"tool":60,"command":61},"bunx","bunx skills add aeonfun\u002Faeon --skill auto-merge",{"tool":63,"command":64},"pnpm","pnpm dlx skills add aeonfun\u002Faeon --skill auto-merge","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Faeonfun\u002Faeon\u002Fblob\u002Fmain\u002Fskills\u002Fauto-merge\u002FSKILL.md",true,[],[69,95,122,146,171,193],{"skillNo":70,"name":71,"nameZh":6,"description":72,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":73,"categories":74,"keywords":75,"publishedAt":35},"01M70R8RA3SFXK2D1XP7XTJGAQ","sns-digest","Digest Internet Computer governance - new and open NNS proposals and SNS DAO proposals - so the operator sees what is up for a vote without watching the dashboard. Fully public, no secret; fires only when there is something new to report.","https:\u002F\u002Fgithub.com\u002Faeonfun\u002Faeon\u002Fblob\u002Fmain\u002Fskills\u002Fsns-digest\u002FSKILL.md",[],[76,77,78,79,80,81,31,82,83,84,85,86,87,88,89,90,91,92,93,94],"sns","digest","sns digest","proposals","for","up","icp","only","new","internet","when","api","notifications","dao","crypto","dashboard","governance","report","write",{"skillNo":96,"name":97,"nameZh":6,"description":98,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":104,"publishedAt":35},"01M70R5BMYW94KZ3XQM3MSWPC0","icp-deploy-check","Pre-deploy safety checklist for an Internet Computer Rust canister - read a local\u002Fcloned canister repo and verify the deploy-time footguns from the canister-security checklist (controller backup, anonymous-principal rejection, no mainnet fetch_root_key, upgrade safety) before it ships. Static, read-only, no network and no mainnet action.","https:\u002F\u002Fgithub.com\u002Faeonfun\u002Faeon\u002Fblob\u002Fmain\u002Fskills\u002Ficp-deploy-check\u002FSKILL.md",[101],{"categoryNo":102,"categoryName":103,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000011","运维与部署",[82,105,106,107,108,109,110,111,112,113,114,83,85,115,116,117,118,119,120,121],"icp deploy check","deploy check","check","deploy","fetch","anonymous","static","time","rust","local","upgrade","controller","read","Safety","network","action","security",{"skillNo":123,"name":124,"nameZh":6,"description":125,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":126,"categories":127,"keywords":136,"publishedAt":35},"01M70R312QZ9T236SZHGX8A8CM","canister-status","Monitor Internet Computer canisters for control-plane changes - a new controller, a module-hash change (upgrade), or a canister going missing from the public index - and alert the moment one happens. Uses only the public IC dashboard API.","https:\u002F\u002Fgithub.com\u002Faeonfun\u002Faeon\u002Fblob\u002Fmain\u002Fskills\u002Fcanister-status\u002FSKILL.md",[128,131],{"categoryNo":129,"categoryName":130,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":132,"categoryName":133,"taxonomyType":134,"taxonomyTypeDesc":135},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[137,138,80,139,140,82,141,83,84,85,115,142,143,87,88,116,90,144,91,145],"canister status","status","missing","monitor","module","alert","plane","change","changes",{"skillNo":147,"name":148,"nameZh":6,"description":149,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":150,"categories":151,"keywords":154,"publishedAt":35},"01M6FVEXRG77BT4VH94P0S1TDT","you-web-search","Web search using You.com Search API with high-quality, cited results — runs keyless out of the box; YDC_API_KEY unlocks higher limits and real-time web crawling","https:\u002F\u002Fgithub.com\u002Faeonfun\u002Faeon\u002Fblob\u002Fmain\u002Fskills\u002Fyou-web-search\u002FSKILL.md",[152,153],{"categoryNo":129,"categoryName":130,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":132,"categoryName":133,"taxonomyType":134,"taxonomyTypeDesc":135},[155,156,157,158,159,112,83,87,160,117,161,162,163,164,165,166,167,168,169,170],"web search","search","you","you web search","web","web crawling","read-only","real-time","basics","keyless","with","research","results","quality","high","limits",{"skillNo":172,"name":173,"nameZh":6,"description":174,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":175,"categories":176,"keywords":177,"publishedAt":35},"01M6FVER8Y2B0H7H26G4GTYK6N","write-tweet","Multi-format tweet studio - standalone drafts (10 across 5 size tiers), a 5-10 tweet thread, or 10 remixes of past tweets, selected via ${var}","https:\u002F\u002Fgithub.com\u002Faeonfun\u002Faeon\u002Fblob\u002Fmain\u002Fskills\u002Fwrite-tweet\u002FSKILL.md",[],[94,178,179,180,87,181,182,163,183,184,185,186,187,188,189,190,191,192],"write tweet","social","studio","across","xai","twitter","multi","via","format","content","standalone","past","drafts","thread","size",{"skillNo":194,"name":195,"nameZh":6,"description":196,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":197,"categories":198,"keywords":202,"publishedAt":35},"01M6FVENCJ0HY2TEE6CNZN4F96","weekly-aeoncard","Build a weekly token-consumption recap image from memory\u002Ftoken-usage.csv - this week + all-time totals, top skills, rendered as a shareable SVG card.","https:\u002F\u002Fgithub.com\u002Faeonfun\u002Faeon\u002Fblob\u002Fmain\u002Fskills\u002Fweekly-aeoncard\u002FSKILL.md",[199],{"categoryNo":200,"categoryName":201,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体",[203,204,205,206,207,208,209,112,30,210,211,212,213,88,214,215,94,216,12,217],"weekly aeoncard","weekly","as","usage","SVG","memory","card","image","recap","csv","monitoring","productivity","build","token","top"]