Semi-automatic topology evolution (D5, human-gated)
DuDuClaw’s autonomous evolution (GVU / AEE, defaulting from v3 onward to AEE playbook
entries rather than rewriting the whole SOUL.md; see chapter 12 of
docs/architecture/evolution-engine.md) only optimizes “nodes”: each agent’s prompt and
behavior rules. The “edges” between agents, the reports_to hierarchy that decides who
a given class of task routes to, have always been hardcoded. D5 turns that edge into
something evolvable, but every change still has to pass human approval; the machine
only handles proposing and gathering evidence.
Design lineage: GPTSwarm (arXiv:2402.16823, topology as a learnable object), AFlow (2410.10762), and ADAS (2408.08435: fully automatic control-flow rewriting is the highest-runaway-risk capability, which is exactly why D5 deliberately stops short of full automation).
Off by default. The whole mechanism only activates when
config.tomlsets[topology_evolution] enabled = true; with it off, the dispatch path is byte-identical to plainFixedHierarchy.
How it works
Section titled “How it works”-
Evidence analysis (a pure function, unit-testable) A background driver scans the task store every
tick_secs, aggregating quality signals for each(agent, task_class)over the lastlookback_daysdays: the MAV/review rejection rate, the needs_human escalation rate, and the count of goal-loop no-progress oscillations.task_classis taken from a task’s first tag (the same convention as D4’s RoundRobin), falling back to priority when there is no tag. The sample base is “settled goal-mode tasks” (status done / needs_human / failed), where needs_human, failed, orretry_count > 0each count as one rejection. -
Proposal (not a direct change) When an agent’s sample count for a given task class is ≥
min_samplesand its rejection rate is ≥reject_rate_threshold, and a sibling under the samereports_toparent handles the same task class better (lower rejection rate, also with enough samples), the driver produces onererouteproposal with evidence attached (sample count, rejection rate, up to 10 sample task ids). With no qualifying sibling, no proposal is made: an empty result beats a fabricated one. At most one proposal per tick. -
The human gate (cannot be bypassed) Every proposal goes through
ApprovalBroker(action_kind = "topology_reroute"). In ActionGuard terms this is always-human: no LLM judge is involved, it is not relaxed byautonomy_level, and the code only ever callsrequest+poll, with no automatic approval path. TTL expiry means DENY (the broker fails closed). A human approves or rejects it via the dashboard’sapprovals.decideor a channel button. -
Taking effect and the observation window Once approved, it is written to
~/.duduclaw/routing_overrides.json(advisory lock- atomic temp-file rename).
FixedHierarchychecks for an active override before dispatching: a match on(task_class, from_agent)reroutes toto_agent. A missing or corrupted override file is always treated as no override, and routing falls back to the status quo (fail-safe). Once active, it enters anobserve_hours(default 24h) observation window.
- atomic temp-file rename).
-
Automatic rollback If, during the observation window,
to_agent’s rejection rate for that task class reaches or exceedsfrom_agent’s historical baseline, the override is immediatelyrolled_backand routing reverts automatically. If the window passes and the new route genuinely beats the baseline, it becomesconfirmed. Insufficient samples extend the observation window once; still insufficient after that and it isrolled_back(a conservative default). -
Guarding against a proposal storm The same
(task_class, from_agent)pair gets at most one proposal withinproposal_cooldown_days(default 7 days, including rejected ones), recorded in the override file’s proposal log; an active override or a pending proposal also suppresses a repeat.dispatch_guard’s sliding window still applies as usual; D5 does not bypass it.
Every proposal, approval, rollback, and confirmation is written to events.db and the
dashboard Activity Feed (topology.proposed / topology.approved /
topology.rejected / topology.rolled_back / topology.confirmed /
topology.extended).
Configuration
Section titled “Configuration”[topology_evolution]enabled = false # master switch, off by defaultlookback_days = 14 # evidence lookback window (days)min_samples = 5 # minimum settled sample count for an (agent, task_class) cellreject_rate_threshold = 0.6 # rejection-rate threshold that triggers a proposalobserve_hours = 24 # observation window after approval (hours)proposal_cooldown_days = 7 # proposal cooldown for the same edge (days)tick_secs = 3600 # driver tick period (seconds)approval_ttl_secs = 86400 # TTL for a reroute approval (seconds); expiry = rejectDashboard RPC
Section titled “Dashboard RPC”topology.list (require_manager) returns the current routing overrides and pending
reroute proposals for the dashboard to render D5 state. Approve/reject reuses the
existing approvals.list / approvals.decide.
Risks and boundaries
Section titled “Risks and boundaries”- Off by default, and it also needs
ApprovalBrokerto be available or D5 does not start at all: a proposal mechanism without a human gate is not allowed to exist. - The machine only ever does reversible things (propose, observe, roll back); the irreversible act (actually changing the route) is always left for a human to decide.
- D5 only layers on top of the default
FixedHierarchyhierarchical routing. When an operator has explicitly chosenRoundRobin/LlmSelect([dispatch] policy), that is a deliberate routing choice, and a D5 override only takes effect when that policy’s empty roster falls back to the hierarchy. - An override is a routing-layer change; it does not retroactively touch tasks already in flight. Already-dispatched in-flight tasks keep their original route; the new route (or a rollback) only applies to future dispatches.
Per the opus-playbook observation-window discipline, D5 should only be enabled once D1–D4 are stable and the eval sample size is sufficient.