Skip to content

feat(*): upgrade everos to 1.4.1, lift the windows gate, guard the embedding width - #791

Merged
0xKT merged 13 commits into
mainfrom
feat/everos_1_4_1_upgrade
Sep 25, 2026
Merged

0xKT merged 13 commits into
mainfrom
feat/everos_1_4_1_upgrade

Conversation

@gloryfromca

@gloryfromca gloryfromca commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Summary

EverOS 1.4.0 runs natively on Windows and 1.4.1 pins openai below 3, so the memory plugin's exact pin moves from everos[multimodal]==1.2.3 to ==1.4.1. The lock follows: pyarrow 24.0.0 -> 25.0.1 (EverOS's new floor), everalgo-boundary 0.2.0 -> 0.2.1, everalgo-core 0.4.0 -> 0.3.0 (EverOS now pins its everalgo layer with ==), and msvc-runtime on win32 only. lancedb stays at 0.34.0. Every internal everos symbol the adapter reaches is present in 1.4.1 with the same signature, so the adapter itself is unchanged. The plugin manifest and package version move to 1.4.0.

The Windows gate is gone. everos_platform_note (#737) and its five call sites, ServiceState.UNSUPPORTED, the import.scan refusal (#750), the wizard's WSL notice and its zh catalogue line are removed; _everos_executable looks for everos.exe on win32. Spawning, probing and reusing the server were already portable. Identifying and stopping a stale server was not: _cmdline_of asked ps, and the marker everos server start never matched a Windows command line (...\Scripts\everos.exe server start). The command line now comes from WMI through PowerShell on win32 and the marker accepts both shapes; _listening_port asks the TCP table through PowerShell there, walking the launcher's descendants: the socket belongs to the base interpreter two launchers below everos.exe. The server is spawned in a process group of its own (the Windows counterpart of start_new_session, which also keeps a Ctrl-C at the gateway off it) and stopped with Ctrl-Break, which uvicorn takes as a shutdown; one that ignores it for ten seconds, or one no group can be addressed to, gets TerminateProcess as before. And because Windows cannot replace a running executable, the upgrade helper stops whatever still runs from under the tool environment before it installs (the server outlives the gateway by design); without that, uv failed on Scripts\everos.exe with os error 32. Recorded in docs/memory-plugin-architecture.md section 7.4.

Two guards, found while verifying against a copy of a real 1.2.x store:

  • set_embedding_endpoint measures the model's vector width before writing the pin and refuses anything narrower than the 1024-wide memory index (REQUIRED_EMBEDDING_DIMENSIONS), but only where an EverOS backend will read the pin: the everos-memory distribution is installed and the config names everos as the memory backend (an absent key is the schema default; an explicit null or another backend is not gated, and a knowledge base sizes itself to whatever width the model returns). The wizard's own check imports the same constant and probe. A 768-dimension model pinned through the settings page had left every memory store and search answering 500 about a mismatched width, with nothing on the page saying why. A probe that cannot reach the provider is not a verdict: the pin is written and a warning logged. The two RPC writers run the (now network-touching) writer in a thread.
  • ensure_everos_server reads the running server's /health version and, for a root raven owns, sends a mismatch with the installed everos through the same precheck / stop / spawn chain a rotated credential takes (stale_reason). Raven used to reuse whatever answered on the port, so an upgrade left the old server serving until something unrelated restarted it. A root the user manages is never touched.

The wire contract is pinned. Every request body the plugin and the memory page send (/add with a tool exchange, /flush, the four /search shapes, the four /get kinds) is validated against everos 1.4.1's own request models in TestRequestBodiesMatchEverosModels, so a schema move on the next upgrade fails in the suite rather than as a 422 in a gateway log.

A six-angle review then hardened the degraded paths (turn survival under every EverOS failure, Windows lifecycle, upgrade paths, the embedding guard, the wire contract, concurrency). The rule: memory degrades with a notice and never blocks a turn, a start or an upgrade.

  • A gateway starts the server again when it finds nothing listening (or a probe that timed out) and nothing holding the OME lock: only for a root raven owns, never over a child of its own, at most once per thirty seconds. The only spawn used to be in start(), so a server that went away left a running gateway without memory until restarted by hand. A stop that was sent and did not finish is not adopted (uvicorn closes the port at once and finishes what it had), so the version-mismatch restart after an upgrade cannot leave the gateway on a dying server.
  • A command-line lookup that failed (no ps, PowerShell blocked, WMI wedged, timeout) is distinct from a process that is gone: the stop keeps waiting, the lock names nothing, and a settings save over a server that answers but cannot be identified is reported as not applied. A losing spawn no longer overwrites the live server's pidfile. stop_pid counts wall time and takes a grace; on Windows a backend drains the server it started at stop().
  • The upgrade helper asks again after Stop-Process and refuses to run uv while anything from the environment survives (an elevated or another user's process): --force removes the environment before writing and would have left every file but the one that could not go.
  • An embedding pin narrower than the index is measured at backend start (once per pin per process) and withheld from the spawn: EverOS runs keyword recall and keeps storing, and the notice names the model and width. This covers a pin written around the write-time check (before memory was on, unreachable at save time, edited by hand). The probe no longer raises on an odd response, times out at ten seconds, honours plugins.disabled; migrate_roles runs off the event loop it had put a provider round-trip onto.
  • The memory page uses the /api/v2 routes (v1 is EverOS's legacy alias), asks /health before a search the way the chat adapter does, and shows the server's own sentence on a refusal. The recalled profile is rendered as lines rather than a namespace(...) repr with its evidence fields; a tool-call-only row stores empty content, not "None"; top_k stays within 1..100; an empty query asks nothing; a failed capability probe is not cached. The wizard accepts a model wider than 1024 (EverOS keeps the first 1024, as the settings page already allowed) and re-prompts on the host's refusal instead of a traceback.

docs/memory-plugin-architecture.md section 7 records the upgrade (7.4) the way 7.3 recorded 1.2.1 -> 1.2.3.

Type

  • Fix
  • Feature
  • Docs
  • CI / tooling
  • Refactor
  • Other

Verification

Unit and RPC tests over every touched area:

env -u SERPER_API_KEY uv run --frozen --python 3.12 --extra dev pytest -q tests/test_everos_server.py tests/test_everos_backend.py tests/test_cli_onboard_commands.py tests/test_everos_discover.py tests/test_rpc_settings.py tests/test_everos_config.py tests/test_everos_plugin_discovery.py tests/test_cli_plugin_commands.py tests/test_core_plugin_stack.py tests/test_rpc_memory.py tests/test_rpc_import_sync.py tests/test_everos_http_adapter.py tests/test_memory_backend_protocol.py tests/test_memory_backend_contract.py tests/test_plugin_boundary.py tests/test_i18n_boundary.py tests/test_config_update*.py
# 1312 passed, 1 skipped (inotify is Linux-only) on the first commit; 960 passed after the review round; full suite 26214 passed, 2 failed, 120 skipped, the 2 failing on an untouched github/main checkout too (LibreOffice path, background child env)
uv run --frozen --python 3.12 --extra dev ruff check <changed files>   # All checks passed!
uv run --frozen --python 3.12 --extra dev ruff format --check <changed files>   # 20 files already formatted

Against a 46 MB copy of a store written by everos 1.2.x (568 episodes, 5528 atomic facts, seven LanceDB tables), never touching the live one:

  • everos server start on 1.4.1 opens it with no schema complaint; /health reports version 1.4.1, cascade.healthy true.

  • The same four /api/v2/memory/search requests on 1.2.3 and 1.4.1 return the same ids in the same order; BM25 scores drift in the third decimal place.

  • raven agent -m on this branch spawns everos 1.4.1, a "remember this" turn lands in episodes/episode-2026-09-24.md and .atomic_facts/, a fresh one-shot session recalls it, and a third session recalls episodes written under 1.2.x months earlier.

  • With a 1.2.3 server left on the port, the next raven agent -m logs runs everos 1.2.3 while this raven installs 1.4.1; restarting, the old pid exits and /health reports 1.4.1. The 1.2.3 server had opened the index 1.4.1 wrote with no complaint.

  • Windows 11 box, this branch checked out and synced: raven serve spawns everos.exe natively, the Memory page shows its four tabs instead of the platform sentence, a chat turn lands as an episode (/add 200, /flush 200, cascade upserted=1), and after a memory role change the next start logs holds credentials raven has since changed; restarting, the old everos pid exits and the new one answers /health 1.4.1.

  • The settings page refuses BAAI/bge-base-en-v1.5 on deepinfra with returns 768-dimension vectors and the memory index is 1024 wide (real probe), and the pin stays unchanged.

  • Adapter import smoke against 1.4.1: all 13 internal symbols present, signatures unchanged.

  • Windows 11 box, second round: with the server up, uv pip install --reinstall --no-deps everos==1.4.1 fails on Scripts\everos.exe (os error 32, exit 2); after the helper's sweep (Stopped what was still running from the old install (pid ...)) the same command succeeds and the relaunched gateway spawns a fresh 1.4.1. A stop from the spawning console runs the full uvicorn shutdown (Shutting down ... Finished server process) in 3 s; a stop from another console falls back and the process is gone in 2 s. lock_holder(root).port reads 18997 (launcher -> venv python -> base python).

  • tests/test_everos_server.py tests/test_updates_upgrade.py: 261 passed, 1 skipped; tests/test_everos_backend.py -k RequestBodies: 7 passed.

  • Review round (env -u SERPER_API_KEY uv run --frozen --python 3.12 --extra dev pytest over the everos, config, settings, memory page, import, onboard, plugin-stack, boundary, cycle-budget and contract files): 1225 passed, 1 skipped; test_everos_server.py 124, test_everos_backend.py 188.

  • Windows 11 box, review round: with the gateway serving, the everos launcher was terminated by hand (Stop-Process, nothing on 18997); the next turn answered without memory (recall failed ... state=unresponsive), and six seconds after it began the gateway logged started everos server / everos server ready, the pidfile named the new pid and /health answered 1.4.1. The first attempt did not fire because a dead port there takes 2-4 s to be refused, longer than the 1 s probe; the respawn now fires on a timed-out probe as well, guarded by the lock holder. The memory page's four counts and a search on the Cases tab went through the v2 routes.

  • Relevant tests pass locally

  • Relevant lint / type checks pass locally

  • User-facing docs or screenshots are updated when needed

Risk

  • Windows installs that used to read "long-term memory is not available on Windows yet" now spawn EverOS, and a stale server is identified and replaced there too. The stop there is Ctrl-Break with a ten-second fallback to TerminateProcess. An upgrade on Windows first stops every process whose executable lives under the tool environment: raven's own gateway, sub-agents and server, nothing outside that directory.

  • An embedding pin narrower than 1024 dimensions is refused at the settings page and the wizard, with the width in the message, only for an install whose memory runs on EverOS (plugin installed, backend everos or the default). Knowledge-only installs, memory off, and other backends keep any width. Existing pins are not touched.

  • An owned EverOS server whose version differs from the installed package is restarted on the next session start. A root the user manages is never restarted.

  • A gateway may now start an EverOS server on its own when its probe finds nothing listening and nothing holding the lock (owned roots only, at most every thirty seconds). On Windows a backend stops the server it started when it shuts down (Ctrl-Break, sixty seconds to drain), so a settings change or an upgrade there restarts the server rather than leaving it. An upgrade on Windows refuses to proceed while a process from the tool environment cannot be stopped, and says which pid.

  • An embedding pin narrower than 1024 that reached the config around the settings check is no longer handed to EverOS: recall runs on keywords with a notice naming the model, until a 1024-dimension model is pinned.

  • Rollback: revert the commit and uv sync. lancedb did not move, so an index written by 1.4.1 opens under 1.2.3 (exercised on the copy).

  • Security impact considered

  • Backward compatibility considered

  • Rollback path is clear for risky changes

Related Issues

N/A

…bedding width

EverOS 1.4.0 runs natively on Windows and 1.4.1 pins openai below 3, so
the plugin's exact pin moves from 1.2.3 to 1.4.1; pyarrow 25.0.1,
everalgo-boundary 0.2.1, everalgo-core 0.3.0 and msvc-runtime on win32
follow through the lock. Every internal everos symbol the adapter
reaches is present in 1.4.1 with the same signature, so the adapter is
unchanged.

The Windows gate from #737 and #750 (everos_platform_note, its five call
sites, ServiceState.UNSUPPORTED, the wizard's WSL notice and the zh
catalogue line) is removed, and _everos_executable looks for everos.exe
on win32. The manifest and package version move to 1.4.0.

Two guards, found while verifying against a copy of a real 1.2.x store:

- set_embedding_endpoint measures the model's vector width before it
  writes the pin and refuses anything narrower than the 1024-wide memory
  index; the wizard's own check reads the same constant and probe. A
  768-dimension model pinned through the settings page had left every
  store and search answering 500 about a mismatched width.
- ensure_everos_server reads the running server's /health version and
  sends a mismatch with the installed everos through the same precheck,
  stop and spawn chain a rotated credential takes, so an upgrade no
  longer leaves the old server serving until something else restarts it.

Verified on a 46 MB copy of a store written by 1.2.x: 1.4.1 opens it with
no schema complaint, keyword searches return the same ids in the same
order, a real turn writes a new episode and a fresh session recalls it
together with episodes written months earlier, and a 1.2.3 server left
on the port is replaced by 1.4.1 on the next start.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: the embedding-width guard must not reject valid non-EverOS configurations.

I found one blocking issue, marked inline.

Coverage: I read the complete github/main...HEAD diff, the embedding/knowledge callers, EverOS backend and server lifecycle callers, relevant history, dependency and lock changes, and the Windows compatibility and stale-server paths. I also checked AGENTS.md/CLAUDE.md, CONTEXT-MAP.md and the Runtime context, backward compatibility, and the deleted/replaced tests for weakening. No additional issue survived concrete-failure refutation.

Verification:

  • uv lock --check: passed.
  • Selected changed-area suite after uv sync --all-packages: 835 passed. The first fresh-environment attempt could not collect because the workspace plugin was not installed; the first synced run then hit one non-reproducible failure in an untouched environment-mirroring test (555 passed before stop), whose isolated retry passed; the full rerun passed.
  • uv run pytest tests/test_knowledge_manager.py tests/test_knowledge_embedding.py -q: 73 passed.
  • uv run python scripts/check_source_language.py github/main...HEAD: passed. (make itself is unavailable in this environment.)
  • git diff --check github/main...HEAD: passed.

Comment thread raven/config/update.py
The width check in set_embedding_endpoint refused a 768-dimension model
for every install, including one whose memory.backend is null or another
backend. A knowledge base sizes itself to whatever width the model
returns, so that refusal blocked a valid knowledge-only configuration.
The check now runs only when the raw config names everos as the memory
backend (an absent key is the schema default, everos; an explicit null is
memory off), read raw so the loader's migrations stay out of a settings
write.

Also covers the three new readers the coverage gate found bare:
probe_embedding_dimensions against a mock transport, running_everos_version
against a faked /health, and installed_everos_version with the package
present and missing. The two autouse network stubs hand the real function
back to the tests about it.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: the missing-plugin knowledge path must bypass the EverOS-only width requirement.

The explicit memory.backend = null and alternate-backend cases from the prior finding are fixed. The original thread remains open because the separately distributed everos-memory plugin can be absent while the schema default still names everos; in that supported knowledge-only installation, this revision still rejects a valid 768-dimensional pin. The concrete reproduction and required boundary are in the thread reply.

Coverage for this revision: the delta from 881428a45e95, the full standing diff, generic embedding and knowledge callers, default-backend and plugin-presence semantics, tests added or changed, relevant history and docs, backward compatibility, AGENTS.md/CLAUDE.md/CONTEXT terminology and layer constraints, and import architecture. No other candidate survived concrete-failure refutation.

Verification:

  • uv lock --check: passed.
  • uv run --frozen --python 3.12 --extra dev pytest -q tests/test_rpc_settings.py tests/test_config_update.py tests/test_everos_server.py tests/test_everos_config.py tests/test_cli_onboard_commands.py tests/test_knowledge_manager.py tests/test_knowledge_embedding.py tests/test_everos_backend.py tests/test_rpc_memory.py tests/test_rpc_import_sync.py: 961 passed.
  • uv run --frozen --python 3.12 --extra dev lint-imports: 10 contracts kept.
  • Ruff on the changed Python files: passed.
  • Source-language gate and git diff --check: passed.

… raven.core

_everos_consumes_the_pin imported SHIPPED_DEFAULT_BACKEND from
raven.core.plugin_stack, and raven.core imports raven.config, which drew
config and core into a mutual pair and pushed the import-cycle budget over
its ceilings (20 packages in cycles over 18, 14 mutual pairs over 13). The
same value is the default of MemoryConfig.backend inside this package, so
it is read from there.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: the missing-plugin knowledge path still has to bypass the EverOS-only width requirement.

The new commit fixes the dependency direction by reading the shipped backend name from the schema, but it does not test whether that backend is installed. With everos-memory absent, everos_plugin_installed() is false while this predicate still follows the schema default; a 768-dimensional knowledge model is therefore rejected and the embedding pin is not written. The existing thread remains outstanding.

Coverage included the repository rules, the revision delta, the shared writer and its knowledge/memory callers, relevant history, backward compatibility, test changes, and import-layer constraints. The direct missing-plugin reproduction failed as above. The affected test selection reported 947 passed and 14 failed in unrelated web-onboarding environment tests; five representative failures passed in isolation, so the run is not green but those failures are not attributed to this one-file delta. Ruff, all 10 import contracts, diff hygiene, and the source-language check passed (the underlying script was run directly because make is unavailable).

…absent

A core or knowledge install ships without the everos-memory distribution
and still carries the schema default memory.backend = "everos", so the
gate read "EverOS consumes this pin" where no EverOS backend existed and
refused a valid 768-dimension knowledge model. _everos_consumes_the_pin
now also asks whether the plugin is present, the same way
raven.core.plugin_stack.everos_plugin_installed does, kept as a copy in
this package because importing it back would close a cycle.

Two cases pin both sides: without the plugin a 768-dimension pin is
written under the default backend; with the plugin it is refused.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; this can merge as far as I am concerned.

The missing-plugin fix is effective: the supported core/knowledge installation can now save a narrow embedding pin, while installed and present-but-broken EverOS states retain the 1024-width guard. The original review thread has been resolved.

Coverage included the repository rules, the full PR diff and latest delta, affected callers, relevant history, backward compatibility, test-strength changes, and config/core architecture constraints. Verification: 237 focused config/RPC/knowledge tests passed; 4 import-cycle budget tests passed; Ruff, all 10 import contracts, diff hygiene, and the source-language check passed. No tests were weakened in this revision.

gloryfromca and others added 2 commits September 25, 2026 00:11
With the platform gate gone the stale-server path runs on Windows, and it
never identified the server raven itself spawned there. Two reasons:
_cmdline_of asked ps, which native Windows does not have, so lock_holder
read an empty command line and answered None; and the marker
"everos server start" never matched a Windows command line, which prints
the executable quoted with its extension ("...\Scripts\everos.exe" server
start). Restart-on-role-change and restart-on-version-change therefore
left the old server serving with "could not be identified".

_cmdline_of now asks WMI through PowerShell on win32 (console set to
UTF-8 so a non-ASCII path survives), _is_everos_server reads through it
instead of running ps a second time, and the marker is a pattern that
accepts both shapes. The pidfile fallback that names the pid already
existed. The listening-port lookup still answers None on Windows: it only
feeds one wizard sentence.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
The case waits out two 1500 ms ready timeouts, 3.0 s of idle against the
suite's 3 s ceiling, and failed two CI shards at 3.04 s and 3.07 s. The
silent stub never answers, so the budget is arbitrary; 1000 ms is what
the neighbouring silent-stub case already uses.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; this can merge as far as I am concerned.

The new revision makes Raven-owned EverOS process recognition work with the quoted everos.exe command line returned on native Windows, while retaining the command-marker and root checks that prevent acting on unrelated processes. POSIX process lookup remains unchanged in behavior, and the subagent timeout adjustment tightens rather than weakens its test.

Coverage included the repository rules, the full PR diff and latest delta, affected lifecycle callers, relevant history, backward compatibility, test-strength changes, and architecture constraints. Verification: 388 EverOS server/subagent tests passed, 4 resolved embedding-width regression cases passed, Ruff passed, all 10 import contracts held, and diff/source-language checks passed. The previously opened thread remains resolved.

Section 7.4 said restart-on-role-change and orphan cleanup answer "unknown"
on Windows; the previous commit made them work there and a Windows 11 box
confirmed the role-change restart. The paragraph now says what changed,
what was verified, and the two things still true on Windows: the
listening-port lookup answers None and the stop is TerminateProcess.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; this can merge as far as I am concerned.

This revision only updates the memory-plugin architecture record, and its Windows lifecycle description matches the implemented command-line lookup, process ownership checks, stop behavior, and optional port reporting.

Coverage included the repository rules, full PR context and latest delta, lifecycle callers and history, backward compatibility, test-strength changes, and architecture constraints. Verification: 114 EverOS server tests passed; large-file, source-language, and diff-hygiene checks passed. No tests were changed or weakened, and the previously opened thread remains resolved.

@0xKT

0xKT commented Sep 24, 2026

Copy link
Copy Markdown
Member

Not a blocker. Accepted on 44f6726b merged onto e1769411. Three lanes, 25 agents, every finding then put to three independent refutation lenses: 12 stood, 1 died, none blocking. The one that died was killed on its own control -- the identical reproduction on base e1769411 gave the identical result, so it PREDATES and is not yours.

The Windows work is real and the doc rewrite in 44f6726b is a good piece of writing: it names what was actually unportable (_cmdline_of asking ps; the marker never matching ...\Scripts\everos.exe server start), cites a Windows 11 verification, and volunteers that os.kill is TerminateProcess there. Two of my findings were about that paragraph being stale and both are closed by it.

But the rewrite kept one sentence, and it is the one I would most want changed.

1. _listening_port returning None is a PREDICATE, not a silenced sentence

server.py:569 and now docs/memory-plugin-architecture.md:463 both say the cost is display only -- "only silences the wizard's 'serves on port N' sentence". Two production branches read holder.port as a decision:

  • onboard.py:1187 -- if holder is not None and holder.port == current: return current. With port=None this never matches, so a Windows user reconfiguring is told "Port N is already in use by something else" about their own raven-managed server, and dropped into the port prompt. The comment two lines above (onboard.py:1183-1185) says that branch exists precisely to prevent this misreading: "When the holder of this root's lock is the thing listening there, the port is not taken -- it is ours."
  • onboard.py:1510 -- if holder is not None and holder.port: is a two-way switch. With None it prints "holds <root> but serves no HTTP. Stop it and re-run raven onboard" and returns _leave_as_is(), about a server that is serving HTTP fine.

Measured with a real subprocess -- a script named everos, really taking the portalocker lock, really binding and listening -- and the only stand-in is the environment (lsof made to raise FileNotFoundError as an absent binary does; /proc genuinely absent on this darwin host):

CONTROL (full POSIX)   lock_holder -> LockHolder(pid=<real>, port=61894)
                       _ask_managed_port returns 61894, prints nothing, prompt never called
WINDOWS-SHAPED         lock_holder -> pid recovered from the pidfile, port=None
                       screen: "! Port 61899 is already in use by something else."
                       prompt: ['Memory service port:']

Positive control that the probe can find a port at all: a sibling test asked for 62285 and got 62285. So the None is the environment, not a broken probe.

The docstring's other clause does not hold either: "the holder is still identified and stopped" -- _use_found_root stops nothing, it returns _leave_as_is() and tells the user to go stop it by hand.

The lane rated this non-blocking and I agree with the rating -- one branch costs a screen, the other sits behind a narrow precondition. What I am flagging is that a rewrite passed over this sentence and kept it, so it is now asserted in two places.

2. With no pidfile, win32 lock_holder has no source left -- and the page reports success

_lock_holder_pid asks /proc/locks (absent on Windows), then lsof (absent), then the pidfile. On win32 the pidfile is the only source, which is exactly the case lock_holder's own docstring says the lock lookup exists to survive. With it gone, lock_holder returns None, stop_for_reload returns None meaning "nothing is serving this root", and restart_for_config_change:1160 treats None as not-a-failure, falls through to ensure_everos_server, adopts the still-running old server and fires on_result(True, None) -- so the settings page says the change applied while the old server keeps serving the old config.

CONTROL (POSIX)   stop_for_reload -> STOPPED, holder dead, results=[(False, 'EverOS memory LLM is not configured: ...')]
WINDOWS-SHAPED    lock_holder -> None, stop_for_reload -> None, old server alive, page told success

3. The spawn's detach is a documented no-op on Windows

server.py:916 passes start_new_session=True. CPython documents it POSIX-only and its Windows _execute_child discards it (bound as unused_start_new_session, no ValueError), and no CREATE_NEW_PROCESS_GROUP / DETACHED_PROCESS is passed instead -- so the "detached" server joins raven's console group. I could not execute the win32 branch (this host is darwin), so that half is read, not run, and I say so. What I did measure: deleting start_new_session=True entirely leaves the suite green (679 passed, 1 skipped), while a control mutant in the same function family (os.kill -> pass in stop_pid) is caught. Nothing pins it in either direction.

4. The guard relaxation stops two states short

_everos_consumes_the_pin asks two things -- distribution installed, memory.backend names everos -- and calls that "whether an EverOS backend will read the pin". Two further states make that false, and the repo already models both:

  • plugins.disabled naming everos-memory: the registry contributes no everos backend, maybe_build_memory_backend returns None, the host prints "Long-term memory is off" -- and the guard still refuses a valid pin.
  • role_is_env_managed("embedding"): exported EVEROS_EMBEDDING__* outrank the pin and everos_env() skips the role whole, so the pin never reaches EverOS -- and the guard still refuses.

Both are the reviewer's own case, a knowledge-only install that should keep any width. Measured, with a positive control proving nothing reads the pin in that state (real build_plugin_registry / maybe_build_memory_backend, no stubs): DISABLED -- backends: [] -> backend: None, host said "Long-term memory is off", and the guard still answered config_validation_error.

So the relaxation answered "does the wrongly-rejected case now pass" for the states it enumerated, and the guard does still bite on a genuinely wrong width -- I checked that half too, which is the half that matters most when a guard is loosened.

5. One definition became two

_everos_plugin_present (config/update.py:694) is a byte-equivalent private copy of plugin_stack.everos_plugin_installed, which six existing sites ask -- three of them in the very pair this re-derives. The copy also silently drops the plugins.disabled term its neighbours honour, which is finding 4. Your cycle justification is real and the lane confirmed it (pointing the copy at the owner trips test_import_cycle_budget at 14 pairs over a ceiling of 13), but it does not force a copy -- a leaf both packages may import passes every gate.

Nits

  • probe_embedding_dimensions breaks its own "a shape failure is a str, not a verdict" contract: three malformed-200 shapes escape as verdicts.
  • _SERVER_CMDLINE is dead after both consumers moved to _SERVER_CMDLINE_RE.
  • _NoOpAdapter lost its only production assignment; the adapter docstring still calls it one of "Two production implementations".
  • The plugin's version is three literals plus a test literal with no checker -- and the base tree shows them already drifted. Predates you.

Receipts

ruff / lint-imports / commit / large / lang rc=0 on 09ba40fc and re-run on 44f6726b. Suite runs through the board's pyt.sh throughout. The 44f6726b delta is one file (docs/memory-plugin-architecture.md), nothing under raven/, so the ten findings above stand as measured and the two doc findings are closed by it.

…addressable

Four gaps the Windows gate removal exposed, each verified on a Windows 11 box:

- The upgrade helper could not replace Scripts\everos.exe while the server
  raven had started was still running (uv: os error 32). After waiting for
  the parent, the helper now stops every process whose executable lives
  under the tool environment, then installs.
- The server is spawned in a process group of its own (the Windows
  counterpart of start_new_session) and stopped with Ctrl-Break, which
  uvicorn takes as a shutdown. A server that ignores it for ten seconds, or
  one no group can be addressed to, gets TerminateProcess as before. A
  Ctrl-C at the gateway no longer reaches the server.
- _listening_port asks the TCP table through PowerShell and walks the
  launcher's descendants: the socket belongs to the base interpreter two
  launchers below everos.exe, so the wizard's port step no longer reports
  raven's own server as a stranger.

Also pins the wire contract: every request body the plugin and the memory
page send is validated against everos 1.4.1's own request models, so a
schema move on the next upgrade fails in the suite rather than as a 422 in
a user's gateway log.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; this can merge as far as I am concerned.

This revision closes the remaining Windows lifecycle gaps coherently: Raven-created servers get their own process group for graceful Ctrl-Break shutdown, listener discovery follows the launcher descendants, forced fallback remains bounded, and the detached upgrade helper sweeps only executables under the Raven tool environment before invoking uv. The added EverOS-model validations also exercise the upgraded wire contracts directly.

Coverage included the repository rules, the full PR diff and latest delta, process/restart/upgrade callers, relevant history, backward compatibility, test-strength changes, and architecture constraints. Verification: 436 EverOS server/backend and upgrade tests passed; Ruff passed; all 10 import contracts held; large-file, source-language, and diff-hygiene checks passed. No tests were weakened, and the previously opened thread remains resolved.

@0xKT

0xKT commented Sep 25, 2026

Copy link
Copy Markdown
Member

Not a blocker. Accepted on fe4527f1 merged onto e1769411. Re-review of the DELTA only (44f6726b..fe4527f1), since I had already accepted 44f6726b: three lanes, 10 agents, every finding then put to independent refutation lenses. 6 findings, all nit severity: 5 stood, 1 died. Nothing blocking, nothing I rated non-blocking from the lanes' own set.

This commit answers two of the five items in my note on the previous head, and it answers them properly rather than by rewording. Item 3 (start_new_session=True is a documented no-op on Windows) is now _spawn_kwargs() with CREATE_NEW_PROCESS_GROUP. Item 1 (_listening_port returning None is read as a decision at two onboard.py branches) is now _windows_listening_port. The doc sentence I said I would most want changed is gone, not patched around.

One thing I checked before anything else, because it was the only way this could have been blocking, and it holds: the sweep cannot kill the upgrade itself. _external_executable refuses any interpreter inside the environment -- if not executable.is_file() or executable.is_relative_to(prefix): raise UpgradeError(...) (upgrade.py:866-867), with prefix = Path(sys.prefix).resolve(strict=True), and sys.prefix is exactly what the sweep targets. Both spawn sites go through it. A junction under UV_TOOL_DIR makes the guard's resolved compare and the sweep's raw StartsWith disagree, and the disagreement points the safe way: the sweep under-matches, never over-matches onto the helper. Answer: no.

And it does run: UV_TOOL_DIR is set by raven itself at upgrade.py:883 and :972; the only other setters in the tree are CI. That last hop was proved by execution, not by reading -- the shipped _UPGRADE_HELPER_SOURCE string was exec'd with only wait_for_parent and subprocess.run stubbed, and the real run() reached the real stop_leftovers_of.

The one thing I would most want changed

The descendant walk validates nothing, and a wrong port is worse than no port.

_windows_listening_port seeds on the pid raven holds -- which is validated, lock_holder checks _SERVER_CMDLINE_RE and that the root appears in the cmdline -- then walks every descendant and takes -First 1 from an unsorted Get-NetTCPConnection over every listening socket owned by the seed or any descendant. Nothing checks the processes the walk adds. Two ways that yields a confident wrong answer: a descendant listening on something that is not the everos HTTP port, and Windows never clearing a dead parent's pid from Win32_Process.ParentProcessId, so a long-lived process spawned by an earlier holder of that pid is pulled in with its whole subtree.

What the wrong value reaches, onboard.py:1508-1518:

if holder is not None and holder.port:
    found_at = f"http://localhost:{holder.port}"
    ...
    _set_base_url(found_at)
    _report_everos_capabilities()
    return StepOutcome.CONFIGURED

So it points memory at a stranger and reports CONFIGURED. Before this commit that branch could not be entered on Windows, because the port was always None -- the old behaviour was wrong but safe-by-omission, and the fix converts "no answer" into "possibly a wrong answer" on the one path that acts on it. Validating the walk's members the way the seed is validated would close it.

_capture compounds this: it never inspects returncode (server.py:538-545; return out.stdout.strip() regardless), so a failing command's partial stdout comes back as an answer. Verified by feeding it rc=1 with digits on stdout and getting the digits. stderr cannot leak in -- capture_output keeps the streams apart.

I am rating this non-blocking, and I want to be exact about why: it needs a coincidence, and I have no Windows host, so this is read, not run. Every Windows statement in this review is a reading of the script text.

What nothing pins

Five of the six findings are the same shape -- the delta's load-bearing decisions ship green when retracted:

  • _GRACEFUL_SIGNAL = getattr(signal, "CTRL_BREAK_EVENT", signal.SIGTERM) (server.py:33) is the line the whole commit rests on. Rewriting it to signal.SIGTERM -- which on Windows silently restores the old unconditional TerminateProcess -- leaves the suite fully green. The new Windows tests stand in for the constant, not the environment, so line 33 executes every run and its value is never asserted.
  • forced = sys.platform != "win32" (server.py:367) is the single line keeping this Windows commit off POSIX. forced = False stays green, and it is not a semantic no-op: at waited >= 10.0 a still-draining POSIX server takes a second SIGTERM, which uvicorn's handler treats as force-exit.
  • The Popen at server.py:983 that actually carries **_spawn_kwargs(): two tests cover the helper, none assert the call site passes it. Deleting the line leaves the everos suite green. (This half predates -- it is the same hole my earlier item 3 measured on start_new_session=True. The behaviour got fixed; the pinning did not.)
  • The sweep's OrdinalIgnoreCase, -Force, and the 2s settle all survive mutation; the one test asserts only that two substrings appear in the generated script.
  • test_upgrade_helper_sweeps_the_environment_after_the_parent_and_before_uv asserts os.path.join(r"C:\tools", "raven"), and on darwin os.path is posixpath -- what it pins is C:\tools/raven, so the Windows-shaped value is never exercised in the default run.

Nit

tests/test_everos_server.py:1180 sets _GRACEFUL_SIGNAL to 21 under the comment "CTRL_BREAK_EVENT's value there". On Windows CTRL_BREAK_EVENT is 1; 21 is SIGBREAK, the signal the receiver sees after a Ctrl-Break is delivered. The shipped code is unaffected -- getattr resolves the real value -- but the number a future reader copies out of that comment is the wrong one. uvicorn's own source makes the distinction: HANDLED_SIGNALS += (signal.SIGBREAK,) # Windows signal 21. Sent by Ctrl+Break.

One finding died

"The sweep prints 'Stopped ...' for pids it may not have stopped" was refuted on its own payoff: run_uv does not capture uv's output, so uv's real diagnostic prints after that line and :434 adds "Unable to upgrade Raven: uv exited with status N". The true blocker is on screen, later and louder. A cosmetic line followed by the real error is not a user being pointed away.

Two observations, neither filed

By the commit's own account of the process tree -- "the pid raven holds is the everos.exe launcher, which runs the environment's python.exe, itself a launcher for the base interpreter, and that grandchild is what holds the socket" -- the sweep kills the two launchers under the environment but not the grandchild, whose executable is the base install and so fails StartsWith($root). That is fine for the stated purpose (uv tool install failing on everos.exe), and I am not calling it a defect; it does mean the thing still holding the socket survives the sweep.

The sweep also leaves the plugin's bookkeeping behind: stop_pid unlinks the pidfile as part of a successful stop, the sweep touches neither pidfile nor OME lock. "Stopping raven's everos server" now has two implementations with different after-states, and the same commit teaches one of them a ten-second grace while the other force-terminates the same process with none.

What I did not check

Nothing on a real Windows host: no powershell.exe, no Get-NetTCPConnection, no Get-CimInstance, no Stop-Process, no uv tool install. Every win32 branch was driven with sys.platform forced and the absent constants stood in for. POSIX was verified by running the real stop_pid against a real child process and is bit-for-bit unchanged from 44f6726b.

…o away

What a six-angle review of the branch turned up, each a scenario a real
install reaches. The rule behind the fixes: memory degrades with a notice
and never blocks a turn, a start or an upgrade.

- A gateway starts the server again when it finds nothing listening and
  nothing holding the OME lock (at most once per thirty seconds, only for
  a root raven owns, never over a child of its own). The only spawn used to
  be in start(), so a server that went away left a running gateway without
  memory until it was restarted by hand.
- A stop that was sent and did not finish is not adopted: uvicorn closes
  the port at once and finishes the requests it had, so ensure raises and
  the probe above starts a replacement once the lock is free.
- A command-line lookup that failed is distinct from a process that is
  gone: the stop keeps waiting, the lock names nothing, and a settings save
  over a server that answers but cannot be identified is not reported as
  applied. A losing spawn no longer overwrites the live server's pidfile.
- stop_pid counts wall time and takes a grace; on Windows a backend drains
  the server it started at stop(), so an upgrade finds nothing to terminate
  mid-write. The helper asks again after Stop-Process and refuses to run uv
  while anything from the environment survives: --force removes the
  environment before writing.
- An embedding pin narrower than the index is measured at backend start
  and withheld from the spawn, so EverOS runs keyword recall and keeps
  storing instead of answering 500 to everything; the notice names the
  model and the width. The probe no longer raises on an odd response, times
  out at ten seconds, honours plugins.disabled, and migrate_roles runs off
  the event loop it had put a provider round-trip onto.
- The memory page uses the /api/v2 routes, asks /health before a search the
  way the chat adapter does, and shows the server's own sentence on a
  refusal. The recalled profile is rendered as lines rather than a
  namespace repr; a tool-call-only row stores empty content; top_k stays
  within 1..100; an empty query asks nothing; a failed capability probe is
  not cached. The wizard accepts a model wider than 1024 and re-prompts on
  the host's refusal.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; suggestions only, and they are marked inline.

This revision materially strengthens the Windows server lifecycle and the degraded-memory paths: stopped servers can respawn, draining servers are not adopted as healthy, unknown process lookups stay unknown, upgrade survivors prevent a destructive install, narrow out-of-band embedding pins degrade to keyword recall, and the memory page now speaks the current EverOS routes and request shapes.

Named nonblocking follow-up: test_upgrade_helper_refuses_to_install_over_a_survivor reads capsys twice and ends its PID assertion with or True, so that assertion cannot fail. Reading stderr once and asserting both fragments would make the new survivor diagnostic coverage real; this does not hold the runtime change.

Coverage included the repository rules, full PR diff and latest delta, lifecycle/config/RPC callers, relevant history, backward compatibility, test-strength changes, and architecture constraints. Verification: 939 affected tests passed; Ruff passed; all 10 import contracts held; source-language and large-file checks passed. The previously opened thread remains resolved.

gloryfromca and others added 2 commits September 25, 2026 15:09
On a Windows box a connection to a port nobody listens on took two to four
seconds to be refused, longer than the probe waits, so the verdict read
"slow" rather than "gone" and the gateway never started a replacement. A
server that is merely slow still holds the lock, which the respawn checks
first. Verified on that box: the server killed behind the gateway was back
six seconds after the next turn began. Also formats the onboard test file
the lint job flagged.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
…ments

The assertion ended in "or True" and read capsys twice, so it could not
fail; the reviewer named it.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; suggestions only, and they are marked inline.

The new TIMEOUT recovery path is safe to merge. _may_respawn still limits it to a Raven-owned HTTP backend with no live child and outside the cooldown, while _respawn checks the EverOS lock before starting anything; a merely slow live service therefore remains untouched in ordinary use. The added regression test covers the Windows dead-port behavior that motivated the change.

The previously named test-only assertion follow-up remains on this revision, so the whole-record stance stays nonblocking rather than clean. No new inline findings.

Covered this round: repository rules, the full PR diff and d8a5ee43f33c..a2dcb6aee778 delta, surrounding probe/respawn callers and history, backward compatibility, test integrity, and the config/core architecture boundary. Verification: git diff --check github/main...HEAD; 313 affected tests passed; Ruff passed; all 10 import contracts passed; source-language and large-file checks passed via their underlying scripts (the make executable is unavailable in this environment).

The lookup returns None for "could not be asked" since the last commit; the
signature still said str, which the type check refused.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; this can merge as far as I am concerned.

The remaining test-only follow-up is fixed: stderr is captured once, and the survivor diagnostic now has to contain both the general warning and pid 2. This removes the unconditional pass without changing runtime behavior. No other issue surfaced in the delta.

Covered this round: repository rules, the full PR diff and a2dcb6aee778..5c4a4dae05ef delta, the affected upgrade helper behavior and history, backward compatibility, test integrity, and architecture constraints. Verification: git diff --check github/main...HEAD; 142 upgrade tests passed; Ruff passed; all 10 import contracts passed; source-language and large-file checks passed. The review thread opened earlier is already resolved.

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; this can merge as far as I am concerned.

The revised _cmdline_of annotation now accurately describes its existing three outcomes: a command line, an empty string for a vanished process, or None when lookup is unavailable. Both callers already narrow None before using the value, so this introduces no runtime or compatibility change.

Covered this round: repository rules, the full PR diff and 5c4a4dae05ef..e8d4306f2486 delta, both callers and relevant history, backward compatibility, test integrity, and architecture constraints. Verification: git diff --check github/main...HEAD; 125 server tests passed; Ruff and targeted ty checks passed; all 10 import contracts passed; source-language and large-file checks passed. The review thread opened earlier remains resolved.

@0xKT

0xKT commented Sep 25, 2026

Copy link
Copy Markdown
Member

Not a blocker. Accepted on e8d4306f merged onto e1769411. Third pass on this PR -- I had already accepted 44f6726b and fe4527f1; this reviews only the delta fe4527f1..e8d4306f (4 commits, 16 files, +988/-86). Four lanes, 32 agents, every finding then put to independent refutation lenses. 18 findings stood, 0 refuted: 0 blocking, 8 non-blocking, 10 nits.

Nothing here blocks the merge. But eight of the eighteen are one shape, and I think naming the shape is worth more than the list.

One predicate, two readers, one of them already fixed

Three times in this delta, a value that means three things is read as if it meant two -- and in each case the correct reading is present in the same commit, a few hundred lines away.

1. _is_everos_server is now tri-state; one of its two readers ignores that.

server.py:384-386 reads it right, under a comment that states exactly the harm the other reader causes:

# ``None`` (the question could not be asked) keeps waiting: the caller
# would otherwise start a second server against a lock this one holds.
if _is_everos_server(pid) is False:

server.py:1016 reads the same predicate as a plain truth value:

if isinstance(recorded, int) and _is_everos_server(recorded):

None is falsy, so it takes the else and writes the losing child's pid over the pidfile naming the live, still-booting server. On Windows that pidfile is the only source _lock_holder_pid has, so after this nothing can name the running server. Reproduced against a real first process whose real ps line matches _SERVER_CMDLINE_RE, with the shipped _start_server_if_unlocked; the only stand-in was _capture returning None, which is the shipped function's own value for "could not be run" (server.py:563-571), with a positive control calling the real _capture on a missing binary to prove it. Control (lookup works): pidfile keeps the live pid, and the guard logs everos pidfile still names a live server (...); not replacing it. Arm (lookup cannot be asked): pidfile names the doomed child.

Your own doc states the protection unconditionally -- docs/memory-plugin-architecture.md:509-511, "A spawn that loses the boot race no longer overwrites the pidfile of the live server ... on Windows the pidfile is the only way back to it" -- while the bullet three lines above it names the WMI/PowerShell failure that breaks it.

2. lock_holder returning None means two different things, and _respawn picks the wrong one.

This is the one I most want changed, because it is the claim the whole TIMEOUT widening rests on. backend.py:674-677 says: "A server that is merely slow still holds the lock, which is what _respawn checks first." But lock_holder collapses "nobody holds it" and "I could not find out who holds it" into one None (server.py:747), and backend.py:709 reads it as free:

if await asyncio.to_thread(lock_holder, everos_root(), with_port=False) is not None:
    return

The correct reading of the same None is in this same commit, at server.py:1293:

if outcome is None and await asyncio.to_thread(_probe_health, base_url):
    # Something answers on the address and the lock could not name it
    # (a lookup that failed, a pidfile lost on Windows).

restart_for_config_change cross-checks with a probe and refuses to act. _respawn -- the automatic path that fires every 30s -- does not. Reproduced with a real TCP listener that accepts and never answers (so the shipped probe_health really returns TIMEOUT on its real 1.0s budget) and a real process holding a real ome.db.lock whose real ps line identifies it. Control: lock names the holder, no second server. Arm: lookup unavailable, lock_holder is None, a second server is started beside the live one. Same slow server, same verdict, same shipped code -- the only difference is whether the lookup can be asked.

Second-order, read but not reproduced: the loser still rewrites <root>/everos.toml [api] and calls record_role_digest(root) while the live server runs, so roles_changed_since_spawn later compares the current digest against itself and a rotated credential never triggers the stale restart.

3. _search_tuning names its owner and then reimplements it.

memory.py:82-83 says it asks "the way the chat adapter asks (_search_tuning in raven_everos.backend)" -- and then writes it again instead of calling it. The two already disagree in 5 of the 16 (health x kind) cells. Sharpest: agent track on a server with no embedding -- the adapter sends {'method':'keyword'} and deliberately stops, its own docstring saying "Moot under KEYWORD, whose agent path does not go through rerank at all", while the new copy also sends enable_llm_rerank=True. They differ structurally too: the adapter's branches are exclusive early returns, the copy accumulates; the adapter reads caps.get("embed") raw, the copy goes through report.available("embedding").

The same architecture fact underneath all three: nothing in the tree owns "the everos server this process is responsible for". A fourth instance: _proc has three writers, but the settings-save path reaches ensure_everos_server(base_url) at server.py:1306 with no on_proc, so that server is a child nobody's backend knows about. After any memory-role save the long-lived backend's _proc names a process stop_for_reload already killed, the win32 drain at backend.py:1041 is skipped for the rest of that gateway's life, and the probe sees a healthy server so _respawn never refreshes it.

The cheapest one to fix, and it is measurable today

tests/test_rpc_memory.py opens with: "tests replace memory._post so no sockets open." That is now false. The new _search_tuning calls the real probe_capabilities -- httpx.get(f"{base_url}/health", timeout=5.0) -- and the autouse fake_cfg sets base_url to "http://x", so test_list_with_query_uses_search resolves the bare hostname x against the machine's real DNS and blocks for five seconds.

This delta recognised the problem twice and fixed it twice -- real_probe_embedding_dimensions and real_running_everos_version in tests/conftest.py exist precisely so two other new probes cannot reach the network from the suite. The third probe got no fixture. Your three new tests do patch raven_everos.health.probe_capabilities; the older one does not. I measured the file: 15 passed in 8.46s.

The pin guard: "Three things" is still not exhaustive

I raised two states on an earlier head. plugins.disabled is now handled -- thank you -- and the docstring was rewritten from "Two things have to hold" to "Three things have to hold", which reads as exhaustive. The second state, role_is_env_managed("embedding"), is handled nowhere in this delta. There, everos_env() skips the embedding section whole so the pin never reaches EverOS, yet _everos_consumes_the_pin still answers True, the new configured_embedding_width returns a measured width where its own docstring promises None, and withhold_role is inert. User-visible: a 768-dim pin saved from the settings page is refused with EmbeddingPinError in a state where its only reader is a knowledge base.

The rest of the non-blocking set

  • The Windows drain is bound to stop(), which is not only process exit. Its comment assumes "an upgrade replaces this environment's executables the moment this process exits", but stop() is also the teardown half of the per-record started_backend pair the sub-agent and DAG recorders use inside a living gateway. When that record's start() is what spawned the server, the record's stop() shuts it down again -- on Windows leaving the gateway without memory for up to _RESPAWN_MIN_INTERVAL_S = 30s. Before this delta stop() touched no child on any platform.
  • Nothing pins the one distinction _search_tuning turns on. report.available("embedding") is False is load-bearing: available returns True/False/None, and is False is what stops an unanswered probe forcing method="keyword". The code is correct; the suite does not hold it -- collapsing both clauses to is not True leaves the file green. You even built the fixture that would catch it (_server_that(embedding=None, rerank=None)) and then asserted only the exception message, never the request body.

Nits

_everos_error raises AttributeError out of the error handler when a 4xx body's error is a JSON string. _capabilities no longer caching an empty probe becomes one extra /health GET per recall against a server that can only answer empty. The tightened probe timeout (15s -> Timeout(10.0, connect=5.0)) turns a provider answering in 12s from measured into "timed out", and the wizard then refuses a model it used to accept. configured_embedding_width puts a synchronous embedding round trip on the awaited one-shot start path, where its per-process cache cannot help. _RESPAWN_MIN_INTERVAL_S = 30.0 is pinned by nothing. And EVEROS_PLUGIN_NAME was inserted between REQUIRED_EMBEDDING_DIMENSIONS and its docstring, orphaning it -- raven/config/update.py:473.

What I checked myself, and what I did not

I read both readers of each predicate directly rather than taking the lanes' word: server.py:386 against :1016, and server.py:1293 against backend.py:709. Both asymmetries are as described. I read tests/test_rpc_memory.py and confirmed the three new tests patch the probe and the older one does not, and timed the file.

No Windows host. Every win32 statement in this review is a reading of code, never a run; where a reproduction stood in for the environment it says so, and the one stand-in used throughout (_capture returning None) is the shipped function's own documented value, proven by a positive control. The everos.toml / record_role_digest consequence in finding 2 was read, not reproduced.

@gloryfromca

Copy link
Copy Markdown
Member Author

No blockers; suggestions only, and they are marked inline.

The evidence in the latest review changes the standing stance from clean to nonblocking. The cited tri-state asymmetries are present: the boot-race pidfile path treats an unavailable command-line lookup as false; automatic respawn treats an unidentifiable lock holder as no holder without the health cross-check used by config restart; and the RPC search path duplicates the backend tuning policy with different outcomes. The environment-managed embedding case is also absent from _everos_consumes_the_pin. These are real follow-ups, but none meets the late-round blocking bar or prevents merging this revision.

Verification on the unchanged head: tests/test_rpc_memory.py passed 15 tests in 6.58s (7.07s wall time), corroborating the reported delay; tests/test_everos_server.py tests/test_everos_backend.py tests/test_config_update.py passed 377 tests; git diff --check github/main...HEAD passed. The review thread opened earlier remains resolved.

…ce at start

Eight of the open items from the 1.4.1 verification round, all in code this
branch added or in the two lines the memory page shares with it.

- Agent-track recall without a rerank role asks for VECTOR instead of the
  LLM rerank lane. Measured against a 1.4.1 store: the LLM lane took 10-12 s
  a call with or without candidates, past the 4 s recall budget, so agent
  cases and skills were never recalled on such installs and an LLM call was
  billed per turn for nothing; VECTOR answers in 0.3-1.5 s. Same change on
  the memory page, which timed out at 15 s the same way.
- A withheld embedding role is bound to the pin it was measured on. The
  notice asks the user to pick another model; the settings page then
  restarts the server without measuring anything, and the withhold used to
  outlive the fix until the gateway itself restarted.
- The self-heal respawn waits for start() to return. A turn arriving while
  start() was still measuring roles found nothing listening and spawned a
  server beside the one start() then spawned, which died on the OME lock
  and was reported as "exited with code 1".
- A server still booting past the start budget raises its own error,
  EverosStillStartingError, and the backend reports it as a wait rather
  than an outage: an upgraded server rebuilding its index over a large
  store outruns the budget every time.
- The memory page names the timeout when the error has no message, instead
  of "everos unreachable: " and nothing after the colon.
- _spawn_kwargs no longer claims the process group shields the server from
  the gateway's Ctrl-C; measured, both stop, the server cleanly.
- Tests: the loser of a start race not overwriting the pidfile gets its
  cases; two search tests stop resolving the hostname "x" (5 s each).

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; suggestions only, and they are marked inline.

This delta is merge-safe and closes several of the named follow-ups: the RPC tests no longer reach DNS, the UI and adapter now agree on keyword/vector degradation, withholding follows the measured pin rather than lingering across a model change, and background self-heal cannot race the initial start. The distinct still-starting result also preserves recovery while improving the operator message.

The previously named pidfile tri-state, unidentifiable-lock respawn, and environment-managed embedding-pin follow-ups remain unchanged, so the whole-record stance stays nonblocking. No new inline findings.

Covered this round: repository rules, the full accumulated PR diff through the reviewed deltas, e8d4306f2486..07fbff1918cf, changed callers and relevant history, backward compatibility, test integrity, and architecture constraints. Verification: git diff --check github/main...HEAD; 475 affected tests passed; Ruff and targeted ty checks passed; all 10 import contracts passed; source-language and large-file checks passed. The review thread opened earlier remains resolved.

@gloryfromca
gloryfromca requested a review from 0xKT September 25, 2026 11:27
@0xKT
0xKT merged commit 2b3cce7 into main Sep 25, 2026
23 checks passed
@0xKT
0xKT deleted the feat/everos_1_4_1_upgrade branch September 25, 2026 11:33
@gloryfromca gloryfromca mentioned this pull request Sep 25, 2026
12 tasks
0xKT pushed a commit that referenced this pull request Sep 25, 2026
## Summary

Bump the package version from 0.2.1 to 0.2.2 for the next release. Only
pyproject.toml and uv.lock change.

Since v0.2.1, main merged one PR (#791). It moves the memory plugin's
everos pin to 1.4.1, removes the Windows memory gate, and adds two
guards: an embedding narrower than the memory index is refused where it
is pinned and withheld from the spawn, and a running server whose
version no longer matches the installed everos is replaced rather than
reused. It carries no BREAKING CHANGE footer, so this is a patch bump,
the way v0.2.1 was for its three features. The tag and release notes
follow once this lands.

## Type

- [ ] Fix
- [ ] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [x] Other

## Verification

- `uv lock` on a clean worktree cut from main: Resolved 251 packages,
Updated raven v0.2.1 -> v0.2.2
- `git diff --stat github/main..HEAD`: pyproject.toml and uv.lock, 2
insertions, 2 deletions
- `PYTHONPATH=. python scripts/check_commit_messages.py
github/main..HEAD`: exit 0
- `npx commitlint --from github/main --to HEAD --config
commitlint.config.cjs`: exit 0
- `python -c "import importlib.metadata as m;
print(m.version('raven'))"` after the sync: 0.2.2
- No test run: the change is version metadata only.

- [ ] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [ ] User-facing docs or screenshots are updated when needed

## Risk

No code change. Rollback is reverting this commit before any v0.2.2 tag
is pushed.

- [ ] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

## Related Issues

N/A

Co-authored-by: gloryfromca <23442919+gloryfromca@users.noreply.github.com>
Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
gloryfromca added a commit that referenced this pull request Sep 25, 2026
## Summary

The everos 1.4.1 upgrade (#791) landed without a changelog entry. This
adds four bullets to `## Unreleased`, in the section's own voice, and
changes nothing else.

Under `### Added`: memory runs on native Windows now that the platform
gate is gone, and an embedding model narrower than the 1024-wide memory
index is refused where it is pinned and withheld from the spawn. Under
`### Changed`: a running EverOS whose version no longer matches the
installed one is replaced rather than reused, with the gateway starting
one again when it finds nothing listening; and recall on the
`agent_case` and `agent_skill` tracks searches by vector when no
reranker is configured.

The numbers each bullet names were read from the source rather than from
the PR text: `REQUIRED_EMBEDDING_DIMENSIONS = 1024`
(raven/config/update.py), `_RESPAWN_MIN_INTERVAL_S = 30.0` and
`_RECALL_TIMEOUT_S = 4.0`
(plugins-dist/everos-memory/raven_everos/backend.py), and
`CTRL_BREAK_EVENT` as the graceful signal (raven_everos/server.py). The
two tracks are named as the page names them, Cases and Know-how.

## Type

- [ ] Fix
- [ ] Feature
- [x] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

- `git diff --stat github/main..HEAD`: CHANGELOG.md, 34 insertions, 0
deletions
- `PYTHONPATH=. python scripts/check_commit_messages.py
github/main..HEAD`: exit 0
- `npx commitlint --from github/main --to HEAD --config
commitlint.config.cjs`: exit 0
- The inserted lines are ASCII only, checked per character
- No test run: the change is a documentation file.

- [ ] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [x] User-facing docs or screenshots are updated when needed

## Risk

No code change. Rollback is reverting this commit.

- [ ] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

## Related Issues

#791

Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
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