Gate hive-mind init/join/leave/spawn/propose/vote/broadcast/shutdown/
memory set+delete/optimize-memory for remote (HTTP/WebSocket) callers only;
local stdio, ruflo mcp exec, the CLI and in-process callers keep working
with no extra step. hive-mind_init never returns hiveToken. Operator secret
from RUFLO_HIVE_BOOTSTRAP_SECRET or a 0600 bootstrap.secret created by the
first local init. state.json is written 0600 atomically (dir 0700).
Constant-time compare. RUFLO_HIVE_REQUIRE_AUTH=1 for bridged stdio.
Co-Authored-By: RuFlo <ruv@ruv.net>
- bridgeStorePattern no longer turns a bridgeStoreEntry {success:false} into a success receipt
- agentdb_pattern-store checks storeEntry's result in the registry-null fallback and refuses failed writes
- hooks pattern-store falls back to the direct store when the bridge write was refused
- graph writer, native recovery/repair and attestation open SQLite via the better-sqlite3 copy AgentDB resolves, so the idle close cannot strip a live AgentDB handle's WAL/SHM sidecars
Co-Authored-By: RuFlo <ruv@ruv.net>
Review follow-ups on the destination pinning:
- null and "" now mean "no assertion" (MCP clients commonly send null for
optional fields) instead of throwing; the destination still comes only
from server configuration, so this cannot widen anything.
- Operator configuration may name a plain-HTTP loopback service
(localhost, 127.0.0.1, [::1]) for a local or self-hosted gateway/LLM.
Public http:// and lookalike hosts (localhost.attacker.example,
127.0.0.1.nip.io, 10.0.0.1) stay rejected. An argument must still equal
the configured base exactly, so it can never select a loopback port the
operator did not.
- Note: this supersedes ADR-125's "tool argument beats env var" precedence
for RUFLO_X_GATEWAY_URL and SERAPHINA_METALLM_URL; arguments are now
assertions only.
Tests: mcp-service-url-security 37/37 (new: absent overrides, loopback
config and port mismatch, lookalike hosts; 0/false/{} still rejected),
x-federation-tools passes; CLI build clean; env-var precedence audit ok.
Co-Authored-By: RuFlo <ruv@ruv.net>
Validate configured HTTPS bases and matching compatibility arguments before I/O, reject redirects, and retain operator-only destination selection for federation and Seraphina tools.
Prepared by Codex in Dragan’s authorized session.
Each mission_* description now states when to use it and why the nearest
alternative (task_create, memory_store, task_status, polling mission_get,
task_cancel) is wrong. scripts/audit-tool-descriptions.mjs: 392/392 with
guidance, all gates pass.
Co-Authored-By: RuFlo <ruv@ruv.net>
`ruflo mission create|plan|get|events|action` and the mission_create,
mission_plan, mission_get, mission_events and mission_request_action MCP
tools are thin transports over the one MissionService and its zod schemas;
MCP calls are still authorized by the existing policy runtime in
mcp-client.ts. A bounded, sanitized .claude-flow/missions/observation.json
(contract ruflo.mission-observation/1, source-time observedAt) is rewritten
atomically after each committed event for the workbench to read.
scripts/e2e-adr-406.mjs runs the built CLI in throwaway HOME,
CLAUDE_CONFIG_DIR and project dirs: catalog current and verified, legacy
command files byte-unchanged, CLI create + MCP plan reading identical
records and events, revision conflict, honest admit refusal, SIGKILL of a
writer then restart, and the §18 fixture replay with mods off vs on
(permissions, policy mode and outcomes unchanged; uninstall restores).
RUFLO_E2E_LIVE=1 adds engine version and plugin validate (no model calls).
Mod hooks the engine confirms are recorded against their declaring source
file, so the catalog is identical with or without an engine. mission_*
tools are classified in the capability brain's tasks/workflows domain.
Co-Authored-By: RuFlo <ruv@ruv.net>
Follow-up to drakeo338's fix, closing the remaining gaps found in review:
- Keys: MCP memory_store rejected `../` in keys, but CLI `memory store`
and `memory_import` accepted it. The MCP rule (DANGEROUS_KEY_PATTERN)
is now exported through `memoryKeyError()` and used by all three;
import checks every key up front, so a bad file writes nothing. Keys
with a plain `/` (`probe/x`) stay legal.
- Purge: `purgeNamespace` used its own NAMESPACE_PATTERN, which rejected
`:`, so `team:alice` could be stored and exported but never purged. It
now uses `validateIdentifier`, like the other three operations.
- MCP memory_store: namespaces only faced the key blacklist, so
`proj/sub` was accepted there but rejected by export and purge. It now
uses `validateIdentifier` too.
Tests added to memory-store-import-namespace-3570.test.ts: each gap fails
on the prior head and passes here, plus a round-trip table asserting that
CLI store, MCP store and purge accept exactly the namespaces export
accepts.
Co-Authored-By: drakeo338 <drakeo338@users.noreply.github.com>
Co-Authored-By: RuFlo <ruv@ruv.net>
#3575: `session import` passed a parsed `data` object to session_import,
which required `inputPath`, so it returned { error }; the CLI then printed
"Session imported" and crashed reading result.stats. The CLI now passes
the path, session_import also accepts an inline `data` record, and every
session subcommand checks the tool's { error } before printing success.
`session export` with no id now refuses (like delete) unless --latest is
given, and import/export help names the required positional argument.
#3573: session_save read only the pre-SQLite .claude-flow/memory/store.json,
which current installs never write, so --include-memory (default true)
saved nothing and reported "Memory Entries: 0". It now snapshots the live
memory DB plus rows found only in the sibling AgentDB store (deduped by
namespace+key), restore writes them back and reports the count actually
written, and the table says "not included" instead of an invented 0.
Co-Authored-By: RuFlo <ruv@ruv.net>
suggestAgentsForTask() ended with a hard-coded { confidence: 0.7 } reached
only when no keyword or learned pattern matched. Blank, punctuation-only and
non-English tasks therefore routed to `coder` at 70%, above a correctly
routed "deploy the release to production" (62%).
The no-match path now returns NO_MATCH_CONFIDENCE (0.3, below every real
signal: keyword hits >= 0.8, learned >= 0.7, eligible semantic > 0.4, and the
same value the helper router already uses) with matched: false,
reason: 'no-match-default' and a note saying why (no words, or the keyword
matchers are English-only). Tasks with no letters or digits skip semantic
scoring. hooks_route, hooks_explain and hooks_pre-task surface the marker;
the CLI labels the box "Default Suggestion (nothing matched)", reports
success probability as unknown, and refuses a whitespace-only --task like an
empty one.
Co-Authored-By: RuFlo <ruv@ruv.net>
`memory search` and `memory retrieve` disclosed the sibling AgentDB store
(.swarm/agentdb-memory.db, written by the MCP path) only in their
empty-result branches, so a partial hit ("Found 1 results") read as
complete while the other store's rows went unread. The disclosure now
runs on every search path (semantic, hybrid, keyword-only, text and
JSON) and on a found retrieve, worded as "Partial result: N matches
came from the store read here". Warnings go to stderr, so --format json
and --value-only stdout stay parseable.
Also: `memory import` reported "Vectors: 0" while every value was
re-embedded. The memory_import tool now counts the vectors storeEntry
actually wrote.
Fixes#3566
Co-Authored-By: RuFlo <ruv@ruv.net>
The CLI store path and memory_import wrote a namespace containing ../,
which memory export and purge reject, leaving entries they cannot read or
clean up. Validate with validateIdentifier before any write; import checks
every entry first so a bad file persists nothing.
Three defects kept a hive task pending with its worker idle forever:
1. Nothing consumed the queue. `hive-mind task` only wrote a pending row;
workers are records, not processes. New `hive-mind work` claims the
oldest pending hive task, assigns it to an idle hive worker, runs it
through `claude -p --output-format json`, and records the outcome via
task_complete (or task_update status=failed).
2. task_assign/task_complete/task_cancel synced only
`.claude-flow/agents/store.json`, but hive workers live in
`.claude-flow/agents.json` (#1916). Assignment never marked a hive
worker busy and completion never counted it. They now update
whichever store holds the agent. task_update gains `result` and
frees workers on `failed`.
3. `spawn --claude` fell back to passing `~/.claude.json` as
--mcp-config. Where that file has no top-level `mcpServers` (a fresh
desktop), Claude exits with "Invalid MCP configuration: mcpServers:
Invalid input". The worker config is now always generated: servers
are extracted from the source config, a `ruflo` server pinned to the
running CLI is guaranteed, and CLAUDE_FLOW_CWD points it at the
dispatcher's stores.
Also fixes the "Track with: hive-mind task-status" hint, which named a
subcommand that does not exist.
This reverts commit 5a45b76c8. The fast-path broke trajectory-recovery-3199's
own regression test ("reloads a clean cache after another process adds a
step"): with vi.resetModules() simulating separate processes, a first
process's clean-but-now-stale in-memory copy would win over the freshly
persisted cross-process step, since the fast path never re-read disk when
the local cache was merely clean rather than empty. That's the exact
scenario #3467 was written to fix — the "cost" the fast path removed was
the correctness guarantee, not just overhead. Local runs of the
unmodified #3467 code average 20-46ms on graph-trajectory-hooks-smoke's
200ms budget; the one observed 1171ms CI failure is far more likely
explained by that round's runner queue (this same PR's other jobs sat
queued 40+ minutes on ordinary infra load) than a real regression.
#3467 made loadPendingTrajectory() always do two DB reads (completed +
pending) before returning, even when the trajectory is already tracked
cleanly in-process — turning every trajectory-step/post-task call into
a synchronous round-trip instead of a Map lookup. CI's
graph-trajectory-hooks-smoke (ADR-130 P3, <200ms budget) failed once on
a loaded runner at 1171ms; local runs stayed at 20-46ms, so this was
likely a transient load spike, but the always-hit-disk path was real
overhead regardless. Skip both reads when the id is already a clean
(non-dirty) in-memory hit — the case #3467 didn't change (same-process,
not-yet-restarted). The disk-recovery path #3467 added still runs
exactly when it's needed: on a cache miss (e.g. after an MCP restart).
- plugins/ruflo-adr smoke step 20: #3432 correctly split the memory-db
cwd into a more precise DB_ROOT (nearest .git/.swarm ancestor of ROOT),
but the smoke check still only recognized the literal `cwd: ROOT`
string. Accept `cwd: DB_ROOT` too.
- plugins/ruflo-core smoke step 6: #3428 replaced the old "pinned to
v3.6" CLI compatibility line with an accurate description of the new
resolve-installed-before-npx-fallback contract. Updated the smoke
check to verify that contract's env vars are documented instead of a
version pin that no longer exists.
- session-tools.ts: CodeQL flagged Math.random()-derived session IDs as
insecure randomness in a security context, newly reachable through
#3486's activate path. Switched both session ID generators to
randomUUID(), matching the convention already used in mcp-server.ts
and autopilot-state.ts.