| name | add-eidolon |
| description | Promote an Eidolon to the nexus roster or bump an existing Eidolon's version. Use when the user says "publish X", "introduce X in the roster", "add X to the roster", "X is now stable", "bump <eidolon> to vX.Y.Z", or when working on a roster/index.yaml entry. Covers the full workflow — upstream repo normalization (EIIS-1.0 install.sh, version strings, CHANGELOG), roster + preset edits, capability-class schema changes for new classes, CI matrix, documentation touchpoints (README, MANIFESTO, composition.md, CHANGELOG), and verification. |
add-eidolon — Promote an Eidolon to the Eidolons nexus roster
This skill codifies the pattern for two related operations:
- Promote a new Eidolon to
shipped (e.g. FORGE, VIGIL) — adds a roster entry, possibly extends the capability_class enum, updates docs, and activates the nightly EIIS conformance check.
- Bump an existing shipped Eidolon's version (e.g. SPECTRA 4.2.7 → 4.2.8) — changes
versions.latest / versions.pins.stable in the roster, adds a CHANGELOG entry, branch name fix/roster-<eidolon>-<version>.
The two flows share ~70% of their checklist. Where they diverge, each section is labeled [NEW] or [BUMP].
Invariants you must respect
These come from CLAUDE.md in the nexus repo and from tooling contracts — violating them breaks CI or consumer installs:
- Roster entries drive CI. The matrix in
.github/workflows/roster-health.yml (line 69 area) hardcodes Eidolon names. A new member means editing both roster/index.yaml and the matrix. [NEW]
- Install target is
./.eidolons/<member>/ — dot-prefixed, hidden. Per-Eidolon install.sh should default to this.
- Canonical repo casing matters.
source.repo must match GitHub's canonical casing (e.g. Rynaro/APIVR-Delta, not rynaro/apivr-delta). The clone works either way, but the manifest is used as an identity key.
- Marker-bounded sections (
<!-- eidolon:<name> start --> … <!-- eidolon:<name> end -->) are how multiple Eidolons coexist in shared files — per-Eidolon installers use them; eidolons remove relies on them.
- Bash 3.2 compatibility. macOS ships bash 3.2.
install.sh scripts must avoid bash 4+ features (no declare -A, no ${var,,}, no readarray/mapfile, no &>>).
- Idempotency.
eidolons sync must produce identical output on repeat runs unless the roster or manifest changed. Every install.sh must be safe to re-run.
- No code execution from
eidolons.yaml. The pipeline is yaml → jq query → bash exec with no eval.
- Direct pushes to nexus
main are not the pattern. Land everything through a PR.
- All log output to stderr when helpers echo the return value on stdout (see
cli/src/lib.sh say/ok/info/warn/die).
Flow 1: Promoting a new Eidolon to shipped [NEW]
Phase 0 — Exploration (understand the Eidolon)
Before writing anything, establish these facts by reading the Eidolon's repo:
| Fact | Where to find it |
|---|
| Canonical name (lowercase slug) + display name | AGENTS.md frontmatter name: field |
| Methodology name, version, cycle, summary | AGENTS.md frontmatter + README/entry-point |
| Capability class | Inferred from role — see "Capability classes" table below |
source.repo (canonical casing) | GitHub URL |
| Current version + tag | git tag -l; if no tags, you'll need to tag in Phase A |
| Handoff shape (pipeline vs lateral) | See "Lateral vs pipeline classification" below |
| Token budget | Entry = wc -w agent.md × 1.33 (or read from CHANGELOG); target = typical working-set measurement |
| Security posture | reads_repo, reads_network, writes_repo, persists — read from install.sh and methodology docs |
Capability classes (current enum — extend if genuinely new):
scout — read-only exploration (ATLAS)
planner — decision-ready specs (SPECTRA)
coder — implementation (APIVR-Δ)
scriber — documentation synthesis (IDG)
reasoner — structured deliberation (FORGE)
debugger — forensic root-cause attribution (VIGIL)
If you need a new class, add it to schemas/roster-entry.schema.json capability_class.enum. This is backward-compatible; existing roster entries still validate.
Lateral vs pipeline classification:
- Pipeline members (scout → planner → coder → scriber): have non-empty
upstream and downstream. Lateral array can reference consultable specialists.
- Lateral specialists (reasoner, debugger):
upstream: [], downstream: [], lateral: [<all other members>]. Called by any member; results flow anywhere. The nexus methodology/composition.md is authoritative — the Eidolon's own AGENTS.md may declare pipeline-style handoffs for its own view but the roster uses lateral-only to keep composition clean.
- When adding a new lateral specialist, update every other Eidolon's
lateral array to include the new one (so eidolons sync surfaces the relationship).
Phase A — Upstream Eidolon repo normalization
The Eidolon repo must satisfy EIIS-1.0 §3 for eidolons sync to work:
-
Required files (CI enforces): AGENTS.md, CLAUDE.md, install.sh, agent.md, README.md. If any are missing, add them in the Eidolon's own repo before touching the roster.
-
install.sh must accept the EIIS-1.0 §3 flag set. The nexus CLI (cli/src/sync.sh:167) invokes:
bash "$clone_dir/install.sh" \
--target "$target" \
--hosts "$HOSTS_CSV" \
[--shared-dispatch|--no-shared-dispatch] \
[--non-interactive] \
--force
Plus expected: --version (prints version, exits 0), --help / -h (CI tests this), --dry-run, --manifest-only. See ../ATLAS/install.sh for the reference shape; ../FORGE/install.sh and ../VIGIL/install.sh for adaptations that preserve Eidolon-specific flags (e.g. VIGIL's --mode read-only|sandbox|write).
-
install.manifest.json emission. The nexus CLI reads this to populate eidolons.lock. Required fields: eidolon, version, methodology, installed_at, target, hosts_wired, files_written, token_budget, security.
-
Per-host dispatch files. At minimum, when claude-code is in HOSTS_CSV, write .claude/agents/<name>.md. The nexus has a safety net (cli/src/sync.sh:179) that writes a stub if the installer doesn't, but installers should do this themselves.
-
Shared-dispatch flag compatibility shim. If the installer supports --no-shared-dispatch (grep for the string), the nexus reads user preference. Installers without the flag get a legacy-mode warning.
-
Version synchronization. If the Eidolon has version drift (release tag says vX.Y.Z but footers across methodology files still say vA.B.C), resolve it — update all *Name vX.Y.Z* footers across AGENTS.md, README.md, CHANGELOG.md document footer, host wirings, all templates, all skills, all evals. Do NOT rewrite historical CHANGELOG dated entries.
-
CHANGELOG. Keep-a-Changelog format. Close out [Unreleased] as a concrete version (e.g. [1.0.1] — 2026-04-23), add a fresh empty [Unreleased] section.
-
Bash 3.2. Run shellcheck: shellcheck -x -S error install.sh.
-
Smoke test locally:
bash install.sh --version
bash install.sh --help | head
bash install.sh --dry-run --target /tmp/<name>-smoke --hosts all
TMPDIR=$(mktemp -d); bash install.sh --target "$TMPDIR/x" --hosts claude-code --non-interactive --force
cat "$TMPDIR/x/install.manifest.json" | python3 -m json.tool
rm -rf "$TMPDIR"
-
Commit, tag, push (ask user first; this hits a shared remote):
cd ../<EIDOLON>
git checkout main
git merge --no-ff <feature-branch> -m "chore(release): <description> for v<X.Y.Z>"
git tag -a v<X.Y.Z> -m "<EIDOLON> v<X.Y.Z> — <description>"
git push origin main && git push origin v<X.Y.Z>
Ordering constraint: Eidolon repo must have the tag/main state before the nexus PR merges, so CI can clone and verify EIIS conformance. If the remote has moved during your work (someone merged your PR via GitHub UI), see "Reconciliation" below.
Phase B — Nexus roster
Branch off origin/main with feat/roster-<name>-shipped:
cd <nexus>
git fetch origin && git checkout main && git merge --ff-only origin/main
git checkout -b feat/roster-<name>-shipped
B.1 — If adding a new capability_class, extend the enum in schemas/roster-entry.schema.json.
B.2 — Add entry to roster/index.yaml using this template. Pipeline members go after existing pipeline members; lateral specialists go after FORGE:
- name: <lowercase-slug>
display_name: <DisplayName>
capability_class: <enum-value>
status: shipped
methodology:
name: <Name>
version: "<major>.<minor>"
cycle: "<phase arrow diagram>"
summary: "<one sentence>"
source:
type: github
repo: Rynaro/<CanonicalName>
default_ref: main
versions:
latest: "<major>.<minor>.<patch>"
pins:
stable: "<major>.<minor>.<patch>"
install:
target_default: "./.eidolons/<name>"
standalone: true
handoffs:
upstream: [<members>]
downstream: [<members>]
lateral: [<members>]
working_set_tokens:
entry: <measured>
target: <measured>
security:
reads_repo: <bool>
reads_network: <bool>
writes_repo: <bool>
persists: [<paths>]
references:
- "research/papers/<paper>.md"
B.3 — For new lateral specialists, add the new name to every other Eidolon's lateral: array.
B.4 — Bump updated_at: at the top of roster/index.yaml to today's ISO-8601 UTC.
B.5 — Update presets. At minimum add to full:
full:
description: "Every shipped Eidolon — pipeline plus lateral specialists."
members: [atlas, spectra, apivr, idg, forge, vigil, <new>]
If the new Eidolon suggests a named bundle, add a preset (e.g. diagnostics: [apivr, vigil, forge]).
B.6 — Release-integrity setup (v1.0+).
Every status: shipped Eidolon should declare release integrity metadata
under versions.releases.<latest>. Compatibility mode (integrity.enforcement: warn at the top of roster/index.yaml) keeps unannotated entries non-fatal,
but a future registry bump will switch enforcement to strict — at which
point every shipped Eidolon must populate metadata or fail the
shipped-status integrity metadata posture step in roster-health.yml.
Authoring loop:
- Adopt the release workflow template. Copy
.github/workflows/eidolon-release-template.yml from the nexus into the
upstream Eidolon repo as .github/workflows/release.yml. The template
uses workflow_call so the upstream repo wraps it with a thin on: workflow_dispatch caller. The template emits a release-manifest.json
with commit, tree, archive_sha256, optional manifest_sha256, and
provenance.github_attestation: true.
- Run a release. Trigger the wrapper workflow with the SemVer (no
leading
v). It tags, attests, and uploads release-manifest.json plus
SHA256SUMS to the GitHub release.
- Run
Roster Intake from the nexus. From the nexus repo's Actions
tab, dispatch roster-intake.yml with the Eidolon name + version. The
workflow downloads the upstream release-manifest.json, verifies the
SHA256SUMS file and the GitHub artifact attestation, edits
roster/index.yaml (writing versions.releases.<v>), and opens a draft
PR.
- Review + merge the intake PR. The body is auto-generated; nothing to
hand-author. Merging activates the new metadata.
After merge, every consumer running eidolons sync or eidolons upgrade
clones the exact tag, verifies commit + tree + archive_sha256 against
the cloned repo, and writes verification: "verified" into
eidolons.lock. Consumers can re-check at any time with eidolons verify,
or surface the per-member status from eidolons doctor.
Hash semantics (see docs/release-integrity.md for full detail):
commit / tree: Git object IDs. Tree is content-addressed and survives
history rewrites that preserve tree contents.
archive_sha256: SHA-256 of git archive --format=tar HEAD. Tree-wide
drift detection.
manifest_sha256: SHA-256 of the single file
./.eidolons/<name>/install.manifest.json after install. File-scoped, so
it survives legitimate host-wiring variation. Optional — many Eidolons
leave it null.
Phase C — CI matrix + documentation
C.1 — .github/workflows/roster-health.yml line 69 area:
matrix:
eidolon: [atlas, spectra, apivr, idg, forge, <new>]
C.2 — README.md roster table: add a row under the existing members.
C.3 — MANIFESTO.md team table: add a row. If the new Eidolon is a lateral specialist, update the "canonical pipeline" phrase (MANIFESTO.md:33 currently: "ATLAS → SPECTRA → APIVR-Δ → IDG, with FORGE (reasoning) and VIGIL (forensic debugging) as lateral specialists").
C.4 — methodology/composition.md:
- Update the pipeline diagram (§ canonical pipeline) to show the new lateral specialist.
- Add a row to the handoff contracts table (§ handoff contracts).
- Update the consultation-pattern example if the Eidolon is lateral.
- Add a row to "Common partial configurations" table.
- Add the Eidolon to the shared-memory model list.
C.5 — CHANGELOG.md (nexus) under [Unreleased] → ### Added:
- <EIDOLON> added/promoted to `shipped` in the roster (v<X.Y.Z>). <One-line why.>
Phase D — Verification
From the nexus root:
jq empty schemas/*.json
yq eval '.' roster/index.yaml > /tmp/roster.json
yq eval -o=json '.' roster/index.yaml > /tmp/roster.json
for name in $(jq -r '.eidolons[].name' /tmp/roster.json); do
jq -e --arg n "$name" '.eidolons[] | select(.name == $n) |
.methodology.name and .methodology.version and .methodology.cycle and
.source.repo and .versions.latest and
.handoffs.upstream and .handoffs.downstream' /tmp/roster.json > /dev/null \
&& echo "$name: ok" || echo "$name: MISSING"
done
EIDOLONS_NEXUS="$(pwd)" bash cli/eidolons list
EIDOLONS_NEXUS="$(pwd)" bash cli/eidolons roster show <name>
find cli -name '*.sh' -type f -print0 | xargs -0 shellcheck -x -S error
shellcheck -x -S error cli/eidolons
bats cli/tests/
cd ../<EIDOLON>
bash install.sh --help > /dev/null
bash install.sh --version
Phase E — PR
Push the feature branch, open PR against nexus main:
git push -u origin feat/roster-<name>-shipped
gh pr create --title "feat(roster): publish <NAME> v<X.Y.Z> (<one-line role>)" --body "$(cat <<'EOF'
## Summary
- Promotes <NAME> from in_construction to shipped (or: adds <NAME> to the roster as shipped).
- Adds to the `full` preset [+ any new presets].
- [Schema change: new `<class>` capability_class — backward-compatible enum extension]
- [Lateral wiring: adds <name> to every other Eidolon's lateral array]
Handoffs are <lateral-only | pipeline> per composition.md.
Upstream <NAME> is tagged v<X.Y.Z> on main (Rynaro/<NAME>@<sha>). The roster-health matrix already includes <name> — this PR activates EIIS conformance + install.sh --help smoke against the upstream repo on every push and nightly.
## Test plan
- [x] jq empty schemas/*.json clean
- [x] yq parse ok; CI validate-roster loop all `ok`
- [x] eidolons list + roster show <name> render correctly
- [x] shellcheck clean, bats N/N passing
- [x] upstream install.sh --help / --version / --dry-run clean
- [ ] CI roster-health green post-merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
Flow 2: Bumping an existing shipped Eidolon's version [BUMP]
Branch: fix/roster-<eidolon>-<version-dashes> (example: fix/roster-spectra-4-2-8).
-
Verify the upstream tag exists: gh release view v<X.Y.Z> --repo Rynaro/<NAME> or git ls-remote --tags git@github.com:Rynaro/<NAME>.git | grep <version>.
-
Edit roster/index.yaml — only two/three lines change:
versions:
latest: "<new-version>"
pins:
stable: "<new-version>"
Also bump methodology.version: if the major.minor changed.
Bump updated_at: at the top.
-
(Preferred) Run Roster Intake from the nexus Actions tab with the
Eidolon name + version. It auto-edits versions.latest,
versions.pins.stable, and versions.releases.<new-version> from the
upstream release-manifest.json and opens a draft PR. Skip the manual
roster edit when the upstream Eidolon ships signed releases via
eidolon-release-template.yml.
-
(Manual fallback) If the upstream Eidolon hasn't adopted the release
workflow, edit the roster as above. Compatibility-mode warnings will
surface in roster-health.yml until release metadata is added — that's
intentional, not a regression.
-
CHANGELOG.md (nexus) under [Unreleased] → ### Added or ### Changed:
- <NAME> v<X.Y.Z> published in the roster. <One-line release summary if non-trivial.>
-
Verification — same commands as Phase D above.
-
PR: commit message fix(roster): publish <NAME> v<X.Y.Z>, PR title matches.
No other touchpoints. Schema doesn't change; docs (README/MANIFESTO/composition) don't change unless the version bump introduces new capabilities worth surfacing.
Reconciliation: remote moved during your work
If you pushed to the upstream Eidolon repo and got ! [rejected] main -> main (fetch first) because someone merged your PR via GitHub UI in parallel, do not force-push. Instead:
git fetch origin
- Compare your local main against origin/main:
git log --oneline --left-right main...origin/main. If origin has the same work via a different merge commit (e.g. PR merge via UI), your local merge commit is redundant.
- Identify which of your commits aren't on origin: typically a
chore(release): bump … or similar.
git reset --hard origin/main — safe only if those commits are preserved on another branch (like your feature branch). Verify first: git log --oneline <feature-branch>.
- Delete the stale tag locally if it pointed into the discarded merge commit:
git tag -d v<X.Y.Z>.
- Cherry-pick the missing commit(s) onto the reset main:
git cherry-pick <sha>.
- Re-tag:
git tag -a v<X.Y.Z> -m "…".
- Push main and tag.
This happened in PR #17 (FORGE v1.1.1) — see the git log for eec2d6e (UI merge of PR #1) followed by ce66734 (cherry-picked version bump). Document any similar incident in the nexus CHANGELOG if it affects consumers.
Anti-patterns (have burned us before)
- Editing
main directly in the nexus. Always a feature branch + PR. Direct pushes violate the established pattern.
- Loose capability_class values.
capability_class is an enum, not free text. Pick from the current list or extend the schema explicitly.
- Forgetting the CI matrix. CI is hardcoded per Eidolon name at
roster-health.yml line 69. Adding an Eidolon without matrix update means no CI coverage.
- Pipeline-style handoffs for lateral specialists. FORGE's own
AGENTS.md declares pipeline-style upstream/downstream because that's how FORGE sees its own role — but the nexus roster is authoritative for composition, and composition.md calls for lateral-only. Keep the roster lateral for reasoners/debuggers.
- Not running shellcheck and bats before pushing. The CI on Ubuntu + macOS (bash 3.2) catches what the dev box misses — but you can catch most of it locally first.
- Rewriting historical CHANGELOG entries. When closing
[Unreleased] as a version, leave prior dated entries alone. Only move the heading down and add a fresh [Unreleased].
- Lowercase canonical repo names.
Rynaro/APIVR-Delta is canonical, not Rynaro/apivr-delta. source.repo is an identity key.
Worked examples in git history
- FORGE v1.1.1 (new shipped) — PR #17 (
feat(roster): publish FORGE v1.1.1). Full new-Eidolon flow: install.sh EIIS-1.0 rewrite, version drift resolution (footer sync G-12/G-13), in_construction → shipped promotion, full preset addition.
- VIGIL v1.0.1 (new shipped) — PR #TBD (
feat(roster): publish VIGIL v1.0.1). New capability_class (debugger), lateral specialist wiring (touches every other Eidolon's lateral array), new diagnostics preset.
- SPECTRA 4.2.7 → 4.2.8 (version bump) —
fix/roster-spectra-4-2-8. Minimal roster-only change.
Follow whichever worked example most closely matches your current task.