Commit Graph

1 Commits

Author SHA1 Message Date
rUv f5a180423d feat(agents): ADR-147 nested subagent (depth=5) infrastructure + P2 stage 1 (#2336)
* docs(adr): add ADR-147 — nested subagent capability integration (depth=5)

Captures the integration plan for the nested-subagent capability Boris
Cherny announced 2026-06-09 (depth=5 cap). Based on direct inspection
of CLI 2.1.169: the plumbing (parentAgentId propagation, hasTaskTool
per-spawn gate, Agent/Task tool aliasing, parent_agent_id OTel tag) is
in the shipped binary, but the depth=5 cap is not encoded as named
symbols and zero ruflo agent definitions declare a tools: field — so
spawned children currently inherit hasTaskTool=false.

Four-phase rollout: grant Task only to orchestrator-class agents
(P1), capture parent_agent_id into AgentDB on post-task (P2),
depth-aware pre-task guardrail with cap=4 default behind
CLAUDE_FLOW_STRICT_NESTING (P3), rewrite CLAUDE.md queen-coordinator
pattern + cross-reference ADR-099/143/144 (P4).

Refs ruvnet/ruflo#2335

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

* feat(ruflo-agent): add nested-subagent agents + skill (ADR-147 P1)

Adds four orchestrator/leaf agent definitions to the ruflo-agent
plugin, each with an explicit tools: frontmatter — the missing piece
that lets spawned children inherit Claude Code 2.1.169's hasTaskTool
gate and actually nest.

  nested-coordinator  — generic deep-delegation orchestrator (has Task)
  nested-researcher   — recursive research orchestrator (has Task)
  nested-reviewer     — find→adversarial-verify reviewer (has Task)
  nested-leaf         — leaf-worker template, deliberately no Task
                        (demonstrates the least-privilege boundary)

Plus a paired SKILL.md (skills/nested-subagents/) documenting when to
nest vs. flat fan-out, the depth budget (cap=4 default, 5 API), and the
required child-summary contract.

Implements P1 of ADR-147. Refs ruvnet/ruflo#2335.

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

* feat(ruflo-agent): add nested-queen — full-ruflo-stack nested orchestrator

Companion to nested-coordinator. Where nested-coordinator gives just
depth (Claude Code Task tool, depth=5 cap), nested-queen wires the
nested spawn tree into ruflo's existing machinery:

  - swarm_init + hive-mind_spawn (queen role, raft consensus)
  - hive-mind_consensus / coordination_consensus for branch decisions
    (replaces inline averaging in the diverse-lens reviewer pattern)
  - hooks_intelligence_* RETRIEVE -> JUDGE -> DISTILL -> CONSOLIDATE
    pipeline (full ADR-074..088 alignment), with trajectory tracking
    per spawn
  - memory_store / memory_search_unified for tree-shape patterns
    (HNSW-indexed lookup of prior similar trees before spawning)
  - claims_claim / claims_handoff / claims_load — ADR-144 AuthScope
    monotonic reduction enforced at every parent->child hop
  - aidefence_scan / aidefence_is_safe on outbound prompts and inbound
    child summaries — ADR-131 / ADR-146 P2 boundary
  - cost-budget pre-spawn check (refuse early when budget tight)
  - Five hard constraints made explicit: depth budget, scope monotonicity,
    AIDefence reject = no consume, pre-spawn budget, mandatory
    trajectory close

Selection guidance: default to nested-coordinator; use nested-queen
only when consensus, tree-shape learning, scope enforcement, content
gating, or hard cost budget is genuinely required. The overhead is
~10x for a reason.

Refs ADR-147, ADR-144, ADR-131, ADR-146, ADR-099, ADR-097.
Refs ruvnet/ruflo#2335.

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

* feat(ruflo-agent): tier-2 specialists — queen-researcher, queen-reviewer, queen-leaf

Mirrors the tier-1 specialist lineup (researcher / reviewer / leaf)
at tier 2 — each wires its role into ruflo's full machinery:

  nested-queen-researcher
    - HNSW pattern-search of prior research trees before spawning
    - hive-mind raft consensus on which followups to pursue (replaces
      silent inline ranking — the bias defence)
    - AIDefence on both outbound prompts (web content quoted in) and
      inbound child summaries (injected results)
    - Full trajectory record + EWC-consolidated pattern-store per tree

  nested-queen-reviewer
    - Phase 2 verifier vote becomes hive-mind byzantine consensus
      (tolerates f < N/3 lying verifiers); raft for the diverse-lens
      variant where verifiers aren't byzantine
    - AIDefence on diff content forwarded to verifiers + verifier
      reasoning returned
    - Pattern-store of review-tree shapes — what catches bugs vs FPs

  nested-queen-leaf
    - Still no Task tool (ADR-147 P1 least-privilege boundary)
    - AIDefence-scans its own inbound prompt; refuses on reject
    - claims_load confirms inherited AuthScope is still valid
    - Records hooks_intelligence_trajectory-step on completion (success
      or failure) — the leaf's contribution to the queen's learning
      pipeline

Together with nested-queen, the tier-2 set now mirrors tier-1
one-for-one. Selection rule remains: default to tier 1; reach for
tier 2 only when consensus, content gating, scope enforcement, or
tree-shape learning genuinely earns the overhead.

Refs ADR-147, ADR-144, ADR-131, ADR-146, ADR-099.
Refs ruvnet/ruflo#2335.

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

* test(adr-147): empirical depth probe + ADR P1 validation results

Adds scripts/probe-nested-spawn-depth.mjs — runs claude -p with
ruflo-agent:nested-coordinator, drives a self-replicating chain
L1->L2->... until refusal or test-limit (level 7), and reports
the observed cap. Writes results to docs/probes/.

Two runs against CLI 2.1.169 (one with default env, one with
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1). Both return:
  FINAL: level=1 status=NO_AGENT_TOOL

Empirical finding: declaring tools: [Task] in an agent's YAML
frontmatter is necessary but not sufficient in 2.1.169. The plugin
loader honors the field (claude plugin details lists the agent with
its declared tool set), but the runtime's parent->child spawn does
not propagate Task to the child based on the child's YAML allow-list.
No env-var or CLI flag found unlocks it.

ADR-147 Validation section updated with the full empirical block:
  - P1 infrastructure (agents + skill) is shipped and correct
  - End-to-end verification of the depth=5 cap is NOT possible in
    2.1.169 — runtime gate appears to be server-side or held behind
    a feature rollout not yet user-enableable
  - P2 + P3 explicitly deferred until the probe returns a positive
    verdict; both require a working nested spawn to exercise
  - P4 must NOT claim nested spawning is currently usable
  - The probe stays in-tree as the regression test — re-running it
    after future CLI upgrades is the first verification step

This is the honest "P1 actually complete" answer: infrastructure
landed, empirical limit captured, follow-on phases gated on the
runtime activation we cannot force from outside the binary.

Refs ruvnet/ruflo#2335

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

* docs(adr-147): add Path-2 sweep findings — denylist confirmed

Sharpens the empirical block in ADR-147 with results from the
four-variant CLI flag sweep:

  - Control (no flags):              L1 has Read,Grep,Glob,Bash
  - --allowedTools (incl Task,Agent): same
  - --permission-mode bypassPermissions: same
  - --agent nested-coordinator (lead is nc): same

All four return EXACTLY the same 4-tool list. Our YAML declares 6
tools (Task, Read, Grep, Glob, TodoWrite, Bash). 4 propagate, 2 are
stripped. The runtime applies a hardcoded denylist that drops Task
and TodoWrite at parent->child spawn time. The strip is consistent
across permission modes, lead agent identity, and explicit
--allowedTools grants -- so the gate is server-side or hardcoded
on specific tool names, not a user-facing toggle.

Favorable for P1: the YAML mechanism IS the right opt-in shape.
Our agents are declaratively correct. When the denylist for Task
lifts (whether by a future build, a server-side rollout, or a
discovered opt-out flag), nested spawning activates with zero
code changes to ruflo's agents.

Refs ruvnet/ruflo#2335

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

* feat(hooks): ADR-147 P2 stage 1 — thread parent_agent_id + depth through post-task

Wires the nested-subagent spawn-tree lineage through the post-task
hook chain so when Claude Code starts populating the
`x-claude-code-parent-agent-id` header (or its OTel span tag) on
spawned subagents, callers can pass it through and the data lands
in the existing feedback storage WITHOUT a schema migration.

Path: CLI flags -> MCP tool input schema -> bridgeRecordFeedback ->
LearningSystem.recordFeedback + memory entry (JSON.stringify(options)
on bridgeStoreEntry preserves the new fields automatically).

Changes:
  - commands/hooks.ts:     post-task gains --parent-agent-id and --depth flags
  - mcp-tools/hooks-tools.ts: hooks_post-task input schema gains parentAgentId
                              (string, validateIdentifier) and depth (integer,
                              0 <= d <= 32). Both optional; rejection returns
                              typed error and short-circuits before bridge call.
  - memory/memory-bridge.ts: bridgeRecordFeedback options extended; forwarded
                             to LearningSystem.recordFeedback. JSON.stringify
                             at the bridgeStoreEntry call picks them up for the
                             persisted memory entry automatically.

Tests (vitest, 7 cases, all green):
  - propagation when supplied
  - omission when not supplied (top-level lead path)
  - depth=0 boundary (must propagate as 0, not be coerced to undefined)
  - validation rejects invalid parentAgentId
  - validation rejects negative depth
  - validation rejects non-integer depth
  - validation rejects depth > 32 (defensive bound)

Why this is testable today despite the runtime denylist documented
in ADR-147 P1: the OTel parent_agent_id tag the binary emits already
exists for flat depth-1 spawns. The same code path captures it for
a depth-5 chain once the Task-tool denylist lifts upstream — no
further code changes needed.

DEFERRED to P2 stage 2 (separate PR):
  - Dedicated parent_agent_id + depth columns on the feedback /
    trajectories table (currently lands in the JSON metadata blob)
  - Automatic capture from the active OTel span context (currently
    caller must pass via CLI/MCP)
  - Query API to read the spawn tree by agent_id (downstream of the
    dedicated columns)

Refs ruvnet/ruflo#2335

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-06-09 12:15:03 -04:00