task: apply 5-critic council review to recursive-delegation rule

Second-round council (5 critics × distinct lenses: faithfulness,
codebase-fit, practical-usability, safety, editor) surfaced findings
the original 3-critic pass missed. Applying.

HIGH-severity fixes:
  - Faithfulness disclaimer: RAO is an RL training paper; its
    quantitative gains (depth-12 generalization, 2.5x wall-clock,
    context-overflow handling) require a trained policy this harness
    does not have. The rule now opens with this disclaimer and
    explicitly notes Max depth / Step budget are prompt-level
    conventions, not runtime caps.
  - launch_subagent vs Agent tool: paper's async REPL primitive runs
    asyncio coroutines with shared state. Harness Agent tool is
    stateless and synchronous. Rule now distinguishes them in
    § Multi-Level Delegation Protocol.
  - Enforcement gate added: .claude/skills/delegate/SKILL.md Step 5
    gains sub-step a.1 — before spawning a worker authorized to
    recurse, confirm Max depth / Max children per level / Step budget
    are ALL present. Missing → flat execution only.
  - .claude/protected-paths.txt: new rule registered as load-bearing
    (now cross-referenced from advisor-model.md and delegate SKILL).

MEDIUM-severity fixes:
  - Branching-factor cap added: Max children per level: 5 (hard cap)
    alongside Max depth, closing the depth-4 * parallel-spawn fanout
    risk.
  - Internal duplication cut: dropped "Recursion as Context
    Expansion" section (already covered by When-to-Use triggers
    + Recursing-for-reasoning anti-pattern); dropped tautological
    Step 4 in protocol; dropped duplicated "no raw content in Goal"
    rule; dropped Orchestrator Boundary second paragraph;
    dropped See Also entry for repo-layout-source.md (no referent
    in this file).
  - Ambiguous "Step 5" cross-ref tightened to
    "§ 5 Execute waves sub-step (a)".
  - "context capacity" trigger reframed to "child's briefing would
    exceed one sub-agent's effective context window" — concrete
    enough to act on.
  - New anti-pattern: "Synthesis pass-through" — mid-tree node
    forwards children's outputs verbatim, adds zero value.
  - New anti-pattern: "Sideways scope escalation" — child rewriting
    a sibling's briefing to lift depth/scope.

Net file size: 121 -> 114 lines. New content (disclaimers,
branching cap, new anti-patterns) more than offset by cuts of
duplicated and tautological text. CHANGELOG entry updated to
reflect the disclaimers and the new enforcement gate.

Closes #269 (continuing).
This commit is contained in:
OpenHarness Bot committed 2026-05-10 23:34:15 -06:00
1 parent c8aec0bf55
commit 8a12562b05
4 files changed
+39 -34

No files matched your search

+1
View File
@@ -46,6 +46,7 @@ crons/cleanup-tasks.md
.claude/rules/git.md
.claude/rules/sandbox-processes.md
.claude/rules/advisor-model.md
.claude/rules/recursive-delegation.md
# --- Specs that other artifacts cross-reference ---
.claude/specs/structure-spec-v0.7.md
+10
View File
@@ -128,6 +128,16 @@ Worker configuration:
- **Model**: as specified in the task decomposition (haiku/sonnet/opus)
- **run_in_background**: true (for waves with 2+ tasks)
**a.1) Recursion-authorization gate**
If any worker's task description authorizes recursive delegation (`Max depth: N` with N ≥ 2), confirm before spawning that **all three** fields are present in that worker's briefing:
- `Max depth: N`
- `Max children per level: M` (M ≤ 5)
- `Step budget: S`
If any field is missing, either add it or downgrade the task to flat execution (`Max depth: 1`). Workers without all three fields MUST stay flat — they have no authority to spawn grandchildren regardless of how the task is described in prose. See `context/rules/recursive-delegation.md` for the full protocol.
**b) Collect results**
After all agents in the wave complete, update each task via `TaskUpdate`:
+1 -1
View File
@@ -16,7 +16,7 @@ Update policy and release automation live in [`.claude/rules/git.md`](.claude/ru
- `opencode` and `opencode /connect` failing with `EACCES: permission denied, mkdir '/home/sandbox/.local/state'` on a fresh sandbox. Docker auto-creates `~/.local` and `~/.local/share` as root to satisfy the `opencode-host` bind-mount path; the entrypoint now non-recursively chowns those parent dirs to `sandbox:sandbox` so the user can create siblings like `.local/state`. The bind-mounted `~/.local/share/opencode` itself stays untouched. Re-applies a fix that was lost when [#241](https://github.com/ryaneggz/open-harness/pull/241) merged a conflicting `entrypoint.sh`. ([#245](https://github.com/ryaneggz/open-harness/issues/245))
### Added
- `context/rules/recursive-delegation.md`: project rule encoding the inference-time design principles from *Recursive Agent Optimization* (arXiv 2605.06639, Gandhi et al., May 2026) — bounding compute (depth + step-budget fields in briefings), `Context` block for context-as-parameter, structured-return schemas in `Acceptance criteria`, multi-level delegation protocol, and the orchestrator-vs-sandbox recursion boundary. `context/rules/advisor-model.md` gains a `Multi-level (recursive)` row in its Pipeline Variants table cross-linking the new rule; `.claude/skills/delegate/SKILL.md` gains a distinct-scope rule and an authorization gate so workers stay flat unless `Max depth` / `Step budget` fields are present. Already-covered principles (fresh context per child, parallel-when-independent, quality-over-quantity, don't-delegate-trivial) intentionally not duplicated. ([#269](https://github.com/ryaneggz/open-harness/issues/269))
- `context/rules/recursive-delegation.md`: project rule encoding the inference-time *structural* patterns from *Recursive Agent Optimization* (arXiv 2605.06639, Gandhi et al., May 2026) — bounding compute (`Max depth` / `Max children per level` / `Step budget` fields in briefings), `Context` block for context-as-parameter, structured-return schemas in `Acceptance criteria`, multi-level delegation protocol, and the orchestrator-vs-sandbox recursion boundary. The rule explicitly disclaims RAO's *training-result* benefits (depth-12 generalization, 2.5× wall-clock) — those require an RL-trained policy this harness does not have — and notes the harness `Agent` tool is stateless/synchronous, unlike the paper's `async launch_subagent` REPL primitive. `context/rules/advisor-model.md` gains a `Multi-level (recursive)` row in its Pipeline Variants table cross-linking the new rule; `.claude/skills/delegate/SKILL.md` gains a distinct-scope decomposition rule and a recursion-authorization gate (Step 5 a.1) that confirms `Max depth` / `Max children per level` / `Step budget` are all present before allowing a worker to spawn grandchildren; `.claude/protected-paths.txt` registers the new rule as load-bearing. Already-covered principles (fresh context per child, parallel-when-independent, quality-over-quantity, don't-delegate-trivial) intentionally not duplicated. ([#269](https://github.com/ryaneggz/open-harness/issues/269), [#270](https://github.com/ryaneggz/open-harness/pull/270))
- Slack integration as a Pi extension at `.pi/extensions/slack/` ([#261](https://github.com/ryaneggz/open-harness/issues/261)).
- Project-local Pi single-line footer extension showing cwd/branch, context usage, model/thinking state, extension statuses, token usage, and session name with width-aware truncation. Header copy now uses a plain `MIFUNE` wordmark with an `agent harness` subtitle.
- Project-local Pi config under `.pi/` standing the agent up at the harness root: OpenAI-default `settings.json` (`gpt-4o`, model-cycling list scoped to GPT/o-series), `mifune-banner.ts` extension replacing pi's startup header with a flush-left `MIFUNE` wordmark + `pi harness · github.com/ryaneggz/mifune` subtitle, `path-guard.ts` extension intercepting `tool_call` events to confirm before write/edit on sensitive paths (`.env*`, `secrets/`, `credentials.{json,yaml}`, `*.pem`, `*.key`, ssh keys) and risky bash (`rm -rf`, `sudo`, `git push --force`, `git reset --hard`, bare `> /dev/sd*|nvme*|hd*|disk*`) with case-insensitive matching and `ctx.hasUI` guards for headless/RPC mode, generic consumer-agnostic `APPEND_SYSTEM.md`, `prompts/review.md` template invokable as `/review`, and `skills` + `rules` directory symlinks to `../.claude/skills` / `../.claude/rules` matching the single-source-of-truth pattern already in `.codex/`. New canonical `.claude/skills/harness-context/SKILL.md` reachable from pi via the symlink. Test suite at `.pi/extensions/__tests__/{path-guard,mifune-banner}.test.ts` (32 + 7 cases, hand-rolled mock `ExtensionAPI`, no pi devDep); pi source verified via `gh api` for tool-name casing (lowercase) and write/edit canonical input key (`path`). `vitest.config.ts` `include` extended to `.pi/**/__tests__/**/*.test.ts`; `.github/workflows/ci-harness.yml` `paths:` filter extended (`.pi/**`, `scripts/**`, `vitest.config.ts`) and a new "Root tests" step runs `pnpm test:scripts`, also bringing the previously-orphaned `scripts/__tests__/cron-runtime.test.ts` into CI. Project Pi settings pin `npm:@tintinweb/pi-subagents@0.7.1` so PR #256 starts with Claude Code-style sub-agents available in pi. ([#255](https://github.com/ryaneggz/open-harness/issues/255))
+27 -33
View File
@@ -1,16 +1,18 @@
# Recursive Delegation
> Reference: *Recursive Agent Optimization (RAO)* — arXiv 2605.06639, Gandhi et al., CMU + Amazon AGI Labs, May 2026.
>
> RAO is an **RL training** paper. Its quantitative gains (depth-12 generalization, 2.5× wall-clock reduction, context-overflow handling) are properties of a *trained* policy. This harness uses an untrained inference-only model; only the **structural patterns** below transfer. Treat the fields in this rule as strong conventions, not runtime-enforced caps — an untrained model may ignore them.
Extends `context/rules/advisor-model.md` for the case where a child sub-agent's task is itself multi-step and may justify its own delegation. The 2-step / 3-step / multi-turn variants in `advisor-model.md` all assume a **flat** tree (root → child). This rule adds the protocol for **multi-level** trees (root → child → grandchild).
Extends `context/rules/advisor-model.md` for multi-level delegation trees (root → child → grandchild).
## When to Use
| Trigger — recurse | Anti-trigger — stay flat |
|-------------------|--------------------------|
| A child's scope spans several files and could be parallelized further | The child's task fits one `advisor-model.md` 5-field briefing |
| The bottleneck is **context capacity** — one child can't read the full file set without overflow | The full task fits in one sub-agent's context |
| Independent sub-sub-trees each need a large, disjoint context | The orchestrator is delegating application code work (out of scope — see § Orchestrator Boundary) |
| A child's scope spans several files and would itself benefit from parallel decomposition | The child's task fits one `advisor-model.md` 5-field briefing |
| The child's briefing would exceed one sub-agent's effective context window | The full task fits in one sub-agent's context |
| Independent sub-trees each need a large, disjoint file set | The orchestrator is delegating application code work (out of scope — see § Orchestrator Boundary) |
| Synthesis is separable from analysis (you'll re-aggregate at multiple levels) | Depth would exceed 4 — flatten instead |
## Bounding Compute
@@ -19,24 +21,27 @@ Unbounded recursion burns budget silently. Every multi-level delegation MUST car
| Field | Default | Hard cap | Required when |
|-------|---------|----------|---------------|
| `Max depth` | 1 | 4 | The child MAY itself delegate |
| `Max depth` (edges from root: child = 1, grandchild = 2) | 1 | 4 | The child MAY itself delegate |
| `Max children per level` | 5 | 5 | Always, when `Max depth` ≥ 2 |
| `Step budget` | (skill-specific) | — | Always, when `Max depth` ≥ 2 |
- `Max depth` counts edges from the root invocation. Root → child = depth 1. Child → grandchild = depth 2.
- A child MUST NOT recurse if `Max depth` is absent or `1`. Silence on this field means "do not delegate further."
- A child that recurses MUST decrement: pass `Max depth: N−1` to its own children.
- A child MUST NOT re-issue or rewrite a sibling's briefing to lift its own depth or scope.
- Always reserve capacity for the parent's synthesis turn. If a child receives `Step budget: N`, it MUST allow at least one final turn for itself to integrate its grandchildren's outputs.
- These fields are **prompt-level conventions**, not runtime caps. Honor them rigorously when you write briefings; do not assume a child will refuse a malformed briefing.
## Briefing the Child — Context Block
For multi-level briefings, extend the `advisor-model.md` 5-field format with an optional **`Context`** block. Large material the child cannot derive itself (prior-wave results, transcript excerpts, file dumps) goes here, never inside `Goal`.
For multi-level briefings, extend the `advisor-model.md` 5-field format with an optional **`Context`** block. Material the child cannot derive itself (prior-wave results, transcript excerpts, content not on disk) goes here, never inside `Goal`.
```
## Advisor Briefing
**Goal**: <one sentence>
**Max depth**: <N> ← present only if recursion is authorized
**Step budget**: <N> ← present only if Max depth ≥ 2
**Max depth**: <N> ← present only if recursion is authorized
**Max children per level**: <N> ← present only if Max depth ≥ 2
**Step budget**: <N> ← present only if Max depth ≥ 2
**Constraints / gotchas**:
- ...
@@ -53,9 +58,7 @@ For multi-level briefings, extend the `advisor-model.md` 5-field format with an
<material the child needs but cannot derive from Start here>
```
Rules:
- MUST NOT paste raw file contents into `Goal` or `Constraints`. Link the path under `Start here` and let the child `Read` it.
- MUST include `Context` only when the child cannot reach the material via `Start here` (e.g., it lives in a prior agent's output, not on disk).
MUST include `Context` only when the child cannot reach the material via `Start here` (e.g., it lives in a prior agent's output, not on disk). The "no raw file contents in `Goal`" rule from `advisor-model.md` § Anti-Patterns still applies; the `Context` block is the place for material that has to travel with the briefing.
## Structured Returns
@@ -80,29 +83,18 @@ State the schema directly:
A child sub-agent invoked with a `subagent_type` whose tool list includes `Agent` (e.g., `implementer`, `general-purpose`) MAY delegate further. Use this protocol:
1. **Parent authorizes** by including `Max depth: N` (N ≥ 2) and `Step budget` in the briefing.
1. **Parent authorizes** by including `Max depth: N` (N ≥ 2), `Max children per level: ≤5`, and `Step budget` in the briefing.
2. **Child reads** the authorization before deciding to recurse. Absent → flat execution only.
3. **Child decrements** `Max depth` when spawning grandchildren.
4. **Child applies this rule** end-to-end at the next level (briefing format, Context block, structured returns, bounding).
5. **Parent receives** structured returns and synthesizes — never just passes them through.
3. **Child decrements** `Max depth` when spawning grandchildren and re-applies this rule.
4. **Parent receives** structured returns and **synthesizes** — never just passes them through. A mid-tree node that forwards children's outputs verbatim adds zero value (see § Anti-Patterns).
Use parallel spawn (one assistant message, multiple `Agent` calls) whenever the children are independent — same rule as `delegate/SKILL.md` Step 5. Recursion is orthogonal to parallelism: a depth-2 child can itself spawn parallel grandchildren.
Use parallel spawn (one assistant message, multiple `Agent` calls) whenever the children are independent — the same parallelism rule as `delegate/SKILL.md` § 5 *Execute waves* sub-step (a). Recursion is orthogonal to parallelism: a depth-2 child can itself spawn parallel grandchildren.
## Recursion as Context Expansion
The main inference-time payoff of recursion in this harness is **context expansion**, not just parallelism. Each child receives a fresh context window, so a sub-tree can process a file set that would not fit alongside the parent's working memory.
Use multi-level delegation primarily when:
- The total context required to solve the task exceeds what one agent can hold.
- Independent sub-trees each need to read a large, disjoint file set.
This is the test for whether to recurse at all. If the bottleneck is reasoning time, not context capacity, flat parallel wave execution via `/delegate` is usually enough.
The paper's `async launch_subagent` runs inside a Python REPL with `asyncio` coroutines and shared variable state. The harness `Agent` tool is **stateless and synchronous** from the parent's perspective. "Parallel" here means multiple `Agent` calls in one assistant turn, not concurrent Python tasks — design briefings accordingly.
## Orchestrator Boundary
`CLAUDE.md` forbids the orchestrator from writing application code. Recursive delegation does not relax this: orchestrator-side trees default to `Max depth: 1` and MUST NOT spawn sub-agents whose authorized task crosses into in-sandbox application work. Multi-level trees are appropriate for orchestrator workflows that decompose **rule/docs/skill changes** (this PR's own drafting being the canonical example) — not for shipping product code on the agent's behalf.
In-sandbox agents (Claude / Pi running inside `openharness`) are not bound by this rule — they operate inside the sandbox boundary CLAUDE.md draws around them.
`CLAUDE.md` forbids the orchestrator from writing application code. Recursive delegation does not relax this: orchestrator-side trees default to `Max depth: 1`, and multi-level trees are appropriate only for orchestrator workflows that decompose **rule/docs/skill changes** — not for shipping product code on the agent's behalf. In-sandbox agents fall under `CLAUDE.md`'s sandbox-side rules, not this rule.
## Anti-Patterns
@@ -110,11 +102,13 @@ In-sandbox agents (Claude / Pi running inside `openharness`) are not bound by th
- **Context in `Goal`** — pasting a long transcript into the goal sentence. Use the `Context` block, or `Start here` + a path.
- **Free-prose returns when structure is needed** — parent tries to parse a child's narrative output. Specify the schema in `Acceptance criteria`.
- **Flat tree in disguise** — `Max depth: 4` set, but every level spawns exactly one child. That is sequential delegation, not recursive — flatten it back to depth 1 with a multi-turn briefing per `advisor-model.md`.
- **Recursing for reasoning, not context** — using multi-level delegation when one flat wave would have done it. The token cost of the tree exceeds the benefit. Recurse for context-capacity reasons; parallelize (flat) for throughput reasons.
- **Orchestrator → application code via recursion** — using a depth-2 sub-agent to write code inside the sandbox. Crosses the CLAUDE.md boundary regardless of how it's framed.
- **Synthesis pass-through** — a mid-tree node receives structured returns from its children and forwards them verbatim to its own parent instead of integrating them. The mid-tree node adds zero value; either it should be doing real synthesis, or it should not exist (collapse the level).
- **Recursing for reasoning, not context** — using multi-level delegation when one flat wave would have done it. The token cost of the tree exceeds the benefit. Recurse when the briefings won't fit; parallelize (flat) for throughput.
- **Orchestrator → application code via recursion** — using a depth-2 sub-agent to write code inside the sandbox. Crosses the `CLAUDE.md` boundary regardless of how it's framed.
- **Sideways scope escalation** — a child rewriting a sibling's briefing to lift its depth, budget, or scope. Authorization flows down the tree from the original advisor only.
## See Also
- `context/rules/advisor-model.md` — the flat 2-step / 3-step / multi-turn parent rules this one extends.
- `.claude/skills/delegate/SKILL.md` — parallel wave execution (the orthogonal axis).
- `context/rules/repo-layout-source.md` — why this rule does not duplicate the layout tree.
- `.claude/skills/delegate/SKILL.md` — parallel wave execution (the orthogonal axis); see § 5 *Execute waves* for the parallel-spawn primitive.
- `CLAUDE.md` § *What You Do NOT Do* — the orchestrator-vs-application-code boundary referenced above.