feat: v1->v2 migration rules, raw-HTTP support, and scan mode - #3
feat: v1->v2 migration rules, raw-HTTP support, and scan mode#3dani1005 wants to merge 1 commit into
Conversation
Adds the everos-cloud 0.4.x -> 1.x (v2 Memory API) migration to the existing v0->v1 skill, and extends it to cover callers that speak HTTP directly. Rules are split into two layers: - migration/http/v1-to-v2.md transport-level, language-agnostic, source of truth - migration/python/v1-to-v2.md maps the Python SDK surface onto those rules Previously only Python SDK users were detected at all: Step 1 grepped for evermemos|everos_cloud, which never matches a raw caller hitting api.evermind.ai. Detection now also matches /api/v1/memories and the EverOS env vars, so a Go/TS/curl caller is covered. SKILL.md changes: - Fix version detection. It keyed on a client.vN. prefix, which 1.x removed entirely (client.add(...)), so a migrated repo was misdetected and re-running the skill was not idempotent. Now keys on the dependency constraint plus bare facade verbs. - Add --scan mode: produce an impact report, edit nothing. - Add the blocker list that must always be flagged and never rewritten, and an impact-report template that leads with those blockers. Findings verified against the published 0.4.1/1.0.0/1.1.0 wheels, the v2 OpenAPI contract, and live prod calls (2026-09-04) — two of which contradict the current public migration guide: - 1.x does NOT read EVEROS_API_KEY (api_key is a required arg); the guide says it still does. - 1.x does NOT read EVER_OS_BASE_URL either. This one fails silently: a client that pointed at dev/test via the environment starts hitting production. Also undocumented: AsyncEverOS is gone, as are max_retries/http_client/ default_headers (0.4.x retried twice by default, 1.x does not retry). Marketplace renamed everos-plugins -> everos-tools. The GitHub repo rename is a separate manual step; docs.evermind.ai links need updating with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Validated this PR end-to-end instead of reviewing the rules on paper: built a throwaway v1 caller The mechanical half is solid: version detection landed on v1 (dependency constraint + 29 1. Defects found1.1 Task polling has no rule — this one breaks the migrated code (blocking)The migrated suite raised on the first async write: Two independent changes, neither covered by any rule: (a) The task id is gone from the add response. Verified on the wire: {"data": {"message_count": 1, "status": "queued"}, "request_id": "0217893676410010..."}
(b) The status vocabulary changed, and this half fails silently. v2 is if response.status in ("completed", "failed", "error"): # never true on v2turns a finished task into an apparently-unfinished one and the poll runs to its timeout. Nothing raises. Related: 1.2
|
| Gap | What the model did | Its own comment |
|---|---|---|
A removed symbol's import (AsyncEverOS) |
moved it into the function body so import memclient keeps working |
"the import is deliberately function-local so that import memclient still works and only calling this fails" |
| 1.x validates locally with pydantic | accepted (EverOSAPIError, ValueError) in the error tests |
"which the SDK-012 rule does not cover" |
| Tests covering a removed capability | marked them skip with the migration reason |
— |
search(memory_types=) removal (§1.4) |
dropped it, documented "filter on the way out" | — |
Note the shape of this: the skill left exactly two comments saying the rules do not cover something,
and one of those two spots (task polling, ingest.py) is the one that broke. The gaps are real and the
model can identify them — which argues for writing them into the rule files rather than re-rolling the
dice each run. It also makes a useful self-check: wherever that phrasing appears in the output, that is
the line a human should review.
Worth calling out that (a) py_compile sees none of §1.1 or the dead-import case, and (b) a stale
top-level import of a removed symbol takes down the entire module — including the paths that migrated
cleanly.
2. Suggested fixes
Ordered by the defect they close. I have these implemented as a patch against this branch
(3 files, +144 / −6, no change to the skill's flow) — happy to push it as a branch if useful.
2.1 → §1.1 Add a task-polling rule to both files
migration/http/v1-to-v2.md — new API-018: Async task polling covering: the add response carries
no task id (with the JSON above); the id is the envelope's request_id; GET /api/v2/tasks/{id} echoes
it as data.id; and a status table completed → success, queued → pending, flagged as a silent
failure. Plus: correct API-001's tasks row from "Path-only change" to "Not path-only — see
API-018", and add a Quick Reference entry under Requires restructuring.
migration/python/v1-to-v2.md — new SDK-016: Task polling with the before/after:
# 0.4.x
response = client.v1.memories.add(user_id=u, session_id=s, messages=msgs, async_mode=True)
task = client.v1.tasks.retrieve(response.data.task_id)
if task.data.status in ("completed", "failed", "error"): ...
# 1.x — the facade drops the envelope, so an async caller that follows its task goes one level down
envelope = client.memory.add_memory(AddInput(
app_id="default", project_id="default", session_id=s, async_mode=True,
messages=[MessageItem(sender_id=u, role=m["role"], timestamp=m["timestamp"],
content=Content(m["content"])) for m in msgs],
))
task = client.task_get(envelope.request_id)
if task.status in ("success", "failed", "error"): ...
# or: client.task_wait(envelope.request_id, timeout=180, interval=3)Two notes belong in that rule: task_get returns the unwrapped TaskItem
(id / status / task_type / created_at / finished_at / error) and task_wait replaces the
hand-rolled loop; and the low-level client does not coerce str → Content, so MessageItem and
Content must be built explicitly or pydantic rejects the call before it is sent.
Search patterns for the rule: .task_id, tasks.retrieve(, "completed" in a status comparison.
2.2 → §1.2 Fix SDK-014's method names
.update(...) → .patch(...) on the groups and senders rows, plus a line noting that only settings
has .update( — otherwise a pattern built on update silently matches nothing.
2.3 → §1.3 Move the agent_memory / raw_message rows onto search
Re-label both rows as memory_types=[...] on search, and state that 0.4.x's get never accepted
them (with its actual literal), so the human decision is made at the search call site.
2.4 → §1.4 Document the two removed search parameters
Two rows in SDK-008's field mapping: memory_types=[...] → (none), "a search can no longer be
restricted to a subset of types; the response still separates them, so filter client-side";
include_original_data= → (none).
2.5 → §1.5 Turn the three runtime work-arounds into rules
SDK-012, new step 4:EverOSAPIErroronly covers errors the gateway returned. 1.x validates the
body with pydantic before sending, and those raisepydantic_core.ValidationError— not an
EverOSErrorsubclass (it derives fromValueError). 0.4.x surfaced the same input as a server-side
BadRequestError, so a caller that turned invalid input into its own 4xx now lets the exception
escape. Suggestexcept (EverOSAPIError, ValueError)where the caller validates user input.SDK-004/SDK-013, new step 0: remove the module-level import of the removed symbol first
("flag, do not rewrite" applies to the call, not to animportof something that no longer exists);
move it into the function body so only calling it fails.SKILL.md→ "Rules for the migration agent": add the removed-symbol-import rule above, and a line
on tests covering a removed capability — mark themskipwith the migration reason rather than
deleting them or leaving them red.SKILL.md→ "Limitations of syntax checking": addtask_idread off an add result; a task status
compared to"completed"; a leftover import of a removed symbol. And suggest verification run one
python -c "import <pkg>"—py_compilereports success on all three.
What
Adds the everos-cloud 0.4.x → 1.x (v2 Memory API) migration to the existing v0→v1 skill, and extends the skill to cover callers that speak HTTP directly instead of using the Python SDK.
Why the rules are split in two
migration/http/v1-to-v2.md(new, 17 rules)migration/python/v1-to-v2.md(new, 15 rules)This migration is fundamentally an API-level change, not just an SDK rename. A customer calling
/api/v1/memoriesfrom Go, TypeScript or curl hits every one of these breaking changes — and previously got zero help: Step 1 only greppedevermemos|everos_cloud, which never matches a raw caller. Detection now also matches/api/v1/memories,api.evermind.aiand the EverOS env vars.SKILL.md changes
client.vN.prefix — but 1.x removed that entirely (client.add(...), notclient.v1.memories.add(...)). A migrated repo was misdetected and re-running the skill was not idempotent. Now keys on the dependency constraint plus bare facade verbs.--scanmode — produce an impact report, edit nothing. Useful for deciding whether to migrate.Findings that contradict the current public migration guide
Verified against the published
0.4.1/1.0.0/1.1.0wheels on PyPI, the v2 OpenAPI contract, and live prod calls (2026-09-04):1.xdoes NOT readEVEROS_API_KEY.api_keyis a required argument; there is noos.environ/getenvreference anywhere inclient.py.sdk-migration-1x.mdxstates it "still readsEVEROS_API_KEYif omitted". This one fails loudly (TypeError), so it is the safe one.1.xdoes NOT readEVER_OS_BASE_URLeither — and this fails silently.0.4.xpicked it up from the environment;1.xonly honourshost=. A client that pointed at a dev or test gateway via the environment starts reading and writing production data after the upgrade, with no error. Flagged as the top-priority finding in the skill.AsyncEverOSis gone (no async client in 1.x at all), as aremax_retries/http_client/default_headers—0.4.xretried twice by default,1.xdoes not retry.The OpenAPI spec's own
AddInputexample uses"timestamp": 1700000000(seconds), which the API rejects with 422 — worth fixing separately in the spec.Capabilities with no v2 equivalent (flagged, never rewritten)
groupmemory (/memories/group,/groups,group_idfilters),/senders,/settings,AsyncEverOS,delete(memory_id=), andmemory_type="raw_message". The v2 contract has zero occurrences ofgroup. These decide whether a given customer's migration can complete at all, so the skill counts them and reports the counts first.Naming
Marketplace renamed
everos-plugins→everos-tools. The GitHub repo rename is a separate manual step (needs admin), anddocs.evermind.ai/api-reference/sdk-migrationinstall commands need updating with it.Testing
claude plugin validate .→ ✔ passes (same check as CI)examples/python/v2.py→py_compilecleanfetchcaller + group usage + seconds timestamps +AsyncEverOS+EVER_OS_BASE_URLpointed at test): detection, scan report, and rewrites all behaved as specified — including catchingMath.floor(Date.now()/1000)in the TS file.🤖 Generated with Claude Code