@@ -446,9 +446,10 @@ def run_paper_iteration(self) -> bool:
446446
447447 # Recompile after writing phase to get the latest PDF
448448 self .log_step ("Recompiling after improvements..." , "progress" )
449+ self ._ensure_clearpage_before_bibliography ()
449450 self .compile_latex ()
450451
451- # Hard page count enforcement — the ONLY place we enforce before delivery
452+ # Hard page count enforcement before delivery
452453 self ._enforce_page_count (context = "pre-delivery" )
453454
454455 # Send iteration summary + PDF to Telegram
@@ -1128,36 +1129,39 @@ def _run_dev_phase(self):
11281129 self .log_section ("✏️ Writing Initial Paper Draft" )
11291130 self ._send_dev_phase_telegram ("writing" , 0 , 0 )
11301131
1131- # Create plotting script from experiment results (if not already present)
1132+ # ── FIGURES FIRST, THEN WRITE ──
1133+ # All figures must be ready before writer starts, so writer can reference them.
1134+
1135+ # Step A: Create plotting script from experiment results
11321136 self ._create_plotting_script_if_needed ()
11331137
1134- # Generate matplotlib figures from results
1135- self .log_step ("Generating figures from experiment results..." , "progress" )
1138+ # Step B: Generate matplotlib figures
1139+ self .log_step ("Generating statistical figures from experiment results..." , "progress" )
11361140 self .generate_figures ()
11371141
1138- # Start AI concept figure generation in background (slow, ~7min/fig)
1139- # Writer can proceed in parallel — it writes text first, figures are \includegraphics refs
1140- nano_banana_future = None
1142+ # Step C: Generate AI concept figures (sequential — must complete before writer)
11411143 if self .config .get ("figure_generation" ) == "nano_banana" :
1142- from concurrent .futures import ThreadPoolExecutor
1143- self ._nano_banana_executor = ThreadPoolExecutor (max_workers = 1 )
1144- self .log_step ("Starting AI concept figure generation (background)..." , "progress" )
1145- nano_banana_future = self ._nano_banana_executor .submit (self ._generate_nano_banana_figures )
1144+ self .log_step ("Generating AI concept figures (PaperBanana)..." , "progress" )
1145+ self ._generate_nano_banana_figures ()
1146+
1147+ # Step D: List all available figures for the writer
1148+ figure_list = self ._list_available_figures ()
11461149
1147- # Writer produces complete initial draft
1150+ # Step E: Writer writes paper with KNOWN figure filenames
11481151 paper_requirements = self .load_paper_requirements ()
11491152 req_summary = yaml .dump (paper_requirements , allow_unicode = True ) if paper_requirements else "No special requirements"
11501153 findings_summary = self ._load_findings_summary ()
11511154
1155+ venue_pages = self .config .get ('venue_pages' , 9 )
1156+ latex_dir = self .config .get ('latex_dir' , 'paper' )
1157+ figures_dir = self .config .get ('figures_dir' , 'paper/figures' )
1158+
11521159 base_prompt = self .config .get ("initial_paper_writing_prompt" , "" )
11531160 if base_prompt :
11541161 prompt = base_prompt .replace ("{req_summary}" , req_summary )
1155- # Enhance with findings context
11561162 prompt += f"\n \n ## Experiment Findings\n { findings_summary } "
1163+ prompt += f"\n \n ## Available Figures (already generated)\n { figure_list } "
11571164 else :
1158- venue_pages = self .config .get ('venue_pages' , 9 )
1159- latex_dir = self .config .get ('latex_dir' , 'paper' )
1160- figures_dir = self .config .get ('figures_dir' , 'paper/figures' )
11611165 prompt = f"""Write a COMPLETE, SUBMISSION-READY research paper draft.
11621166
11631167## Research Idea
@@ -1169,6 +1173,15 @@ def _run_dev_phase(self):
11691173## Paper Requirements
11701174{ req_summary }
11711175
1176+ ## Available Figures (already generated — DO NOT recreate these)
1177+ { figure_list }
1178+
1179+ **CRITICAL**: The figures above are already generated. Use \\ includegraphics to include them.
1180+ - AI concept figures (marked as "AI concept") must NOT be recreated as TikZ or matplotlib.
1181+ - Statistical plots (marked as "matplotlib") are already generated from experiment data.
1182+ - Use the EXACT filenames listed above in your \\ includegraphics commands.
1183+ - For multi-column templates, use \\ begin{{figure*}} for wide concept figures, \\ begin{{figure}} for single plots.
1184+
11721185## MANDATORY — every item below is required, NO exceptions:
11731186
11741187### 1. All sections must be fully written (zero placeholders)
@@ -1180,51 +1193,35 @@ def _run_dev_phase(self):
11801193- Analysis/Discussion: explain WHY results are good/bad, failure cases
11811194- Conclusion: 1 paragraph summary + 1 paragraph future work
11821195
1183- ### 2. Figures are REQUIRED (paper will fail without them)
1184- - Minimum 2 figures in the body:
1185- a) System/architecture overview (TikZ diagram OR simple block diagram in LaTeX)
1186- b) Main results figure (bar chart or line plot from actual results data)
1187- - Each figure needs: \\ caption{{...}} and \\ label{{fig:...}}
1188- - Generate result figures using Python: save to { figures_dir } / then \\ includegraphics
1189-
1190- ### 3. Data integrity
1196+ ### 2. Data integrity
11911197- Every performance claim must use actual numbers from findings
11921198- Include at least one \\ begin{{table}} comparing against baselines
11931199- No vague statements like "our method is better" — use exact percentages
11941200
1195- ### 4. Page target: { venue_pages } pages of body text
1196- - Every section must be substantively written
1197- - Related Work and Experiments sections should each be 1.5-2 pages
1198- - Do NOT leave any section with only 1-2 sentences
1201+ ### 3. Page target: { venue_pages } pages of body text
1202+ - The last page must be at least 90% filled
1203+ - Ensure `\\ clearpage` before `\\ bibliography{{...}}`
11991204
1200- ### 5 . LaTeX mechanics
1205+ ### 4 . LaTeX mechanics
12011206- Edit { latex_dir } /main.tex directly
12021207- Verify compilation: cd { latex_dir } && pdflatex -interaction=nonstopmode main.tex
1203- - All \\ ref and \\ cite must resolve (no undefined references)
1204- - If figures don't exist yet, create simple placeholder TikZ diagrams
1208+ - All \\ ref and \\ cite must resolve
12051209
12061210Produce the complete paper. Do not stop until all sections are written and it compiles.
12071211"""
12081212
12091213 self .run_agent ("writer" , prompt , timeout = 3600 )
12101214
1211- # Wait for background AI figure generation to complete (if running)
1212- if nano_banana_future is not None :
1213- self .log_step ("Waiting for AI concept figures to complete..." , "progress" )
1214- try :
1215- nano_banana_future .result (timeout = 1200 ) # 20 min max
1216- self .log_step ("AI concept figures ready" , "success" )
1217- except Exception as e :
1218- self .log (f"AI concept figure generation failed: { e } " , "WARN" )
1219- finally :
1220- self ._nano_banana_executor .shutdown (wait = False )
1221-
1222- # Compile initial draft (must succeed before moving to review)
1215+ # Step F: Inject \clearpage before \bibliography + enforce page count
1216+ self ._ensure_clearpage_before_bibliography ()
12231217 self .log_step ("Compiling initial draft..." , "progress" )
12241218 draft_compiled = self ._compile_until_success (
12251219 context = f"Dev Phase complete ({ dev_state ['iteration' ]} iterations)"
12261220 )
12271221
1222+ if draft_compiled :
1223+ self ._enforce_page_count (context = "dev-phase-delivery" )
1224+
12281225 if draft_compiled and self .telegram .is_configured :
12291226 pdf_path = self .latex_dir / "main.pdf"
12301227 if pdf_path .exists ():
@@ -1551,6 +1548,20 @@ def _create_plotting_script_if_needed(self):
15511548 else :
15521549 self .log (f"Coder agent did not create { script_rel } " , "WARN" )
15531550
1551+ def _list_available_figures (self ) -> str :
1552+ """List all figures in paper/figures/ with their type (AI concept vs matplotlib)."""
1553+ if not self .figures_dir .exists ():
1554+ return "No figures generated yet."
1555+ lines = []
1556+ for f in sorted (self .figures_dir .iterdir ()):
1557+ if f .suffix not in (".png" , ".pdf" ):
1558+ continue
1559+ size_kb = f .stat ().st_size // 1024
1560+ # AI concept figures are typically >150KB (PaperBanana/Gemini output)
1561+ fig_type = "AI concept diagram — DO NOT recreate" if size_kb > 150 else "matplotlib statistical plot"
1562+ lines .append (f"- { f .name } ({ size_kb } KB, { fig_type } )" )
1563+ return "\n " .join (lines ) if lines else "No figures generated yet."
1564+
15541565 def _send_dev_phase_telegram (self , event : str , current : int , total : int ):
15551566 """Send dev phase notifications to Telegram."""
15561567 if not self .telegram .is_configured :
0 commit comments