Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit 203456bbdf419d85b6808015c8394576082c6dfc
parent 547493411ac3be12af6ff6c0776279ce72141dad
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Fri,  2 Oct 2026 01:15:58 -0400

editor: Clear hold's sentence outlives the strip it empties (review H2)

A clear that leaves nothing to list unmounts the Rate-limit cooldown
region, which held the sentence; it now lives on the lane view.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Diffstat:
Meditor/app/operations/components/RunnerOperationView.tsx | 19++++++++++++-------
Mplans/FACTS.md | 17+++++++++++++----
2 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/editor/app/operations/components/RunnerOperationView.tsx b/editor/app/operations/components/RunnerOperationView.tsx @@ -68,6 +68,9 @@ export function RunnerOperationView({ onRefresh: () => Promise<void>; }) { const [busy, setBusy] = useState(false); + // What the last Clear hold did. Held HERE, not in the strip: a clear that + // leaves nothing to list unmounts the strip, and the sentence must outlive it. + const [holdNote, setHoldNote] = useState<string | null>(null); const now = useNow(); const control = useCallback( @@ -129,9 +132,15 @@ export function RunnerOperationView({ subtitleDeferred={status.subtitleDeferred ?? []} laneRunning={status.runner.running} onRefresh={onRefresh} + onCleared={setHoldNote} now={now} /> )} + {holdNote && ( + <span aria-label="clear hold result" className="text-xs text-muted-foreground"> + {holdNote} + </span> + )} <SnoozeControl kind={kind} @@ -228,6 +237,7 @@ function CooldownStrip({ subtitleDeferred, laneRunning, onRefresh, + onCleared, now, }: { cooldowns: PlatformCooldownView[]; @@ -236,15 +246,15 @@ function CooldownStrip({ subtitleDeferred: SubtitleDeferralView[]; laneRunning: boolean; onRefresh: () => Promise<void>; + onCleared: (message: string) => void; now: number | null; }) { const [clearing, setClearing] = useState<string | null>(null); - const [cleared, setCleared] = useState<string | null>(null); const clearHold = async (platform: string) => { setClearing(platform); try { const res = await clearPlatformHoldAction(platform); - setCleared(res.ok ? res.message : res.error); + onCleared(res.ok ? res.message : res.error); await onRefresh(); } finally { setClearing(null); @@ -318,11 +328,6 @@ function CooldownStrip({ </ul> </> )} - {cleared && ( - <span aria-label="clear hold result" className="text-xs text-muted-foreground"> - {cleared} - </span> - )} {cooling.length > 0 && ( <> <span className="font-medium text-warning"> diff --git a/plans/FACTS.md b/plans/FACTS.md @@ -6410,7 +6410,11 @@ Line numbers are `plans/FACTS.md` lines at `e172749b`, before this record's in-p returns it when a subtitle-429 line is present and every `ERROR:` line is a subtitle line (no soft block, no bot check) — `isSubtitleRateLimitOnly` / `hasSubtitleRateLimit`. It is set on a **SUCCESS** record (`status: ok…`, `failureClass: "subs_rate_limit"`), the one class that is. -- **yt-dlp's `--ignore-errors` turns a subtitle failure into a WARNING and carries on** +- **yt-dlp's `--ignore-errors` turns ANY subtitle failure into a WARNING and carries on** — a 429, a + 403/404/5xx, a failed `live_chat` replay, an OSError writing the file (review H1, verified offline for + a 403) — so the downloader parses the WARNING's class: only a rate limit takes the media path + (`hasNonRateLimitSubtitleFailure` keeps any other a failed attempt, not archived, classified from the + tail) (`YoutubeDL._write_subtitles`: `ignoreerrors is True` → `report_warning`; `'only_download'`, i.e. `--no-abort-on-error`, still raises). Without it, a `--load-info-json` run that fails re-extracts from the URL once (`download_with_info_file` "The info failed to download … trying with URL") — @@ -6437,9 +6441,14 @@ Line numbers are `plans/FACTS.md` lines at `e172749b`, before this record's in-p `detectPlatform(url) ?? "unknown"`, the cooldown's key. - **The hold**: `FAILS_TO_REACH_CAP` = 6 (60 s doubling reaches the 30-min cap at the sixth); `failsAtCap(fails) = max(0, fails − 5)`. At `pacing.holdAfterFailsAtCap` the backoff's `until` - becomes `now + holdProbeMinutes` (no jitter) and `platformHolds[pf].probeAt` mirrors it; a held - platform's backoff entry is never pruned. Only a `transcribed` unit with no subtitle 429 clears - it. A manual Sync / download / metadata scan / fetch-window on a held platform is refused with + becomes `now + holdProbeMinutes` (no jitter) and `platformHolds[pf].probeAt` mirrors it + (`rateLimited` false for a hold reached through network failures alone); a held platform's backoff + entry is never pruned. It is cleared by a clean lane unit (a `subs_rate_limit` unit included), by a + clean manual run once the probe is due (`runYtdlp`'s `onPlatformClean` → `recordPlatformClean`, and a + clean metadata scan), or by **Clear hold** (`clearPlatformHold`, a `clear-platform-hold` job on queue + `pacing:<pf>`). Manual actions are refused only while held AND before the probe time. +- **The one time-based rule:** a raised pace eases one step per `PACE_TIME_DECAY_MS` (1 h) since its + `steppedAt` with no rate limit (`effectivePaceSeconds` for readers, `decayPaceByTime` on writes). A manual Sync / download / metadata scan / fetch-window on a held platform is refused with `heldPlatformSentence` (`<pf> is held: … the next probe is in N min. <What> will run once a probe comes back clean.`). - **The download lane now waits between units**: `platformNextStartAt` (in memory) =