Two symptoms reported after a successful resume:
1. Banner stays visible even though the report finished — because the
historical 'error' log entry still lives in agent_log.jsonl, and every
fetch from line 0 re-flipped reportStatus to 'failed'.
2. Polling dies mid-resume — stopPolling() was called when the error
entry was re-read, killing the timers before the resume_start /
report_complete entries landed.
Fixes:
- On 'resume_start': reset reportStatus back to 'generating' so the
banner clears as soon as the resume kicks in.
- On 'report_complete': force reportStatus='completed', clear
reportError. Belt-and-suspenders even if an older error entry is
re-read later.
- Remove stopPolling() from the error handlers (both agent-log and
progress-poll). The polling naturally stops on report_complete; if
a real terminal failure never resumes, the component unmounts when
the user navigates away. Better to waste a few extra polls than to
soft-lock the UI during a legitimate resume.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The progress.json update in the failure path is wrapped in a silent
except: pass, so if the write fails (concurrent read, disk issue) the
frontend never sees status === 'failed' and the resume banner stays
hidden even though the generation is dead.
Also, the agent log records action === 'error' synchronously inside the
exception handler, which is a more reliable signal than polling
progress.json. Flip reportStatus to 'failed' the moment that entry
appears so the banner renders without waiting for the next 3s progress
tick.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When report generation in Step 4 fails mid-way (e.g. AI credits exhausted,
network error), users previously had to restart from scratch, losing the
outline and any already-completed sections.
This change preserves partial progress and lets users continue from where
generation stopped:
- Backend: `generate_report(resume=True)` reloads the saved outline and
per-section markdown files, skips the planning phase, and only generates
the missing sections. The `/api/report/generate` endpoint accepts a
`resume` flag and reuses the failed report's `report_id`.
- Frontend: Step4Report polls `/progress` and, when `status === 'failed'`,
shows a "Resume Generation" banner with the failure message and a button
that calls `generateReport({ simulation_id, resume: true })` and
restarts log polling.
- Adds `resumeStart` / `sectionResumed` / `reportFailedTitle` /
`resumeGeneration` locale keys in en and zh.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add sans-serif font for English left-pane (status, workflow sections)
- Shorten English workflow step descriptions
- Reduce English report title font-size from 36px to 28px
- Use sans-serif font for English titles, descriptions and navbar
- Shorten English hero text to avoid overflow
- Fix :global() scoped CSS issue that was setting root font-size to 3.5rem
- Use separate unscoped style block for html[lang] selectors
Background threads (graph building, simulation prep, report generation,
profile generation) now inherit the requesting user's locale preference.
Previously these fell back to 'zh' because Flask request context was
unavailable in spawned threads.
Ensure poster_type stays PascalCase English and stance stays English enum
values regardless of language setting. Only natural language fields follow
the user's language preference.
The language instruction was causing LLM to change entity/relation naming
conventions. Now explicitly enforce PascalCase/UPPER_SNAKE_CASE for technical
identifiers while only applying language preference to description fields.