All notable changes to this project are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
gep/trajectory/(new package): ports the core oftrajectoryExport.test.js.builder.py—build_trajectories()/build_trajectory_from_rows(): group proxy-trace rows by session intoevomap.coding_trajectory.v1trajectories; per-turn extraction (provider, endpoint, response_id, previous_response_id, request/response bodies, reasoning, encrypted_content, per-turn tokens, error); tool-call extraction across Anthropic/v1/messages, OpenAI/v1/responses,/v1/chat/completions(declared tools vs actual invocations; Anthropictool_usededuped by id); full streamed tool-argument reconstruction (Anthropicinput_json_delta; OpenAI Chat delta + full-snapshot dedup; OpenAI Responses delta +.doneoverride); Bedrock provider normalisation; language detection (keywords + file extensions); failure-correction marking; test-execution / code-edit /test_commandsdetection from tool inputs; stats (turns, tokens,has_tool_calls,tool_call_count,tool_types,has_test_execution,has_code_edit,test_commands).io.py—write_trajectories(): atomic (temp +os.replace); a pre-placed symlink is not followed (PR #294 C4); owner-only0o600on POSIX.crypto.py—read_trace_rows_detailed()/decrypt_trace_row(): AES-256-GCM under a node-secret-derived key withsecret_versionkeyring selection; RSA-OAEP-SHA256 hub-key envelope unwrap with node-secret fallback; fail-closed (3 distinct messages) unless--allow-partial.sources.py— non-proxy session logs: Codex rollout JSONL (session_meta+response_itemrecords — message/reasoning/function_call/ function_call_output/custom_tool_call/tool_search*), Claude Code transcript JSONL (user/assistantwithmessage.contentblocks), and OpenAI generic-chat messages JSONL (top-level role-tagged records,prompt_tokens/completion_tokens,thinking+signature, OpenAItool_calls/tooloutputs) — with reasoning turns, custom tool calls, tool-search events, and test-execution / code-edit / failure-correction detection; plusdetect_source()classification.- CLI:
evolver trajectory --input <file|dir> --output [...]auto-detects session logs vs proxy traces, recurses directories, and decrypts with--node-secret/--hub-private-key/--node-secret-keyring/--allow-partial.
tests/gep/trajectory/(27 cases: 11 builder incl. full streaming + 10 crypto + 6 sources).- Deferred (niche vendor sources): Cursor vscdb (SQLite), Gemini CLI+Gateway, Kimi Wire — bespoke low-frequency parsers, each with its own format-specific test file.
force_update.py(262→490+ lines): ports the portable subset of Node'sforceUpdate*.test.js(Node-specific npx/degit/package.json/index.js/exit-78 mechanics are N/A for Python and intentionally omitted).- Sentinels —
FORCE_UPDATE_BUSY/FORCE_UPDATE_NOOP(distinct singletons; identity-comparable, no truthy collision). - Concurrency guard — module-level mutex in
execute_force_update(): a re-entrant call mid-upgrade returnsFORCE_UPDATE_BUSYwithout re-downloading; mutex resets viafinally(and on throw). (Fills a gap: the docstring claimed a file-lock guard that was never implemented.) - Idempotent floor —
required_versionis a minimum floor, not an exact target: operator (>=/>/=) + leading-vnormalisation; an install that already satisfies the floor returnsFORCE_UPDATE_NOOP(no downgrade, no re-download). Anti-downgrade guard (#213): an unparsable current version is refused, not silently satisfied. - Coded frozen failures — every failure is an immutable result carrying a
stable
code+detail;is_force_update_failure()+FORCE_UPDATE_FAIL_CODESregistry. - Safe extraction —
_safe_extract()refuses Zip-Slip (path-traversal) entries (keep-list/tarball-fallback safety). report_force_update_outcome(noop/updated)persists status (skipped/success);noopwins defensively.
- Sentinels —
tests/test_force_update.py(+19 cases).
proxy/sync/outbound.py(108→290+ lines): portsproxyOutboundSync.test.js.- Body-size budgeting — one size-bounded batch per flush
(
EVOMAP_OUTBOUND_SYNC_MAX_BODY_BYTESenv, overridable by store state after a 413); a single message that cannot fit is rejected, not sent. - 413 handling — single-message 413 quarantines; multi-message 413 backs the budget down and leaves all messages pending (1 Hub call).
- Retryable vs terminal — retryable per-message failures defer (status
pending, retry count untouched,
next_retry_atset); terminal finalises.terminalwins over retry hints (PR #301). - proxy_trace gating —
proxy_tracedropped whentrace_collection_enabledstore state isFalse. - Redaction — Hub non-2xx response text redacted before persistence.
- Rich result shape:
sent/synced/dropped/deferred/payload_too_large/error/responses.
- Body-size budgeting — one size-bounded batch per flush
(
proxy/mailbox/store.py:Message.next_retry_atfield;poll_outboundskips deferred-not-due messages; newdefer()(backoff without burning retry).tests/test_proxy_outbound_sync.py(new, 11 cases);test_proxy_sync.pyupdated to Node v1.90.0sent=batch-size semantics.- Encryption-envelope validation of
proxy_tracepayloads deferred to G10.1.
gep/host_error_classifier.py(new):is_host_client_error()+ non-globalHOST_PROVIDER_ERR_RE— classifies 4xx provider errors (invalid_api_key / insufficient_quota / rate limit / MaxTokens / HTTP 4xx) with bare-number-safe context.None/non-str/empty →False.gep/signals.py: under a host client error the failure-streak path is skipped — noban_gene/failure_loop_detected/consecutive_failure_streak/force_innovation_after_repair_loop; the actionablehost_llm_client_errorsignal is surfaced instead. An LLM quota/auth storm can no longer ban a gene.tests/gep/test_host_error_classifier.py(new, 5 cases): portshostClientErrorSignals.test.js.
solo/subsystem (new):breaker.py(network "no escape valve" hard cut) +git_guard.py(local-git-only guard, wired intogit_ops.run_cmd) +__init__.py(banner). Solo state =EVOLVER_SOLOenv (process-wide, import-race-safe).cli.py:--soloflag (implies--loop); activates before dispatch so env overrides + hub cut land at the source. Even a user-setA2A_HUB_URLis ignored. Validator daemon + ATP auto-spend + task pickup are hard-cut in both the startup path (start_validatorreturnsNone; ATP envs forced off) and the in-cycle path (post_cycleguards).config.py:resolve_hub_url()returns""under solo (no escape valve); newMAX_CYCLES_PER_PROCESS(EVOLVER_MAX_CYCLES_PER_PROCESS, 0=unlimited).evolve/runner.py: daemon loop honoursMAX_CYCLES_PER_PROCESS(exits after N cycles — solo/CI testability).- Fix:
cli._cmd_loopnow guardsadd_signal_handler/remove_signal_handleragainstNotImplementedErrorso--loop/--solowork on Windows (ProactorEventLoop lacks signal-handler support). tests/solo/test_solo.py(new, 11 cases): portssoloMode.test.js, including a subprocess smoke test asserting banner + service cut + clean exit.
- Tests: +73 (5 host-error + 11 solo + 11 outbound + 19 force-update + 27 trajectory); 0 regressions (1 pre-existing cognition test failure unrelated).
- Baseline: tracking v1.89.14 → v1.90.0 (G10.5, G10.2, G10.8, G10.9 closed; G10.1 trajectory core complete — proxy + full streaming + crypto + Codex/Claude/generic sources — Cursor/Gemini/Kimi vendor sources deferred; G10.3 cliContracts / G10.4 recipe pending).
gep/memory_graph.py:stable_no_error/heuristic_delta/predictiveoutcomes now classified as inert — they build no Bayesian confidence and no longer count as successes forpreferredGeneId.- New
_count_trailing_inert(): afterGENE_INERT_BAN_STREAK(=8) consecutive trailing inert outcomes with no real success, the gene is added tobannedGeneIdsso the selector yields null and the pipeline mutates. - A single real success (e.g.
error_cleared) resets the inert streak. - 5 regression tests ported from
test/issue562InertGeneBan.test.js.
proxy/lifecycle/manager.py:parse_node_secret_version(),node_secret_versionproperty (store > env precedence), stale-secret detection (store version < env version → Hub rotated → clear store secret).hello()/heartbeat()persist the Hub-returnednode_secret_version.
proxy/lifecycle/manager.py:_record_hub_unreachable()/_record_hub_reachable()/_hub_unreachable_wait_ms()/hub_unreachable_backoff_ms()— exponential backoff (5s→15min cap) on network errors (ConnectError/TimeoutException).hello()/heartbeat()check backoff before sending and record reachable/unreachable on success/failure.
gep/anti_abuse_telemetry.py:build_heartbeat_anti_abuse()— privacy-preserving envelope with HMAC-pseudonymized device/workspace hashes, source-confidence labels (hub_required/hub_service/hub_observed), integrity hashes, task timing.config.py:ANTI_ABUSE_TELEMETRY_MODE(defaultheartbeat, explicit opt-out).proxy/lifecycle/manager.py: heartbeatmeta.anti_abuseinjection when mode=heartbeat.
config.py:OUTCOME_REPORT_MODE(defaultoff) +outcome_report_mode()resolver (on/enforce/true →on).
proxy/lifecycle/manager.py:_maybe_trigger_force_update_from_heartbeat()withEVOLVER_FORCE_UPDATE_RETRY_COOLDOWN_MS(default 5min) — prevents Hub from hot-spinning force-updates on every heartbeat.
proxy/lifecycle/manager.py:read_pending_last_update()/set_pending_last_update(); heartbeat carrieslast_update_ack+node_secret_versionin payload.
- Tests: 1573 → 1609 (+36 new tests, 0 regressions)
- Baseline: tracking v1.89.11 → v1.89.14 parity on lifecycle + GEP selection
- Fixed
gep/sanitize.pyimport jsonposition bug (was at file bottom, caused NameError). gep/sanitize.py: reverse leak scan now skips path/URL-shaped env values (#568).- Added 6 credential redaction patterns (jwt, aws, github, slack, connection_string, high_entropy).
CHANGELOG.mdupgraded from stub to Keep-a-Changelog format.CONTRIBUTING.md: conventional commits with scope; baseline updated to 1331→1534 tests.
- Rewrote
adapters/scripts/runtime_paths.py(24→315 lines): host-env project dir resolution, workspace-id atomic create with symlink guards, FS-only fallback. - Rewrote
session_start.py(41→211): workspace-scoped memory recall, non-git notice (throttled), dedup, lazy memory read from newest end. - Rewrote
session_end.py(48→214): HEAD~1 diff, workspace-id stamping, Cursor systemMessage suppression. - Rewrote
signal_detect.py(39→160): context-aware stratification, Claude Code payload parsing, multilingual (CJK) fallback. - New
lock_paths.py(52 lines): daemon singleton-lock location + lease staleness tunables. - New
task_recall.py(103 lines):@evolver recalltriggered capsule recall. - Updated
memory_filtering.py: addedfilter_relevant_outcomes(Node contract).
- New
gep/exec_bridge.py(93 lines): Windows npm .cmd shim resolver (CVE-2024-27980). - New
gep/conversation_sniffer.py(240 lines): scan_corpus with local co-occurrence, off/shadow/enforce modes, cooldown, CJK support. - Extended
gep/bridge.py: addeddetermine_bridge_enabled(). evolve/runner.py: Ralph-loop stale bridge-mode break (#559).
- Expanded
genes.seed.json(3→11 genes): full alignment with Node v1.87.0 seed library. - Rewrote
gep/skill2gep.py(187→400+):parse_skill_mdwith frontmatter + CJK sections,infer_categorywith word-boundary matching,skill_to_gene_dictwith asset_id + quality heuristics. - Deepened
gep/idle_scheduler.py(220→300+):EVOLVER_IDLE_OVERRIDE, build-activity detection, FS-only idle fallback. gep/schemas/gene.py: addedavoidand_source(alias) fields.
- New
proxy/router/gemini_route.py(160 lines): Google Gemini API proxy with SSE. - New
proxy/router/vertex_route.py(145 lines): Vertex AI proxy with ADC auth. - New
proxy/router/ollama_route.py(140 lines): local Ollama proxy. - New
proxy/router/responses_route.py(150 lines): OpenAI-compatible API proxy. - New
proxy/router/models_route.py(85 lines):/v1/modelsaggregator. - New
proxy/server/settings.py(62 lines): proxy settings persistence. - New
proxy/trace/extractor.py(65 lines): multi-format token usage extraction. - New
proxy/trace/usage.py(60 lines): usage aggregator singleton. - New
proxy/envelope.py(45 lines): structured message envelope. - New
proxy/inject.py(50 lines): context injection + internal field stripping. - Updated
model_router.py: 5-provider upstream detection.
- Rewrote
atp/cli.py(86→270): 15 subcommands (buy/orders/tasks/claim/deliver/settle/dispute/publish/policy/proofs/tier/order/status/enable/disable). - Deepened
atp/atp_execute.py(87→230): sandbox validation, structured proof building. - Deepened
atp/atp_task_pickup.py(99→200): ROI scoring, capability matching, concurrent limit. - New
gep/mailbox_transport.py(115 lines): proxy mailbox client with auto-start.
- New
gep/token_savings.py(120 lines): token/USD cost savings tracker with monthly reports. - New
gep/narrative_memory.py(85 lines): evolution history narrative compressor. - New
gep/memory_graph_adapter.py(120 lines): advanced queries (success trajectory, failure clustering, fuzzy match). - New
gep/directory_client.py(75 lines): EvoMap directory service client. - New
gep/oauth_login.py(115 lines): OAuth 2.0 device-code flow with keychain integration. - New
gep/claim_nudge.py(75 lines): throttled task-claim suggestion generator. - New
gep/device_id.py(85 lines): cross-platform anonymous hardware fingerprint. - New
gep/anti_abuse_telemetry.py(120 lines): abuse pattern detector (flood/bypass/exhaustion).
- New
experiment/module (4 files): agent_runner, metrics, comparison, cli — controlled A/B evaluation. - New
README.ja-JP.md: Japanese README. - New
README.ko-KR.md: Korean README.
- Tests: 1331 → 1546+ (215+ new tests, 0 regressions)
- Source files: 192 → 217+ (25+ new modules)
- Seed genes: 3 → 11
- Proxy routes: 4 → 9
- ATP subcommands: 5 → 15
- mypy strict: 0 errors across all files
- Baseline comparison: v1.89.2 → tracking v1.89.11
- Initial Python port release tracking Node.js v1.89.2.
- GEP data layer, evolution pipeline, Proxy infrastructure, ATP marketplace (partial), IDE adapters (partial), WebUI (partial).