Releases: EvoMap/evolver
v1.84.0
v1.83.0
[1.83.0] - 2026-05-16
Added — Publish → recall round-trip verification (#53, #56)
After every successful Hub publish (Capsule, AntiPattern, or SkillBundle),
the daemon now confirms that the asset can actually be recalled from
Hub. A new src/gep/recallVerifier.js worker runs Phase 2 deterministic
asset_id lookups on a sampling of published assets, with exponential
backoff to absorb indexing latency. Outcomes (roundtrip_ok /
roundtrip_missing / roundtrip_mismatch / verification_skipped) land
in memory_graph.jsonl as kind=recall_verify events.
A new scripts/recall-verify-report.js aggregates those events into a
Markdown table grouped by asset type with success rate + p50/p95/p99
latency. Exit code is a ship-gate: 0 when every asset type meets ≥ 0.95
success and zero mismatches, exit 2 otherwise. Designed to plug into
deploy.sh as a pre-publish guard.
This closes the "daemon knows results" theme set up by v1.82.0's open-PR
overlap detection: v1.82.0 made the daemon aware of in-flight work so it
stops re-inventing it; v1.83.0 makes the daemon aware of whether its own
uploaded experience is actually retrievable later. Together they bound
the daemon's two main blind spots — "what other people are doing" and
"whether what I just uploaded actually stuck."
Architecture
| Layer | Behavior |
|---|---|
src/gep/recallVerifier.js (new) |
Bounded ring queue (default 256), single-flight by asset_id, sample-rate gate, async setInterval worker (unref'd so process exit is unblocked). Retries on missing with [5s, 15s, 60s] backoff up to 3 attempts. |
src/gep/hubSearch.js |
Phase 2 fetch logic extracted into a reusable fetchAssetById(assetId, opts) helper. Cache-first ordering preserved so hot payloads return instantly even when the search-phase budget is exhausted. |
| Three publish hooks | solidify.js capsule + anti-pattern, skill2gep.js publishBundleChannel. Each enqueues only after res.ok && !res.dry_run, and uses the sanitized capsule's asset_id so the verifier looks up the same hash the Hub indexed. |
scripts/recall-verify-report.js (new) |
Markdown report with monotonic gate severity (RED never downgrades to YELLOW). |
index.js |
[RecallVerify] ENABLED/DISABLED startup banner, starts the worker once, with sample-rate range clamping. |
Mismatch detection
verifyOnce recomputes computeAssetId on the recalled body and
compares to results[0].asset_id. A mismatch means the Hub re-encoded or
corrupted the asset between publish and fetch — surfaced as
roundtrip_mismatch and flips the gate to RED.
Configuration (all default-on)
| Env var | Default | Purpose |
|---|---|---|
EVOLVE_RECALL_VERIFY |
1 |
Master switch |
EVOLVE_RECALL_VERIFY_SAMPLE_RATE |
1.0 |
Fraction of publishes to verify (clamped to [0, 1]) |
EVOLVE_RECALL_VERIFY_QUEUE_MAX |
256 |
Ring queue size |
EVOLVE_RECALL_VERIFY_INITIAL_WAIT_MS |
5000 |
First-attempt delay |
EVOLVE_RECALL_VERIFY_POLL_MS |
5000 |
Worker tick rate |
EVOLVE_RECALL_VERIFY_ATTEMPTS |
3 |
Retries on roundtrip_missing |
EVOLVE_RECALL_VERIFY_FETCH_TIMEOUT_MS |
8000 |
Phase 2 timeout |
Hub mirror staging
recall_verify is intentionally not in HUB_SYNC_KIND_ALLOWLIST on
first ship — local-only until Hub side confirms it accepts the schema.
One-line follow-up patch enables mirroring later.
Hardened — five Bugbot review iterations on the publish/verify
data flow
PR #53 + #56 went through five Cursor Bugbot rounds. Each surfaced a
real defect that would have shipped silently:
- schemas/protocol obfuscation gap.
recallVerifier.jswas missing
frompublic.manifest.json:obfuscate; would have shipped in plaintext
to npm. - Phase 2 cache bypass regression. Refactored time-budget check
ordered before cache lookup, so hot payloads were silently re-fetched
(or worse, dropped) under deadline pressure. - Gate severity could downgrade.
recall-verify-report.js's gate
loop overwrote later rows, so AntiPattern@RED followed by Capsule@YELLOW
reported YELLOW — misleading dashboards even though exit code was
correct. Replaced with monotonic escalation (RANK ordinal). - Wrong asset_id used for recall lookup.
solidify.js's capsule and
anti-pattern paths enqueued the pre-sanitizeasset_idfor
verification, but Hub indexes by the sanitized hash. Any PII
redaction would have produced persistent falseroundtrip_missing
results, dragging the ship gate toward RED forever. Both paths now
recompute on the sanitized capsule and pass that hash to the verifier. - Sample-rate banner-vs-implementation drift.
index.jsclamped
sample rate to [0, 1] but_getSampleRate()did not, so a negative
env value silently disabled all verification while the banner still
reported 1.0. - Dry-run guard masked Hub rejections.
else if (res && res.dry_run)
classified{ok:false, dry_run:true}(a real Hub reject during
dry-run) as a clean dry-run skip. Now requiresres.ok && res.dry_run.
Notes for operators
- This release is enabled-by-default; no opt-in required to start
collecting recall_verify events. To disable, set
EVOLVE_RECALL_VERIFY=0before the daemon starts. - A 30-minute real-Hub daemon smoke +
recall-verify-reportship-gate
read is the recommended pre-deploy ritual once a non-trivial volume of
publishes has accumulated. - Memory graph events with
kind=recall_verifyare local-only this
release; the next release will add them to the Hub mirror allowlist
once Hub-side schema acceptance is confirmed.
v1.82.1
[1.82.1] - 2026-05-16
Fixed — P0: force-update no longer overwrites user projects (#51, #52)
executeForceUpdate previously used getRepoRoot() to decide which
directory to update. getRepoRoot() preferentially returns the user's
surrounding git project — correct for evolution signals, catastrophic
for "delete everything except .git/node_modules/memory/MEMORY.md and copy
the evolver tarball on top of it." When the Hub pushed a force_update
directive (via heartbeat or event poll) to a node whose process.cwd() was
inside any other git repository, the deletion loop ran inside that repo
and the user lost their files.
This was reproducible end-to-end via cd /tmp/anything-with-.git; trigger executeForceUpdate — the user's files were replaced with the
evolver package contents and the user's package.json was rewritten to
@evomap/evolver. We confirmed the bug actually triggered against
evolver-private-dev itself during fix development (recovered via
git reset --hard HEAD) — that's the strongest possible evidence for
the severity.
The fix introduces a new paths.getEvolverInstallRoot() that returns
the evolver package directory (path.resolve(__dirname, '..', '..'))
regardless of process.cwd() or EVOLVER_REPO_ROOT. forceUpdate.js
now uses this INSTALL_ROOT for every read, delete, and copy. A
defense-in-depth guard refuses the update entirely if
INSTALL_ROOT/package.json does not name @evomap/evolver — so any
future path-resolution regression fails closed instead of wiping an
unrelated directory. The staging tmp directory also moves to
os.tmpdir() because INSTALL_ROOT's parent is typically not writable
under a global npm install.
Recommended action: upgrade immediately. Anyone running
evolver --loop from inside any git repository is at risk under
v1.82.0 and earlier.
Credit: issue reported by cloudcarver, codex POC reproducer
diagnosed by cloudcarver, fix landed in #52.
v1.82.0
[1.82.0] - 2026-05-16
Added — daemon avoids re-inventing work from open PRs (#50)
The evolver --loop daemon now sees the current repo's open-PR list and
automatically rolls back any cycle whose changed files substantially
overlap with one. This closes a class of failure observed during the
v1.81.0 development window where the daemon independently re-implemented
work that was already in flight on PRs #38 and #43, dirtying the working
tree and tripping the rollback risk documented in the daemon-rollback
feedback. New module src/gep/openPRRegistry.js wraps gh pr list with
in-process caching and graceful degradation (missing gh, unauthenticated,
API errors all yield [] with a single warn line — never blocks a cycle
on its own infrastructure failures).
Two layers of defense, configurable independently:
- File-level rollback (the hard guard). At solidify time, after the
LLM has finished editing, the daemon checks whether
blast.changed_filesoverlap any open PR's files at >= 70% of the
daemon's own changes. On match,open_pr_overlap:#N:RATIOis pushed
ontoprotocolViolations, which feeds the existingsuccessformula
and triggers the establishedrollbackTracked+
rollbackNewUntrackedFilespath. The cycle's edits are reverted before
the working tree gets dirty.EvolutionEvent.outcome.skip_reason = 'open_pr_overlap'plus apr_overlapsub-object are written to the
memory graph and (via the existingoutcomeallowlist) mirrored to
the Hub recall stream, so subsequent cycles in the same selector
context can dedupe without re-walkinggh. - Token-level prompt hint (the early warning). At gene-selection
time, the daemon checks whether the selected gene'ssignals_match
token-overlaps any open PR's title or branch above 50%. On match, a
new "Open PR Hint" block is injected into the LLM prompt warning that
if the planned changes touch overlapping files the cycle will be
rolled back. Advisory only — the LLM may still proceed, but a thinking
model can pick a different gene before wasting compute.
Why solidify and not select for the file-level check: mutation.target
is gene:gene_xyz or behavior:protocol, not file paths. The selector
does not predict changed files. Only solidify (after LLM execution) has
the actual diff. Putting the abort there is "undo cycles that turn out
to overlap" rather than "block cycles that might overlap" — same
outcome, lower false-positive rate, and reuses the established rollback
infrastructure (which already handles baseline-untracked-aware deletion
and tracked-file restore).
Configuration
Three new environment variables, all default-on with conservative thresholds:
| Env var | Default | Purpose |
|---|---|---|
EVOLVE_OPEN_PR_DEDUP |
1 |
Master switch; 0 fully disables both layers |
EVOLVE_OPEN_PR_TTL_MS |
60000 |
Open-PR list cache TTL (per process) |
EVOLVE_OPEN_PR_OVERLAP_ABORT |
0.7 |
File overlap ratio above which solidify rolls back |
Notes for operators
- Cycles aborted by overlap leave no working-tree residue — the
established solidify rollback path handles cleanup. If you saw "daemon
silently dirtied my tree mid-edit" before, this should fix it. - Memory graph events with
outcome.skip_reason='open_pr_overlap'are
intentionally short-lived signals; they're not displayed in any UI by
default but are observable viagep_recallfrom the MCP server. - If you operate without GitHub auth (e.g. air-gapped), set
EVOLVE_OPEN_PR_DEDUP=0to short-circuit the entire feature. Without
this, the daemon will still cycle normally but emit one
[OpenPR] gh CLI not availablewarning at startup.
v1.81.0
[1.81.0] - 2026-05-16
This minor bump bundles two structural-hardening PRs that grew out of the
v1.80.7 → v1.80.9 development cycle. No new user-facing features —
everything here makes the existing protocol and release pipeline more
robust against the failure modes those releases revealed.
Added — single source of truth for prompt-vs-code enums (#45)
src/gep/schemas/protocol.js is now the canonical home for every string
literal enum that lives both in JS validation code AND in the LLM-facing
prompt schema: VALID_CATEGORIES (re-exported from gene.js),
VALID_OUTCOME_STATUSES (from capsule.js), VALID_RISK_LEVELS,
VALID_TRACE_STAGES. Plus renderEnum() / renderEnumList() helpers.
prompt.js's SCHEMA_DEFINITIONS is no longer a const string with the
enum literals baked in — buildSchemaDefinitions() interpolates from
protocol.js at load. mutation.js's isValidMutation and
normalizeMutation import the enums instead of inlining them. The
stale "keep this in sync with schemas/gene.js" warning comment in
solidify.js is removed because the rule is now structural.
A new test/schemaPromptConsistency.test.js adds 4 cases that fail loudly
if any future contributor reintroduces a hardcoded enum literal in
prompt.js, drifts protocol.js from the underlying schema files, or
removes a category from the canonical whitelist without auditing
strategy.js presets. Fundamentally closes the bug class that produced
the v1.80.7 → v1.80.8 explore-mode-drift incident.
Added — cooperative yield via .evolver.lock (#46, P1-1)
The evolver --loop daemon now honors a cooperative file lock at
<repo>/.evolver.lock. Whoever creates the file (the user, a release
script, an IDE refactor task) owns the cycle for as long as it lives. The
daemon yields the entire cycle without touching the working tree. Stale
locks older than EVOLVE_USER_LOCK_TTL_MS (default 1h) are auto-ignored
so a forgotten file does not permanently dormant the daemon. NaN /
non-positive / suspiciously-small (< 1000ms) TTL values are rejected
with a clear console diagnostic, so users who write
EVOLVE_USER_LOCK_TTL_MS=5m (which parseInt quietly turns into 5)
see why the lock is being ignored.
Added — release-window quiet period (#46, P2-5)
If the most recent commit on the current branch matches
/^chore\(release\)/i AND was committed less than
EVOLVE_RELEASE_WINDOW_MS ago (default 5min), the daemon yields the
cycle on its own — no manual lock needed. Stops the daemon from
auto-creating fix/* branches or modifying solidify.js mid-deploy
when publish_public.js's ensureClean() is about to assert a clean
working tree. Same NaN / parseInt-prefix / clock-skew protection as
the user lock above.
Added — Hub mirror startup diagnostic (#46, P1-2)
Daemon startup now emits one explicit [HubMirror] line saying whether
outcome / attempt / solidify / skill_emit events will be mirrored to
<hub>/a2a/memory/event (default ON via MEMORY_GRAPH_SYNC_HUB). For
the inactive case the line names which credential is missing (hub URL,
node_id, or node_secret). No behavior change — the mirror was already
on by default — but the state is now observable.
Hardened — scripts/deploy.sh fails fast before npm publish (#45)
Step 5 (publish_public.js) and step 7 (npm publish) used
|| echo "WARN" to swallow failures, which let a failed GitHub Release
step be followed by a successful npm publish — leaving the registry
pointing at a version with no GitHub release. npm publish cannot be
undone, so this was a real one-way state. v1.80.8 actually hit this when
publish_public.js failed with a missing-git-identity error in the
temp publish repo, but deploy continued and shipped npm@1.80.8 anyway.
Two layered defenses now apply: (1) step 5 exits 1 with an actionable
error if publish_public.js returns non-zero, instead of WARN-and-continue;
(2) step 7 runs gh release view v$VERSION as a precondition before
invoking npm publish and refuses to ship if the GitHub Release is
missing or in draft state. Step 7 itself also fails fast if npm publish
errors. Step 6 (binary upload) intentionally keeps WARN-and-continue
because it is recoverable via gh release upload --clobber.
Why minor not patch
This release introduces two new env-var contracts (EVOLVE_USER_LOCK_TTL_MS,
EVOLVE_RELEASE_WINDOW_MS), one new operational primitive (.evolver.lock),
and a new public schema module (src/gep/schemas/protocol.js). All are
backwards-compatible additions, but operators may want to know the contract
surface grew. patch would have understated the change.
v1.80.9
[1.80.9] - 2026-05-16
Fixed
- GEP prompt schema enums now flow from a single source of truth (#45,
follows up #42). v1.80.8 patched three hardcodedrepair|optimize|innovate
literals insrc/gep/prompt.jsafter theexplorecategory had been
silently dropped from the LLM contract for months. This release makes the
whole class of bug structurally impossible: a newsrc/gep/schemas/protocol.js
is the single source of truth for every enum that appears both in JS
validation code (mutation.js) and in the LLM-facing prompt schema
(prompt.js). It re-exportsVALID_CATEGORIESfromschemas/gene.js,
VALID_OUTCOME_STATUSESfromschemas/capsule.js, plus declares
VALID_RISK_LEVELSandVALID_TRACE_STAGES.prompt.js's
SCHEMA_DEFINITIONSis now generated bybuildSchemaDefinitions()which
interpolatesrenderEnum()/renderEnumList()calls instead of
hardcoding each value.mutation.js'sisValidMutationand
normalizeMutationimport the enums instead of inlining them. The
stale "keep this in sync with schemas/gene.js" warning comment in
solidify.jsis removed because the rule is now enforced by the type
system, not by reader vigilance. A new test
test/schemaPromptConsistency.test.jsadds 4 cases that fail loudly if
any future contributor reintroduces a hardcoded enum literal in
prompt.js, driftsprotocol.jsfrom the underlying schema files, or
removes a category from the canonical whitelist without auditing
strategy.jspresets.
Hardened
scripts/deploy.shnow fails fast before the irreversible
npm publishstep (#45). The previous pipeline used
|| echo "WARN: ..."after step 5 (publish_public.js) and step 6
(binary upload), which let a failed GitHub Release step be followed by
a successfulnpm publish— leaving the registry pointing at a version
with no GitHub release.npm publishcannot be undone, so this was a
real one-way state. v1.80.8 actually hit it whenpublish_public.js
failed with a missing-git-identity error in the temp publish repo, but
deploy continued and shipped npm@1.80.8 anyway. Two layered defenses
now apply: (1) step 5 exits 1 with an actionable error if
publish_public.jsreturns non-zero, instead ofWARN-and-continue;
(2) step 7 runsgh release view v$VERSIONas a precondition before
invokingnpm publishand refuses to ship if the GitHub Release is
missing or in draft state. Step 7 itself also fails fast ifnpm publisherrors. Step 6 (binary upload) intentionally keeps
WARN-and-continue because binary upload is recoverable via
gh release upload --clobberand missing binaries do not put the
registry in an inconsistent state.
v1.80.8
[1.80.8] - 2026-05-16
Fixed
- GEP prompt: expose
exploreas a valid mutation/intent/category to the
LLM (#42). Theexploreevolution mode was already wired through
schemas/gene.js, every preset instrategy.js(with non-zero ratios:
10% inbalanced, 15% insteady-state, ...), andmutation.js
(validation, normalization, signal-driven trigger, expected-effect
copy). But the GEP prompt's mandatory schema literals only listed
repair|optimize|innovateforMutation.category,EvolutionEvent.intent,
andGene.category. Net effect: agents could not emit explore-class
objects even when the active strategy biased toward exploration -- the
category was structurally unreachable through the prompt contract.
Three schema literals insrc/gep/prompt.jsare nowrepair|optimize|innovate|explore,
and directive 3 (Execution) describes the explore semantic: scan code /
external knowledge, surface opportunities as new signals or low-risk
discovery Capsules, no premature edits. Aligns with the long-standing
warning atsrc/gep/solidify.js:355("hardcoding the list inline used to
drop 'explore' silently -- keep this in sync with schemas/gene.js").
Documentation
- README.zh-CN.md: strategy table covers all 6 presets x 4 intents.
The Chinese README's strategy table previously listed only 3 intents
(repair/optimize/innovate) for 4 presets, and omitted bothearly-stabilize
andsteady-stateentirely. It now matchesstrategy.jsexactly: 6 presets
(balanced/innovate/harden/repair-only/early-stabilize/steady-state)
x 4 intents (with the realexploreratios, e.g. 15% forsteady-state).
Adds a 4-line glossary explaining what each intent does, and updates the
EVOLVE_STRATEGYenv var entry to list all six legal values.
v1.80.7
v1.80.7
Bug fixes / UX
- Resolved confusion reported in #531: the opencode adapter generates a server plugin (event hooks), and opencode's TUI "Plugins" tab only lists TUI plugins (registered via tui.json). Server plugins are auto-loaded from .opencode/plugins/ and run silently. The install message now spells this out so users do not assume the plugin failed to load.
- Added
evolver setup-hooks --platform=opencode --verify(read-only). Reports plugin file presence, evolver-managed marker, requireability, hook script presence, and AGENTS.md section. Returns non-zero exit code on any failed check. - Install result now includes
plugin_pathfor downstream tooling.
Compatibility
- No runtime behavior change for the plugin itself; existing installations continue to work without re-installing.
v1.80.6
Bug fixes
- Validate Gene/Capsule before persisting and publishing.
upsertGene,upsertCapsule,appendCapsuleand the publish builders now run the canonical schema validators and emit aconsole.warnon failure. Validation is warn-only on the client; the hub re-validates server-side. (#31, audit issue #30 H1) - Compute
Capsule.asset_idexactly once per cycle. A previous code path calledupsertCapsuletwice -- first with a staleasset_id(computed beforesuccess_streakanda2awere finalized), then again with the correct one. The duplicate write is now removed;appendEventJsonl(event)runs beforecomputeCapsuleSuccessStreakso the just-completed event still counts toward its own streak. (#31, audit issue #30 H5) - Bound
readAllEventsmemory footprint. Long-running daemons accumulated dozens of MB inevents.jsonl, causing heap spikes on every successfulsolidifycycle.readAllEventsnow stat()s the file and tail-reads when above the configurable cap (EVOLVER_EVENTS_FULL_READ_MAX_BYTES, default 2MB). The first-line discard only triggers when the read truly starts mid-file, never when the chunk covers the whole file. (#31, audit issue #30 H6)
Schemas
- Task schema factory. New
createTask/validateTaskhelpers undersrc/gep/schemas/task.js, mirroring the Gene and Capsule factories shipped earlier. Adopted bytaskReceiver.js. (#28)
Internal-only
- New tunables
EVOLVER_EVENTS_FULL_READ_MAX_BYTESandEVOLVER_EVENTS_TAIL_READ_BYTESfor operators that want to cap memory more aggressively on resource-constrained hosts.
v1.80.5
What's Changed
Schema layer follow-ups to v1.80.4 createGene factory
- Bugbot follow-ups on createGene normalization (PR #26): three small but real defensive fixes the Cursor Bugbot flagged on the v1.80.4 Gene factory: (a) integrity guard so a synthesized gene's
idmatches the asset_id its content hashes to, (b) tighter category fallback so a malformedcategoryfrom an LLM-emitted gene cannot bypassvalidateGene, (c) preserve provenance metadata fields that are not part of the canonical Gene shape (e.g.source_capsule_id) when the factory rebuilds the object. - Capsule schema factory (PR #27): introduces
src/gep/schemas/capsule.jswithcreateCapsule(partial)andvalidateCapsule(c), the Capsule mirror of the Gene factory shipped in v1.80.4. Same mutation-isolation guarantees ascreateGene: array fields (trigger,strategy,execution_trace) are always freshly sliced per call, so downstream.push()in the solidify path can never leak across capsule instances. Three capsule-construction sites insrc/gep/solidify.jsmigrated to use the factory; legacy inline normalization removed.
Tests
- 27 unit tests for the Capsule factory (defaults, normalization, idempotency, mutation isolation, validator errors).
- Updated 2 createGene tests for the Bugbot fixes.
- Full suite: 1343 / 1343 passing on this commit.
Internal improvements and stability enhancements.