* fix(console): accept Windows paths in the autopilot envelope
#4434 added Windows path parity across the console, but the autopilot
envelope's own path validator still rejected any `\`, and the downstream
`isProtectedPath` and `pathAllowed` guards compared with POSIX-only string
ops. The code is unreachable today because autopilot start is still gated
off on Windows (ap-windows-gate.spec.ts), but it is the next thing a
Windows user will trip on when that gate is lifted — the natural
`C:\work\app` form, and every UNC path, is refused with the opaque "is
not an absolute folder below the root with no .." error.
Four narrow fixes in `hooks/data/ap-envelope.ts`, driven by the
separator-aware helpers already in `hooks/data/paths.ts`:
- `isProtectedPath`: fold `\` to `/` on a drive-lettered or UNC path
before the protected-dir compare.
- `validateEnvelope` path check: drop the unconditional
`path.includes('\')` reject (it contradicted `isAbsolutePath` which
already accepts `C:\x` and `\srv\share`), and tighten the drive-root
regex `/^[A-Za-z]:\/?$/` → `/^[A-Za-z]:[\/]?$/` so `C:\` is still
refused explicitly (was previously caught by the backslash reject).
- `validateEnvelope` output canonicalisation: use `trimTrailing` instead
of `/\/+$/` replace, so a trailing `\` on a Windows path is stripped
the way `/` already is on POSIX.
- `pathAllowed`: use `hasParentSegment`, `samePath` and `below` from
paths.ts. A file at `C:\work\app\file.ts` is now inside envelope path
`C:\work\app` (and `c:\WORK\app\file.ts` is too), while
`C:\work\application\file.ts` still is not.
Semantics on a POSIX envelope are unchanged — the new helpers degrade
exactly to their old string ops when neither side is a Windows path.
New `tests/ap-envelope-windows-paths.spec.ts` pins what should and should
not be accepted (13 cases: backslash drive, forward-slash drive, UNC,
trailing-separator strip, `..`, bare-drive-root, protected-folder,
pathAllowed under case + separator variation, prefix-match rejection).
Verified on Windows 11: new spec 13/13, ap-core.spec.ts 31/31 (no POSIX
regression), 5 envelope-adjacent specs 64/64 combined, full console suite
3444 total · 3399 pass · 13 fail — same pre-existing baseline as #4434's
own run, zero regressions.
Follow-up to #4434 R1 (https://github.com/ruvnet/ruflo/pull/4434#issuecomment-6371423874).
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix: keep the backslash reject for POSIX-rooted envelope paths
#4436 review (@ruvnet): dropping `path.includes('\')` outright also
dropped the refusal for a POSIX-rooted path with backslashes smuggled
in. `/work/app\..\..\other` passes `isAbsolutePath` (POSIX form), and
`hasParentSegment` splits POSIX-only when `isWindowsPath` says the path
is not Windows, so the `..` segments are invisible to it. On a Windows
host, that leading-`/` path then resolves to `\other`, outside the
folder the user named. Unreachable today because autopilot is still
gated off on Windows, but it is the guard that gate's removal depends
on.
Keep the backslash reject, but only for non-Windows paths (POSIX
should never have `\` in its path). The native Windows forms
`C:\work\app` and `\srv\share\app` continue to pass; the mixed
`/work/app\..\..\other` form is refused as before.
Added case to tests/ap-envelope-windows-paths.spec.ts.
Co-Authored-By: RuFlo <ruv@ruv.net>
---------
Co-authored-by: RuFlo <ruv@ruv.net>
The parser stores `--keep-config` and `-k` as `keepConfig`, but cleanup
read `flags['keep-config']`, which is always undefined. So
`cleanup --force --keep-config` deleted claude-flow.config.json and
stripped the hooks and claudeFlow blocks from .claude/settings.json
anyway. Read `keepConfig`, keeping the kebab key as a fallback as
update, init and mods do.
* test(console): fixture file system accepts Windows drive paths
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(console): Windows project folders are project folders (ADRs, export, catalog, mission cost)
Guards that accepted only a path starting with "/" treated a Windows session
folder (C:\Users\x\proj) as "no project folder is known". Adds hooks/data/paths.ts
(pure string helpers: isAbsolutePath, joinPath, samePath, plus hasParentSegment,
normalizePath, below, trimTrailing, isWindowsPath) and uses it at the absolute-path
guards in adr, event-export, wf-file, plugin-catalog, mission-cost (and the ledger
install path), wf-worktrees, skills-lab, wf-drill-io, facts, ap-envelope and
register. Each caller keeps its own ".." refusal; POSIX paths take the same code
path as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(console): protected autopilot folders match ignoring case on Windows paths
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(console): find the home folder on Windows (sessions, plugin health, What's new)
Implements homeOf function that checks HOME → USERPROFILE → HOMEDRIVE+HOMEPATH
with comprehensive test coverage. Fixes state.home initialization on Windows
where HOME is unset.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(console): disk writes work on Windows through the host file API
Windows has none of uname, dd, sh, install, mkdir or cp in Claude Code's process
environment, so every console write (Events, Timeline, autopilot journal, ADRs,
exports, .gitignore) failed there. New write flavor host-fs, chosen on win32 (no
uname is run), carries the enumerated writes through $.fs.read/write/exists via
writeVia. Linux GNU and macOS/BSD argv are unchanged (pinned by test).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(console): autopilot kill flag can be cleared on Windows; document host-fs create window
Windows has no delete, so clearKill rewrites the KILL flag with a fixed marker and
killSeen treats exactly that content as not set (host-fs only); stopNow rewrites
the flag empty so a second stop is seen. POSIX rm/stat behaviour is unchanged.
write-via and wf-file now say host-fs creates are check-then-write (no O_EXCL).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(console): no Linux-only process probe on Windows; clear probe timers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(console): a hive started from the console shows at once
When a start's CLI run exited 0 but its verify fails (the CLI can return
before its state file lands), wait 1 s via host.after, read fresh and verify
once more before reporting the "expected ..." text. No retry when the run
failed; exactly one retry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(console): pin Windows containment, parent-segment and UNC rules in paths.ts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(console): workspace keys equal across drive-letter case and trailing separator
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(console): autopilot start is refused on Windows until atomic appends and Windows-aware path checks exist
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore(console): 0.42.2 changelog and version
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(console): read home variables by literal name so the module loads
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* post-merge cleanup: finish #4036 integration
The Option B merge commit (4313013cb) landed cleanly against upstream/main
but a batch of merge-resolution edits in the working tree were not persisted
into the commit itself. This commit finishes the integration:
Path-helper consolidation (abs-path.ts → paths.ts):
- hooks/data/wf-drill-io.ts: add missing paths.ts import for below,
hasParentSegment, isAbsolutePath, samePath, trimTrailing (fixes 4
wf-drill-read.spec.ts failures that threw `ReferenceError: isAbsolutePath
is not defined` at runtime).
- hooks/adr.ts: migrate from ./data/abs-path to ./data/paths, pulling in
joinPath which is needed at lines 89/94.
- hooks/register.ts: drop the rootedPath import from the deleted
abs-path.ts; inline the equivalent isAbsolutePath+joinPath expression.
- hooks/data/event-export.ts, hooks/data/wf-file.ts: drop the duplicate
isAbsolutePath import from the deleted ./abs-path (the main-side wider
'./paths' import already covers it).
VERIFY_RETRY_MS lifecycle (ADR-450 interaction with Marius' feature):
- hooks/runner.ts: extend the retry condition — `verified === 'n/a' &&
spec.verify !== undefined` also retries, so a snapshot that hasn't
loaded yet (null, not "no verifier") gets the one-second re-check.
Tests brought in line with the new timing / platform guards:
- tests/tui-actions.spec.ts: settle(600) → settle(1200) at the point the
runner's VERIFY_RETRY_MS one-second re-check has to resolve before the
verdict is drawn.
- tests/windows-drive-path-sites.spec.ts: fsWithDir made separator-agnostic
via a normSeps helper, since joinPath now uses the base path's own
separator (`\` on a Windows base).
Version bump:
- .claude-plugin/plugin.json, hooks/version.ts, scripts/smoke.sh: 0.44.7
→ 0.44.8.
- CHANGELOG.md: prepend 0.44.8 entry crediting #4036 and @socrates6969.
Verified: full console suite 3400 total · 3362 pass · 15 fail · 23 skipped.
Every one of the 15 remaining fails is pre-existing on main or a
platform/env limitation (Windows chmod, fake-timer timing, codex not
installed) — zero merge-caused failures remain.
Co-Authored-By: RuFlo <ruv@ruv.net>
---------
Co-authored-by: Marius Carlsson <109006081+socrates6969@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Mario Jauvin <marioja@users.noreply.github.com>
Co-authored-by: r <r@x>
getUpdateType compared major, minor and patch independently. With 3.60.0
running and 3.59.17 as the registry latest, the minor check failed but
patch(17) > patch(0) returned 'patch', so the startup check auto-installed
3.59.17: a downgrade. It also made 'ruflo init' run npm install in the project
and time out the mods-cli-bin test on the 3.60.0 release PR.
An update is a strictly newer version: guard with semver.gt first.
Co-authored-by: r <r@x>
* feat(ruflo-console): ruRE page in TOOLS — static reverse engineering from the console (ADR-494)
A TOOLS page for the six rure_* tools of ruflo-rure, built the way the Sandbox
page is: six Dev Tools entries (dt-re-*) drawn on a page of their own, so they
share the confirm gate, palette ids and result panel. Five rows are $0 reads;
UNPACK .ASAR writes outside the project and asks first. A new 'sample' field
takes only a project-relative path. Results are the analyzed file's own text
and pass plain() so a hostile binary cannot put an escape on the terminal; a
number that is not a measurement reads n/a. A CLI without the tools says to
update. Enter in a ruRE field runs that page's row.
Tests: rure-page.spec.ts (catalog vs real schemas, readers vs real captured CLI
output, hostile answers, page, guide, palette, Enter routing) and
rure-e2e.spec.ts (every row's argv through the built CLI on real files,
including a hostile ELF; fails with plain() removed). Console 0.44.7 -> 0.45.0;
ruflo-rure 0.4.1 -> 0.4.2 (docs only).
Co-Authored-By: RuFlo <ruv@ruv.net>
* test(ruflo-console): ruRE e2e accepts either AIDefence outcome (CI has no AIDefence)
The .NET e2e asserted a string literal was 'hidden by AIDefence', true only
where the optional package is installed. In CI the tool reports screened:false
and the page says 'not screened: AIDefence was unavailable'. Both are honest;
the test now pins that the screening state is always on screen.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(ruflo-console): ruRE EVM result no longer shows a doubled ellipsis
plain() already marks a clipped value; the reader added a second one to the
event topic and the IPFS hash. Test pins it.
Co-Authored-By: RuFlo <ruv@ruv.net>
---------
Co-authored-by: r <r@x>
* fix(github): stop reporting merge/close/update success when nothing happened
github_pr_manage merge/close and github_issue_track update/close
returned success when nothing happened on GitHub:
- A failed `gh` call (merge conflict, failing checks, auth, unknown
number) fell through to the local JSON store and returned
{ success: true, source: 'local-store' }.
- That fallback matched records with key.includes(String(n)) against
`pr-<Date.now()>` ids. It returned success whether or not anything
matched, and could mark an unrelated record (prNumber 12 vs
pr-1786319612345).
- github_pr_manage validated owner/repo but never passed them to `gh`,
so merge/close acted on the cwd's repository (fixed for issues in
#2963).
Now:
- When `gh` is present, a failed command returns success: false.
- Offline, only a local record whose id is exactly pr-<n> / issue-<n>
is updated; otherwise the call returns success: false.
- A missing number is an error.
- PR actions pass --repo owner/repo.
list/create and their offline local-store behaviour are unchanged.
Fixes#4424
* fix(github): include gh's error text when merge/close/update fails
Per the #4424 triage: a failed `gh` call now reports gh's stderr (or
the error message when there is none) along with the action, e.g.
"gh pr merge 12 failed: X Pull request #12 is not mergeable: ...".
runArgv keeps its string | null contract and is now a thin wrapper over
runArgvResult, which returns { out } or { error }. The four mutating
paths use runArgvResult directly.
Refs #4424
neural_compress prune deleted every pattern whose usageCount was below
targetSize and saved the result. Nothing in the neural store records
usage: patterns are written with usageCount 0 and only max-merged by
distill and import. So any targetSize above 0, including the 0.5
default and a "10% reduction" of 0.1, deleted and persisted every
pattern while reporting success.
Prune now returns success: false with the reason, and never calls
saveNeuralStore(), until usage is actually tracked. This is the same
treatment quantize received in #4195. The targetSize schema description
no longer promises a 0-1 size reduction. neural_optimize's zero-norm
prune is a separate path and is unchanged.
Fixes#4423
daa_agent_adapt read the score as
`(input.performanceScore as number) || 0.8`:
- A failing score of 0 became 0.8, raised successRate, and was sent to
AgentDB as { success: true, quality: 0.8 }.
- A missing score was recorded as the same fabricated 0.8 success.
- Out-of-range scores were accepted, although the schema says 0-1.
A given score must now be a finite number in [0, 1], and an invalid one
returns success: false without touching the agent. An omitted score
records no score: successRate and the AgentDB outcome are left alone,
while the adaptation itself (feedback and the adaptation count) is
still recorded.
Fixes#4426
FallbackEmbeddingService.embed() spawned 'npx agentic-flow@alpha embeddings generate' with execFileSync, so whenever the primary embedder was unavailable each embedding could freeze the process for up to 10 seconds. Swap to async execFile and await the callback; keep the same 10s timeout, shell:false argument array, JSON parsing, cache and hash fallback (#4410 memory scan).\n\nExport the service for a focused regression. The test mocks both execFile and execFileSync: candidate lets a 10ms timer fire while the 60ms child is pending and returns the child's vector; baseline (export-only) blocks the timer and fails.
rescueAgentdbEmbedder() used a process-wide boolean to avoid re-patching. AgentDB registries are keyed by dbPath and each owns a distinct embedder, so after the first store was rescued every later store kept its mock embedder and wrote/searchable vectors from AgentDB's random fallback (#4410 memory scan).\n\nThe per-embedder __ruvectorRescued sentinel already makes the patch idempotent. Remove the global flag, keep the sentinel check, and export the helper for a focused two-store regression. Baseline (export-only, no behavior change) rescues the first embedder and leaves the second false; candidate rescues both.
Move the external agentic-qe-fleet entry from v3.15.11 (221d6de1) to
v3.15.12 (90a91fe3), the current npm latest, and update the README links,
the external-plugins test fixture and ADR-484.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(reasoningbank): count each duplicate removal once
consolidate() built its candidate array once, then kept comparing entries after deleting them. In a 3+-way similarity cluster it removed a tombstoned pattern again and incremented duplicatesRemoved a second time, issuing redundant storage/HNSW deletes and overstating the cleanup metric (#4410 deferred memory finding).\n\nSkip any pattern that is no longer live before starting its outer iteration or comparing it as a candidate. Add a 3-way cluster regression: baseline reports 3 removals for 2 duplicates; candidate reports 2 and leaves one survivor.
* fix(reasoningbank): stop comparing after the outer pattern is removed
Review repro: with lowest-quality-first insertion, the outer candidate could be removed inside its own inner loop, then keep comparing as a tombstone and remove a later live pattern. Break out of the inner loop when toRemove is patterns[i]. Parametrize the regression over high-first, low-first and mid-first insertion orders (3/3 candidate, low-first fails on baseline).
DaemonManager's failure backoff used a bare setTimeout stored only in a local closure. stop() cleared the interval but not that timeout, so an explicit stop could be undone about a second later when the pending restart fired. Repeated failures could also accumulate multiple backoff timers (#4410 automation scan).\n\nTrack backoff timers per daemon, clear them in start() and stop(), supersede an older backoff before scheduling a new one, and re-check stopped/stopping/disabled state inside the timer callback. unref() the timer so it does not keep the process alive. Add a fake-timer regression; baseline restarts after stop (2 attempts), candidate stays stopped (1).
setWorkerEnabled(type, true) called scheduleWorker() without clearing an existing timeout. this.timers kept only the newest handle, so each redundant enable leaked the previous timer; the leaked timer survived stop() (which only clears current map entries) and could run the worker again untracked. Disabling also left the type in pendingWorkers, and an in-flight run rescheduled itself even after disable (#4410 automation scan).\n\nClear any existing timer at schedule time, avoid scheduling when a timer or in-flight run already owns the type, remove disabled types from the pending queue, and only reschedule after a run while the worker is still enabled. Add fake-timer regressions; baseline leaks three timers after three enables and keeps the disabled pending worker.
detectAgntcyRuntime() treated any module that resolved as configured, even though the three AGNTCY commands call createSlimTransport/joinGroup/publishAgentRecord and the real @agntcy/slim-bindings exports a different class-based API (#4376). Users with the package installed and an endpoint set therefore got configured:true and only discovered the mismatch at the command call site.\n\nAdd an optional requiredExports probe to detectAgntcyRuntime, pass each command's actual function names, and report a resolved-but-incompatible SDK as configured:false with the missing exports in the reason. Commands now take the normal not-configured path with an accurate message.\n\nThis does not implement the missing SDK API; it stops the capability check from claiming an API exists when it does not.
Node rejects execFile of .cmd/.bat on Windows, so the npm-global shim read
as 'version unknown'. Probe shims via cmd.exe with a quoted path; skip paths
containing cmd metacharacters.
Co-authored-by: r <r@x>
The Stop hook spawned the CLI with no cwd, so it created a stray
.swarm/memory.db in the session project instead of the MCP data root.
Pass cwd: CLAUDE_FLOW_CWD || process.cwd().
Co-authored-by: r <r@x>
The #2982 ORDER BY fix kept LIMIT 1000, so rows outside the newest 1000
were unfindable (3.38.3 and the sql.js path use 10000). Default is now
10000; CLAUDE_FLOW_SEARCH_WINDOW overrides (clamped 100..100000).
Co-authored-by: r <r@x>
Closes the findings from an independent review of ruRE 0.1.0-0.4.0:
screen every attacker-derived string and report screened/skippedFiles,
walk without following symlinks under a total-byte budget, linear EVM
file sniffing, an iteration budget for ELF/PE table walks, guarded ELF
e_entry, and single-descriptor reads. Each fix has a test that fails
without it.
Co-authored-by: r <r@x>
Adds rure_analyze_evm per ADR-0005: aligned disassembly and pattern scan
for dispatcher selectors, hazardous opcodes, embedded addresses, event
topics, EIP-1167 minimal proxies, solc metadata and embedded strings.
No interpreter, RPC or network; PUSH data is never read as opcodes;
input capped at 256 KiB; embedded strings pass the AIDefence gate.
Co-authored-by: r <r@x>
Adds rure_analyze_dotnet per ADR-0004: an original pure-TypeScript
ECMA-335 metadata reader (assembly identity and references, types,
method count, P/Invoke imports, user-string literals, entry point).
No dependency, no IL decoding, never loads or runs the assembly. Table
extents are validated against the stream before any row is read, and all
extracted text passes through the AIDefence gate.
Co-authored-by: r <r@x>
Adds rure_analyze_binary per ADR-0003: an original pure-TypeScript
container parser (format, arch, entry, sections, linked libraries,
imported/exported symbols, optional strings). No disassembler, no new
dependency, never loads or executes the target. Reads are bounds-checked,
tables and strings are capped, files over 128 MiB are refused, and all
extracted text passes through the AIDefence gate.
Co-authored-by: r <r@x>
* docs(ruflo-rure): ADR-0001 scope/dual-use policy, ADR-0002 Phase 1 architecture
Fixes#4395 (part 1 — research/planning phase)
Co-Authored-By: RuFlo <ruv@ruv.net>
* feat(ruflo-rure): Phase 1 implementation — JS/Electron static analysis
Implements the plugin scaffold (manifest, skill, command, agent, smoke
test, benchmark) and the three Phase 1 MCP tools per ADR-0001/0002:
- rure_extract_asar: resource-capped .asar extraction via @electron/asar,
checking the archive header's declared size/file count before writing
anything (decompression-bomb guard).
- rure_analyze_js_app: TypeScript-compiler-API-based static analysis —
import/export graph, Electron process-boundary detection (ipcMain/
ipcRenderer/contextBridge, require("electron"), nodeIntegration flags),
every finding evidence-sourced to file + line range.
- rure_find_feature: deterministic lexical/structural feature search,
explicitly not semantic/LLM matching.
Every extracted string is routed through the AIDefence content gate
before being returned; flagged content is redacted, never dropped
silently. Parses only — never executes analyzed code, independently
verified by scripts/smoke.mjs grepping the real source (comments
stripped first, so the doc comment describing the property can't trip
its own check).
27 new tests, all passing. Typecheck clean. Tool-description audit
(ADR-112) clean. Benchmark: ~3.6k-18k files/s on a synthetic Electron
bundle (plugins/ruflo-rure/scripts/benchmark.ts).
Trust tier: experimental. Marketplace/directory submission is a
separate, explicit decision, not part of this change (ADR-0001).
Part of #4395.
Co-Authored-By: RuFlo <ruv@ruv.net>
* feat(console): add ruflo-rure to the plugin map (devtools section)
Every plugin directory needs a line in PLUGIN_MAP or the console's own
plugin-coverage test fails. ruflo-rure lands in devtools, same section
as the other inspection/analysis plugins (ruflo-browser, ruflo-jujutsu,
ruflo-testgen).
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(ruflo-rure): fix CI failures — real deps, capability-brain classification
Three real bugs surfaced by PR #4396's CI, not flakes:
- typescript was only a devDependency of @claude-flow/cli, but
src/rure/ts-walk.ts needs it at runtime. Moved to dependencies.
(CI: "Cannot find package 'typescript'" in a from-source install.)
- @claude-flow/aidefence was never declared as a dependency of
@claude-flow/cli at all (only resolvable locally via monorepo
hoisting), so the AIDefence gate silently no-op'd in CI's isolated
install. Added as an optionalDependency, matching the existing
graceful-degradation code path and the browser_act precedent this
also fixes.
- capability-brain.ts classified every registered MCP tool into a
domain by prefix; the three new rure_* tools had no domain, so
guidance-brain.test.ts's "classifies every live registered tool"
assertion failed. Added a 'reverse-engineering' domain (experimental
maturity, matching ADR-0001's trust tier) and its package-owner
lookup entry.
All 35 rure + guidance-brain tests pass; typecheck clean;
audit-tool-descriptions and audit-cli-mcp-tools clean.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(ruflo-rure): revert @claude-flow/aidefence optionalDependency, mock it in tests instead
Reverts the optionalDependency added in 2570cf39d — it's on the #2561
forbidden-heavy-optionalDependencies list (CI's "Enforce CLI + ruflo
optionalDependencies budget" guard caught it): adding it back to
@claude-flow/cli regresses the cold `npx -y @claude-flow/cli@alpha
--version` startup time that #2561 fixed.
gateWithAIDefence's dynamic import was always meant to be speculative
— present only if something else in the install graph happens to pull
it in, same as the existing (unexported) copy in
browser-intent-tools.ts, which never declares it as a dependency
either. The real fix for "my tests need deterministic flagging
behavior" is mocking the module boundary, not forcing the real
optional package to be installed. Both rure-aidefence-gate.test.ts and
rure-tools.test.ts now vi.mock('@claude-flow/aidefence', ...), matching
the precedent in issue-2670-security-defend.test.ts. Added one more
test asserting graceful pass-through (not a thrown error) when the
module genuinely isn't resolvable.
36/36 rure + guidance-brain tests pass; typecheck clean; CLI
optionalDependencies back to 12 (budget 13), no forbidden entries.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(ruflo-rure): alias typescript/@electron/asar in the isolated analyze-command test
analyze-json-output.test.ts runs analyzeCommand in a fully isolated temp
directory (scrubbed PATH/HOME/TMPDIR, no real node_modules reachable),
resolving only the handful of packages it explicitly aliases to this
package's own resolved paths (vitest, semver, bcryptjs, zod, plus the
cli-core/security source trees). The capability-brain / mcp-tools
registry analyzeCommand transitively loads now reaches rure-tools.ts,
so the same treatment is needed for its two real dependencies:
typescript (compiler API, src/rure/ts-walk.ts) and @electron/asar
(src/rure/extract-asar.ts) — both aliased to requireHere.resolve(...),
matching the existing zod/semver/bcryptjs pattern exactly. This is the
sanctioned mechanism this test already uses for a dependency it
genuinely needs to execute; it's unrelated to the heavy-package
absence assertions earlier in the same file (ruvector, agentic-flow,
@huggingface/transformers, etc.), which stay untouched.
20/20 analyze-json-output tests pass; full rure + guidance-brain suite
(56 tests) passes alongside it in the same run.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(ruflo-rure): memoize the aidefence module resolution, make gateAll sequential
CI's "Test V3 Packages" job reproducibly failed exactly one test —
gateAll's "preserves order and length across mixed benign/flagged
input" — twice, while every other mock-dependent test in the same file
passed both times. gateAll was the only caller that resolves
@claude-flow/aidefence via several concurrent gateWithAIDefence calls
(Promise.all over the input array); every other test calls it once.
In CI, @claude-flow/aidefence is genuinely absent from node_modules
(confirmed by this job's own "@claude-flow/aidefence not loadable"
log line elsewhere in the same run) — unlike this dev environment,
where it's pnpm-hoisted and physically present, just mocked over.
Fix: memoize the dynamic import behind a single shared promise, so
concurrent callers all await the same resolution attempt instead of
each firing their own against a module vi.mock has to intercept
without a real file backing it. Also made gateAll sequential as
defense in depth — gating is cheap, and there's no reason multiple
calls need to race the first-ever resolution attempt at all.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(ruflo-rure): add @electron/asar to the root package.json for CI's Test Suite job
Root CI's "Test Suite" job never installs v3/'s own pnpm workspace —
it runs `npm ci` at repo root only, then runs vitest from repo root
against v3/@claude-flow/cli/__tests__/*. Module resolution for those
test files walks up from v3/@claude-flow/cli (no node_modules there in
this job) through v3/ (also none) to the repo root's node_modules,
which is why `typescript` already resolved (already a root
devDependency, for an unrelated reason) while `@electron/asar` did
not (never referenced anywhere in the root tree before this).
Added @electron/asar to the root dependencies, matching how zod/ws
are already placed there for the same reason, and regenerated
package-lock.json. The regen also synced two pieces of real,
pre-existing lockfile staleness this surfaced: the recorded root
version (3.58.0, several releases behind the actual 3.59.17) and a
leftover `toml` dependency entry (removed from package.json before
this PR, but never swept from the lockfile until this install).
Verified against CI's actual invocation shape: `node
node_modules/vitest/vitest.mjs run v3/@claude-flow/cli/__tests__/rure-*`
from repo root, not just the v3-workspace-scoped `npx vitest run` I'd
been using to verify until now.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(ruflo-rure): lazy-load the TypeScript compiler instead of a top-level import
ts-walk.ts statically imported `typescript` at module scope. Since
rure-tools.ts is registered into mcp-client.ts's shared tool list, every
CLI invocation loaded the full TypeScript compiler into memory at
startup, not just commands that use ruRE — the same failure shape as the
#2561 cold-start regression (@claude-flow/aidefence).
This also explains three straight CI failures on the exact same file
(mods-cli-bin.test.ts, which exec's the built CLI's `init` through
execFileSync): the heavier startup made it the most likely victim of the
repo's known tinypool worker-crash flake under CI's resource limits.
Fix: import type-only `typescript` for AST types, and lazy-load the real
module (memoized) inside analyzeModuleSource, matching the pattern
already used by aidefence-gate.ts and extract-asar.ts. This makes
analyzeModuleSource/analyzeJsApp/findFeature async; updated their two
call sites in rure-tools.ts and the benchmark script, plus the test
files's call sites.
Verified: `node bin/cli.js --version` succeeds with node_modules/typescript
physically removed (previously would have crashed), confirming the
compiler is no longer loaded on every invocation.
Co-Authored-By: RuFlo <ruv@ruv.net>
---------
Co-authored-by: r <r@x>
The parser stores `--dry-run` as `dryRun`, but `update all` read
`flags['dry-run']`, which is always undefined. executeMultipleUpdates
then ran with dryRun = false, so a dry run ran `npm install
<pkg>@<latest> --save-exact` for every package and recorded it in the
update history. Read `dryRun` (keeping the kebab key as a fallback, as
daemon, init and mods do) for the executor call and the summary.
ruflo's federation layer already supports pointing at a self-hosted
gateway/relay instead of x.ruv.io — RUFLO_X_GATEWAY_URL,
RUFLO_X_RELAY_WS and RUFLO_X_RELAY_HTTP are already read by
v3/@claude-flow/cli/src/mcp-tools/x-federation-{tools,join,channels}.ts
before falling back to the x.ruv.io/relay.ruv.io defaults. The gap was
discoverability, not capability: nothing told a user this override
exists, and the console's x.ruv.io board never mentioned it.
- plugins/ruflo-x-gateway/README.md: new "Self-hosting a custom
federation gateway" section explaining both layers (client-side env
vars for the CLI's own tool calls; server-side deploy vars for
running your own gateway instance) and what deliberately stays
fixed (the Claude plugin directory submission's .mcp.json, guarded
by test/claude-directory.test.mjs — that one endpoint is reviewed
and vetted by Anthropic, so it can't be templated).
- plugins/ruflo-console/hooks/xruv.ts: a one-line comment pointing at
the README from the RELAY constant. The constant itself is
unchanged — this file is documented "Pure: entries and parsers
only, no $", so it can't read process.env to show a self-hoster's
actual configured relay; the honest fix is pointing at the real
override, not guessing at a value this file can't observe.
- plugins/ruflo-console/hooks/help-topics-more.ts: the "Use the
x.ruv.io board" help topic now has a tip naming the three env vars
and pointing at the README.
No behavior change — x.ruv.io/relay.ruv.io remain the defaults
everywhere they already were.
Co-authored-by: r <r@x>
While researching a cross-ecosystem architecture question, found that
witness-receipt-contract.md §9 and the ruflo-anchor-record-v1 schema
both still say "PROPOSED-EXTENSION... nothing above is decided" —
but the two consumers that placeholder was gating (ruvnet/rvm#35,
ruvnet/autogenous#10) already merged real implementations against
exactly this shape:
- ruvnet/rvm#37 (merged): verify and anchor ruflo ADR-322C receipts
into the witness chain
- ruvnet/autogenous#13 (merged): export promotion evidence in the
ruflo ADR-322C receipt shape
- ruvnet/ruflo#3066, the PIR tracking issue that scoped this work,
is itself closed with a status comment confirming the schema
(PR #3067) is canonical on main and both conformance audits cleared
No schema or behavior change — this is a documentation-accuracy fix.
ADR-322D formally promotes the record from PROPOSED-EXTENSION to
Accepted to match what's already shipped and consumed, and explicitly
scopes it against a different, still-unwired "RVM" relationship
(LatentMesh's own aspiration to have RVF/RVM gate its candidate
cognition) so the two aren't conflated in future reads.
Claude-Session: https://claude.ai/code/session_01G3Fkc9qcZ2AkwGPce83yTa
Adds a JSON report from a structural validation pass over the 108 agent
definition files under .claude/agents/. All 108 files have valid
frontmatter (name + description) and non-empty system-prompt bodies.
Also surfaces 11 agent names that are defined twice under different
paths (e.g. code-analyzer, backend-dev, pr-manager, python-specialist,
typescript-specialist, tdd-london-swarm, production-validator,
goal-planner, sublinear-goal-planner, database-specialist,
project-coordinator) - mostly a legacy flat file plus a newer
categorized copy. Not a parse failure, but worth a look since it's
ambiguous which definition wins when agents are loaded by name.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: rUv <ruv@ruv.net>
annotation-review.md's readOnly/destructive/idempotent/openWorld classification
table only listed 12 of the 14 live MCP tools. team_board (added at 0.2.0) and
run_complete (added at 0.2.1) were never added to the table, even though the
doc claims "the server tests compare the live tool list with this
classification" — nothing actually enforced that until now.
Both tools' real annotations (read from src/server.mjs) fit the existing rows
exactly: team_board uses READ(...) like the other read-only tools, and
run_complete uses WRITE(..., true) like the other idempotent upserts.
scripts/smoke.mjs now asserts the doc's documented tool set matches the live
server tool set exactly, so this can't silently drift again. Verified by
reverting the doc fix alone and confirming smoke failed with the exact
"documents every live tool" check, then restoring it.
Co-authored-by: r <r@x>
ruflo init wrote memory.persistPath: .claude-flow/data into .claude-flow/config.yaml, but getMemoryRoot() only reads the JSON config candidates (claude-flow.config.json and .claude-flow/config.json) and otherwise uses .swarm. The YAML key was dead text: editing it moved nothing and could mislead users into thinking a new store location was active (#3193 part b).\n\nRemove the ignored key and point the YAML at the JSON key that actually controls the store. Add an init regression test that fails on the old YAML and asserts the generated file no longer advertises persistPath.
* fix(memory): distinguish recent dead locks from live writers
inspectAgentdbLock() labeled a dead holder as 'live' when the lock was younger than the 10s stale threshold. The same state then produced 'another process is writing this database' guidance, so callers could wait for a writer that did not exist or leave a recoverable lock in place indefinitely (#3979 item 5).\n\nAdd a distinct 'recent' LockState for dead-PID locks still inside the stale window, clamp clock-skewed ages to zero, and give withAgentdbLockRecovery a precise retry message. The guard still refuses to clear the lock until it is old enough; only the diagnosis changes.
* chore(memory): bump @claude-flow/memory to 3.0.9
The leaf-package-publish audit correctly requires a version bump whenever publishable @claude-flow/memory source changes. Bump the leaf and both CLI dependency pins, and update the pnpm importer specifier, matching the #4279/#4152 release pattern.
Move the external agentic-qe-fleet entry from v3.15.10 (e5814a2f) to
v3.15.11 (221d6de15c73bb385961e4e649d8fcc1f5dc2dea). The plugin folder
changed only its version and its MCP pin (agentic-qe@3.15.11). Updates
the README links, the external-plugins test fixture and ADR-484.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The parser stores `--no-browser` as `browser: false`, and the declared
`no-browser` option defaults `noBrowser` to `false`, so
`noBrowser ?? ['no-browser']` was always false and the login used the
loopback browser flow unless the host looked headless. Treat
`browser: false` as the opt-out too.
The parser stores `--no-color` as `color: false`, and the declared
`no-color` option leaves `noColor` at its default of `false`. The
statusline action only read `no-color` and `noColor`, so
`hooks statusline --full --no-color` and `ruflo --no-color hooks
statusline` still printed ANSI escape codes. Also check
`color === false`, as index.ts and init.ts already do.
Standalone publish for #4385 (stop injecting retracted performance
claims in GuidanceProvider.generateSessionContext()) — @claude-flow/cli
does not depend on @claude-flow/hooks directly, but @claude-flow/guidance
and @claude-flow/plugins do (both via ^3.0.0, so no pin bump needed there).
Co-authored-by: r <r@x>
GuidanceProvider.generateSessionContext() still emitted the exact multipliers the May 2026 intelligence audit retracted: HNSW '150x-12,500x' and Flash Attention '2.49x-7.47x'. This string is the SessionStart additionalContext path, so every session received claims the repo's own docs had already corrected. The existing test even pinned '150x' (#4381 memory-scan finding).\n\nReplace them with honest status lines: HNSW points at the memory package's measured benchmarks instead of a universal multiplier, Flash Attention is labeled as pending an in-tree benchmark, and the memory reduction is labeled a target. Update the regression test to assert the retracted substrings never reappear.
* fix(daemon): read config_set's values envelope in config.json reader
Fixes#3192
config_set's default-scope handler writes {"values": {key: val}, ...},
but the daemon's readDaemonConfigFromFile() only ever checked
scopes.project or the file's flat root — never `values`, which is where
config_set actually puts default-scope writes. As a result,
config_set daemon.idleSecs 0 silently had no effect on the running
daemon, even though it "succeeded" and updated config.json.
Add raw?.values as a fallback between scopes.project and the flat-root
read, matching the fix the issue reporter verified against the pinned
upstream sha.
* fix(daemon): resolve each config key independently (scopes.project > values > root)
Addresses review feedback on #3239 (Copilot): picking one whole object
as "cfg" meant a key only present in the values envelope was still
missed whenever scopes.project existed at all, even for unrelated
keys, because cfg resolved to scopes.project and the per-key fallback
only ever dropped to raw (root), never to raw.values.
Resolve each daemon.* key independently through
scopes.project -> values -> root so a config.json with keys spread
across more than one shape (e.g. one project-scoped config_set call
and one default-scope call) still resolves correctly.
---------
Co-authored-by: SID-6921 <99672439+SID-6921@users.noreply.github.com>