Skip to content

Commit f07cf03

Browse files
vikast908claude
andcommitted
refactor(orchestrator): extract _base_run_state (shared book/article run-state) [Tier 1]
start_book/start_article now spread a shared _base_run_state (~18 identical keys); mode-specific keys (id, phase, unit counter, per-mode flags) added by each caller. Resulting state dict is identical to before (both full-run tests green). Deliberately did NOT merge _commit/_commit_section - their canon-vs-citation handling makes a shared helper read worse than the small dup. plan.md §20 updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 62bb26e commit f07cf03

3 files changed

Lines changed: 47 additions & 43 deletions

File tree

plan.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,15 +1016,15 @@ The book (chapter) and article (section) pipelines run near-parallel code in `or
10161016
down **incrementally and test-gated**: these paths have a history of *silent drift* (the revise-parity
10171017
bug), so behavior-preserving extraction + the full suite (and ideally a live run) between steps is
10181018
mandatory. Already shared (do not re-extract): `_pick_variant`, `_save_version`, `_record_preference`,
1019-
`_length_note`, `_merge_fix_notes`, `_escalate`. **Done:** `_run_learner` (shared learner tail).
1019+
`_length_note`, `_merge_fix_notes`, `_escalate`. **Done:** `_run_learner` (shared learner tail);
1020+
`_base_run_state` (shared run-state keys for `start_book`/`start_article`).
10201021

10211022
Prioritized, by risk:
10221023

1023-
- **Tier 1 (LOW - do next):** `_init_project_state` - a shared run-state dict builder for
1024-
`start_book`/`start_article` (mode-specific keys as kwargs; mind the `current_chapter`/`current_section`
1025-
+ `num_*` name differences). `_commit`/`_commit_section` - extract the shared humanize+summarize+
1026-
version+skill-record scaffold; keep canon-extraction (book) vs citation-renumbering (article) as a
1027-
`finalize` callback.
1024+
- **Tier 1:** ✅ `_base_run_state` (the shared run-state dict; mode-specific keys spread in by each
1025+
caller) - **done**. ❌ `_commit`/`_commit_section` - **evaluated and deliberately NOT merged**: the
1026+
paths differ structurally (canon-extraction + Store updates vs citation-renumber-before-gather), so a
1027+
shared helper would be callback-soup that reads worse than the ~8 duplicated lines. Leave separate.
10281028
- **Tier 2 (MEDIUM - schema/strategy callbacks):** `_chapter_fetch`/`_section_fetch` - shared
10291029
`concurrency.gather` shell, research/images/skills passed as strategy fns (article also returns
10301030
`source_text` for claim-verify). The divergent-draft + revision attempt loop inside

resume.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -284,11 +284,12 @@ redundancy items are large refactors, left as recommendations.
284284
source docstrings (`cli`, `orchestrator`, `brain`, `prompts`, …) + the CLAUDE workflow - moving them
285285
would be high-churn for no gain. `requirements.txt` stays (a documented convenience mirror; pyproject
286286
is canonical).
287-
- **Book↔article de-dup STARTED (safe, test-gated):** mapped the duplication (Explore agent), then
288-
extracted the leaf-level **`_run_learner`** shared by `_learn`/`_learn_article` (suite green). Committed
289-
a prioritized, risk-rated **refactor plan in plan.md §20** for the rest (Tier 1: `_init_project_state`,
290-
`_commit` scaffold; Tier 2: fetch + attempt-loop with callbacks; Tier 3 defer the run-loop). Each tier
291-
= its own PR, suite + both-mode fake run between steps - deliberately NOT bulldozed (drift-bug history).
287+
- **Book↔article de-dup IN PROGRESS (safe, test-gated; plan.md §20):** mapped it (Explore agent), then
288+
extracted `_run_learner` (learner tail) and **Tier 1 `_base_run_state`** (shared run-state keys for
289+
`start_book`/`start_article`; mode-specific keys spread in). Suite green after each. **Deliberately
290+
did NOT merge `_commit`/`_commit_section`** - the paths differ structurally (canon vs
291+
citation-renumber), so a shared helper reads worse than the small dup (good judgment ≠ blind DRY).
292+
Remaining (own PRs): Tier 2 (fetch shell + attempt-loop via callbacks), Tier 3 (defer run-loop).
292293
- **Still flagged:** giant `shell.py` (144KB) / `orchestrator.py` (115KB) - split AFTER the dedup (do
293294
§20 first, since the dup is intertwined). Optional: consolidate `SampleRun/``examples/`;
294295
collapse `requirements.txt`→pyproject+lock.

src/book_agent/orchestrator.py

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,29 @@ def _record_author(uid: str, author: str | None) -> None:
347347

348348

349349
# ── Setup (human picks a direction, then autonomous) ─────────────────────────
350+
def _base_run_state(uid, abstract, *, intake, author, max_revisions, autonomous, humanize,
351+
settings) -> dict:
352+
"""Run-state keys shared by every project (book + article). Callers spread this and add
353+
the mode-specific keys (the id, phase, unit counter, and any per-mode quality flags)."""
354+
return {
355+
"uid": uid, "abstract": abstract,
356+
"intake": intake or "", "author": author or "",
357+
"max_revisions": max_revisions, "committed": 0, "pending_review": False,
358+
"use_researcher": settings.use_researcher,
359+
"deep_research": settings.deep_research,
360+
"autonomous": autonomous,
361+
"humanize": settings.humanize if humanize is None else humanize,
362+
"use_images": settings.use_images,
363+
"diagram_engine": settings.diagram_engine,
364+
"use_embeddings": settings.use_embeddings,
365+
"divergent_drafts": settings.divergent_drafts,
366+
"tournament_judge": settings.tournament_judge,
367+
"min_insight": settings.min_insight,
368+
# Autonomous runs never pause on low confidence.
369+
"escalate_below_confidence": 0.0 if autonomous else settings.escalate_below_confidence,
370+
}
371+
372+
350373
def start_book(
351374
cfg: ModelConfig, settings: Settings, uid: str, abstract: str,
352375
chosen: S.Direction, book_id_override: str | None,
@@ -369,23 +392,14 @@ def start_book(
369392
brain.write_text(paths.toc, render.render_toc_md(toc))
370393

371394
state = {
372-
"uid": uid, "book_id": book_id, "abstract": abstract,
373-
"intake": intake or "", "author": author or "",
395+
**_base_run_state(uid, abstract, intake=intake, author=author,
396+
max_revisions=max_revisions, autonomous=autonomous,
397+
humanize=humanize, settings=settings),
398+
"book_id": book_id,
374399
"phase": "chapters", "num_chapters": len(toc.chapters),
375-
"max_revisions": max_revisions, "consolidate_every": settings.consolidate_every,
376-
"current_chapter": 1, "committed": 0, "pending_review": False,
377-
"use_researcher": settings.use_researcher,
378-
"deep_research": settings.deep_research,
379-
"autonomous": autonomous,
380-
"humanize": settings.humanize if humanize is None else humanize,
381-
"use_images": settings.use_images,
382-
"diagram_engine": settings.diagram_engine,
383-
"use_embeddings": settings.use_embeddings,
384-
"divergent_drafts": settings.divergent_drafts,
385-
"tournament_judge": settings.tournament_judge,
386-
"min_insight": settings.min_insight,
387-
# Autonomous runs never pause: no confidence/contradiction escalation.
388-
"escalate_below_confidence": 0.0 if autonomous else settings.escalate_below_confidence,
400+
"consolidate_every": settings.consolidate_every,
401+
"current_chapter": 1,
402+
# Autonomous runs never pause on contradictions either.
389403
"escalate_on_contradiction": False if autonomous else settings.escalate_on_contradiction,
390404
}
391405
brain.write_json(paths.run_state, state)
@@ -1593,30 +1607,19 @@ def start_article(
15931607
brain.write_text(paths.root / "thesis.md", nodes.render_thesis(thesis))
15941608

15951609
state = {
1596-
"uid": uid, "article_id": article_id, "abstract": abstract,
1597-
"intake": intake or "", "author": author or "",
1598-
"mode": "article",
1610+
**_base_run_state(uid, abstract, intake=intake, author=author,
1611+
max_revisions=max_revisions, autonomous=autonomous,
1612+
humanize=humanize, settings=settings),
1613+
"article_id": article_id, "mode": "article",
15991614
"phase": "sections", "num_sections": len(outline.sections),
1600-
"max_revisions": max_revisions, "current_section": 1,
1601-
"committed": 0, "pending_review": False,
1602-
"use_researcher": settings.use_researcher,
1603-
"deep_research": settings.deep_research,
1604-
"autonomous": autonomous,
1605-
"humanize": settings.humanize if humanize is None else humanize,
1606-
"use_images": settings.use_images,
1607-
"diagram_engine": settings.diagram_engine,
1608-
"use_embeddings": settings.use_embeddings,
1615+
"current_section": 1,
16091616
"article_cohesion": settings.article_cohesion,
16101617
"strip_inline_citations": settings.strip_inline_citations,
16111618
"rank_references": settings.rank_references,
16121619
"table_read": settings.table_read,
16131620
"table_read_revise": settings.table_read_revise,
1614-
"divergent_drafts": settings.divergent_drafts,
16151621
"divergent_skeletons": settings.divergent_skeletons,
1616-
"tournament_judge": settings.tournament_judge,
16171622
"verify_claims": settings.verify_claims,
1618-
"min_insight": settings.min_insight,
1619-
"escalate_below_confidence": 0.0 if autonomous else settings.escalate_below_confidence,
16201623
"escalate_on_contradiction": False,
16211624
}
16221625
brain.write_json(paths.run_state, state)

0 commit comments

Comments
 (0)