一键导入
polling-mechanism
Use when modifying HTMX polling behavior, generation status responses, the hx-trigger stop pattern, or debugging infinite spinner issues.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when modifying HTMX polling behavior, generation status responses, the hx-trigger stop pattern, or debugging infinite spinner issues.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use when selecting a Gemini model, checking rate limits or quotas, configuring the gemini-rust crate, or debugging model name mismatches.
Create new Kiro skills (Agent Skills). Use when asked to create a skill, write a SKILL.md, add a new workflow to .kiro/skills/, or package agent instructions as a reusable skill.
Use when creating a release, bumping the version, tagging a commit, or working with the GitHub Actions release workflow for rs-summarizer.
Use when editing or creating Askama HTML templates, adding template variables, fixing compile-time template errors, or working with HTMX attributes in templates.
Use when working with tokio::spawn background tasks, the summarization pipeline, task state transitions, or the mark_error/mark_summary_done pattern.
Use when building, running, linting with clippy, formatting with rustfmt, or running cargo commands for the rs-summarizer project.
| name | polling-mechanism |
| description | Use when modifying HTMX polling behavior, generation status responses, the hx-trigger stop pattern, or debugging infinite spinner issues. |
rs-summarizer uses HTMX polling to provide real-time progressive updates during summary generation. The frontend polls the backend every second, and the backend signals completion by omitting HTMX attributes from the response.
When a user submits a YouTube URL:
summary_done = falsetokio::spawnThe template conditionally includes HTMX attributes based on summary_done:
<div id="generation"
{% if !summary_done %}hx-post="/generations/{{ identifier }}" hx-trigger="every 1s" hx-swap="outerHTML"{% endif %}>
<article>
{% if summary_done %}
<header>Summary Complete</header>
{% else %}
<header>Generating summary... <span aria-busy="true"></span></header>
{% endif %}
<div>{{ summary|safe }}</div>
{% if summary_done && !timestamps.is_empty() %}
<footer>{{ timestamps|safe }}</footer>
{% endif %}
</article>
</div>
Key behavior:
summary_done = false: the div has hx-post, hx-trigger="every 1s", and hx-swap="outerHTML" — HTMX replaces the entire div every secondsummary_done = true: no HTMX attributes are rendered — polling stops automatically<span aria-busy="true">) only shows when summary_done = falseEach poll hits get_generation() which:
db::fetch_summary()GenerationPartialTemplate with current summary_done stateThe process_summary() function orchestrates the pipeline:
db::mark_summary_done() setting summary_done = truetimestamps_done = trueOn error: mark_error() stores the error message in the summary field AND sets summary_done = true, ensuring the frontend always stops polling.
[Insert row] → summary_done=false, summary=""
↓
[Streaming] → summary_done=false, summary grows chunk by chunk
↓
[Complete] → summary_done=true, tokens/cost/timestamp_end recorded
↓
[Timestamps] → timestamps_done=true, youtube_format populated
↓
[Embedding] → embedding blob stored (non-fatal)
On error at any step:
[Error] → summary_done=true, summary contains error message
summary_done must always eventually become true — whether the pipeline succeeds or fails. This is what stops the HTMX polling loop. If summary_done never becomes true, the frontend polls forever (the "infinite spinner" bug).
| Field | Type | Role |
|---|---|---|
summary_done | BOOLEAN | Controls HTMX polling (false = keep polling, true = stop) |
summary | TEXT | Grows during streaming, contains final text or error message |
summary_timestamp_end | TEXT | ISO 8601 timestamp when summary generation completed |
summary_input_tokens | INTEGER | Token count from Gemini usage metadata |
summary_output_tokens | INTEGER | Token count from Gemini usage metadata |
timestamps_done | BOOLEAN | Set after YouTube format conversion |
timestamped_summary_in_youtube_format | TEXT | YouTube-compatible text (no ** markers) |
templates/generation_partial.html — HTMX polling templatesrc/routes/mod.rs — get_generation() polling endpoint, render_generation_partial() helpersrc/tasks.rs — process_summary() background task, mark_error() error handlersrc/db.rs — mark_summary_done(), update_summary_chunk(), mark_timestamps_done()src/services/summary.rs — Streaming chunk generation via GeminiThe following browser integration tests verify polling behavior end-to-end:
test_form_submission_shows_processing — Verifies #generation div appears with hx-post after form submissiontest_polling_stops_on_error — Verifies no hx-trigger attribute when error occurs (polling doesn't start)test_deduplication_returns_same_id — Verifies duplicate submissions return existing generation partialtest_aria_busy_during_generation — Verifies aria-busy="true" present during generation, absent when donetest_server_restart_recovery — Verifies browser recovers when server restarts mid-polltest_full_summarization_e2e — Verifies polling terminates and hx-trigger is removed after completionRun with: cargo test --test integration_browser -- --ignored