Repository navigation
Conversation
Replace the hardcoded, stale commit-scope list in CLAUDE.md section 3.3 with a pointer to the README Repo layout section, and finish the public-phase wording: drop the internal-AI-stats framing and the GitLab-era JIRA / merge-request residue from the trailer rules, the PR-open flow, and the PR template. Co-authored-by: Claude (claude-opus-4-8) <noreply@anthropic.com>
…ate_cleanup docs: point commit-scope at README and finish public-phase cleanup
…adius Prompt injection can't be fully prevented, so this hardens defense-in-depth: label untrusted content with a tamper-resistant boundary the model is told to treat as data, and shrink the blast radius of a successful injection. Scope follows the analysis report — high-value, low-complexity items; deeper items (sandbox default flip, capability tokens, MCP/skill signing, memory audit log) are deliberately deferred. - security/trust.py: wrap_untrusted() fences external content with a per-call random nonce, so embedded fake close-markers can't escape the fence. - system prompt: a guideline telling the model to treat fenced/external content as data, watch for "ignore the above" / "you are now" style directives, and confirm high-impact actions via ask_user. - fence untrusted tool output at the single chokepoint (ContextBuilder.add_tool_result) and inside the subagent's own loop, plus the subagent result re-injected into the main agent. - fence recalled memory and the sentinel's memory/attention context as unverified; the planner is told unverified content must not drive privileged actions (spawn / nudge). - web_fetch: use security.network.validate_url_target (DNS-resolving SSRF check) instead of the previous scheme-only validator. - DirectExecutor: pass only an env allowlist (no host secrets) rather than the full host environment; warn loudly when running unsandboxed (backend=none). - subagents: per-session rolling-hour spawn rate limit (auto-recovering) to stop a prompt-injected re-injection loop without locking out legitimate use. Co-authored-by: Claude (claude-opus-4-8) <noreply@anthropic.com>
…-turns switch Wire the TUI /model command end to end: model.* RPC handlers (options / save_key / disconnect / add_model / remove_model); a config.set "model" branch that rebuilds the live provider and reassigns the agent loop so a switch takes effect on the next turn (guarded by is_turn_active); ProviderConfig.models for the manual picker catalogue; the custom provider routed through LiteLLM; and a base LLMProvider.chat_stream non-streaming fallback so non-litellm providers degrade gracefully instead of crashing. Frontend: the model picker gains an api_base input, inline model add/delete, and a structured switch through config.set; openrpc-derived types regenerated. Co-authored-by: Claude (claude-opus-4-8) <noreply@anthropic.com>
feat: add TUI /model — provider/model config CRUD and between-turns model switch
…ted_content feat(security): label untrusted content and contain injection blast radius
Two related changes to the EverOS-backed memory subsystem. Migrated three recall improvements from the legacy everclaw repo (env vars renamed EVERCLAW_* -> RAVEN_*): - drop current-session hits from the # Memory recall so it does not duplicate the live conversation already in the history window (over-fetch top_k*2, then filter by session_id) - add `raven agent --message-file` for prompts beyond the shell ARG_MAX - env-gated episodic recall + preference annotation in the consolidator (RAVEN_EPISODIC_TOPK / RAVEN_ANNOTATE_PREFS / RAVEN_EPISODIC_CLUSTER), default-off Restructured when EverOS is invoked so native memory owns short-term and EverOS owns long-term: - recall is now on-miss: native runs every turn; EverOS user-track recall fires only when native surfaces nothing relevant to the query - the per-turn EverOS store is removed; EverOS consolidation runs only (a) intra-day when a session crosses everos_consolidation_threshold_pct of the context window (default 80, background, non-blocking) and (b) nightly at everos_nightly_consolidation_hour (default 0, offline) - backend.store gains force_flush to promote a batch immediately; consolidation is incremental per session (metadata watermark) so the two triggers never double-ingest - native short-term compaction (maybe_consolidate_by_tokens) is unchanged Tests: unit coverage for the threshold / nightly / on-miss paths plus a full in-memory lifecycle smoke, and a real-everos e2e for the force_flush single-store recall path. Co-authored-by: Claude (claude-opus-4-8) <noreply@anthropic.com>
EverOS defaulted both its user-level config toml and its data root (sqlite / lancedb / .index / ome.db) to a bare ~/.everos. Scope raven's instance under ~/.everos/raven so config and data stay isolated from any other EverOS consumer on the machine. Add update_everos.configure_everos_env(): it sets EVEROS_CONFIG_FILE and EVEROS_MEMORY__ROOT via setdefault (so an explicit operator override still wins) before EverOS's @cache-d load_settings() first runs. The everos backend factory and the understand_media tool factory call it as the earliest raven-controlled EverOS entry points; _EVEROS_CONFIG now derives from a single _EVEROS_BASE constant. No data migration: existing ~/.everos data is left untouched and a fresh ~/.everos/raven is created lazily on first write/use. Co-authored-by: Claude (claude-opus-4-8) <noreply@anthropic.com>
A fresh onboard now materializes the user-facing subset of the memory / plugins / skillForge extension blocks into ~/.raven/config.json so the knobs are discoverable and editable without reading the source. Seeding is setdefault-based (non-clobbering) and runs on every onboard, so it also backfills a pre-existing config that predates these blocks. - memory.backend defaults to "everos" (schema default); _memory_enabled now gates on an llm model being present, so a fresh modelless seed is treated as "not yet enabled" and the skip / non-interactive guard still resolves it to None when memory is opted out or left unconfigured. - skillForge.everos.enabled is seeded on; router.hub.endpoint is seeded to the shared Skill Hub URL with apiKey left null for the user to fill. - plugins.config["everos-memory"] is seeded with identity wiring that mirrors memory.userId / agentId so recall identities match. - internal-infra fields (embedding / reranker endpoints + their API keys, mass_library_db) are NOT written, so a real Bearer token never lands in a plaintext config; they stay at their schema defaults. Co-authored-by: Claude (claude-opus-4-8) <noreply@anthropic.com>
…n_blocks Feat/onboard default extension blocks
…ovements feat: native short-term memory + everos long-term consolidation
revert: pr #9 native short-term memory + everos long-term consolidation
chore: prepare open source launch
## Summary - switch project metadata and license text to Apache-2.0 while retaining upstream notices - add public-ready community files: issue templates, SECURITY, CONTRIBUTING, CODE_OF_CONDUCT, Dependabot, and a stricter PR template - align README license copy with the public launch posture ## Repository settings updated via GitHub API - squash-only merge enabled; merge commit and rebase merge disabled - delete branch on merge and update branch enabled - Discussions enabled; Projects and Wiki disabled - Actions default workflow permissions set to read-only and PR self-approval disabled - description and homepage updated for Raven ## Verification - npm ci --ignore-scripts --no-audit --no-fund (root) - npm ci --ignore-scripts --no-audit --no-fund (ui-tui) - npm ci --ignore-scripts --no-audit --no-fund (bridge) - npx --no-install commitlint --from origin/main --to HEAD --config commitlint.config.cjs - PYTHONPATH=. python3 scripts/check_commit_messages.py origin/main..HEAD - uv run pre-commit run --from-ref origin/main --to-ref HEAD - git diff --check origin/main...HEAD ## Notes - GitHub branch protection/rulesets returned 403 while the repository is private on the current plan. Enable required checks after making the repository public. - Auto-merge remained disabled after the API patch, likely due repository/org availability or policy.
## Summary - add lightweight overview files for top-level repository sections that still displayed old non-conventional commit messages - update existing benchmark and demo READMEs with public-facing guidance - make the GitHub directory listing show a clean conventional commit message after merge without rewriting main history ## Verification - uv run pre-commit run --files LICENSES/README.md benchmarks/README.md demos/README.md docs/README.md raven/README.md tests/README.md - npx --no-install commitlint --from origin/main --to HEAD --config commitlint.config.cjs - PYTHONPATH=. python3 scripts/check_commit_messages.py origin/main..HEAD - uv run pre-commit run --from-ref origin/main --to-ref HEAD ## Note A normal pull request cannot rename historical commits already merged into main. This PR instead updates the affected directories with useful public-facing overview docs so GitHub displays the new conventional commit as the latest directory change.
Bumps [python-socketio](https://github.com/miguelgrinberg/python-socketio) from 5.16.1 to 5.16.2. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/miguelgrinberg/python-socketio/releases">python-socketio's releases</a>.</em></p> <blockquote> <h2>Release 5.16.2</h2> <p>See <a href="https://github.com/miguelgrinberg/python-socketio/blob/main/CHANGES.md">CHANGES.md</a> for release notes.</p> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/miguelgrinberg/python-socketio/blob/main/CHANGES.md">python-socketio's changelog</a>.</em></p> <blockquote> <h1>python-socketio change log</h1> <p><strong>Release 5.16.3</strong> - 2026-06-15</p> <ul> <li>Catch all exceptions in redis and rabbitmq client managers <a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1581">#1581</a> (<a href="https://github.com/miguelgrinberg/python-socketio/commit/80bb5c9b07e6b53e45f610ec29a24124c539d41b">commit</a>)</li> </ul> <p><strong>Release 5.16.2</strong> - 2026-05-21</p> <ul> <li>Prevent unnecessary resource allocations <a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1574">#1574</a> (<a href="https://github.com/miguelgrinberg/python-socketio/commit/ca140fe44d0ceb3004073645222abec182d8784b">commit</a>)</li> <li>Add zizmor to CI builds <a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1570">#1570</a> (<a href="https://github.com/miguelgrinberg/python-socketio/commit/664dc27ec6f34179ed1724430eda3520627fc642">commit</a>)</li> </ul> <p><strong>Release 5.16.1</strong> - 2026-02-06</p> <ul> <li>Use configured JSON module in managers <a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1549">#1549</a> (<a href="https://github.com/miguelgrinberg/python-socketio/commit/6229261ae6e8c01e675097242e333ee84587a544">commit</a>)</li> <li>Admin UI fixes: remove duplicate tasks, report transport upgrades (<a href="https://github.com/miguelgrinberg/python-socketio/commit/1c2eab13a92fac9e43663eb0b5f099eb1c40ea5b">commit</a>)</li> <li>Switch to Furo documentation template (<a href="https://github.com/miguelgrinberg/python-socketio/commit/add47d8c7abca697a2804141bbf29bfb095f7d5e">commit</a>)</li> <li>Add Python free-threading to CI <a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1554">#1554</a> (<a href="https://github.com/miguelgrinberg/python-socketio/commit/ccdd2004a038ae4b8171a05120c5d0787332f7ee">commit</a>)</li> </ul> <p><strong>Release 5.16.0</strong> - 2025-12-24</p> <ul> <li>Address deprecation warnings (<a href="https://github.com/miguelgrinberg/python-socketio/commit/b235699d9b06564753c570b76055997e9d62a938">commit</a>)</li> <li>Drop Python 3.8 and 3.9 from CI builds (<a href="https://github.com/miguelgrinberg/python-socketio/commit/d0728d2f74538762dd551fa9cd0cd1fd5aedfa37">commit</a>)</li> </ul> <p><strong>Release 5.15.1</strong> - 2025-12-16</p> <ul> <li>Restore support multiple arguments via pubsub emits <a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1540">#1540</a> (<a href="https://github.com/miguelgrinberg/python-socketio/commit/c279f26bb8c9887c4ca99d4d81ad331c4844438c">commit</a>)</li> </ul> <p><strong>Release 5.15.0</strong> - 2025-11-22</p> <ul> <li>Retry initial Redis connection <a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1536">#1534</a> ([commit <a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1">#1</a>](<a href="https://github.com/miguelgrinberg/python-socketio/commit/1e903e173a2d7b04599c4f7f9630c1abbb531fad">https://github.com/miguelgrinberg/python-socketio/commit/1e903e173a2d7b04599c4f7f9630c1abbb531fad</a>) [commit <a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/2">#2</a>](<a href="https://github.com/miguelgrinberg/python-socketio/commit/5e898a9b93526e6e667767e54c60f4c84589989d">https://github.com/miguelgrinberg/python-socketio/commit/5e898a9b93526e6e667767e54c60f4c84589989d</a>))</li> <li>Correctly regenerate RabbitMQ binding after a connection failure <a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1516">#1516</a> (<a href="https://github.com/miguelgrinberg/python-socketio/commit/c52e93b4a328d98a968bfbdec0cfd598b73ee913">commit</a>) (thanks <strong>Gritty_dev</strong>!)</li> <li>Support <code>ext_type</code> in the <code>MsgPackPacket</code> class <a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1521">#1521</a> (<a href="https://github.com/miguelgrinberg/python-socketio/commit/208925344a48485d2cd56e40eb74266c3bcb5311">commit</a>)</li> <li>Support sending <code>bytesarray</code>s when using pub/sub managers (<a href="https://github.com/miguelgrinberg/python-socketio/commit/6c9b9974f72e2efdf62407ecab24ee6995448098">commit</a>)</li> <li>Fix typos in documentation <a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1520">#1520</a> (<a href="https://github.com/miguelgrinberg/python-socketio/commit/db3f1c2a0105c30cb833ddfca8f05fe4320468fd">commit</a>) (thanks <strong>Lê Nam Khánh</strong>!)</li> <li>Improvements to the logging documentation (<a href="https://github.com/miguelgrinberg/python-socketio/commit/b423d0e38eef559b7e81acb7e32059de305f982c">commit</a>)</li> </ul> <p><strong>Release 5.14.3</strong> - 2025-10-29</p> <ul> <li>Support Python's native <code>ConnectionRefusedError</code> exception to reject a connection <a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1515">#1515</a> (<a href="https://github.com/miguelgrinberg/python-socketio/commit/f3b18bde3f16437b223491d4c3e440ea37105fe3">commit</a>)</li> <li>Push binary data to the aiopika client manager <a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1514">#1514</a> (<a href="https://github.com/miguelgrinberg/python-socketio/commit/194e1b7f277b5f72e1de78d3f614e7b8b6c788ac">commit</a>)</li> </ul> <p><strong>Release 5.14.2</strong> - 2025-10-15</p> <ul> <li>Restore binary message support in message queue setups <a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1509">#1509</a> (<a href="https://github.com/miguelgrinberg/python-socketio/commit/bab4a10f48aaae11d7f832ebe5c30ad3f85d31b3">commit</a>)</li> <li>Fix formatting of client connection error <a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1507">#1507</a> (<a href="https://github.com/miguelgrinberg/python-socketio/commit/f298c9b54d76ab09ff72935937e1b9575bc45ffd">commit</a>)</li> <li>Add 3.14 and pypy-3.11 CI tasks (<a href="https://github.com/miguelgrinberg/python-socketio/commit/1f4cd3b025c294f25208ec3c05b5f8df6209e403">commit</a>)</li> <li>Improve documentation of the <code>BaseManager.get_participants()</code> method (<a href="https://github.com/miguelgrinberg/python-socketio/commit/33722a0d96036f005188b07b8b46a5ef091fe65f">commit</a>)</li> </ul> <p><strong>Release 5.14.1</strong> - 2025-10-02</p> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/miguelgrinberg/python-socketio/commit/6e2b7175c97c2785edd2ea60df026c12240c785c"><code>6e2b717</code></a> Release 5.16.2</li> <li><a href="https://github.com/miguelgrinberg/python-socketio/commit/cb658291085842372a16863d83e14373f85b3d3e"><code>cb65829</code></a> update python-engineio version</li> <li><a href="https://github.com/miguelgrinberg/python-socketio/commit/ca140fe44d0ceb3004073645222abec182d8784b"><code>ca140fe</code></a> prevent unnecessary resource allocation (<a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1574">#1574</a>)</li> <li><a href="https://github.com/miguelgrinberg/python-socketio/commit/b29beef6bfd38d5a3c3b89da96b7a4574a91efcf"><code>b29beef</code></a> tox configuration</li> <li><a href="https://github.com/miguelgrinberg/python-socketio/commit/e8981302a3d5b525ee73c2ec9c13859ce2f764e8"><code>e898130</code></a> Bump ujson from 5.4.0 to 5.12.1 in /examples/server/sanic (<a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1573">#1573</a>) #nolog</li> <li><a href="https://github.com/miguelgrinberg/python-socketio/commit/05c32f5da85fe5e5d647c937c7eab0972760ca05"><code>05c32f5</code></a> Bump qs and body-parser in /examples/server/javascript (<a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1572">#1572</a>) #nolog</li> <li><a href="https://github.com/miguelgrinberg/python-socketio/commit/287dc6715b801d319f5baa0fb727e232e728b8aa"><code>287dc67</code></a> Bump qs and body-parser in /examples/client/javascript (<a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1571">#1571</a>) #nolog</li> <li><a href="https://github.com/miguelgrinberg/python-socketio/commit/664dc27ec6f34179ed1724430eda3520627fc642"><code>664dc27</code></a> add zizmor to ci (<a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1570">#1570</a>)</li> <li><a href="https://github.com/miguelgrinberg/python-socketio/commit/14c62366a85cfe34e0bd69a22b905eb329873dd3"><code>14c6236</code></a> Bump django in /examples/server/wsgi/django_socketio (<a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1566">#1566</a>) #nolog</li> <li><a href="https://github.com/miguelgrinberg/python-socketio/commit/29b2e5cdbe87e8216bc46fccb864b84ab9f55cf9"><code>29b2e5c</code></a> Bump aiohttp from 3.13.3 to 3.13.4 in /examples/server/aiohttp (<a href="https://redirect.github.com/miguelgrinberg/python-socketio/issues/1565">#1565</a>) #nolog</li> <li>Additional commits viewable in <a href="https://github.com/miguelgrinberg/python-socketio/compare/v5.16.1...v5.16.2">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/EverMind-AI/Raven/network/alerts). </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Summary The deep_research API key prompt (onboard Step 5 and `raven deep-research enable`) exited raven with no message when a blank key was entered, and had no clean way to cancel once you started configuring. Root cause: `_prompt_api_key` validated the raw input length (`len(v) >= 8`) but consumed the stripped value. An all-whitespace key (e.g. 8 spaces) passed the check, stripped to empty, and hit `typer.Exit` -- quitting raven. This shared helper backs 8 call sites, so the crash affected every no-back caller. Changes: - Validate the stripped value. Whitespace-only input is now rejected and re-prompts (the reported "8 spaces exits raven" case); a truly-empty submit is the back/cancel signal only where a caller opts in. - Give deep_research's key prompt a cancel path: an empty submit cancels configuration without touching the stored key (previously only Ctrl+C, which killed the whole process). `_back_placeholder` / `_prompt_api_key` take an optional label so the in-field hint reads "cancel" here instead of the default "go back", which would have been misleading. ## Type - [x] Fix ## Verification - `uv run pytest tests/test_cli_onboard_commands.py tests/test_cli_deep_research_commands.py` -> 95 passed - `uv run ruff check` on the changed files -> clean - [x] Relevant tests pass locally - [x] Relevant lint / type checks pass locally - [ ] User-facing docs or screenshots are updated when needed ## Risk - [x] Security impact considered - [x] Backward compatibility considered The fix only changes rejection/cancel behavior of a blank submit; a valid key flows through unchanged. The stripped-value validation and the empty-submit cancel were verified against all 8 `_prompt_api_key` call sites (2 already used allow_back and are behavior-identical for a real key). ## Related Issues Closes #180 Co-authored-by: Claude (claude-opus-4-8) <noreply@anthropic.com>
## Change description The one-click installers ran `uv tool install` without a lockfile, so they re-resolved dependencies to the newest versions allowed by `pyproject.toml` instead of the tested set. Once `litellm` 1.92+ shipped a Rust extension with Linux-only wheels, Windows/macOS installs broke: with no wheel, the sdist builds from source and needs a compiler (`link.exe` / a Rust toolchain). Fix, without touching the `pyproject.toml` constraints (so `uv lock` still governs when litellm upgrades): - **release.yml**: export the locked dependency set as a `raven-constraints.txt` release asset via `uv export --locked --all-extras --no-hashes`. `--locked` fails the release if `uv.lock` is stale vs `pyproject.toml`, so we never ship constraints that conflict with the wheel's own metadata. - **install.sh / install.ps1**: pass the constraints to `uv tool install -c`. The constraints URL is derived from the wheel URL (same release directory) so it always matches the wheel being installed, including the `RAVEN_WHEEL_URL` escape hatch. A missing asset or a failed download degrades to an unconstrained install rather than aborting. - **raven upgrade** (`upgrade_commands.py`): the self-upgrade helper does the same derive-and-pin, so upgrading an installed copy no longer re-triggers the litellm source build either. With the pin, litellm resolves to the locked 1.85.0 (pure-Python, wheels on all platforms), so no compiler is needed anywhere. **Rollout note:** this only takes effect once a new release is cut (so the `raven-constraints.txt` asset exists). Existing releases have no such asset and the scripts degrade to the current unconstrained behavior. Recommend cutting an rc first and verifying a zero-compile install on real Windows and macOS before the stable tag. ## Type of change - [x] Bug fix - [ ] New feature - [ ] Document - [ ] Others ## Related issues Closes #181 ## Checklists ### Development - [x] Lint rules pass locally - [ ] Application changes have been tested thoroughly - [x] Automated tests covering modified code pass > Unit tests and the full CI preflight pass locally (upgrade-helper tests cover > both the pinned and the graceful-fallback paths). End-to-end Windows/macOS > installs were not run on real machines; that should happen on an rc tag before > the stable release. ### Security - [x] Security impact of change has been considered - [x] Code follows security best practices and guidelines > The constraints URL is derived from the wheel URL, which `_parse_release_payload` > already validates as a trusted github.com release path; constraints only pin > versions, they do not execute code. ### Code review - [x] Pull request has a descriptive title and context useful to a reviewer --------- Co-authored-by: Claude (claude-opus-4-8) <noreply@anthropic.com>
#187) ## Change description The bespoke `subject-case: [2, "always", ["lower-case"]]` rule forced all-lowercase subjects and rejected proper nouns (EverOS, HTTP), so a PR title like "... with EverOS HTTP backend" passed the pre-merge PR-title check but failed commit-lint on the squash commit after merge. This switches `subject-case` to the standard `@commitlint/config-conventional` value (forbid Title Case / ALL CAPS / Sentence case; allow lowercase subjects with proper nouns), plus one matching example in AGENTS.md. It intentionally does NOT `extends` config-conventional, to avoid newly enforcing body/footer line-length rules on the squash body (= PR description). Verified locally: proper-noun titles pass, Title Case / Sentence case / ALL CAPS still rejected, `style:` still rejected (type-enum unchanged), long body lines unaffected. ## Type of change - [x] Bug fix - [ ] New feature - [ ] Document - [ ] Others ## Related issues (if there is) Closes #186 ## Checklists ### Development - [x] Lint rules pass locally - [x] Application changes have been tested thoroughly - [ ] Automated tests covering modified code pass ### Security - [x] Security impact of change has been considered - [x] Code follows security best practices and guidelines ### Code review - [x] Pull request has a descriptive title and context useful to a reviewer. Screenshots or screencasts are attached as necessary Co-authored-by: Claude (claude-opus-4-8) <noreply@anthropic.com>
## Summary Bump version from 0.1.7 to 0.1.8. Release commit only touches `pyproject.toml` and `uv.lock` (kept in sync via `uv lock`). Changes bundled into this release since v0.1.7: - feat(importer): cold-start import from other AI tools with EverOS HTTP backend (#170) - fix(build): pin locked dependency versions in installers (#185) - fix(cli): stop blank api key exiting deep_research prompt (#184) - fix: use redirect-free install.ps1 url on windows powershell 5.1 (#182) - fix(tui): brighten selection highlight for readability (#175) - fix(commitlint): align subject-case with conventional-commits standard (#187) ## Type - [x] Other ## Verification - [x] Relevant lint / type checks pass locally ``` python3 scripts/check_commit_messages.py origin/main..HEAD # exit 0 git diff --stat # pyproject.toml + uv.lock, 1 line each ``` ## Risk - [x] Backward compatibility considered Version bump only; no source changes. ## Related Issues N/A Co-authored-by: Claude (claude-opus-4-8) <noreply@anthropic.com>
## Change description Closes #114. Every failed agent turn surfaces in the TUI as the same opaque line -- `error: turn_failed (code=-32099)` -- regardless of the real cause (a missing dependency, a provider error, a Node version problem, ...). That made #119 (missing `orjson`) impossible to diagnose from the UI and forced a from-scratch reproduction. Root cause: `TurnFailed` already carries the underlying error (`error=str(exc)` in the scheduler), but the spine sink discarded it -- `raven/tui_rpc/spine.py` called `emit_error(..., "turn_failed", "internal")` with the detail hardcoded, so `TurnFailed.error` never reached the wire or the UI. ## Fix Thread the failure detail end to end: - `ui-tui/rpc-schema/openrpc.json`: add an optional `detail` string to the `ErrorEvent` payload (single source of truth). - `ui-tui/src/rpc/generated.ts`: regenerated from the schema (`npm run lint:rpc` confirms it is in sync). - `raven/tui_rpc/models.py`: add `detail: str | None = None` to `ErrorEventPayload`. - `raven/tui_rpc/spine.py`: `emit_error` accepts an optional `detail` and includes it only when non-empty; the `TurnFailed` sink passes `event.error`. - `ui-tui/src/app/chatStream.ts`: `onError` appends the first line of `detail` (capped) to the error line, so the user sees e.g. `error: turn_failed (code=-32099): No module named 'orjson'`. `code` / `message` / `reason` are unchanged, so the `cancelled_by_client` path and existing consumers are unaffected; `detail` is omitted when empty (back-compat). ## Tests - `tests/test_tui_rpc_spine.py`: `emit_error` with a detail includes it in the payload; the existing no-detail test still asserts it is omitted. - `ui-tui/src/__tests__/chatStream.test.ts`: an error event carrying `detail` renders `error: turn_failed (code=-32099): <detail>`. ## Type of change - [x] Bug fix - [ ] New feature - [ ] Document - [ ] Others ## Checklists ### Development - [x] Lint rules pass locally (ruff, eslint, prettier, tsc, lint:rpc) - [x] Application changes have been tested thoroughly - [x] Automated tests covering modified code pass (python spine + tui chatStream + full `npm test`) ### Code review - [x] Pull request has a descriptive title and context useful to a reviewer Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Change description Follow-up to #187 (which fixed the subject-case dimension). The length dimension had the same "passes the PR, fails after merge" flaw: `scripts/commit_lint.py` capped the subject at 72 (commits) / 90 (PR titles) while commitlint capped the whole header at 100, so a PR title with a 73-90 char subject passed the pre-merge PR-title check but failed commit-lint on the squash commit after merge. This makes commitlint `header-max-length` (100) the single length authority (the config-conventional standard) and removes the bespoke Python 72/90 caps. CI now validates the PR title as the prospective squash header `<title> (#NN)` (in the commit-messages job, which already has Node), so a title that passes cannot fail after merge; the `edited` trigger re-checks title edits. Deliberately NOT `extends @commitlint/config-conventional`, to avoid pulling in body/footer line-length rules. Verified locally: proper-noun titles pass, a 79-char subject (header 102) is now rejected at PR stage, header exactly 100 passes and 101 fails, Title Case still rejected, `tests/test_commit_lint.py` green. ## Type of change - [x] Bug fix - [ ] New feature - [ ] Document - [ ] Others ## Related issues (if there is) Closes #195 ## Checklists ### Development - [x] Lint rules pass locally - [x] Application changes have been tested thoroughly - [x] Automated tests covering modified code pass ### Security - [x] Security impact of change has been considered - [x] Code follows security best practices and guidelines ### Code review - [x] Pull request has a descriptive title and context useful to a reviewer. Screenshots or screencasts are attached as necessary Co-authored-by: Claude (claude-opus-4-8) <noreply@anthropic.com>
## Change description
Fixes `raven tui` failing with a "Node version too low (found 20.20.1,
need >= 22)" error on machines that **do** have a recent Node installed
(e.g. Homebrew node 26), while `raven agent` works fine.
Two bugs in `find_node()` (`raven/cli/tui_commands.py`):
1. **No minimum-version filtering.** It returned the first candidate
whose `--version` merely parsed, regardless of `_MIN_NODE_VERSION`. The
caller then rejected it if `< 22` without ever asking for the next
candidate -- so a too-old node in an earlier priority tier shadowed a
valid newer one.
2. **PATH probed with `shutil.which("node")`,** which returns only the
*first* hit. A stale `< 22` node earlier on PATH (an old
`/usr/local/bin/node`, an nvm/asdf shim, etc.) hid a newer node later on
PATH.
Together: a user with node 26 on PATH but a 20.x earlier on PATH got
"node too low". `raven agent` was unaffected (no Node needed), which
made it look like a TUI/streaming problem.
### Fix
- Enumerate **every** `node` on PATH (not just the first).
- Return the first candidate that meets `_MIN_NODE_VERSION`, in priority
order (RAVEN_NODE -> venv -> PATH -> private runtime).
- Keep the highest below-minimum candidate so, when nothing qualifies,
the caller still reports the real version (`found 20.20.1, need >= 22`)
instead of a bare "not found".
### Tests
Added to `tests/test_cli_tui_bootstrap.py`:
- `test_find_node_skips_below_min_and_scans_all_path` -- a 20.20.1
earlier on PATH + a 26.5.0 later => `find_node` returns 26.5.0.
- `test_find_node_reports_below_min_when_nothing_qualifies` -- only a
20.20.1 => returns it (so the caller can show the version).
## Type of change
- [x] Bug fix
- [ ] New feature
- [ ] Document
- [ ] Others
## Checklists
### Development
- [x] Lint rules pass locally (`ruff check` / `ruff format`)
- [x] Application changes have been tested thoroughly
- [x] Automated tests covering modified code pass
### Code review
- [x] Pull request has a descriptive title and context useful to a
reviewer
## Related issues
Closes #198
## Follow-up fix included (appended commit)
`test_find_node_discovers_windows_private_runtime` was failing on
non-Windows hosts (it mocked `sys.platform="win32"`, but `find_node`
keys its
Windows branches off `os.name == "nt"`, so the mock never took effect).
This
PR now also fixes it, replacing the earlier "flagged for a separate fix"
note:
- Add a `_is_windows()` seam that `find_node` consults for its venv,
PATH, and
private-runtime platform checks.
- The test patches `_is_windows` instead of `os.name` -- patching
`os.name`
directly is not viable, since pathlib selects `PosixPath`/`WindowsPath`
by
`os.name` and would raise `NotImplementedError` on a POSIX host.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Summary `raven onboard` was unreadable on light-background terminals: the palette was hardcoded for a dark background and the CLI had no light/dark detection, so near-white / bright-yellow text vanished on white (issue #169, reproduced on a released build). This adds `raven/cli/_theme.py`: light/dark detection (env `RAVEN_THEME` / `RAVEN_TERM_BACKGROUND` hint / OSC 11 background probe / `COLORFGBG`, falling back to dark) plus a scheme-aware palette, wired into the onboarding wizard and the shared questionary style. - Dark rendering is byte-for-byte unchanged (dark token values are the current literals; verified by a rendering diff and a parity test). - Light values clear WCAG AA 4.5:1 on white; most mirror `ui-tui/src/theme.ts`. - Detection runs on first themed render, never at import, so plain `raven ...` commands do not probe the terminal. - Also drops two hardcoded `white` table columns (`skill` / `cron`) that were invisible on light backgrounds. Scope is deliberately limited to what fails on light: named ANSI colors (`red`/`green`/`cyan`...) elsewhere are terminal-adaptive and were left as-is. ## Type - [x] Fix ## Verification - [x] Relevant tests pass locally - [x] Relevant lint / type checks pass locally ``` uv run pytest tests/test_cli_theme.py # 44 passed (new) uv run pytest tests/test_cli_onboard_commands.py # 64 passed uv run pytest tests/test_cli_deep_research_commands.py \ tests/test_cli_skill_commands.py tests/test_cli_cron_commands.py \ tests/test_cli_smoke.py # 142 passed uv run ruff check / ruff format --check # clean ``` Note for reviewers: the OSC 11 terminal probe can only be exercised on a real TTY (not in CI). Please confirm on a light-background terminal that `raven onboard` is now legible, and on a dark terminal that it is unchanged (`RAVEN_THEME=dark` / `RAVEN_THEME=light` force a scheme). ## Risk - [x] Security impact considered - [x] Backward compatibility considered - [x] Rollback path is clear for risky changes Dark output is unchanged; the new env vars are additive; rollback is a single commit revert. ## Related Issues Closes #169 --------- Co-authored-by: Claude (claude-opus-4-8) <noreply@anthropic.com>
## Summary Internal LLM calls (main loop and sub-agents) had only an httpx per-read timeout, or none on the streaming path. A backend that trickles bytes without finishing resets that per-read timer forever, so tasks hang indefinitely with no error (about 1 in 6 tasks in a 1000+ batch rollout). This adds a configurable wall-clock cap enforced with `asyncio.wait_for` on every provider call: - New `agents.defaults.llm_call_timeout` (default 600s), carried on `GenerationSettings` and threaded to every provider construction (eager, lazy, and knn per-model). - litellm, custom, azure and codex providers all honor it. Streaming paths use a per-chunk idle cap, so a long but steadily-progressing generation is not cut off while a mid-stream stall raises after `llm_call_timeout` seconds of silence. - `classify_error` now treats the builtin `TimeoutError` raised by `wait_for` as a retryable `network` error, so the existing retry and model-fallback ladder engages (previously it fell to `unknown` and never retried). - The streaming loop converts a timeout into a structured error response and closes the stream, instead of letting it crash the turn. - `PerModelProvider` now propagates generation settings to its per-model sub-providers, so knn-routed calls honor the configured timeout, temperature and max_tokens. ## Type - [x] Fix ## Verification - [x] Relevant tests pass locally - [x] Relevant lint / type checks pass locally Commands run: - `uv run pytest` over the provider / config / agent-loop test files (litellm timeout, azure, codex, fallback chain, per-model, lazy, agent-loop stream, gateway, stream, config loader): 74 passed. - `bash .claude/scripts/preflight_ci.sh`: all checks passed (commitlint, commit-message lint, large-files, pre-commit, ruff lint, focused tests 58 passed). New tests cover: provider-layer wall-clock and per-chunk idle timeouts, the `classify_error(TimeoutError)` regression, the streaming structured-error path, and per-model generation propagation. ## Risk - [x] Security impact considered - [x] Backward compatibility considered - [x] Rollback path is clear for risky changes The default of 600s matches the previous hardcoded litellm cap, so behavior does not regress; the cap is now a wall-clock bound (closing the trickle-stall hole) and is configurable. New config field with a default, no schema or API breaking change. Rollback is a single-commit revert. ## Related Issues Fixes #150 Refs #183 (this PR incidentally fixes the knn generation-propagation leak tracked there) and #149 (the timeout surfaces as a classified error; the run-level structured termination reason is to be coordinated with that issue). Co-authored-by: Claude (claude-opus-4-8) <noreply@anthropic.com>
## Summary Bump the package version from 0.1.8 to 0.1.9 (patch release). All five PRs merged since v0.1.8 are fixes, no new features: - #204 fix: add configurable llm_call_timeout to bound stalled LLM calls - #199 fix(cli): make onboard readable on light-background terminals - #189 fix(cli): pick the first node >= 22 across all PATH entries - #196 fix(commitlint): unify commit/PR length to header-max-length - #190 fix(tui): surface the real turn-failure detail, not just the code ## Type - [ ] Fix - [ ] Feature - [ ] Docs - [ ] CI / tooling - [ ] Refactor - [x] Other ## Verification - [x] Relevant tests pass locally - [x] Relevant lint / type checks pass locally - [ ] User-facing docs or screenshots are updated when needed Bump is limited to `pyproject.toml` and `uv.lock` (`uv lock` sync). Commit message lint passes locally. ## Risk - [x] Security impact considered - [x] Backward compatibility considered - [x] Rollback path is clear for risky changes Version-only change; no code or behavior change. ## Related Issues N/A Co-authored-by: Claude (claude-opus-4-8) <noreply@anthropic.com>
## Summary - Replace the legacy anonymous Star History SVG with the official sealed-token embed in both English and Chinese READMEs. - Support light and dark color schemes for the Star History chart. - Keep the raw GitHub PAT out of the repository. The committed sealed token is the public embed value generated by Star History. ## Type - [ ] Fix - [ ] Feature - [x] Docs - [ ] CI / tooling - [ ] Refactor - [ ] Other ## Verification - [ ] Relevant tests pass locally - [ ] Relevant lint / type checks pass locally - [x] User-facing docs or screenshots are updated when needed - `git diff --check origin/main...HEAD` passed. - `make check-large-files` passed. - The interactive Star History chart rendered with the configured token. The embed endpoint also accepted and decrypted the sealed token, but a direct cold request intermittently returned HTTP 500 when its server-side GitHub fetch exceeded 10 seconds. ## Risk - [x] Security impact considered - [x] Backward compatibility considered - [x] Rollback path is clear for risky changes The chart remains dependent on the remote Star History service rendering reliably on GitHub. ## Related Issues Tracks #202
## Summary Two-part refresh of the proactivity eval, plus a small product fix. **1. pbench: uniform prompt re-test.** All three agents re-tested with the same minimal task prompt (no persona, no decision principles - `runners/prompts/uniform/`), each driven black-box through its own CLI on the same backend (OpenRouter `qwen/qwen3.5-27b`). The README main table is now the 3-run mean under this protocol. New `hermes_cli_pbench.py` drives pbench through the installed hermes CLI; legacy per-agent persona templates are kept only to reproduce the ablation appendix. **2. longrun: matched-model rerun.** Full rerun of the 6-persona x 30-day x 3-agent benchmark as a single batch: raven, hermes, and openclaw all on OpenRouter `qwen/qwen3.5-27b`, with `claude-sonnet-4.5` as user simulator and judge, so the comparison isolates framework architecture rather than model quality. Supporting harness changes: - `run_meta` trajectory stamp + scorecard `soft_dnd` field so every artifact self-identifies its run mode. - MCP cron bridge gains `repeat` (daily/weekdays/weekly) for openclaw. - Scorer: decision-attribution for Type A (cron provenance + novelty window), sonnet judge for Type B, weekend quota aligned to factory default. - All three READMEs (main + data/longrun en/zh) refreshed to the new results with per-dimension caveats disclosed. **3. Product fix.** Adds `tools.exec.extra_deny_patterns`: operators or eval harnesses running the agent un-sandboxed can append host-specific deny regexes. The shipped default deny list is unchanged; the eval injects osascript / `open -a` blocks via config only. ## Type - [x] Fix - [x] Docs - [x] Other ## Verification - `uv run pytest tests/test_sandbox_unit.py -q` - 68 passed (also re-run after rebasing onto latest `main`). - `uv run ruff check` / `uv run ruff format --check` on all changed Python files - clean. - pbench: 3 independent n=120 runs per agent under the uniform prompt (2026-07-22); README table reports the mean with per-run F1 range. - longrun: 18 legs completed with zero LLM/API errors; scored via `longrun_scorecard.py --all --compare --aggregate` (18/18 scorecards, single judge pass). - [x] Relevant tests pass locally - [x] Relevant lint / type checks pass locally - [x] User-facing docs or screenshots are updated when needed ## Risk - [x] Security impact considered - [x] Backward compatibility considered - [x] Rollback path is clear for risky changes `extra_deny_patterns` is append-only on top of the built-in deny list and defaults to empty, so shipped behavior is unchanged; both commits revert cleanly and independently. ## Related Issues N/A --------- Co-authored-by: Claude (claude-opus-4-8) <noreply@anthropic.com> Co-authored-by: Elliot Chen <2340896+cyfyifanchen@users.noreply.github.com>
## Summary - Remove the Star Us and Star History section from the English README. - Remove the corresponding Star section from the Chinese README. - Remove both sections from their tables of contents. The embedded Star History chart still does not render reliably, so the promotional section is being removed instead of retaining a broken README surface. ## Type - [ ] Fix - [ ] Feature - [x] Docs - [ ] CI / tooling - [ ] Refactor - [ ] Other ## Verification - `make check-commits` passed. - `PR_TITLE='docs: remove star history section' make check-pr-title` passed. - `make check-large-files` passed. - Python test target passed: 58 tests. - The isolated ModelPicker test passed. - The full TUI run passed 901 tests and failed 1 unrelated ModelPicker timing assertion. The failure reproduces only in the full suite and is outside this README-only diff. - [ ] Relevant tests pass locally - [x] Relevant lint / type checks pass locally - [x] User-facing docs or screenshots are updated when needed ## Risk - [x] Security impact considered - [x] Backward compatibility considered - [x] Rollback path is clear for risky changes ## Related Issues N/A
## Summary Add first-class MiniMax Token Plan OAuth support for both Global and CN regions. This change adds: - `minimax_global` and `minimax_cn` provider entries with curated MiniMax-M3 and M2.7 models. - OAuth 2.0 device authorization with PKCE S256 and strict state validation. - The shared MiniMax public client ID `coding-plan-cli` with the `openid profile coding_plan` scope. - Region-specific OAuth and Anthropic-compatible API endpoints. - Proactive access-token refresh with a five-minute buffer, cross-process locking, transient retry handling, and atomic refresh-token persistence. - Separate Global and CN credential files, isolated from existing Codex OAuth storage. - Runtime token refresh and MiniMax authentication headers for both normal and streaming requests. - CLI login commands, onboarding, provider status/test/reset support, TUI setup detection, and model-picker entries. - English and Chinese documentation updates. Public model references remain under `minimax-global/` and `minimax-cn/`. The runtime translates them to the Anthropic-compatible LiteLLM route only when sending a request, so provider selection continues to use the correct MiniMax OAuth credential. The implementation accepts MiniMax expiry values expressed as relative seconds, Unix epoch seconds, or Unix epoch milliseconds. OAuth-returned verification and resource URLs are restricted to the expected HTTPS hosts before use. ## Type - [ ] Fix - [x] Feature - [x] Docs - [ ] CI / tooling - [ ] Refactor - [ ] Other ## Verification - `uv run pytest tests/test_minimax_oauth.py tests/test_cli_provider_commands.py tests/test_provider_catalog.py tests/test_config_update_providers.py tests/test_tui_rpc_model.py tests/test_tui_rpc_setup.py tests/test_cli_status_commands.py -q -x` - 143 passed. - Focused MiniMax onboarding tests - 3 passed. - `uv run ruff check raven tests` - passed. - `uv run ruff format --check raven tests` - 761 files already formatted. - `make check-large-files` - passed. - `make check-commits` - passed. - Two independent read-only reviews completed; the final review found no blocking issues. - The full test collection was not run because the local environment does not include the optional `dingtalk_stream` channel dependency. All provider, OAuth, onboarding, TUI setup, status, and configuration tests relevant to this change passed. - [x] Relevant tests pass locally - [x] Relevant lint / type checks pass locally - [x] User-facing docs or screenshots are updated when needed ## Risk - OAuth credentials are stored in region-specific files with restrictive permissions and atomic replacement. - Refresh operations use a cross-process lock and persist rotated refresh tokens before exposing them to the runtime. - OAuth verification and API resource URLs are validated against region-specific host allowlists. - Existing `minimax` API-key configuration remains unchanged; the new OAuth providers use separate slugs and storage. - Rollback consists of reverting this change. Existing API-key provider configuration and credentials are not migrated or modified. - [x] Security impact considered - [x] Backward compatibility considered - [x] Rollback path is clear for risky changes ## Related Issues N/A - Live OAuth smoke test: MiniMax Global MiniMax-M3 returned "MiniMax OAuth works" successfully after normalizing the Anthropic-compatible resource URL. --------- Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
## Summary CI ran `make test-python` over 4 hardcoded files, so ~268 of 272 top-level test files never executed on any PR. A bare `uv run pytest` could not stand in for it either: collection aborted with 7 errors (channel-adapter tests importing optional SDKs at module level), and `testpaths` pulled `tests/integration/` into every run. This makes the whole unit suite the blocking CI gate. - `Makefile`: `test-python` runs the whole unit tree instead of a hardcoded file list. - `ci.yml`: the `python` job splits into `lint-python` and a `unit` job running the whole suite on ubuntu / py3.12; the matrix block is kept so more versions are one list entry away. - `pyproject.toml`: register `integration` / `e2e` markers, enable `--strict-markers` / `--strict-config`, exclude `tests/integration` via `norecursedirs`, and default to `-m "not integration and not e2e"` so a bare `pytest` is safe. - Guard the 7 channel-adapter tests with `pytest.importorskip`; CI installs `--all-extras`, so they run there rather than skip. - Relocate 9 integration / real-resource files out of the flat unit tree into `tests/integration/`, renaming the token_wise benchmarks to the `real_llm` kind required by AGENTS.md section 5.2. Turning the suite on surfaced 44 pre-existing failures, all fixed here: - 29 from `asyncio.get_event_loop().run_until_complete()` in three producer test files, which picks up a closed loop once other async tests have run; now `asyncio.run()`. - 2 stale `run_turn` stubs missing the `inline_tool_stream` keyword the production callers pass. - 12 from cross-test state leaking through `raven.config.loader._current_config_path`, litellm's global logging handlers, and an ambient dependency on a populated `~/.raven` in the TUI init-error tests. - 1 flaky concurrency test whose timing bound left only 0.05s of margin. The full run also recovered 38 genuine unit tests under `tests/tui/autotest` that a directory-level exclusion would have orphaned; they are selected normally now, with the 24 subprocess-spawning e2e tests deselected by marker. Deliberately out of scope: an integration / nightly CI tier (real-LLM runs are hard to make meaningful in CI and cost money -- to be designed separately), and the naming and assertion debt tracked in #223. ## Type - [x] CI / tooling ## Verification ``` uv run --all-extras pytest -q # 4611 passed, 24 deselected in 193s bash .claude/scripts/preflight_ci.sh # ALL PREFLIGHT CHECKS PASSED (commitlint, large files, pre-commit, ruff, tests) ``` - [x] Relevant tests pass locally - [x] Relevant lint / type checks pass locally ## Risk - [x] Security impact considered - [x] Backward compatibility considered - [x] Rollback path is clear for risky changes No production code changes -- test files, test configuration, and CI workflow only. The repository ruleset currently declares no required status checks, so renaming the job blocks nothing. If the `unit` job should become a merge gate, add `unit (py3.12 / ubuntu-latest)` to the ruleset separately. ## Related Issues Closes #222 --------- Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
## Summary
Two packaging defects, plus the CI simplification the second one
unblocks.
**`raven[sandbox]` could not be installed on Windows.** boxlite
publishes wheels for macOS and Linux only -- it is a microVM runtime
built on KVM / Hypervisor.framework, and its PyPI classifiers say so --
but our declaration carried no platform marker, so the install failed
outright:
```
error: Distribution `boxlite==0.9.5` can't be installed because it doesn't have
a source distribution or wheel for the current platform
```
The code already degrades gracefully when boxlite is absent:
`sandbox/interfaces.py` keeps the shared types importable without it,
and both call sites probe with `try: import boxlite / except
ImportError`. So a Windows user should have gotten a Raven that reports
the sandbox as unavailable, not a failed install. `python-socks` and
`matrix-nio` already carry the same `sys_platform != 'win32'` marker;
boxlite now does too.
**The `retrieval` extra had no consumer.** It declared torch and
transformers, which nothing imports -- a whole-tree grep finds them only
in that declaration, two comments, and a doc note, with no dynamic
loading and no doc telling users to install it. It dates back to the
initial commit. Keeping it cost a multi-hundred-megabyte download per
environment and added a native crash surface of the kind `cli/_exit.py`
documents. Removed via `uv remove`; the lockfile drops from 220 to 194
packages.
**CI follows.** With torch out of the extras, `--all-extras` now
resolves to what the hand-maintained `channels + tools + sandbox` list
covered, so the unit job goes back to it and stays correct as extras
change. Every job also gains `timeout-minutes`.
A `windows-latest` cell was added to measure whether the marker fix was
enough to run the suite there. It is not: the install now succeeds, but
the suite hit 121 failures by 28 percent, across sixteen files, from
systemic platform assumptions (assertions hardcoding forward slashes,
fcntl locking, socket and subprocess differences). That is a port, not a
fix, so the cell was dropped. This matches comparable projects:
hermes-agent runs every job on ubuntu and substitutes a blocking static
check for Windows-unsafe primitives, and openclaw's PR-blocking workflow
is ubuntu-only with Windows behind workflow_dispatch. The dedicated
Windows self-upgrade job still covers the install path.
The macos cell is dropped for the same reason, one level milder: it
reported the same failures as ubuntu on every run while ubuntu
additionally caught two Linux-specific problems it could not, and
development happens on macos anyway. The unit job is now a single ubuntu
cell, with the matrix block kept so another entry is one list item away.
## Type
- [x] Bug fix
## Verification
```
uv run --all-extras pytest -q
# 4611 passed, 24 deselected -- same count as before, torch was never used
uv run python -c "import importlib.metadata as m; m.version('torch')"
# PackageNotFoundError, and boxlite still installs on this macOS host
bash .claude/scripts/preflight_ci.sh
# ALL PREFLIGHT CHECKS PASSED
```
- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
## Risk
- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes
`raven[retrieval]` stops being a valid extra. Nothing in the repo or the
docs referenced it, and installing it only pulled torch into the
environment without any code path using it, so no behaviour changes.
The Windows matrix experiment is described above and left out of the
final state; both dependency fixes stand on their own.
## Related Issues
Closes #225
---------
Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
|
Heads-up on a naming change that will collide with this branch. A test-debt cleanup (#223) renames Sorry for the churn. Nothing to do until the rename merges. |
## Summary
The TUI already read `update_behind` / `update_command` out of the
session init
bundle, but nothing on the Python side ever filled those fields, so the
hint
never appeared. This wires up the missing half and moves where it shows.
**The check**
`raven/cli/update_notice.py` compares the running version against a
small
cache in `~/.raven/update_check.json`:
- `raven tui` calls `maybe_refresh_async()` once per launch. It spawns a
daemon
thread only when the cache is missing or older than 24h, so a normal
launch
touches the network at most once a day and never blocks startup.
- The gateway only *reads* that cache when it builds the session info
bundle
(`_default_session_info`). Reading is pure and fast; no network on the
session-create path, which is why the check is cached rather than live.
- Every failure path is swallowed: no cache, unparseable version,
corrupt JSON,
network error, rate limit. An update hint must never break startup.
The tradeoff is staleness by one launch: the first launch after a
release
lands refreshes the cache, and the hint shows on the next launch.
**Where it shows**
The status bar's right slot, in the warn color, replacing the cwd/branch
label
while an update is available. No extra line, no layout shift.
The old banner block in `branding.tsx` is removed. The banner is the
first
thing on screen at startup and an upgrade nudge does not deserve that
weight.
`update_behind` is now a flag rather than a commit count, so the text is
version-agnostic (`Update available - run raven upgrade`) instead of the
old
`N commits behind`.
## Type
- [ ] Fix
- [x] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other
## Verification
```
uv run ruff check raven/ tests/ # All checks passed
uv run ruff format --check raven/ tests/ # 760 files already formatted
uv run pytest tests/test_cli_update_notice.py \
tests/test_cli_tui_commands.py \
tests/test_tui_rpc_session.py -q # 95 passed
cd ui-tui && npm run type-check # clean
cd ui-tui && npm test # 904 passed | 3 skipped
cd ui-tui && npm run lint # 0 errors
cd ui-tui && npx prettier --check "src/**/*.{ts,tsx}" # clean
```
New tests cover the cache read/write and refresh throttle
(`tests/test_cli_update_notice.py`), the status-bar slot swap
(`ui-tui/src/__tests__/statusRule.test.tsx`), and the banner no longer
rendering the block (`branding.test.tsx`).
Also exercised the five edge cases by hand: absent cache, newer cached
release, cache equal to current, unparseable version string, and a
corrupt
cache file. Only the second one returns a notice; the rest return
`None`.
The lint warning reported on `appChrome.tsx`
(`react-hooks/exhaustive-deps`,
line 275) is pre-existing on `main` and untouched by this change.
- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [ ] User-facing docs or screenshots are updated when needed
## Risk
- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes
The GitHub releases fetch reuses
`upgrade_commands._fetch_latest_release`, so
no new network surface or credential handling. The cache file holds a
version
string and a timestamp only.
Wire-compatible: `update_behind` / `update_command` were already
optional
fields in the info bundle, and a client that never receives them behaves
as
before. Rollback is reverting this commit; the feature degrades to its
current
state, which is showing nothing.
To see it locally:
```
printf '{"latest_version":"9.9.9","checked_at":%s}' "$(date +%s)" > ~/.raven/update_check.json
```
## Related Issues
N/A
---------
Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
## Summary
Reworks the TUI transcript so a turn reads as a sequence of steps
instead of
interleaved thinking blocks and tool lines. Every model call becomes one
"episode" holding its reasoning, narration and tools.
**Rendering**
- A step with no narration collapses to one summary line ("reasoning for
8s,
read 2 files"); a narrated or running step shows a reasoning fold, the
narration as prose, then its tool rows.
- Reasoning expands while it streams and folds once the step produces
anything
visible (narration, a tool, or the answer text).
- A run of same-name calls renders as a tree: one header plus a child
row per
call, so a burst of parallel searches no longer fills the screen.
- In-flight tools report elapsed time; finished ones keep their measured
span.
- Rows are bound to the transcript width and clipped with an ellipsis.
Without
the bound, long details overflowed the container and the terminal
soft-wrapped
them, which looked like stray blank lines and edge-cut text.
**Tool display contract**
Tools now own how they appear. `Tool.display_call` and the new
`ToolResult`
(`model_text` plus optional `display_text`) separate what the model
reads from
what the transcript shows:
- `ask_user` renders its question and the answer instead of a raw
arguments
blob, pairing each question with its answer when several are asked at
once.
- Tools with no explicit verb derive a readable one from their own name,
so a
newly added tool renders sensibly with no changes to the verb table (it
is an
overrides list, not a required registry).
- The call preview picks the query, question or command out of the
arguments
instead of the first value, which could be a numeric flag.
**Also**
- Episodes are the default transcript style; `/transcript legacy` stays
as an
escape hatch and the legacy path is untouched.
- An interrupt commits the collapsed episode view instead of dumping the
raw
expanded segments.
- The virtual-height estimate accounts for episode rows. Without it the
transcript reserved too few rows and left stale cells on screen.
- Markdown table columns fit the available width with CJK-aware
clipping.
A cell wider than its column is truncated with an ellipsis and cannot be
expanded in the TUI (it used to soft-wrap, which kept every character
but
looked broken), and inline markup inside a cell is flattened to plain
text
rather than rendered.
- The deep-research offer states that a regular search is the user's
chosen
path, not a failure, so the model stops narrating it as a broken
fallback.
**Also in this diff, beyond the stated scope**
- The queued-message panel is restyled: the `queued (N)` header is gone
and the
edit hints moved to a trailing line. The count stays derivable from the
window
plus `...and N more`, and the edited row is still highlighted.
- User messages get their own tier: an accent chevron and bold text, so
a prompt
reads as a prompt next to the assistant's prose.
- `transcriptGutterWidth` takes the tool glyph, so the user and
assistant gutters
are derived from one place instead of two hardcoded widths.
## Review follow-ups
Addressed in the second commit, by the reviewer's IDs:
- **R1** an interrupt with no `episode.start` dropped the accrued
segments and
tool trail; it now falls back to the legacy trail exactly like the
completion
path, with a regression test.
- **R2** the `ToolResult` / `display_call` contract had no Python
coverage. Adds
`tests/test_ask_user_tool.py` and a loop test asserting
`add_tool_result` gets
`model_text` while `tool.complete` carries `display_text`.
- **R3** the transcript-mode comment was wrong on all three claims.
- **R4** `ToolRegistry.execute` now unwraps at the boundary and returns
`ToolOutput`, a `str` subclass carrying the display string, so the
sentinel
action executor, subagent manager, curator and tracing can no longer
receive a
dataclass. `isinstance(.*ToolResult)` hits the registry only.
- **R7** drops `episodeLabel`, `turnSummary`, `toolLine` and
`episodeIndex`.
- **R8** the height estimate counts the inter-step margin row.
- **R9** (mid-turn `/transcript` switch) and **R10** (clarify answer
beyond the
200-char preview) are left for a follow-up: both add state or a wire
field
rather than fixing this diff.
## Type
- [ ] Fix
- [x] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other
## Verification
```
cd ui-tui && npm run type-check # clean
cd ui-tui && npm test # 927 passed | 3 skipped
cd ui-tui && npm run lint # 0 errors, no warnings in touched files
cd ui-tui && npx prettier --check "src/**/*.{ts,tsx}" # clean
uv run ruff check raven/ tests/ # All checks passed
uv run ruff format --check raven/ tests/ # 758 files already formatted
make test-python # 4621 passed, 2 pre-existing failures
cd ui-tui && npx vitest run # 925 passed | 3 skipped
```
New tests cover the episode commit path (including interrupt), the tool
summary
and verb fallback, the argument preview, and the rendered layout.
- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [ ] User-facing docs or screenshots are updated when needed
## Risk
- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes
The wire gains one optional `tool.start` field (`display`); older
payloads
without it fall back to the generic preview. `execute` may now return
either a
string or a `ToolResult`, so existing tools are unaffected. Rollback is
`/transcript legacy` for the view, or reverting this commit.
Known follow-ups, not addressed here: occasional screen garbling seen
when
messages are queued (root cause not yet identified), and `web_search`
being
registered even without an API key.
## Related Issues
N/A
---------
Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
|
Got it, thanks for the heads-up. I'll rebase onto the latest main and move my test changes to |
## Summary A read-only audit of all 298 test files found the suite in good shape -- exactly one genuinely fake test -- with the real debt sitting in file names and a scattering of assertions that passed for the wrong reason. This clears that, and fixes the bugs found while clearing it. **Three dead tests dealt with.** Two removed because neither could ever assert anything: - `test_chat_e2e.py` was a module-level skip over three `NotImplementedError` stubs. The wire-up it waited for has shipped (`tui_commands` instantiates `SubscriptionEmitter`) and the proposal doc it cited is gone. - `test_tui_exit_e2e.py` asserted that building the agent loop leaves lancedb's thread live. It does not: the loop is constructed but never started, and the memory plugin talks to everos over HTTP, so lancedb is not even imported. The assertion could not hold in any configuration. The third, `slashParity.test.ts`, shelled out to python for a `raven_cli.commands.COMMAND_REGISTRY` that no longer exists, and skipped all three of its cases when the import failed -- three skips on every run. The parity it wanted needs no python: `createSlashHandler.ts:45-51` routes a slash locally when `findSlashCommand` resolves it and otherwise hands it to `slash.exec`, a CLI subprocess a mutation cannot reach the live session from. It now asserts that predicate directly. `NATIVE_MUTATING_COMMANDS` and the three-way `classifyRoute` went with it: all six names in that set are also in the local registry, so the native branch could not change any outcome. **19 files renamed** off phase, ticket and bug codes (AGENTS.md section 5.1), including one reversed CLI name and two integration files moved onto a legal `<kind>`. Reference updates ride along: `.pre-commit-config.yaml` names one of these files in a `detect-private-key` exclude, and three docs pages point at them. **Assertions that passed for the wrong reason, tightened**: eight over-broad `pytest.raises(Exception)` narrowed to the type each site actually raises; an `except` clause that swallowed the `AssertionError` its own fakes use as a tripwire; a `hasattr` probe replaced by binding a broker and checking it stuck; and a recall assertion that read as a pass on an empty result set. **Dead weight removed**: ten unused locals, three dead code blocks, six `importorskip` guards on hard dependencies, two hardcoded `/tmp` paths, and ticket codes left in docstrings after the renames. **Two coverage gaps the deletions exposed are now closed**: - `tests/test_cli_exit.py` pins the hard-exit guard, which had no tests at all. - The TUI chat e2e gains a multi-turn case. It reads the persisted session rather than the screen, because a prompt is echoed into the transcript the moment it is typed -- screen-scraping for a planted word passes even when history is broken. **Three timing bugs fixed in the TUI e2e tests.** They keyed off the banner, which paints about seven seconds before the app accepts input, and submitted the moment text was typed. Dropped prompts made the pipeline look dead in one test, and made a negative assertion (`assert not leak_detected`) pass vacuously in another -- no prompt means no streaming means no leak. **Gaps in the two features that landed during review**, small enough to close here: the `session.py` wiring that fills `update_available` / `update_command` (removing it left the suite green), `_upgrade_command_works` (a fixture stubbed it to `True` everywhere), an `episodeSummary` case with an empty test body, and `clipToWidth` / `hasMeaningfulReasoning`, which had no coverage at all. The larger items from that audit are tracked in #229. **Upstream review attributions dropped from ui-tui comments.** 36 mentions of another project's review thread (#19835 as "Copilot round-N review"), plus #18994, #19194 and #14045 -- none resolve to anything here, and AGENTS.md section 1.1 rules out comments pointing at information only visible elsewhere. The rationale each carried is kept; four `describe()` titles lose a trailing `(#18994)`. Three went further and documented a python counterpart that does not exist: `platform.ts` credited key-spelling normalization to `raven_cli/voice.py` (no such module -- `raven/tui_rpc/methods/_stubs.py` answers `voice.toggle` with "voice not supported in Raven v0.1"), and `useConfigSync.ts` plus its test credited an `interrupt` default to `raven_cli/config.py` and `tui_gateway/server.py::_load_busy_input_mode` (`busy_input_mode` appears nowhere in python; neither module exists). **One flaky test fixed, caught by this PR's own CI run.** `test_stdio_no_executor_does_not_raise` set `command="true"` and let `connect_mcp_servers` really spawn it, so the result depended on how a live child process raced the MCP handshake -- it failed once on CI in the full-suite run while passing 30 out of 30 standalone runs locally. It also asserted only that whatever came back was not a `SandboxInitError`. The transport now raises instead of running (the idiom its sibling test already uses) and the test asserts the stdio branch was reached. Review follow-ups, folded in: the hard-exit test now skips rather than asserting no lancedb thread exists process-wide, `tests/conftest.py` no longer credits the pytest segfault to a lancedb thread the suite never starts (raven imports lancedb nowhere), and the status-bar patterns the two TUI e2e tests wait on live in one module instead of a hand-mirrored copy per file. Every new or changed assertion was checked by mutating the code under test and confirming the assertion fails. ## Type - [x] Others Test files, test configuration, and comments. The only non-test files touched are ui-tui comments and four `describe()` titles -- no behaviour change. ## Verification ``` uv run --all-extras pytest -q # 4654 passed, 25 deselected cd ui-tui && npx vitest run # 936 passed, no skips uv run --all-extras pytest tests/tui/autotest -m e2e -q # passes locally with tui-use installed and ui-tui built bash .claude/scripts/preflight_ci.sh # ALL PREFLIGHT CHECKS PASSED (including the TUI lane) ``` Mutation checks, each confirming the new assertion fails when the code under test is broken: the `session.py` nudge wiring removed; `clipToWidth` replaced with identity; `hasMeaningfulReasoning` forced to `true`; a never-sent prompt added to the multi-turn session assertion. - [x] Relevant tests pass locally - [x] Relevant lint / type checks pass locally ## Risk - [x] Security impact considered - [x] Backward compatibility considered - [x] Rollback path is clear for risky changes No behaviour changes: the non-test diff is comments only. The renames touch git history for 19 test files; #208 is the only open PR affected and has been notified. Two known items are deliberately left alone: `test_dogfood_whitelist.py` is flaky when the whole file runs (tracked in #228, unchanged here), and the `lancedb_finalization_hazard` gate in `commands.py` never fires today -- its test docstring says so explicitly, so the coverage does not read as an argument for keeping it. ## Related Issues Closes #223 --------- Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
## Summary `test_dogfood_whitelist.py` was reported as flaky: run the file and a few cases fail, a different few each time. It was not flaky. Three defects stacked, and each one hid the next. **Every one of the 14 output assertions was vacuous.** The expected pattern is matched against the whole screen, which includes the composer echoing the command just typed. 13 of the 14 were satisfied by that echo alone -- `skill list` on `skill`, `cron show` on `cron`, `sandbox list` on `sandbox`. The fourteenth, `status` (`OpenRouter|Model:`), was satisfied by the welcome frame's own status line. This is a pure string property, no timing involved. **Readiness keyed off the banner**, which paints before the app accepts input. Typing right after it, the composer was observed still showing its placeholder -- the keystrokes were dropped entirely. Defect 1 is why that was invisible: a dropped command still satisfied the assertion. **Ctrl+C is a ladder, not an exit key.** In `useInputHandlers.ts` a busy UI cancels the turn, a non-empty composer clears the input, and only then does it quit. The test pressed it once, so any command that left the UI busy timed out in `expect_exit`. That is the reported flakiness, and pressing until the process exits takes the file from `3 failed, 4 passed, 4 xfailed, 3 xpassed` to `0 failed`. The three together also explain the experiment recorded in #228 as inexplicable: fixing readiness alone made one case fail *deterministically*, because the command then really submitted and really ran a turn, so the UI was busy when the single Ctrl+C arrived. Ruled out with instrumentation rather than argument: a pytest plugin logging `tui-use list` at every setup and teardown shows no session ever leaked between cases, and `tui-use info` retains `Status: exited` indefinitely, so `expect_exit` cannot lose a code it already had. ## What else the audit turned up **Three commands in the whitelist do not exist.** `skill refresh` and `skill stats` (the CLI has only `skill list` / `skill get`), and `cron show`, whose rename to `cron get` shipped long ago. All three were answered with "unknown command" and passed anyway. **The same three defects were in every other file in the tier**, so they are fixed there too, and the seven `xfail(strict=False)` markers are gone -- their reason blamed a Raven overlay-exit defect, but with a correct exit sequence all seven pass. **The README was the source.** Its reference example keyed readiness off the banner, asserted `OpenRouter|Model:`, and exited with a single Ctrl+C. Every file that copied it inherited all three. It now documents the correct sequence, states plainly what the tier cannot do, and drops eight references to documents that no longer exist. **The tier is now scoped to what only it can prove.** Dogfood covers four output shapes instead of 12 commands: `createSlashHandler.ts` routes every dispatched slash through the same `slash.exec` call, and the Python side already has 31 dedicated tests, so per-command repetition proved nothing the first case did not. `test_e2e_tui_status_slash.py` is deleted (same spawn, same command, same assertion as the dogfood `status` case) and so is `test_e2e_raven_chat_cli.py`, an xfail-strict placeholder spawning a `raven chat` that is neither implemented nor planned -- its tripwire moves to `tests/test_cli_smoke.py`, where it costs nothing and actually runs. **The Ctrl+C ladder moves into vitest.** `decideCtrlC` is now a pure function, the shape this file already used for `applyVoiceRecordResponse`, so the rungs are pinned without a PTY or a live turn. Hook behaviour is unchanged: `isTurnActive()` is still only consulted on the busy path. A static sweep of all 301 test files for assertions their own input can satisfy found exactly one more, in `test_session_fork_help`, which asserted `"fork"` against `fork --help` output whose usage line echoes the subcommand name. ## Type - [x] Others Tests, test configuration and comments, plus one behaviour-preserving extraction in `ui-tui/src/app/useInputHandlers.ts`. ## Verification ``` uv run --all-extras pytest -q # 4655 passed, 13 deselected cd ui-tui && npm test # 943 passed uv run --all-extras pytest tests/tui/autotest -m e2e # 13 passed, no xfail, no skips, 2m36s (was 22 passed / 4 xfailed / 3 xpassed in 4m21s) bash .claude/scripts/preflight_ci.sh # ALL PREFLIGHT CHECKS PASSED (including the TUI lane) ``` The dogfood file was run three consecutive times: 12 passed each, no flakes. Every new assertion was mutation-checked: restoring `skill refresh` fails on missing output; pointing a pattern at `OpenRouter` trips the idle-screen guard; spawning `sh -c 'cat > /dev/null'` makes the cat-echo assertion fail where the old one passed; registering a `chat` command trips the new tripwire; three separate mutations of the Ctrl+C ladder each turn vitest red. - [x] Relevant tests pass locally - [x] Relevant lint / type checks pass locally ## Risk - [x] Security impact considered - [x] Backward compatibility considered - [x] Rollback path is clear for risky changes The only non-test change is extracting `decideCtrlC` out of the `useInputHandlers` Ctrl+C branch; the branch order and the guard on `isTurnActive()` are preserved, and three mutations confirm the new tests fail when it is not. Left alone deliberately: no CI job runs this tier, so these runs rest on local execution -- the repo's existing gap, now stated in the README rather than implied. The tier also still lives at `tests/tui/autotest/tests/`, a third location AGENTS.md section 5 does not sanction; splitting it into `tests/` and `tests/integration/` needs its own PR, since it is renames plus a fixture move with no bearing on this fix. ## Related Issues Closes #228 --------- Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
6e2f1fb to
f98b71f
Compare
This PR was auto-closed by a history rewrite, not by a maintainerOn 2026-09-12 the Your work is safe in your fork. Nothing in your repository was modified. To restore this PR, rebase onto the new git remote add upstream https://github.com/EverMind-AI/Raven.git # if you don't have it
git fetch upstream 'refs/tags/backup/pre-linearize-20260912:refs/tags/old-main'
git fetch upstream main
git rebase --onto upstream/main $(git merge-base <your-branch> old-main) <your-branch>
git push --force-with-leaseThen open a new PR and reference this one. Thank you for the contribution, and sorry for the disruption. |
## Summary A read-only audit of all 298 test files found the suite in good shape -- exactly one genuinely fake test -- with the real debt sitting in file names and a scattering of assertions that passed for the wrong reason. This clears that, and fixes the bugs found while clearing it. **Three dead tests dealt with.** Two removed because neither could ever assert anything: - `test_chat_e2e.py` was a module-level skip over three `NotImplementedError` stubs. The wire-up it waited for has shipped (`tui_commands` instantiates `SubscriptionEmitter`) and the proposal doc it cited is gone. - `test_tui_exit_e2e.py` asserted that building the agent loop leaves lancedb's thread live. It does not: the loop is constructed but never started, and the memory plugin talks to everos over HTTP, so lancedb is not even imported. The assertion could not hold in any configuration. The third, `slashParity.test.ts`, shelled out to python for a `raven_cli.commands.COMMAND_REGISTRY` that no longer exists, and skipped all three of its cases when the import failed -- three skips on every run. The parity it wanted needs no python: `createSlashHandler.ts:45-51` routes a slash locally when `findSlashCommand` resolves it and otherwise hands it to `slash.exec`, a CLI subprocess a mutation cannot reach the live session from. It now asserts that predicate directly. `NATIVE_MUTATING_COMMANDS` and the three-way `classifyRoute` went with it: all six names in that set are also in the local registry, so the native branch could not change any outcome. **19 files renamed** off phase, ticket and bug codes (AGENTS.md section 5.1), including one reversed CLI name and two integration files moved onto a legal `<kind>`. Reference updates ride along: `.pre-commit-config.yaml` names one of these files in a `detect-private-key` exclude, and three docs pages point at them. **Assertions that passed for the wrong reason, tightened**: eight over-broad `pytest.raises(Exception)` narrowed to the type each site actually raises; an `except` clause that swallowed the `AssertionError` its own fakes use as a tripwire; a `hasattr` probe replaced by binding a broker and checking it stuck; and a recall assertion that read as a pass on an empty result set. **Dead weight removed**: ten unused locals, three dead code blocks, six `importorskip` guards on hard dependencies, two hardcoded `/tmp` paths, and ticket codes left in docstrings after the renames. **Two coverage gaps the deletions exposed are now closed**: - `tests/test_cli_exit.py` pins the hard-exit guard, which had no tests at all. - The TUI chat e2e gains a multi-turn case. It reads the persisted session rather than the screen, because a prompt is echoed into the transcript the moment it is typed -- screen-scraping for a planted word passes even when history is broken. **Three timing bugs fixed in the TUI e2e tests.** They keyed off the banner, which paints about seven seconds before the app accepts input, and submitted the moment text was typed. Dropped prompts made the pipeline look dead in one test, and made a negative assertion (`assert not leak_detected`) pass vacuously in another -- no prompt means no streaming means no leak. **Gaps in the two features that landed during review**, small enough to close here: the `session.py` wiring that fills `update_available` / `update_command` (removing it left the suite green), `_upgrade_command_works` (a fixture stubbed it to `True` everywhere), an `episodeSummary` case with an empty test body, and `clipToWidth` / `hasMeaningfulReasoning`, which had no coverage at all. The larger items from that audit are tracked in EverMind-AI#229. **Upstream review attributions dropped from ui-tui comments.** 36 mentions of another project's review thread (#19835 as "Copilot round-N review"), plus #18994, #19194 and #14045 -- none resolve to anything here, and AGENTS.md section 1.1 rules out comments pointing at information only visible elsewhere. The rationale each carried is kept; four `describe()` titles lose a trailing `(#18994)`. Three went further and documented a python counterpart that does not exist: `platform.ts` credited key-spelling normalization to `raven_cli/voice.py` (no such module -- `raven/tui_rpc/methods/_stubs.py` answers `voice.toggle` with "voice not supported in Raven v0.1"), and `useConfigSync.ts` plus its test credited an `interrupt` default to `raven_cli/config.py` and `tui_gateway/server.py::_load_busy_input_mode` (`busy_input_mode` appears nowhere in python; neither module exists). **One flaky test fixed, caught by this PR's own CI run.** `test_stdio_no_executor_does_not_raise` set `command="true"` and let `connect_mcp_servers` really spawn it, so the result depended on how a live child process raced the MCP handshake -- it failed once on CI in the full-suite run while passing 30 out of 30 standalone runs locally. It also asserted only that whatever came back was not a `SandboxInitError`. The transport now raises instead of running (the idiom its sibling test already uses) and the test asserts the stdio branch was reached. Review follow-ups, folded in: the hard-exit test now skips rather than asserting no lancedb thread exists process-wide, `tests/conftest.py` no longer credits the pytest segfault to a lancedb thread the suite never starts (raven imports lancedb nowhere), and the status-bar patterns the two TUI e2e tests wait on live in one module instead of a hand-mirrored copy per file. Every new or changed assertion was checked by mutating the code under test and confirming the assertion fails. ## Type - [x] Others Test files, test configuration, and comments. The only non-test files touched are ui-tui comments and four `describe()` titles -- no behaviour change. ## Verification ``` uv run --all-extras pytest -q # 4654 passed, 25 deselected cd ui-tui && npx vitest run # 936 passed, no skips uv run --all-extras pytest tests/tui/autotest -m e2e -q # passes locally with tui-use installed and ui-tui built bash .claude/scripts/preflight_ci.sh # ALL PREFLIGHT CHECKS PASSED (including the TUI lane) ``` Mutation checks, each confirming the new assertion fails when the code under test is broken: the `session.py` nudge wiring removed; `clipToWidth` replaced with identity; `hasMeaningfulReasoning` forced to `true`; a never-sent prompt added to the multi-turn session assertion. - [x] Relevant tests pass locally - [x] Relevant lint / type checks pass locally ## Risk - [x] Security impact considered - [x] Backward compatibility considered - [x] Rollback path is clear for risky changes No behaviour changes: the non-test diff is comments only. The renames touch git history for 19 test files; EverMind-AI#208 is the only open PR affected and has been notified. Two known items are deliberately left alone: `test_dogfood_whitelist.py` is flaky when the whole file runs (tracked in EverMind-AI#228, unchanged here), and the `lancedb_finalization_hazard` gate in `commands.py` never fires today -- its test docstring says so explicitly, so the coverage does not read as an argument for keeping it. ## Related Issues Closes EverMind-AI#223 --------- Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
Summary
llm_gate_modelis unsetRoot cause
The context-engine factory did not pass the active agent model into either Skill Forge LLM stage.
QueryRewriterhad no model parameter, whileLLMGateFilterreceivedNonewhen no gate-specific override was configured. Both calls could therefore fall back to a provider-level default that differed fromagents.defaults.model, including models unavailable in the user's region.Impact
The rewriter and gate now use the same configured model as the agent by default. Deployments that set
skillForge.llmGateModelcontinue to use that explicit model for gate calls.Follow-up consideration
A future change could add an optional
rewrite_modeloverride, withagents.defaults.modelas its fallback. That would let operators pin a cheaper or lower-latency model for query rewriting without relying on an implicit provider default. This PR intentionally does not add a new configuration field so the fix remains focused on model inheritance.Validation
uv run pytest tests/test_skill_forge_rewriter.py tests/test_skill_forge_gate.py tests/test_phase_a_default_engine.py -q— 33 passeduv run --extra dev ruff checkon all changed Python filesuv run --extra dev ruff format --checkon all changed Python filesgit diff --checkCloses #155.