Skip to content

Phase 1: extract the climb loop's decide step into a composition seam - #128

Merged
renmengye merged 2 commits into
mainfrom
feat/composition-seam
Aug 23, 2026
Merged

renmengye merged 2 commits into
mainfrom
feat/composition-seam

Conversation

@renmengye

Copy link
Copy Markdown
Member

What

Phase 1 of the research-loop buildout (docs/design/research-loop-buildout.md, merged in #127): extract the climb loop's decide-next step into a pluggable composition seam.

climb_once's inner loop is already run → measure → decide. run (run_role) and measure (measure_and_decide) were factored; the "decide the next invocation" half was inlined in the loop. This pulls it out as a pure policy:

  • _panel_revise_policy(verdict, session, harness, reads, revisions) -> _Revise | _Halt
  • the loop now just runs the policy: _Halt ends (drafting if findings stay open), _Revise wakes the author with the policy's prompt and re-enters run → measure.

Why

Depth (Phase 2a) is a results-driven decision policy; parallel (Phase 3) fans out the run step. With the decision behind a seam, they become apps on the loop, not new drivers — the whole point of Phase 1, and the antidote to the per-lane-orchestration mistake (#123).

No behavior change

The one policy today is the existing panel-driven revision rule, making byte-for-byte the same decisions (clean → halt; blocking + resumable + under cap → revise; unresumable or capped → halt-and-draft). This is deliberately thin — run_role consolidation was already done (consolidation.md), so Phase 1 is only this decide-seam extraction.

Test plan

  • New test_panel_revise_policy_decisions covers each decision branch directly (the seam).
  • The full existing climb/orchestrator suite stays green (no behavior change): uv run pytest — 697 passed. Full gate (ruff check + format, mypy) — green.

No secrets / no large files

Confirmed.

🤖 Generated with Claude Code

First build step of the research-loop buildout (docs/design/research-loop-buildout.md).
climb_once's inner loop is already run -> measure -> decide; run (run_role) and
measure (measure_and_decide) were factored, but the 'decide the next invocation'
half was inlined. Pull it into a pure, pluggable policy (_panel_revise_policy ->
_Revise | _Halt) so the loop is agnostic to WHY it iterates.

No behavior change: today's one policy is the existing panel-driven revision rule,
byte-for-byte the same decisions (clean -> halt; blocking + resumable + under cap
-> revise; unresumable or capped -> halt-and-draft). The full suite is green.

This is the seam depth (Phase 2a, a results-driven policy) and parallel (Phase 3,
fanning out the run step) become apps on, not new drivers — the point of Phase 1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 1 — reviewed head 91040698 — reviewer hermes/gpt-5.6-terra.

second opinion — terra
Advisory findings from autoresearch — the code owner decides. Reply to disagree; the autoresearch:no-review label opts this PR out.

Verdict: no defects found.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 1 — reviewed head 91040698 — reviewer claude/claude-opus-5.

Advisory findings from autoresearch — the code owner decides. Reply to disagree; the autoresearch:no-review label opts this PR out.

Verdict: nothing blocking — 1 advisory note.

Advisory (non-blocking):

  • Docstring names a credited argument the function does not take (src/autoresearch/orchestrator.py:957; medium)

I compared the extracted policy against the deleted inline branches: the order and conditions are identical (not blocking -> halt; no session_id or supports_resume False -> halt+draft; reads > revisions -> halt+draft; else revise with verdict.wake_text), and the call passes panel_reads after the increment, matching the old code. The one behavioral difference is that the clean-read halt now assigns panel_blocking_open = False instead of leaving it untouched, but panel_blocking_open is initialized False at line 1085 and every place that sets it True breaks immediately, so the value cannot differ. The new test exercises all four branches with distinct inputs and would fail if a branch were inverted. No other files changed in the diff; I did not run the suite (no execute tool).

…d in Phase 2a)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@renmengye
renmengye merged commit e6a8996 into main Aug 23, 2026
1 check passed
@renmengye
renmengye deleted the feat/composition-seam branch August 23, 2026 13:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant