Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .claude/skills/aeon/references/skill-anatomy.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ Claude Code's Bash permission analyzer **blocks any command containing a secret

Braces, not `$`. A skill that writes `-H "Authorization: Bearer $XAI_API_KEY"` will be blocked at run time, not at author time.

In a scheduled run `./secretcurl` only fills placeholders for secrets the skill lists in `requires:` (plus `GH_TOKEN` / `GITHUB_TOKEN`). A placeholder for any other set key is refused with a message that names it, never its value, so declare every key you put in braces. curl runs with a stripped environment, and `--variable`, `--expand-*`, `-K` / `--config` and option values that read a file under `/proc` are refused. (#1215, #1216)

There is **no network sandbox** - plain `curl` works for unauthenticated GETs.

## Memory
Expand All @@ -130,7 +132,7 @@ This is also the **dedup substrate**. The standard rule, and the one to add to a

### `memory/MEMORY.md` - the durable index (138 references across 59 skills)

Long-lived facts, not run history. Skills read it for context; the `memory-flush` skill promotes important log lines into it and prunes stale ones. Don't append per-run noise here - that's what `logs/` is for.
Long-lived facts, not run history. Skills read it for context; the `memory-flush` skill promotes important log lines into it and prunes stale ones, holds it under 8 KB with no line over 300 chars (it loads into every run), and ends each fact it adds with `[source: logs/YYYY-MM-DD#<skill>]`. Don't append per-run noise here - that's what `logs/` is for.

### Domain state files

Expand Down
78 changes: 77 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,36 @@ from or pin to; the template keeps serving the latest `main` to new forks.

### Added

- **Langfuse: one root span per run, priced cost, scores and slicing.** Each claude call now
sends a root span (also on failed runs) with one priced `usage <model>` row per model, and
Claude Code's own spans nest under it again: a 1s SessionStart hold fixes the trace tree that
split into one trace per span on subscription tokens (`AEON_OTEL_STARTUP_DELAY=0` turns it off).
Cost rows carry the skill as trace name and user, plus environment (`production` on the default
branch, `branch` elsewhere), release, SKILL.md version and a `cron` / `chain` / `manual`
trigger (new optional `trigger` dispatch input). New scores: a NUMERIC quality score and a
BOOLEAN `run_ok` per run. Failed runs get an `error_class` (timeout, rate_limit, auth,
budget_cap, cancelled and the rest of the shared table) and a status message. 1-hour cache
writes are priced at the 1-hour rate (cost read about 35% low before). The feed render and the
scorer run traced and lean (`run-harness --no-tools`, empty scratch dir), and usage rows are
zero-length so latency charts show real requests only. See `docs/langfuse.md`. (#1221, #1222,
#1223, #1230, #1232, #1233)
- **`memory-flush` cites a source on every fact and skips idle runs.** Facts it adds or changes
end with `[source: logs/YYYY-MM-DD#<skill>]`, and `memory_prep.py source <ref>` prints that log
section (archive too), so contradictions are settled from the log. A new `Idle gate
(memory-flush)` step skips the model call when no daily log changed since a flush under 7 days
old. It also holds `memory/MEMORY.md` to 8 KB with no line over 300 chars. (#1217, #1232)
- **OpenRouter app attribution on every harness.** Every OpenRouter request (claude via
`gateway: openrouter`, codex, kimi, vibe, pi, hermes) carries `HTTP-Referer:
https://www.aeon.fun`, `X-OpenRouter-Title: Aeon` and `X-OpenRouter-Categories`. Override with
`OPENROUTER_SITE_URL`, `OPENROUTER_APP_TITLE` and the new `OPENROUTER_APP_CATEGORIES`.
(#1235, #1239)
- **Feedback section in the aeon setup skill.** When a `./aeon` command, a Connect MCP tool or a
skill step fails, the agent may send one short report through Connect's `send_feedback` tool or
the keyless `https://www.aeon.fun/api/feedback`, never with secrets or private repo contents.
(#1226)
- **Sentinel Pack listed** in the community registry: `robinhood-chain-monitor` watches addresses
on Robinhood Chain (4663), keyless, alerts only on change. (#1227)
- **Ecosystem: bon travail and skopos** added to `docs/ECOSYSTEM.md`. (#1220)
- **`anatomy`: interactive isometric SVG figures from a brief.** New `dev` skill (vendored from
upstream at 689dcf9, MIT, provenance in `skills/anatomy/UPSTREAM.md`) that builds a crafted,
interactive isometric figure explaining a technical idea as a physical object, with live readout,
Expand Down Expand Up @@ -202,6 +232,19 @@ from or pin to; the template keeps serving the latest `main` to new forks.

### Changed

- **Claude Code pinned to 2.1.301.** Usage-limit waits are capped at 10 minutes
(`CLAUDE_CODE_RETRY_WATCHDOG`), and WebFetch is turned off when egress runs in `enforce` mode
with the proxy up, since it never passes through iron-proxy. (#1234)
- **Test branches stay quiet.** Notifications and health-regression Issues now come only from
runs on the default branch; repo variable `NOTIFY_ON_BRANCH=1` opts a test branch back in.
(#1230, #1232)
- **Runner label from `AEON_RUNNER`.** Workflow jobs read their runner from the `AEON_RUNNER`
repo variable (unset = `ubuntu-24.04`, e.g. `blacksmith-2vcpu-ubuntu-2404`); the CI gate and
small catalog checks stay on GitHub runners. (#1228, #1236)
- **CONTRIBUTING lists every CI gate** with the local command that matches it, plus the test
lanes. (#1211, #1213, #1238)
- **`SECURITY.md` has a contact email** (`aaron@aeon.fun`) for reporters who can't use GitHub's
private form. (#1219)
- **Setup skill secrets guidance tightened.** A new "Handling secrets" rule in `SKILL.md` and
`references/secrets.md`: never ask for a key in chat, never echo, log or commit one, never read
credential files to fill one in; values go in through `./aeon secrets set NAME --stdin` or the
Expand Down Expand Up @@ -338,9 +381,24 @@ from or pin to; the template keeps serving the latest `main` to new forks.

### Fixed

- **Scheduler:** `SAT-SUN` / `FRI-SUN` ranges run (SUN closes a range as 7) (#1200, #1212); an
invalid schedule now raises a `::warning::` naming the skill instead of a silent skip (#1206);
a cron-state push that fails every retry fails the tick, so owed skills are not dispatched
twice (#1207, #1213).
- **CLI:** `aeon skills schedule` refuses a schedule CI would reject and `skills set --harness`
checks the name (#1212); `aeon mcp add` and `saveFile` (and the dashboard MCP, strategy and soul
routes) treat only a missing file as missing, so an auth or network error no longer overwrites
`.mcp.json` with one server (#1210, #1213).
- **Frontmatter readers handle CRLF, comments and mixed case,** so a `mode: read-only` skill no
longer runs in the write tier and `requires:` / `depends_on:` / `attest:` lists stay intact.
(#1203, #1213)
- **Dropped notifications are visible:** a payload no channel accepted raises a `::warning::`
with the count; no warning when no channel is configured. (#1208, #1213)
- **MCP server:** a multi-line `var` is folded onto one prompt line (#1204), and a failed skill run
returns an MCP error result instead of looking like success (#1209).
- **Post-run Git pushes retain trusted checkout authentication.** The config guard rebuilds
host-scoped HTTP auth from workflow secrets after removing untrusted includes. Push failures
now show redacted diagnostics and retry only stale refs, with authenticated HTTP push coverage.
now show redacted diagnostics and retry only stale refs, with authenticated HTTP push coverage. (#1241)
- **Stale Claude Code npm cache no longer blocks pin bumps.** `restore-keys` restored an old cache
whose packument did not list the new version, so every run failed at "Install Claude Code"; an
exact-key miss now downloads fresh. (#1169)
Expand Down Expand Up @@ -1234,6 +1292,20 @@ from or pin to; the template keeps serving the latest `main` to new forks.

### Security

- **Run guards after a skill run.** On main, changes a run made under `scripts/`, `.github/` or
`harness-adapter/` are restored to a freshly fetched `main` with an `::error::`; feature-branch
PR flows are unaffected. Push helpers run from the starting commit with hooks off, and a new
`Lock git config` step drops `.git/config` keys that run programs, then restores checkout auth
from fresh secrets. (#1214, #1216, #1241)
- **`./secretcurl` fills only secrets the skill declared** in `requires:` (plus `GH_TOKEN` /
`GITHUB_TOKEN`); curl runs with a stripped environment, and `--variable`, `--expand-*`, `-K` /
`--config` and `/proc` file reads are refused with an audit record. (#1215, #1216)
- **`bin/add-skill` accepts only plain skill names,** so `..` or `/` can't write outside
`skills/`. (#1202)
- **Inbound message text stays out of Actions run names and logs** in `messages.yml` (source,
length and a short hash only). (#1201, #1213)
- **`vuln-poc-gate` hardening:** a PoC must sit inside the PoC dir (no `..` or links), and files
a PoC creates in the target count as mutation. (#1218)
- **Security audit fixes (2026-10-01).** The dashboard binds to `127.0.0.1` only; the health step
takes its values through `env:` (no shell injection); `secretcurl` escapes newlines, rejects
malformed option tokens and no longer sends its config (with the substituted secret) as a
Expand Down Expand Up @@ -1288,6 +1360,10 @@ from or pin to; the template keeps serving the latest `main` to new forks.

### Maintenance

- CI and housekeeping 2026-10-12 to 2026-10-17: catalog sha refresh (#1198), cached bubblewrap
`.deb` (#1205, #1231), one parallel shellcheck pass (#1224), CI on Blacksmith runners with small
checks on GitHub (#1225, #1228, #1236), test suites in 10 parallel lanes (#1238), aeon-agent refs
moved to aeonfun (#1229).
- Dependency bumps 2026-10-07: `sharp`, `@modelcontextprotocol/sdk`, `proxy-addr`,
`source-map-js` and an npm_and_yarn group (#1171, #1172, #1173, #1174, #1175). PR numbers for
entries above that shipped without one: Aeon Connect onboarding docs (#1163), hosted MCP pointers
Expand Down
2 changes: 1 addition & 1 deletion docs/ADK.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ Report only signal via ./notify - a clean run sends nothing.

Conventions that matter:

- **`requires:`** - exact env-var names. This drives the dashboard's "which skill needs which key" UI and your own pre-flight in §4.1. Secrets reach the run environment; secret-bearing calls go through `./secretcurl` with `{PLACEHOLDER}` tokens (a raw `curl …$KEY` is blocked by the permission layer).
- **`requires:`** - exact env-var names. This drives the dashboard's "which skill needs which key" UI and your own pre-flight in §4.1. Secrets reach the run environment; secret-bearing calls go through `./secretcurl` with `{PLACEHOLDER}` tokens (a raw `curl …$KEY` is blocked by the permission layer). In a run, `./secretcurl` only fills placeholders for names listed in `requires:`, so a key you forget to declare is refused, not sent.
- **`mode: read-only`** if the skill shouldn't touch the repo - write tools are stripped and stray writes reverted. Declare honest `capabilities` (§5.2) for everything else.
- **If your product is an MCP server**, declare `mcp: [your-slug]` instead of raw API calls, and ship the server entry for `.mcp.json`. OAuth-connected MCPs get a Connect button in the dashboard.
- **Notify on signal only.** The `./notify` tool fans out to whatever channels the operator configured (Telegram/Discord/Slack/email) - your skill's reports arrive where the operator already lives, for free.
Expand Down
12 changes: 12 additions & 0 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,16 @@ mode: write # full access (the default): adds Write / Edit / git / gh / py

`read-only` strips the repo-mutation tools from Claude Code's `--allowedTools` (`Write`, `Edit`, `Bash(git:*)`, `Bash(gh:*)`, python) **and** the OS sandbox write-locks the workspace for the run (see [Capabilities → enforcement layers](CAPABILITIES.md)), so a research-and-notify skill **physically can't** commit, push, open a PR, or change code or config. The two state dirs are the exception: `memory/` and `output/` stay writable (via a shell redirection or `node`) so a read-only skill can keep its own state and artifacts (since #1042). Route the run's result through your **final message** (the run's captured output) and `./notify`. After the run the workflow persists your captured output to `output/.chains/`, **appends the `### <skill>` entry to `memory/logs/` on your behalf** (so read-only skills should not self-log, or the entry appears twice), and reverts any stray write outside `memory/` and `output/`. Use it for pure read-and-notify skills; `write` (the default, a strict superset) for anything that writes code. It's the runtime half of the install-time [`capabilities:`](../docs/CAPABILITIES.md) hint.

## Run guards

A skill run has push credentials, so the steps after it treat what it left behind as untrusted:

- **Code that later runs stays off main.** On the default branch, any change a run made under `scripts/`, `.github/` or `harness-adapter/` (left in the tree or committed) is restored to a freshly fetched `main` and reported with an `::error::`; the rest of the run's state is committed as usual. Skills that change those paths on a feature branch and open a PR (`aeon-update`, `skill-repair`, `self-improve`, `create-skill` and others) are unaffected. (#1214, #1216)
- **Push helpers come from the run's starting commit.** The commit steps run `git-push-retry.sh` and `audit.sh` from `$GITHUB_SHA`, never the working tree, with git hooks off. A `Lock git config` step right after `Run` drops `.git/config` keys that make git run a program (fsmonitor, filters, merge drivers, credential helpers, signing, includes, url rewrites) and then rebuilds the checkout's HTTP auth from fresh workflow secrets, so pushes keep working. (#1216, #1241)
- **`./secretcurl` fills only declared secrets.** A placeholder is substituted only when the skill lists that name in `requires:` (`GH_TOKEN` and `GITHUB_TOKEN` are always allowed); curl runs with a stripped environment and options that make it read env vars or `/proc` are refused. A refused call leaves an audit record with names only. (#1215, #1216)

These are backstops, not a hard boundary: a write-mode run still has `GH_GLOBAL` in its env.

## Dry-run gate for self-authored skills

`create-skill` and `self-improve` write skills that reach `main` through auto-merging PRs. Left alone, the only thing between a generated skill and production is a Haiku score computed *after* it has already run with real secrets against real repos. For the part of the system that writes its own code, that ordering is backwards.
Expand Down Expand Up @@ -277,6 +287,8 @@ The template ships a few starter files under `memory/` (e.g. `MEMORY.md`, `watch

Private repos: Free plan = 2,000 min/mo, Pro/Team = 3,000 + $0.008/min overage. To reduce usage: switch to `*/15` or hourly cron, disable unused skills, keep the repo public. Every run logs token usage to `memory/token-usage.csv` for a per-skill, per-model cost breakdown.

`memory-flush` skips itself before the model starts when no daily log changed since its last flush and that flush is under 7 days old (the `Idle gate (memory-flush)` step; cron state still records a success). Any doubt means it runs, so the worst case is one extra run. (#1217)

---

## Authentication
Expand Down
10 changes: 10 additions & 0 deletions docs/harnesses.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ through OpenRouter: `moonshotai/kimi-k2.7-code` (default, code-tuned) and `kimi-
routes through the same harness the skill
ran on, so a repo with **no** Claude credentials still gets every run scored.

### OpenRouter app attribution

Every OpenRouter request aeon makes carries app attribution headers, so usage is credited to Aeon's
OpenRouter app: `HTTP-Referer: https://www.aeon.fun`, `X-OpenRouter-Title: Aeon` (plus the legacy
`X-Title`) and `X-OpenRouter-Categories: personal-agent,cloud-agent`. One helper
(`harness-adapter/lib/openrouter-attribution.sh`) sets them for claude (via `gateway: openrouter`),
codex, kimi, vibe, pi and hermes; cursor, fx and grok have no OpenRouter path. A fork can set its own
with `OPENROUTER_SITE_URL`, `OPENROUTER_APP_TITLE` and `OPENROUTER_APP_CATEGORIES` in the run
environment. (#1235, #1239)

### Native auth — run on your own provider account

OpenRouter is the shared fallback; each harness can also run on its **own**
Expand Down
4 changes: 3 additions & 1 deletion plugin/skills/aeon/references/skill-anatomy.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ Claude Code's Bash permission analyzer **blocks any command containing a secret

Braces, not `$`. A skill that writes `-H "Authorization: Bearer $XAI_API_KEY"` will be blocked at run time, not at author time.

In a scheduled run `./secretcurl` only fills placeholders for secrets the skill lists in `requires:` (plus `GH_TOKEN` / `GITHUB_TOKEN`). A placeholder for any other set key is refused with a message that names it, never its value, so declare every key you put in braces. curl runs with a stripped environment, and `--variable`, `--expand-*`, `-K` / `--config` and option values that read a file under `/proc` are refused. (#1215, #1216)

There is **no network sandbox** - plain `curl` works for unauthenticated GETs.

## Memory
Expand All @@ -130,7 +132,7 @@ This is also the **dedup substrate**. The standard rule, and the one to add to a

### `memory/MEMORY.md` - the durable index (138 references across 59 skills)

Long-lived facts, not run history. Skills read it for context; the `memory-flush` skill promotes important log lines into it and prunes stale ones. Don't append per-run noise here - that's what `logs/` is for.
Long-lived facts, not run history. Skills read it for context; the `memory-flush` skill promotes important log lines into it and prunes stale ones, holds it under 8 KB with no line over 300 chars (it loads into every run), and ends each fact it adds with `[source: logs/YYYY-MM-DD#<skill>]`. Don't append per-run noise here - that's what `logs/` is for.

### Domain state files

Expand Down