Skip to content

Releases: EvoMap/evolver

v1.84.0

17 May 03:25

Choose a tag to compare

Release v1.84.0

v1.83.0

16 May 13:30

Choose a tag to compare

[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.js was missing
    from public.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-sanitize asset_id for
    verification, but Hub indexes by the sanitized hash. Any PII
    redaction would have produced persistent false roundtrip_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.js clamped
    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 requires res.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=0 before the daemon starts.
  • A 30-minute real-Hub daemon smoke + recall-verify-report ship-gate
    read is the recommended pre-deploy ritual once a non-trivial volume of
    publishes has accumulated.
  • Memory graph events with kind=recall_verify are 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

16 May 11:38

Choose a tag to compare

[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

16 May 10:22

Choose a tag to compare

[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_files overlap any open PR's files at >= 70% of the
    daemon's own changes. On match, open_pr_overlap:#N:RATIO is pushed
    onto protocolViolations, which feeds the existing success formula
    and triggers the established rollbackTracked +
    rollbackNewUntrackedFiles path. The cycle's edits are reverted before
    the working tree gets dirty. EvolutionEvent.outcome.skip_reason = 'open_pr_overlap' plus a pr_overlap sub-object are written to the
    memory graph and (via the existing outcome allowlist) mirrored to
    the Hub recall stream, so subsequent cycles in the same selector
    context can dedupe without re-walking gh.
  • Token-level prompt hint (the early warning). At gene-selection
    time, the daemon checks whether the selected gene's signals_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 via gep_recall from the MCP server.
  • If you operate without GitHub auth (e.g. air-gapped), set
    EVOLVE_OPEN_PR_DEDUP=0 to short-circuit the entire feature. Without
    this, the daemon will still cycle normally but emit one
    [OpenPR] gh CLI not available warning at startup.

v1.81.0

16 May 09:20

Choose a tag to compare

[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

16 May 07:15

Choose a tag to compare

[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 hardcoded repair|optimize|innovate
    literals in src/gep/prompt.js after the explore category had been
    silently dropped from the LLM contract for months. This release makes the
    whole class of bug structurally impossible: a new src/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-exports VALID_CATEGORIES from schemas/gene.js,
    VALID_OUTCOME_STATUSES from schemas/capsule.js, plus declares
    VALID_RISK_LEVELS and VALID_TRACE_STAGES. prompt.js's
    SCHEMA_DEFINITIONS is now generated by buildSchemaDefinitions() which
    interpolates renderEnum() / renderEnumList() calls instead of
    hardcoding each value. 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 enforced by the type
    system, not by reader vigilance. A new test
    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.

Hardened

  • scripts/deploy.sh now fails fast before the irreversible
    npm publish step (#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 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 it 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 binary upload is recoverable via
    gh release upload --clobber and missing binaries do not put the
    registry in an inconsistent state.

v1.80.8

16 May 05:57

Choose a tag to compare

[1.80.8] - 2026-05-16

Fixed

  • GEP prompt: expose explore as a valid mutation/intent/category to the
    LLM (#42).
    The explore evolution mode was already wired through
    schemas/gene.js, every preset in strategy.js (with non-zero ratios:
    10% in balanced, 15% in steady-state, ...), and mutation.js
    (validation, normalization, signal-driven trigger, expected-effect
    copy). But the GEP prompt's mandatory schema literals only listed
    repair|optimize|innovate for Mutation.category, EvolutionEvent.intent,
    and Gene.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 in src/gep/prompt.js are now repair|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 at src/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 both early-stabilize
    and steady-state entirely. It now matches strategy.js exactly: 6 presets
    (balanced/innovate/harden/repair-only/early-stabilize/steady-state)
    x 4 intents (with the real explore ratios, e.g. 15% for steady-state).
    Adds a 4-line glossary explaining what each intent does, and updates the
    EVOLVE_STRATEGY env var entry to list all six legal values.

v1.80.7

09 May 10:56

Choose a tag to compare

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_path for downstream tooling.

Compatibility

  • No runtime behavior change for the plugin itself; existing installations continue to work without re-installing.

v1.80.6

09 May 09:04

Choose a tag to compare

Bug fixes

  • Validate Gene/Capsule before persisting and publishing. upsertGene, upsertCapsule, appendCapsule and the publish builders now run the canonical schema validators and emit a console.warn on failure. Validation is warn-only on the client; the hub re-validates server-side. (#31, audit issue #30 H1)
  • Compute Capsule.asset_id exactly once per cycle. A previous code path called upsertCapsule twice -- first with a stale asset_id (computed before success_streak and a2a were finalized), then again with the correct one. The duplicate write is now removed; appendEventJsonl(event) runs before computeCapsuleSuccessStreak so the just-completed event still counts toward its own streak. (#31, audit issue #30 H5)
  • Bound readAllEvents memory footprint. Long-running daemons accumulated dozens of MB in events.jsonl, causing heap spikes on every successful solidify cycle. readAllEvents now 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 / validateTask helpers under src/gep/schemas/task.js, mirroring the Gene and Capsule factories shipped earlier. Adopted by taskReceiver.js. (#28)

Internal-only

  • New tunables EVOLVER_EVENTS_FULL_READ_MAX_BYTES and EVOLVER_EVENTS_TAIL_READ_BYTES for operators that want to cap memory more aggressively on resource-constrained hosts.

v1.80.5

09 May 05:33

Choose a tag to compare

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 id matches the asset_id its content hashes to, (b) tighter category fallback so a malformed category from an LLM-emitted gene cannot bypass validateGene, (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.js with createCapsule(partial) and validateCapsule(c), the Capsule mirror of the Gene factory shipped in v1.80.4. Same mutation-isolation guarantees as createGene: 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 in src/gep/solidify.js migrated 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.