Commit Graph

4 Commits

Author SHA1 Message Date
ruv 3b81e1c096 fix(parser): subcommand's non-boolean flag overrides global boolean set
The parser's `getBooleanFlags()` walks every registered command +
subcommand and adds every boolean-typed option into ONE flat set.
That set is then used by `parseFlag()` to decide whether a flag
consumes the next argv as its value. Consequence: if ANY command
declares a flag as boolean (e.g. `swarm start --parallel`), that
name is treated as boolean everywhere — even on a subcommand that
declares it as `type: 'number'` (e.g. `hooks route --parallel N`).
`--parallel 7` then parses as `parallel:true` and drops the `7`.

This bug forced the `--moa-parallel` rename in v3.32.13.

Fix: `getScopedBooleanFlags(resolvedCmd)` now REMOVES flags from the
boolean set when the resolved subcommand declares them as a
non-boolean type. Narrowest-scope wins. Backward compatible for the
common case (a flag that's boolean on the resolved subcommand stays
boolean; a flag that's boolean-elsewhere-only is only removed here
when this subcommand re-declares it as non-boolean).

Also restores the canonical `--parallel` flag on `hooks route`
(with short `-p`) and keeps `--moa-parallel` as a deprecated compat
alias for anyone who upgraded to v3.32.13 in the 15-minute window.

Parser regression tests: 55/55 → 56/56 (new test guards that a
numeric --parallel on hooks route survives when swarm start
declares it as boolean).

E2E matrix on `hooks route --mode moa`:
  --parallel 5        → 5 ✓
  --moa-parallel 6    → 6 ✓  (compat alias)
  -p 4                → 4 ✓
  (default)           → 3 ✓
  both + precedence   → canonical --parallel wins ✓

Refs: #2778 (dream-cycle), documented as follow-up in the #2778 commit.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-07-26 22:19:48 -04:00
rUv 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
c36cb4d66) so `store → delete → store` on the same (namespace, key)
doesn't trip the `UNIQUE(namespace, key)` constraint against a
soft-deleted row. The `memory_store` MCP tool schema still defaulted
`upsert: false`, so every agent-facing store-to-existing-key hit the
strict-insert path unless the caller explicitly passed `upsert: true` —
different behavior than the CLI documented.

Fix: schema description now states default true, handler reads
`input.upsert !== false` (only explicit `upsert: false` opts out).
Symmetric with the CLI-side workaround in `commands/memory.ts`.

Refs: #2775 (MCP parity follow-up flagged by the sweep agent).

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

* docs(changelog): re-scope tracker sweep to 3.32.10 + reflect final commit set

- Rename header 3.28.1 → 3.32.10 (patch on the actual current 3.32.9;
  the original 3.28.1 heading was left over from the stale-base draft
  before the rebase onto origin/main).
- Remove the #2774 bullet from the "Fixed" list — that fix was reverted
  during rebase (upstream d20f1323b superseded it and the reporter's
  diagnosis appears incorrect). Move #2774 to a new "Investigated"
  subsection with the analysis and a note recommending closure.
- Fold the #2775 parser follow-up (parser.ts:applyDefaults walks
  subcommand + command options) and the MCP parity follow-up
  (memory_store default upsert=true) into the #2775 bullet, since
  they're the same defect surfacing at three layers.
- Drop the "stale rebrand test assertion" bullet — that assertion is
  now on origin/main already; no notable delta in this PR.

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

* fix(hooks): dual-write routing decisions to JSON store for metrics reader (#2786 fix-2)

Previously `hooks_post-task --store-results` wrote routing outcomes to
AgentDB namespace `patterns` via `storeFn`, but the sync reader
`getIntelligenceStatsFromMemory()` only reads
`.claude-flow/memory/store.json`. Result: the "Pattern Learning" and
"Agent Routing" numbers in `hooks_metrics` stayed at zero even when
routing decisions were being recorded.

Fix: after the AgentDB write, mirror the same entry into the JSON store
with `metadata.type = 'routing-decision'` so the reader's filter
picks it up. AgentDB stays authoritative for cross-session retrieval;
the JSON store is the counter surface the sync reader depends on. Both
writes are wrapped in try/catch so a failure at either layer doesn't
break `hooks_post-task`.

Same shape as the existing dual-write pattern for `hooks_post-command`
at line 910-931. Keeps `getIntelligenceStatsFromMemory` synchronous
(would require async ripple through 3 callers otherwise).

Refs: #2786 fix-2 (flagged as architectural by the sweep agent — turns
out to be one bounded additive write, not a rewire).

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

* test(parser): regression coverage for command + subcommand defaults (#2775)

Three new tests in the existing `defaults` describe block:
- command-level `option.default` applies to a flagless invocation
- subcommand-level `option.default` applies to a subcommand invocation
  (this is the exact shape that trapped `memory store --upsert`)
- explicit `--no-upsert` still overrides the default:true

Guards against a re-regression of parser.ts:applyDefaults collapsing
back to only walking `globalOptions`.

52/52 → 55/55 parser tests pass.

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

* test(hooks): regression guard for #2786 fix-2 JSON dual-write

New test file `hooks-post-task-routing-dual-write-2786.test.ts` runs
`hooksPostTask.handler` in a temp cwd and asserts that when called with
`storeDecisions=true`:

  - `.claude-flow/memory/store.json` is created
  - It contains a `routing-decision:<taskId>` entry
  - The entry's shape matches what `getIntelligenceStatsFromMemory()`
    filters on (`key.includes('routing') || metadata.type === 'routing-decision'`)
  - `metadata.confidence` maps from `quality` (used to compute avgConfidence)
  - `namespace` is `patterns` (parity with the AgentDB write)

Second test guards backwards compat: when `storeDecisions` is omitted,
no routing-decision entry appears in the JSON store.

Bridge is mocked (the AgentDB path's real behavior is covered elsewhere).

2/2 pass.

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

* test(funnel): regression coverage for cold-start seed pool

New test file `funnel-messages-seed-pool.test.ts` asserts the seven
invariants of the 2026-07-26 cold-start pool restoration:
  - MESSAGES is non-empty
  - contains ≥1 disclosure so the disclosure gate can unlock cold
  - every disclosure carries the exact ' · manage: ruflo settings' tail
    (ADR-301 invariant)
  - every seed message passes isValidMessage() (schema / host allowlist
    / control-char strip / 80-col cap)
  - every URL is on the exact-host allowlist (no third-party leaks)
  - contains ≥1 educational (4-in-5 rotation slots)
  - all ids are unique (rotation dedups on id)

7/7 pass. Guards against a re-empty of MESSAGES that would blank the
statusline promo row again on new installs.

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

* fix(funnel): disclosure gate now consults local seed pool (E2E follow-up)

E2E validation of the 2026-07-26 sweep exposed a gap: on true cold
start (no remote cache, no network), the promo row stayed blank
because `selectDisclosureMessage` only read `getRemoteMessages()` —
my local seed disclosure in messages.ts never reached the disclosure
gate, so the gate stayed `never_seen` forever.

Fix: `getDisclosureMessagePool` now merges local `MESSAGES` with the
remote pool via the same `eligibleMessagesFromPools` helper rotation
uses (remote wins by id). This mirrors the design of the rotation
selector — the seed is a cold-start bootstrap, remote takes over as
soon as its cache populates.

Verified end-to-end: cleared funnel cache + pointed
`RUFLO_FUNNEL_MESSAGES_ENDPOINT` at an unreachable host; statusline
render emits the local disclosure ("Ruflo shows occasional tips and
sponsor notes here · manage: ruflo settings") — exactly the string
seeded in messages.ts (`local.disclosure.v1`).

Refs: 2026-07-26 tracker sweep, promo row cold-start.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-07-26 15:05:33 -07:00
rUv 01070ede81 fix: Tier A blockers #1596, #1567, #1556 (v3.5.80) (#1598)
* fix: resolve Tier A blockers #1596, #1567, #1556

#1596 — CLI lazy-command routing:
  `ruflo daemon start` was mis-routed to the top-level `start` command
  because the parser only registered the 10 core sync commands and its
  greedy Pass 1 walk happily matched `start` as the command with `daemon`
  left as a positional. Teach the parser about lazy command names via
  `registerLazyCommandName()`, stop Pass 1 at the first non-flag
  positional, and recognize lazy names in Pass 2 so commandPath becomes
  `['daemon', 'start']`. The existing positional-based subcommand
  fallback in CLI.run() then dispatches correctly. Affects every
  lazy-loaded command (daemon, doctor, embeddings, neural, config,
  workflow, hive-mind, ...).

#1567 — agent_spawn Zod field mismatch:
  validate-input.ts was passing `{ agentType, name }` to
  @claude-flow/security's SpawnAgentSchema, which expects `{ type, id }`,
  so every MCP agent_spawn call failed with `type: Required`. Align the
  field names. Also swallow `invalid_enum_value` issues because the
  schema enumerates only 15 built-in agent types — we support custom
  types and the inline identifier validator already gated unsafe input.

#1556 — AutoMemoryBridge destroys hand-curated MEMORY.md:
  `curateIndex()` fired on every Stop hook tick and overwrote MEMORY.md
  with a single-line title stub whenever none of the files in memoryDir
  matched the 7 hardcoded DEFAULT_TOPIC_MAPPING filenames — which is
  exactly the situation for any project using Claude Code's native
  `<type>_<topic>.md` memory convention. Skip the write entirely when
  there are no sections to curate and emit `index:skipped` instead;
  `curate` must be non-destructive when there's nothing to curate.

Tests:
  - __tests__/parser.test.ts: 4 new cases cover lazy command routing,
    bare lazy commands, and global flags before a lazy command.
  - __tests__/validate-input-agent-spawn.test.ts: built-in type, custom
    type, bad identifier, and no-domain cases.
  - src/auto-memory-bridge.test.ts: new #1556 case + updated empty-file
    case to assert the new non-destructive semantics.
  All 120 CLI + 74 memory tests pass locally.

Co-Authored-By: claude-flow <ruv@ruv.net>

* chore: bump to 3.5.80 / memory 3.0.0-alpha.14 for Tier A fixes

- @claude-flow/cli 3.5.78 → 3.5.80
- @claude-flow/memory 3.0.0-alpha.13 → 3.0.0-alpha.14
- claude-flow umbrella 3.5.78 → 3.5.80
- ruflo umbrella 3.5.78 → 3.5.80

Also fixes memory package's prebuild script to clean
tsconfig.tsbuildinfo so tsc --incremental produces fresh
output after rm -rf dist.

Matches published npm artifacts for issues #1596, #1567, #1556.

Co-Authored-By: claude-flow <ruv@ruv.net>

---------

Co-authored-by: Reuven <cohen@ruv-mac-mini.local>
2026-04-11 12:20:01 -04:00
rUv 1e105c799b fix: v3.5.8-v3.5.14 ADR-061 security fixes, cross-platform hooks, Windows parity
ADR-061 Deep Audit Fixes (12 P0/P1/D issues):
- S-1: Replace execSync with execFileSync to prevent command injection (GCS)
- S-2: Add MAX_BUFFER constant (10MB) to prevent unbounded stdout capture
- S-3: Add validatePackageName() to sanitize plugin names
- S-4: Add IPFS CID format validation before fetch
- S-5: Add buffer size limits to execSync calls
- D-1: Fix CFP magic-number check (use subarray, not slice)
- D-2: Fix unsupported format error (throw instead of silent fallback)
- D-3: Fix MCP partial-JSON accumulator (per-session buffering)
- D-4: Fix duplicate provider registration guard
- D-5: Fix memory namespace parameter passthrough
- D-6: Fix process command error handler (use err.message)
- D-7: Fix GCS credential loading (resolve path, validate fields)

ADR-062 Cross-Platform Hook Commands:
- Replace node -e "..." one-liners with node script subcommand invocation
- Add cmd /c prefix on Windows to bypass PowerShell stdin issues
- Fix stdin reading with 500ms timeout (prevents Windows hanging)
- Guarantee process.exitCode = 0 in all hook scripts
- StatusLine uses plain node (no cmd /c) for proper stdin forwarding
- Fix invalid SubagentEnd → SubagentStop hook event name
- Restore valid SubagentStart hook event

Tests: 1600 passed, 46 skipped across 23 test files

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-03-06 00:25:28 +00:00