Skip to content

feat: v1->v2 migration rules, raw-HTTP support, and scan mode - #3

Merged
dani1005 merged 14 commits into
EverMind-AI:mainfrom
dani1005:feat/v1-to-v2-migration
Sep 16, 2026
Merged

dani1005 merged 14 commits into
EverMind-AI:mainfrom
dani1005:feat/v1-to-v2-migration

Conversation

@dani1005

@dani1005 dani1005 commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

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

File Scope
migration/http/v1-to-v2.md (new, 17 rules) Transport level — endpoints, payloads, responses. Language-agnostic, source of truth
migration/python/v1-to-v2.md (new, 15 rules) Maps the Python SDK surface onto those rules

This migration is fundamentally an API-level change, not just an SDK rename. A customer calling /api/v1/memories from Go, TypeScript or curl hits every one of these breaking changes — and previously got zero help: Step 1 only grepped evermemos|everos_cloud, which never matches a raw caller. Detection now also matches /api/v1/memories, api.evermind.ai and the EverOS env vars.

SKILL.md changes

  • Fixed version detection. It keyed on a client.vN. prefix — but 1.x removed that entirely (client.add(...), not client.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.
  • Added --scan mode — produce an impact report, edit nothing. Useful for deciding whether to migrate.
  • Added the blocker list that must always be flagged and never rewritten, plus an impact-report template that leads with those blockers rather than with the mechanical diff count.

Findings that contradict the current public migration guide

Verified against the published 0.4.1 / 1.0.0 / 1.1.0 wheels on PyPI, the v2 OpenAPI contract, and live prod calls (2026-09-04):

  1. 1.x does NOT read EVEROS_API_KEY. api_key is a required argument; there is no os.environ/getenv reference anywhere in client.py. sdk-migration-1x.mdx states it "still reads EVEROS_API_KEY if omitted". This one fails loudly (TypeError), so it is the safe one.
  2. 1.x does NOT read EVER_OS_BASE_URL either — and this fails silently. 0.4.x picked it up from the environment; 1.x only honours host=. 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.
  3. Also undocumented: AsyncEverOS is gone (no async client in 1.x at all), as are max_retries / http_client / default_headers0.4.x retried twice by default, 1.x does not retry.

The OpenAPI spec's own AddInput example uses "timestamp": 1700000000 (seconds), which the API rejects with 422 — worth fixing separately in the spec.

Capabilities with no v2 equivalent (flagged, never rewritten)

group memory (/memories/group, /groups, group_id filters), /senders, /settings, AsyncEverOS, delete(memory_id=), and memory_type="raw_message". The v2 contract has zero occurrences of group. 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-pluginseveros-tools. The GitHub repo rename is a separate manual step (needs admin), and docs.evermind.ai/api-reference/sdk-migration install commands need updating with it.

Testing

  • claude plugin validate . → ✔ passes (same check as CI)
  • examples/python/v2.pypy_compile clean
  • End-to-end on a synthetic v1 customer repo (Python SDK + a TypeScript fetch caller + group usage + seconds timestamps + AsyncEverOS + EVER_OS_BASE_URL pointed at test): detection, scan report, and rewrites all behaved as specified — including catching Math.floor(Date.now()/1000) in the TS file.

⚠️ Not yet run against real customer code. Worth doing before this goes out in any customer-facing email.

🤖 Generated with Claude Code

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>
@GaussDing

Copy link
Copy Markdown

Validated this PR end-to-end instead of reviewing the rules on paper: built a throwaway v1 caller
(7 modules / 6 test files) against the introspected 0.4.1 surface — deliberately not against this
skill's own examples/python/v1.py, so the skill isn't graded on its own answer key — got it green on
everos-cloud==0.4.1 against the dev gateway (21 passed), installed this PR's plugin, ran
/everos-sdk-upgrade, then re-ran the suite on 1.1.0 with a v2 key.

The mechanical half is solid: version detection landed on v1 (dependency constraint + 29 client.v1.
sites, 0 bare facade verbs — the idempotency fix works), every rewritten signature matches the real
1.1.0 wheel, all 27 blocker call sites were flagged and none rewritten, and all three "contradicts the
published guide" findings reproduce (api_key required positional, no os.environ anywhere in
client.py, AsyncEverOS gone). Final state after the migration: 12 passed / 9 skipped, every skip
a flagged blocker — once the issue in §1.1 was fixed by hand.


1. Defects found

1.1 Task polling has no rule — this one breaks the migrated code (blocking)

The migrated suite raised on the first async write:

AttributeError: 'AddData' object has no attribute 'task_id'

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..."}

AddData.model_fields == ['message_count', 'status', 'additional_properties']. The skill correctly
applied SDK-011 ("drop one .data level"), so response.data.task_id became response.task_id — valid
Python, AttributeError at runtime. The id to poll is the envelope's top-level request_id;
GET /api/v2/tasks/{request_id} answers 200 and echoes it back as data.id. SDK-011 already says the
facade discards the envelope, but never connects that to task polling.

(b) The status vocabulary changed, and this half fails silently. v2 is queued → pending → success;
v1 was completed. Observed values across GET /api/v2/tasks: only pending and success. A leftover

if response.status in ("completed", "failed", "error"):   # never true on v2

turns a finished task into an apparently-unfinished one and the poll runs to its timeout. Nothing raises.

Related: API-001 labels GET /api/v1/tasks/{task_id}GET /api/v2/tasks/{task_id} as
"Path-only change". It is not — the id source and the status values both changed.

1.2 SDK-014 has the wrong method names — the search patterns miss every real call site

The table lists client.v1.groups.update(...) and client.v1.senders.update(...). In 0.4.x both
resources expose create / retrieve / patch — there is no .update( on either. Only settings
has .update(. A grep built from that table finds nothing.

1.3 SDK-009 aims the agent_memory decision at a call shape that cannot exist

The table lists memory_type="agent_memory" and "raw_message" as get() values. 0.4.1's get
literal is ['episodic_memory', 'profile', 'agent_case', 'agent_skill'] — those two values exist only on
search(memory_types=[...]). So the "needs a human decision" flag points at get, while the place that
actually needs the decision (search) is not covered.

1.4 Two search parameters were removed without being documented

1.1.0's search() has no memory_types= and no include_original_data=; SDK-008's field mapping
lists neither. Consequence for callers: a search can no longer be restricted to a subset of memory
types at all — the response still separates them, so filtering moves client-side.

1.5 Three gaps the model patched at runtime — and it said so in the output

These did not break, because the model worked around them itself. But it left comments naming the
gap, which makes them reproducible findings rather than luck:

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 strContent, 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: EverOSAPIError only covers errors the gateway returned. 1.x validates the
    body with pydantic before sending, and those raise pydantic_core.ValidationError — not an
    EverOSError subclass (it derives from ValueError). 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. Suggest except (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 an import of 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 them skip with the migration reason rather than
    deleting them or leaving them red.
  • SKILL.md → "Limitations of syntax checking": add task_id read 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_compile reports success on all three.

All six defects from GaussDing's end-to-end review, re-verified here against the
installed 0.4.1, the published 1.1.0 wheel, and live prod calls before writing
anything into the rules.

Task polling (the blocking one, review §1.1). Neither half was covered:

- The add response carries no task id. Verified on the wire: an async add returns
  202 with data {message_count, status} only, and AddData's fields are
  message_count / status / additional_properties. The id to poll with is the
  envelope's top-level request_id, and GET /api/v2/tasks/{request_id} echoes it
  back as data.id. SDK-011 rewrote response.data.task_id into response.task_id,
  which is valid Python that raises on the first async write.
- The status vocabulary changed and this half is silent. A live poll went
  queued -> processing -> success; "completed" never appears. A stale
  in ("completed", "failed", "error") check is simply never true, so the loop
  spins to its own timeout with nothing raised.

Note the review observed only pending and success. processing showed up here too,
and /api/v2/tasks/stats lists all five (queued, pending, processing, success,
failed), so the rules treat only success and failed as terminal. Treating
processing as terminal is the mirror-image bug.

Adds API-018 and SDK-016 covering both halves, corrects API-001's tasks row from
"Path-only change", and documents that the generated client does not coerce
str -> Content the way the facade's _to_message does.

Wrong method names (§1.2). SDK-014 listed groups.update / senders.update. 0.4.1
introspects as create / patch / retrieve on both; only settings has update. A
search pattern built on that table matched nothing.

Misplaced rows (§1.3). SDK-009 put agent_memory and raw_message on get(). 0.4.1
types get's memory_type as Literal['episodic_memory', 'profile', 'agent_case',
'agent_skill'] — both values live on search(memory_types=[...]) instead, so the
human decision was being pointed at the wrong call site.

Undocumented removals (§1.4). 1.1.0's search() has neither memory_types= nor
include_original_data=. Both now appear in SDK-008, with the consequence spelled
out: a search can no longer be restricted to a subset of types, so that filtering
moves client-side.

Runtime workarounds promoted to rules (§1.5):

- SDK-012 gains a step on pydantic. 1.x validates the body before sending and
  raises ValidationError, which derives from ValueError, not EverOSError. 0.4.x
  surfaced the same input as a server-side BadRequestError.
- SDK-004 and SDK-013 gain a step 0: "flag, do not rewrite" applies to the call,
  not to an import of a removed symbol. A stale module-level import takes down the
  whole module, including the paths that migrated cleanly.
- SKILL.md gains both rules, plus the instruction to report gaps in the rules
  rather than silently working around them.

Verification. py_compile sees none of the above, so SKILL.md now asks for
python -c "import <module>" as well, and python -c is added to allowed-tools so
the skill can actually run it. The impact report grows a "verify by hand" section
led by async task-polling call sites, and examples/python/v2.py carries the
corrected pattern since that file is the diff target for verification.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dani1005

Copy link
Copy Markdown
Collaborator Author

Thanks for validating it end-to-end rather than on paper, and for building the fixture against the introspected 0.4.1 surface instead of examples/python/v1.py. Grading the skill on its own answer key would have hidden §1.3 entirely.

All six are fixed in e10773e. I re-verified each claim before writing it into a rule rather than taking the report at face value, and everything reproduced.

Task polling (§1.1)

Confirmed on the wire. Async add returns 202 with data holding only message_count and status, and GET /api/v2/tasks/{request_id} answers 200 with data.id equal to the request_id I sent.

One correction to the status list. You saw only pending and success; a live poll here went queued → processing → success, and /api/v2/tasks/stats reports all five buckets (queued, pending, processing, success, failed). So the rules treat only success and failed as terminal and call out the mirror-image bug explicitly: a check that stops on processing reports a task done before it is. Worth having, since ("success", "failed", "error") is the natural thing to write and processing is the state you are most likely to catch it in.

Added API-018 and SDK-016 covering both halves, corrected API-001's tasks row away from "Path-only change", and documented the strContent point: MessageItem.content is typed Content and the coercion lives in the facade's _to_message, so the low-level path has to build it explicitly.

§1.2, §1.3, §1.4

All three reproduced by introspecting 0.4.1 directly:

  • groups and senders expose create / patch / retrieve. No update on either; only settings has it. SDK-014 corrected, with a note on why a pattern built on update matches nothing.
  • get's memory_type is Literal['episodic_memory', 'profile', 'agent_case', 'agent_skill']. Both rows moved onto search(memory_types=[...]), so the agent_case vs agent_skill decision is now aimed at the call site that actually has it.
  • 1.1.0's search() has neither memory_types= nor include_original_data=. Both documented in SDK-008 with the consequence stated: filtering by type moves client-side.

§1.5

Your framing of this was the most useful part of the review, so I took it literally: the two spots where the model left a comment naming an uncovered rule are now rules, and SKILL.md asks it to keep doing that rather than working around gaps silently. Those comments are the cheapest signal we have about what the rules are missing.

  • SDK-012 gains a pydantic step. ValidationError derives from ValueError, so it escapes an except EverOSError, where 0.4.x surfaced the same input as a server-side BadRequestError.
  • SDK-004 and SDK-013 gain a step 0: "flag, do not rewrite" applies to the call, not to an import of a symbol that no longer exists. Your point that a stale top-level import takes down the paths that migrated cleanly is what makes this worth a rule of its own.

Verification

SKILL.md now asks for python -c "import <module>" alongside py_compile, and python -c is added to allowed-tools — without that the skill would have been told to run a command it could not run. The impact report grows a "verify by hand" section led by async task-polling call sites, and examples/python/v2.py carries the corrected pattern, since SKILL.md points verification at that file as the diff target.

On your patch

No need to push it. Everything above is in e10773e. If your version covers anything I have missed, a diff against that commit is the fastest way to see it.

One thing your run gives us that I could not get on my own: 12 passed / 9 skipped, every skip a flagged blocker, on a fixture you wrote independently. That is the real-customer-code validation this was missing, and it is the last thing gating the customer emails. Would you be willing to keep the fixture around so we can re-run it when the rules change?

A review pass over the skill as a whole, rather than over the migration rules.
Three real problems, all in code this branch introduced.

Secret leakage. Step 1B's detection grep matched EVEROS_API_KEY and
EVER_OS_BASE_URL with content output, alongside the endpoint patterns. A .env
holds the key and its value on the same line, so running the documented command
against a fixture pulls a live key straight into the transcript:

  .env:1:EVEROS_API_KEY=sk-live-8f3a9c2e1b7d4f6a0e5c8b2d9a4f7e1c

The two patterns are now separate calls and the env-var one is files-only. The
skill needs to know which files reference these variables, never their values.
SDK-002 told the agent to search .env files for EVER_OS_BASE_URL and now carries
the same caveat, and there is a rule forbidding reading or quoting a secret.

Over-broad permission. The previous commit added Bash(python -c *) to
allowed-tools so the skill could run the import check. That pattern permits any
command beginning with "python -c", which is arbitrary code execution
pre-authorized inside a customer's repository. There is no narrower pattern that
still permits the check, so it is removed: the instruction stays, the user gets
one prompt showing the exact command, and SKILL.md explains why.

No way back. The skill rewrites source files and said nothing about git. A
customer running it on a dirty tree could not separate its edits from their own
work. New Step 0 runs git status --porcelain before anything is read or written,
warns when the tree is dirty or is not a repository, and recommends a branch.
The run now ends with a review line and an undo line. Bash(git status *) is
added to allowed-tools; it is read-only.

Also: repository contents are now explicitly framed as data rather than
instructions, since this skill's whole job is to read and act on files written by
someone else. And the impact report is called out as safe to share, since it
carries counts and locations but no source and no secrets, which makes it the
thing a customer can paste to us instead of describing their integration.

README gains a "what it does to your repository" section covering all of the
above, plus the point that the source is read by whichever model the assistant
runs, with the by-hand path for anyone who cannot accept that.

Verified against a fixture: the old pattern surfaces the key, the new one returns
only paths; detection stays at zero hits on a repo with no EverOS usage; and an
already-migrated repo still reads as v2 (dependency >=1, no client.v1., bare
facade verbs present).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dani1005

Copy link
Copy Markdown
Collaborator Author

Separate pass over the skill as a whole rather than the migration rules, pushed as c6ab5fb. Three real problems, all in code this branch introduced, so this is self-review rather than a critique of the earlier feedback.

1. The detection grep leaked secrets

Step 1B matched EVEROS_API_KEY|EVER_OS_BASE_URL with content output, in the same call as the endpoint patterns. A .env holds the variable and its value on one line. Running the documented command against a fixture:

.env:1:EVEROS_API_KEY=sk-live-8f3a9c2e1b7d4f6a0e5c8b2d9a4f7e1c

That is a live key in the transcript, and SDK-002 made it worse by instructing the agent to search .env files specifically.

The two patterns are now separate calls, and the env-var one is files-only. Same fixture after the fix returns svc/mem.py and .env, nothing else. The skill needs to know which files reference these variables, never their values.

2. Bash(python -c *) was arbitrary code execution, pre-authorized

I added that in e10773e so the import check could run without a prompt. python -c * matches any command starting with python -c, which is the whole language, pre-approved inside a customer's repository.

There is no narrower pattern that still permits the check, so it is gone. The instruction stays and the user gets one prompt showing the exact command. That is the correct trade for a tool that runs in repositories we do not own, and SKILL.md now says why so nobody re-adds it for convenience.

Bash(pytest *) stays. It runs the customer's own suite in their own repo, which they would run anyway, and the migration is not verifiable without it.

3. No way back

The skill rewrote source files and said nothing about git. A customer running it on a dirty tree could not separate our edits from their own work in progress, which is the worst outcome this tool can produce.

New Step 0 runs git status --porcelain before anything is read or written: warns on a dirty tree, warns harder when it is not a repository at all, recommends a branch so the migration is one reviewable diff. The run now ends with a review line and an undo line. Bash(git status *) added to allowed-tools, read-only.

Smaller

  • Repository contents are data, not instructions. This skill's entire job is to read and act on files written by someone else, including comments. One line in the agent rules.
  • The impact report is now called out as safe to share (counts and locations, no source, no secrets). It is exactly what we need in order to help, so a customer can paste it instead of describing their integration.
  • README gains a "what it does to your repository" section, including the point that source is read by whichever model the assistant runs, with the by-hand path for anyone who cannot accept that.

What I checked and found fine

  • Detection returns zero hits on a repo with no EverOS usage. It will not false-trigger.
  • An already-migrated repo still reads as v2 (dependency >=1, no client.v1., bare facade verbs present), so the idempotency fix holds.
  • Zero verbatim overlap between the two rule files at 45 characters or more. The http/python split is genuine layering, not copy-paste, so it will not drift.

Still open, and it needs a decision rather than a patch

migration/python/v0-to-v1.md is 1202 lines for a version we believe has no users left. It is discovered by Step 4's glob on every run. Harmless today, but it is the largest single file in the skill and nobody will maintain it. Worth deleting outright, or moving somewhere unreachable by the chain. Happy to do either, just not without someone confirming v0 really is at zero.

🤖 Generated with Claude Code

dani1005 and others added 2 commits September 14, 2026 15:50
Caught downstream in EverOS-Docs d0c3321: the agentic-retrieval page offered
min_score=0.3 with method="agentic" as an optimisation. The server applies
min_score on the episode hybrid path only and agentic ignores it, so the tip was
a silent no-op.

The error itself was confined to that docs page, not to these rules, which only
list min_score as a new parameter. But the rules are what a migrating customer
reads when deciding which new parameters to adopt, so both files now carry the
caveat rather than leaving the same trap one step further back.

Also notes in API-006 that agentic retrieval is not new in v2. v1 offered the
same four methods, and it should not be sold as an upgrade.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Four independent test passes built real fixtures, ran the skill against them, and
audited the instructions against the published wheels and the v2 contract. They
found around forty defects. The design held up — flag-don't-rewrite, the two-layer
rule split, and the file:line report were all judged correct — but the process
wrapped around it was not safe to point at a customer's code. This addresses that.

## It reported success on repositories that do not run

A realistic FastAPI app migrated clean: py_compile passed on all 21 files, all 11
modules imported, pytest was green. Every business endpoint returned 500, because
23 call sites still called client.v1.*. A flagged call site is still a call site,
and nothing in the flow said so.

Step 7 now counts what still targets the old surface and the report leads with it:
"N call sites will still raise at runtime". Step 7 also checks which version is
actually installed first — Step 6 deliberately does not install anything, so the
import check added in e10773e was running against 0.4.x, where it failed on
correctly migrated code and passed on broken code. An agent following it would
have reverted good work.

Verification is now per language. The previous list was py_compile and pytest,
which is nothing at all for the TypeScript and Go repos this skill claims to
support; both of those migrations broke the build and the skill could not have
noticed. tsc, go build/vet, bash -n and jq are allowlisted. The test suite is
explicitly not run: it can carry live credentials, and SDK-002 means a suite that
used to point at a dev gateway now points at production.

## It could destroy work with no way back

Step 0 probed `git status --porcelain` and read empty output as "clean tree". That
is also what a non-repository prints, because the fatal goes to stderr, and what a
gitignored subdirectory of an unrelated repo prints. In the second case the
migration rewrote seven files, `git diff` showed nothing, and the original source
was gone. Step 0 now resolves `rev-parse --show-toplevel`, compares it to the
working directory, and checks `check-ignore`.

The dirty-tree branch warned and continued, which cost a customer an uncommitted
feature in a file the migration rewrote. The overlap between "files you have
modified" and "files I will touch" is the only thing that matters, and Step 0 ran
too early to know the second set. That check moved to the new Step 3, after
detection, and a non-empty intersection now stops the run.

The undo line was `git checkout -- .`, which discards uncommitted work in files
this skill never touched and leaves untracked files behind. Destructive and
incomplete at once. Step 6 now takes a stash snapshot before the first edit and
the undo restores it.

## An interrupted or repeated run made things worse

Step 5 bumped the dependency first and Step 2 classified on the dependency, so any
interruption left a half-migrated repo that reported itself already current. The
customer was stranded with no resume path. The bump is now the last edit.

A correctly migrated repo was classified v1 forever, because this skill requires
leaving client.v1. calls in place for removed capabilities and Step 2 detected on
exactly that. Re-running then corrupted the flags the first run had placed. Step 2
now ignores call sites carrying an EVEROS-MIGRATION marker.

## It did not find the customers it claims to support

Two of three idiomatic non-Python repos were not detected at all: a typed client
assembles paths from a version constant, so no /api/v1/memories literal appears
anywhere. The detection pattern was also anchored on "memories", making
/api/v1/groups, /senders and /settings invisible — three of the five blocker
categories. Detection now matches /api/v[12]/, assembled path fragments and
version-constant names, and follows importers one hop out so a customer's own
wrapper does not hide the call sites that construct sender_id and timestamps.

## Both Quick Reference tables are gone

They were the last section of each file, labelled "safe to apply directly", and
they produced four of the eight worst findings. `client.v1.memories.` -> `client.`
also rewrites .group. and .agent. calls that SDK-014 requires be flagged — and it
is self-concealing, because once the .v1. marker is gone the blocker pass cannot
find them and the report shows zero group calls on a codebase full of them. The
HTTP table was order-dependent: applying the add row first turns
/api/v1/memories/get into /api/v2/memory/add/get.

Both are replaced by an ordered procedure that records blockers before any rewrite
and names the two substitutions that are genuinely context-free.

## New pre-flight gate (Step 3)

everos-cloud 1.x requires Python >= 3.12; 0.4.x required >= 3.9. This was not
mentioned anywhere — not in the rules, not in the public migration guide, not on
the retirement page. A project on 3.11 gets every call site rewritten and then
pip resolves back to 0.4.x, leaving code that runs on neither version while every
syntax check passes. PRE-001 stops the run instead.

The gate also refuses async codebases before editing rather than thirty minutes
in — 1.x has no async client, so a FastAPI request path cannot be migrated — and
publishes the blocker inventory before the first change rather than after the
last.

## Rule corrections, all re-verified against the wheels

- SDK-016 claimed v1 used "completed" for task status. 0.4.1 declares
  Literal["processing", "success", "failed"] and the string "completed" does not
  appear in the wheel. The claim came from a review comment I propagated without
  checking, under a heading stamped "verified live on prod" — what I had actually
  verified was the v2 half. Corrected, and scoped to raw HTTP callers.
- SDK-016 sends async pollers to client.memory.*, which raises ApiException, not
  EverOSAPIError — so following it together with SDK-012 produces a handler that
  never fires. Both rules now say so.
- SDK-002 filed EVER_OS_BASE_URL under "flag only", which is the one case where
  the literal instruction creates the production hit it warns about. Now rewrite
  and flag the review.
- SDK-003's removed kwargs are hard TypeErrors, not flags; adds the httpx to
  urllib3 transport change (respx and httpx mocks stop intercepting) and
  EVER_OS_CUSTOM_HEADERS, which 0.4.x read from the environment.
- SDK-006's example silently flipped async_mode to False.
- SDK-009: rank_by to sort_by is an enum narrowing, not a rename.
- SDK-010 claimed delete "is now keyword-only"; 0.4.1's already was.
- API-003 overwrote sender_id that v1 code had set correctly, and never said where
  an agent id comes from for a raw caller.
- API-008's v1 add sample showed request_id; 0.4.1's AddResult carries task_id.
- API-010's error body was one invented shape. Live probing found three: flat for
  400/404/422-InvalidParameter, enveloped with request_id for model validation,
  enveloped without it for 401. Branch on status and unwrap defensively.
- API-004 gains per-language forms, including the portable shell millisecond
  clock, and searches the sink rather than the source — the old pattern did not
  match the skill's own v1 example.

## New rules and examples

SDK-017 (object.sign to presign: positional, and a returned status becomes a
raised EverOSStorageError) and SDK-018 (test doubles, fakes, cassettes and
Postman collections — the largest hand-edit in a real migration and the engine
behind every false green).

examples/typescript/ and examples/go/ v1 and v2 pairs. These fix Step 7's dead
glob for non-Python callers, give API-003's "where does the agent id come from"
question an answer outside a Python file, and demonstrate the two things a typed
caller has to get right: splitting a version constant instead of bumping it, and
re-declaring legacy types locally so a flagged module still compiles.

Verified: tsc --noEmit clean under strict, go vet clean, claude plugin validate
passes. The Go examples carry //go:build ignore because v1 and v2 share a package
directory.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dani1005

Copy link
Copy Markdown
Collaborator Author

Four test passes (realistic Python app end-to-end, non-Python raw HTTP in TS/Go/shell, adversarial safety rails, cold instruction audit) found ~40 defects. All the substantive ones are fixed in 4eea8c8. Details are in the commit message; this is a reviewer's map.

The two findings that mattered most

It reported success on repositories that do not run. A FastAPI app migrated clean — py_compile on 21 files, 11 module imports, pytest green — and every business endpoint returned 500, because 23 call sites still called client.v1.*. A flagged call site is still a call site. Step 7 now counts what still targets the old surface and the report's first line is N call sites will still raise at runtime.

Step 0 was theatre. git status --porcelain printing nothing was read as "clean tree". That is also what a non-repository prints (the fatal goes to stderr) and what a gitignored subdirectory of an unrelated repo prints. In the second case the migration rewrote seven files, git diff showed nothing, and the source was unrecoverable. Now: rev-parse --show-toplevel compared to the working directory, check-ignore, a stash snapshot before the first edit, and an undo that restores it instead of git checkout -- . (which discarded uncommitted work in files the tool never touched).

Self-inflicted, for the record

Four of the defects were mine, three from earlier commits on this branch:

  • Bash(python -c *) in allowed-tools (added in e10773e) is arbitrary code execution pre-authorized inside a customer's repo. Removed; there is no narrower pattern, so it prompts.
  • The import check that permission was for ran against 0.4.x, because Step 6 deliberately does not install. It failed on correctly migrated code and passed on broken code. Step 7 now checks the installed version first and defers.
  • EVER_OS_BASE_URL was filed under "flag only" — the one case where the literal instruction creates the production hit the rule warns about. Now rewrite, and flag the review.
  • SDK-016's "completed" claim was wrong, under a heading I stamped "verified live on prod". 0.4.1 declares Literal["processing", "success", "failed"] and the string does not appear in the wheel. I took it from a review comment and verified only the v2 half. Corrected and scoped to raw HTTP.

Both Quick Reference tables are gone

They were labelled "safe to apply directly" and produced four of the eight worst findings. client.v1.memories. -> client. also rewrites .group. and .agent. calls that SDK-014 requires be flagged — and it is self-concealing: once the .v1. marker is gone the blocker pass cannot find them, so the report shows zero group calls on a codebase full of them. Replaced with an ordered procedure that records blockers before any rewrite.

New: Step 3 pre-flight

everos-cloud 1.x requires Python >= 3.12; 0.4.x required 3.9. This was documented nowhere — not here, not in the public migration guide, not on the retirement page. A project on 3.11 gets every call site rewritten, then pip install -U resolves back to 0.4.x, leaving code that runs on neither version with every syntax check green. The gate also stops async codebases before editing rather than thirty minutes in, and publishes the blocker inventory before the first change.

How claims were verified

Everything asserted about a surface was checked against the installed 0.4.1, the published 1.1.0 wheel, or live prod — not against this skill's own examples. API-010's three error shapes were found by probing prod directly; the contract declares none of them consistently and its own GatewayError description admits the shape "is not yet uniform".

tsc --noEmit clean under strict, go vet clean, claude plugin validate passes.

Knowingly not fixed — please check these deliberately

  • top_k 1-100: the audit flagged this as unverified because the schema has no minimum/maximum. The contract's field description does state it. Left as-is; I think the audit was wrong here.
  • migration/python/v0-to-v1.md (1202 lines) still ships. It costs nothing at runtime (Step 4 globs filenames; only chain files are read) and deleting it is the one irreversible move. It should go once the gateway-log segmentation confirms zero v0 callers.
  • ${CLAUDE_PLUGIN_ROOT} replaces the undefined ${CLAUDE_SKILL_DIR}, but I have not tested that it resolves on a real install. Step 4 has a fallback, but this is worth one manual check before merge.
  • The Impact Report is still a hand-filled template. It is now consistent with the seven-row blocker table, but the audit's point stands that it should be derived rather than maintained in parallel.
  • The Go examples are not covered by CI — no Go toolchain in the workflow.

Outside this repo

The Python 3.12 requirement belongs on the retirement page and in the customer emails regardless of what happens to this tool. A customer on 3.11 has a broken migration path whether or not they ever run this.

🤖 Generated with Claude Code

Dani and others added 5 commits September 14, 2026 19:03
…sk states

Findings from a review that verified every rule against the 0.4.1 and 1.1.0 wheels
and the v1/v2 OpenAPI documents, then ran the skill headlessly on four fixtures.

Safety
- Step 0/6/8: the snapshot is `git stash create` + `git stash store`, which leaves the
  working tree untouched. `git stash push --keep-index` removed the customer's uncommitted
  work for the duration of the run, did nothing on a clean tree, and `git stash pop`
  re-applied their changes without reverting ours. In practice the model skipped the stash
  and printed `Undo: git checkout main`, which reverts nothing when the branch has no
  commits. The report now prints a file-scoped `git restore --source=<snapshot>` block.
- The working branch is created after the pre-flight gate, so a STOP leaves no branch.
- Not a git repository: stop and ask for `git init`, never copy trees ourselves.
- Content-mode greps carry a source glob so `.env*` never enters content mode; `.env`
  files are never opened.

Rule accuracy
- API-018: the v1 contract enumerates processing | success | failed (v1 OpenAPI and the
  0.4.1 wheel); `completed` never existed. v2 adds queued and pending. Removed the
  `completed -> success` table and the hunt for it in SDK-016 and examples/python/v2.py.
- SDK-012: connection failures on 1.1.0 raise urllib3.exceptions.MaxRetryError, timeouts
  ReadTimeoutError, both HTTPError subclasses (verified against a closed port). Rewrite
  the handler to `except urllib3.exceptions.HTTPError`; do not delete it.
- API-011: a synchronous add returns extracted or accumulated (AddData enum).

Flow
- `--yes`: proceed past the blocker question with blockers flagged. Without it a run
  where nobody can answer produces the report and edits nothing.
- 3b: async is a STOP only when every EverOS call site is async; otherwise a blocker.
- 3c: removed constructor options are deleted, not flagged, and never count in STATUS.
- Step 2 row 2 also requires zero unflagged `client.v1.` sites, so a customer who bumped
  the pin first and got `AttributeError: v1` is not told the tree is current.
- Step 5 counting rules shared by both modes; Postman requests count as call sites.
- Flag comments sit directly above the statement so a re-run recognises them.

Tooling
- allowed-tools covers python3, pip show, uv pip show, go version and the read-only
  shell commands the model reaches for; Step 7 no longer needs `python -c`.
- Tool discipline section: Grep/Glob/Read tools, one Bash command per call.

Re-tested headlessly (sonnet) on the four fixtures: dirty tree keeps the customer's files
and the printed undo reverts exactly the migrated files; Python 3.11 stops with no branch;
`--yes` migrates an 8-module app whose migrated test suite passes on 1.1.0 with all eight
blocker sites flagged adjacent; the TypeScript scan counts now match ground truth.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A default-permission run of --yes on the Python fixture prompted for exactly two things
the allowlist should have covered: a shell find on the plugin directory (outside the
project, so it prompts regardless of the allowlist) and git -C <path> stash create (the
-C form does not match the git stash create prefix). Say so in Tool discipline.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@dani1005

Copy link
Copy Markdown
Collaborator Author

Review round 2026-09-14: what changed since 4eea8c8, and how to test it

Four commits on top of the earlier review rounds: b59457d (the fixes), a9ce4e9 and 1e72494/a6a24c5 (docs), 12e6eb7 (merge of main, one-line README title conflict). Total diff against main is still 11 files.

How this round was verified

Two layers, both independent of the skill's own examples/:

  1. Every factual claim in the two rule files was checked against the published 0.4.1 and 1.1.0 wheels (introspected signatures, exception classes, transport, model fields) and against the v1 and v2 OpenAPI documents (openapi-cloud-v1.json in EverOS-Docs, openapi.public.json in the SDK factory).
  2. Headless runs of the skill (claude -p --plugin-dir …, sonnet) on four fixtures written from the introspected 0.4.1 surface: an 8-module Python service with tests, a TypeScript + shell + .http + Postman + Go raw-HTTP project, eight one-rail safety cases (3.11, no git, dirty overlap, dirty non-overlap, already-migrated, prompt injection, gitignored nested dir, empty repo), and a "dependency already bumped, code not migrated" trap. After the fixes: the migrated Python app imports on 1.1.0 and its migrated test suite passes; it also completed add → flush → get → search → delete against prod with a real key. --scan was additionally run under Codex CLI and Cursor.

What was wrong and what changed

Area Before Now
Way back (Step 0 / 6 / 8) git stash push --keep-index removed the customer's uncommitted work from the tree during the run, did nothing on a clean tree, and git stash pop re-applied their changes without reverting ours. In practice runs skipped the stash and printed Undo: git checkout main, which reverts nothing (branch and main share the commit). git stash create + git stash store: working tree untouched. Report prints git restore --source=<snapshot> -- <files edited>. Verified: customer's notes.md and README edit survive the run; the printed undo reverts exactly the migrated files.
Branch creation Created in Step 0, so a 3.11 STOP left a stray branch Created right before the first edit
API-018 (http) Table said v1 task status was completed v1 contract enumerates processing | success | failed; completed never existed in either API generation. v2 adds queued/pending; unknown values are terminal only with finished_at set
SDK-012 step 2 "Flag" except APIConnectionError, which in practice got deleted Connection failures on 1.1.0 raise urllib3.exceptions.MaxRetryError, timeouts ReadTimeoutError, both HTTPError subclasses (verified against a closed port). Rewrite to except urllib3.exceptions.HTTPError; never delete the handler
API-011 Sync add returns extracted extracted or accumulated (both in the AddData enum)
3b async Any async path was a hard STOP, so one async helper blocked 40 sync call sites STOP only if every EverOS call site is async; otherwise a blocker, flagged per SDK-004
Blocker question Non-interactive runs (CI, -p) always stopped at the question and edited nothing --yes proceeds with blockers flagged; 3a/3b/3d STOPs still hold
Step 2 row 2 A repo with the pin bumped to >=1 but code still on client.v1. could read as "already current" Also requires zero unflagged client.v1. sites
Counting scan and migrate reported different MECHANICAL numbers on the same tree; Postman was migrated but counted as "0 fixtures" Shared counting rules in Step 5; Postman requests are call sites; field access is a rename, only a memory_types request value is a decision
Flag placement Comments sat 5 to 7 lines above the call, outside the 3-line window Step 2 uses to recognise them Last comment line directly above the statement
Permissions python3, pip show, python -c, git -C all prompted Allowlist covers python3, pip show, uv pip show, go version, read-only shell commands; Tool discipline section. Measured in a default-permission run: --scan 2 prompts, full migrate 3 plus the first-edit approval
Secrets Step 1B content grep had no file filter, so .env lines could be quoted Content greps carry a source-extension glob; .env* is never opened; README says "never prints", not "never reads"

Where a second pair of eyes helps most

  1. SKILL.md Step 0 "The snapshot" and Step 8's undo block. This is the safety promise.
  2. migration/http/v1-to-v2.md API-018 and migration/python/v1-to-v2.md SDK-012 step 2, the two places where factual content changed.
  3. The --yes wording in Modes and 3c: it must not be read as overriding the 3.11 / all-async / dirty-overlap STOPs.

Reproducing a test yourself

# in any git repo on everos-cloud 0.4.x, or a throwaway one
claude --plugin-dir ./plugins/everos-sdk-upgrade
/everos-sdk-upgrade --scan          # report only, no edits, no snapshot
/everos-sdk-upgrade                 # stops to ask if any blocker is non-zero
/everos-sdk-upgrade --yes           # proceeds with blockers flagged

Things worth trying that the fixtures covered: uncommitted edits in a file the migration does not touch (they must survive, and the printed undo must revert only the migrated files); a .python-version of 3.11 (must stop with no branch created); an EVEROS-MIGRATION: comment left directly above a client.v1.groups.create(...) call in an otherwise-1.x repo (must read as already current).

Known limits, not blockers

Full migrate mode was exercised on Claude Code only; Codex and Cursor ran --scan. No real customer repository yet. Go was inspected, not compiled (no toolchain on the test machine). A full migration takes about 7 minutes with no progress output.

🤖 Generated with Claude Code

@GaussDing

Copy link
Copy Markdown

Re-verified from scratch against the updated branch (12e6eb7). The task-polling defect is fixed and
the migration now comes out working on the first run.
One correction I owe you, and one small nit.

What I ran

Nothing was reused from the previous round. Fresh clone of the v1 test-bed at its baseline tag, fresh
venv on everos-cloud==0.4.1, baseline re-established (21 passed against the dev gateway), then
Steps 0–8 of the new SKILL.md, then 1.1.0 + a v2 key.

Stage Result
v1 baseline, fresh copy 21 passed
Pre-flight (Steps 0–3) all clear: toplevel == cwd, clean tree, requires-python >=3.12, 5 async sites (blocker, not STOP), no dirty overlap
Blocker inventory all seven reported: group 26 / senders 3 / settings 4 / async 5 / memory_id 3 / raw_message 6 / constructor kwargs 4
Migration 14 files, +510/−213, 30 EVEROS-MIGRATION flags
Verification py_compile clean; every module imports; all 17 remaining client.v1. sites carry an adjacent flag (checked line by line)
Live run on 1.1.0 + v2 key 12 passed / 9 skipped / 0 failed — first run, no hand-patching

Last round the same point raised AttributeError: 'AddData' object has no attribute 'task_id' and needed
47 lines of hand-patching to reach the same 12 passed. SDK-016 catches it now, and the low-level
add_memoryenvelope.request_idtask_get path it prescribes works end to end.

Also confirmed: the migrated tree satisfies Step 2 row 2, so a re-run correctly reads it as already
current rather than re-migrating it.

Correction: my completedsuccess claim was wrong

SDK-016 pushes back on it, and you are right. Verified both ways:

  • everos_cloud/types/v1/task_status_result.py:11status: Literal["processing", "success", "failed"]
  • "completed" does not appear anywhere in the 0.4.1 wheel except an unrelated spot in _multimodal.py
  • Live v1 gateway (POST /api/v1/memories → poll /api/v1/tasks/{id}) → the terminal string is success

That string came from my own test-bed, which I had written from the Go memtest harness's polling
logic rather than from the SDK's own types. I mistook my own bug for a contract change. Your framing is
the correct one: the terminal pair is unchanged, v2 only adds queued / pending as non-terminal
states, and what actually needs checking is that the non-terminal set covers processing too.

One nit: the skill's own comments feed its idempotency check

Step 2 row 2 wants "zero /api/v1/ outside flagged call sites" and Step 7d counts
client\.v1\.|/api/v1/. But the rules ask the agent to explain what it did, and those explanations
naturally name the old endpoint. This run left one:

memclient/ingest.py:146:    API-015: /api/v1/memories/agent folded into the one `add` endpoint

A docstring, not a call site — a careful agent judges it correctly, which is exactly the kind of thing
worth making deterministic. Either exclude comments and docstrings from those two checks, or ask for the
old path to be written without the /api/ prefix in prose (v1 /memories/agent).

One thing worth telling users explicitly

7b's "do not run the test suite" is the right call (a suite that used to point at a dev gateway now points
at production, precisely because 1.x ignores EVER_OS_BASE_URL). The trade-off is that the one defect
that broke last round is in the class only a real run catches. 7c's checklist now names all four of them,
which is the right mitigation — worth saying plainly in the final report that the post-migration run is
the human's job
, so nobody reads "12 passed" out of py_compile.

Everything else I raised is closed: .update(.patch(, agent_memory/raw_message moved onto
search, the two removed search params documented, the removed-symbol import rule split per language,
SDK-018 for test doubles, and "say so when you work around a gap in these rules" — which is the line I'd
keep above all the others.

…run is the human's job

From GaussDing's second review round. Step 2 row 2 and Step 7d grep for /api/v1/, and the
agent's own flag comments and rule citations name that path, so a re-run could read its
own explanation as a leftover call site. Count code only, and write old endpoints in prose
without the /api/ prefix. The BEFORE YOU SHIP block now says plainly that this tool did not
run the test suite and that py_compile passing is not tests passing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@dani1005

Copy link
Copy Markdown
Collaborator Author

Thanks for re-running from a fresh baseline rather than reusing the earlier bed; a first-run 12 passed / 9 skipped / 0 failed on the updated branch is the number this PR needed. And thank you for the completed correction: same lesson on both sides of that one.

Both remaining points are in 0456b19:

  • Idempotency check reads code only. Step 7d now runs the grep in content mode and says explicitly that a match inside a comment, docstring or Markdown file is not a call site; Step 2 row 2 says the same. On top of that, the agent rules now ask for old endpoints in prose to be written without the /api/ prefix (v1 /memories/agent), so ingest.py:146 in your run would not have matched in the first place.
  • The post-migration run is the human's job, said in the report itself. BEFORE YOU SHIP gained the line: "This tool did not run your test suite, and a passing py_compile is not a passing test. Run the suite yourself, against a non-production key, before you switch traffic."

claude plugin validate passes; CI should be green shortly.

🤖 Generated with Claude Code

GaussDing's breakdown of the skips on the test bed: nine skipped tests are five
capabilities behind seventeen functions, and the human work behind each is a different
kind (product decision, customer engineering, a question for EverOS). The BLOCKERS block
now says which is which, so the customer knows whom to talk to.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@dani1005

Copy link
Copy Markdown
Collaborator Author

That table is the part of the report the customer actually needs, and it was missing: BLOCKERS gave counts and locations but not who has to do what. 062458e adds it to the Impact Report template. Under the seven rows the report now says, per category, whether it is a product decision (group memory, delete by memory_id: ask EverOS before picking a workaround), engineering on the customer's side (async, sender registry, with the SDK-004 / API-013 options), or a question for EverOS (settings carry-over), and reminds that the removed constructor options mean there is no retry layer left.

Your transport-errors row is already handled a step earlier: SDK-012 step 2 now rewrites except APIConnectionError to except urllib3.exceptions.HTTPError instead of flagging it, since both MaxRetryError and ReadTimeoutError derive from it on 1.1.0.

🤖 Generated with Claude Code

Dani and others added 2 commits September 15, 2026 15:16
…applies

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…round it

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@dani1005
dani1005 merged commit d1b8d4f into EverMind-AI:main Sep 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants