Skip to content

feat(ruflo): rolling support window, 3.46.x adoptions and running backups - #247

Merged
pacphi merged 45 commits into
mainfrom
feat/ruflo-support-window
Sep 27, 2026
Merged

pacphi merged 45 commits into
mainfrom
feat/ruflo-support-window

Conversation

@pacphi

@pacphi pacphi commented Sep 27, 2026

Copy link
Copy Markdown
Owner

Summary

Adopts a rolling Ruflo support window and Ruflo 3.46.x's fixes, turns Ruflo's backups and distillation on, and implements the maintainer's Branch 3 decisions (B3-D1–D5, recorded in the audit record).

Verification

  • Guarded runner (scripts/run-tests.mjs), Node 26.4: 5,324 pass / 0 fail. Node 22.22.3: green. UI: 495/0. Coverage: about 93.96 / 82.76 / 93.19.
  • tsc, eslint (0 errors), complexity ceiling, markdownlint, build-check, doc-citations and ga-surface-guard: all clean. The real-state tripwire reported no change.
  • Adversarial review found two critical issues and three important ones, all fixed test-first:
    • the global ak rejecting --host claude;
    • a convergence test;
    • rebase conflicts;
    • Codex's imported launcher entry;
    • a bare .claude-flow/ becoming a Ruflo project marker.
  • Read-only real-data pass on Ruflo 3.46.1. The real write pass (auto-start, launcher, probe-row cleanup) runs after release with the released build.
  • Windows paths (ak.cmd/ak.ps1 capability probe, shim resolution) are proven only by this CI run.

Refs #239, #213 · remediation program Branch 3

🤖 Generated with Claude Code

The Ruflo dependency policy now carries supportWindow (newest 6 minors,
never fewer than those first published in the last 30 days). The loader
validates the optional field: positive integer newestMinors and minDays
and a string basis; anything else invalidates the policy.
…st 30 days)

ak status adds a Ruflo support-window row computed from release dates
remembered in kit.json (versionCheck.rufloMinors): inside the window is
info, below it is a fail row that sync repairs by upgrading, and with no
remembered dates the row says the window is not yet known and names
ak sync. A plain read never calls the network. A non-dry, upgrading sync
records each minor's first stable npm publish right after its forced
drift lookup; a failed lookup keeps the old dates. The versions section
gains drift/loadConfig/now seams and sync.run a releaseDatesRunner seam.
…uflo has the fix

The watch computes the Ruflo support-window floor from the npm release
dates it reads (reusing a gate's fetch) and the registry's supportWindow.
A released Ruflo entry whose fix version is above the floor moves from
the act-now groups to 'Released, waiting for the support window' with no
dispatch; its 'released' ledger line carries no branch. With no floor
(offline, npm unreadable, no window) nothing is held. The upstream-status
skill names the new group.
Ruflo fixed its fabricated CVE count (ruvnet/ruflo#2694) in 3.32.2, below
the support window's floor. ak no longer injects the getStatuslineData
wrapper, the footer drops rufloLocalSecurity and rufloHonestInsight, and
status and sync drop the statusline/cve subsystem. SEC_WRAP_STRIP stays
for one release so a statusline an older ak patched is cleaned on the
next sync. The registry marks ruvnet/ruflo#2694 adopted with its removal
proof.
Ruflo shipped `migrate fix --agents` in 3.38.2, below the support window.
The scaffold-agents info row now says the installed Ruflo predates it and
points to ak sync. The section gains gaps/fixAvailable seams for a
hermetic test. The registry marks ruvnet/ruflo#2986 and ruvnet/ruflo#2985
(the same ak change) adopted.
ADR-0041 §7 records the rule, where it lives (supportWindow on the Ruflo
dependency policy), the remembered evidence (kit.json
versionCheck.rufloMinors, written only by an upgrading ak sync) and the
watch's hold. The hook-assurance DDD context gains SupportWindow.
UPGRADING, UPSTREAM-WATCH and TROUBLESHOOTING describe the window, the
waiting-for-window group and the retired statusline overlay; ak sync
--help says when release dates are read.
A live daemon that deferred backup or distillation (CPU load or low free
memory, read from .claude-flow/logs/daemon.log after the last daemon start)
now gets a daemons warning until the job's own metrics are newer. On macOS
a low-memory deferral names ruvnet/ruflo#2935 and is a sync repair;
elsewhere it names the flat threshold key. With no daemon and start-on-use
on, the row says Ruflo starts it on the next ruflo command.
…ettings

ak setup no longer turns claudeFlow.daemon.autoStart to false. It writes
flat keys in .claude-flow/config.json before starting the daemon:
daemon.idleSecs 0 below 3.46.0 (ruvnet/ruflo#3194) and, on macOS,
daemon.resourceThresholds.minFreeMemoryPercent 0 (ruvnet/ruflo#2935), never
through ruflo config set (ruvnet/ruflo#3449). It turns init's autoStart
false to true unless kit.json rufloDaemon.autoStart is false, with receipts
in kit.json that ak uninstall uses to restore. A malformed file or a user
value is left alone. ak status reports drift in a Ruflo repository and ak
sync applies it, removing keys a newer Ruflo no longer needs and restarting
a live daemon so it reads them. Setup discloses both writes.
SETUP and TROUBLESHOOTING describe what ak writes in .claude-flow/config.json
and .claude/settings.json, the new daemons rows, and the kit.json opt-out.
ADR-0016 and the ubiquitous language record the managed daemon settings;
the audit record's Addendum 3 Item 1 gets the 3.46.1 implementation note
and the disposable-project proof. ak setup --help and ak sync --help
mention the daemon step.
The macOS deferral row promised a sync repair in any project with a live
daemon, but sync manages daemon settings only in a Ruflo repository, so
elsewhere the planned fix could never converge. Outside one it is now the
manual step. Sync also no longer restarts a daemon whose deferred job has
run since: status and sync share one pendingDeferral check.
…ps the built-in engine

Ruflo 3.32.2+ falls back to security/builtin-aidefence.js when
@claude-flow/aidefence is missing (ruvnet/ruflo#2670), so a missing
aidefence now costs only adaptive learning and the aidefence_* MCP tools.

- natives: rufloBuiltinDefence() probes the CLI's built-in engine.
- status: warn (sync repair kept) instead of fail when the engine exists.
- verify: run defend with -o json and read the verdict
  (parseDefendVerdict); the text-mode post-detection crash
  (ruvnet/ruflo#3473, still in 3.46.1) is reported as a crash.
- footer: a fourth, silent "builtin" state; no alarm.
- about and healAidefence detail describe what aidefence adds.
- registry: #2670 adopted; #3473 re-checked on 3.46.1.
Ruflo 3.46.0 honours --no-codex-detect and --no-skills-sh (ruvnet/ruflo#3167,
PR #3434). rufloProjectInitInvocation(version) passes the flags alone from
3.46.0; below it (and for an unknown version) it keeps --format json and
RUFLO_NO_SKILLS_SH=1, since 3.39 to 3.45 are inside the support window.

The registry's affected ranges now accept <major>.<minor>.x, the
suppression constraint covers 3.38.21 to 3.45.x, and #3167 is adopted
with the removal proof run on 3.46.1.
Ruflo 3.46.0 wires the MCP policy enforcer into both stdio entry points
(ruvnet/ruflo#3415, PR #3423); 3.45.0 has no evaluateToolCall there. The
not-wired explanation now applies below 3.46.0 (it stopped at 3.44.0),
and the catalogue no longer calls the policy inert.

Live proof on 3.46.1 with ak's policy capped at 2: two memory_stats calls
allowed, the third refused, all three audited. From a subfolder with no
.harness/ every call is refused; that exposure stays open until the
launcher's Claude mode (plan Task 4.1).
A committed .harness/mcp-policy.json has no receipt on a teammate's
machine, so reconcilePolicy reports it user-managed there and stops
managing it. Other tools (Agentic QE) commit their own policy file on
purpose, so ak excludes only its own file, never .harness/ as a whole,
and never touches .gitignore.

excludeFromGit adds one anchored line under a "# agentic-kit" comment to
the repository's info/exclude, resolved without spawning git (a linked
worktree's .git file and commondir lead to the main repository's file,
the only one git reads). It runs whenever ak's file is written or already
converged; removing the policy removes only that line and its comment.
Sync reports the change and the trust manifest discloses it.
…er since 3.46.0)

PR #3441 shipped in Ruflo 3.46.0: ruflo doctor reports the agent-browser
CLI (ADR-122) against a 0.27.0 minimum, confirmed in the @claude-flow/cli
3.46.0 tarball and the installed 3.46.1. ak never carried a workaround.
PR #3420 (3.46.0) makes the daemon parse .claude-flow/config.yaml, but the
memory root still reads JSON only (memory-initializer.js, rechecked on
3.46.1), so ak keeps its memory pin and the entry stays watching. The
entry now names the ak files that cite it.
The note said "open as of 2026-08-13". It now cites the Aug 31 triage and
the 2026-09-27 hosted probe on Ruflo 3.46.1 (run 36333572972): 10/10
aborts by default and 10/10 with single-threaded ONNX Runtime sessions.
The step stays continue-on-error. The registry records the comment.
…058)

ADR-0058 Updated 2026-09-27: Ruflo 3.46.0 enforces the policy on stdio
(#3415); ak's boundary corrected from 3.44.0 to below 3.46.0; ak excludes
its policy file from git (section 5, the section 7 table row, Consequences
and Implementation status).

User-facing docs describe the current state: the security rows in
TROUBLESHOOTING (built-in engine, the #3473 crash message), the version-
gated init flags in SETUP and HOST-SUPPORT, the 3.46.0 governance boundary
and git exclusion in MANAGED-TOOLS and DASHBOARD, and the footer's alarm
in README. The audit record gets the slice 3 implementation note.
GIT_CONFIG_GLOBAL=/dev/null has no Windows equivalent path, so the tests
set it only off Windows; -c user.name/user.email already give git what the
tests need.
Sync and project setup now add a line to the repository's
.git/info/exclude where MCP governance is on; the upgrade note says what
is written, what is never touched, and how to untrack an already
committed copy.
ak x ruflo-mcp --host claude starts Ruflo at the repository root (else the
folder, else the user-level store) like Codex's mode, but keeps Claude
Code's settings env: no componentEnv and no governance deletion
(ADR-0058 §3). ak's agent-browser config stays. An unknown --host exits 2.
Started from a subfolder, Ruflo now reads <repo>/.harness/mcp-policy.json,
which closes the 3.46.x stdio-governance exposure for Claude sessions
opened in a subfolder (B3-D1).
register() now adds claude-flow at user scope as
`ak x ruflo-mcp --host claude` (with ak's AGENT_BROWSER_CONFIG). ak's
earlier `ruflo mcp start` entry and the launcher entry are both ak's to
replace; any other command, scope or env key stays the user's. When
`ak` is not on PATH, register() changes nothing and returns
reason 'ak-not-on-path', which setup, sync and ak x mcp pick report.
Status warns on ak's old entry (a sync repair when registration is
managed) and, for the launcher, names the store it picks from the
current folder. Hermetic Claude seats use the Claude mode too, and the
memory rows now say the launcher serves Claude Code and Codex (B3-D1).
…utside projects

Harvest takes rufloMemoryLocation(cwd) directly: from a repository it
still runs at <repo> with <repo>/.swarm/memory.db; from the home folder,
a temporary root or a tool's own folder it runs in the flat user-level
store with CLAUDE_FLOW_DB_PATH and CLAUDE_FLOW_MEMORY_PATH pinned and
distils that store. An explicit root (ak x verify harvest) is unchanged.
Project setup from such a folder now refuses before spawning anything
("this folder is <reason>; run ak setup from a project folder").
memoryProjectRoot is unchanged for the daemons row and projectMemoryEnv
(B3-D1).
…receipt

Older ak setup runs left `_setup/verify-<pid>-<ms>` rows (content
setup-verify, namespace _setup) in project and user-level stores; Ruflo
mirrors them into agentdb-memory.db and its own delete leaves the mirror
(ruvnet/ruflo#3450, re-checked on 3.46.1). Status now warns with the
count per store folder, and a new memory-probe-cleanup sync step backs
each affected file up with VACUUM INTO, deletes exactly the matched ids
from both files, and writes a receipt under the state folder. kit.json
cleanups.setupProbeRows records each cleaned file so it is cleaned at
most once. Scope: the current project's store and the user-level store
(B3-D2).
The audit record gains the four Branch 3 decisions (B3-D1 to B3-D4) in
its decision format, with the implementing commits and the disposable
proofs. ADR-0058 §3 and ADR-0016 record that Claude Code reaches Ruflo
through ak x ruflo-mcp --host claude. SETUP, HOST-SUPPORT, UPGRADING,
TROUBLESHOOTING and the DDD glossary describe the launcher for both
hosts, the ak-on-PATH requirement and the one-time probe-row cleanup.
The launcher that Claude Code and Codex register spawned a bare `ruflo`.
On Windows that is an npm .cmd shim CreateProcess cannot start, so every
launch failed with ENOENT while readiness (have/run, which resolve the
shim) passed. It now resolves the command through exec.mjs resolveShim
against the launch env's PATH and refuses with a message when no safe
invocation exists.
A malformed .claude-flow/config.json, or one holding the user's own
value for a key ak wants, is left untouched and never counts as drift,
so ak status said nothing about it. The dry-run reconcile now names the
held keys (reconcileRufloDaemon `held`, daemonConfigHeld), and status
adds a manual row that names the file, the user's value and the flat
key to set.
When .claude-flow/config.json was malformed or held the user's own
minFreeMemoryPercent, status still offered the macOS deferral as a sync
repair. Sync then changed nothing and only restarted the daemon, which
hid the row until the next deferral and repeated on every sync. Status
now makes that deferral a manual step naming the key, sync skips the
restart when the floor is held by the user, and its warning says the
daemon was not restarted for that key.
register() refuses with 'ak-not-on-path' when it would write a
registration that starts `ak`, so the mcp sync step failed on every run
while status kept offering it as a sync repair. When ak manages the
registration, status now looks ak up and, when it does not resolve,
makes the three rows whose fix goes through register() manual: the
registration that does not start through the launcher, the missing
agent-browser config and the missing registration. Each names "put ak
on PATH, then run ak sync". The legacy-migration row stays a sync fix:
it does not need a new registration when claude-flow is already right.

The offline status fixture has an empty PATH, so the golden snapshot
now records the manual row. The two sync plan tests that expect the mcp
heal run with a fake ak on PATH, and a new test pins that the heal is
not planned without it.
SETUP, TROUBLESHOOTING and ADR-0016 say a .claude-flow/config.json that
is unreadable or holds the user's own value is left alone, ak status
names the key to set, and sync does not restart the daemon for it.
UPGRADING says status lists "put ak on PATH, then run ak sync" when the
launcher registration cannot be written.
After the rebase onto #245, the guarded unit run failed on two rules
that the branch's own tests broke. The git-exclude test built git's
environment from process.env, and the spawn-env guard now refuses that.
It now runs git through spawnEnv with its own throwaway home. Three test
files left their sandbox homes behind, and the leftover check reported
ak-about-security-home, ak-probe-cleanup-home and
ak-security-status-home. Each file now removes its home when it
finishes.
Claude Code starts whatever `ak` it finds on PATH. That can be an older
install than the kit writing the registration. ak 4.0.0-alpha.56 rejects
`--host` (exit 2, "Unknown option"). So a sync run by a newer kit
re-registered claude-flow to a command that cannot start, and every
Claude session lost Ruflo.

register() now asks the PATH `ak` for `ak x ruflo-mcp --help` before it
writes anything. `--help` is answered before the command runs. When the
help does not name `--host`, it returns the new reason
'ak-launcher-outdated' and leaves the working registration in place.
The status mcp section asks the same question, but only when sync would
re-register. It then makes those rows manual, with the fix "update the
`ak` on PATH ..., then run `ak sync`". Setup, sync and `ak x mcp pick`
name the new reason. `ak` joins exec.mjs's Windows shim list, so the
check resolves its npm shim.

The hermetic Claude seat of `ak run` and qe-court now starts Ruflo
through the running kit itself (node plus this kit's
bin/agentic-kit.mjs), never a PATH `ak`.
"fresh dual-host Codex provisioning and repeated refresh retain one
canonical connection" went stale when claude-flow moved onto
`ak x ruflo-mcp --host claude`. It had two faults. It injected no
launcher check, so on the sandbox's empty PATH register() refused
before the runner ran (false !== true). It also still expected
`ruflo mcp start`. It now injects a ready launcher and expects the
launcher arguments with ak's AGENT_BROWSER_CONFIG. The fake
~/.claude.json gets the launcher entry with that env, so later
refreshes find it already in place and claude-flow is added once.
…er entry

Codex's Claude config import copies Claude Code's MCP servers by name.
Since B3-D1, Claude Code's claude-flow is `ak x ruflo-mcp --host claude`.
On a machine without the disabled placeholder, Codex therefore gained a
second Ruflo transport. ak recognised only `ruflo mcp start` under that
name, so both tables had no repair kind, the repair plan was empty, and
every sync reported an unrepairable duplicate and exited 1.

A Codex claude-flow table in ak's Claude launcher form now counts as the
same alias. It needs exactly command and args, with at most ak's
AGENT_BROWSER_CONFIG child. Sync disables it in place with the existing
placeholder, after the usual backup and confirmation. Consent is
remembered for that form as well. The Codex-mode launcher under the
claude-flow name stays the user's. The repair reason names the imported
copy instead of calling it a deprecated transport.
…ject

rufloProjectRoot accepts any Git repository root with a .claude-flow/
folder. Ruflo 3.46.1 does not count a bare folder as a project
(services/daemon-autostart.js:90-123, isRufloProject), because its
startup migration can create one on its own (ruvnet/ruflo#2852). It does
count .claude-flow/config.json. On macOS ak always wants the free-memory
floor, so sync wrote that file in such a repository and made it a Ruflo
project. The next ruflo command there then started a detached daemon.

applyRufloDaemon and the status drift, held and deferral rows now use
rufloDaemonProjectRoot. It requires the same gate plus one of Ruflo's
durable markers: .claude-flow/config.{yaml,yml,json},
claude-flow.config.json, .swarm/memory.db, a claudeFlow block in
.claude/settings.json, or a ruflo/claude-flow server in .mcp.json.
rufloProjectRoot keeps its ADR-0058 role for the policy file.
The Open items still listed three things this branch resolved. Each is
now marked resolved with its commits:
- Claude-side memory outside a project (B3-D1): 518b4be, 8c91658 and
  8966a18.
- The one-time _setup/verify-* cleanup (B3-D2): ec6c936.
- setup turning daemon start-on-use off (Addendum 3 Item 1): a9d59cd.

The Branch 3 decision notes now cite the SHAs after the rebase onto
main. B3-D1 records the launcher check and the disabled Codex import
copy. The daemon note records the durable-marker gate.
The launcher note said the PATH `ak` "must be this version or newer".
But the released 4.0.0-alpha.56 has the same version number as this
build and still lacks `--host`. The note now names the capability ak
checks (`ak x ruflo-mcp --help` must list `--host`), as SETUP.md does.
…uflo bundles them

Decision B3-D5: an AgentDB fix delivered through Ruflo (doneWhen.release.bundledBy)
counts as released for ak only when the oldest Ruflo inside the support window
bundles a fixed agentdb, the same rule Ruflo's own fixes follow.

- fetch: bundled() takes an optional carrier version, so the floor Ruflo's
  dependency chain resolves exactly like the newest one's.
- watch: once the floor is known, resolve what the floor Ruflo bundles for the
  entries the newest carrier was checked for and for bundled entries recorded
  as released with a first fixed version.
- classify: such an entry waits for the window until the floor bundles the
  fix; a floor bundle that could not be resolved is "Could not check". The
  held ledger line carries no branch and a newer Ruflo never changes it.
- render: the held line names what the floor Ruflo bundles.
…ed range

The registry schema now describes a dependency policy's optional supportWindow
(newestMinors, minDays, basis, unsupported) and the affected-range grammar
upstream.mjs applies: an exact version, <major>.x or <major>.<minor>.x. A
package-qualified affected item is noted as reader-only. bundledBy's
description names the support-window floor (decision B3-D5).
Branch 3's fifth decision: an AgentDB fix delivered through Ruflo counts as
released for ak only when the oldest supported Ruflo bundles a fixed agentdb
(choice A, oldest supported Ruflo; B was the newest Ruflo). The audit records
it with its limit (npm resolves each range to its highest match, so the answer
is a fresh install of the floor Ruflo). UPSTREAM-WATCH.md describes the floor
check and the held group; ADR-0041 section 7 and its Updated line note it.
@pacphi
pacphi merged commit 2646099 into main Sep 27, 2026
16 checks passed
@pacphi
pacphi deleted the feat/ruflo-support-window branch September 27, 2026 20:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant