Skip to main content

aeon-update

Pull framework updates from the upstream Aeon repo into this instance - 3-way merges canon's new commits into a PR, never clobbering operator config.

跳到安装

来源信息

仓库
aeonfun/aeon
最近来源活动
2026年9月14日 14:26
检测到的 SKILL.md 语言
英语
星标
738
分支
265

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
aeon-update
description
Pull framework updates from the upstream Aeon repo into this instance - 3-way merges canon's new commits into a PR, never clobbering operator config.
metadata
{"title":"Aeon Update","category":"core","var":"","tags":["dev","meta"],"cron":"0 11 * * 1","mode":"write"}
> **${var}** — mode selector; space-separated tokens, order-independent, all optional: > - **mode** (`sync` | `report`, default `sync`) — `sync` opens a PR with the framework changes; `report` computes the delta and notifies, mutating nothing (dry run). > - **`repo=owner/name`** — override the upstream source repo (else auto-resolved from this instance's `parent`, falling back to `aeonfun/aeon`). > - **`reset=<sha|fork-point>`** — force the stored baseline to `<sha>` (or the merge-base with upstream) before running. Recovery / backfill lever. > > Empty ⇒ **sync** from the auto-resolved upstream. Examples: `` · `report` · `repo=aeonfun/aeon` · `reset=fork-point`. Today is ${today}. This is the fleet's **downstream updater** - the counterpart to `fork-fleet`. `fork-fleet` looks *outward* from the parent to find work in the forks worth pulling *up*; this skill runs *inside* an instance and pulls the parent's shipped framework changes *down* - new skills, script/harness fixes, workflow and doc updates - and lands them as a reviewable PR. It is how an instance stays current with `aeonfun/aeon` without a hand-run rsync-overlay rebase. ## Operating principles - **PR, never push to `main`.** Every framework change ships as one reviewable PR. The operator merges. - **Never clobber operator config.** `aeon.yml`, `STRATEGY.md`, `soul/`, `memory/`, `output/`, `.mcp.json` and the git-derived catalogs are instance-owned. Upstream changes to them are **surfaced for manual review in the PR body**, never written into the tree. - **3-way, not blind overwrite.** A framework file the operator has already customized is **auto-merged** when its local edits and upstream's edits touch disjoint regions (a real `git merge-file` 3-way, S6); it is only listed as a **conflict** for a human when the same lines changed on both sides. This is what lets a hand-narrowed workflow keep receiving unrelated upstream fixes without a manual merge every run. - **Silent when in sync.** Baseline == upstream HEAD ⇒ nothing to do, no notification. - **The baseline is the watermark, and it advances by merge.** The new baseline SHA is written *into the PR branch*, so the watermark only moves when the operator merges the PR. Unresolved conflicts are tracked separately so advancing the baseline can never silently drop them. --- ## Steps ### S0. Bootstrap + load state ```bash mkdir -p memory/topics [ -f memory/topics/aeon-update-state.json ] || echo '{"baseline_sha":null,"upstream":null,"last_run":null,"last_pr":null,"pending_conflicts":[]}' > memory/topics/aeon-update-state.json ``` Read `memory/MEMORY.md` for context and scan the last ~3 days of `memory/logs/` - drop anything already reported so a repeat run isn't re-sent. Read the state file: - `BASELINE` = `.baseline_sha` (the upstream commit this instance was last synced to). - `PENDING` = `.pending_conflicts` (files surfaced as conflicts in a prior run, not yet resolved). ### S1. Parse `${var}` - `MODE` = `report` if the token `report` (or `dry`) is present, else `sync`. - `REPO_OVERRIDE` = value of a `repo=owner/name` token, if any. - `RESET` = value of a `reset=` token, if any (`fork-point` or a 7-40 char SHA). ### S2. Resolve the upstream source ```bash SELF=$(gh repo view --json nameWithOwner -q .nameWithOwner) UPSTREAM="${REPO_OVERRIDE:-$(gh api "repos/${SELF}" --jq '.parent.full_name // empty')}" [ -z "$UPSTREAM" ] && UPSTREAM="aeonfun/aeon" ``` If `UPSTREAM` == `SELF`, this instance **is** canon - there is nothing upstream to pull. Write status `AEON_UPDATE_IS_UPSTREAM` to `memory/logs/${today}.md`, send **no** notification, and stop. ```bash UP_DEFAULT=$(gh api "repos/${UPSTREAM}" --jq '.default_branch') HEAD_SHA=$(gh api "repos/${UPSTREAM}/commits/${UP_DEFAULT}" --jq '.sha') ``` ### S3. Establish / reset the baseline - **`reset=fork-point`** → `BASELINE=$(gh api "repos/${UPSTREAM}/compare/${HEAD_SHA}...$(git rev-parse HEAD)" --jq '.merge_base_commit.sha')`. `reset=<sha>` → `BASELINE=<sha>`. Persist immediately to state, then continue. - **First run (`BASELINE` null and no `reset`):** a fresh instance already carries all of canon from fork time, so there is no delta to apply - just **anchor the watermark**. Set `baseline_sha = HEAD_SHA`, write state, log `AEON_UPDATE_BASELINE_SET`, send a one-line notify (`baseline initialized at <head7>; future runs sync from here`), and stop. (To backfill everything since the fork point instead, re-run with `reset=fork-point`.) - **`BASELINE` == `HEAD_SHA`:** in sync. Re-verify `PENDING` (S8) in case a prior conflict is now resolved, update state, log `AEON_UPDATE_IN_SYNC`, notify nothing, stop. ### S4. Compare baseline → HEAD ```bash gh api "repos/${UPSTREAM}/compare/${BASELINE}...${HEAD_SHA}" --jq '{ ahead: .ahead_by, behind: .behind_by, status, commits: [.commits[]? | {sha: .sha[0:7], msg: (.commit.message | split("\n")[0]), date: .commit.author.date}], files: [.files[]? | {filename, status, previous_filename, additions, deletions}] }' > /tmp/aeon-update-compare.json ``` Error handling: - **404 / `status: "diverged"` with no merge base** (baseline not an ancestor of HEAD - history rewrite or unrelated repo): stop with `AEON_UPDATE_BASELINE_UNREACHABLE`; notify the operator to re-run with `reset=fork-point` or `reset=<sha>`. - Cross-repo compare returns at most **300 files**; if `.files` looks truncated, note `files_truncated=true` in the report - the operator can run again after merging to pick up the remainder. ### S5. Partition changed files Classify every entry in `.files` by path. A file is **OPERATOR-owned** (surfaced, never auto-written) if its path matches any of: ``` aeon.yml STRATEGY.md soul/** memory/** output/** .mcp.json .env* aeon.db skills.lock eyebrowlock.json catalog/*.json .claude/** (except .claude/skills/aeon/**) apps/dashboard/outputs/** ``` Everything else is **OWNED** (a candidate for auto-apply): `skills/**`, `scripts/**`, `bin/**`, `harness-adapter/**`, `.github/**`, `apps/**` (except `apps/dashboard/outputs/**`), `CLAUDE.md`, `AGENTS.md`, `docs/**`, `.github/README.md`, `LICENSE`, `CHANGELOG.md`, `.gitignore`, `eyebrow.policy.json`, and tracked root helpers (`aeon`, ...). `catalog/*.json` and `eyebrowlock.json` are OPERATOR-owned here **only** so they are never blindly copied - they are **regenerated** from the synced sources in S7, which is the correct way to reconcile them. ### S6. 3-way classify each OWNED file Set up a workspace and, for each OWNED file `f`, fetch upstream's HEAD and BASELINE blobs: ```bash WORK=$(mktemp -d) fetch() { gh api "repos/${UPSTREAM}/contents/$1?ref=$2" --jq '.content' 2>/dev/null | base64 -d; } # $1=path $2=ref h() { sha256sum 2>/dev/null | cut -d' ' -f1; } ``` Decide `f`'s disposition from its `status` and a content 3-way (local-current vs upstream@BASELINE vs upstream@HEAD): | `status` | Test | Disposition | |----------|------|-------------| | `added` | path absent locally | **CLEAN-ADD** (write HEAD blob) | | `added` | path present locally (collision, e.g. a fork-only skill) | **CONFLICT** | | `modified` | `sha256(local) == sha256(HEAD blob)` | already synced → **SKIP** | | `modified` | `sha256(local) == sha256(BASELINE blob)` (operator never touched it) | **CLEAN-UPDATE** (write HEAD blob) | | `modified` | otherwise (operator customized it) | **3-WAY MERGE** → CLEAN-MERGE or CONFLICT (see below) | | `removed` | `sha256(local) == sha256(BASELINE blob)` | **CLEAN-DELETE** (`git rm`) | | `removed` | local differs or absent | **CONFLICT** (or already gone → SKIP if absent) | | `renamed` | treat as `removed previous_filename` + `added filename` under the rules above | per-part | Never CLEAN-DELETE a `skills/<name>/` directory whose `<name>` is not present in upstream's tree - fork-only skills are operator work and are structurally untouched (upstream's compare can only reference paths that exist upstream). Also never CLEAN-DELETE a `skills/<name>/` directory if `<name>` is currently `enabled: true` in the operator's `aeon.yml` (`grep -E "^ ${name}: *\{[^}]*enabled: true" aeon.yml`) - upstream retiring a skill the operator has actively scheduled is exactly the case `validate-config.js`'s skill-refs check exists to catch, but only *after* the PR is merged; nothing in the PR review itself would otherwise flag it. Downgrade this case to **CONFLICT** (reason: `enabled-skill-removed-upstream`) instead of deleting - the directory stays, and S9 surfaces it as its own loud PR-body section rather than folding it into "Applied cleanly" or the generic conflict list. **3-way content merge (OWNED files only).** A `modified` file that reached the `otherwise` row means the operator diverged from BASELINE *and* upstream changed the file too. Do not give up on it - most of the time the two sets of edits are in different parts of the file (e.g. the operator narrowed the workflow's `env:` secrets block while upstream bumped a `timeout` and a retry loop elsewhere), and a real 3-way merge combines both losslessly. Attempt it before declaring a conflict: ```bash fetch "$f" "$BASELINE" > "$WORK/base" # upstream@BASELINE (the common ancestor) fetch "$f" "$HEAD_SHA" > "$WORK/head" # upstream@HEAD (what to bring in) cp "$f" "$WORK/local" # operator's current copy (ours) if git merge-file -p --diff3 "$WORK/local" "$WORK/base" "$WORK/head" > "$WORK/merged.$$" 2>/dev/null; then disposition=CLEAN-MERGE # exit 0 = disjoint hunks; the merged file carries BOTH edits - write it in S7 else disposition=CONFLICT # exit >0 = the same lines changed on both sides; surface for a human as before fi ``` `git merge-file` exits `0` only when the merge is clean (operator and upstream touched disjoint regions); the merged output preserves the operator's customization AND applies upstream's change. A non-zero exit means a genuine overlap - keep it a **CONFLICT** and list it with the upstream diff (S9). **Only OWNED files are ever 3-way-merged; OPERATOR-owned paths are always surfaced, never written.** A merged file gets the same S7 YAML/JSON parse-check as any written file - if the merge produced something that no longer parses, abort that file back to CONFLICT rather than committing it. ### S7. Apply CLEAN changes on a branch (sync mode only) If `MODE == report`, skip to S9. Otherwise: ```bash BR="aeon-update/sync-$(echo "$HEAD_SHA" | cut -c1-7)" git checkout -b "$BR" ``` Write every **CLEAN-ADD** / **CLEAN-UPDATE** (`mkdir -p "$(dirname f)"` then write the HEAD blob to `f`), write every **CLEAN-MERGE** (the merged file `$WORK/merged.$$` from S6, over the existing `f`), and `git rm` every **CLEAN-DELETE**. **CONFLICT** and **OPERATOR** files are *not* touched - they go in the PR body only. (CLEAN-MERGE counts as a clean apply for the "nothing CLEAN applied" test below.) If any `skills/**` path was applied, regenerate the derived catalogs from the synced sources (never copy them from upstream): ```bash bin/generate-skills-json && bin/generate-packs-json && bin/generate-skill-icons node scripts/gen-agents-md.js || true ``` **Refresh the eyebrow integrity lock for any NEWLY-ADDED skill.** `ci-skill-integrity` fails a PR when a present skill's `skills/<slug>/SKILL.md` has no `"discoveredFrom": "skills/<slug>/SKILL.md"` entry in `eyebrowlock.json`. That entry is produced only by the `eyebrow` binary, which is **not preinstalled in this run** - so a CLEAN-ADD of a new skill would otherwise land the PR CI-red. Fetch the binary at the **exact version `ci-skill-integrity.yml` pins**, parsed from that workflow's `alexverify/eyebrow/action@<sha> # vX.Y.Z` line so it can never drift from CI - a hardcoded version writes a lock that CI's (newer) `eyebrow verify` then rejects as drift, which is the recurring cause of red sync PRs. Verify the downloaded tarball against the release's own `checksums.txt` before running it (the same checksum-verified install the action does), then rescan: ```bash EYEBROW_OK=0 EB=$(command -v eyebrow || true) if [ -z "$EB" ]; then # Use the SAME eyebrow version ci-skill-integrity.yml pins, parsed from the # workflow's `alexverify/eyebrow/action@<sha> # vX.Y.Z` comment. A hardcoded # version silently drifts from CI (the lock this writes with an older binary # then fails CI's newer `eyebrow verify` as drift - the recurring sync red); # deriving it self-heals across action bumps. Fall back to v0.4.2 if unparsable. EBV=$(grep -oE 'alexverify/eyebrow/action@[0-9a-f]+ *# *v[0-9]+\.[0-9]+\.[0-9]+' .github/workflows/ci-skill-integrity.yml | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -1)
在 GitHub 查看
这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看