Commit Graph
41 Commits
Author SHA1 Message Date
randRuFlo 733596cc1f feat(x-gateway): read-only GitHub/npm query tools on /mcp (ADR-485)
Adds ruv_github_search, ruv_github_repo, ruv_github_file and ruv_registry_latest
to the legacy /mcp surface: allowlisted owner (default ruvnet), public repos only,
SSRF-pinned URLs, redirects refused, size/time caps, TTL+ETag cache, per-client
and shared upstream budgets, results fenced as untrusted GitHub data. Directory
profiles (/chatgpt/mcp, /claude/mcp) are unchanged at twelve tools.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-09 14:28:35 -04:00
ruv c4116f2377 ruflo-console 0.33.2: Room and Mods UX — clickable Mods detail, blocked filter, jump from feed entries
Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-04 21:17:55 -04:00
rUv 668f99dc33 fix: address Ruflo mod and console beta feedback (#3698) 2026-10-04 13:25:23 -07:00
ruv d9a6258895 chore: bump ruflo-console 0.28.0, ruflo-goals 0.3.0, ruflo-mods 0.1.1; baseline research.test.ts
Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-04 00:28:33 -04:00
ruv 1156cb6b82 feat(ruflo-console): 0.25.0 — a compact nav: groups, one group's pages and a search, ADR-424
Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-03 14:42:14 -04:00
ruv 9716a2e483 feat(ruflo-console): 0.24.0 — Start here steps on pages with an order, ADR-423
Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-03 11:42:25 -04:00
ruv 2d39675494 Merge branch 'feat/console-watch' into feat/console-learning
# Conflicts:
#	plugins/ruflo-console/.claude-plugin/plugin.json
#	plugins/ruflo-console/hooks/bindings.ts
#	plugins/ruflo-console/hooks/views/common.ts
#	plugins/ruflo-console/scripts/smoke.sh
#	scripts/ci-test-baseline.txt
2026-10-03 02:09:29 -04:00
ruv 5346b8df27 Merge branch 'feat/console-optimizer' into feat/console-learning
# Conflicts:
#	plugins/ruflo-console/.claude-plugin/plugin.json
#	plugins/ruflo-console/hooks/bindings.ts
#	plugins/ruflo-console/hooks/views/common.ts
#	plugins/ruflo-console/scripts/smoke.sh
#	v3/docs/adr/ADR-408-ruflo-console-mission-control.md
2026-10-03 02:08:19 -04:00
ruv 881067bc01 feat(ruflo-console): 0.16.0 — Learning page: animated pulse, folded settings and configuration, self-learning actions (pretrain, consolidate, pattern store)
Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-03 02:07:52 -04:00
ruv 20d280148b feat(ruflo-console): 0.18.0 — Timeline look-back and who was busy; Events kind chips, find, pause, paging and detail
Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-03 02:02:56 -04:00
ruv ba8a84212c feat(ruflo-console): 0.17.0 — ruflo Optimizer: findings with confirm-gated fixes, scope, before/after, on the Overview
Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-03 01:36:29 -04:00
ruv b3d8e88cb2 feat(ruflo-console): 0.15.0 — Loop Manager: 16 presets from practical to exotic, a configurator, a launcher into the Claude UI
Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-03 01:06:19 -04:00
ruv 589ccadc19 feat(ruflo-console): 0.14.1 — confirms and answers open under the element that was clicked, on every page; clearer confirm indicator
Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-03 00:50:10 -04:00
ruv bc578a5fdd feat(ruflo-console): 0.13.0 — Ask Claude on every section, per-view regression matrix, no-spend live smoke
Every section's footer gets an Ask Claude button (the view's text as quoted data, secrets scrubbed, asks first, fills the box mid-turn, /btw aside, fitting plugin command); /ruflo ask <question>. tests/matrix.test.ts mounts all 27 views at two widths and asks through the bridge; scripts/e2e-smoke.sh opens every view in a real Claude Code with no AI turn. ADR-408 s10.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-02 23:53:22 -04:00
ruv 4aa8e5ca43 feat(ruflo-console): 0.11.0 — Mission Control: lifecycle planner, claude -p guidance, inline confirms, menu strip, hotkeys for every view
A goal becomes a plan over one lifecycle (Research, Create with ADRs and SOP, Build, Test, Validate, Secure, Benchmark, Learn), then a governed mission and ruflo tasks that the primary Claude session carries out one at a time. After a goal is entered, claude -p (plan mode, read-only, budgeted; asks first unless always accept) writes guidance by stage with the ruflo capabilities to bring in. Confirms sit under the field that raised them, fields can be edited again, Mission Control leads the main menu, the band shows the active mission, Missions is key 1 and every view has a hotkey. ADR-408.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-02 23:02:46 -04:00
ruv bf1b5550ed feat(ruflo-console): 0.10.0 — Settings with search, AI terminal (claude default, auto-ask, always accept, scrollbar), remembered actions, NAV styles, Hive ACT menu, collapsible sections
Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-02 22:18:56 -04:00
ruv ed47d37556 feat(ruflo-console): 0.9.0 — Plugin Catalog (every ruflo plugin, mod and skill), button sweep, tour GIF
Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-02 20:46:14 -04:00
ruv eebd30db3b Merge branch 'feat/console-integrate' into feat/console-starts
# Conflicts:
#	plugins/ruflo-console/hooks/bindings.ts
#	plugins/ruflo-console/hooks/palette.ts
#	plugins/ruflo-console/hooks/runner.ts
#	plugins/ruflo-console/hooks/state.ts
#	plugins/ruflo-console/hooks/views/common.ts
#	plugins/ruflo-console/hooks/views/pane.ts
#	plugins/ruflo-console/tests/views.test.ts
2026-10-02 18:21:57 -04:00
ruv 089d2d6fba Merge branch 'feat/console-evolve' into feat/console-starts
# Conflicts:
#	plugins/ruflo-console/hooks/actions.ts
#	plugins/ruflo-console/hooks/bindings.ts
#	plugins/ruflo-console/hooks/palette.ts
#	plugins/ruflo-console/hooks/runner.ts
#	plugins/ruflo-console/hooks/state.ts
#	plugins/ruflo-console/hooks/views/common.ts
#	plugins/ruflo-console/hooks/views/pane.ts
#	plugins/ruflo-console/tests/views.test.ts
2026-10-02 18:15:09 -04:00
ruv 3e2b2def51 Merge branch 'feat/console-vector' into feat/console-starts
# Conflicts:
#	plugins/ruflo-console/hooks/actions.ts
#	plugins/ruflo-console/hooks/bindings.ts
#	plugins/ruflo-console/hooks/palette.ts
#	plugins/ruflo-console/hooks/runner.ts
#	plugins/ruflo-console/hooks/state.ts
#	plugins/ruflo-console/hooks/views/common.ts
#	plugins/ruflo-console/hooks/views/pane.ts
#	plugins/ruflo-console/tests/views.test.ts
2026-10-02 18:13:17 -04:00
ruv 1251fc3ffc Merge branch 'feat/console-skillsplus' into feat/console-starts
# Conflicts:
#	plugins/ruflo-console/hooks/bindings.ts
#	plugins/ruflo-console/hooks/dispatch.ts
#	plugins/ruflo-console/hooks/palette.ts
#	plugins/ruflo-console/hooks/state.ts
#	plugins/ruflo-console/hooks/views/common.ts
#	plugins/ruflo-console/tests/views.test.ts
2026-10-02 18:07:52 -04:00
ruv 4394677afc Merge branch 'feat/console-memory' into feat/console-starts
# Conflicts:
#	plugins/ruflo-console/hooks/actions.ts
#	plugins/ruflo-console/hooks/palette.ts
#	plugins/ruflo-console/hooks/runner.ts
#	plugins/ruflo-console/hooks/state.ts
2026-10-02 18:03:48 -04:00
ruv 49452e2eb1 feat(ruflo-console): the x.ruv.io board runs every federation capability itself
The board was a menu that typed `ruflo federation …` into the terminal. Each
capability is now a row with its own button or field, run as one fixed argv
through `mcp exec -t x_federation_*`:

- identity: whether ~/.ruflo/nostr.key exists (never read), the pubkey a
  result names, join with an optional invite code (masked on the confirm row
  and in answers), the agentbbs node identity; unregister is shown as not
  offered, since the gateway has no leave endpoint
- live: registry, roster, work-claims board and the last 20 swarm messages,
  kept live with federationNetwork, else fetched once per click
- channels: list, read, publish as yourself, create, grant, accept
- admin (gateway identity): hub publish and admit ask first and need
  RUFLO_X_ADMIN_TOKEN, of which only "is it set" is kept; invites are minted
  in the terminal, never by the board, since the code is a bearer secret
- a result panel that j/k scroll, and every action as an x-* palette entry,
  so `/ruflo run x-roster` works headless

Reads run at once; joins, publishes, grants, creates and accepts ask first and
say what they send. Channel reads ask until a Nostr key exists, because the CLI
makes one on first use. Claims and sync are two new network probes gated by the
same option.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-02 17:49:44 -04:00
ruv b84cd3b777 feat(ruflo-console): auto-open only in a ruflo project, remember a person's close, every action argv tested
- autoOpen is gated on a ruflo project; Esc or the pane's mark persists
  isClosedByPerson so a dismissed cockpit stays shut next session
- tests/actions.test.ts: each confirm-gated palette action asks, runs nothing
  until yes, then exactly its fixed argv (13 actions)
- radar axis words, handoff arrows name the target agent, gantt now marker
- READMEs: /ruflo swarm and /ruflo mods, old names deprecated for one release;
  ruflo-swarm panel now defaults to command

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-01 22:26:03 -04:00
ruv cbbabb8339 feat(ruflo-console): one /ruflo command, auto-start, diagrams, palette and management views
- /ruflo is the single command for every ruflo mod: views, palette, run/yes/no,
  agent drill-down, help; /ruflo mods is answered by ruflo-mods and /ruflo swarm
  <pane|status|topology|claims|consensus> by ruflo-swarm (both hook the same
  command; the old /ruflo-mods and /ruflo-swarm-* stay as deprecated aliases)
- auto-start: panel auto opens the cockpit at session start where it docks,
  never taking the keys; the band says /ruflo to open otherwise. ruflo-swarm's
  own pane now defaults to panel command so two panes do not open
- diagrams (Raster, one pure function per diagram shared by paint and blit):
  topology with event pulses and a leader heartbeat, claims lanes with TTL
  rings and handoff arrows, federation map by trust, plugin health matrix,
  learning curve draw-in and RETRIEVE→JUDGE→DISTILL→CONSOLIDATE pipeline,
  MetaHarness radar and audit trend, cost gauge and burn, agent timeline
- management: command palette (fuzzy, text entries), confirm-gated actions
  through fixed argv, agent drill-down with logs, approvals queue (votes,
  stealable claims, refused mods, policy denies, budget), event stream with
  filter, health alerts with the fix named
- tests: 28 kit tests, 28 vitest specs; 100-agent frame 0.25 ms median

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-01 22:15:40 -04:00
ruv 6eb25a823c feat(ruflo-console): tests, smoke, bench, marketplace entry, CI baseline
Kit tests (19) for every view, hotkeys, confirm-gated claims, refusal,
empty project, narrow and desktop fallbacks, the frame loop stopping when
hidden or unfocused, hostile text and the band. Pure vitest specs (18)
for readers, CLI JSON, cell graphics and $.ruflo type parity.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-01 21:34:10 -04:00
ruv d3a3979ae6 Merge origin/main into feat/ruflo-ruos
Resolves .claude-plugin/marketplace.json and scripts/ci-test-baseline.txt
after #3608/#3607 merged: both sides appended entries, so the resolution
keeps both (ruflo-mods + ruflo-ruos marketplace entries; all baseline blocks).

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-01 19:29:44 -04:00
rUv fbaeca0695 Merge pull request #3607 from ruvnet/feat/claude-code-mods-swarm-ui
feat(ruflo-swarm): live swarm pane mod for Claude Code (function hooks)
2026-10-01 19:28:45 -04:00
ruv c957086f3b feat(ruflo-ruos): mod part — ruos status segment via $.ruflo.segment
ruflo-mods landed the flat segment API (#3608, ADR-404). The ruflo-ruos mod
reads only .claude-flow/ruos/hosts.json and sets $.ruflo.segment({id:'ruos',
text}) on session start, each prompt and every 15 s; try/catch, no feature
detection, no network, no $.ruos dependency. Vendored $.ruflo type with a
parity test; claude plugin validate + 3 claude plugin tests pass; smoke runs
them when the claude CLI is present. CI baseline lists the two new test files.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-01 18:13:29 -04:00
ruv 707977d2bc feat(plugin-creator): scaffold governed Claude Code mods (create-mod + templates/mod, ADR-404)
A working template mod, validated and kit-tested on Claude Code 2.1.287 and
live-loaded with --plugin-dir: hybrid hooks.json (classic UserPromptSubmit
fallback that exits while the module runs, via MY_MOD_ACTIVE), a host
adapter over literal $ calls, refusal-tolerant status drawing, a namespaced
command, userConfig, engine-kit tests and a tsconfig extending the
generated types. create-mod skill carries the rules (tighten-only
tool.check, no process/http from event input, state in $.store/$.state,
namespaced commands) and the validate/test/typecheck loop.

ruflo-plugin-creator 0.2.1 -> 0.3.0 (smoke sentinel + step 11).

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-01 18:13:02 -04:00
ruv 8a1e11f3bd feat(mods): $.ruflo noun, status segments, mod trust gate, refusal-tolerant $ (ADR-404)
- engine.create adds a flat $.ruflo noun (types/index.d.ts, plugin.json
  "types"): segment({id,text}), lastRoute(), snapshot(). Other mods (ruOS,
  ruflo-ruos) contribute status segments instead of drawing a second bar.
  Segments are untrusted: id token, control/bidi stripped, 48 chars, max 8,
  sorted by id. One drawer for the status line, from the $ beneath.
- plugin.register trust gate: names what a later-registering user-tier mod
  can do (process.run, http.fetch, env.set; tool.check, tool.call, *,
  classic.*, plugin.register, prompt.compose), from the host scan.
  userConfig modTrust: observe (default) | refuse-risky | off, plus
  modTrustAllow. Under refuse-risky a gate failure refuses.
- every ui.status/log/toast call tolerates refusal (admins can withhold $)
- tsconfig extends the types Claude Code >= 2.1.287 writes on load
- kit tests: 15/15 on Claude Code 2.1.287, incl. two tighten-only tool.check
  mods composing, trust refusal at load, budget ladder with options
- vitest: 95/95 (noun/segment and trust suites added)
- CI ratchet baseline: the four engine-kit files (claude-code/testing is not
  resolvable by root vitest), as #3605/#3607 do

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-01 18:09:25 -04:00
ruv e1bf696d1a fix(ruflo-ruos): echo-proof nonce markers, CI ratchet entries, ISO heartbeat
- every remote command gets a fresh 64-bit nonce; markers print as
  RUOS<nonce>_NAME via printf 'RUOS%s_NAME' '<nonce>', so the token never
  appears in the command or its desktop_exec echo; parsers accept only
  whole lines with that command's nonce (test replays raw echoes that
  contain every marker name)
- test that poll command + 2 KiB slice fit the ~4 KiB cap, echo counted
- pid file holds digits only; audit hash masks the nonce (stable)
- scripts/ci-test-baseline.txt: the 6 node:test files (run by smoke.sh)
  that root vitest reports as 'No test suite found' (lead-authorized)
- hosts.json heartbeatAt is ISO for the swarm pane (#3607)

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-01 18:02:46 -04:00
ruv db6f422163 ci: list ruflo-swarm engine-kit tests in the root vitest baseline
They import 'claude-code/testing', which exists only under
`claude plugin test plugins/ruflo-swarm`; root vitest cannot resolve it
(same pattern as ruflo-x-gateway). The pure readers still run under root
vitest via tests/parse.spec.ts.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-01 17:59:42 -04:00
ruv 2dccf29085 ci: baseline x-gateway claude-directory node:test file (main Test Suite red since #3534)
#3534 added plugins/ruflo-x-gateway/test/claude-directory.test.mjs, a
node:test file. Root vitest finds no suite in it and the ratchet fails on
main and on every PR since. Same treatment as its three sibling files; the
plugin's own `npm test` passes 84/84.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-09-29 09:51:23 -04:00
ruv af6d49a3bd release 3.48.0: re-sign helper manifest, re-anchor witness, baseline x-gateway registration test
- helpers.manifest.json re-signed for 3.48.0
- witness #1825 marker re-anchored to `"@claude-flow/memory": "3.` (the
  stable pin removed the `3.0.0-alpha.` string it quoted); all three OS
  manifests regenerated on a clean, fully built tree: 117/117 each
- plugins/ruflo-x-gateway/test/registration.test.mjs joins its siblings in the
  ratchet baseline: root vitest cannot resolve the plugin's own deps
  (nostr-tools). Its own `npm test` passes 74/74. This was the only CI failure
  on #3510.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-09-28 17:20:09 -04:00
ruv 0d0ebbe305 fix(ci): add two new node:test-only ADR files to the vitest ratchet baseline
#3432 and #3437 add plugins/ruflo-adr/scripts/__tests__/import-root-exit-3097.test.mjs
and verify-read-safety-3147.test.mjs, both node:test-only (unlike
parser-relations-3096.test.mjs's dual-mode shim). Root vitest can't discover a
test suite in either, which the CI ratchet correctly flagged as 2 new
unexpected failing files. Same documented class as the plugin's four other
baselined test files (see the file's own comment on plugins/ruflo-x-gateway).
2026-09-27 13:40:58 -04:00
rUvandClaude e036e35dd7 dream(memory): #3231 wire embedding near-dup detection into MemoryConsolidator.dedup() (evaluated, ACCEPT) (#3232)
* dream(memory): wire embedding near-duplicate detection into MemoryConsolidator.dedup()

MemoryConsolidator.dedup() only ever deduplicated entries via byte-exact
SHA-256 content hashing, even though every entry already carries a
computed .embedding and the adapter's HNSWIndex (already held in scope
for removal bookkeeping) is incrementally kept in sync. Paraphrases and
reformattings of the same memory were never caught.

Adds a second pass: for hash-pass survivors with an embedding, query the
already-populated HNSWIndex for cosine-similarity neighbors above a
configurable similarityThreshold (default 0.95, matching the unwired
domain-layer consolidator's own constant), and merge using the same
keeper-selection strategies (now factored into shared selectKeeper/
mergeGroup helpers). Guarded to cosine-metric indexes only; disabled via
similarityThreshold >= 1.

Evaluation: 471/472 passing in @claude-flow/memory (1 pre-existing,
unrelated, environmental failure); baseline (git-stash-isolated source)
fails exactly the 2 new discriminating tests and passes the rest,
confirming the fix is additive and non-regressive. tsc --noEmit clean.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01H4kfasS4XkYaSGYXFLSSEG

* dream(memory): fix-forward — converge near-dup pass to a fixed point in one dedup() call

Independent adversarial critique (STEP 10) reproduced a real, bounded gap
in the prior commit: a near-duplicate cluster larger than
NEAR_DUP_SEARCH_K (8) split into multiple leftover sub-group survivors
within a single dedup() call, because each round's `consumed` bookkeeping
permanently excluded a group's keeper from further matching even though
it was still fully present in the index. 15 pairwise-identical-embedding
entries collapsed to 2 survivors (merged: 13) instead of 1 (merged: 14).

It self-healed across repeated runAll() calls (background timer /
nightlyLearner), so this was never a permanent-data-loss bug, but a
single manual dedup() call under-converged relative to its documented
"collapse duplicates" contract.

Fix: loop pass 2 to a fixed point (re-scan until a full round produces
zero merges) instead of a single scan. Always terminates — entries
strictly decrease each round that merges anything. `groups` now counts
merge operations across all rounds, which can exceed the number of
underlying duplicate clusters when one needed more than one round;
documented in the method's doc comment.

Added a discriminating regression test reproducing the critic's exact
15-entry scenario; confirmed it fails against the pre-fix single-round
code (merged: 13) and passes against this fix (merged: 14, single
survivor). Full package suite: 472/473 passing (1 pre-existing,
unrelated, environmental failure, unchanged from before this commit).
tsc --noEmit clean.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01H4kfasS4XkYaSGYXFLSSEG

* dream(memory): 2026-09-08 research gist — embedding near-dup consolidation

Full SOTA report: 5-role parallel research fan-out, ledger check with
GitHub-verified fates for the last 14 rows (3 merged since 09-03, one PR
now crossing the 14-day stale threshold for the first time this cycle),
competitor comparison (CrewAI/Mem0/Zep/LangMem/Qdrant/SemDedup), plugins
and automation scan findings, adversarial critique with a real bug found
and fixed, and witness stamp.

No gist-creation MCP tool available in this session (GitHub MCP tools
cover issues/PRs/repos, not gists) — committing to docs/dream-cycle/
instead, matching every dream-cycle night since 2026-08-14.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01H4kfasS4XkYaSGYXFLSSEG

* dream(memory): #3231 update LEDGER.md for 2026-09-08

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01H4kfasS4XkYaSGYXFLSSEG

* dream(memory): #3231 empirical receipts for dedup() near-dup pass (response to review)

Response to PR #3232's INCONCLUSIVE review, which asked for a threshold
contract rather than a smoke fixture: a contamination-checked embedding
corpus with pinned model, precision/recall including hard negatives and
cross-namespace cases, baseline vs. candidate latency/memory at realistic
cardinality, and deterministic replay + unchanged exact-dedup behavior.

Real pinned-model corpus: attempted directly against both ONNX backends
this monorepo ships. Both fail for reasons unrelated to this candidate,
reproduced on an unmodified checkout before writing anything:
- @huggingface/transformers@4.2.0: transformers.node.mjs does
  `import { Tensor } from "onnxruntime-common"` against a CJS module — a
  real ESM/CJS interop break under this sandbox's Node version.
- @xenova/transformers@2.17.0: pulls in sharp@0.32.6, whose native
  binding (sharp-linux-x64.node) isn't present for this platform — the
  same failure independently reproduced moments later by this package's
  own nightlyLearner test, which falls back to mock embeddings.
Neither is fixable within this PR's scope without touching unrelated,
pre-existing native/module-resolution infrastructure. Documented in
detail at the top of the new test file rather than silently worked
around or omitted.

What's delivered instead, in
v3/@claude-flow/memory/src/consolidator-embedding-benchmark.test.ts:
- A deterministic synthetic corpus where pairwise cosine similarity is
  constructed EXACTLY via vectorAtSimilarity() (Gram-Schmidt against a
  random orthogonal vector), not measured after the fact — a rigorous
  instrument for threshold mechanics specifically, disclosed as not
  validating real-world semantic accuracy.
- Precision/recall/false-merge-rate across a 5-point threshold sweep,
  every point at a deliberate margin from every corpus similarity value
  after an exact-boundary collision (sweep value == corpus value) proved
  float32-jitter-sensitive while writing this — documented as a finding,
  not silently patched around.
- Namespace isolation: proved cross-namespace near-dup merging matches
  cross-namespace hash-exact merging exactly (both pre-existing,
  unscoped-by-namespace behavior — this candidate doesn't change it).
- Determinism: 3 independent runs, compared by deterministic entry KEY
  (not raw id, which is randomly generated per store() and was a second
  bug this file's own first draft had to fix).
- Latency/memory at N=5000 (matching this repo's own HNSW-benchmark
  convention): initially measured 9.8s for the candidate pass, which
  traced to an unset `ef` search parameter defaulting to efConstruction
  (200) — every dedup() search traversed a 200-candidate list for a
  duplicate-detection task that only needs very-close neighbors. Fixed
  by passing an explicit NEAR_DUP_SEARCH_EF=32 in consolidator.ts,
  re-measured at 3.7s (~2.7x), precision/recall unchanged (still exact).
  Real numbers logged in the test output either way, not asserted away.

Full package suite: 478/479 passing (472 pre-existing + 7 new; same 1
pre-existing unrelated environmental failure as every prior commit on
this branch). tsc --noEmit clean.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01H4kfasS4XkYaSGYXFLSSEG

* dream(memory): #3231 add new benchmark file to CI test-ratchet baseline

CI on a2e99cf failed with "1 unexpected failing file(s): +
v3/@claude-flow/memory/src/consolidator-embedding-benchmark.test.ts".

Root-caused via the vitest JSON report artifact (test-results-ubuntu-latest,
downloaded and parsed directly, not guessed): a suite-level collection
failure, "Cannot find package '@claude-flow/security' imported from
'.../v3/@claude-flow/memory/src/agentdb-retrieval-guard.ts'" — the same
unbuilt-sibling-package gap already accepted in scripts/ci-test-baseline.txt
for 9 other @claude-flow/memory test files, including consolidator.test.ts
itself (line 95). Every file in this package fails identically in this CI
checkout state; my new file just wasn't in the baseline yet because it's
new. Not a regression this PR introduced — confirmed by the failure being
a pre-import-time module-resolution error, not a test assertion in the
new file's own logic (which passes locally in a checkout with @claude-flow/
memory's dependencies actually built: see the 6/6 local run in the prior
commit's message).

Fix: add the new file to the baseline, alongside its 9 memory-package
siblings already there, matching this repo's own established convention
for this exact gap rather than working around it some other way.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01H4kfasS4XkYaSGYXFLSSEG

* dream(memory): #3231 baseline 2 more federation/gateway plugin test files

CI on 20afdc8 failed (run 34905889339) after main was rebased in — 3
unexpected failing files, all pre-existing, unrelated to this PR's diff:

  plugins/ruflo-chatgpt-federation/test/oauth.test.mjs
    Cannot find package 'jose'
  plugins/ruflo-chatgpt-federation/test/publisher.test.mjs
    Cannot find package 'nostr-tools/pure'
  plugins/ruflo-x-gateway/test/oauth.test.mjs
    Cannot find package 'jose'

Verified via the vitest.json artifact (downloaded and parsed, not
guessed): all three are plugin-local-dependency collection failures,
identical in kind to the already-baselined sibling
plugins/ruflo-x-gateway/test/gateway.test.mjs ("node:test suite with its
own deps... root vitest cannot resolve them"). Confirmed these are new
on main itself (main's own ci-test-baseline.txt at the rebase point does
not list them either) and unrelated to v3/@claude-flow/memory — nothing
this PR touches.

Re-verified locally: re-ran the full @claude-flow/memory suite against
the rebased main (496/497 passing, same 1 pre-existing unrelated
failure as always) and recomputed the ratchet's unexpected-set directly
against the CI run's vitest.json artifact — 0 unexpected after this
change.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01H4kfasS4XkYaSGYXFLSSEG

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-14 23:13:14 +00:00
rUvandClaude 1bd734e272 dream(security): #3102 bind ADR-377 caller-identity verification into live authorizeMcpTool chokepoint (evaluated, ACCEPT-with-caveats) (#3103)
* dream(security): #TBD bind ADR-377 caller-identity verification into live authorizeMcpTool chokepoint (evaluated, ACCEPT-with-caveats)

authorizeMcpTool() trusted the plain, unsigned CLAUDE_FLOW_PRINCIPAL_ID env
var as caller identity with zero verification (OWASP ASI07-class gap).
ADR-377 Phase 3 already implemented Ed25519 issueInvocationToken/
verifyInvocationToken but never wired it into a live dispatch path.

Binds the two: DualModeOrchestrator mints a per-worker signed
InvocationToken at spawn (worker-lifetime TTL, wildcard tool scope --
disclosed reduction from the primitive's original per-call design);
authorizeMcpTool verifies it before trusting identity, failing closed on
missing/forged/expired tokens. Off by default (CLAUDE_FLOW_MCP_CALLER_AUTH).

6 new deterministic Vitest scenarios plus an independent adversarial
critique (fresh session, verdict CONFIRMED-SAFE-WITH-CAVEATS) are recorded
in docs/dream-cycle/dream-gist-2026-08-26.md. Darwin skipped (scope
mismatch -- binary crypto gate, no tunable parameter space).

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_015bfVASxkNXKpZbMSLhVU1L

* fix(ci): register ADR-377 Phase 3 caller-identity env vars as known escape hatches

CLAUDE_FLOW_MCP_CALLER_PUBKEY and CLAUDE_FLOW_MCP_INVOCATION_TOKEN
(DualModeOrchestrator's per-worker Ed25519 credential pair, read by
resolveMcpCallerIdentity()) weren't registered in
audit-env-var-precedence.mjs's KNOWN_ESCAPE_HATCHES, so the audit flagged
CLAUDE_FLOW_MCP_CALLER_PUBKEY as a real violation (exit 1). Same no-CLI-flag
reasoning as the existing CLAUDE_FLOW_PRINCIPAL_ID entry: these are
credentials minted into a spawned worker's own environment, not a value
any caller should be able to select via a flag.

Verified: audit-env-var-precedence.mjs now exits 0. policy-runtime.test.ts
still 18/18.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01RYP825phHMjPRmmjkcAWpq

* fix(ci): regenerate root package-lock.json for codex's new @claude-flow/security dep

The CI Test Suite job runs plain `npm ci` at repo root (no v3 pnpm
install step), so it resolves workspace deps strictly from
package-lock.json — not from v3/pnpm-lock.yaml, which this PR had
already updated. codex/package.json's new @claude-flow/security
dependency was missing from package-lock.json's codex entry, so CI's
npm ci never linked it and every dual-mode test importing
orchestrator.ts failed with "Cannot find package '@claude-flow/security'".

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01RYP825phHMjPRmmjkcAWpq

* fix(ci): build @claude-flow/security before Test Suite runs it

Complements d492ea835 (codex's package-lock.json entry) rather than
duplicating it: that commit fixed `npm ci` not *linking*
@claude-flow/security for codex at all ("Cannot find package"); this
fixes the fact that even where it IS linked (codex now, @claude-flow/cli
already), nothing builds its dist/ before tests run, so importers hit
ERR_MODULE_NOT_FOUND on dist/index.js instead.

This was already a known, pre-existing gap for @claude-flow/cli:
v3/@claude-flow/cli/__tests__/policy-runtime.test.ts has carried a
scripts/ci-test-baseline.txt entry for exactly this reason since before
this PR existed. dual-mode.test.ts and dual-mode-stdin-2947.test.ts
(added by the caller-identity candidate on this branch) newly trip the
same gap in @claude-flow/codex, confirmed by diffing main's own Test
Suite ratchet failures (3, pre-existing, unrelated oauth/publisher
plugin tests) against this branch's (5: the same 3 plus these 2). Adding
new files to ci-test-baseline.txt to paper over this is explicitly
forbidden by that file's own header ("Never add entries to make a
regression green"), and gutting the tests to avoid the import would
remove real coverage of the orchestrator/security integration point.

Real fix: build @claude-flow/security before "Run all tests", using its
own existing `npm run build` (tsc && verify-oauth-exports.mjs). Verified
locally end-to-end by removing both dist/ and the stale
tsconfig.tsbuildinfo (simulating a fresh CI checkout, since both are
gitignored) -- the build step produces a full dist/, both new codex test
files pass, and (bonus, explicitly allowed by the baseline file's own
"remove entries when fixed" rule) the pre-existing policy-runtime.test.ts
baseline entry is now consistently green too, so it's removed here.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_015bfVASxkNXKpZbMSLhVU1L

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-14 21:05:45 +00:00
rUv 8008bc3562 fix(federation): verified event identity must win over publisher-controlled content (#3322)
* fix(federation): verified event identity must win over publisher-controlled content

fetchRecent/fetchManyOn/fetchChannel spread the parsed message content
after the signature-verified id/pubkey/created_at, so a publisher could
put those keys in their own content and overwrite their verified
identity downstream (reduceClaims trusts .pubkey for claim
release/handoff authorization). Reorder so verified fields always win.

Also in x-federation-channels.ts: reqEvents() never called verifyEvent()
at all, so channel-read/grant-accept events were trusted unsigned; add
the check. Same content-override reorder in x_federation_channel_read.

fix(hooks): escape argv for the Windows shell:true npm-shim path

ruflo-hook.cjs's invokeHook() passes hook-derived values (command text,
file paths) through cmd.exe via shell:true to resolve npm's .cmd/.ps1
shims. Node does no escaping in that mode, so a value containing a
cmd.exe metacharacter could be reinterpreted as a separate command or
redirection instead of reaching the CLI as data. Add escapeCmdArg()
(qntm.org/cmd algorithm, same one cross-spawn uses) and apply it to
every argv element on that path.

No native Windows/cmd.exe available to execute this end-to-end in this
environment — verified at the string-transform level only (8 unit
tests). Real Windows execution remains a required follow-up gate.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01RYP825phHMjPRmmjkcAWpq

* fix(ci): register new hook test env var and test file with CI guards

Follow-up to the previous commit's escape-cmd-arg.test.cjs:
- Register RUFLO_HOOK_UNIT_TEST in audit-env-var-precedence.mjs's
  KNOWN_ESCAPE_HATCHES, matching the existing RUFLO_HOOK_CLI_OVERRIDE/
  RUFLO_HOOK_DEBUG_STDOUT entries for the same file.
- Register plugins/ruflo-core/scripts/escape-cmd-arg.test.cjs in
  ci-test-baseline.txt, matching the sibling mcp-launch.test.cjs entry —
  both are node:test-based .cjs files that vitest's own JSON reporter
  marks "failed" (it doesn't recognize the node:test API) even though
  every test inside genuinely passes under `node --test`.

Verified locally: node scripts/audit-env-var-precedence.mjs now passes
(exit 0). A scoped `vitest run` + `ci-test-ratchet.mjs --report` against
just these two files confirms escape-cmd-arg.test.cjs is no longer an
unexpected failure (the one remaining flagged file is a stale local
worktree artifact under .claude/worktrees/, not part of a clean CI
checkout).

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01RYP825phHMjPRmmjkcAWpq
2026-09-14 17:12:05 +00:00
rUv 6396ff636c feat(x-gateway): open Nostr swarm federation gateway — MCP + ruv:// + ws proxy (deployed at x.ruv.io) (#3255)
* feat(x-gateway): MCP + ruv:// gateway for open Nostr swarm federation

New plugin `ruflo-x-gateway` — the service behind x.ruv.io. An MCP server
(Streamable HTTP at /mcp) that exposes ruflo swarm FEDERATION and CLAIMS over
an open, membership-gated, SIGNED Nostr relay (buzz-relay).

Why Nostr: every coordination message is a signed Nostr event, so authorship is
cryptographically verifiable and anyone the relay admits can participate — open
but secure. The relay gates membership + NIP-42 auth; no pre-pinning needed.

Surface:
- Tools: federation_identity / federation_join / federation_publish /
  federation_sync, claims_issue / claims_release / claims_status.
- Resources: ruv://federation/registry, ruv://swarm/roster, ruv://claims/board.
- src/nostr-federation.mjs: NIP-42 authenticated connect, signed publish, and
  verified fetch of #t=ruflo-swarm coordination events.
- src/server.mjs: node http server routing /, /health, /mcp (stateless
  Streamable HTTP), plus the ruv:// resources.
- Dockerfile for Cloud Run; persistent Nostr identity at /data (0600).

Smoke-tested locally: server starts, /health + / respond, POST /mcp tools/list
returns the tool set over SSE. Federation tools require relay membership (by
design) — deployment wires membership + DNS (x.ruv.io) as follow-up.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67

* feat(x-gateway): stable identity via RUFLO_NOSTR_KEY_HEX (GCP secret) + read-only-FS fallback

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67

* feat(x-gateway): v0.2.0 — ws proxy, admin-gated writes, invite/admit tools, tests

Security model made explicit: /mcp is public, so every tool that writes
using the GATEWAY's own identity (join, publish, claims_issue/release,
invite_mint, admit) now requires `adminToken`, checked constant-time and
fail-closed (no configured token => all writes denied). Reads and ruv://
resources stay open. Users publish with THEIR OWN keys via invite->claim.

- src/ws-proxy.mjs: transparent WebSocket proxy so wss://x.ruv.io (and
  /relay) fronts the Nostr relay; 500-conn cap, 502/503 on failure.
- src/relay-admin.mjs: mintInvite (NIP-98 POST /api/invites) and
  admitMember (NIP-43 kind 9030) — the gateway holds relay admin role, so
  the owner key never leaves GCP.
- src/security.mjs: per-IP token-bucket rate limit (60/min), 256KB body
  cap enforced before buffering, security headers, timingSafeEqual admin.
- src/claims.mjs: owner-per-resource reducer extracted for testing.
- src/server.mjs: createGateway() factory (testable), stateless MCP.
- test/gateway.test.mjs: 8 tests — claims rules, gating, rate limit, body
  cap, NIP-42 against a mock relay (verifies the signed challenge), routes.

Verified live before this commit: gateway pubkey admitted + promoted to
relay admin; invite minted and a fresh key self-joined via claim and
passed NIP-42 auth; deployed gateway publishes/reads over the relay.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67

* fix(x-gateway): surface canonicalRelay — relay verifies NIP-42 relay tag strictly

Empirical: auth through the wss://x.ruv.io proxy is ACCEPTED when the client
signs relay=<canonical relay URL> and REJECTED (verification failed) when it
signs relay=wss://x.ruv.io. Expose canonicalRelay + authNote at GET / and in
ruv://federation/registry so clients sign the right tag.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67

* ci: retrigger test-suite (ratchet flake; main green at ac08e4e02)

* feat(x-gateway): v0.3.0 — Seraphina swarm-queen guidance tool (admin-gated, cognitum meta-llm)

seraphina_guidance reads the live roster/claims/recent from the relay, compacts
them (dedupe by from|type, cap 15), and asks https://api.cognitum.one/v1/messages
with cognitum-auto (tier override). Admin-gated because it spends meta-llm
budget. JSON extracted by slicing the outermost object so fenced answers parse.
Key from SERAPHINA_METALLM_KEY. +1 test (9/9).

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67

* ci(ratchet): baseline ruflo-x-gateway node:test file (own deps; runs via plugin npm test)

Root vitest sweeps plugins/**/test/*.test.mjs and cannot resolve the gateway's
deps (nostr-tools, ws, MCP SDK live only in the plugin's own node_modules), so
the ratchet flagged it on 3 consecutive runs — deterministic, not a flake.
Follow the existing convention (17 plugin tests already baselined, e.g.
plugins/ruflo-adr). The real runner is `npm test` in the plugin (9/9 pass).

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67

* fix(x-gateway): v0.3.1 — security hardening + relay-connection optimization

Security review findings (live battery all green; static gaps fixed):
- publish(): bound msgType ([A-Za-z0-9_-]{1,64}) and payload (<=32KB) before signing
- rate limiter: evict idle buckets (10m) and cap the map (10k) — unbounded IP churn
  could grow memory without bound
- ws proxy: maxPayload 256KB on both legs; bad path now answers HTTP 404 instead
  of a bare socket destroy (Cloud Run surfaced that as 503)
Optimization:
- fetchManyOn(): several REQs over ONE authenticated connection — Seraphina now
  does 1 NIP-42 handshake per call instead of 3
- 5s TTL cache on the roster/claims resources to absorb read bursts
+1 test (10/10): bounds, bounded buckets, proxy limits, single-handshake multi-REQ.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67

* feat(x-gateway): v0.3.2 — canonical relay wss://relay.ruv.io + legacyRelay metadata

relay.ruv.io is a Cloud Run domain mapping for buzz-relay (Cloudflare CNAME, unproxied so
WebSocket upgrades go straight to Cloud Run). The old run.app host remains routable and is
advertised as legacyRelay in / and ruv://federation/registry so pinned clients keep working.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67
2026-09-10 02:34:56 +00:00
ruv 13cbd697db fix(release): enforce CI and idle assignment safety 2026-08-15 11:14:06 -04:00