Real IMAP connection via imapflow library: - Connects to IMAP server (IMAPFlow, TLS on port 993) - Fetches recent messages (configurable limit, default 20) - Extracts: from, fromEmail, subject, date, snippet (200 chars) - Thread-to-contact matching (PRD §6.1): 1. Match sender email against persons table 2. If no person match, extract domain and match against company.website 3. Returns matchedCompany + matchedPerson for each email - Audit-logged (action: crm.mailsync:imap) - Zod validated (host, port, user, password, folder, limit) - 502 on IMAP connection failure with detail - 400 on validation failure Tested: invalid creds → 502 'IMAP connection failed', missing host → 400
7.5 KiB
Executable File
Incremental Execution — How a Survey Actually Gets Built
Why this exists
A survey with 100+ references and 7 multi-paragraph sections does not fit into a single LLM tool call. WebSearch returns ~10 useful candidates per query, not 100. A single message can write one solid section, not seven. If you treat the build as "do it all in one shot", you will hit context limits, fail mid-task, and have nothing to recover.
This reference defines the only execution mode that works: incremental, file-persisted, resumable. Every other reference (01–05) describes what to produce; this one describes how to produce it without losing work.
The three invariants
Invariant 1 — Filesystem is the source of truth
Every intermediate result is written to disk immediately. A new BibTeX entry is appended to bibliography.bib the moment it is extracted from a WebFetch result, not after the batch finishes. A rewritten section is Written to sections/<name>.tex the moment it is drafted, not after all seven are drafted. A generated figure is committed to figures/fig_NN_<name>.pdf and the matching script is saved alongside.
If the conversation context fills, the agent crashes, or the user pivots, the files survive. Resumption reads the files and continues.
Invariant 2 — Progress is observable via filesystem, not memory
cd output/literature-survey/<slug>/survey_paper
grep -c "^@" bibliography.bib # bib entries so far
ls figures/*.pdf | wc -l # figures so far
for f in sections/*.tex; do
echo "$(basename "$f"): $(wc -w < "$f") words"
done # per-section drafted size
If the count does not match the target, do another batch. Don't rely on remembering what you did.
Invariant 3 — Each batch is small enough to succeed, atomic enough to retry
A "batch" is one WebSearch+WebFetch cycle, or one section's prose, or one figure's script+render. Failure → redo just that batch. Success → write to disk and move on.
Batch sizes that work
| Operation | Batch size per turn | Total batches | Persistence |
|---|---|---|---|
| Bibliography expansion | 1 WebSearch query → triage → ~5–15 WebFetch'd entries | 10–15 batches to reach 100+ | Append to bibliography.bib after each |
| Section drafting | 1 section per turn | 7 batches total | Write each sections/<name>.tex immediately |
| Figure generation | 1 figure per turn (TikZ inline or matplotlib script + render) | 6–10 batches | Save script + render alongside |
| Compile + check | 1 cycle after major artefact | Several | main.log + grep |
A turn that tries to do 5 sections produces shallow prose and probably truncates. A turn that tries to "WebSearch all 15 queries at once" hits tool budgets. One per turn is the rhythm.
Bibliography expansion — incremental loop
read existing bibliography.bib
n_entries = grep -c "^@" bibliography.bib
target = 110 # aim above the 100 recommended floor (hard gate 60)
query_plan = [...] # 12–20 angles from 01-bibliography-expansion.md
already_run = read_state(.bib_progress) # set of executed query strings
seen_urls = extract_urls_from_bib() # already-cited URLs
for q in query_plan:
if q in already_run: continue
candidates = WebSearch(q) # ~5–15 hits
for c in triage(candidates): # filter by venue, dedup
if c.url in seen_urls: continue
meta = WebFetch(c.url) # canonical title/authors/year/venue
if meta is bad: continue
append_bibtex(meta, bibliography.bib)
seen_urls.add(c.url)
append(.bib_progress, q) # persist that query was run
if grep -c "^@" bibliography.bib >= target: break
In Claude Code execution this is one WebSearch + one or more WebFetch + Edit/Write per turn for ~15 turns. If turn 7 fails, turn 8 reads the progress file, sees queries 1–6 are done, restarts from 7. No work lost.
Section drafting — one section per turn
Order: introduction → background → methods (themed survey) → discussion → conclusion → related work → abstract.
For each section:
- Read
references/03-survey-section-playbook.mdrules for that section (open it fresh — don't trust cached recollection). - Read
bibliography.biband pick relevant keys for this section. - Draft the section in one go (one section is 600–1500 words for a survey; a single LLM turn can comfortably produce that).
- Immediately Write the result to
sections/<name>.tex. - Compile (
pdflatexonce +bibtex+pdflatex×2). Checkmain.logfor new undefined citations. - Fix errors before moving to the next section.
"Abstract last" rule
The abstract names the field's structure as you have organised it. Until you have actually organised it (in Background through Discussion), you can't write a good abstract. Drafting it last takes 5 minutes; drafting it first wastes 30.
Figure generation — one figure per turn
For each figure (6–10 total):
- Open
references/02-survey-figures.mdfor the family that fits this figure (taxonomy / timeline / matrix / architecture / quantitative). - Decide what the figure answers in the survey. That answer becomes the caption.
- Write the figure (TikZ inline in the relevant
sections/*.tex, or afigures/make_fig_NN_*.pyscript). - Render (TikZ compiles via main.tex; matplotlib via
cd figures && python make_fig_NN_*.py). - Verify the PDF exists and is non-trivial.
- Reference it from the appropriate section with a caption.
- Recompile and check.
Recovery — what to do when a session resumes mid-task
cd output/literature-survey/<slug>/survey_paper
grep -c "^@" bibliography.bib # bib progress
cat .bib_progress.txt 2>/dev/null | wc -l # queries executed
for f in sections/*.tex; do
echo "$(basename "$f"): $(wc -w < "$f") words"
done # which sections drafted
ls figures/*.pdf 2>/dev/null
ls -la main.pdf
grep -c "Citation .* undefined" main.log
Resume the sub-step that's incomplete. Never redo work that's already on disk; only fill in what's missing.
Checkpoint files
Three small bookkeeping files in the survey paper directory:
.bib_progress.txt— one line per executed query string.section_progress.txt— one line per drafted section.figure_progress.txt— one line per generated figure id
echo "transformer time series Informer Autoformer" >> .bib_progress.txt
Atomic write rule
Use Write/Edit tools (atomic). Never stream partial content via python -c "open(...).write(...)". Don't parallelize bib appends — they must serialise.
Sanity check before delivery
ls -la sections/ # all 7 .tex files non-trivial?
ls -la figures/*.pdf # 6+ figure PDFs present?
test -s main.pdf
grep -c "^@" bibliography.bib # ≥ 60 (100+ recommended)
total=0; for f in sections/*.tex; do
total=$((total + $(grep -o "\\\\cite{" "$f" | wc -l)))
done
echo "TOTAL cites: $total" # ≥ 60 across all sections
Quick checklist
- Bibliography assembled in 12+ small WebSearch+WebFetch batches, one query per turn
- Sections written one per turn, abstract last
- Figures generated one per turn
- Three checkpoint files maintained
- Compile+verify after each major artefact
- Never tried to "do it all in one turn"