Skip to content

Commit 264aecb

Browse files
JihaoXinclaude
andcommitted
Hard guarantees: FloatBarrier injection + overfull fix loop in pre-delivery
Pre-delivery chain (all hard guarantees, no agent dependency): clearpage → FloatBarrier → compile → fix overfull (loop ×3) → page enforcement (loop until OK) → citation verification → send PDF _ensure_float_barrier(): - Finds last \section in main.tex, injects \FloatBarrier before it - Auto-adds \usepackage{placeins} if missing - Prevents figures from floating past Conclusion into blank pages _fix_overfull(): - Parses main.log for Overfull \hbox warnings (>3pt only) - Asks writer to reword, loops up to 3 times until 0 warnings - Quota-aware: stops if API exhausted Both applied in Dev phase delivery and Review loop delivery. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 487d354 commit 264aecb

File tree

2 files changed

+112
-6
lines changed

2 files changed

+112
-6
lines changed

ark/execution.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,111 @@ def _ensure_clearpage_before_bibliography(self):
750750
except Exception as e:
751751
self.log(f"Failed to inject \\clearpage: {e}", "WARN")
752752

753+
def _ensure_float_barrier(self):
754+
"""Ensure \\FloatBarrier before the last \\section in main.tex.
755+
756+
Prevents figures from floating past the last section (e.g., Conclusion)
757+
into blank pages. Also ensures \\usepackage{placeins} is in preamble.
758+
"""
759+
main_tex = self.latex_dir / "main.tex"
760+
if not main_tex.exists():
761+
return
762+
try:
763+
import re as _re
764+
content = main_tex.read_text()
765+
766+
# Ensure \usepackage{placeins}
767+
if 'placeins' not in content:
768+
# Insert after last \usepackage in preamble
769+
pkgs = list(_re.finditer(r'\\usepackage(\[.*?\])?\{[^}]+\}', content))
770+
if pkgs:
771+
pos = pkgs[-1].end()
772+
content = content[:pos] + '\n\\usepackage{placeins}' + content[pos:]
773+
774+
# Find all \section positions (not \subsection)
775+
sections = list(_re.finditer(r'(?<!sub)\\section\{', content))
776+
if len(sections) < 2:
777+
main_tex.write_text(content)
778+
return
779+
780+
# Last section position
781+
last_sec = sections[-1]
782+
783+
# Check if \FloatBarrier already before it
784+
before = content[max(0, last_sec.start() - 30):last_sec.start()]
785+
if '\\FloatBarrier' in before:
786+
main_tex.write_text(content)
787+
return
788+
789+
# Insert \FloatBarrier before last \section
790+
insert_pos = last_sec.start()
791+
content = content[:insert_pos] + '\\FloatBarrier\n' + content[insert_pos:]
792+
main_tex.write_text(content)
793+
self.log(f"Injected \\FloatBarrier before last section", "INFO")
794+
except Exception as e:
795+
self.log(f"Failed to inject FloatBarrier: {e}", "WARN")
796+
797+
def _fix_overfull(self, context: str = "pre-delivery"):
798+
"""Fix overfull \\hbox warnings by asking writer to reword.
799+
800+
Loops until 0 overfull warnings or max attempts.
801+
"""
802+
MAX_ATTEMPTS = 3
803+
latex_dir = self.config.get("latex_dir", "paper")
804+
805+
for attempt in range(MAX_ATTEMPTS):
806+
# Parse overfull from log
807+
log_path = self.latex_dir / "main.log"
808+
if not log_path.exists():
809+
return
810+
811+
log_text = log_path.read_text(errors="replace")
812+
import re as _re
813+
overfull_lines = _re.findall(
814+
r'Overfull \\hbox \(([0-9.]+)pt too wide\) in paragraph at lines (\d+)--(\d+)',
815+
log_text
816+
)
817+
# Only fix significant ones (>3pt)
818+
significant = [(float(pt), int(l1), int(l2)) for pt, l1, l2 in overfull_lines if float(pt) > 3.0]
819+
820+
if not significant:
821+
if attempt > 0:
822+
self.log(f"[{context}] All overfull warnings fixed (attempt {attempt})", "INFO")
823+
return
824+
825+
if self._quota_exhausted:
826+
self.log(f"[{context}] {len(significant)} overfull warnings remain (quota exhausted)", "WARN")
827+
return
828+
829+
self.log(f"[{context}] Fixing {len(significant)} overfull warnings (attempt {attempt + 1}/{MAX_ATTEMPTS})...", "WARN")
830+
831+
overfull_desc = "\n".join(
832+
f"- Lines {l1}-{l2}: {pt:.1f}pt too wide" for pt, l1, l2 in significant[:10]
833+
)
834+
835+
self.run_agent("writer", f"""## FIX OVERFULL HBOX (attempt {attempt + 1})
836+
837+
The paper has {len(significant)} overfull \\hbox warnings (text extending past column margin):
838+
839+
{overfull_desc}
840+
841+
Fix each by rewording the sentence to allow LaTeX better line breaking:
842+
- Shorten long compound words or phrases
843+
- Add \\- hyphenation hints for technical terms
844+
- Split long inline math or URLs
845+
- Rephrase to use shorter synonyms
846+
847+
Do NOT change figures, template, or page structure.
848+
After fixing, compile: cd {latex_dir} && pdflatex -interaction=nonstopmode main.tex
849+
""", timeout=600)
850+
851+
self.compile_latex()
852+
853+
# Final check
854+
remaining = len([1 for pt, _, _ in significant if float(pt) > 3.0]) if significant else 0
855+
if remaining:
856+
self.log(f"[{context}] {remaining} overfull warnings remain after {MAX_ATTEMPTS} attempts", "WARN")
857+
753858
def _run_writing_phase(self, action_plan: dict, prior_context: str = ""):
754859
"""Execute writing phase for all writing tasks."""
755860
issues = action_plan.get("issues", [])

ark/pipeline.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -444,15 +444,13 @@ def run_paper_iteration(self) -> bool:
444444
# Record to Memory
445445
self.record_score_to_memory(score)
446446

447-
# Recompile after writing phase to get the latest PDF
448-
self.log_step("Recompiling after improvements...", "progress")
447+
# ── Pre-delivery checks (all hard guarantees) ──
448+
self.log_step("Pre-delivery checks...", "progress")
449449
self._ensure_clearpage_before_bibliography()
450+
self._ensure_float_barrier()
450451
self.compile_latex()
451-
452-
# Hard page count enforcement before delivery
452+
self._fix_overfull(context="pre-delivery")
453453
self._enforce_page_count(context="pre-delivery")
454-
455-
# Final citation verification — re-apply NEEDS-CHECK tags that writer may have removed
456454
self._run_citation_verification()
457455

458456
# Send iteration summary + PDF to Telegram
@@ -1226,6 +1224,9 @@ def _run_dev_phase(self):
12261224
)
12271225

12281226
if draft_compiled:
1227+
self._ensure_float_barrier()
1228+
self.compile_latex()
1229+
self._fix_overfull(context="dev-phase-delivery")
12291230
self._enforce_page_count(context="dev-phase-delivery")
12301231
self._run_citation_verification()
12311232

0 commit comments

Comments
 (0)