Commit Graph
238 Commits
Author SHA1 Message Date
randRuFlo a457cbf4dc fix(plugins): clean claude.ai plugin-validator findings (nested manifest, unknown keys, XML tags in skill descriptions)
- ruflo-plugin-creator 0.4.2: mod template manifest is plugin.json.tmpl (no nested plugin); scripts/scaffold-mod.mjs + test-scaffold-mod.mjs validate and test a scaffolded mod
- ruflo-workflows 0.6.5 / ruflo-arena 0.2.4 / ruflo-graph-intelligence 0.2.0-alpha.5: drop unused components / categories keys
- ruflo-metaharness 0.2.5: seven harness-* descriptions use {braces}, not angle brackets
- ruflo-ai-team 0.2.3 (+directory 0.1.1) / ruflo-x-gateway 0.9.5 (+directory 0.8.2): documentationUrl points at main, not the feat branch; store-listing keys kept (the directory reads them)
- scripts/check-plugin-manifests.mjs wired into all-plugins-smoke

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-09 10:18:04 -04:00
rUv 54628be615 Merge branch 'main' into fix/console-0.40.2-security 2026-10-08 15:02:44 -07:00
randRuFlo 5db1c788b6 ci: run the native Codex hook tests with node --test, not the root vitest ratchet
Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-08 17:52:51 -04:00
r 4a133615c7 Merge branch 'pr-3523' into feat/release-3.56.0-batch 2026-10-08 16:54:34 -04:00
Mr. Makandruflo 2015071b98 fix(plugins): run the installed ruflo CLI before npx @claude-flow/cli@latest in cost-tracker, adr and metaharness (#3558)
A plugin installed from the Claude Code marketplace is copied on its own to
~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/, with no CLI next
to it. ruflo-cost-tracker and ruflo-adr always spawned
`npx -y @claude-flow/cli@latest` for their memory/hooks calls, and #3366's
resolveRufloCli() in ruflo-metaharness only looks for the CLI that ships the
plugin or a repo checkout, so all three fell back to npx @latest there. For
cost-tracker that is every turn (its Stop hook): a registry round trip per
call, and a different CLI than the one the user installed, including a
release published minutes earlier. Each such run also refreshes
~/.claude/helpers ahead of the installed CLI.

All three plugins now resolve the CLI in the same order:
1. CLI_CORE=1: unchanged (npx @claude-flow/cli-core@alpha, ADR-100); adr
   still ignores it (#2781).
2. The CLI that ships the plugin, or a repo checkout, with #3366's guards
   (package.json named @claude-flow/cli + bin/cli.js + dist/src/index.js).
3. New: `ruflo`, then `claude-flow`, on PATH. fs-only probe (X_OK + isFile),
   shell: false; relative and empty PATH entries are skipped; on win32 an npm
   `.cmd` shim is mapped to its package's bin entry and run with node, and
   anything unmappable is skipped, never run through cmd.exe.
4. The unchanged npx @latest fallback.

- ruflo-cost-tracker: new scripts/_ruflo-cli.mjs; spawnNpxSync() in _npx.mjs
  sends `@claude-flow/cli@latest` to the resolved CLI (signature unchanged,
  no call-site edits).
- ruflo-adr: new scripts/lib/ruflo-cli.mjs; import.mjs, reindex.mjs and
  verify.mjs use it (verify too, so reader and writers share a store, #2781).
- ruflo-metaharness: step 3 added to resolveRufloCli() in _invoke.mjs.
- Test seams RUFLO_PLUGIN_SKIP_LOCAL_CLI=1 / RUFLO_PLUGIN_SKIP_PATH_CLI=1
  (same style as RUFLO_METAHARNESS_SKIP_LOCAL). The existing npx-stub tests
  (import-root-exit-3097, verify-read-safety-3147, test-no-tool-time-npx)
  set them; without that they fail on any machine with ruflo on PATH.
- New regression tests for each plugin, run in a marketplace-style plugin
  cache with fake ruflo/claude-flow/npx on PATH: fail on 49da6ecc4
  (cost-tracker 5/12, adr 2/6, metaharness 5/8), pass with the fix (14/14,
  6/6, 8/8). Wired into v3-ci.yml and metaharness-ci.yml.

Co-Authored-By: ruflo <ruv@ruv.net>
2026-10-05 09:57:49 -07:00
ruv 02e4305ff9 ci: run the status-forwarding tripwire in the fleet smoke workflow (ADR-450 T7)
Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-05 05:40:35 -04:00
ruv 84c435df0d ci: install esbuild (0.25.0, into RUNNER_TEMP) before the fleet smoke — the guard-probe row needs it
smoke-all and smoke-without-metaharness failed on this PR with 'probe-mod-guards: esbuild not found'.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-04 21:48:13 -04:00
ruv b1a6243a86 Merge remote-tracking branch 'origin/main' into loop/screen-vendor
# Conflicts:
#	.github/workflows/all-plugins-smoke.yml
2026-10-04 21:06:45 -04:00
ruv 71bf281bf0 refactor(mods): one source for the secret screen — sync-mod-screen.mjs regenerates the shared region of 37 screen.ts copies, --check in all-plugins-smoke (ADR-445)
Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-04 19:03:37 -04:00
ruv 00eed549d4 test(mods): check that every tool name a plugin guard watches exists in the registry
Adds scripts/check-guard-tool-names.mjs, an allowlist for tools other servers
serve, node:test tests, and a step in the all-plugins-smoke workflow. Fails
loud when nothing is extracted or the registry is empty.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-04 19:02:02 -04:00
rUv 1b62011183 fix(guidance): close source identity, offline export and serialization edge cases 2026-10-04 13:43:46 -07:00
rUv 2207f8779a feat(guidance): bind mod exports to Git blobs and explain review signals 2026-10-04 13:38:29 -07:00
Rudy Celekli 8d81c77e79 merge: refresh Windows doctor fix against current CI baseline
Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>
2026-09-30 21:32:50 -04:00
ruv 07fc94197a fix(ci): deterministic browser pattern ids; macOS node-gyp Python for Build V3
reasoningbank-adapter: generatePatternId() appended Date.now(), so a repeated
successful trajectory merged into its existing pattern only when both calls
fell in the same millisecond. Otherwise it created a duplicate pattern with
usageCount 1 (the adapter never learned from repeats), and the
"update existing pattern" test failed intermittently in CI. The id is now a
pure function of the goal (slug + FNV-1a of the full normalized goal). A new
test pins Date.now to two different values; it fails on the old code.

v3-ci Build V3 (macos-latest): when the better-sqlite3/hnswlib-node prebuild
download times out, node-gyp compiles from source and macos-latest's Python
3.12 has no distutils. Pin Python 3.11 on macOS so the fallback works.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-09-29 12:54:20 -04:00
ruv 0423eaf7fb fix(release): preserve archives and refresh version witnesses 2026-09-28 17:16:02 -04:00
ruv e69bc340cf feat(federation): add guarded public registration and release 3.47.0 2026-09-28 17:16:02 -04:00
Rudy Celekli 665e62df2e ci(doctor): run Windows regression after workspace build 2026-09-28 10:17:46 -04:00
Rudy Celekli c3b025a9d1 fix(doctor): import MetaHarness modules via file URLs on Windows 2026-09-28 10:12:07 -04:00
Rudy Celekli ff4b2af271 fix(metaharness): handle help and reject unknown arguments before execution 2026-09-26 19:38:32 -04:00
ruv 012daa5fed Merge branch 'pr-3389' into integ/train-3.45.1 2026-09-26 11:32:48 -04:00
ruv 43901973b6 Merge branch 'pr-3343' into integ/train-3.45.1 2026-09-26 11:32:47 -04:00
ruv 4a6e063de7 Merge branch 'pr-3391' into integ/train-3.45.1 2026-09-26 11:32:47 -04:00
ruv 2f192d4eb9 Merge branch 'pr-3367' into integ/train-3.45.1 2026-09-26 11:32:47 -04:00
ruv 9be73667da Merge branch 'pr-3423' into integ/train-3.45.1 2026-09-26 11:32:47 -04:00
rUvandClaude 1e7a33a768 dream(security): #3384 add PATH/search-path family to validateEnv() denylist (evaluated, ACCEPT-scoped) (#3385)
* dream(security): #3384 add PATH/search-path family to validateEnv() denylist (evaluated, ACCEPT-scoped)

validateEnv()'s loader-hijack denylist blocked LD_PRELOAD/NODE_OPTIONS-class
vars but not PATH itself or the equivalent interpreter/VCS search-path
family (CWE-427) -- flagged grade-A by last night's security scan.

Adds PATH, PYTHONPATH, PERL5LIB, RUBYLIB, GIT_EXTERNAL_DIFF,
GIT_SSH_COMMAND, BASH_ENV, IFS to the denylist, closing the only
MCP-reachable exposure (terminal_create/terminal_execute). An independent
adversarial critic found the lookup was case-sensitive (path/Path bypass
it on case-insensitive Windows); fixed same session via .toUpperCase()
case-folding, which also hardens the original 9 names.

13 new discriminating tests (32/32 total), baseline-fails/candidate-passes
verified via git-stash isolation and independently reproduced twice by
the critic. Full suite failed-file set byte-identical to baseline (36
pre-existing environmental failures) -- zero new failures.

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

* docs(dream-cycle): update LEDGER.md with 2026-09-21 security row

Records tonight's PATH/search-path denylist fix, the new 2026-09-13/14
no-run gap (confirmed via git ls-remote), the 09-15..09-20 backlog
(confirmed real, not transcribed per established convention), and a
correction that search_pull_requests's merged field is stale/unreliable
(pull_request_read is authoritative).

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

* fix(ci): build+overlay local @claude-flow/cli-core before running the test ratchet

PR #3385's own new tests (validate-input-env.test.ts) passed locally but
failed in CI's Test Suite job: package-lock.json resolves
@claude-flow/cli-core from the published npm registry tarball, not an
npm workspace link (unlike @claude-flow/security, which the previous
step in this same job already handles for the same reason). Since
scripts/ci-test-ratchet.mjs runs vitest from repo root,
v3/@claude-flow/cli's tests resolve @claude-flow/cli-core by walking up
to root node_modules -- the stale published dist/, never the local
source. Building the local package and overlaying its dist/ onto the
installed one (same version, so not a version bump) makes CI test the
actual PR diff instead of a stale registry snapshot, without touching
package-lock.json.

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

* dream(security): #3384 re-validate persisted session.env at execute time

Post-review fix on PR #3385: terminal_execute() trusted session.env from
the on-disk store unconditionally after terminal_create's one-time
validateEnv() call. A session persisted before a denylist update (or a
store.json edited/restored out of band) could carry a now-denylisted
env var (PATH, etc.) forever, silently bypassing tonight's fix -- and
the original 9-name denylist -- for every execute on that pre-existing
session.

terminal_execute now re-validates session.env via validateEnv() and
drops it to {} on failure rather than merging it unchecked, so a stale/
tampered session degrades to a clean environment instead of blocking.
2 new tests (stash-isolated: fails on the pre-fix handler, passes on
the fix); full package suite unaffected (same 36 pre-existing
environmental failures as before this commit).

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-26 10:07:43 -05:00
Rudy Celekli ea4661f815 fix(mcp): enforce opt-in policy in shipped stdio launchers 2026-09-25 14:01:48 -04:00
ruvnetandClaude Opus 5 1116724a65 ci(guard): make the leaf-publish diff check actually run in PR CI
The guard's first real CI run passed but reported:

  NOTE: diff check NOT EXERCISED — could not diff against 'origin/main' (shallow clone?)

So only the range check ran; check 2 — the half that replays #3335/#3390 by
catching a leaf source change with no version bump — sat out entirely. The
NOT EXERCISED note is what made that visible instead of reading as a pass,
which is precisely why a skipped check must never print like a clean one.

Two causes, both fixed:

  - `git fetch --depth=1 origin $GITHUB_BASE_REF` fetches the base tip with no
    shared history, so `base...HEAD` dies with "fatal: no merge base". Now
    fetches --depth=50 with an explicit
    +refs/heads/<base>:refs/remotes/origin/<base> refspec.
  - The script only attempted three-dot. It now falls back to two-dot, which
    needs no common ancestor, and reports which mode actually ran.

Verified against the exact failing condition — an orphan branch with no merge
base against origin/main, where `git diff origin/main...HEAD` returns
"fatal: no merge base": the guard now reports "diff check ran (two-dot ...)"
rather than skipping. NC1 re-confirmed: a leaf source change without a version
bump still fails, now accompanied by "diff check ran (three-dot ...)" proving
the check executed rather than being skipped into a pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 17:20:11 -04:00
ruvnetandClaude Opus 5 f02759cd6d ci(guard): fail when a non-bundled @claude-flow/* leaf changes without a publish
Adds scripts/audit-leaf-package-publish.mjs, wired into v3-ci.yml's existing
audit job (semver is already installed there).

Only four @claude-flow/* components are bundled into the release tarballs —
INTERNAL_RUNTIME_PACKAGES in stage-internal-runtime-bundles.mjs: security,
codex, mcp, plugin-agent-federation. cli-core, neural, shared and memory
resolve from the REGISTRY at whatever v3/@claude-flow/cli/package.json pins, so
a merged source change in one of those does NOT ship with the three-package
train: the release silently carries the last published copy.

This has bitten twice. #3334 fixed RUFLO_INTELLIGENCE_MODE in
@claude-flow/memory; v3.42.1 shipped the train only and a fresh consumer
install had zero references to the fix (#3335). #3390's reasoningBank
activation spanned cli/src/memory/memory-bridge.ts AND
memory/src/controller-registry.ts — publishing only the train would have
shipped the bridge half and left the embedder half unreachable, leaving
reasoningBank exactly as inert as before. It was caught by hand and
memory@3.0.0-alpha.25 went out alongside v3.42.5.

What makes it easy to miss is that CI goes GREEN: the leaf's own package tests
run against workspace source, so the change looks verified while being
unshippable.

The audit asserts:
  1. every non-bundled leaf's workspace version is covered by the CLI's
     declared range (a miss means the release resolves to OLDER code than
     this repo's source);
  2. diff mode — if a leaf's publishable source changed vs the base ref but
     its version did not, fail with remediation.

It imports INTERNAL_RUNTIME_PACKAGES rather than restating it, so the bundled
and non-bundled sets cannot drift apart; exits non-zero if it inspects zero
leaves, so a broken classifier can't report clean; and reports a skipped diff
check as NOT EXERCISED rather than as a pass.

@claude-flow/shared is entered as a time-boxed accepted finding (expires
2026-10-21): the CLI pins 3.0.0-alpha.7 while the workspace is at
3.0.0-alpha.8, which IS published — stale, not unshippable. Raising the pin
edits v3/@claude-flow/cli deps and so forces a pnpm-lock.yaml regen in the same
change (#2540 -> #2552), which wants its own PR. An EXPIRED entry fails the
guard rather than muting it.

Negative-controlled, all four paths:
  - memory source changed, no bump      -> FAIL (replays #3390)
  - same change with a version bump     -> pass
  - test-only change in a leaf          -> pass (not publishable)
  - waiver expiry backdated             -> FAIL, "no longer honoured"

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 16:49:17 -04:00
ruvnetandClaude Opus 5 9b07e5b789 fix(release): don't build workspaces in the release-audit install step
The v3.42.5 release (run 35641592705) failed at "Install root release-audit
dependencies", 9 steps before any publish. `npm ci --omit=dev --omit=optional`
triggers v3/@claude-flow/security's "prepare": "npm run build" (it is both a
root workspace and a root dependency). That package does not declare
@types/node, relying on a hoist from the root dev tree, so --omit=dev leaves
tsc with no Node types and --omit=optional removes @napi-rs/keyring:

  TS2591: Cannot find name 'crypto' / 'Buffer' / 'process'
  TS2307: Cannot find module '@napi-rs/keyring'

The step only needs `semver` for audit-umbrella-version-lockstep.mjs, so it
should not link or build workspaces. Verified locally against the tagged tree:
the current command reproduces the CI failure exactly; adding --workspaces=false
installs 175 packages and the lockstep audit passes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 15:22:54 -04:00
rUv 75c43591a6 ci: use supported Node engines for resource admission 2026-09-21 15:01:31 +00:00
rUv 46c88e0b88 ci: run full resource admission benchmark report 2026-09-21 14:59:10 +00:00
rUv 215e284a51 ci: use supported Node 22 engine for resource admission 2026-09-21 14:58:11 +00:00
rUv 359a49e97f ci: add resource admission validation workflow 2026-09-21 14:55:32 +00:00
Mr. MakandClaude Opus 5 0a2df2bfd2 fix(metaharness): use the @metaharness/* and ruflo CLI already installed instead of npm-installing at tool-call time (#3366)
On a stock ruflo 3.42.4 install every metaharness_* MCP tool reached for the
npm registry at tool-call time, although the packages it needs are already on
disk. Two causes, in the same six helper files, which is why they ship
together: the helper PINS rejected what ruflo installs, and three code paths
never looked for an installed copy at all. Fixing only the pins leaves darwin
and the memory tools on npx; fixing only the lookups leaves them resolving a
pin that nothing on disk satisfies.

1. Pins (metaharness, @metaharness/darwin)

The metaharness_* tools resolve MetaHarness through the plugin helpers' own
tilde pins, not through package.json. Those pins were never bumped:
_harness.mjs stayed at ~0.3.0 after the CLI declared metaharness ^0.4.1
(b8ec03f34), and _darwin.mjs stayed at ~0.8.0 while the CLI moved
@metaharness/darwin to ~0.9.0 (#2958) and ~0.10.2 (#3262). On a stock install
(metaharness 0.4.2, darwin 0.10.2) findLocalPackageDir() therefore rejected
the bundled metaharness, and every score / genome / mcp_scan / threat_model /
oia_audit / learn call ran
`npm install --prefix ~/.ruflo/metaharness-cache-0.3.0 metaharness@~0.3.0`
at tool-call time — a download of the older 0.3.2 (180 s install budget inside
a 120 s MCP subprocess budget), degraded offline. The darwin tools fetched
darwin 0.8.x through npx while gepa imported the bundled 0.10.2.

- _harness.mjs: METAHARNESS_PIN_VERSION ~0.3.0 -> ~0.4.1 (= CLI ^0.4.1)
- _darwin.mjs:  DARWIN_PIN_VERSION      ~0.8.0 -> ~0.10.2 (= CLI ~0.10.2)
- scripts/check-metaharness-pins.mjs: the watcher compared only the CLI's
  declared ranges (and MH_DARWIN_PIN), so it reported "all current" while the
  pins that decide what runs were two minors behind. It now reads each
  helper's *_PIN_VERSION: the pin must be a `~X.Y.Z` range (the only form
  _invoke.satisfiesTildeRange accepts), and must cover the declared range, or
  admit npm latest when the CLI declares nothing (redblue). An unreadable
  constant is fatal. `satisfies` now shares a `bounds()` helper with the new
  `covers()`; the results for the existing rows are unchanged.
- metaharness-pin-drift.yml also triggers on the three helper files.
- The two current-pin comments in TypeScript (distill-oracle.ts MH_DARWIN_PIN,
  metaharness-tools.ts ADR-153 header) named the old ~0.8.0 plugin pin; they
  now name ~0.10.2. Comments only, no behaviour.

2. No npm/npx at tool-call time (darwin, redblue, memory)

- _darwin.mjs ran `npx -y -p @metaharness/darwin@<pin> metaharness-darwin` on
  every evolve / bench / security_bench call: npm resolved the range each
  time, it ignored the optionalDependency the CLI ships, and it could run a
  different darwin than the gepa import in the same plugin. It now resolves
  like _harness.mjs — installed copy satisfying the pin, then the one-time
  versioned darwin-cache-<pin> that gepa already shares, then
  `process.execPath <abs bin>` with shell:false.
- _redblue.mjs always npm-installed into redblue-cache-<pin> on first use,
  never looking at the installed copy (ruflo 3.42.4 carries 0.1.4). It now
  checks the installed copy first. The chosen CLI path is realpath'd, because
  redblue only dispatches when import.meta.url === file://argv[1]; a
  pnpm-symlinked package dir (or a cache base under /tmp on macOS) otherwise
  makes it exit 0 having done nothing. Its spawn moves to process.execPath
  with shell:false: the argv carries MCP-supplied --config/--out/--in, which
  under the previous `shell: win32` went through cmd.exe unquoted.
- audit-list / audit-trend / oia-audit / similarity ran
  `npx @claude-flow/cli@latest memory ...` — an npm-registry resolution per
  call (the check _harness.mjs already removed for its own npx path), and
  possibly a different CLI version than the server that spawned the tool
  (#3306; cold-cache npx cost #3145/#3154). Without registry access oia_audit
  reported persisted:false and audit_list silently showed 0 records. They now
  call _invoke.runRufloCli(): process.execPath + bin/cli.js of the
  @claude-flow/cli that ships the plugin (<cli>/plugins/ruflo-metaharness, or
  v3/@claude-flow/cli in a checkout). A candidate needs dist/src/index.js next
  to bin/cli.js (mcp-launch.cjs's unbuilt-checkout guard) plus a package.json
  named @claude-flow/cli (a new check, so an unrelated parent directory can
  never qualify). CLI_CORE=1 still opts into `npx @claude-flow/cli-core@alpha`,
  and a layout with no usable local CLI still falls back to
  `npx @claude-flow/cli@latest`.
- drift-from-history.mjs: drop its unused CLI_PKG constant.

The two new lookups end in a spawn of a package bin with MCP-supplied argv, so
they pass findLocalPackageDir(..., { fromCwd: false }) and search only the
ruflo install that owns the plugin — never the tool's cwd or its ancestors.
The pre-existing metaharness lookup keeps its cwd walk unchanged.

Tests (both hermetic, published layout, stub packages, npm/npx PATH trap):

- test-pin-alignment.mjs (+ smoke 17z74a): 3 passed / 9 failed on main,
  12 / 0 here.
- test-no-tool-time-npx.mjs (+ smoke 17r2): 7 passed / 19 failed on main,
  26 / 0 here. It covers both darwin success branches (installed copy and the
  versioned cache), the memory round trip through the shipping CLI, the kept
  contracts (CLI_CORE, npx fallback, a bin/cli.js that is not
  @claude-flow/cli, darwin absent -> degraded exit 0), and the hardening
  above: a darwin/redblue in the tool's own cwd node_modules is never
  executed, a cache dir with package.json but no bin gets one reinstall
  instead of degrading forever, and neither helper spawns the resolved bin
  through a shell.

ADR-150 is unchanged: no static @metaharness import is added, no new
dependency, and every unresolvable path still returns the same
{degraded:true, reason} with exit 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KTedGmPKpEdArVBMMn3JaP
2026-09-20 11:55:51 -04:00
rUv 4884f1f7cc ci(federation): build shared dependency before descriptor validation 2026-09-16 14:38:25 +00:00
rUv fd6014ac5a ci(federation): validate planner safe descriptor benchmark 2026-09-16 14:35:18 +00:00
rUvandClaude 1bd734e272 dream(security): #3102 bind ADR-377 caller-identity verification into live authorizeMcpTool chokepoint (evaluated, ACCEPT-with-caveats) (#3103)
* dream(security): #TBD bind ADR-377 caller-identity verification into live authorizeMcpTool chokepoint (evaluated, ACCEPT-with-caveats)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-14 21:05:45 +00:00
ruv 506b3b90eb feat(federation): give ChatGPT Federation its own key and its own relay connection
buzz-relay refuses any EVENT whose pubkey differs from the NIP-42 identity that
authenticated the connection, so "sign it here, let the gateway relay it" is not a
policy we chose against — it is impossible. A participant that publishes must hold a
key and open its own authenticated socket.

This adds the smallest service that does that for the ChatGPT Federation connector:
three tools, no resources, public channels only.

Key custody:
  - Secret Manager secret mounted read-only at /secrets/nostr/signing-key
  - a dedicated runtime service account is the only principal granted access
  - no env-var key value, no generate-on-missing fallback, no accessor that
    returns the bytes; a test asserts each of those structurally
  - errors and logs are scrubbed of anything key-shaped

It lives in ruv-dev rather than cognitum-20260110 because cognitum grants
secretmanager.secretAccessor to the default compute service account project-wide,
and the x.ruv.io gateway runs as that account — so no per-secret binding there can
keep the gateway out. ruv-dev grants that account only roles/editor, which does not
include versions.access.

The caller token travels as x-caller-token, not Authorization: Cloud Run consumes
Authorization for its own IAM check and answers 401 before the container sees it.

Tests run a local NIP-42 relay that enforces the same identity binding as
buzz-relay, so publish is exercised end to end, including the case the whole design
turns on — an event signed by a key other than the authenticated one is refused.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67
2026-09-10 16:25:44 -04:00
rUv 72df96c297 feat(agentbbs): cross-host federation — signed envelopes, pinned peers, union merge (#3251)
* feat(agentbbs): cross-host federation — signed envelopes, pinned peers, union merge

Phase 1 gave every host a local append-only room log and derived roomId
deterministically from the room label, so two hosts that register #sales
already compute the same roomId without talking. This adds the layer that
actually moves envelopes between them.

Design. A room log is an append-only set of immutable envelopes, so reconciling
two hosts is a set union — there is no conflicting write to arbitrate and
nothing for a consensus round to decide. Union is commutative, associative and
idempotent, so sync is order independent and safe to retry. That is a
grow-only set keyed on envelopeId, and it is cheaper and less failure-prone
than the Byzantine agreement the plugin README gestures at.

What union does not give you is authenticity: if any peer can inject, the merge
faithfully replicates forgeries. So each host now holds a persistent Ed25519
identity (Phase 1's key was ephemeral per process, which no peer can pin),
every published envelope is signed, and a receiver verifies against the key it
pinned at peer-add time rather than one carried in the envelope — otherwise an
attacker signs with their own key and claims any origin.

Transport is pull-based HTTP: peers poll GET /agentbbs/v1/rooms/:roomId/
envelopes?since=N. Pull needs no inbound connectivity, tolerates a node being
offline, and leaves ingest volume under the receiver's control; push would make
every node an unauthenticated write target. The server binds 127.0.0.1 unless
a bindHost is passed explicitly, and exposes no route that mutates state.

Bounds are enforced receive-side where a sender cannot negotiate them away:
envelopes per sync, bytes per envelope, bytes per response, peer count, and a
hop limit that is incremented on merge so a cycle terminates. hops is excluded
from the signed material, since it is mutated in transit by design.

Five tools: identity, peer_add, peers, serve, sync.

Tested: 35 new tests, most of them the trust boundary — forged payload,
forged origin, key substitution, unpinned signer, cross-room injection, replay,
oversize, hop exhaustion, traversal at the HTTP boundary, and private key never
served. Convergence and idempotence are asserted against two real nodes over a
real socket, not mocks. Verified end to end with three independent nodes:
all three converge on the same envelope set, a second round merges nothing,
and an unpinned node's injection is rejected.

The Phase 1 structural test pinned the surface at exactly 4 tools; it now pins
9 and names both groups, so adding a tool stays a deliberate contract change.
Full package suite: 68 failures against 70 on main, +35 passing — no
regressions.

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

* ci(agentbbs): stop rule 2 firing on the federation wire namespace

ADR-164 rule 2 exists to stop `agentbbs` becoming a mandatory dependency: a
static `import ... from 'agentbbs'` is forbidden, and any other mention has to
sit behind loadAgentbbs / a dynamic import. Its catch-all is a substring test
for "agentbbs" anywhere in the file.

agentbbs-federation.ts trips that catch-all without importing anything. Its
only remaining mentions after comment-stripping are string literals that never
reach a module resolver: the '/agentbbs/v1/...' HTTP route prefix and the
'agentbbs:<kind>:' hash domain-separators.

Adds those two shapes to the existing benign-pattern strip list. The
static-import check is untouched and remains the real gate.

Verified this narrows the check rather than loosening it, by running the
workflow's own rule-2 body verbatim against fixtures:
  - the real federation module            -> passes (false positive gone)
  - `import { x } from 'agentbbs'`        -> still caught
  - unguarded runtime reference           -> still caught
  - guarded `await import('agentbbs')`    -> passes

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

* ci(agentbbs): feed rule 2 via a quoted heredoc so bash stops mangling it

The rule 2 fix in fda44cd1f passed locally but still failed in CI. Cause:
the check runs as `node -e "..."` — a bash double-quoted string — so bash
rewrites the script before node ever parses it:

  - `\\?` collapsed to `\?`, turning the intended optional-backslash
    lookahead into a lookahead for a literal '?'. It could never match
    `agentbbs/v1`, so the strip was a no-op and rule 2 kept firing.
  - backticks in the explanatory comments were command substitution, so
    bash actually executed `export { agentbbsTools } from ...` etc. and
    spliced their (empty) output back into the script. Pre-existing since
    #2503 — visible as `import: command not found` noise in every run.

Fix the boundary rather than re-escaping: feed the script through a
QUOTED heredoc (`node - <<'RULE2'`), which bash passes through verbatim.
Regex backslashes and comment backticks now reach node as written. Also
drops backslashes from the new lookaheads so the pattern is robust even
if the step is ever converted back to an inline string.

Verified by executing the step's actual `run:` block through `bash -c`
(parsed out of the YAML), not a hand-retyped copy — that gap is what hid
the bug. Negative controls, all through that same path:

  static import of agentbbs ............................. CAUGHT
  unguarded runtime ref (deps['agentbbs']) .............. CAUGHT
  wire route + real unguarded ref (bypass test) ......... CAUGHT
  guarded dynamic import ................................ pass
  wire route only ('/agentbbs/v1/...') .................. pass
  hash domain only ('agentbbs:node:') ................... pass
  escaped-slash route (/agentbbs\/v1\/rooms/) ........... pass
  real tree ............................................. pass

The static-import check is untouched and remains the real gate.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67
2026-09-09 21:54:14 +00:00
ruv 8a85001942 fix: Windows CI build, dead agentdb controller exports, memory driver doctor check
- #2992: pin windows-latest -> windows-2022 for the Build V3 matrix job.
  windows-latest moved to VS2026, which node-gyp can't detect, breaking
  native builds for hnswlib-node and better-sqlite3.
- #2977: remove dead agentdb import attempts in controller-registry.ts for
  8 exports no installable agentdb range provides. Promote the working
  tieredMemoryFallback/createConsolidationStub implementations to
  first-class for hierarchicalMemory/memoryConsolidation; the other 6
  controllers (semanticRouter, mutationGuard, attestationLog, gnnService,
  rvfOptimizer, guardedVectorBackend) have no fallback and now return null
  directly instead of attempting an import that never resolves. The
  consolidation stub now reports source:'stub' and an explicit note so
  agentdb_consolidate reads as "did not run" rather than "nothing to do".
- #2968: add a read-only doctor check (checkMemoryPersistenceDriver) that
  reports whether the active SQLite driver is native better-sqlite3
  (durable, WAL-capable) or the sql.js fallback (silently drops
  wal_checkpoint writes), using the table-count signal from the issue
  (~47 native vs ~10 fallback). Warns, never fails; does not touch install
  behavior.

#2990 (MCP HTTP transport protocolVersion/tools-list/IPv6 bind) required
no code change — all three fixes are already present on main; the bug in
published ruflo/@claude-flow/cli only persists because no release has been
cut since they landed.

#3002 (ruvocal hidden-tab stream freeze) also required no code change —
already fixed on main.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_018ZkgSrVRWNMcXmaFiMZ7Z8
2026-08-22 12:14:24 -04:00
ruv 13cbd697db fix(release): enforce CI and idle assignment safety 2026-08-15 11:14:06 -04:00
ruv 2ec82b0cd1 chore(release): prepare stable 3.38.10 train 2026-08-14 17:35:47 -04:00
rUv f7672a104a ci: extend npm-ci-retry coverage to the OS-conditional fallback blocks (#3001)
#2998 wrapped 15 plain npm ci call sites with the retry composite
action but deliberately left ci.yml and verification-pipeline.yml's
two Linux/non-Linux --omit=optional fallback blocks untouched, on the
assumption they were a separate concern. That gap was real: PR #2999's
Test Verification (ubuntu-latest, Node 20) job hit the exact same
sharp/libvips "socket hang up" flake on the untouched Linux branch of
this exact block within hours of #2998 merging.

These two steps can't call the composite action directly — they need
the OS branch to choose between a plain npm ci and the
--omit=optional/--force fallback chain — so this wraps the existing
logic in the same 3-attempt/15s-backoff retry inline instead.
2026-08-12 16:44:25 -04:00
rUv 130193a376 ci: retry npm ci to absorb sharp/libvips transient download flakes (#2998)
Confirmed across at least 8 unrelated PRs this session (#2988, #2989,
#2991, #2994, #2995, #2996, #2997, and this one): npm ci intermittently
fails during sharp's postinstall with "socket hang up" fetching a
prebuilt libvips binary from GitHub Releases. It's a direct HTTPS
download outside npm's own package cache, so actions/setup-node's
npm cache doesn't help — every occurrence required a manual
`gh run rerun --failed`.

Add a local composite action (.github/actions/npm-ci-retry) that
retries npm ci up to 3 times with a 15s backoff, and switch every
plain `npm ci --legacy-peer-deps` / `npm ci --no-audit --no-fund
--legacy-peer-deps` call site (15 of them, across ci.yml,
integration-tests.yml, verification-pipeline.yml, v3-ci.yml) to use it.

Left untouched: the three rollback-manager.yml invocations (separate,
low-traffic rollback-validation path) and the two existing
Linux/non-Linux --omit=optional fallback blocks in ci.yml and
verification-pipeline.yml, which already have their own resilience
logic for a different failure mode (optional native deps on
macOS/Windows) — not this flake.
2026-08-12 16:14:40 -04:00
rUv 83e536396f fix(scaffold): add dead-reference guard + deterministic CLI remap (ADR-382 Part C) (#2974)
Adds scripts/smoke-init-scaffold-references.mjs (ADR-382 Part C, #2971):
four static assertions over v3/@claude-flow/cli/.claude/** and the
plugin/marketplace surface, deriving the live MCP tool set and canonical
CLI form from source rather than hand-maintaining them.

  1. dead `npx claude-flow` (bare) invocation
  2. dead `mcp__claude-flow__<tool>` references not in the live registry
  3. plugin .mcp.json launches with no local-bin-first resolver (Part A regression guard)
  4. plugins/* directories missing from .claude-plugin/marketplace.json

Ships warn-only (no --strict) with a documented --strict flag to flip once
backlogs clear. Wired into .github/workflows/v3-ci.yml as a new job,
gated on plugins/*/.mcp.json, .claude-plugin/marketplace.json, and the
script itself (v3/@claude-flow/cli/.claude/** was already a trigger path).

Deterministic remap applied: 701 occurrences of bare `npx claude-flow`
across 142 files -> `npx @claude-flow/cli@latest` (mechanical, 1:1, verified
by rerunning the guard: check 1 701 -> 0, check 2 unchanged at 410). The
386 legacy `npx claude-flow@alpha` / `@v3alpha` occurrences elsewhere in
the same tree were left untouched (still-maintained dist-tags).

NOT remapped in this PR (tracked follow-up, per ADR-382 Part C's own
guidance not to guess): 410 dead `mcp__claude-flow__<tool>` occurrences
across 98 files, 54 distinct dead tool names (memory_usage: 53 files,
task_orchestrate: 41, sparc_mode: 34, swarm_monitor: 16, plus 50 more at
lower counts). Each requires per-call-site judgment about the surrounding
example's intent (store vs retrieve vs list, 1-line vs restructured
2-line orchestration calls) that a bulk regex pass cannot make safely.

Checks 3-4 are expected-red until ADR-382 Part A merges (plugin/ruflo-core
.mcp.json resolver + the 3 missing marketplace entries:
ruflo-agntcy, ruflo-bbs-federation, ruflo-business-pods).
2026-08-11 18:39:17 -04:00
rUv f35c545fbe feat(metaharness): pull in @metaharness/turn-credit + fix stale router/darwin pins (#2958)
* feat(metaharness): pull in @metaharness/turn-credit + fix stale router/darwin pins (post metaharness#176)

metaharness#176 shipped @metaharness/turn-credit (ADR-248, recursive
turn-level credit assignment) and bumped darwin to 0.9.0 (ADR-249 signal
seams) + router to 0.4.0 (calibration module). This brings ruflo's
dependency contract back in sync and makes the new package available:

- Add @metaharness/turn-credit ~0.1.0 as a new optionalDependency,
  following the same "must be installable, not peer-only" pattern
  darwin/flywheel/radio already use (ADR-150) — dependency-free, 64.9K
  unpacked, zero lifecycle scripts, same profile as the other three.
- Bump @metaharness/darwin ~0.8.3 -> ~0.9.0 and the paired MH_DARWIN_PIN
  constant in distill-oracle.ts (was already out of range: tilde only
  absorbs patches, and 0.9.0 is a minor bump).
- Bump @metaharness/router peer range ^0.3.2 -> ^0.4.0 (still deliberately
  peer-only + triple-gated behind CLAUDE_FLOW_ROUTER_NEURAL=1, per
  neural-router.ts — that design choice is unchanged, only the stale
  range is fixed) and update the matching manual-install hint in neural.ts.
- scripts/check-metaharness-pins.mjs + scripts/metaharness-clean-install-test.mjs:
  add turn-credit to the watched/contract-checked package list.
- .github/workflows/no-cli-optdep-bloat-2561.yml: CLI_MAX 10 -> 13. The
  prior bump (PR #2956) left zero slack (budget == count exactly), which a
  code review flagged as a latent trap — the very next unrelated optional
  dep would trip this guard. This bump leaves real headroom (11 declared
  today, budget 13) instead of repeating that mistake.
- Also fixes a live ReferenceError in distill-oracle.test.ts (MH_DARWIN_PIN
  used but never imported) — a gap from the prior release's test fix that
  somehow didn't surface in that PR's CI; caught here while touching the
  same file.

Co-Authored-By: RuFlo <ruv@ruv.net>

* fix: regenerate v3/pnpm-lock.yaml — was out of sync with package.json edits

The previous commit edited v3/@claude-flow/cli/package.json directly
(darwin/router/turn-credit pin changes) without regenerating the pnpm
workspace lockfile, so every CI job running `pnpm install --frozen-lockfile`
failed immediately with ERR_PNPM_OUTDATED_LOCKFILE — cascading into every
downstream smoke/test job that depends on that install step.

Regenerated with pnpm@8.15.9 (matching CI's pinned version) in an isolated
worktree to avoid the lockfile-version drift a newer local pnpm would
introduce.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-08-10 15:08:06 -02:30
rUvandClaude 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>
2026-08-10 11:32:40 -02:30
rUv 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
2026-07-29 15:40:17 -04:00
rUv 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
2026-07-29 15:08:51 -04:00
rUv c1957608d4 fix(autopilot): enforce normalized CLI task sources (#2845) 2026-07-29 08:53:25 -04:00