mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-09-14 14:01:28 +08:00
codex/seraphina-guidance-streaming
395 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e477d3a5aa | Verify SDK progress notification contract | ||
|
|
a692d5ef83 | Test incremental guidance and streaming failure paths | ||
|
|
24bb42afca | Wire opt in MCP guidance progress and cancellation | ||
|
|
001b7d75de | Wire opt in MCP guidance progress and cancellation | ||
|
|
1e897c02aa | Implement bounded guidance stream decoding | ||
|
|
31b3e6a69f | Add Seraphina streaming | ||
|
|
005a0ed25e |
chore(x-gateway): 0.7.1 — version label for the claims TTL fix (#3293)
Claude-Session: https://claude.ai/code/session_015y2UjPSd3VX1CYnPstZJ5D |
||
|
|
4bb9ca182d |
fix(x-gateway): claims reducer honours ttlSeconds — expired leases free the resource (#3292)
Found by an external node's failure-path probes on the live swarm (T1 expired-lease, T5 disconnected-worker): reduceClaims recorded ttlSeconds but never applied it, so a worker that claimed and then vanished held the resource forever, and the docs' promise that a timed-out claim frees itself was false. - Expiry is evaluated against the next event's timestamp while reducing (deterministic replay: a later ClaimIssued after expiry wins) and against `now` for the final ledger (a disconnected owner's lease lapses without a release). - Claims without ttlSeconds never expire (unchanged). - Ledger entries now carry `expiresAt` (ISO) so clients can show remaining lease. - Tests: ttl expiry lets a later claim win; live lease is kept; disconnected worker frees; no-ttl never expires; releasing an expired claim is a no-op. Claude-Session: https://claude.ai/code/session_015y2UjPSd3VX1CYnPstZJ5D |
||
|
|
8485302f10 |
feat(x-gateway): 0.6.1 — bound Seraphina by budget instead of an admin token (#3275)
* feat(x-gateway): 0.6.1 — Seraphina bounded by budget, not by an admin token Squashes what is actually running on x.ruv.io so git and production agree. Seraphina was gated alongside the write tools, but it is not like them: it reads the roster, the claims board and recent messages, asks a model, and returns advice. It writes nothing and carries no authority. The gate answered the wrong question — the exposure is model spend, and spend is bounded with a budget, not a password. The practical cost was worse than a wrong abstraction. A browser cannot hold a bearer secret, so a published UI was either locked out or tempted to ship RUFLO_ADMIN_TOKEN to the client — the same token that mints invites and publishes as the gateway identity. The safe-looking option was the catastrophic one. Seraphina now answers with no token, bounded by a shared daily cap and a per-client hourly cap, and anonymous callers cannot select the high or ultra tiers. An admin token lifts both. Every write tool keeps its gate; verified live that claims_issue, federation_invite_mint, federation_publish, federation_admit and channel_publish all still refuse an anonymous caller. Not included, deliberately: a tool to relay member-signed events through the gateway. It was built, tested against the live relay, and removed. buzz-relay refuses any EVENT whose pubkey differs from the NIP-42 authenticated connection, so a gateway cannot publish on another identity's behalf — and it does not need to. Members already publish as themselves through the wss://x.ruv.io proxy, verified end to end. Shipping a tool that always fails would be worse than none. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67 * feat(x-gateway): serve onboarding guidance, and deliberately not key generation The registry said "generate a Nostr keypair" and stopped, and it never named the two identities in play. That gap was not theoretical: a capable agent inspected the surface, found only gateway-signed publishing, and concluded ordinary members could not publish from a dashboard at all. They can. It just was not written anywhere they could reach. federation_onboarding and ruv://federation/onboarding now answer it, open, no token. The guide leads with the distinction that causes the confusion — your key signs for you, the gateway's key signs for the service, and the gated tools are gated precisely because they speak as the service. It carries the five steps, the four things never to do, and the three traps this service has actually taught us: sign the NIP-42 relay tag with the canonical URL even when proxied, the relay binds publishing to the authenticated connection, and the channel tag is `c` not `h`. It does NOT generate keys, and that is the point rather than an omission. A service that mints your keypair and returns the secret has seen your secret, and becomes custodian of every identity it "helped" — the same custody mistake as putting an admin token in a browser, inverted. The guide ships the code so the caller runs it locally and the key never crosses the wire. One test note worth keeping: the first version of the safety assertion regex- matched for "send your secret" and failed on the guide's own "Never send your secret key" line. It is now structural — no field may be named for secret material — because a string search cannot tell an instruction from its negation. Verified live on 0.7.0 (ruflo-x-gateway-00015-qj7) with an anonymous call. 20/20. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67 * fix(ci): register the gateway's credential and budget knobs, and skip test/ like tests/ The ADR-125 precedence audit failed the gateway PR, and it was right to look — but for two reasons that are both about the audit, not the code. RUFLO_ADMIN_TOKEN was not registered as an escape hatch even though its sibling RUFLO_X_ADMIN_TOKEN is, with the same reasoning: a secret must never be a CLI flag, where it lands in shell history and process listings. The gateway is a long-running service with no typed command surface at all, so there is no invocation to attach a flag to. Same for the two Seraphina budget knobs added in #3275. The other half was a naming gap. SKIP_DIRS already excludes `tests` and `__tests__` because the audit is about production precedence, not test setup — but the flagged lines were in `plugins/ruflo-x-gateway/test/`, singular, which was not in the set. Two `process.env.X = 'test-admin-token'` assignments inside a test fixture were reported as undeclared production reads. Verified: the audit now exits clean on this branch. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67 |
||
|
|
a64f8b1ad8 |
feat(x-gateway): declare default channels so a quiet one is still discoverable (#3274)
channel_list reports what has been published to recently. That is right for finding activity and wrong for onboarding: a channel nobody posted in today does not exist as far as a newcomer can tell, so everyone starts in the flat firehose and the channels stay empty. The emptiness was self-sustaining. Four declared channels, always listed with their purpose even at zero messages: announce for releases and status, help for anyone stuck, claims so cross-host ownership has one home, showcase for what people built. Kept deliberately small. A directory of plausible-sounding empty rooms costs a newcomer more attention than no directory, because it makes them guess which ones are alive; the listing now sorts active channels first and marks the declared ones. They are also published in ruv://federation/registry, so the set is discoverable without calling a tool, and named in channel_list's own description. Second fix in the same area: a malformed `c` tag value was being reported as a channel. My own tag probe left `ruflo-probe-c` sitting in the directory looking like a real room. Listings now drop anything that is not a well-formed pub:/prv: id. Verified live on 0.5.0 (revision ruflo-x-gateway-00010-nh4): all four appear, seeded so each opens with something to read, probe traffic gone, 16/16 tests. Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67 |
||
|
|
2b40aee077 |
fix(seraphina): stop reporting a truncated reasoning dump as coordination guidance (#3267)
Seraphina returned HTTP 200 and a well-shaped object with zero proposals, zero risks, and a `guidance` string that was the model thinking out loud mid-sentence. Measured on the live gateway: output_tokens 2000, reasoning_tokens 2000, stop_reason max_tokens. The tier had resolved to a reasoning model whose reasoning tokens are billed against max_tokens, so the entire budget went to private reasoning and the JSON answer was never emitted. The dangerous part was not the truncation, it was that it looked like success. An empty proposals array reads as "the swarm needs nothing", which is the one conclusion a coordinator must never reach by accident. Two changes. The budget goes to 8000 so there is room to answer after reasoning. And extractJson now reports whether it actually parsed, so askSeraphina can return an explicit `degraded` result naming the cause and a retry hint instead of passing raw reasoning off as guidance. Verified live before and after: before, stop_reason max_tokens with 0 proposals and reasoning text as guidance; after, stop_reason end_turn on anthropic/claude-sonnet-5 with 4 proposals, 4 risks and real guidance. Gateway suite 14/14, including two new guards — one that a truncated reply is marked degraded with empty guidance, one that a well-formed reply is untouched. Deployed as ruflo-x-gateway-00009-qxb. Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67 |
||
|
|
9576a1032b |
docs(federation): teach the skills and gateway docs about channels (#3263)
ADR-386 shipped the capability but the guidance still describes the old world: the open-federation skill has no channels at all and never names the canonical relay, and the gateway README and plugin manifest list a tool surface that is now three tools short. open-federation gains a Channels section with the two visibilities, the CLI verbs, and the three things worth saying before anyone trusts a private channel: metadata is not hidden, there is no revocation, and losing the key file loses the channel. It also records the tag trap — buzz-relay enforces NIP-29 group membership on `h`, so an h-tagged event publishes and then cannot be read back, which is why ruflo channels use `c`. claims gains a short section on scoping a claim stream to a channel, with the caveat that matters: a claim nobody outside the channel can read cannot arbitrate against one made outside it, so swarm-wide ownership stays on the open stream. Verified: marketplace validation passes over all 39 plugins, the manifest audit exits 0, and both skills still parse their frontmatter. Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67 |
||
|
|
90bff10f15 |
feat(x-gateway): public and private swarm channels (ADR-386) (#3261)
Coordination on x.ruv.io was one flat topic: every member read every message,
and there was no way to keep a stream separate — or confidential — without
standing up another relay tenant.
Channels are one tag on the existing events, in two visibilities:
public c=pub:<name> plaintext, any relay member reads it
private c=prv:<16 hex> NIP-44 v2 ciphertext, k=enc hides the message type
The private channel key is generated client-side and cached at
~/.ruflo/channels.json (0600). Access is granted by sealing that key to a
member's pubkey over ECDH and publishing it as a ChannelGrant only they can
open. The gateway holds no channel keys and cannot decrypt — a hosted gateway
that encrypted on your behalf would be a custodian of every private channel on
the service, which is the thing worth not building.
Not hidden, and said so in the ADR: the relay sees that a channel exists, its
opaque id, who published and when. There is no revocation either; removing
someone means rotating the channel.
The channel tag is 'c', not the obvious 'h'. buzz-relay already implements
NIP-29-style server-side group membership on : an h-tagged event publishes
fine and the matching REQ comes back CLOSED "restricted: not a channel member",
so the author cannot read back their own message. Measured every other
single-letter tag against the live relay — c/d/g/l/m/r/x/y/z are all indexed and
unrestricted.
Surfaces: gateway channel_list / channel_sync / channel_publish (public only,
admin-gated) + ruv://swarm/channels; client x_federation_channel_{create,grant,
accept,publish,read,list} and 'ruflo federation channel --action ...'.
Verified: gateway 12/12, CLI 11/11, and a live two-key E2E on wss://relay.ruv.io
— grantee reads the plaintext, an outsider key cannot decrypt, the ciphertext
does not contain the secret, and the gateway's own channel_sync returns
encrypted:true. Deployed as ruflo-x-gateway-00008-s6n.
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67
|
||
|
|
6396ff636c |
feat(x-gateway): open Nostr swarm federation gateway — MCP + ruv:// + ws proxy (deployed at x.ruv.io) (#3255)
* feat(x-gateway): MCP + ruv:// gateway for open Nostr swarm federation
New plugin `ruflo-x-gateway` — the service behind x.ruv.io. An MCP server
(Streamable HTTP at /mcp) that exposes ruflo swarm FEDERATION and CLAIMS over
an open, membership-gated, SIGNED Nostr relay (buzz-relay).
Why Nostr: every coordination message is a signed Nostr event, so authorship is
cryptographically verifiable and anyone the relay admits can participate — open
but secure. The relay gates membership + NIP-42 auth; no pre-pinning needed.
Surface:
- Tools: federation_identity / federation_join / federation_publish /
federation_sync, claims_issue / claims_release / claims_status.
- Resources: ruv://federation/registry, ruv://swarm/roster, ruv://claims/board.
- src/nostr-federation.mjs: NIP-42 authenticated connect, signed publish, and
verified fetch of #t=ruflo-swarm coordination events.
- src/server.mjs: node http server routing /, /health, /mcp (stateless
Streamable HTTP), plus the ruv:// resources.
- Dockerfile for Cloud Run; persistent Nostr identity at /data (0600).
Smoke-tested locally: server starts, /health + / respond, POST /mcp tools/list
returns the tool set over SSE. Federation tools require relay membership (by
design) — deployment wires membership + DNS (x.ruv.io) as follow-up.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67
* feat(x-gateway): stable identity via RUFLO_NOSTR_KEY_HEX (GCP secret) + read-only-FS fallback
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67
* feat(x-gateway): v0.2.0 — ws proxy, admin-gated writes, invite/admit tools, tests
Security model made explicit: /mcp is public, so every tool that writes
using the GATEWAY's own identity (join, publish, claims_issue/release,
invite_mint, admit) now requires `adminToken`, checked constant-time and
fail-closed (no configured token => all writes denied). Reads and ruv://
resources stay open. Users publish with THEIR OWN keys via invite->claim.
- src/ws-proxy.mjs: transparent WebSocket proxy so wss://x.ruv.io (and
/relay) fronts the Nostr relay; 500-conn cap, 502/503 on failure.
- src/relay-admin.mjs: mintInvite (NIP-98 POST /api/invites) and
admitMember (NIP-43 kind 9030) — the gateway holds relay admin role, so
the owner key never leaves GCP.
- src/security.mjs: per-IP token-bucket rate limit (60/min), 256KB body
cap enforced before buffering, security headers, timingSafeEqual admin.
- src/claims.mjs: owner-per-resource reducer extracted for testing.
- src/server.mjs: createGateway() factory (testable), stateless MCP.
- test/gateway.test.mjs: 8 tests — claims rules, gating, rate limit, body
cap, NIP-42 against a mock relay (verifies the signed challenge), routes.
Verified live before this commit: gateway pubkey admitted + promoted to
relay admin; invite minted and a fresh key self-joined via claim and
passed NIP-42 auth; deployed gateway publishes/reads over the relay.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67
* fix(x-gateway): surface canonicalRelay — relay verifies NIP-42 relay tag strictly
Empirical: auth through the wss://x.ruv.io proxy is ACCEPTED when the client
signs relay=<canonical relay URL> and REJECTED (verification failed) when it
signs relay=wss://x.ruv.io. Expose canonicalRelay + authNote at GET / and in
ruv://federation/registry so clients sign the right tag.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67
* ci: retrigger test-suite (ratchet flake; main green at
|
||
|
|
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 |
||
|
|
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 |
||
|
|
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
|
||
|
|
f9537a240b |
fix(ruflo-adr): skip .brain when walking for ADRs (#2911) (#2994)
* fix(ruflo-adr): skip .brain when walking for ADRs (#2911) ruvnet-brain clones ~50 external repos under .brain/repo/clones/, many carrying their own docs/adr/. Without .brain in SKIP_DIRS, findAdrs() picked up hundreds of foreign ADRs alongside the project's own — and since .brain sorts before docs, the project's ADRs landed dead last in the walk order. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(ruflo-adr): smoke step 22 should not hard-code the test-file count Was `-eq 3`, breaking every time a new *.test.mjs file is added to __tests__/ even when the new tests themselves pass. `-ge 3` keeps the "regression tests exist and run" guarantee without re-breaking on growth — caught by this PR's own new test file (skip-brain-dir-2911). Co-Authored-By: RuFlo <ruv@ruv.net> |
||
|
|
8e62a8bd8d |
fix(plugins): pin ruflo-core MCP launch path + register 3 missing marketplace plugins (#2975)
ADR-382 Part A:
- plugins/ruflo-core/.mcp.json launched the MCP server via a bare
`npx -y @claude-flow/cli@latest`, which always re-resolves to the
newest registry version regardless of any local `npm install
@claude-flow/cli` a user separately pinned. Add scripts/mcp-launch.cjs,
a small launcher mirroring hook-handler.cjs's resolveCliBinForHook()
(candidate list + dist/src/index.js existence guard against a
source-only marketplace checkout), and point .mcp.json at it via the
documented ${CLAUDE_PLUGIN_ROOT} substitution. Falls back to the
original npx @latest invocation only when no local install resolves.
- .claude-plugin/marketplace.json listed 35 plugins while plugins/ has
38 directories; register the 3 missing ones (ruflo-agntcy,
ruflo-bbs-federation, ruflo-business-pods), descriptions sourced from
each plugin's own .claude-plugin/plugin.json.
Ref: ADR-382, #2971
|
||
|
|
0b3cfb77d6 |
MetaHarness hardening: repair the dependency contract + strict sequential promotion evidence (#2956)
* feat(metaharness): repair dependency contract + strict sequential promotion evidence Item 1 — dependency & compatibility repair (the contract was silently broken): - @metaharness/darwin ^0.8.3, @metaharness/flywheel ^0.1.10, and @metaharness/radio ^0.1.0 are now explicit optionalDependencies of @claude-flow/cli (optional PEER deps are never auto-installed, so a clean ruflo install shipped with zero MetaHarness packages on disk) - check-metaharness-pins.mjs now searches dependencies, optionalDependencies, AND peerDependencies; UNDECLARED and PEER-ONLY (for installable pins) are fatal drift instead of silently passing; radio added to the watch list; --require-installed asserts the real published symbol contracts (RefineMutator, withSequentialEvidence, RadioBus, ...) - new scripts/metaharness-clean-install-test.mjs: installs the declared ranges into a pristine temp dir and asserts every advertised export; wired as a MANDATORY clean-install job in metaharness-ci.yml - doctor: new "MetaHarness declared packages" check FAILS (not warns) when a declared optional dep does not resolve at runtime; --component metaharness now runs upstream + declared-deps + integration checks - MH_DARWIN_PIN bumped 0.8.0 → 0.8.3 in lock-step with the declared range Item 2 — strict promotion evidence at the transaction authority: - receipts now carry task-level pairedOutcomes (taskId + per-task baseline/ candidate scores) behind heldOutDeltas; verifyFlywheelReceipt refuses rows that cannot reproduce their aggregate; evaluateFlywheelCandidate populates them; pre-existing receipts still verify byte-identically - new flywheel-sequential-evidence.ts: anytime-valid e-process over discordant paired outcomes (testing-by-betting) composed with per-candidate alpha allocation (alpha_k = alpha_total * 6/(pi^2 k^2)), so the family-wise false-promotion probability across an ADAPTIVE candidate stream is bounded by alpha_total = 5% - promoteFlywheelCandidate requires paired evidence by DEFAULT — aggregate- only receipts are refused, never silently downgraded (the upstream withSequentialEvidence fallback hole); explicit --allow-aggregate-evidence / allowAggregateEvidence escape hatch for pre-upgrade receipts; alpha spend is persisted per receiptId in the transaction state (looking spends alpha; retries reuse their index) - acceptance test: 1,000 null-improvement streams (20 adaptive candidates x 40 worst-case discordant pairs) — measured family-wise false promotion 0.6%, within the <=5% budget Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01FKWLVWewYaWmcUg2q8Y9wy * chore: gitignore node-compile-cache build artifact Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01FKWLVWewYaWmcUg2q8Y9wy * fix(ci): satisfy #2561 guard, dep-overlap audit, and smoke contract for metaharness optional deps Three CI failures from the dependency-contract repair, three fixes: - tilde-pin @metaharness/{darwin,flywheel,radio} (~0.8.3 / ~0.1.10 / ~0.1.0) per the ADR-150 anti-caret rule enforced by smoke step 17z73 — upstream is unstable, tilde absorbs patches but never minors - remove the three from peerDependencies/peerDependenciesMeta: an entry in BOTH optionalDependencies and peerDependencies crashes npm 11.x arborist on dedupe (#1147/#2018 dep-overlap audit); optionalDependencies alone is the declaration that actually installs - update the #2561 cold-startup guard per its own escape clause: budget 8 → 10 and drop @metaharness/darwin from the forbidden list — that entry dated from the pre-0.8 heavy-tree era; darwin@0.8.3 is 1.8M unpacked with ZERO dependencies and no lifecycle scripts (all three packages combined: 2.3M, 753ms cold install into an empty dir, measured 2026-08-10; the clean-install CI job re-verifies on every pin change) - smoke.sh 17h now accepts the componentMap ARRAY form ('metaharness': [checkMetaharness, ...]) introduced with the declared-packages doctor check Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01FKWLVWewYaWmcUg2q8Y9wy * fix(ci): remove apostrophe that broke the single-quoted guard script The #2561 guard runs as `node -e '...'` inside bash; an apostrophe in a comment ("guard's") terminated the quoted string and bash tried to execute the next // comment line (exit 126, '//: Is a directory'). Verified by executing the extracted run block end-to-end: all three checks OK, exit 0. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01FKWLVWewYaWmcUg2q8Y9wy * feat(flywheel): ADR-381 — alpha-stream governance, reset epochs, accept/v2+seq Completes the sequential-evidence governance PR #2956 opened: ADR-381 (new) records the decisions: - the ADR-322 promotion ledger IS the alpha stream (per project root) — receipt lineageIds default to fresh UUIDs and cannot scope the control - evidence epochs: resetSequentialEvidence({confirm, reason}) archives the spend into an append-only sequentialResets audit trail, EXPIRES every outstanding evaluated receipt (fresh-data enforcement — old-epoch evidence cannot be replayed against a reopened budget), and increments evidenceEpoch; surfaced as `flywheel evidence-reset --reason … --confirm` (CLI) and the metaharness_flywheel MCP op, both behind the same policy gate as promotion - accept/v2+seq: the ADR-176 generations loop now decides each bundle with a third conjunct — the e-process over the bundle's own embedded per-task holdout at alpha_k for its position in the attempts stream — with the full verdict recorded in the bundle and independently replayed by verifyReceiptBundle (v1 bundles keep v1 semantics; versions pin per bundle); flywheelStatus surfaces next test index / threshold / minimum pairs / remaining budget so exhaustion reads as plateau, not mystery - two-layer pre-flight: evaluateFlywheelCandidate annotates (never blocks) when the promotion holdout cannot clear the next threshold on a perfect sweep; promoteFlywheelCandidate refuses size-inviable receipts BEFORE allocating an alpha index — sample size is ancillary, so the refusal looks at no evidence and spends no budget Tests: reset semantics (archive/expire/epoch/fresh-promote), alpha-free size refusal vs alpha-spending e-process refusal, v2 promote/reject/replay including tamper + index-shopping detection, v1 compat, helper math anchors (min pairs = 9 at test 1), budget monotonicity. 82 tests green across the eight affected suites; tsc build clean. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01FKWLVWewYaWmcUg2q8Y9wy * fix(flywheel): close 3 concurrency/epoch gaps in ADR-381 sequential evidence Code review of this PR found three real correctness bugs that can silently violate the <=5% family-wise false-promotion guarantee that is the PR's own core deliverable: - flywheel-transaction.ts: resetSequentialEvidence's "fresh data only" guarantee only expired receipts already registered at reset time; a receipt whose evidence predates a reset but is registered afterward was silently admitted into the new, cheaper epoch (index shopping). Now tracks evidenceEpochStartedAt and promoteFlywheelCandidate refuses any receipt whose payload.issuedAt predates it, before allocating an alpha index. - harness-flywheel-generations.ts: the daemon generations loop computed its sequential-evidence testIndex via an unlocked loadAttempts(root).length+1 read before appending. Two overlapping runFlywheelGeneration calls on the same root could be assigned the same test index and spend the same alpha_k twice. The read-index -> build-bundle -> append critical section now runs under the same O_EXCL lock pattern flywheel-transaction.ts already uses. - harness-flywheel.ts: evaluateFlywheelCandidate's sequentialPreflight (backing the `promotable` flag) was computed from an unlocked snapshot taken before the async retrieval/scoring work, so it could go stale by the time promoteFlywheelCandidate allocates the real index under its own lock. Moved to the latest possible read (after receipt registration) to minimize the window; documented as advisory, since promoteFlywheelCandidate remains the sole authority. Added a regression test for the epoch-boundary refusal and a concurrency test proving two racing generations get distinct sequential test indices. Co-Authored-By: RuFlo <ruv@ruv.net> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
30d49c6fe2 |
fix(agntcy): pin @agntcy/slim-bindings to the confirmed-working alpha (#2888)
The SLIM maintainers confirmed (agntcy/slim#1916) they've moved off uniffi-bindgen-react-native onto @ubjs/core/@ubjs/node (compiled output, not raw TypeScript) in the alpha dist-tag (2.0.0-alpha.4+), not yet promoted to latest. Verified live: a real server bring-up + client connect + graceful shutdown against @agntcy/slim-bindings@2.0.0-alpha.5 succeeds under plain Node with zero errors — no ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING. Verified detectAgntcyRuntime() itself (not a mock) now returns configured: true when RUFLO_AGNTCY_SLIM_ENDPOINT is set, exactly as its existing graceful-degradation design predicted it would once the upstream bug was fixed — zero logic changes needed to that function. Pinned to the exact alpha version (not a caret range — deliberate for a pre-release channel) rather than latest, until the fix is promoted. Added detectAgntcyRuntime()'s optional packageName parameter so the "package genuinely not installed" fallback path stays covered by a real regression test now that the real package resolves. pnpm-lock.yaml regenerated (scoped to this one dependency). |
||
|
|
791d24b36f |
ADR-380 correction: real @agntcy/slim-bindings package + 2 upstream bugs filed (#2880)
* fix(agntcy): correct SLIM package name to the real @agntcy/slim-bindings ADR-380's original scaffold guessed a placeholder package name (@claude-flow/agntcy) and concluded no SLIM SDK existed anywhere — that check only tried guessed names. The real SLIM Node.js bindings are published as @agntcy/slim-bindings (v1.4.1 stable), documented for exactly this plain-Node use case. It currently fails to load, but for a specific, verified, upstream reason rather than "doesn't exist": a transitive dependency (uniffi-bindgen-react-native) ships raw, uncompiled TypeScript as its package.json "main" with no build output and no exports field — works inside a bundler (Metro, its primary React Native use case), not plain Node require/import. Reproduced and confirmed the exact failure (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING, not MODULE_NOT_FOUND) via a real install in isolation. Filed upstream: - agntcy/slim#1916 (downstream impact — their own README's plain-Node example is currently broken) - jhugman/uniffi-bindgen-react-native#422 (root cause) detectAgntcyRuntime()'s existing graceful-degradation design already handles this correctly with zero logic changes needed: its catch-all branch (anything other than MODULE_NOT_FOUND) surfaces the real error and falls back to local transport — verified this is exactly what happens today. Once the upstream packaging bug is fixed, this same code path starts succeeding with no changes needed here. Added @agntcy/slim-bindings to optionalDependencies (real package now, matching ADR-150's optional-only rule ADR-380 §1 follows). * docs(adr): ADR-380 update section — correct the false 'no packages exist' claim Documents both real corrections in-place (this repo's convention for a still-Proposed ADR) rather than silently rewriting history: real @agntcy/slim-bindings and agntcy-dir packages exist; Directory is now live-integrated; SLIM is blocked by a specific, filed, upstream packaging bug, not a missing SDK. Synced to the plugin-bundled copy. * fix(ci): regenerate v3/pnpm-lock.yaml for the new @agntcy/slim-bindings optionalDependency 'pnpm install --frozen-lockfile' (what CI actually runs) failed after the previous commit's hand-edit to package.json, since pnpm-lock.yaml was never regenerated: ERR_PNPM_OUTDATED_LOCKFILE, cascading into ~20 unrelated-looking CI job failures (every job that installs deps first). Used a scoped 'pnpm install @agntcy/slim-bindings --lockfile-only --save-optional' (not a full lockfile regeneration) specifically to avoid an unrelated, pre-existing issue: @claude-flow/codex is pinned to 3.0.3 in package.json but only 3.0.2 is actually published, which breaks a full-tree re-resolution but doesn't block a scoped single-package update. Verified 'pnpm install --frozen-lockfile' now succeeds cleanly from the correct v3/ workspace root (the one CI's failing jobs were actually running from — this repo has two separate pnpm workspaces, root and v3/, and I'd initially checked the wrong one). As a side effect, the frozen-lockfile install's postinstall step rebuilt @claude-flow/security's stale dist/, which also cleared the pre-existing 19 unrelated tsc errors noted throughout this session's earlier verification passes — tsc --noEmit is now fully clean, not just clean of agntcy-related errors. |
||
|
|
1e64c2fd3b |
ADR-378/379/380: npm Trusted Publishing, statusline segments, AGNTCY/Outshift runtime integration (#2879)
* docs(adr): ADR-378/379/380 — npm trusted publishing, statusline segments, AGNTCY runtime integration Renumbered from the original 322/323/324 (chosen when this branch was based on an older main) — origin/main has since merged real, Accepted ADRs at those numbers (322=metaharness-flywheel-integration, 323=typed-memory-provenance, 324=agentic-policy-engine-codex-swarm). Content unchanged from the original commit, only the numbers and filenames moved to the next free slot after ADR-377. ADR-378: npm Trusted Publishing (OIDC) for CI/CD release automation, replacing the standing NPM_TOKEN class with per-run short-lived tokens; pairs GCP WIF for the helpers-signing-secret fetch instead of a static service-account key. ADR-379: optional context/session/week usage segments and extra statusline tip lines for .claude/helpers/statusline.cjs, following the existing CONFIG.hideCost toggle precedent; session/week/effort default OFF pending a stdin-schema verification spike. ADR-380: AGNTCY/Outshift runtime integration (SLIM transport, CASA enforcement, IOC Layer 9 coordination events) as an optional, removable augmentation per ADR-150's pattern. Companion to metaharness repo ADR-237 (agent/adr-237-agntcy-outshift-integration branch), which owns the build-time half (identity, OASF export, observability). * feat(agntcy): ADR-380 scaffolding — CASA envelope, SLIM/IOC CLI verbs, Rust crate Renumbered from the original ADR-324 (origin/main has since merged a real, unrelated, Accepted ADR-324 — see the companion docs commit). Content and behavior unchanged; only the ADR number/filename and every in-file reference to it moved to ADR-380. Implements the buildable-now portion of ADR-380 (AGNTCY/Outshift runtime integration) as optional, removable augmentation per ADR-150's pattern. No AGNTCY/SLIM/Outshift npm or crates.io packages exist yet under any plausible name (verified live) — every network touchpoint is a clearly-logged stub gated behind explicit config, never a fake success. - plugins/ruflo-agntcy/: CASA envelope schema (Zod) + deterministic compiler + enforcement gate (checkAuthorization, deny-by-default) + bypass-attempt tests; Ed25519-signed CASA decision receipts (.swarm/casa-receipts.jsonl); AGNTCY OTel span attribute constants this repo owns (coordination.episode, authorization.decision). - v3/@claude-flow/cli/src/commands/agntcy/: `ruflo transport use slim`, `ruflo agent publish`, `ruflo swarm join <namespace>` — wired into the top-level command registry and into agent.ts/swarm.ts's existing subcommand arrays (merged alongside main's concurrently-added pheromoneCommand); all exit 0 with a clear message when AGNTCY/SLIM isn't configured (RUFLO_AGNTCY_SLIM_ENDPOINT unset). - v3/crates/ruflo-agntcy/: Rust CasaEnvelope + check_authorization mirroring the TS enforcement logic exactly, LocalTransport (real, working, in-process) + SlimTransport stub behind a non-default `slim` Cargo feature. Added to the workspace; also fixed a pre-existing `cargo check` blocker (v3/plugins/gastown-bridge's nested [workspace] table conflicting with the parent workspace) by moving it from `members` to `exclude`. Adversarial security review (as ADR-380 §3 requires before trusting this gate) found and this commit fixes two real bugs in the first pass: - enforce.ts trusted the erased TS type instead of validating its own input — a non-array `allow`/`deny` collapsed `.includes()` into JS's substring-match overload, and a malformed `now`/`expires_at` silently skipped the expiry check instead of failing closed. checkAuthorization now runs CasaEnvelopeSchema.safeParse() on entry, requires an explicit timezone offset on all timestamps, and treats any unparseable timestamp as expired. - compile.ts's keyword table matched bare "push"/"publish"/"release"/ "deploy" with no contextual gate, false-permissively granting git.push/deployment.create for objectives like "push notification integration" or "review the release notes". Patterns now require a co-occurring contextual noun (git/commit/branch for push; service/app/production/version/etc. for deploy). Deferred to a follow-up pass (documented, not silently dropped): plugins/ruflo-agntcy has no package.json yet (this repo's plugins/ ruflo-* convention is Claude Code plugin content, not an npm workspace package — unlike @metaharness/agntcy's sibling-package convention on the metaharness side), so no optionalDependencies entry was added anywhere; adding one now would reference a package shape that doesn't exist and break installs. Companion: metaharness repo ADR-237 (agent/adr-237-agntcy-outshift- integration branch) — build-time half of this integration. * docs(agntcy): fix stale companion-ADR cross-reference (metaharness ADR-237 -> ADR-240) Metaharness's own numbering collided too (ADR-237 was already taken by evals-math-live-gsm8k-fast-domain on their main) and got renumbered to ADR-240 — this repo's references to it were stale. * fix(agntcy): add missing allowed-tools frontmatter to agntcy-status skill CI's fleet-wide SKILL.md frontmatter audit correctly flagged this — no implicit 'all tools' is allowed. Scoped to Read since this is a scaffolding stub (not yet implemented) that will only ever need to inspect local package.json/config state, never execute commands. |
||
|
|
fabcc9261a |
fix(hooks): Codex hooks.json schema + PreToolUse verdict compat (#2857)
Codex's plugin hook-manifest loader accepts only `description` and
`hooks` at the top level and hard-rejects the rest of the manifest on
anything else. plugins/ruflo-core/hooks/hooks.json and
plugins/ruflo-cost-tracker/hooks/hooks.json carried `_note` /
`_platform_note` documentation fields, so a fresh Codex install of
ruflo-core@ruflo (still true as of the currently-published 3.32.38 /
ruflo-core 0.2.5) fails to load the plugin at all with:
failed to parse plugin hooks config .../hooks.json:
unknown field `_note`, expected `description` or `hooks`
Fold the doc content into `description` (content preserved, no hook
commands changed) for both marketplace-listed manifests, and add a
strict top-level-key check to
scripts/audit-plugin-hooks-cross-platform.mjs so this class of
regression fails CI going forward. `.claude-plugin/hooks/hooks.json`
is POSIX-only and not marketplace-distributed (not what Codex fetches
for ruflo-core@ruflo); its doc fields are folded too but its
audit-script flags (`_platform`, `_legacy_unaudited_shim`) are kept.
Second, deeper bug once the manifest loads at all: `modify-bash`/
`modify-file` PreToolUse hooks always echo Cursor's
`{"permission":"allow"}` verdict. Codex's own strict output schema
(additionalProperties: false) rejects that shape outright and reports
"hook returned invalid pre-tool-use JSON output" on every single tool
call — verified directly against the real parser
(codex-rs/hooks/src/engine/output_parser.rs,
codex-rs/hooks/src/events/pre_tool_use.rs) and its generated JSON
schema. Empty stdout is the one input Codex's parser treats as
no-opinion/implicit-allow with no error. `isCodexPluginHost()` (added
for #2816, ruflo-core 0.2.5) already detects Codex via its
PLUGIN_ROOT/PLUGIN_DATA env vars and correctly suppresses the verdict
in that case — harden it with a `turn_id`-based fallback (a documented
Codex-only field always present in Codex's PreToolUse input JSON, per
codex-rs/hooks/schema/generated/pre-tool-use.command.input.schema.json)
so detection doesn't depend solely on those env vars being set.
Bump ruflo-core 0.2.5 -> 0.2.6 and ruflo-cost-tracker 0.26.2 -> 0.26.3
so Codex's per-version plugin cache invalidates and re-fetches the
fixed manifest instead of serving a stale cached copy indefinitely.
Fixes #2855, #2856.
|
||
|
|
401e02d511 |
fix: complete reports and consistent initialization for v3.32.37 (#2851)
* fix(metaharness): preserve readiness verdict payloads * test(metaharness): cover blocked genome verdicts * fix(adr): parse bullet metadata and relationships (#2659) * fix(adr): align adr-create with AgentDB schema (#2651) * fix(adr): make index updates idempotent (#2660) * fix(memory): bound session-end graph consolidation (#2628) * fix(memory): align active row visibility (#2652) * fix(memory): honor database path during init * fix(hooks): keep all shim fallback tags aligned * fix(codex): omit unbacked full-template skills * fix(init): generate complete native dual projects * test(memory): isolate path and legacy-row regressions * chore(release): prepare v3.32.37 |
||
|
|
67ff9898c0 |
fix: resolve current runtime and verification defects for v3.32.36 (#2850)
* fix(cli): make runtime status and learning signals truthful * fix(metaharness): accept padded scan severities * fix(codex): make core hooks and status skill native * fix(security): make witness verification hermetic * fix(metrics): expose grounded learning outcomes * fix(hooks): deduplicate project and plugin events * fix(verification): preserve security audit payloads * fix(runtime): stabilize dual memory skills and MCP schemas * fix(cli): harden helper signing and reflexion health * test(release): align helper and container invariants * chore(release): prepare v3.32.36 * test(funnel): align gates with cold-start seed pool * fix(ci): document MCP environment precedence |
||
|
|
b08246b04a |
chore(release): bump to 3.32.27 (#2823)
* chore(release): bump to 3.32.27 * chore(release): publish policy security dependency * chore(release): sign 3.32.27 helper manifest * ci: harden policy release gates |
||
|
|
495a451cf6 |
feat: add ADR-324 agentic policy engine (#2822)
* feat(memory): typed provenance in AgentDB (ADR-323) Corrects and implements the 2026-07-28 dream-cycle research proposal (PR #2804, closed as superseded) for typed memory provenance in AgentDB, addressing MemIR/MemSyco-Bench's "provenance-role collapse" and memory-induced sycophancy findings. The dream-cycle PR's proposed schema change targeted the wrong table (`vector_indexes`, which is per-namespace HNSW index metadata, not per-entry) and its filename collided with the already-merged `ADR-322-metaharness-flywheel-integration.md` (#2817) — both corrected here as ADR-323 against `memory_entries`, the real per-record table. - memory-initializer.ts: `provenance_type` column (user_claim | agent_output | system_observation | tool_result | unknown, default unknown) on `memory_entries`, migrated for existing DBs via `ensureSchemaColumns()`. `storeEntry()`/`searchEntries()` accept and validate `provenanceType`/`provenanceFilter`. - memory-bridge.ts: same wiring for the native better-sqlite3 AgentDB v3 path (`bridgeStoreEntry`/`bridgeSearchEntries`). - commands/memory.ts: `memory store --provenance <type>`, `memory search --provenance-filter <types>` (comma-separated, OR semantics). - mcp-tools/memory-tools.ts + cli-core/mcp-tools/memory-defs.ts: `memory_store`'s `provenance_type` param, `memory_search`'s `provenance_filter` param (kept in sync across both tool-def sets). Found and fixed during implementation: an initial design skipped the RaBitQ/HNSW acceleration paths entirely whenever a provenance filter was requested (neither carries provenance_type on their candidates). This reproducibly crashed the CLI process on exit (a libuv assertion on Windows) after printing correct results. Fixed by never skipping those paths — RaBitQ now fetches provenance_type in its existing per-candidate query, HNSW does one batched (namespace, key) lookup afterward — eliminating the crash across repeated runs. See ADR-323's "Implementation note" for detail. 7 new end-to-end tests (adr-323-memory-provenance.test.ts), including a repeated-run regression guard for the crash. Full existing memory suite re-verified with no regressions (one pre-existing, environment- specific #2558 test failure confirmed identical against unmodified main — better-sqlite3's native bridge is unavailable in this dev environment, unrelated to this change). Closes the follow-up from dream-cycle PR #2804 (closed as superseded). Co-Authored-By: claude-flow <ruv@ruv.net> * fix(memory): enforce provenance across retrieval paths * feat: add agentic policy engine * fix: align Codex MCP integration * fix: keep MetaHarness dependencies optional * fix: declare MetaHarness as optional peers * fix: sync workspace lockfile * fix: document policy tool selection guidance * test: cover governed in-process flywheel * ci: make MetaHarness absence drill deterministic |
||
|
|
810b13dcd6 |
fix: tracker-sweep 2026-07-26 (v3.32.10) — 9 bugs + promo seed + follow-ups (#2788)
* fix: tracker sweep 2026-07-26 — 9 bug fixes + promo seed Closes: #2770 #2774 #2776 #2777 #2781 #2782 #2785 #2786 (Codex follow-up) Refs: #2775 (memory upsert semantics) - Statusline promo row blank on new installs — restored cold-start local seed pool that ADR-311 had emptied. Remote Cognitum-served pool (funnel.ruv.io/v1/messages) remains authoritative via eligibleMessagesFromPools (remote wins by id); seed only fills the ≤ 60s cold-start window before first successful remote fetch. - #2777 — ruflo init no longer imports the entire ruvnet/ruflo repo (97 MB, 384 SKILL.md) into .agents/skills/ruflo/. Materializes single platform SKILL.md; detects "bloated" prior install and re-materializes. - #2781 — ruflo-adr adr-index no longer silently drops ADR data: status regex accepts "- **Status**: proposed"; single-line and wrapped relation lines parse fully; CLI_CORE=1 warns and unifies namespace. - #2770 — Windows: browser-session MCP tools + two init execFileSync npx sites now set shell: process.platform === 'win32' so cmd.exe resolves npx.cmd. POSIX behavior unchanged. - #2782 — WorkerDaemon.saveState + autopilot-state.saveState + appendLog no longer race on shared .tmp filename; all three call writeFileAtomic (pid + timestamp + random-suffixed temp). - #2785 — ruflo hooks post-task accepts --task/-t and --store-results flags matching CLAUDE.md-documented usage; routing outcomes finally persist to the namespace hooks_metrics reads. - #2786 — AgentDB no longer silently fails to initialize under CLAUDE_FLOW_ENCRYPT_AT_REST=1. New getAgentDbPath() returns agentdb-memory.db in the same directory as memory.db, so the ControllerRegistry native better-sqlite3 opens a distinct file from the sql.js CRUD writer's encrypted memory.db. - #2776 — Statusline security STALE/IN_PROGRESS branches reachable via local overlay recomputing freshness on every render from .claude/security-scans/scan-*.json. Env: RUFLO_SCAN_STALE_HOURS (24), RUFLO_SCAN_PENDING_CAP_MIN (30). STALE renders dim gray. - #2774 — Codex MCP generator registers dedicated stdio server binary claude-flow-mcp instead of the management CLI ruflo mcp start that never answers initialize. All 7 wrong-command sites fixed (initializer.ts + 6 template sites in generators/config-toml.ts + 2 in migrations/index.ts + generators.test.ts:653 assertion). - #2775 — Memory store to existing key no longer dead-ends: bridgeStoreEntry uses INSERT ... ON CONFLICT with tombstone auto-resurrect; UNIQUE returns typed error instead of null (no more misleading demotion into #2735 guard); bridgeDeleteEntry runs wal_checkpoint(PASSIVE); CLI memory store --upsert default materialized locally (parser.applyDefaults bug). - Stale ruflo-rebrand assertion in codex/tests/generators.test.ts:192 updated Co-Authored-By: claude-flow → ruflo-bot. - #2786 fix-2/3 (hooks_metrics reads dead .claude-flow/memory/store.json; bridgeRecordFeedback calls non-existent agentdb API) — architectural rewire, needs separate PR. - #2775 defect #3 root cause (parser.ts applyDefaults doesn't apply subcommand-declared defaults) — flagged by agent, other subcommands affected too. - #2775 memory-tools.ts MCP tool schema still defaults upsert:false (parity fix flagged). Co-Authored-By: RuFlo <ruv@ruv.net> * fix(parser): applyDefaults now walks subcommand + command options (#2775 follow-up) Previously only `this.globalOptions` were walked. Any subcommand-declared `default: <value>` silently dropped and the action handler received `undefined`. The immediate victim was `memory store --upsert` (per #2775), but the bug is generic — every subcommand that relies on a per-flag default was affected. Fix: applyDefaults now accepts optional command + subcommand, walks all three layers narrow-to-broad (subcommand > command > global), and the first layer to supply a value wins (because it only writes when the flag is `undefined`, so earlier writers stick). Parser tests: 52/52 pass. Build: tsc exit 0. Refs: #2775 (root cause behind the CLI --upsert workaround already in `commands/memory.ts`; the workaround is now redundant but harmless). Co-Authored-By: RuFlo <ruv@ruv.net> * fix(mcp): memory_store defaults upsert=true for CLI parity (#2775 follow-up) The CLI `memory store` command defaults `--upsert=true` (issue #2594, fix |
||
|
|
bf8e766875 |
fix(witness): re-anchor #1862 marker + refresh manifests after #2721
The #2721 hooks.json rewrite legitimately moved the -f/-s CLI-flag construction logic out of hooks.json and into scripts/ruflo-hook.cjs, which drifted the #1862 witness marker (it cited the literal bash string that no longer exists). Re-anchored the marker to the new location: plugins/ruflo-core/scripts/ruflo-hook.cjs, marker "['-f', String(file), '-s', 'true']" -- the actual flag-construction line that replaced the old bash string. Also did a full witness regen for all three manifests (linux/macos/ windows) against a properly built tree, refreshing the 14 other entries that had gone stale (sha256 no longer matching current file content, though their marker text was still verified present -- "drift" per verify.mjs's own terminology, not a real regression: only "regressed" i.e. marker text actually missing counts as a failure). Confirmed those 14 are pre-existing on main, unrelated to this PR (zero diff against origin/main for every file they cite), and were already recorded as unverified in the previously-committed manifest -- this regen doesn't mask or fix any of them, it just brings sha256 current. All three manifests now show pass=117/drift=0/regressed=0/ missing=0. (Regenerated with an OS-aware variant of regen.mjs's own algorithm, since the tool's osDir() stamps whatever OS it's actually run on -- regenerating linux/macos manifests from this Windows machine via the CLI as-is would have mislabeled their "os" field.) Also fixed plugins/ruflo-cost-tracker/scripts/smoke.sh's step 28b, which asserted literal substrings (track.mjs, TRACK_QUIET=1, || true) directly in hooks.json -- true before #2721, no longer true now that those live one level down in ruflo-hook.cjs. Updated to verify the indirection is actually wired (hooks.json references ruflo-hook.cjs, which references track.mjs/TRACK_QUIET/a resilient always-exit-0 done()). |
||
|
|
b68ad4ccba |
fix(plugins): make ruflo-core/ruflo-cost-tracker hooks Windows-native (#2721)
Both plugins' hooks.json wrapped every command in `/bin/bash -c '...'`,
which fails outright on native Windows (no such path) -- Codex/Claude
Code report "PreToolUse hook (failed) -- exit code 1" on every tool
call. The `_platform: posix` / "ruflo init overrides this on Windows"
claim in both files was never actually true: Claude Code merges
plugin-declared hooks additively with any init-generated
.claude/settings.json, it doesn't replace them, and there's no `ruflo
init` step at all in the reported Codex marketplace install flow.
Fix: every hook command is now a `node -e` bootstrap that resolves
plugins/*/scripts/ruflo-hook.cjs from process.env.CLAUDE_PLUGIN_ROOT
inside Node -- no shell env-var expansion (${VAR} vs %VAR%), so the
exact same command string runs unchanged on Windows/macOS/Linux.
ruflo-core's ruflo-hook.cjs (previously a full port of ruflo-hook.sh
that existed on disk but was never referenced by hooks.json) gained:
- JSON parsing of the hook event from stdin (replaces jq) for
post-command/post-edit, deriving the same CLI flags the bash
version computed
- the PreToolUse permission-allow stdout echo Cursor's stricter
contract requires (previously only the bash wrapper's trailing
printf did this)
- precompact-manual/precompact-auto guidance text (previously plain
bash echoes, no CLI call)
- a real Windows shell-quoting fix: shell:true with an args array
does NOT quote array elements, so "echo hi" silently truncated to
"echo" and a heredoc's `<<` errored as unexpected -- skip the
shell entirely for `node` invocations (never a .cmd shim, so
CreateProcess gets the argv array byte-for-byte)
cost-tracker's existing ruflo-hook.cjs (already correct, just
orphaned) needed no logic changes, only wiring.
Also:
- corrected the false "_platform_note" claims about ruflo init
overriding plugin hooks
- hardened scripts/audit-plugin-hooks-cross-platform.mjs: a
POSIX-exempt hooks.json now must actually reference its sibling
.cjs shim, not just have one sitting on disk unreferenced (which
is exactly the shape cost-tracker shipped in undetected)
- added windows-latest to the plugin-hooks-smoke CI matrix (it was
ubuntu/macos-only because the old bash-based hooks.json couldn't
run on Windows at all) and rewrote test-hooks.mjs to drive hooks.json's
literal command strings via `shell: true` -- exactly how Claude
Code/Codex invoke them -- instead of wrapping everything in an
explicit `bash -c` that could never have caught this bug
- flagged (not fixed) a separate, currently-published, actively
maintained plugin package (.claude-plugin/ + plugin/, the older
"claude-flow" plugin, not listed in the ruflo marketplace) with
the same underlying bug via jq/xargs pipes instead of bash --
explicitly marked _legacy_unaudited_shim so the hardened audit
doesn't silently regress on out-of-scope work
Verified locally on native Windows (this fix's actual target
platform): all 17 ruflo-core hook cases pass, all 3 cost-tracker
cases pass, the existing 12-case smoke-ruflo-hook-cjs.mjs passes
unchanged, both hook-command audits pass clean.
Fixes #2721
|
||
|
|
1fb874005c | test(plugins): preserve standalone MCP catalog assertions | ||
|
|
5e66f065e9 | test(plugins): align namespace and stable hook shims | ||
|
|
e332689b8c | fix(release): harden hooks, statusline, security, and plugin MCP integration | ||
|
|
49b36d7acd | fix(cost): preserve memory JSON across Windows npx | ||
|
|
25b16cfdf2 | test(cost): confirm npx installs in fixtures | ||
|
|
72875da937 | chore(release): prepare stable Ruflo 3.32.1 | ||
|
|
d20f1323b1 | fix(codex): ship stable Windows-safe Ruflo integration | ||
|
|
dc015986dc |
feat(adr): reconcile deleted ADRs — hard-delete primitive + drop-and-rebuild reindex (#2668)
* feat(adr): reconcile deleted ADRs — hard-delete primitive + drop-and-rebuild reindex Closes #2666. `adr-index` can only add/upsert; deleting an ADR file (or a relation line from a surviving one) left an orphan row in `adr-patterns`/ `adr-edges` forever, and `adr-verify` certified the result as healthy since an orphan has no dangling ref and forms no cycle. Root cause traced into @claude-flow/cli itself, not just this plugin: `memory delete` only ever soft-deletes (status='deleted'), so the row keeps occupying its UNIQUE(namespace, key) slot and blocks a later non-upsert re-store (#2652). `memory cleanup` reaps by age/TTL/quality, not by "does the source still exist". Neither the AgentDB v3 bridge nor the sql.js fallback exposed any hard, namespace-scoped delete anywhere in the CLI/MCP surface. Separately, import.mjs/verify.mjs's spawnSync calls never passed `cwd`, so ADR_ROOT scanned the right files but could write to the wrong `.swarm/memory.db`. CLI (@claude-flow/cli): - memory-bridge.ts: bridgePurgeNamespace() — real DELETE FROM memory_entries WHERE namespace = ? against the live bridge SQLite handle, routed through the existing MutationGuard/AttestationLog hooks. - memory-initializer.ts: purgeNamespace() (bridge-then-sql.js-fallback, same shape as deleteEntry) + withMemoryDbLock() (new <dbPath>.lock O_EXCL advisory lock, same stale-takeover pattern as global-ai-budget.ts — protects against a second concurrent purge/delete on the same file; does not close #2621, which needs every writer to adopt the same lock). - commands/memory.ts: `memory purge --namespace <ns> --force [--dry-run]` — requires --namespace explicitly (no default), confirms unless --force. - package.json: sql.js was already imported directly by memory-initializer.ts's fallback path but never declared as a dependency — worked by accident under npm's loose hoisting, silently broken under this repo's strict pnpm workspace (confirmed: unmodified deleteEntry/ storeEntry fail identically). Declared explicitly. Plugin (ruflo-adr): - scripts/reindex.mjs (new) — purge both namespaces via `memory purge`, rescan every ADR currently on disk, store fresh, then re-list and assert the count equals files-on-disk (a storedRecords tally can't see a concurrent-writer clobber of the purge; a fresh recount can). Exits non-zero on failure. Full-namespace wipe rather than a selective orphan diff, since adr-edges keys always carry a fresh timestamp-rand suffix and are never deduplicated across imports — a selective pass would still leave duplicate edges accumulating for ADRs that do still exist. - scripts/lib/parse-adrs.mjs (new) — findAdrs/parseAdr and sub-parsers extracted from import.mjs so reindex.mjs doesn't duplicate ~150 lines of dual-format parsing; import.mjs shrank 322 -> 156 lines, same dry-run output verified before/after (530 ADRs, same status/relation counts). - import.mjs/verify.mjs: every npx subprocess call now passes `cwd: ROOT`. - New `/adr-reindex` skill; adr-index/adr-verify SKILL.md cross-link it. - ADR-0002 records the decision. plugin.json 0.3.0 -> 0.4.0. Verified end-to-end against a scratch repo + local CLI build: 2 ADRs indexed, one deleted from disk, re-run adr-index -> orphan survives (bug reproduced exactly as filed), run adr-reindex -> orphan gone, post-condition OK, exit 0. New tests: __tests__/memory-purge-namespace-2666.test.ts (8 cases) — namespace-scoped hard delete, cross-namespace isolation, the specific #2652 regression (non-upsert re-store of a purged key must succeed), invalid-namespace rejection, lock acquire/release/stale-takeover/ serialization. smoke.sh: 15 -> 21 checks. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(ci): restore sql.js lockfile entry lost during local drift verification A `git stash` + reinstall done while investigating unrelated pre-existing pnpm-lock.yaml drift (verifying it wasn't caused by this branch) ran with @claude-flow/cli/package.json temporarily reverted, regenerating v3/pnpm-lock.yaml without the sql.js entry the prior commit added. `git stash pop` restored package.json but not the lockfile state written while the stash was active, so the previous commit's lockfile silently lost that entry — CI's `pnpm install --frozen-lockfile` correctly caught the drift (ERR_PNPM_OUTDATED_LOCKFILE). Re-ran `pnpm install` with package.json in its correct state; frozen-lockfile install now passes clean locally, matching CI's exact step. Co-Authored-By: RuFlo <ruv@ruv.net> |
||
|
|
a02b46e4fb | fix(verification): unblock ADR-104 and witness source checks | ||
|
|
1d5b06e4a2 |
fix(federation): unblock main — TS shim types + dispatcher import + witness re-sign (#2604)
* fix(federation): sync shim types + guard optional close() to unblock Build V3
Fixes 7 TS errors on main that block Build V3 across all 3 platforms:
- src/transport/midstream-aware-loader.ts:39-52 — local AgentMessage/
AgentTransport shim was over-minimized by #2578. Restored required
{id, type, payload, metadata?} on AgentMessage and made send/onMessage
2-arg (address, message) to match how plugin.ts actually calls them.
- src/transport/midstream-aware-loader.ts:84 — cast dynamic import()
through `unknown` since upstream's exported AgentTransport carries a
richer InboundMessageHandler surface we intentionally hide behind the
minimal shim.
- src/plugin.ts:481 — this.transport.close is optional (`?`) on the
shim; guard with optional chaining `close?.()`.
Local tsc build clean after fix.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(federation): dispatcher import + re-sign witness manifests (3.25.3 drift)
Two more preexisting main-red root causes, in the same fix:
(1) inbound-dispatcher.ts:22 still imported `AgentMessage` from
`agentic-flow/transport/loader` — the exact phantom subpath #2578
eliminated in midstream-aware-loader.ts. Repointed to the local
shim. This is the residual site #2578 missed; the graph schema smoke
(ADR-130 P1) failure was a cascade of this same TS type collision.
(2) Re-signed verification/{linux,macos,windows}/manifest.md.json via
plugins/ruflo-core/scripts/witness/regen.mjs. The 3.25.3 release
(PR #2602) edited the ADR-104-transport `desc` string in all 3 per-OS
manifests but did not re-run regen.mjs, so the stored
integrity.manifestHash no longer matched sha256(JSON.stringify(
manifest)) and Witness verify exited 1 across all 3 platforms.
Manifests now verify cleanly (117 pass, 0 drift, 0 regressed).
Note: witness/regen.mjs takes --manifest + --history and re-signs
deterministically from manifest.gitCommit; already wired but the
3.25.3 release process skipped it.
Local verify: pass=117 drift=0 regressed=0 missing=0 on all 3 OSes.
Local tsc build: clean.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(hooks): PreToolUse hook emits valid JSON verdict (#2613)
Cursor imports third-party Claude Code hooks under its stricter
`preToolUse` contract, which requires stdout to be a valid JSON
permission verdict (`{"permission":"allow"|"deny"|"ask"}`) and
fail-closes on any other text. The prior PreToolUse commands ran the
ruflo hook shim which prints telemetry to stdout — Cursor then blocked
every Bash/Write/Edit tool call across every workspace.
Fix:
1. ruflo-hook.sh (all three copies) — redirect stdout to /dev/null in
addition to stderr. Claude Code doesn't consume this stdout either,
so silencing it is a pure cleanup with no functional cost.
2. hooks.json PreToolUse commands — after invoking the shim, always
`printf` a well-formed `{"permission":"allow"}` verdict. Valid for
both Claude Code's JSON-hook contract and Cursor's stricter
preToolUse contract, so telemetry keeps working while nothing is
blocked. Also guard `${CLAUDE_PLUGIN_ROOT:-}` so an unset env var
collapses to the safe empty branch instead of a bash parse error.
Simulated Cursor invocation (CLAUDE_PLUGIN_ROOT unset) now emits:
{"permission":"allow"}
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(ci): indirect optional-dep imports in cli — @ruvector/*, @metaharness/router (#2608)
Same install-safety anti-pattern as #2586's prime-radiant fix, now
surfaced in @claude-flow/cli once plugin-agent-federation's TS2307
stopped masking it (
|
||
|
|
11f34ec947 |
chore(release): 3.25.3 — 10 fixes + CI guards (#2602)
* fix(ci): neural status ReasoningBank row shows Empty despite persisted patterns (#2575) Derive the Status cell from the same count Details displays so the label matches the number (7798 patterns → Active) instead of the stale in-memory handle. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(ci): default memory store --upsert=true to fix store→delete→store UNIQUE violation (#2594) memory_entries has UNIQUE(namespace, key) that does not exclude soft-deleted rows, so a store→delete→store cycle reliably hits UNIQUE constraint failed when --upsert defaults to false. storeEntry() already honors upsert via INSERT OR REPLACE; flipping the CLI default closes the footgun without touching schema or plumbing. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(ci): remove reverse-substring collision in GAIA isAnswerCorrect (#2566) The reverse check normExpected.includes(normModel) scored fragmentary model answers as correct whenever they normalized to any substring of the expected answer (e.g. "a" vs "Paris, France" → true), inflating GAIA scores via normalization collision. ADR-169 R1 forbids this vector. Forward-substring and numeric-tolerance paths remain. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(ci): skip onnxruntime-node postinstall to unblock v3 memory smoke (#2590) onnxruntime-node's postinstall fetches a GPU nupkg from nuget.org which ETIMEDOUTs from GitHub runners, taking pnpm install down. Skip it via neverBuiltDependencies (CPU prebuilds ship in the npm tarball, so runtime is unaffected) and pass --ignore-scripts in the memory smoke workflow as a belt-and-braces safety net. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(ci): drop verifyMemoryInit writeback that races better-sqlite3 handle on Windows (#2596) sql.js verification holds an in-memory DB copy; writing it back via atomic rename fights the open better-sqlite3 WAL handle owned by ControllerRegistry / repairVectorIndexes, producing EPERM on Windows. Verification is read-only — close and discard the copy instead. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(ci): auto-heal doctor Learning Bridge sidecar on plain run (#2599) Co-Authored-By: RuFlo <ruv@ruv.net> * fix(ci): prune CLI optionalDependencies to fix cold npx timeout (#2561) Cold `npx -y @claude-flow/cli@alpha --version` (and the ruflo wrapper) timed out because npm had to resolve and place ~30 optional deps before Node ever ran the in-process --version fast-path in bin/cli.js. Trim optionalDependencies to the 5 actually used by the default CLI path (agentdb, ruvector, agentic-flow, @claude-flow/memory, @claude-flow/security); everything else is already gated behind try/require or the plugins-store lazy-install path. Mirror the pruning in the ruflo wrapper. Add a pre-warm npm install step in verification-pipeline.yml as defense-in-depth. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(ci): align ruflo-hook.cjs npx fallback with .sh dist-tag (#2600) The three shipped .cjs Windows shims hardcoded ruflo@latest while the bash shim used ruflo@alpha, breaking the "mirrors ruflo-hook.sh" contract from #2132. Point all three .cjs shims at ruflo@alpha and extend the smoke test with a static parity assertion so future drift fails CI. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(ci): replace phantom agentic-flow/transport/loader marker in ADR-104 witness (#2578) Co-Authored-By: RuFlo <ruv@ruv.net> * fix(ci): re-sign + verify helpers manifest in prepublishOnly (#2593) Manifest drift (intelligence.cjs was bumped in 3.24/3.25 but the manifest still carried the 3.23.0 hash) triggered writeCriticalHelpers' fail-closed tamper warning on every CLI run in stamped projects. Root cause: sign-helpers.mjs existed but was never wired into publish. This wires sign+verify into prepublishOnly so drift fails the release, not every user. Co-Authored-By: RuFlo <ruv@ruv.net> * test(2590): guard against regression of #2590 Adds a CI guard step to the memory-smoke job that fails fast if either part of the #2590 fix regresses: 1) v3/package.json drops "onnxruntime-node" from pnpm.neverBuiltDependencies 2) the memory-smoke pnpm install loses --ignore-scripts Either regression would let onnxruntime-node's postinstall fetch a GPU nupkg from nuget.org and ETIMEDOUT on GitHub runners, taking pnpm install down as it did on main. Co-Authored-By: RuFlo <ruv@ruv.net> * test(2594): guard against regression of #2594 Co-Authored-By: RuFlo <ruv@ruv.net> * test(2561): guard against regression of #2561 Co-Authored-By: RuFlo <ruv@ruv.net> * test(2593): guard against regression of #2593 Co-Authored-By: RuFlo <ruv@ruv.net> * test(2596): guard against regression of #2596 Adds a vitest regression test asserting verifyMemoryInit() does not modify the on-disk DB file — snapshots bytes+mtime before, calls verify, asserts unchanged after. Fails if the sql.js writeback is re-added (bytes change from re-serialization, mtime bumps), passes on the fix. Co-Authored-By: RuFlo <ruv@ruv.net> * test(2566): guard against regression of #2566 Locks in the removal of the reverse-substring branch in isAnswerCorrect() (v3/@claude-flow/cli/src/benchmarks/gaia-agent.ts). The reverse rule `normExpected.includes(normModel)` scored fragmentary model answers (e.g. "a") as correct against any longer expected answer that contained them ("Paris, France"), inflating GAIA scores via normalization-collision — the vector ADR-167/169 R1 forbid. Verified: FAILS if the reverse-substring branch is reintroduced, PASSES on the current fix. Co-Authored-By: RuFlo <ruv@ruv.net> * test(2578): guard against regression of #2578 Co-Authored-By: RuFlo <ruv@ruv.net> * test(2599): guard against regression of #2599 Co-Authored-By: RuFlo <ruv@ruv.net> * chore(release): 3.25.3 — 10 fixes + CI guards Bumps @claude-flow/cli, claude-flow (umbrella), ruflo (wrapper) to 3.25.3. Regens v3 pnpm-lock.yaml after optionalDependencies pruning (#2561) and neverBuiltDependencies additions (#2590). Fixes included: - #2561 npx cold-install timeout — pruned CLI optionalDependencies - #2566 GAIA isAnswerCorrect reverse-substring collision - #2575 neural status ReasoningBank Empty vs 7798 patterns display - #2578 ADR-104 phantom agentic-flow/transport/loader witness marker - #2590 CI Node24/ubuntu memory smoke — onnxruntime-node postinstall block - #2593 helpers.manifest.json auto-refresh — verify + sign in prepublishOnly - #2594 memory store UNIQUE violation — flip --upsert default to true - #2596 memory init Windows EPERM — drop sql.js writeback race - #2599 doctor Learning Bridge — self-heal via recordMemoryPackagePath - #2600 Windows shim dist-tag parity — align @alpha across all shims CI guards added per fix. Co-Authored-By: RuFlo <ruv@ruv.net> |
||
|
|
cd8a9c0988 |
feat: memory distillation self-learning loop (ADR-174) + page-agent browser intent (ADR-175) (#2570)
* feat(memory): distillation service — mine memory_entries into intelligence substrate (ADR-174 M0+M1) Root cause the goal-planner surfaced: the daemon's consolidate worker (worker-daemon.ts:1443) is a STUB writing hardcoded zeros — 6000+ commits recorded to memory_entries but never distilled. reasoning_patterns / episodes / pattern_embeddings / causal_edges were all empty (hence 🧠 0%). M0 (safety) + M1 (service): runDistillation() mines memory_entries into episodes → reasoning_patterns (+pattern_embeddings) → causal_edges. - $0: reuses structured-distill.ts extractor + the embeddings already on each row; no LLM, no re-embedding. - Incremental: distill_state rowid cursor per namespace; never rescans. - Non-destructive: only inserts into the empty target tables; never touches memory_entries. Per-batch transactions. - Safe on the just-recovered DB: quick_check gate skips corrupt DBs; better-sqlite3 optional (silent no-op). - Provenance (ADR-171): feedback outcomes = oracle:test-exec (promotable); everything else = proxy:structural (written, NEVER promoted). Measured on a copy of the real 7,900-entry DB: 7,899 → 4,260 patterns (182 promoted, 4,078 proxy), 4,259 embeddings, 4,258 edges; source unchanged; dry-run wrote nothing; 2nd run processed 0. 6 tests green. ADR-174 records the decision, root cause, param surface, self-opt plan, alternatives, rollback. Follow-ups on branch: M2 CLI, M3 daemon (replace the stub), M4 self-optimization, M5 platform default. Co-Authored-By: RuFlo <ruv@ruv.net> * feat(memory): distill CLI, daemon auto-distill, self-optimization (ADR-174 M2+M3+M4) M2 — CLI: 'memory distill run|status|config' consuming the frozen M1 runDistillation. Full param surface: --dry-run, --namespace, --batch-size, --dedup-distance, --max-entries, --since, --budget-usd, --judge (fable gated on budget per ADR-172), --aggressive/--conservative presets, --config, --db. status reports substrate counts + cursor + provenance breakdown; degrades gracefully. 12 tests. M3 — daemon: replaced the runConsolidateWorker STUB (the root cause — it wrote hardcoded zeros) with a real runDistillation call every 30-min tick, bounded to 1000 entries/tick (cursor drains a backlog across ticks, stays well under the 16-min worker timeout). Real DistillReport written to the metrics file. Opt-out via --no-distill / RUFLO_DAEMON_NO_DISTILL and the existing 'daemon enable -w consolidate --disable'. Never throws (corrupt/ skipped surfaced, not fatal). 7 tests. The loop is now self-sustaining. M4 — self-optimization ('ruflo tuning ruflo'): tuneDistillation grid- searches {batchSize, dedupDistance, promoteThreshold} on ISOLATED copies, scored by MRR@10/recall@10 on a rowid-based held-out split (train/held-out disjointness asserted; held-out scored once). Source checksum unchanged. Honest measured result on the real 7.9k-entry corpus: winner batchSize=200 dedupDistance=0.2 promoteThreshold=execution+corroborated, held-out MRR@10 0.753 vs 0.749 baseline — roughly on par (the substrate/training value is the payoff, not raw retrieval uplift; measured-not-marketing). 11 tests. Combined M1+M2+M3+M4: 30 tests green. Co-Authored-By: RuFlo <ruv@ruv.net> * feat(browser): page-agent natural-language intent layer — browser_act (ADR-175) Adds an in-page NL intent tool on top of the selector-based browser tools: browser_act injects page-agent (Alibaba, MIT) into the agent-browser-driven page and runs 'execute(task)' — one natural-language call instead of manual selector orchestration. Verified against the real page-agent@1.11.0 (ESM-only, split @page-agent/* packages) — three load-bearing deviations handled: - import('page-agent') throws 'window is not defined' in Node even when installed → availability probed via require.resolve (path only, no eval), like browser-session-tools.ts. - the injectable IIFE auto-constructs a second PageAgent pointed at Alibaba's PUBLIC test endpoint (data leak on every call) → stripped before injection (stripDemoAutoInit). - execute() returns { success, data, history } → mapped to { result, steps }. Safety + routing: - Optional dependency (~1.11.0) + v3 pnpm-lock regenerated same change; browser_act returns { degraded:true } (never throws) when absent. - LLM routed through this CLI's Tier-3 ladder (OPENROUTER/OLLAMA, OpenAI- compatible); bare ANTHROPIC_API_KEY degrades (wrong API shape). - KEY SAFETY: page-agent runs in page context, so the real key never enters the injected string — startLocalLLMProxy holds it server-side on a loopback http server and injects Authorization itself; page sees 127.0.0.1 + a placeholder. Tests assert no key substring in the injection. - Trajectory → memory namespace 'browser' (feeds ADR-174 distillation). - AIDefence gates returned content (optional; passes through if absent). ADR-175 records the decision, the outer-harness/in-page split, constraints, the 6 follow-on use cases, alternatives, and rollback. 16 tool tests + 145 mcp-client regression tests green; full build clean. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(browser): fail-closed demo-endpoint firewall for page-agent injection The demo auto-connect (page content → Alibaba's public sandbox at page-ag-testing-*.fcapp.run) was only neutralized by a best-effort text strip that fell OPEN — stripDemoAutoInit returned the source unchanged if the DEMO_MODEL marker moved, silently re-enabling the leak on an upstream bundle change. Verified via the real tarball: the demo endpoint lives ONLY in dist/iife/page-agent.demo.js (6 refs); the library core ESM entry is clean (0 refs). So no fork is needed — the leak is a property of the demo bundle, not the library. Fix: findDemoLeak() firewall + DemoLeakError. buildPageAgentInjection now scans the stripped bundle for known demo-endpoint signatures (page-ag-testing, .fcapp.run, DEMO_MODEL) and THROWS if any survive; the browser_act handler catches it and degrades rather than injecting. This is version-independent and fail-CLOSED — a moved/renamed marker can no longer leak, because the guarantee is 'endpoint absent', not 'marker found'. +4 tests (signature detection, clean-bundle pass, moved-marker refusal, clean-bundle injection). 20/20 green. ADR-175 updated with the fork-vs-fix rationale. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(browser): add ADR-112 discoverability guidance to browser_act description CI failed 4 checks (tool-description audit + 3 smoke contracts that run it): browser_act was the 1 tool of 361 without a 'Use when …' guidance signal. The audit regex captures only the FIRST quoted segment of a multi-line description, so the guidance phrase must lead. Reworded to open with 'Use when …' + 'Prefer … over …' + 'pair with …' + 'Falls back …'. Audit now 361/361, exit 0. Co-Authored-By: RuFlo <ruv@ruv.net> * feat(memory): M5 — promote M4 winning distill config as daemon default (ADR-174) The daemon's consolidate worker now runs the M4-tuned config (dedupDistance=0.2, chosen by the self-optimization grid-search on the real corpus). Honest note in code + ADR: held-out MRR@10 0.753 vs 0.749 baseline — on-par, the substrate/training value is the payoff. Overridable per-run via 'memory distill'. Completes ADR-174 M0-M5. Co-Authored-By: RuFlo <ruv@ruv.net> * docs+fix(memory): address ADR-174 review — honest default, edge contract, invariants (ADR-174) Review fixes: - STATUS: Proposed → Accepted (M0-M5 implemented+tested, pending #2570 merge). - TUNED DEFAULT honestly justified: the M4 retrieval delta (0.753 vs 0.749) is a TIE, so dedupDistance=0.2 is sold as COMPRESSION, not retrieval lift — measured 2,723 vs 4,350 patterns (37% fewer) at 11.6ms/batch, 0 violations. Added an explicit promotion rule (retrieval-neutral within 0.002 AND >=15% compression AND <250ms/batch AND 0 proxy violations). - PARAM TABLE / DEFAULT consistency: dedup default is 0.2 everywhere (service, CLI, daemon). Presets now bracket it: --aggressive 0.3 / --conservative 0.1. - CAUSAL_EDGES OVERCLAIM: edges now carry an explicit contract (edge_type=cooccurrence, provenance_tier=proxy:structural, confidence=0.3, promoted=false) with the rule 'may rank retrieval, must NOT justify autonomous action'. Also fixed a latent bug: edges linked epId-1 (assumed consecutive rowids) → now the actual previous episode id. - EMBEDDING 1:1 invariant enforced: a cluster with no parseable vector is skipped, so every pattern has exactly one embedding (was off-by-one on a malformed source vector). - ADR adds operational invariants + audit SQL, business-value framing, and an acceptance test. +2 invariant tests (embedding coverage, edge contract). 32 distill tests green. Co-Authored-By: RuFlo <ruv@ruv.net> * feat(hooks): capture edit/task FAILURES, not just successes (ADR-174) The DB analysis found 898 feedback records — 100% success, 0 failures — because the post-edit/post-task hooks recorded a hardcoded success:true and never inspected the tool outcome. With no negative examples the oracle tier can't teach good-vs-bad, which caps what distillation can learn. Fix: read Claude Code's PostToolUse tool_response and record the REAL outcome. - isToolFailure(hookInput): conservative detector (positive error signal only) — tool_response.is_error / error / success:false, non-zero Bash exit code, or an error string. Exported + unit-tested (5 cases). - Generated hook (helpers-generator.ts) + the shipped .claude/helpers/ hook-handler.cjs both inline it: post-edit → recordEdit(file, !toolFailed), post-task → feedback(!toolFailed) (feedback() already boosts on success / decays on failure — it was just never called with false). - intelligence.cjs recordEdit(file, success) persists success to pending-insights so consolidation/distillation gets the negative example. The distillation JUDGE (memory-distillation.ts judgeFeedback) already maps success:false → reward 0 / not-promoted, so the substrate is failure-ready; this closes the upstream gap that never produced failures. End-to-end: a failed-edit PostToolUse payload now logs '[LEARN] Edit FAILURE recorded' through the actual shipped hook; a success stays '[OK] Edit recorded'. Co-Authored-By: RuFlo <ruv@ruv.net> * feat(init): version-stamped auto-refresh of critical helpers — hook fixes reach all users, no re-init (ADR-174) The propagation gap: Claude Code hooks run the PROJECT-LOCAL .claude/helpers/*.cjs copies, not the npm package — so npx ruflo@latest never updated them, and users didn't know to re-run init. Any hook fix (e.g. the ADR-174 failure-capture change) reached only people who happened to re-init. Fix: stamp the critical helpers (hook-handler.cjs, intelligence.cjs, auto-memory-hook.mjs) with the installed CLI version; on the next CLI command, if a project's stamp is older than the installed version, silently re-copy them. New lightweight src/init/helper-refresh.ts (fs/path/module only at load; lazily imports the heavy generators only on the rare source-unresolvable fallback). Wired AWAITED into index.ts startup so a fast command can't exit before the copy lands — fast path is a stamp read + string compare (sub-ms); the copy runs at most once per version bump. No-op outside a ruflo project (never scaffolds files in an unrelated dir). Skipped for init/update. executeUpgrade also stamps so an explicit upgrade is current. Verified end-to-end: a stale-stamped project with an OLD (no-failure-capture) hook silently upgrades to the toolFailed logic on a plain 'ruflo --help', and a current project is a no-op (a marker file survives). +4 tests. Co-Authored-By: RuFlo <ruv@ruv.net> * feat(security): Ed25519 signed provenance gate for the helper auto-refresh (ADR-174) The auto-refresh copies auto-EXECUTING hook code from the installed package into projects. npm verifies the tarball at install, not the files on disk after — a sibling package's postinstall (or disk tamper) could overwrite the helpers and the refresh would faithfully propagate the malicious code. Fix (fail-closed): every helper is verified against a ruflo-signed manifest before install. - helper-signing.ts: baked RUFLO_HELPERS_PUBKEY + verifyHelpersManifest() (native Ed25519, same primitive as rvfa-signing.ts). Fail-closed — any bad signature / tampered hash / wrong algorithm → null → refuse. - scripts/sign-helpers.mjs: publish-time signer; private key from $RUFLO_HELPERS_SIGNING_KEY, NEVER committed (kept out of the repo tree). Not shipped (scripts/ is narrowed to postinstall.cjs in files). - helper-refresh.writeCriticalHelpers: verifies the signed manifest + every source helper's SHA-256 BEFORE copying anything; a mismatch is refused and the project's existing helpers are left intact. The generator fallback needs no manifest (it's the CLI's own compiled code = trust root). - index.ts warns loudly on a blocked refresh (potential tampering signal). Threat model: closes post-install/on-disk tampering of the helper files; does not defend a wholesale-compromised CLI (which could swap the baked key too — but then it owns the binary you run). A CI test ('hashes match shipped files') fails if a helper changes without re-signing, so a stale manifest can't ship. Verified end-to-end: a tampered shipped helper → refresh REFUSED (blocked, project helper untouched); restored → refresh succeeds. +6 signing tests. Co-Authored-By: RuFlo <ruv@ruv.net> * chore(security): source helper-signing key from GCP Secret Manager sign-helpers.mjs now resolves the private key from GCP Secret Manager (RUFLO_HELPERS_SIGNING_SECRET, via gcloud secrets versions access) as the preferred source for CI/publish, with the local-PEM fallback (RUFLO_HELPERS_SIGNING_KEY) for air-gapped signing. Key never committed. ADR-174 security note updated. Co-Authored-By: RuFlo <ruv@ruv.net> |
||
|
|
d012042800 |
feat: review-driven upgrades — perf, security, SOTA capabilities (6-agent concurrent implementation) (#2547)
* chore(arch): untrack the prepublish plugin mirror — build artifact, not source
v3/@claude-flow/cli/plugins/ is regenerated by prepublishOnly from
plugins/ruflo-metaharness/ at publish time. Keeping it committed caused
silent drift between publishes (4 files diverged this week alone) and
every source edit required a manual mirror sync. npm pack still bundles
it — the files array + prepublish copy are unaffected by gitignore.
Co-Authored-By: RuFlo <ruv@ruv.net>
* feat(gaia): submission integrity checklist — fail-closed pre-submit audit (Berkeley RDI hardening)
Adds a 4-part integrity section to the gaia-validate pre-submit gate,
motivated by UC Berkeley RDI's 'Agents' Last Exam' (April 2026), which
gamed all 8 major agent benchmarks to near-100% without solving tasks.
- scripts/gaia-integrity.mjs: (A) answer-key-shaped path reads outside
the sanctioned dataset dir — FAIL-CLOSED exit 2; (B) eval()/new
Function()/exec-of-non-literal strings in gaia-bench runner code
paths — FAIL-CLOSED exit 2; (C) judge-prompt-injection markers in
produced answers/trajectories — WARN; (D) provenance stamp (results
+ git SHA + dataset content hash) written for gaia-submit to embed.
- --allow-integrity-override suppresses the exit code but records
'overridden: true' in the stamp — never silent.
- --self-test plants a fake answer-key read, an eval-of-task runner,
and injection markers in temp fixtures and proves fail-closed/pass/warn.
- gaia-validate.md: new check 7 documenting the checklist + flags.
- gaia-submit.md: package now embeds integrity.json under the Ed25519
witness manifest (tamper-evident attestation); stale/failed stamps block
packaging.
- smoke-gaia.sh: 3 new steps (script wired, self-test green, repo scan
clean); plugin bumped to 0.5.0.
Co-Authored-By: RuFlo <ruv@ruv.net>
* perf(cli): lazy ONNX embedder init + local-first ruvector resolution
Fix 1 — lazy ONNX init (measured, biggest win):
neural-tools.ts had a module-level try/catch with TOP-LEVEL AWAIT that ran
`await import('ruvector')` + `initOnnxEmbedder()` + a probe embed at import
time. Because mcp-client statically imports neural-tools, this added the
full ONNX startup to every CLI command. The block is now a memoized
`ensureEmbeddings()` (module-level promise guard) invoked from
generateEmbedding() and from the three handlers that report the provider
without embedding (neural_compress / neural_status / neural_optimize), so
degraded/fallback semantics and provider reporting are byte-identical.
The probe embed moved inside ensureEmbeddings() — it still guards the
ADR-086 type-load-success-but-runtime-fails trap, but only on first use.
Measured (warm, darwin-arm64):
node bin/cli.js --help 0.50s -> 0.09s
node bin/cli.js status 0.64s -> 0.24s
import(mcp-client.js) 438ms -> 30ms
"Loading ONNX model" no longer appears on non-embedding commands;
appears exactly once on first embed (neural_predict / memory search).
Fix 2 — ruvector pin + local-first resolution:
browser-session-tools.ts pinned `npx -y ruvector@0.2.25` while 0.2.27 is
what the package installs, forcing cold npx downloads of a second version
(4 per browser session). The pin is now 0.2.27 and a memoized
resolveLocalRuvectorCli() (createRequire + package.json bin walk) spawns
`node <local-cli>` directly, falling back to the npx pin only when no
local install exists.
While verifying the 0.2.27 subcommand surface (required before bumping),
found the trajectory-* flags passed here were never valid on 0.2.25 OR
0.2.27 (`--session-id/--task/--args/--verdict` vs the real
`--context/--agent`, `--action/--result`, `--success/--quality`) — every
trajectory call exited with commander "unknown option", same bug class as
the #2015 `rvf create --kind` fix documented in this file. Flags are now
mapped correctly; browser_session_record and browser_session_end verified
end-to-end (rvf create + trajectory-begin/step/end + rvf compact all pass).
Fix 3 — lazy heavy MCP tool groups: profiled and NOT converted.
Per-module timed dynamic imports of all 36 tool modules show neural-tools
was 469.6ms and every other group is 0.3-5.5ms (hooks-tools 3.5ms,
agentdb-tools 2.5ms, embeddings 0.9ms, wasm-agent 0.6ms). After fix 1 the
whole registry builds in ~30ms; converting handler dispatch to lazy
import() would add risk for <6ms of residual win, so mcp-client.ts is
intentionally untouched.
Validation:
- tsc build clean
- plugins/ruflo-metaharness/scripts/test-mcp-tools.mjs: 171/171 passed
- memory search / neural predict still work; ONNX loads lazily then
Co-Authored-By: RuFlo <ruv@ruv.net>
* security(deps): lift OTel floor-became-ceiling pins — core >=2.8.0, otlp family >=0.220.0
The exact pins @opentelemetry/{core,resources,sdk-trace-base}@1.25.1 and
the otlp exporter/transformer family@0.52.1 (root + ruflo wrapper
overrides) were added in #1997/#2112 as a 'fewer-knobs-for-arborist'
defense against the npm arborist Invalid Version crash (empty-version
placeholder from optionalDependencies/peerDependencies overlap). They
were never API-compat pins — but they became a ceiling that forces the
tree onto @opentelemetry/core <2.8.0, which npm audit flags for
GHSA-8988-4f7v-96qf (unbounded memory allocation in W3C Baggage
parsing), and they blocked every upstream fix.
Convert to floors while keeping the lockstep families coherent:
- stable line: core / resources / sdk-trace-base -> >=2.8.0
- otlp line: sdk-node, exporter-prometheus, the three
exporter-trace-otlp-* packages, otlp-exporter-base,
otlp-grpc-exporter-base, otlp-transformer -> >=0.220.0
(sdk-node@0.220.0 declares exactly core@2.9.0 + otlp@0.220.0, so the
floors resolve to one mutually consistent set; verified in both the
root lockfile and a fresh ruflo/ resolution: core 2.9.0 everywhere,
otlp 0.220.0 everywhere.)
The arborist defense is preserved — every package in both families is
still forced to a single resolution via overrides; the override set is
now broader (whole otlp family at root too), which gives arborist fewer
ambiguous nodes than before, not more.
Also adds the exporter family overrides to the root manifest so root
and the published ruflo wrapper stay aligned (#2112 lesson: root
overrides do not propagate to the published wrapper).
Validation:
- root: npm audit --omit=dev 30 moderate -> 0 vulnerabilities
- ruflo: npm install --package-lock-only succeeds, 0 vulnerabilities
- runtime probe: new NodeSDK({}) via @opentelemetry/sdk-node@0.220.0
with core@2.9.0 loads and exposes start(); OTLPTraceExporter loads
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01S7GYqnVUVxBfZ5W8znqry3
* security(deps): npm audit fix on v3 workspace lock — grpc-js, hono/node-server, brace-expansion
Plain `npm audit fix --omit=dev --package-lock-only` (no --force) on the
v3 npm workspace lockfile. Clears the three targeted advisories:
- @grpc/grpc-js 1.14.3 -> 1.14.4 GHSA-5375-pq7m-f5r2 (+GHSA-99f4-grh7-6pcq)
- @hono/node-server 1.19.9 -> 1.19.14 GHSA-wc8c-qw6v-h7f6 (+GHSA-92pp-h63x-v22m)
- @isaacs/brace-expansion 5.0.0 removed/deduped GHSA-7h2j-956f-4vf2
Audit summary: 82 vulnerabilities (2 low, 56 moderate, 24 high)
-> 55 vulnerabilities (50 moderate, 5 high).
The remainder needs --force / dependency swaps and is out of scope for
this pass.
Note: the reify was run with the two pnpm-style `workspace:*` devDep
specs (plugin-agent-federation, plugin-iot-cognitum) temporarily
substituted with the registry pins the lockfile already records
(@claude-flow/shared@3.0.0-alpha.8, @claude-flow/security@3.0.0-alpha.10),
because npm's arborist does not support the workspace: protocol; the
manifests themselves are unchanged. The v3 workspace is otherwise
pnpm-driven (`pnpm -r build`).
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01S7GYqnVUVxBfZ5W8znqry3
* security(deps): align agentdb floor in @claude-flow/memory — ^3.0.0-alpha.17
Root overrides force agentdb >=3.0.0-alpha.17, but @claude-flow/memory
declared ^3.0.0-alpha.16, so its nested/published tree could still
resolve alpha.16 (the #2112 lesson: overrides do not propagate to
nested or published trees). The v3 pnpm lock indeed had memory pinned
to agentdb 3.0.0-alpha.16 — bumping the package's own floor makes both
branches resolve the same version regardless of the consumer's
override set.
pnpm-lock.yaml is regenerated by `pnpm install`; besides the
agentdb 3.0.0-alpha.16 -> 3.0.0-alpha.17 move under memory, it also
reconciles previously committed manifest bumps (metaharness family)
that had drifted from the stale lock.
Verified: `npm ls agentdb` from root shows a single deduped
agentdb@3.0.0-alpha.17 across direct, @claude-flow/memory,
@claude-flow/neural, and agentic-flow branches.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01S7GYqnVUVxBfZ5W8znqry3
* security(deps): correct FEDERATION_BIND_HOST doc default — 127.0.0.1 (ADR-166 Phase 3d)
The bin.ts header JSDoc still advertised `default: 0.0.0.0`, but the
code (makeContext) has defaulted to 127.0.0.1 since the ADR-166
Phase 3d loopback-by-default hardening. Doc-only change.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01S7GYqnVUVxBfZ5W8znqry3
* fix(memory): drop vitest-4 style vi.fn generic that breaks tsc build
The hoisted vitest 1.x types Mock as Mock<Args extends any[], Returns>,
so the single-generic vi.fn<SearchFn> form fails the package build
(pre-existing baseline failure). Annotate the variable instead — the
inferred mock signature is identical.
Co-Authored-By: RuFlo <ruv@ruv.net>
* perf(memory): batch embeddings in the migration import path
MemoryMigrator.processBatch batched entries (batchSize=100) but then
awaited this.embeddingGenerator(content) ONE ENTRY AT A TIME inside the
loop — N sequential ONNX inferences per batch on the
memory_import_claude hot path.
Now:
- New BatchEmbeddingGenerator type ((contents: string[]) =>
Promise<Float32Array[]>) accepted as an optional 4th constructor /
factory param — one true batch call per migration batch when the
embedder supports it (e.g. bge-embedder's single padded forward pass).
- Fallback: bounded concurrency over the single-text generator
(MigrationConfig.embeddingConcurrency, default 8 — never unbounded;
set 1 to restore sequential behavior).
- If the batch call fails, falls back to per-entry embedding so one
backend hiccup doesn't strand the batch.
Per-entry error semantics preserved: validation failures skip, store
failures fail, embedding failures warn and store the entry without a
vector — one bad entry never fails the batch.
Measured (200 synthetic entries, 5ms simulated single-embed latency):
sequential 1220ms -> concurrency-8 156ms (7.8x) -> true batch 115ms
(10.6x).
Co-Authored-By: RuFlo <ruv@ruv.net>
* feat(memory): temporal validity on hierarchical memory (Zep/Graphiti-style)
Facts in hierarchical memory can now carry validity windows and are
INVALIDATED on conflict instead of overwritten:
- New TieredMemoryStore module (@claude-flow/memory) promotes the inline
controller-registry stub to a first-class, tested store with optional
validFrom / validUntil (ISO) + supersededBy fields.
- store accepts supersedes=<entryId|key>: the old entry is stamped
validUntil=now + supersededBy=<newId> and archived — never deleted.
- recall filters temporally-invalid entries (expired, superseded, or
not-yet-valid) by default; includeExpired=true is the audit escape
hatch.
- Backward compatible: entries without the fields behave exactly as
before (always valid); legacy store(key,value,tier) / recall(query,
topK) call shapes unchanged; same-key stores still overwrite; the
bridge's real-vs-stub detection signal (getStats+promote) is
preserved.
- CLI bridge passes the new optional fields through
bridgeHierarchicalStore/-Recall. On the native agentdb
HierarchicalMemory backend, validFrom/validUntil ride in metadata and
are filtered at recall; supersedes is reported as unsupported there
(no public update API) rather than silently dropped.
- MCP inputSchemas for agentdb_hierarchical-store (validFrom,
validUntil, supersedes) and agentdb_hierarchical-recall
(includeExpired) expose the fields to agents, with ISO-8601
validation.
Tests: supersede flow (including same-key supersede preserving
history), expired + not-yet-valid filtering, includeExpired escape
hatch, legacy entries untouched, unparseable timestamps fail open.
Co-Authored-By: RuFlo <ruv@ruv.net>
* feat(routing): confidence-gated tier escalation — hooks_model-verify ($0 post-generation verifier)
ADR-026/143 classify BEFORE generation only. This adds the 2026-SOTA
post-generation gate: attempt the cheap tier, verify the output with
CHEAP structural signals (no LLM call), escalate only on failure.
- src/ruvector/output-verifier.ts — pure verifier: empty/truncated
output, refusal patterns, degenerate repetition, unbalanced
delimiters, and a real syntax parse for code/JSON tasks (TypeScript
transpileModule diagnostics / JSON.parse, graceful degradation when
typescript is absent). Returns {confident, reasons[], suggestedTier,
suggestedModel, escalate}; ladder haiku→sonnet→opus, tier 2→3.
- hooks_model-verify MCP tool (hooks-tools.ts, surgical) — wraps the
verifier and feeds the verdict into the SAME learning stream as
hooks_model-outcome (ModelRouter.recordOutcome: success when
confident, escalated when not) so the Thompson priors learn which
task shapes the cheap tier fails on. A dedicated tool (not a verify
mode on hooks_model-outcome) because verify is a mid-loop decision
point returning a verdict the agent acts on, while outcome is a
terminal write — the sequence is route → generate → verify →
(escalate) → outcome.
- __tests__/output-verifier.test.ts — refusal/truncation/parse/
escalation-ladder coverage + MCP handler tests (18 tests).
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01S7GYqnVUVxBfZ5W8znqry3
* feat(federation): A2A Agent Cards — spec-1.0 discovery adapter over the bespoke federation schema
Implements the Agent2Agent (A2A, Linux Foundation) Agent Card layer as an
ADAPTER over the existing federation registration/discovery shapes. Cards
only — A2A Tasks/messaging are deliberately out of scope this iteration.
Spec: A2A Protocol 1.0.0 (a2a-protocol.org; schema source of truth
specification/a2a.proto in a2aproject/A2A). Required AgentCard fields per
spec 4.4.1: name, description, supportedInterfaces, version, capabilities,
defaultInputModes, defaultOutputModes, skills. Well-known discovery path
per 8.2 + IANA registration: /.well-known/agent-card.json.
- src/a2a/agent-card.ts: toAgentCard() maps FederationManifest -> spec-
compliant card (agent types -> AgentSkills; federation endpoint -> open-
form RUFLO-FEDERATION AgentInterface; bespoke identity rides in a spec
4.6 extension urn:ruflo:federation:manifest:v1 for lossless round-trip);
validateAgentCard() enforces the 1.0 required-field shape;
fromAgentCard() maps a remote card into a FederationNode — ALWAYS
TrustLevel.UNTRUSTED (cards are self-asserted metadata; trust is earned
via the normal handshake + TrustEvaluator path).
- src/a2a/well-known.ts: minimal node:http endpoint serving GET/HEAD on
the exact well-known path with spec 8.6 Cache-Control + ETag/304.
Binds 127.0.0.1 by default and REFUSES non-loopback binds without an
explicit allowNonLoopback opt-in (ADR-166 posture preserved — no
loosening of federation bind or auth).
- src/a2a/consume.ts: fetchAgentCard (injectable fetch, timeout, size cap,
structural validation) + consumeAgentCard registering the peer into
DiscoveryService so A2A peers appear in federation discovery.
- DiscoveryService: new 'a2a-card' discovery mechanism +
registerExternalPeer() (refresh-not-replace preserves trust state).
- plugin.ts: opt-in wiring via config.a2aCard/a2aCardPort/a2aCardHost;
bind failure degrades gracefully; server closed on shutdown.
- 40 new unit tests incl. golden-file card fixture
(__tests__/fixtures/a2a-agent-card.golden.json), foreign-card mapping,
round-trip, loopback guard, and a live serve->consume e2e over HTTP.
- plugin.test.ts: fixed pre-existing stale assertion (10 -> 11 CLI
commands; 'federation trust elevate' was added without updating the
count) — baseline suite failed before this change.
Build clean; 600/600 tests green.
Co-Authored-By: RuFlo <ruv@ruv.net>
* feat(intelligence): execution-state-tree retrieval prototype (MAGE-style positional recall)
Research basis: MAGE (arXiv 2606.06090) — semantic-similarity retrieval
fragments decision trajectories on long-horizon tasks; retrieving by
POSITION in a hierarchical execution-state tree (root→current path)
preserves coherence.
- src/ruvector/trajectory-tree.ts — TrajectoryTree prototype:
trajectory-start opens a node under the session root (nesting under
the deepest open trajectory), steps append children, end closes and
pops the current position. recallPath({sessionId, depth}) returns
the exact root→current path + recent siblings — no embedding search.
Best-effort JSON snapshot at .claude-flow/intelligence/
trajectory-tree.json, ALONGSIDE existing trajectory storage (no
migration). Limitations documented in the module header.
- hooks-tools.ts (surgical): non-fatal tree mirrors in
hooks_intelligence_trajectory-start/step/end (+ optional sessionId
param on start), and an opt-in strategy:"state-tree" on
hooks_intelligence_pattern-search (+ sessionId/depth params).
Default strategy stays "semantic" — zero behavior change unless
opted in.
- __tests__/trajectory-tree.test.ts — synthetic 3-level trajectory
path assertions, nesting/pop, sibling window, persistence
round-trip, and default-strategy-untouched checks (12 tests).
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01S7GYqnVUVxBfZ5W8znqry3
* fix(metaharness): kill third-party @latest, extract shared _invoke.mjs, derive smoke counts
Three of the four converged security/perf/arch review fixes:
1. SECURITY (HIGH) + PERF — _harness.mjs no longer shells to
`npx -y metaharness@latest` (a compromised upstream publish would have
executed arbitrary code on user machines, and @latest forced a registry
check per call). Resolution is now: (a) locally installed metaharness
satisfying the PINNED range ~0.3.0 (walk-up node_modules), then (b) a
one-time `npm install --prefix ~/.ruflo/metaharness-cache-0.3.0`, then
plain `node <abs-path-to-cli>` spawns — zero network per call. Both bins
(metaharness + harness) are read from the resolved package.json bin map.
The {stdout, stderr, exitCode, json, degraded, reason, durationMs}
contract and ADR-150 graceful degradation are preserved; timeouts now
report 'metaharness-timeout' (matching _darwin/_redblue) instead of being
conflated with 'metaharness-not-available'.
Measured: `score.mjs --path .` completes in ~0.35s via the cache.
2. ARCH — new scripts/_invoke.mjs consolidates the copy-pasted plumbing:
DEGRADED_RX (superset incl. `npm ERR`), classifyDegraded (keeps the
-timeout vs -not-available distinction), injectJson, parseTrailingJson
(the LAST-{...}-block variant from _darwin — the old _harness first-block
parse was a live bug), ensureCachedInstall (generalizes gepa/_redblue,
cache-dir naming kept compatible so existing caches keep serving),
importOptionalLibrary, findLocalPackageDir, makeDegradedEmitter.
_harness/_darwin/_redblue/gepa are now thin adapters with IDENTICAL
exported signatures; their genuinely-different parts stay (redblue's
node-direct isMain workaround, darwin's async streaming for evolve,
harness's dual binaries). test-graceful-degradation.mjs gains
RUFLO_METAHARNESS_CACHE_BASE / RUFLO_METAHARNESS_SKIP_LOCAL seams so the
ADR-150 drill still exercises the degraded path on warm-cache machines.
3. ARCH — smoke.sh derives EXPECTED_TOOLS / EXPECTED_SUBS once from
metaharness-tools.ts / metaharness.ts and replaces the 7 independently
hardcoded literals (15 at :750/:829/:2046, 13 at :725/:851, 16 footnote,
15 success-pattern). Cross-surface value preserved: independent surfaces
(CLAUDE.md catalog, test-mcp-tools' own `tools.length === 15` literal,
footnote appends, runScript refs) are compared against the derived count,
never a surface against itself; a >=15/>=13 floor step catches
extraction-regex rot.
Validation: smoke.sh 122/122, test-graceful-degradation 16/16,
test-mcp-tools 171/171.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(metaharness): add --diagnose GEPA failure diagnosis to evolve
GEPA's key trick — natural-language failure diagnosis from execution traces
feeding the next mutation, not just scalar fitness — scoped modestly into
the evolve wrapper as an opt-in flag.
`evolve.mjs --diagnose` now appends a `diagnosis` section to the emitted
JSON after the run completes: losing/failed variants' transcripts are run
through darwin's gepa `analyzeTranscript` + `classifyFailure` ops (via the
shared importGepa resolver, moved to _darwin.mjs because gepa.mjs runs
main() on import) and aggregated into failure classes + counts + dominant
class per variant.
Upstream shape findings (verified against @metaharness/darwin@0.8.0 with a
tiny mock-sandbox evolve):
- `metaharness-darwin evolve --json` emits a TEXT leaderboard — stdout
carries no JSON and no transcripts (r.json has always been null here).
- Per-variant run records live at <repo>/.metaharness/runs/<id>.json as
sandbox exec traces ({taskId, exitCode, stdout, stderr}) — NOT gepa
{actionRaw, obs} transcripts.
Diagnosis therefore layers: gepa-shaped transcripts when a run record
embeds them (agent sandbox / future upstream) → champion-transcript
fallback → `diagnosis: {available: false, reason, traceSummary}` with a
mechanical per-variant tally. Never fails the run — internal errors
degrade to {available: false, reason: 'diagnosis-failed: ...'}.
Both paths exercised end-to-end: mock evolve yields the traceSummary
fallback; a seeded gepa-shaped run record yields
{available: true, scope: 'losing-variants', failureClasses:
{exploration-loop: 1, edit-mechanics: 1}, dominantClass: 'exploration-loop'}.
Documented in skills/harness-evolve/SKILL.md.
Co-Authored-By: RuFlo <ruv@ruv.net>
* Revert "feat(gaia): submission integrity checklist — fail-closed pre-submit audit (Berkeley RDI hardening)"
This reverts commit
|
||
|
|
e0d0af1cef |
feat(gaia): pre-submission exploit audit + signed attestation (ADR-167) (#2543)
* feat(gaia): pre-submission exploit audit + signed attestation (ADR-167) Motivated by UC Berkeley RDI's April-2026 finding that all 8 major agent benchmarks were gamed to ~98-100% without solving a single task (GAIA leaked ~98% of answers via public answer DBs + normalization collisions; o3/Claude-3.7 monkey-patch graders in 30%+ of runs per METR). Signing proves transport-integrity; this audit proves earning-integrity. The audit report is deterministic ($0, offline, pure-core) and is registered as an ADR-103 witness fix marker so its sha256 + "clean": true marker are signed INTO manifest.md.json. - ADR-167 (v3/docs/adr): threat model, check catalogue mapped to RDI/METR vectors, manifest-attestation design, honest harness-gap limitations. - gaia-audit.mjs: 7 pure checks (answer-leakage, no-work, oracle-leakage, grader-isolation, normalization-collision, voting-disclosure, split-integrity); CLI exit 0 clean / 1 CRITICAL / 2 usage. Schema-blocked checks return skip with a harness_gap note, never a false pass. - Tests: 27 passing; clean + dirty fixtures triggering every vector. - Wired --audit into /gaia validate; pre-sign gate into /gaia submit (refuses CRITICAL fail unless --allow-dirty) + gaia-submission skill. - Bump ruflo-workflows 0.4.0 → 0.5.0. Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01V84YJJK7RBF9TuUo4eVSW7 * docs(adr): ADR-168 — GAIA harness evidence recording (implements ADR-167 §7 forward contract) Co-Authored-By: RuFlo <ruv@ruv.net> * docs(adr): ADR-169 — benchmark reporting integrity standard (strict-EM headlines, view-labeled scaling, disclosed gaps) Codifies the discipline the FRAMES ablation self-audit measured against the Berkeley RDI vectors. Completes the integrity family: ADR-167 audits the submission, ADR-168 records the evidence, ADR-169 governs how numbers are reported. Co-Authored-By: RuFlo <ruv@ruv.net> * feat(gaia): port static source-scan checks (AUD-8/9/10) into gaia-audit Ports the unique, non-overlapping checks from the reverted #2547 duplicate gaia-integrity.mjs into gaia-audit.mjs as a new static source-scan family, matching ADR-167's check-registry / status / harness_gap design: - AUD-8 answer-key-reads (CRITICAL, fail-closed): scans runner sources + produced artifacts for answer/gold/solution/ground-truth-shaped paths referenced outside the sanctioned dataset dir(s). - AUD-9 dynamic-eval (CRITICAL, fail-closed): static scan of the gaia-bench runner sources for eval()/new Function()/exec-family calls with a non-literal first arg. Preserves the #2547 false-positive refinements (multi-line aware, RegExp.prototype.exec excluded, fixed-string commands + comments ignored) — passes clean against ruflo's own multi-line `gcloud secrets … execSync`. - AUD-10 judge-injection (WARN): scans the agent's PRODUCED answer/outputs for judge-directed prompt-injection markers. Distinct from AUD-3 oracle-leakage (input-side gold-answer scan), so both run. Unlike AUD-1/3/4 these need no trajectory instrumentation — they enforce today and skip honestly (never false-pass) when the source population is absent (standalone/ejected run or --skip-source-scan). The #2547 provenance stamp and --allow-integrity-override are NOT ported: #2543's signed attestation and the `/gaia submit --allow-dirty` gate already own provenance + override, so the new checks flow through the same attestation.clean/strict_clean path. CLI adds --repo-root, --dataset-dir (repeatable), --skip-source-scan. Fixtures: planted evil runner (eval + interpolated exec), planted answer-key read, judge-injection results. Tests: 44 green (17 new). Docs: ADR-167 §3/§7 check table, gaia-validate.md, gaia-submission SKILL.md. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01S7GYqnVUVxBfZ5W8znqry3 * fix(ci): advance ruflo-workflows smoke version assertion to 0.5.0 The AUD-8/9/10 port bumped plugin.json to 0.5.0 but the plugin's own smoke step 1 still asserted 0.4.0 — the single failing step behind both smoke-all and the meta-smoke leg of smoke-without-metaharness on #2543. Co-Authored-By: RuFlo <ruv@ruv.net> |
||
|
|
ae9bba523e |
feat(metaharness): learn + gepa integration — metaharness@0.3.0 / darwin@0.8.0 (15 MCP tools) (#2540)
* fix(mcp-bridge): streamable-HTTP session cleanup — DELETE + Mcp-Session-Id + 202 (#2425) Closes #2425. Cherry-picked from djimit's commit |
||
|
|
19e4277e12 |
feat: ADR-164 agentbbs business autopilot — Phases 1-4 (4 MCP tools, 7 pods, http_fetch, atomic budget tracker) (#2503)
* docs(adr): ADR-164 — AgentBBS federated business-management autopilot Draft ADR proposing agentbbs@0.1.0 integration as a federation-peer kind + business pod scaffolding. Covers the 4 new MCP tools, 7 domain pod templates (sales/marketing/finance/ops/support/hr/exec), upstream changes required in both ruflo and ruvnet/agentbbs, 5-phase rollout, and 7 honest risks. Builds on the optional-dep + graceful-degraded pattern shipped today in PR #2500 (agenticow) and ADR-150 (metaharness). Status: Draft. Implementation gated on upstream confirmation of agentbbs persistence semantics + latency baseline. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01WbBa4nccx5aGhXphoHxcni * docs(adr): amend ADR-164 + add ADR-164.1 (budget atomicity) ADR-164 amendments (Draft v2, 1193 → 1412 lines, +219 net): - Rewrite agentbbs maturity assessment — it's a 13-crate Rust workspace with 7 existing CI workflows + 30+ release tags + existing federation crate, NOT a 16h-old greenfield. The npm package is the launcher only. - §3.2.4 — two-phase auth: 15-min token is handshake-only; long-lived stream sessions persist past expiry until idle-close. - §3.5.4 — new "Founder-bootstrap trust elevation" admin escape hatch so #exec cross-pod synthesis works on Day 1 without 500-interaction wait. - §4.4 — Ops pod gets aidefence_*, terminal_execute, agent_execute, new http_fetch tool, cloud-provider MCP servers; concrete bench scenario. - §5.1.8 — new MCP tool `http_fetch` (URL allowlist, timeout, size cap). - §5.2 — survey-first upstream changes; many requirements may already exist in crates/agentbbs-federation and crates/agentbbs-mcp. - §7.3 + §9.5 — forward-reference to ADR-164.1 for race resolution. - §9.1 — risk #1 rewritten: real risks are npm/Rust version drift, FSL license implications, cargo first-run requirement, MCP server compatibility. ADR-164.1 (new, 724 lines): - Atomic reserve-and-commit token bucket against AgentDB-style SQLite store with BEGIN IMMEDIATE serialization. - Schema: bbs_budget_reservations + bbs_budget_rooms tables. - Three-op API: reserve / commit / release with discriminated returns. - 5 concurrency tests (vitest-implementable) closing the race window. - Migration plan: feature-flagged rollout behind CLAUDE_FLOW_BBS_ATOMIC_BUDGET, default flip after 1 week zero incidents. - p99 < 5ms target with ~1000 reserves/sec throughput ceiling honestly documented (partition-per-pod as the scale-out path, not replacing SQLite). Both Draft status. Phase 1 implementation gated on these. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01WbBa4nccx5aGhXphoHxcni * docs(adr): ADR-164.1 amendments — close the Expired Commit Leak Peer review (2026-06-29) identified three issues: 1. CRITICAL — Expired Commit Leak (§8.1, §5.3, §4.1) Original draft rejected commit() of an expired reservation, so the actual API spend went unrecorded. A high-latency loop could silently burn unbounded $ past the monthly cap. Fixed: commit() now ACCEPTS the spend, transitions state to new 'committed_post_expiry' value, records actual_usd, returns { ok: true, warned: 'COMMIT_AFTER_EXPIRY' }, and emits a reservation.committed_post_expiry audit envelope. 2. SQLite locking clarification (§3.2 _lock_bump field note) BEGIN IMMEDIATE itself acquires the RESERVED lock; _lock_bump is now documented as belt-and-suspenders + code-review visibility, not the primary serialization primitive. 3. Per-pod reservationExpiryMs (§3.2 expires_at field note) Default 60s is too short for local large-model pods. Pod templates may now set reservationExpiryMs in [5_000, 300_000] ms; ceiling defends against unbounded runaway. Plus new Test 6 in §9: load-bearing proof that COMMIT_AFTER_EXPIRY captures the real spend and the room's subsequent gate-check reflects it. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01WbBa4nccx5aGhXphoHxcni * feat(mcp): Phase 1 — agentbbs integration scaffold (4 MCP tools) Implements ADR-164 §3.2 + §5.1 with the same architectural pattern as PR #2500 (agenticow): optional-dep + graceful-degraded fallback. New MCP tools (4): - federation_bbs_register register a BBS room as a federation peer - federation_bbs_publish publish a typed envelope to a room - federation_bbs_watch poll recent envelopes (streaming = Phase 4) - federation_bbs_human_join single-use Ed25519 token + web/SSH handshake Phase 1 scope (scaffolding the surface, not the wire): - loadAgentbbs() checks importability; degrades to {degraded:true, reason:'agentbbs-not-found'} when missing — never throws - Validation runs BEFORE the optional-dep check so path-traversal / label / msgType rejection is deterministic regardless of install state - Backing store: local JSON-lines in <basePath>/room-<id>.jsonl + rooms.json registry. The Rust agentbbs-federation crate will replace this with signed wire envelopes in Phase 2. - Ed25519 keypair is per-process ephemeral; real federation keypair wiring is Phase 2 (documented inline at the call site). Gates (all PASS): - Build: clean (v3/@claude-flow/cli npm run build) - Vitest: 8 pass + 4 skip-conditional (agentbbs binary not built locally) - ADR-112 audit: 349/349 tools with Use-when guidance (was 345; +4 new) - Bash smoke: 8/8 (end-to-end register→publish→watch cycle via dist handlers) Plus: - .github/workflows/no-agentbbs-smoke.yml — CI guard mirroring no-metaharness-smoke.yml; asserts agentbbs stays in optionalDependencies and runtime drill exits 0 with --ignore-optional install - plugins/ruflo-bbs-federation/.claude-plugin/plugin.json — plugin metadata Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01WbBa4nccx5aGhXphoHxcni * docs(adr-164): replace §5.2 hypotheticals with concrete agentbbs survey findings Phase 1 survey of ruvnet/agentbbs @ ca3c6e0 resolved each compatibility check in §5.2 against the live upstream source. Six "VERIFY FIRST / if- exists / if-missing" branches are replaced with definite answers: - §5.2.1 FederationPayload — already covers ruflo's needs (4 variants, Ed25519-sealed); map publish→ReplicateMessage, register→AnnounceBoard. No upstream PR. - §5.2.2 MCP tool registration — static 4-tool surface (list_boards, read_board, post_message, search_memory). Re-scoped from "web UI rendering" to the actual MCP shape concern. Gap #1 filed for missing create_board tool. No upstream PR for Phase 1. - §5.2.3 Subscription/streaming — genuinely missing (subscribe=false hardcoded), but matches ruflo's Phase 1 polling design. Gap #2 filed for Phase 4 streaming. No upstream PR for Phase 1. - §5.2.4 Integration seam — surprise finding: agentbbs already drives `npx ruflo federation` via RufloAdapter (other direction). Ruflo's CLI now has a contract obligation to keep init/join/status alive. - §5.2.5 Durable retention — postgres backend exists in CI but `agentbbs mcp` defaults to MemoryStore; gap #3 filed for Phase 3. - §5.2.6 CI regression coverage — no existing end-to-end agentbbs↔ruflo test. Closed by ruvnet/AgentBBS#3 regression guard (PR opened upstream in this same Phase 1 cycle). The regression guard at ruvnet/AgentBBS#3 asserts the four MCP tool names, four FederationPayload variants, and three RufloAdapter subcommand names continue to exist on every agentbbs PR + nightly cron. Cross-ref: - Upstream PR: https://github.com/ruvnet/AgentBBS/pull/3 - Survey commit: ruvnet/AgentBBS@ca3c6e0 * feat(pods): Phase 2 — sales pod end-to-end (dry-run + validator + smoke) Implements ADR-164 Phase 2: scaffold the first concrete business pod (sales) end-to-end with a typed validator, a tick runner, and full smoke coverage. Dry-run mode is the Phase 2 default; --live mode exits 3 with a "Phase 3" message until Managed Agent + claude-p wiring lands. New surface (~1,597 LOC): v3/@claude-flow/cli/src/business-pods/pod-schema.ts — PodTemplate type + validatePodTemplate() + KNOWN_AGENT_TYPES mirror v3/@claude-flow/cli/src/mcp-tools/business-pod-tools.ts — 1 new MCP tool (business_pod_validate) plugins/ruflo-business-pods/ .claude-plugin/plugin.json templates/sales.json — Phase 2 sales pod template (matches ADR-164 §4.1 + §3.3 schema) scripts/pod-tick.mjs — dry-run tick runner + file-based budget ledger (TODO(adr-164.1): swap to atomic SQLite tracker) scripts/pod-tick.test.mjs — 15 node:test tests scripts/smoke.sh — 8-step smoke contract skills/pod-sales/SKILL.md .github/workflows/business-pods-smoke.yml — CI gate Gates (all PASS): - Build: clean - vitest business-pod-tools: 19/19 PASS - node:test pod-tick: 15/15 PASS - ADR-112 audit: 350/350 with guidance (was 349; +1 new tool) - business-pods smoke: 8/8 PASS - agentbbs regression smoke: 8/8 PASS (no regression from Phase 1) - agenticow regression smoke: 8/8 PASS (no regression from PR #2500) Honest Phase 2 deviations: - Schema validator duplicated TS / JS (single canonical TS in cli/src/, JS copy in pod-tick.mjs so script runs without prebuilt CLI). Phase 3 consolidates via shared agents/registry.ts. - Budget ledger is file-based JSON stub; ADR-164.1 atomic SQLite tracker swap deferred to Phase 3. - agentbbs subprocess wiring not invoked; envelopes go directly to the Phase 1 JSONL backing store (matches ADR-164 §5.2.2 phase plan). Phase 3 follow-ups inline-marked TODO(adr-164.1) / TODO(phase-3). Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01WbBa4nccx5aGhXphoHxcni * feat(pods): Phase 3 — remaining 6 pods + domain-affinity router policy Implements ADR-164 Phase 3: templates for marketing/finance/ops/support/hr/exec + the @metaharness/router domain-affinity policy hook. New templates (6) at plugins/ruflo-business-pods/templates/: marketing.json SOC2, $40/mo, cloud-tolerant finance.json GDPR, $80/mo, local-pinned, expiryMs=180_000 (3min) ops.json SOC2, $50/mo, local-pinned, synthetic-HTTP bench scenario; TODO(phase-3-http-fetch) for the unbuilt http_fetch tool support.json SOC2, $40/mo, cloud-tolerant hr.json GDPR, $30/mo, local-pinned (employee PII = finance-grade) exec.json GDPR, $60/mo, local-pinned, references §3.5.4 founder-bootstrap trust elevation for share-context on Day 1 New code: v3/@claude-flow/cli/src/business-pods/domain-affinity-policy.ts (86 LOC) - selectAgentBackend(pod): {backend, reason} - rules: preferLocalExecution=true → local-stdio else if budget.monthlyUsd >= 50 → cloud-managed else → remote-peer v3/@claude-flow/cli/src/mcp-tools/business-pod-tools.ts (+107 LOC) - new MCP tool: business_pod_route_backend - shared loadTemplate(input: object | string) helper Schema mirror in lockstep: added `base-template-generator` to KNOWN_AGENT_TYPES in BOTH TS (pod-schema.ts) and JS (pod-tick.mjs) copies. Gates (all PASS): - Build: clean - vitest business-pod-tools: 44/44 PASS (was 19; +18 Phase 3 cases) - node:test pod-tick: 21/21 PASS (was 15; +6 happy-path tests) - ADR-112 audit: 351/351 with guidance (was 350; +1 new tool) - business-pods smoke: 11/11 PASS (was 8; +3 Phase 3 steps) - agentbbs regression smoke: 8/8 PASS - agenticow regression smoke: 8/8 PASS Phase 4 follow-ups (inline TODOs): - http_fetch MCP tool implementation (referenced by ops.json) - @metaharness/router wire point in policy-engine.ts - ruflo federation trust elevate ... --to TRUSTED CLI escape hatch - Live-mode dispatch (Managed Agent + claude -p wiring) - Atomic SQLite budget tracker swap (ADR-164.1) Per ADR-164 §3.4, all rules codified verbatim; smoke step 11 + 4 vitest cases assert what the rules produce per template. Live mode still exits 3 with "Phase 4" message — Phase 3 is templates + validator + routing-policy only. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01WbBa4nccx5aGhXphoHxcni * feat: Phase 4 — http_fetch + atomic budget tracker + federation trust elevate Three Phase 4 deliverables landing per ADR-164: A. http_fetch MCP tool (ADR-164 §5.1.8) - 1 new tool (#352) for ops-pod's synthetic-endpoint bench - Security-default-rejects: private IPs (RFC-1918, localhost), auth headers (Authorization/Cookie/X-Auth-*) unless env vars opt-in - URL allowlist + timeout + response-size cap (256 KB default, 1 MB max) - 25/25 vitest pass (URL allowlist, timeout abort, truncation, auth-header rejection, happy path against node:http mock) B. Atomic SQLite budget tracker (ADR-164.1 §3-§5) - bbs_budget_rooms + bbs_budget_reservations tables - reserve/commit/release with BEGIN IMMEDIATE + _lock_bump - committed_post_expiry state machine (Expired Commit Leak fix from peer review 2026-06-29) implemented - expires_at clamped to [5_000, 300_000] ms per-pod override - Behind CLAUDE_FLOW_BBS_ATOMIC_BUDGET=1 — file-based stub remains the default until next flip; pod-tick.mjs migration deferred to Phase 5 - 15/15 vitest pass (Tests 1, 2, 3, 6 from ADR-164.1 §9, skipping Test 5 clock-skew which would require system-clock manipulation) - reservationId is randomUUID v4 (ADR-164.1 §3.2 notes v7 is purely aesthetic — v7 generator would add a transitive dep we don't need) C. ruflo federation trust elevate CLI (ADR-164 §3.5.4) - New subcommand: bypasses 500-interaction TRUST_TRANSITION_THRESHOLDS - Mandatory --reason + --audit flags (both required by spec) - Refuses unknown peer node-ids - Reuses existing trust_level_changed audit envelope with metadata.tag='bootstrap_elevation' + metadata.operatorBypass=true (cheapest path — keeps SEVERITY_BY_EVENT_TYPE / CATEGORY_BY_EVENT_TYPE stable rather than introducing a new event-type that needs propagation through downstream consumers) Gates (all PASS): - Build: clean (tsc) - vitest http-fetch + bbs-budget-tracker: 40/40 PASS (25 + 15) - vitest business-pod-tools regression: 44/44 PASS (Phase 3 still green) - ADR-112 audit: 352/352 with guidance (was 351; +1 http_fetch) - business-pods smoke: 11/11 PASS - agentbbs smoke: 8/8 PASS - agenticow smoke: 8/8 PASS Phase 5 follow-ups (explicit deferrals): - Live-mode dispatch (pod-tick --live still exits 3) - Sweeper timer integration site (daemon worker, not pod-tick) - Spend-reporter audit adapter (write to federation_spend namespace BEFORE BEGIN IMMEDIATE per ADR-164.1 §6.1) - pod-tick.mjs migration from file-based to atomic tracker - ACL gate on `federation trust elevate` (currently consistent with `federation evict` / `reactivate` — no founder-only crypto check yet) - http_fetch wiring into pod-tick live-mode for ops bench Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01WbBa4nccx5aGhXphoHxcni * chore(lock): regen v3/pnpm-lock.yaml with agentbbs entry (PR #2503 CI fix) Same fix as we did on the agenticow PR #2500 push — pnpm install at root regenerates pnpm-lock.yaml (the npm workspace), but the real workspace is v3/pnpm-workspace.yaml and its sibling v3/pnpm-lock.yaml. CI uses --frozen-lockfile so the v3 lockfile must be in sync with v3/@claude-flow/cli's package.json (which declares agentbbs: ~0.1.0 as optional). * fix(ci): re-export-tolerant agentbbs guard + skip business-pods smoke under --ignore-optional PR #2503 CI surfaced 2 ADR-164 issues: 1. no-agentbbs-smoke Rule 2 false-positive on mcp-tools/index.ts. The static-grep treated `export { agentbbsTools } from './agentbbs-tools.js';` as an unguarded agentbbs reference. The agentbbsTools symbol itself contains the loadAgentbbs() guard — the index.ts re-export is benign. Fixed: strip re-export + spread + import-of-agentbbsTools patterns + all comments before the unguarded-reference check. 2. ruflo-business-pods smoke 9/11 under no-metaharness-smoke's runtime drill (which does `npm install --ignore-optional`). Without optional deps, vitest may be absent AND the cli dist may not exist. Steps 4 (vitest) and 11 (call into dist) now SKIP cleanly when those are missing instead of failing — the main test-suite jobs still gate the full vitest + build runs. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(ci): business-pods-smoke uses pnpm (root npm chokes on workspace:*) The root package.json declares some workspace deps with the pnpm `workspace:*` protocol that npm doesn't support. Symptom: `npm install` step exited with EUNSUPPORTEDPROTOCOL. The v3/ tree is a real pnpm workspace (v3/pnpm-workspace.yaml). Switch the workflow to install + build from there using pnpm + frozen-lockfile, then fall through to vitest / node:test / smoke contract. Pattern matches the existing v3-ci.yml install steps. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(ci): pnpm --filter '@claude-flow/cli...' to include workspace deps The previous `pnpm --filter @claude-flow/cli build` didn't build workspace dependencies first, so the cli's tsc step failed with TS2307 errors looking for @claude-flow/memory, @claude-flow/neural, @claude-flow/cli-core types. The `...` suffix tells pnpm to include all of the cli's workspace dependencies in the build graph. Applied to both business-pods-smoke.yml and no-agentbbs-smoke.yml's runtime drill. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(ci): pnpm -r build (whole workspace, dep-first) — pnpm filter can't discover cross-package dist/ imports Round 4 surfaced two pre-existing fragilities in the workspace's build wiring: 1. business-pods-smoke: @claude-flow/cli imports from @claude-flow/swarm/dist/ not via package.json deps but via direct file path. `--filter "@claude-flow/cli..."` doesn't traverse those. Fixed: `pnpm -r --no-bail build` builds the whole workspace dep-first, with a fallback to a direct cli build. 2. no-agentbbs-smoke runtime drill: `--no-optional` stripped agentdb, which is a transitive consumer of @claude-flow/neural and breaks neural's tsc even though neural has its own optional-degradation path internally. Fixed: install everything, then SURGICALLY rm node_modules/agentbbs only, so only the dep we're testing graceful-degradation for is missing. Co-Authored-By: RuFlo <ruv@ruv.net> |
||
|
|
a63cdf0526 |
feat(metaharness): integrate @metaharness/redblue@~0.1.1 — adversarial red/blue LLM testing
ADR-150 sibling-package integration. `@metaharness/redblue` runs an
adversarial red-team → judge → blue-team patch → retest → report loop
against an LLM target you own. Covers OWASP LLM Top-10 / NIST AI RMF
families (prompt injection, tool misuse, data leakage, jailbreaks,
denial-of-wallet). Joins score/genome/mcp-scan/threat-model/oia-audit
as the run-time red/blue layer of the security plugin surface.
Why integrate here vs ruflo-aidefence or ruflo-security-audit:
- aidefence is detection (PII / prompt-injection scan); redblue is
*generation* — they complement, redblue doesn't replace it
- security-audit is dep CVEs / static scan; redblue is run-time LLM eval
- ruflo-metaharness already carries the @metaharness/* wrapper pattern
(`_darwin.mjs`, `_harness.mjs`), the locator, the MCP-tool registration
surface, and the smoke/CI gates — redblue slots in as one more sibling
Surface added:
- `npx ruflo metaharness redblue {init|run|patch|attack|report}` CLI
- `metaharness_redblue` MCP tool (subcommand + family + tests + patch +
mockJudge + alertOnFail + timeoutMs)
- Skill discoverability via CLAUDE.md (both CLI catalog + MCP tool catalog)
- 11 metaharness subcommands (was 10), 13 MCP tools (was 12)
Safety posture (inherited verbatim from upstream; wrapper does NOT relax):
- upstream `src/config/safety.ts` hard-enforces no real credentials, no
live external targets (loopback / *.test / *.internal only), no shell,
no arbitrary network, no eval at config-load time — a config that
enables any of these errors at load
- `--mock-judge` is the $0 marker-fixture path for CI / offline; the real
model judge requires `OPENROUTER_API_KEY` and is capped by
`max_cost_usd` / `max_runtime_minutes` / `max_tests`
- wrapper shells out with `shell: false` argv arrays; never string interp
- ADR-150 graceful-degradation rule #3 — on install/network failure the
wrapper returns `{degraded: true, reason: ...}` and exits 0 so ruflo
stays operational
Upstream bug workaround (track for revert):
`@metaharness/redblue@0.1.1`'s CLI bootstrap uses
`isMain = import.meta.url === \`file://\${process.argv[1]}\``. When npx
links `redblue` → `dist/cli/index.js`, argv[1] is the symlink path but
import.meta.url is the resolved real path, so isMain is false and the
binary silently exits 0 with no output, no file, no error. Darwin's CLI
calls `main()` unconditionally and is unaffected. Workaround: install the
package once into `~/.ruflo/redblue-cache` via `npm install --prefix` and
invoke `node <abs_path>/dist/cli/index.js` directly — argv[1] then equals
the real path and isMain becomes true. First install hits npm's cache so
the cold path is ~200ms; warm invocations are ~20ms.
Locator hardening (prevents recurrence of artifact-mirror staleness):
The publish artifact at `v3/@claude-flow/cli/plugins/ruflo-metaharness/`
is regenerated only at `prepublishOnly`. On a dev tree with a fresh
script in source but a stale mirror, the locator would pick the mirror
because `_harness.mjs` exists in both. Locators in `commands/metaharness.ts`
and `mcp-tools/metaharness-tools.ts` now accept an optional `requiredScript`
that narrows the match to dirs containing the dispatched script. The
artifact mirror is also synced with the two new redblue scripts so the
publish artifact stays usable until the next `prepublishOnly`.
Gates updated to lock new state:
- smoke.sh: 9 subcommands in dispatcher (was 8), 11 in description list
(was 10), 13 MCP tools (was 12), 13 mcp-script-count (was 12), 14
MCP_SUCCESS_SEMANTIC footnotes (was 13), 13 `success: r.success`
occurrences (was 12), tilde-pin check covers @metaharness/redblue
- test-mcp-tools.mjs: `tools.length === 13`, `metaharness_redblue`
enumerated, attack-preview Phase-3 input, 180s timeout (cold-cache npx
fetch class)
- CLAUDE.md: surfaced under both CLI catalog and MCP-tool catalog (gated
by smoke 17z56 + 17z57)
Verification:
- `bash plugins/ruflo-metaharness/scripts/smoke.sh` → 120 passed, 0 failed
- `node plugins/ruflo-metaharness/scripts/test-mcp-tools.mjs` → 153 passed, 0 failed
- `node v3/@claude-flow/cli/bin/cli.js metaharness redblue init --out <path>`
→ writes the safety-asserting sample config, exits 0
- attack preview, mock-judge run (3-test → real findings report), and
CLI dispatcher all exercised end-to-end before commit
Co-Authored-By: RuFlo <ruv@ruv.net>
|
||
|
|
12ed081f6b |
darwin-plugins iter 4 — ruflo-security-audit: --depth full → --depth deep (CLI doesnt support full)
This iter-4 edit was made by the headless agent but its commit step didnt land in the branch (rate-limit at end of workflow). Cleanly landing it now alongside the lockfile fix. |
||
|
|
0b5badac7e | darwin-plugins iter 3 — ruflo-security-audit: align memory_store namespace to documented security-findings |