mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-09-14 14:01:28 +08:00
ac08e4e02f8bcc6f73ed384d04f5730b7619b643
7426 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ac08e4e02f |
docs(plugins,skills): surface v3.40.0 cross-host federation + claims (#3254)
* docs(plugins,skills): surface v3.40.0 cross-host federation + claims capabilities The agentbbs federation plugin and the claims skill still described only Phase-1 room coordination. Update them to the shipped 3.40.0 surface. - plugins/ruflo-bbs-federation/plugin.json: 0.1.0 -> 0.2.0. Document the Phase-2 cross-host tools (identity, peer_add, peers, serve, sync), Ed25519-signed envelopes, registry-anchored pinning, drop/count of unverified envelopes, network-agnostic HTTP-pull transport, and claim coordination. Keywords: drop phase-1-mvp; add cross-host, signed-envelopes, pinned-peers, registry-anchored-pinning, union-merge, claims. - plugins/ruflo-bbs-federation/skills/cross-host-federation/SKILL.md (new): the practical join/serve/publish/sync flow, the pull-not-push model, the JSON-stable-payload gotcha, work-claim messages + rules, and the security model (registry-anchored pinning, no secrets in payloads, content-is-data). - .agents/skills/claims/SKILL.md: add a Cross-Host Work Claims section — the claims_* runtime ledger tools plus the federated ClaimIssued/Released/ Handoff/Ack messages and ownership rules, distinct from the existing authorization claims. - marketplace.json: bbs-federation description updated to Phase 2. Docs only — no code or tool-signature changes. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67 * fix(plugins): add missing manifest for ruflo-deepseek-harness The Validate Marketplace workflow requires every plugins/*/ dir to carry .claude-plugin/plugin.json. ruflo-deepseek-harness has agents/commands/ scripts/skills but no manifest, so main's validate has been red since 2026-08-21 and every PR touching plugins inherits the failure. Pre-existing; surfaced here because this PR is the one touching plugin manifests. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67 |
||
|
|
2629de628b |
chore(release): 3.39.3 -> 3.40.0
Ship cross-host federation (agentbbs) + claims. New MCP tools:
federation_bbs_{identity,peer_add,peers,serve,register,publish,sync,watch}.
Signed Ed25519 envelopes, registry-anchored pinning, HTTP pull transport.
Includes the memory-search recall fix (#3252).
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67
v3.40.0
|
||
|
|
e111bfbd7f |
fix(memory,tests): restore memory search recall and green the CLI suite (69 → 0) (#3252)
* fix(memory,tests): restore `memory search` recall and green the CLI suite (69 → 0) The CLI suite had 69 failing tests across 50 files. Root-causing each rather than adjusting expectations turned up one real product regression, two real bugs, and a set of tests asserting things about the machine instead of the code. PRODUCT REGRESSION — `memory search` recalled nothing (re-break of #2558) `memory search` returned zero results for content that matched word for word, while store/list/retrieve worked. Bisected to the threshold default: * 2026-07-04 #2558 restored recall, designed around a 0.3 threshold. Its fusion scores a full-coverage keyword hit as 0.6*max(0,semantic) + 0.4*lexical, so with a non-positive cosine — routine for a one-word query — a PERFECT keyword match tops out at exactly 0.40. * 2026-07-26 #2790 set the CLI default to 0.7. Above 0.4, keyword recall is mathematically unreachable, so #2558 silently regressed. Instrumented the live path to confirm before changing anything: key=note/alpha terms=["connectivity"] cov=1 bm25=0.029 sem=-0.883 lex=1.000 score=0.400 thr=0.7 → dropped Default restored to 0.3 at both sites, with the ceiling documented so it is not raised past 0.4 again. Verified end to end: a shared keyword recalls all three entries, a unique keyword recalls only its own. This was broken on main too. REAL BUG — anchor containment rejected any project under a symlink containedPath() compared a realpath'd root against a NON-realpath'd candidate, so on macOS (/tmp → /private/tmp) every project looked like an escape. Now compares like with like. Both guarantees re-verified by probe: ../ traversal, deep traversal, absolute-outside and symlink-escape all still rejected; symlinked-root spelling now accepted. REAL BUG — a test that could not fail funnel.test.ts asserted `elapsed < 100ms` as a proxy for "no network call" — its own comment conceded a future fetch() would still pass. It also failed intermittently (118ms) under full-suite CPU contention. Replaced with an actual assertion on fetch/http.request/https.request, patched via the CJS copies (ESM namespaces are non-writable). Negative-controlled: the technique observes a real call and restores cleanly. TESTS THAT MEASURED THE MACHINE, NOT THE CODE * 51 wasm tests asserted `available === true` for optional packages that were simply not installed, gated only on `process.env.CI`. Now also gated on real availability. The 7 tests in those files that need no WASM were lifted into their own blocks so they keep running rather than being skipped along with the rest — no coverage traded away for a green tick. * 4 agenticow tests lacked the `skipIf(!havePkg)` guard their 4 siblings had; in degraded mode the verb is inert and returns before validation. * policy-runtime hashed the raw tmpdir path while the source hashes the CANONICAL one, so it looked for a trust anchor that is never written there — and its cleanup was deleting a nonexistent path, leaking real anchors into ~/.config on every run. * sona's provider allowlist predated the ruvector / wasm-embedder / @claude-flow/embeddings backends. * ruvector/index asserted vi.mock satisfies a *dynamic* import() of a bare specifier. It does not under vitest 4 — the specifier is rewritten to '/@id/@ruvector/core' and fails with ERR_MODULE_NOT_FOUND. Now pins the contract the try/catch actually provides: unloadable → false, never throws. TIMEOUTS — the "flakiness" was a fixed 5s budget Six memory/intelligence files failed with "Test timed out in 5000ms", never an assertion. They initialise a real ONNX embedder and SQLite; alone they are fast, but the full suite saturates every core (~440% CPU). Because it depended on scheduling, a different file failed each run. Raised testTimeout/hookTimeout to 30s. (Process-level isolation was tried first and ruled out — forks changed nothing, which is what identified the cause as time, not shared state.) Verified: 3 consecutive full runs at 0 failed / 3572 passed on the feature base, and 0 failed / 3537 passed on this branch's base. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67 * fix(tests): make ruvector availability assertion environment-independent CI caught what my first pass missed. The rewritten test asserted `isRuvectorAvailable()` resolves to `false`, but whether the dynamic `import('@ruvector/core')` resolves is environment-dependent: - under vitest's module runner a bare-specifier dynamic import is rewritten to '/@id/@ruvector/core' and fails (ERR_MODULE_NOT_FOUND) → false → my `toBe(false)` passed locally - a normal install where the package resolves → true → `toBe(false)` fails, which is exactly what the CI test-ratchet flagged The prior `toBe(true)` had the same defect in the other direction. Pin the invariant that holds in BOTH environments instead — the only thing the try/catch actually promises: it resolves to a boolean and never throws. No specific resolution outcome is asserted. Verified 33/33 locally; the sibling "should return boolean" test already covers the type, so this now documents the no-throw contract distinctly. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67 |
||
|
|
72df96c297 |
feat(agentbbs): cross-host federation — signed envelopes, pinned peers, union merge (#3251)
* feat(agentbbs): cross-host federation — signed envelopes, pinned peers, union merge
Phase 1 gave every host a local append-only room log and derived roomId
deterministically from the room label, so two hosts that register #sales
already compute the same roomId without talking. This adds the layer that
actually moves envelopes between them.
Design. A room log is an append-only set of immutable envelopes, so reconciling
two hosts is a set union — there is no conflicting write to arbitrate and
nothing for a consensus round to decide. Union is commutative, associative and
idempotent, so sync is order independent and safe to retry. That is a
grow-only set keyed on envelopeId, and it is cheaper and less failure-prone
than the Byzantine agreement the plugin README gestures at.
What union does not give you is authenticity: if any peer can inject, the merge
faithfully replicates forgeries. So each host now holds a persistent Ed25519
identity (Phase 1's key was ephemeral per process, which no peer can pin),
every published envelope is signed, and a receiver verifies against the key it
pinned at peer-add time rather than one carried in the envelope — otherwise an
attacker signs with their own key and claims any origin.
Transport is pull-based HTTP: peers poll GET /agentbbs/v1/rooms/:roomId/
envelopes?since=N. Pull needs no inbound connectivity, tolerates a node being
offline, and leaves ingest volume under the receiver's control; push would make
every node an unauthenticated write target. The server binds 127.0.0.1 unless
a bindHost is passed explicitly, and exposes no route that mutates state.
Bounds are enforced receive-side where a sender cannot negotiate them away:
envelopes per sync, bytes per envelope, bytes per response, peer count, and a
hop limit that is incremented on merge so a cycle terminates. hops is excluded
from the signed material, since it is mutated in transit by design.
Five tools: identity, peer_add, peers, serve, sync.
Tested: 35 new tests, most of them the trust boundary — forged payload,
forged origin, key substitution, unpinned signer, cross-room injection, replay,
oversize, hop exhaustion, traversal at the HTTP boundary, and private key never
served. Convergence and idempotence are asserted against two real nodes over a
real socket, not mocks. Verified end to end with three independent nodes:
all three converge on the same envelope set, a second round merges nothing,
and an unpinned node's injection is rejected.
The Phase 1 structural test pinned the surface at exactly 4 tools; it now pins
9 and names both groups, so adding a tool stays a deliberate contract change.
Full package suite: 68 failures against 70 on main, +35 passing — no
regressions.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67
* ci(agentbbs): stop rule 2 firing on the federation wire namespace
ADR-164 rule 2 exists to stop `agentbbs` becoming a mandatory dependency: a
static `import ... from 'agentbbs'` is forbidden, and any other mention has to
sit behind loadAgentbbs / a dynamic import. Its catch-all is a substring test
for "agentbbs" anywhere in the file.
agentbbs-federation.ts trips that catch-all without importing anything. Its
only remaining mentions after comment-stripping are string literals that never
reach a module resolver: the '/agentbbs/v1/...' HTTP route prefix and the
'agentbbs:<kind>:' hash domain-separators.
Adds those two shapes to the existing benign-pattern strip list. The
static-import check is untouched and remains the real gate.
Verified this narrows the check rather than loosening it, by running the
workflow's own rule-2 body verbatim against fixtures:
- the real federation module -> passes (false positive gone)
- `import { x } from 'agentbbs'` -> still caught
- unguarded runtime reference -> still caught
- guarded `await import('agentbbs')` -> passes
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67
* ci(agentbbs): feed rule 2 via a quoted heredoc so bash stops mangling it
The rule 2 fix in
|
||
|
|
e855ae2e4e |
chore(release): 3.39.2 -> 3.39.3
Ships #3250: the agentbbs MCP tools gated on `await import('agentbbs')`, but the published package is a CLI-only launcher with no importable entry point, so all four federation_bbs_* tools reported degraded regardless of install state. Replaced with a subprocess probe. Includes the follow-up hardening: the probe's shell is now gated to win32 instead of always on. The probed binary comes from AGENTBBS_BIN, and under a shell that argument is command-interpreted, so a crafted value executed a trailing command (verified locally). POSIX now spawns without a shell. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67v3.39.3 |
||
|
|
07f1cf3a3f |
fix(agentbbs-tools): detect the real CLI-only agentbbs package instead of a nonexistent JS import (#3250)
* fix(agentbbs-tools): detect the real CLI-only agentbbs package instead of a nonexistent JS import
The published `agentbbs` package (0.2.1) is a CLI-only launcher — a `bin`
script with no importable JS entry point — that downloads/builds a Rust
binary and shells out to it. loadAgentbbs()'s `await import('agentbbs')`
could therefore never resolve, even with the CLI correctly installed and
on PATH, so all 4 federation_bbs_* tools reported degraded:true
unconditionally regardless of install state.
Replace the dynamic import with a subprocess presence probe
(agentbbsCliAvailable(), `agentbbs --version`), matching how the rest of
the codebase treats optional CLI tools. shell:true is required for the
probe to find npm's Windows .cmd shim (child_process does not consult
PATHEXT otherwise).
Making the probe actually succeed surfaced two further pre-existing bugs
in code that was previously always skipped:
- vitest.config.ts externalized @noble/ed25519 alongside genuinely-optional
deps (agentic-flow, agentdb, ...), but it is an always-installed hard
dependency; externalizing it broke dynamic-import resolution under Vite's
SSR transform.
- federation_bbs_human_join dynamically imported @noble/ed25519 a second
time (getSigningKey() already does), which is now deduplicated behind one
memoized loadEd25519() loader.
All 12 tests in agentbbs-tools.test.ts now pass, including the 4
happy-path tests that were previously always skipped.
* fix(agentbbs-tools): gate the probe shell to win32 instead of always on
`shell: true` was unconditional, and the probed binary comes from
`process.env.AGENTBBS_BIN`. Under a shell that argument is command-interpreted,
so `AGENTBBS_BIN='agentbbs --version; touch /tmp/PWNED'` executes the trailing
command. Verified locally: the file is created with `shell: true` and the spawn
fails with ENOENT once the shell is gated off.
This is the case browser-tools.ts already warns about in the comment beside the
same pattern -- "if user-controlled args are ever added, escape them before
spawn" -- and `shell: process.platform === 'win32'` is the convention already
used at browser-tools.ts:49, commands/init.ts:856 and :1194, and
init/helpers-generator.ts:1459. Unconditional `shell: true` appeared nowhere
else in the package.
The Windows behaviour the original was reaching for is unchanged: cmd.exe still
resolves the `agentbbs.cmd` shim via PATHEXT. POSIX simply stops interpreting
the override. Added windowsHide to match the sibling call sites.
Verified the fix still does what it set out to do: with the CLI resolvable the
suite is 12/12 and the four previously-always-skipped happy-path tests execute,
with the shell disabled on POSIX.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67
|
||
|
|
498a238799 |
fix(security): make the fast-uri CVE fix actually reach shipped packages (#3222) — 3.39.2 (#3247)
* fix(security): make the fast-uri CVE fix actually reach shipped packages (#3222) 3.39.1 bumped fast-uri only in the ROOT package.json. Post-publish validation caught that this never reached users: a fresh `npm install ruflo@3.39.1` still resolved fast-uri@3.1.5, squarely inside the advisory range (GHSA-5jgf-p345-68v8, high, CVSS 7.5, >=3.1.3 <3.1.6). Root cause: v3/@claude-flow/cli/package.json carried its own hard pin "fast-uri": "3.1.5" in dependencies, and that is the package that actually ships. The root bump did not override it -- the #2112 lesson recorded in CLAUDE.md, that root overrides do NOT propagate to the published ruflo wrapper, applies to the CLI package too. - v3/@claude-flow/cli: fast-uri 3.1.5 -> ^3.1.6 (the shipping dependency) - ruflo: add "fast-uri": ">=3.1.6" override so the wrapper cannot regress independently of the CLI Verified by fresh-installing the published tarball rather than trusting the lockfile: that is what surfaced the gap in the first place. Refs #3222, #2112 Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCk * fix(ci): refresh v3 pnpm lockfile for the fast-uri bump The 3.39.2 fast-uri change touched v3/@claude-flow/cli/package.json but only the npm lockfile was regenerated. v3/ is a separate pnpm workspace and CI runs pnpm install --frozen-lockfile with working-directory: v3, which rejected the mismatch (ERR_PNPM_OUTDATED_LOCKFILE) and cascaded into 34 failing checks -- every one an install failure, not a real regression. Diff is 6 lines, fast-uri only: specifier 3.1.5 -> ^3.1.6, resolved 3.1.7. Verified pnpm install --frozen-lockfile now exits 0. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCkv3.39.2 |
||
|
|
40878cca35 |
dream(swarm): #3242 fix AgentPool health checks that could never detect a dead agent (evaluated, ACCEPT-scoped) (#3243)
* dream(swarm): #3242 fix AgentPool health checks that could never detect a dead agent performHealthChecks() stamped lastHeartbeat = now on every "still healthy" tick, and updateAgentHeartbeat() (the only other legitimate way to advance that field) has zero callers anywhere in v3/ — so the check's own tick was the sole thing keeping every pooled agent's heartbeat fresh, meaning timeSinceLastActivity could never exceed one health-check interval and replaceUnhealthyAgent() could never fire for any agent, in any pool, ever. Fixes the self-stamp; matches the correct pattern already implemented in UnifiedCoordinator.checkHeartbeats() in the same package. New deterministic test file (fake timers): baseline fails 2/3 tests, candidate passes 3/3, 223/223 full package suite, tsc clean. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01TAzfKzdv9uRVjKt5TeBgkv * dream(swarm): fold in adversarial-critique caveats, re-verify, ACCEPT-scoped Independent critic (separate context) confirmed the fix and re-derived everything itself (zero-callers grep, full suite, tsc, stash-isolated baseline/candidate). Two disclosed, non-blocking caveats folded into the gist: (1) @claude-flow/swarm is not currently a dependency of the shipped @claude-flow/cli, so tonight's fix is real within the swarm package's own UnifiedSwarmCoordinator/tests but has no reachable path through the actual CLI today; (2) health still optimistically recovers +0.1/tick inside the grace window even with zero real heartbeats (pre-existing, out-of-scope, mirrors UnifiedSwarmCoordinator's own no-tick-recovery design). Also fixes a cosmetic class-name typo in the source comment (UnifiedCoordinator -> UnifiedSwarmCoordinator). Re-ran full suite (223/223) and tsc (clean) after the comment edit; recomputed the witness stamp over final content. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01TAzfKzdv9uRVjKt5TeBgkv * dream(swarm): update LEDGER.md for #3242/#3243 Records tonight's row plus verified GitHub state for the trailing nights (MCP tools, not inferred) and the 2026-09-08 gap note (same ledger-rows-only-reach-main-via-merge structural issue tracked since 2026-08-19). Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01TAzfKzdv9uRVjKt5TeBgkv --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
d55b1bfeac |
fix(memory): stop reporting a gated native bridge as the active backend (#3228) + fast-uri CVE (#3245)
* fix(memory): stop reporting a gated native bridge as the active backend (#3228) On Windows the #3024 kill-switch makes getRegistry() return null, so every memory read/write silently falls back to the sql.js store (memory.db) instead of the AgentDB corpus (agentdb-memory.db) the bridge would have opened. The reporter measured the consequence: a canary store/retrieve round-trip SUCCEEDS because both halves use the same unintended file, while an existing live-store key returns found:false against 31,673 real rows. The status surface hid the substitution. getHNSWStatus() gated its bridge branch on whether the bridge MODULE was loaded -- which it is, even when the registry is gated off -- so describeBackend() printed "sqlite (bridge, brute-force cosine)" for a store the bridge never touched. Gate on whether the bridge is permitted to be the active path rather than on whether the module resolved. shouldDisableNativeBridge() is sync and a pure function of platform + env, so it is a faithful discriminator and needs no warm registry. describeBackend() now names the gate and its reason, so an upgrade that changes which file receives writes says so. This does not fix the opt-in allocation abort (#2948) or reroute the fallback to the AgentDB corpus; both need Windows validation. It makes the silent case loud, which is what the report asks for first. Also bumps fast-uri 3.1.5 -> ^3.1.6 (resolves 3.1.7), clearing GHSA-5jgf-p345-68v8 (high, host confusion via skipped IDN canonicalization) and the ajv high that inherited it. Same change as #3222 by @aeonframework, applied here because CI was never authorized to run on that PR. Refs #3228, #3222, #2948, #3024 Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCk * chore(release): 3.39.0 -> 3.39.1 Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCkv3.39.1 |
||
|
|
e341ec8c4a |
chore(release): 3.38.23 -> 3.39.0
Ships the ADR-322C receipt number-domain fix (#3229 / #3235). MINOR rather than patch: receipt output changes shape (fractional candidatePolicy values become scale-12 decimal strings) and policySchemaVersion moves v1 -> v2, so pre-fix receipts refuse promotion with 'policy schema changed'. Not MAJOR because no CLI surface or MCP tool signature changed. Existing signed receipts stay valid as history and are deliberately NOT migrated — re-encoding changes the content, hence the ID, hence invalidates the signature. Re-establish a champion through the explicit reset path, which already requires confirmation and a recorded reason. Validated end to end against the built dist before publishing: a receipt carrying the exact candidatePolicy from #3229 encodes to {"alpha":"0.3","subjectWeight":1,"mmrLambda":"0.5","bodyWeight":"1.5", "typePenaltyFactor":"0.5"}, contains zero fractional JSON numbers anywhere in the payload, and verifies VALID. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01J2s8oQsAZjJtJ6inoYRFPjv3.39.0 |
||
|
|
ea2df4726c |
fix(flywheel): encode candidatePolicy fractions as decimal strings (#3229) (#3235)
`ruflo metaharness flywheel run` wrote receipts whose candidatePolicy
carried binary floats — {"alpha": 0.3, "mmrLambda": 0.5} — contradicting
the contract each receipt claims to satisfy:
witness-receipt-contract.md rule 2 "Fractional values are decimal
strings, never binary floats"
conformance-checklist A3 "Every fractional value is a
canonical decimal string"
ADR-322C L22 "Signed policy fractions use
schema-quantized decimal strings"
The spec's own example strings it: {"hnswEf": 128, "hybridWeight": "0.65"}.
Confirmed in the PUBLISHED 3.38.23 artifact, not just source:
dist/src/services/harness-flywheel.js:219 `candidatePolicy: candidate`.
Two releases shipped after the 3.38.21 report without fixing it.
Root cause was two-part, and the second half is what let it survive:
1. Producer: `candidate as unknown as Record<string, unknown>` — a DOUBLE
cast (a single one will not compile) that erased RetrievalConfig's
`number` fields and laundered floats into the receipt.
2. Verifier: `assertJsonValue` accepted any finite non--0 number. That was
its entire number rule, so ruflo verified its own non-conforming
receipts while autogenous's stricter verifier correctly rejected them
(ruvnet/autogenous#15). The JSON Schema pins `decimalString` on ten
named fields and cannot express it inside `candidatePolicy`, which is
opaque by design — so the rule lived only in prose.
Fix:
- `encodePolicyFractions` encodes non-integers to scale-12 decimal strings
and leaves integers as JSON numbers, matching the contract's example.
Recurses through nested objects and arrays.
- `policyCandidateId` encodes at the hashing boundary, so the content ID is
always over the canonical form. Encoding is idempotent, so
policyCandidateId(raw) === policyCandidateId(encoded) and `verify` can
still recompute the ID from the payload.
- `assertReceiptNumberDomain` enforces the rule at the RECEIPT boundary, on
produce (before signing) and on verify. This is the half that closes the
class: without it the next writer drifts again invisibly.
- The producer's type-erasing double cast is gone.
- policySchemaVersion v1 -> v2, because fixing the encoding changes the
candidate content ID. Pre-fix receipts now refuse promotion with an
ACCURATE 'policy schema changed' instead of a confusing 'stale baseline'
hash mismatch. Signed receipts are deliberately NOT migrated: re-encoding
changes the content, hence the ID, hence invalidates the signature — a
migration would mean minting new receipts claiming to be old ones. They
stay valid as history and unpromotable; the champion is re-established
through the existing confirm-and-reason reset path.
Scope note worth keeping: a first attempt put the rule inside
`canonicalizeJcs` and broke the proposer envelope and the promotion ledger,
which share that canonicalizer and which the contract does not govern. The
full suite caught it; a test now pins that the shared canonicalizer stays
permissive.
Mutation-verified: disabling the encoder fails 16 tests, disabling the
enforcement fails 1, and dropping the boundary encoding in
policyCandidateId fails 13.
Measured A/B on one installed+built tree — strictly better, nothing broken:
baseline 40 files / 83 tests failed, 2801 passed
with fix 39 files / 82 tests failed, 2811 passed
The remaining failures are @ruvector/*-wasm optional native deps absent on
this host, present identically on origin/main.
Claude-Session: https://claude.ai/code/session_01J2s8oQsAZjJtJ6inoYRFPj
|
||
|
|
a295c68703 |
docs: replace RuFlo Explained visuals with the source renders
Swaps the LinkedIn-derived stills for the original Codex-generated renders found in ~/.codex/generated_images. 1400x788 rather than LinkedIn's 800x450 re-encode, and taken from the PNG masters instead of a first frame lifted out of a 100-frame GIF. The store held 24 renders for 14 chapters -- three concepts had a second take and there were three unused billboard plates -- so chapter assignment was resolved by perceptual matching against the published stills rather than by filename, timestamp or eyeball. Every chapter matched at MAE 1.6-2.2 against a runner-up of 17-23, then was verified visually against its caption. 2.8 MB for the set. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67 |
||
|
|
4f77741d91 |
docs: add RuFlo Explained guide with chapter visuals, link from README
The 14-chapter guide (originally published on LinkedIn) covering what RuFlo is, how the model / skill / MCP / runtime pieces differ, setup for Claude Code, Codex, Claude Desktop, ChatGPT and Grok, a first bounded project review, cost, and what to verify yourself. Full text preserved: all 14 sections, 6 "Try it" and 7 "Success check" callouts, prompts and commands verbatim, pinned to ruflo@3.38.23. Chapter visuals recovered from the published article. The originals are 100-frame animated GIFs at 5-7 MB each -- ~82 MB for the set, which is not reasonable permanent git weight -- so each chapter carries a static first-frame still instead (1.1 MB total). The animated versions remain in the linked article. Chapter order was verified visually against each caption rather than assumed from DOM or upload order. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67 |
||
|
|
7d76560a10 |
Revert "docs: add Cognitum Media showcase and link it from the README"
This reverts commit
|
||
|
|
674581294b |
docs: add Cognitum Media showcase and link it from the README
A two-minute broadcast master produced end to end by AI, in which the creative models were never allowed to publish it. It earns a place in Ruflo's docs because it demonstrates the pattern Ruflo is built around -- generation authority and promotion authority are different things -- applied somewhere other than a codebase. The run terminates at `awaiting_approval` with `publication_authorized: false`, the same shape as a witness receipt carrying `authority: 'none'`. Walkthrough covers all twelve scenes with a still, narration line, style, timing and transition for each, plus the governance envelope, the consent model (four presenter variants inheriting one attestation), and the declared audio mastering targets. Numbers were verified against the artifacts rather than copied from the production notes: all six SHA256SUMS check out, and ffprobe independently confirms codec, resolution, frame rate, sample rate, channels, duration and byte size. Two caveats from the source are preserved rather than rounded away -- the cost is a bounded estimate, not an invoice, and captions are a sidecar, not burned in. One rendering defect is documented rather than cropped out: scene 9's headline clips to "Creative does not mean loos". At 28 characters it is the longest in the production and every other panel is <=25, pointing at fixed-width truncation in the overlay renderer. Assets: 12 scene stills (1280px JPEG) + poster + contact sheet, 3.4 MB total. The 84 MB master is deliberately not committed -- it is referenced, not vendored. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01Ff2xRKvYrqXJhefvcapfE1 |
||
|
|
384b923986 |
chore(release): 3.38.21 -> 3.38.23
Patch release covering the six commits landed since 3.38.21: - #3221 gate EnhancedModelRouter's forwarded modelId on tier match - #3169 wire embedding-cosine into SmartRetrieval's MMR step - #3184 wire real cold-start measurement, drop the fabricated V2-vs-V3 benchmark - #3177 bound two ReDoS patterns, stateless PII regexes, O(P) confidence pass - #3204 fix two stale main-tree Test Suite failures - #3203 add v3/@claude-flow/mcp to the root npm workspaces 3.38.22 is intentionally skipped: @claude-flow/cli's publish for that version stuck in npm's "staged" state (published umbrellas, cli 404) and the registry refused to republish over it even after unpublish, so all three packages moved to .23 together to restore version lockstep. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01Ff2xRKvYrqXJhefvcapfE1v3.38.23 |
||
|
|
6acc689150 |
dream(intelligence): #3220 gate EnhancedModelRouter's forwarded modelId on tier match (evaluated, ACCEPT-scoped) (#3221)
Merged after independent re-verification: 109/109 CI green, the earlier '168 failures' finding traced to an unbuilt-sibling-package local artifact (unrelated to changed files), and a subsequent CI flake confirmed unrelated via clean re-run. Overriding the PR's self-merge gate on explicit user (ruvnet) authorization. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_013XaRQntabVwGT9SvnW8cKK |
||
|
|
277c7bc03a |
dream(memory): #3168 wire embedding-cosine into SmartRetrieval's MMR step (evaluated, ACCEPT) (#3169)
* dream(memory): wire embedding-cosine into SmartRetrieval's MMR step (evaluated, ACCEPT) smart-retrieval.ts's MMR diversity re-ranking used token-Jaccard text overlap as its "similarity to already-selected" term, even though memory-initializer.ts's searchEntries() already computes real ONNX-embedding cosine similarity for the primary relevance score and discards it before returning. 2025-2026 production practice (LangChain's reference maximal_marginal_relevance, Qdrant's native Mmr query shipped Sept 2025, Weaviate's MMR reranker shipped in 1.37) universally uses embedding-cosine for this term; token overlap misses low-token-overlap paraphrases embeddings correctly flag as near-duplicates. Threads the embedding through: searchEntries()'s two live compute sites (RaBitQ rerank, brute-force SQL) now return it instead of discarding it; the two CLI/MCP bridge mappers (memory-tools.ts, commands/memory.ts) pass it through; SearchCandidate gains an optional `embedding` field; mmrRerank uses cosine when both candidates have one, falling back to token-Jaccard otherwise (existing callers/tests without embeddings are unaffected). controller-registry.ts's separate toCands()/applyMMR path had the same gap for a different reason (embedding available on r.entry.embedding but never copied) — fixed the same way. Evaluated via deterministic Vitest, zero LLM calls, $0 cost. New discriminating test: a low-token-overlap paraphrase (near-1.0 cosine to the seed) vs. a genuinely different topic (near-0 cosine). Baseline (git stash-isolated): picks the paraphrase second (Jaccard can't tell "low overlap because paraphrase" from "low overlap because unrelated"). Candidate: correctly picks the different topic second. Full @claude-flow/memory suite: 460/461 passing identically with/without the candidate (1 pre-existing chmod-based environmental failure, same class documented since 2026-08-15). tsc --noEmit: zero new errors (verified @claude-flow/cli's 455 pre-existing errors, from unbuilt workspace packages, are identical with and without this diff). Darwin: skipped, scope mismatch (correctness/similarity-metric fix with a discriminating unit test, not a continuous parameter with a gold-labeled corpus). Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01Qho5K4p5ACBt4yzCWjkBGe * dream(memory): address review remediation — split CI, real benchmark, hardening Response to ruvnet's REJECT review on #3169: 1. Split — dream-cycle-backlog-guard.yml moved out to #3205; this branch now carries only the MMR fix, rebased onto main (which already includes the merged #3203 CI fix). 2. Real benchmark (new src/mmr-benchmark.test.ts): 18-doc/6-topic labeled corpus (1 canonical + 2 low-token-overlap paraphrases per topic), run through the actual smartSearch/mmrRerank code, swept across mmrLambda in {0.3, 0.5, 0.7, 0.9} rather than one favorable value. Honest finding: at diversity-heavy lambda, candidate's recall/nDCG drop sharply vs. baseline, because cosine correctly suppresses same-topic near-duplicates that Jaccard fails to detect at all (and so "accidentally" keeps full recall). At smart-retrieval.ts's own default (lambda=0.7), recall@6/nDCG@6 are identical to baseline (1.000/0.777 both) while topic-diversity improves 0.472->0.667 and ground-truth duplicate-rate drops 0.289->0.200. Reports latency (sub-ms both paths at this corpus size) and a memory-overhead estimate. 3. Malformed/dimension-mismatch: found and fixed a real bug while writing these cases. A NaN/Infinity-poisoned embedding component reached cosineSimilarity unguarded; the poisoned NaN score meant `mmr > bestMmr` was never true (NaN comparisons are always false in JS), which could silently break MMR's selection loop and under-fill results below the requested limit. Added isWellFormedEmbedding() so malformed/empty embeddings fall back to token-Jaccard, same as absent/mismatched ones. 3 new cases (NaN/Infinity, empty array, explicit dimension-mismatch) plus a test mirroring the two real CLI/MCP output whitelists (memory-tools.ts:559-569, commands/memory.ts:633-639) to assert `embedding` cannot appear in either shape. 4. Determinism: new test runs the same input through smartSearch 5 times concurrently, asserts byte-identical result ordering every time, including a genuine same-embedding tie. Full @claude-flow/memory suite: 465/466 (1 pre-existing, unrelated environmental failure). tsc --noEmit: zero errors. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01Qho5K4p5ACBt4yzCWjkBGe --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
33a2c3ed07 |
dream(performance): #3183 wire real cold-start measurement, remove fabricated V2-vs-V3 benchmark (evaluated, ACCEPT-scoped) (#3184)
* docs(dream-cycle): backfill LEDGER rows for 2026-08-24..09-03 Ledger-append step silently failed again for 10 consecutive nights (3rd occurrence of this failure class, first flagged 2026-08-19). Verified via git ls-remote + GitHub MCP issue/PR search before concluding this (all 10 nights ran to completion: real branch, issue, and draft PR each) rather than inferring failure from a sparse table. 2026-08-20..08-23 is a separate, confirmed-genuine 4-night gap where the pipeline did not run at all. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01YQUz3bkMpqgtLXgkYa1LFF * dream(performance): wire real cold-start measurement, remove fabricated V2-vs-V3 benchmark (evaluated, ACCEPT-scoped) cli-cold-start.bench.ts's own measureColdStart() (a real, spawn-based process timer) was never called anywhere in the file. Every reported number instead came from setTimeout() delays, including a "V2 vs V3 Speedup: 5.00x" that was guaranteed by construction (setTimeout(100) vs setTimeout(20), no code between them) regardless of any real behavior. - Export measureColdStart(), add runRealColdStartMeasurement() and CLI_BIN_PATH; wire the real spawn-based measurement into the suite. - Remove the fabricated V2-vs-V3 comparison outright (no V2 binary remains to honestly compare against). - Add a deterministic Vitest regression test (4 assertions); fails against baseline (functions not exported, fabricated pattern still present), passes against candidate — confirmed via git-stash isolation. Full @claude-flow/performance suite: 99/99 passing. tsc --noEmit: clean. - Real measured number: node bin/cli.js --version, 5 runs, mean 54.65ms (min 51.94 / max 57.92) — well under the 500ms target. Caveat disclosed in-code: --version is the one subcommand that succeeds without a built dist/ in this worktree; this is not a full-CLI cold-start measurement. - Correct the two stale "Achieved" claims this fed in ADR-STATUS-SUMMARY.md's Performance Targets table (CLI Startup, and the already-known-false HNSW 150x-12,500x figure one row up). Darwin: skipped, scope mismatch (dead-code-wiring fix, not a tunable parameter). Flywheel: no signed bundle, deterministic Vitest evidence retained instead. Follow-ups documented in tonight's gist: same anti-pattern in agent-spawn/mcp-server-init/cli-warm-start bench files (not touched, kept to one conceptual change); a Raft election safety gap and a plugin supply-chain enforcement gap found by tonight's scan roles (hive-mind, security) but out of scope for the performance DEEP surface. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01YQUz3bkMpqgtLXgkYa1LFF * docs(dream-cycle): append 2026-09-05 ledger row Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01YQUz3bkMpqgtLXgkYa1LFF --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
4dcfa0fadd |
fix(aidefence): bound two ReDoS patterns, stateless PII regexes, O(P) confidence pass (#3177)
* fix(aidefence): bound two quadratic patterns, make PII regexes stateless, hoist the indicator count out of the per-match loop Three findings from the 2026-09-04 upstream AIDefence review (ruvnet/midstream PR #105, corpus of 55 bypass / 30 legitimate inputs), fixed here in the vendored detector that the aidefence_scan MCP tool runs: - ReDoS: `\[\[.*?\]\]|<<.*?>>|\{\{.*?\}\}` and `\bDAN\b.*\bmode\b|…` were measured at 1.3–1.4 s and 0.65–1.0 s per 100 KB adversarial input. Bounded to one line / 200 chars; the regression test requires a full detect() under 200 ms on 100 KB of `[[` and of `DAN …` repeats. - Stateful PII: PII_PATTERNS carried /g (and /gi) but were only used with .test(); a shared RegExp's lastIndex made detectPII alternate true/false across calls on the same service instance. Flags dropped. The email class `[A-Z|a-z]` typo (matched `|` as a TLD char) is corrected to `[A-Za-z]`. - O(P²): calculateConfidence re-tested every pattern for every match. The indicator count is now computed once per detect() and passed in; the boost semantics are unchanged (test asserts the multi-indicator input scores at least the single-indicator one). Tests: 19 passed (16 existing + 3 regressions). Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_013PKv3picsfzoQJDKLLW7Rt * fix(aidefence): bound the email PII regex (RFC 5321 lengths) and pin it with a timing test Review of this PR (2026-09-05 mission, agent pr-review-ruflo) found the fix left a worse quadratic behind: the email PII pattern's unbounded `[A-Za-z0-9._%+-]+@` local part took 3.8 s on 'a.' × 50 000, and detect() calls detectPII on every scan — the same aidefence_scan path this PR hardens. Bounded to RFC 5321 lengths ({1,64} local, {1,253} domain, {2,24} TLD); a regression test runs three 100 KB dotted/dashed inputs under the 200 ms budget and checks a real address and a limit-length one are still found. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_019xHM4rAH4aaShb4DTr1n6s |
||
|
|
66e4a12366 |
test/hooks: fix two stale main-tree Test Suite failures exposed by #3203 (embedding width; hook-handler exports) (#3204)
* test(context-hook): assert the hash embedding width is EMBEDDING_DIM (384), not the legacy 768 tests/context-persistence-hook.test.mjs still expected createHashEmbedding to return 768 values; the hook has defaulted to 384 (the ONNX all-MiniLM-L6-v2 width, EMBEDDING_DIM) since February so hash-fallback blobs stay comparable with ONNX vectors. The mismatch was hidden while the Test Suite job died at ETARGET (fixed in #3203) and surfaced on the first PR runs after it (#3177, #3184). Assert EMBEDDING_DIM and pin it to 384; fix the stale "768-dim" docstring on the blob store. node --test tests/context-persistence-hook.test.mjs: 66 pass, 0 fail. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_019xHM4rAH4aaShb4DTr1n6s * fix(hooks): restore hook-handler's require.main guard and test exports dropped by the July helper sync tests/hook-handler-runwithtimeout.test.cjs failed with "runWithTimeout is not a function" on the first Test Suite runs after #3203 (#3177, #3184). |
||
|
|
efadf92b97 |
fix(ci): add v3/@claude-flow/mcp to the root npm workspaces so npm ci resolves the unpublished alpha.10 pin (#3203)
Root package.json and v3/@claude-flow/cli/package.json pin
@claude-flow/mcp@3.0.0-alpha.10 (since
|
||
|
|
db4991967c |
chore(release): 3.38.20 -> 3.38.21
Publishes the #3155 fix (fix(memory): stop seeding the bridge's ControllerRegistry with the sql.js dbPath, PR #3156) and the CI-fixing PR #3059 (agentic-flow-agent duration-assertion flake) to npm. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_011N1hncQ1p4pVt15q2VqaQDv3.38.21 |
||
|
|
b3db018011 |
fix(test): stop asserting strict-positive task duration in agentic-flow-agent tests (#3059)
CI's test ratchet flagged agentic-flow-agent.test.ts as an unexpected new
failure (run 32091740244, same commit
|
||
|
|
4d0134e59b |
Merge pull request #3156 from ruvnet/fix/mcp-http-bridge-rehydrate-3155
fix(memory): stop seeding the bridge's ControllerRegistry with the sql.js dbPath |
||
|
|
d413297dde |
fix(memory): stop seeding the bridge's ControllerRegistry with the sql.js dbPath
Fixes #3155. `initializeMemoryDatabase()` passed its just-resolved sql.js-facing `memory.db` path straight through to `activateControllerRegistry()` -> `bridge.getControllerRegistry(dbPath)`. That seeded the process-wide ControllerRegistry singleton with `memory.db` as AgentDB's own native better-sqlite3 database, instead of the dedicated `agentdb-memory.db` sibling `getAgentDbPath()` exists specifically to provide (#2786). Because this activation only runs on a database's first-ever init (an already-initialized `.swarm/` skips it via the #1791.6 idempotent no-op branch), a bridge started against a brand-new directory would activate against `memory.db` and work fine for that process's lifetime. A restarted bridge process, whose `.swarm/memory.db` already exists, would skip that call entirely, and its first real bridge operation would activate the registry against `agentdb-memory.db` instead (via the `dbPath || getAgentDbPath()` fallback in memory-bridge.ts). That file never received the earlier writes, so every read after a restart came back `found:false` / an empty list, silently, with no error anywhere. `activateControllerRegistry()` no longer forwards the sql.js dbPath to the bridge at all — it calls `bridge.getControllerRegistry()` with no argument, so both the init-time warm-up and every later bridge call (bridgeStoreEntry, bridgeGetEntry, ...) resolve the same file via `getAgentDbPath()`, regardless of whether this is a fresh directory or a restart against an already-initialized one. Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01MND33UDrtWPRH851k9w8M3 |
||
|
|
29f048fc3b |
Merge pull request #3133 from ruvnet/fix/meta-proxy-stale-owner
fix(proxy): transactionally activate the effective daemon |
||
|
|
0a5182b427 | fix(ci): align CLI workspace lockfile | ||
|
|
36740e4304 | fix(proxy): verify effective daemon on install | ||
|
|
d33ef4bf8a |
feat(plugin): add ruflo-music plugin for Cognitum Music (cogmusic MCP)
Wraps the cogmusic MCP server (music.cognitum.one, MiniMax-Music3) as a Ruflo plugin: 2 agents (music-composer for lyrics/prompt writing, music-producer as the pipeline entry point), 7 skills mapped onto the 6 cogmusic MCP tools plus a one-time connect/setup skill, a /music dispatcher command, and an ADR documenting the PAT auth model, audio_url delivery pattern, and disclosed reliability history. First plugin in this marketplace whose skills reference MCP tools from a server other than ruflo-core (mcp__cogmusic__*) — documented explicitly in ADR-0001 as a deliberate, live-verified deviation from the CLI-wrapping convention every sibling plugin follows. Verified: 10/10 structural smoke checks, all cross-file relative links resolve, all six referenced MCP tool names match the real cogmusic surface exactly. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01G3Fkc9qcZ2AkwGPce83yTa |
||
|
|
e21aa352fd |
chore(release): 3.38.19 -> 3.38.20
Publishes PR #3092 (fix(statusline): stop pinning intelligence to a hardcoded 0%). Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01BGiC4SoXiGcUHxs4TsFCehv3.38.20 |
||
|
|
a0b39b9dd0 |
fix(statusline): stop pinning intelligence to a hardcoded 0% (#3092)
Closes #3091. `intelligencePct` had exactly one assignment in the whole file, inside `buildLocalFallback()`. Everywhere else it was only read. So the segment showed either whatever the CLI returned or a literal 0 -- and since `applyLocalOverlays()` repairs adrs/agentdb/tests/hooks/integration/security from disk but never touched `system`, any CLI failure left the brain at 0% sitting beside segments that were still live. Nothing distinguished the two, so an absent measurement rendered exactly like a real one. That is reachable from ordinary conditions, not just exotic ones. Both candidates fail together on a normal setup: Claude Code's plugin marketplace git-clones the repo with no install step, so `bin/cli.js` exists while `@claude-flow/cli-core` does not (the case resolveCliBinCandidates() already documents), and the npx backstop dies with `npm error Invalid Version:` in any npm-workspaces root holding a member with no version field. Three changes: - `getLocalIntelligence()` joins the overlay set, deriving the percentage from `.claude-flow/neural/patterns.json` (project, then home) with the formula the sibling statusline.js already used. A CLI value greater than zero still wins. - `countPatternsCached()` keys the parse on the store's mtime+size. The store is routinely large -- 14.5MB / 1277 patterns on the machine this was diagnosed on -- and the statusline re-renders every prompt, so parsing it each time was not an option. Measured: 57.2ms cold, 0.2ms warm. - Unknown now renders as `—` rather than `0%`, and the fallback constant is `null` instead of `0`. A metric that silently pins to zero when a subprocess dies is indistinguishable from a metric that is genuinely zero, which is what made this read as a display bug rather than an outage. This is the same failure `getLocalAgentDB()` already carries a comment about -- "the statusline showed Vectors 0 despite thousands of real vectors" -- in a different segment. Applied to both committed copies. The CLI package's copy is the generator's single source of truth (#2679) and the one the drift guard byte-compares, so editing it keeps generator output and artifact in step by construction; the repo-root copy is CRLF and its endings are preserved. Verified by execution, both copies: 1277-pattern store -> 100% (previously 0%) no store anywhere -> — (previously 0%) Claude-Session: https://claude.ai/code/session_016QSCkKnxDjqU49NVVpWMK5 |
||
|
|
3c99b1c84a |
fix: repin root claude-flow package.json's @claude-flow/mcp (hygiene)
Same defect as v3/@claude-flow/cli's package.json, already fixed in
|
||
|
|
68a9c9f6a4 |
chore(release): 3.38.18 -> 3.38.19 (supersedes broken 3.38.17/3.38.18)
A subagent I spawned earlier (intended only to relay a status message) went out of scope and independently ran its own full fix/verify/publish cycle in parallel with mine on this same checkout, racing my edits. Between its publish and a set of stale-cached `claude-flow daemon` background processes self-healing package.json mid-write (#3005), @claude-flow/cli was published twice in a row (3.38.17, 3.38.18) with a broken `@claude-flow/mcp` dependency pin, and 3.38.18 additionally shipped entirely missing the `@claude-flow/memory` dependency. claude-flow and ruflo were published at 3.38.18 too, inheriting the same defect. 3.38.19 is the verified-correct cut: @claude-flow/mcp repinned to ^3.0.0-alpha.9 (published, has the #2990 fixes), @claude-flow/memory present at ^3.0.0-alpha.23 (has the #2977 fix). Deprecating 3.38.17/3.38.18 on npm as a follow-up; this commit does not touch the doc/metadata files that raced independently of the version bump. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_018ZkgSrVRWNMcXmaFiMZ7Z8v3.38.19 |
||
|
|
127a6546c4 |
chore(release): 3.38.17 -> 3.38.18, @claude-flow/memory 3.0.0-alpha.22 -> alpha.23
3.38.17 was published by a concurrent session mid-race with a stale
package.json (the @claude-flow/mcp pin had been reverted to the broken
3.0.0-alpha.10 by a background helper-refresh process just before publish;
see #3005). npm forbids republishing over an existing version, so cutting
3.38.18 with the verified-correct pin instead of trying to fix 3.38.17.
Also discovered @claude-flow/memory is NOT in @claude-flow/cli's
bundleDependencies (unlike @claude-flow/mcp/codex/security/plugin-agent-federation)
-- it's resolved from the registry via a normal ^3.0.0-alpha.22 semver range,
and that exact version is what's currently published, without the #2977 fix
from
v3.38.18
|
||
|
|
c313276442 |
docs: correct MCP-server-registration claims, refresh catalog/helpers metadata (retry)
Same content as
|
||
|
|
b5b12a751a |
docs: correct MCP-server-registration claims, refresh catalog/helpers metadata
- README (root + ruflo/): the "no MCP server on plugin-only install" claim was inaccurate for ruflo-core, which ships its own .mcp.json; correct the comparison table and the plugin-install walkthrough to reflect the real mcp__plugin_ruflo-core_ruflo__* tool names. Fix the `claude mcp add` example to use the server name actually registered (claude-flow). - catalog-manifest.json: regenerate (agents 164->165, current gitSha). - helpers/.helpers-version, helpers.manifest.json, statusline.cjs, .proven-config-version, proven-config.json: refresh to match current release metadata (forward version bump, not a revert). Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_018ZkgSrVRWNMcXmaFiMZ7Z8 |
||
|
|
16b0dc4d5b |
chore(release): 3.38.16 → 3.38.17
Fixes #2990's root cause at the publish layer: v3/@claude-flow/cli and the claude-flow umbrella both pinned @claude-flow/mcp to 3.0.0-alpha.10, a version that was never published (npm registry tops out at alpha.9). Since @claude-flow/mcp is bundled (bundleDependencies) via a pnpm workspace link, this didn't affect the monorepo build, but it broke plain `npm install` against the published packages and left package.json/pnpm-lock.yaml out of sync with what's actually resolvable. Repinned to ^3.0.0-alpha.9 (the real latest, which already carries the protocolVersion-string fix) and regenerated v3/pnpm-lock.yaml accordingly. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_018ZkgSrVRWNMcXmaFiMZ7Z8 |
||
|
|
8a85001942 |
fix: Windows CI build, dead agentdb controller exports, memory driver doctor check
- #2992: pin windows-latest -> windows-2022 for the Build V3 matrix job. windows-latest moved to VS2026, which node-gyp can't detect, breaking native builds for hnswlib-node and better-sqlite3. - #2977: remove dead agentdb import attempts in controller-registry.ts for 8 exports no installable agentdb range provides. Promote the working tieredMemoryFallback/createConsolidationStub implementations to first-class for hierarchicalMemory/memoryConsolidation; the other 6 controllers (semanticRouter, mutationGuard, attestationLog, gnnService, rvfOptimizer, guardedVectorBackend) have no fallback and now return null directly instead of attempting an import that never resolves. The consolidation stub now reports source:'stub' and an explicit note so agentdb_consolidate reads as "did not run" rather than "nothing to do". - #2968: add a read-only doctor check (checkMemoryPersistenceDriver) that reports whether the active SQLite driver is native better-sqlite3 (durable, WAL-capable) or the sql.js fallback (silently drops wal_checkpoint writes), using the table-count signal from the issue (~47 native vs ~10 fallback). Warns, never fails; does not touch install behavior. #2990 (MCP HTTP transport protocolVersion/tools-list/IPv6 bind) required no code change — all three fixes are already present on main; the bug in published ruflo/@claude-flow/cli only persists because no release has been cut since they landed. #3002 (ruvocal hidden-tab stream freeze) also required no code change — already fixed on main. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_018ZkgSrVRWNMcXmaFiMZ7Z8 |
||
|
|
5234333c34 |
chore(release): 3.38.15 → 3.38.16 (#3081)
Cuts a release train off main after the 3 dream cycles landed unversioned: - #3062 dream(swarm): bound MessageBus retry attempts, fix unreachable message.failed (closes #3061) - #3057 dream(memory): hybridSearch controller reachable via explicit opt-in — was silently null-returning despite config flag (closes #3056) - #3049 dream(intelligence): discounted Thompson sampling for model-router bandit — opt-in prior decay recovers faster after workload shift (closes #3048) No code changes in this commit — just the version bump across all three publishable package.jsons. Claude-Session: https://claude.ai/code/session_0118jMsYhwHD5dx2vStENsEBv3.38.16 |
||
|
|
b291ac4049 |
dream(intelligence): #3048 discounted Thompson sampling for model-router bandit (evaluated, ACCEPT-scoped) (#3049)
* dream(intelligence): discounted Thompson sampling for model-router bandit Adds an opt-in `priorDecay` config field (default 1 = disabled, fully backward compatible) to ModelRouter's Beta-Bernoulli bandit. When enabled, every bucket/model's alpha/beta decays geometrically once per recordOutcome() call before that round's reward is added (arXiv 2305.10718 discounted Thompson sampling), so accumulated routing history from a long-running persisted state file no longer permanently dominates the posterior after a real-world model-quality shift. Benchmark (n=30 paired trials, seeded PRNG, identical stream baseline vs candidate): non-stationary regime-shift recovery 26.5 -> 21.9 rounds (-17.6%, t=7.24); post-shift correct-routing rate +1.3pp (t=6.10); stationary-workload invariant held (delta +0.02pp, not a regression). Receipt: v3/@claude-flow/cli/benchmarks/results/prior-decay-receipt.json. Mirrors the epsilon-decay pattern q-learning-router.ts already applies to its own exploration rate -- an internal-consistency fix, not an imported pattern. Evaluation and adversarial critique in progress; this commit is the checkpointed, test-passing candidate + its baseline/candidate receipt. Co-Authored-By: RuFlo <ruv@ruv.net> * dream(intelligence): #3048 fix adversarial-critique findings, add gist + ledger Independent adversarial critique found and this commit fixes: a paired-t statistics bug (population vs sample stddev inflated every t-value by n/(n-1)), an unguarded priorDecay input (NaN/negative could silently poison persisted .swarm/model-router-state.json forever, bypassing sampleBeta's own defensive fallback), and a single-bucket benchmark blind spot (now covers low + med complexity buckets -- the med bucket shows no significant effect, disclosed rather than hidden). Also adds the research gist and backfills 3 missing ledger rows for 2026-08-14/15/16 (real PRs/issues existed for those nights but the ledger table wasn't updated) plus tonight's own row. Co-Authored-By: RuFlo <ruv@ruv.net> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
41efca8be3 |
dream(memory): #3056 hybridSearch reachable via explicit opt-in (evaluated, ACCEPT-scoped) (#3057)
* dream(memory): #3056 hybridSearch reachable via explicit opt-in without memoryService The ADR-125 Phase 5 three-arm (dense+sparse+entity) RRF/MMR fusion controller was fully built and tested but unreachable from any production call site: its explicit-enable config override worked at the isControllerEnabled() gate but the createController() factory still unconditionally required a hand-built UnifiedMemoryService, silently returning null otherwise. Falls back to the existing this.backend when it type-guards as AgentDBAdapter-compatible, reachable only via explicit controllers: { hybridSearch: true } -- default auto-enable behavior is unchanged. New deterministic $0 benchmark quantifies the feature's actual retrieval-quality effect for the first time (previously only functional/unit tests existed): +0.267 aggregate recall@10, with a disclosed, statistically significant regression on pure-paraphrase queries where fusion noise dilutes an already-perfect dense signal. Independent adversarial critic (fresh subagent): CONFIRMED-SAFE, reproduced bit-for-bit. 75/75 tests passing in touched files, 458/459 in the full package (1 pre-existing environmental failure, unrelated). Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_016T1JRz8NBeGGmxQJVPReWa * dream(memory): #3056 update ledger with 2026-08-14..18 nights + PR #3057 Reconstructs the v2 live-entries table from PR/issue history since each night's own LEDGER.md edit lives only on its unmerged branch and never reaches main (0% merge rate). Adds tonight's row (memory, hybridSearch reachability fix, ACCEPT scoped, witness b28714fb...). Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_016T1JRz8NBeGGmxQJVPReWa --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
dcea520b4e |
dream(swarm): #3061 bound MessageBus retry attempts, fix unreachable message.failed (evaluated, ACCEPT) (#3062)
* dream(swarm): recover 5 missing live ledger rows (2026-08-14..18) The v2 live-entries section of docs/dream-cycle/LEDGER.md was never appended to for 5 consecutive nights despite each night running to completion (branch + draft PR + issue verified to exist for all 5 via git ls-remote and GitHub search before concluding this, per the ledger's own anti-inference rule). Backfilled directly from the PRs/issues. Root cause of the append gap not diagnosed tonight; flagged in the gist/issue as a candidate finding for a future automation/meta scan. * dream(swarm): bound MessageBus retry attempts, fix unreachable message.failed Given a subscriber whose callback always throws, MessageBus.handleDeliveryError() incremented the queue entry's attempts counter but addToQueue() always constructed a fresh entry with attempts:0 on re-queue, discarding it. Retries were unbounded (96-98 invocations / 0 failures observed in 500ms, git-stash- verified pre-fix) instead of capped at config.retryAttempts, and the message.failed branch was unreachable. Fix: addToQueue() takes an optional existingAttempts parameter; the retry path in handleDeliveryError() now threads entry.attempts through instead of resetting it. Fresh-message call sites are unaffected (default 0). Candidate evaluated: exactly 3 invocations / 2 retries / 1 failure, stable thereafter. 220/220 tests passing (2 new, 0 regressions). Independently re-verified by an adversarial critic (fresh session, no authoring context): CONFIRMED-WITH-CAVEATS — fix and scope match the claim; flagged (and now disclosed in-code) a separate pre-existing, unfixed broadcast-retry queue-key bug this diff does not touch. Full research/evaluation writeup: docs/dream-cycle/dream-gist-2026-08-19.md * dream(swarm): append 2026-08-19 ledger row --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
87bacd5582 |
chore(release): 3.38.14 → 3.38.15 (#3080)
Cuts a release train off main after the following PRs landed unversioned: - #3060 fix(cli): honor MCP tool selection when listing (closes #3055) - #3076 fix: resolve Claude Code launches on Windows (closes #3071) - #3077 fix(hooks): persist real session-end state (closes #3063) - #3044 dream(security): #3043 advisory scanner for untrusted settings.json hooks/allow-rules — CVE-2025-59536-class defense-in-depth, advisory-only (non-blocking), hardened against 6 concrete critic bypasses. No code changes in this commit — just the version bump across all three publishable package.jsons. Claude-Session: https://claude.ai/code/session_0118jMsYhwHD5dx2vStENsEBv3.38.15 |
||
|
|
0e50fe7f57 |
dream(security): #3043 advisory scanner for untrusted settings.json hooks/allow-rules (evaluated, ACCEPT) (#3044)
* dream(security): #3043 advisory scanner for untrusted settings.json hooks/allow-rules carried forward by ruflo init/--upgrade mergeSettingsForUpgrade() and writeSettings() both read a target project's pre-existing .claude/settings.json and spread its hooks / permissions.allow entries into the merged output verbatim — the same trust shape as CVE-2025-59536 (settings.json hook payload achieving command execution with no review step), reached via ruflo init's own merge logic rather than Claude Code's loader. Neither of last night's flagged CVEs (2025-59536, 2025-6514) applies to Ruflo's own hook dispatch or OAuth client directly, but this distinct gap in the same family was genuinely unaddressed. Adds a static, advisory-only (non-blocking) scanner that inspects hook commands and Bash allow-rules for known-dangerous patterns before the merge and surfaces findings as CLI warnings. Merge/write behavior itself is completely unchanged. Hardened after an independent adversarial-critic pass found 6 concrete bypasses (intermediate-pipe-stage downloads, two-step download+exec, eval-wrapped base64, interpreter-mediated dangerous commands, absolute-path-prefixed commands, whitespace-evasive allow rules) — all fixed and pinned as a held-out regression set. Also fixes an ANSI/control-character injection risk in the warning output itself, self-identified during review. 87/87 relevant tests green (1 pre-existing environmental failure elsewhere in the package, unrelated — unbuilt sibling package). Dream Cycle 2026-08-16, security surface. Full research, evaluation receipts, and adversarial critique: issue #3043 and docs/dream-cycle/dream-gist-2026-08-16.md. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01NVdeUqt73Bv7vnPxYpZy7i * docs(dream-cycle): update ledger — backfill 2026-08-14/2026-08-15, add 2026-08-16 2026-08-14 (swarm, REJECT) and 2026-08-15 (performance, REJECT) both had fully-run pipelines whose live ledger rows never reached main (every dream-cycle PR to date is unmerged, so branch-local ledger commits never merge forward) — verified via git ls-remote / search_pull_requests before backfilling, not assumed. 2026-08-16 (security, ACCEPT): advisory scanner for untrusted settings.json hooks/allow-rules carried forward by ruflo init/--upgrade. Issue #3043, PR #3044. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01NVdeUqt73Bv7vnPxYpZy7i --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
02eae9c117 | fix(hooks): persist real session-end state (#3077) | ||
|
|
f7aab68602 |
fix: resolve Claude Code launches on Windows (#3076)
* fix: launch Claude Code from Windows npm installs * test: use Windows path semantics in resolver |
||
|
|
3a1b2257fb | fix(cli): honor MCP tool selection when listing (#3060) | ||
|
|
703e7741d7 |
fix(memory): #3051 memory_store tags lost via write-through cache (3.38.14) (#3079)
Root cause: bridgeStoreEntry's post-write cache set stored only a partial
entry shape — { id, key, namespace, content, embedding } — with no `tags`,
no timestamps, no metadata, no access_count. The DB row itself had the
tags JSON-serialized correctly. But any subsequent bridgeGetEntry hit the
cache first and returned `tags: cached.tags || []` = `[]`, so
`memory_store({ tags })` → `memory_retrieve()` looked like it silently
dropped tags.
Fix: replace the partial write-through with cache invalidation. The next
read then re-fetches the row via the DB reader (which correctly parses
the tags JSON column) and repopulates the cache from the full shape.
Cleaner than expanding the write-through payload — future entry-shape
fields don't need to remember to update the cache-write signature.
Regression test verifies: store {tags:[a,b,c]} → get returns {tags:[a,b,c]}.
Proven to catch the bug: pre-fix the test fails (tags: [] returned);
post-fix it passes.
The same defect would have caused reads to see access_count stuck at 0
and drifted createdAt/updatedAt timestamps from the moment the cache
was first populated until eviction; cache invalidation fixes those too.
Release
- Bumps @claude-flow/cli, claude-flow, ruflo: 3.38.13 → 3.38.14 (PATCH).
Claude-Session: https://claude.ai/code/session_0118jMsYhwHD5dx2vStENsEB
v3.38.14
|
||
|
|
0f3c45101b |
fix: address #3045 #3064 #3065 + new ruflo-deepseek-harness plugin (3.38.13) (#3078)
Bug fixes
- #3045 statusline.cjs: getGitInfo() runs the 5-command git chain per render;
on large repos with concurrent Claude Code sessions this queued subprocesses
faster than they finished (reporter observed hundreds of orphan children +
load-avg in the hundreds). Added a per-cwd tmp file cache with 5s TTL —
dirty status still feels live, pileup is bounded.
- #3064 hooks post-task: the narrow ad-hoc regex /^[a-zA-Z0-9_-]+$/ silently
dropped every colon-namespaced plugin agent (ruflo-core:reviewer,
feature-dev:code-explorer, ...) — i.e. every Claude Code plugin agent. The
canonical validateIdentifier() upstream already allows ':' and '.', so the
redundant regex is removed. Regression test locks in 4 agent-shape cases,
proven to catch the bug: 2 pass / 2 fail on revert, 4 pass with fix.
- #3065 harness-gepa SKILL.md: unquoted colon in `description` broke YAML
parsing in `npx skills add`. Quoted the description; rephrased the bare
"(default:" to avoid the leading colon.
New plugin: ruflo-deepseek-harness
- plugins/ruflo-deepseek-harness/: sibling to ruflo-metaharness (ADR-150
shape). Two skills: `deepseek-chat` (non-reasoning) and `deepseek-reason`
(surfaces reasoning_content separately). Reads DEEPSEEK_API_KEY from env;
degrades gracefully (exit 0 with `{status: 'degraded', reason, hint}`)
when the key is missing or the API is unreachable. `--alert-on-error`
flag opts into hard exit 1 for CI gates. Smoke-tested locally.
Release
- Bump @claude-flow/cli, claude-flow, ruflo: 3.38.12 → 3.38.13 (PATCH:
bug fixes; the new plugin is scaffolding under plugins/ and not part of
the npm-published CLI packages).
Not fixed
- #3051 memory_store tags: current source's memory_store handler passes
tags straight through to storeEntry; a maintainer already verified live
round-trip works on
v3.38.13
|