Fix: Resume Workflow Uses Wrong Paths for Firm-Scoped IO
When resuming a workflow with `cli/resume_from_interruption.py` for firm-scoped deals, the writer agent would use legacy `output/` paths instead of firm-scoped `io/{firm}/deals/{deal}/outputs/` paths.
Changelog - 2025-12-09 (#3)
Fix: Resume Workflow Uses Wrong Paths for Firm-Scoped IO
Two Related Bugs Fixed
Problem
When resuming a workflow with cli/resume_from_interruption.py for firm-scoped deals, the writer agent would use legacy output/ paths instead of firm-scoped io/{firm}/deals/{deal}/outputs/ paths.
Error observed:
Error in agent draft: [Errno 2] No such file or directory: 'output/RavenGraph-v0.0.1/2-sections/01-executive-summary.md'Expected path:
io/dark-matter/deals/RavenGraph/outputs/RavenGraph-v0.0.2/2-sections/01-executive-summary.mdRoot Cause
Two bugs in the resume workflow:
cli/resume_from_interruption.py- Thefirm,outline_name, andscorecard_namevalues were loaded from the deal config but NOT passed tocreate_initial_state(). This causedstate["firm"]to beNone.src/agents/writer.py- Always created a new version directory, ignoring any existingstate["output_dir"]set by the resume script.
Solution
Fix 1: Pass firm context to state (resume_from_interruption.py)
Before:
state = create_initial_state(
company_name=company_name,
investment_type=investment_type,
memo_mode=memo_mode,
deck_path=deck_path,
# ... other params
# MISSING: firm, outline_name, scorecard_name
)After:
firm = ctx.firm if ctx else None
state = create_initial_state(
company_name=company_name,
investment_type=investment_type,
memo_mode=memo_mode,
firm=firm, # NOW PASSED
deck_path=deck_path,
# ... other params
outline_name=outline_name, # NOW PASSED
scorecard_name=scorecard_name # NOW PASSED
)Fix 2: Respect existing output_dir (writer.py)
Before:
firm = state.get("firm")
safe_name = sanitize_filename(company_name)
if firm:
# Always creates new version - ignores state["output_dir"]
version = version_mgr.get_next_version(safe_name)
output_dir = create_artifact_directory(...)After:
firm = state.get("firm")
safe_name = sanitize_filename(company_name)
# Check if output_dir already set (e.g., by resume script)
existing_output_dir = state.get("output_dir")
if existing_output_dir:
output_dir = Path(existing_output_dir)
print(f" Using existing output directory: {output_dir}")
(output_dir / "2-sections").mkdir(parents=True, exist_ok=True)
elif firm:
# Create new version only when not resuming
version = version_mgr.get_next_version(safe_name)
output_dir = create_artifact_directory(...)Files Changed
| File | Change |
cli/resume_from_interruption.py | Pass firm, outline_name, scorecard_name to create_initial_state() |
src/agents/writer.py | Check for existing state["output_dir"] before creating new version |
Verification
After the fix, resume correctly uses firm-scoped paths:
[DEBUG] About to call create_initial_state for RavenGraph (firm=dark-matter)
[DEBUG] create_initial_state completed (state firm=dark-matter)
Using existing output directory: io/dark-matter/deals/RavenGraph/outputs/RavenGraph-v0.0.2Impact
Resume workflow now correctly handles firm-scoped IO paths
Writer agent respects pre-set output directories when resuming
All 12 agents now respect
state["output_dir"]for correct path resolutionFix enables resuming interrupted workflows without path errors
Additional Fix: All Agents Now Respect state["output_dir"]
The Second Bug
After fixing the resume script, agents were still using wrong paths because they called get_latest_output_dir() which returns the newest version - not the version being resumed.
Solution
Added new helper get_output_dir_from_state() in src/utils.py that:
First checks
state["output_dir"](set by resume script)Falls back to
get_latest_output_dir()if not set
Updated 12 agent files to use this helper:
citation_enrichment.py
fact_checker.py
internal_comments_sanitizer.py
link_enrichment.py
perplexity_section_researcher.py
revise_summary_sections.py
scorecard_agent.py
scorecard_evaluator.py
socials_enrichment.py
toc_generator.py
trademark_enrichment.py
validator.py