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.

Aller à l'installation

Informations de source

Dépôt
aeonfun/aeon
Dernière activité de la source
14 septembre 2026 à 14:26
Langue détectée de SKILL.md
anglais
Étoiles
738
Forks
265

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
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)
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub