Archilyzer · Source

archilyzer

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

commit 3da64705ccff7146af9abc5904711b32c66e22de
parent d417d8f27be891f23be89b43eb431f2b766398d8
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Sat, 29 Aug 2026 18:31:45 -0400

operations: the sync page reads the scheduler payload once

The cadence-console comment claimed buildSchedulerStatusPayload ran only in
that arm, and it was false: buildSyncRow in the Promise.all above calls it too,
so /operations/sync listed every channel config twice for one screen.

buildSyncRow takes an optional payload now. The sync page starts ONE and does
not await it: the rail row folds it, the console awaits the same already-settled
promise, and the three reads beside it still run in parallel. Every other page
passes nothing and builds the payload inside buildSyncRow exactly as before —
that read is the whole cost there. The comments say what is true.

Also the one preserve line on /settings that had none: syncScheduler is edited
on /operations/sync, in the shape of the workers line above it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Diffstat:
Meditor/app/operations/[id]/page.tsx | 25+++++++++++++++++++------
Meditor/app/operations/syncRow.ts | 26+++++++++++++++++++-------
Meditor/app/settings/actions.ts | 2++
3 files changed, 40 insertions(+), 13 deletions(-)

diff --git a/editor/app/operations/[id]/page.tsx b/editor/app/operations/[id]/page.tsx @@ -155,10 +155,22 @@ export default async function OperationPage({ // OperationDetail renders that as "no console here" rather than borrowing one. const laneId = sweepLaneIdFor(op); + // THE SCHEDULER PAYLOAD, READ ONCE PER RENDER. The rail's sync row is a fold + // of it and the cadence console below is the whole of it, so the sync page + // would otherwise list every channel config twice for one screen. Started + // here and NOT awaited: the row and the console await the same promise, and + // the three reads below still run in parallel. + const schedulerPayload = + op.trigger === "cadence" ? buildSchedulerStatusPayload() : null; + const [initial, channels, sync] = await Promise.all([ buildAutoQueueStatusPayload(), listChannelConfigs(getPaths()), - buildSyncRow(), + // Every other page wants only the row, and builds the payload inside + // buildSyncRow — which is the whole cost there, and unchanged. + schedulerPayload + ? schedulerPayload.then((payload) => buildSyncRow(payload)) + : buildSyncRow(), ]); const channelOptions = channels.map((c) => ({ slug: c.slug, @@ -193,11 +205,12 @@ export default async function OperationPage({ // its schedule. `runner` cannot say this (it is typed AutoQueueKind, and the // heartbeat is not one), so `trigger` does. ONE such operation exists; a // second would need the descriptor to name its console, not this file to - // branch on an id. buildSchedulerStatusPayload runs ONLY in this arm. - const cadenceConsole = - op.trigger === "cadence" ? ( - <SyncConsole key="sync" initial={await buildSchedulerStatusPayload()} /> - ) : null; + // branch on an id. The same promise the rail row folded, awaited a second + // time and therefore already settled — one scheduler read on this page, and + // none at all on any other. + const cadenceConsole = schedulerPayload ? ( + <SyncConsole key="sync" initial={await schedulerPayload} /> + ) : null; const laneJobKinds = runnerKind || !laneId ? [] : JOB_KINDS_BY_LANE[laneId]; diff --git a/editor/app/operations/syncRow.ts b/editor/app/operations/syncRow.ts @@ -3,12 +3,20 @@ import { SYNC_OPERATION, operationLabel, } from "yt-dlp-transcript-common/lib/operations"; -import { buildSchedulerStatusPayload } from "../scheduler/status"; +import { + buildSchedulerStatusPayload, + type SchedulerStatusPayload, +} from "../scheduler/status"; // THE SYNC ROW ON THE OPERATIONS RAIL, folded down to what a one-line row can // say. Sync is channel-scoped, so these are channel figures and there is no // band to draw — see SyncRailRow. The full per-channel schedule is the sync // operation's own page, /operations/sync. +// +// The row and that page's console are two readings of ONE scheduler payload, +// which is why buildSyncRow takes an optional one: /operations/sync builds the +// payload itself (the console needs the whole thing) and hands it here, so the +// page reads it once rather than listing every channel config twice. export type SyncRowView = { // Off the descriptor, so the row's link and its name cannot drift from the // catalog the rest of the rail is built from. `label` travels as a prop @@ -32,16 +40,20 @@ export type SyncRowView = { heartbeatSeconds: number; }; -export async function buildSyncRow(): Promise<SyncRowView> { - const payload = await buildSchedulerStatusPayload(); - const eligible = payload.channels.filter((c) => c.autoSyncEligible); +export async function buildSyncRow( + // Absent on every page that wants only the row — the board and the non-sync + // operation pages — where building the payload here is the whole cost. + payload?: SchedulerStatusPayload, +): Promise<SyncRowView> { + const status = payload ?? (await buildSchedulerStatusPayload()); + const eligible = status.channels.filter((c) => c.autoSyncEligible); return { id: SYNC_OPERATION.id, label: operationLabel(SYNC_OPERATION.id), - enabled: payload.scheduler.enabled, + enabled: status.scheduler.enabled, eligible: eligible.length, due: eligible.filter( - (c) => c.nextDueAt !== null && c.nextDueAt <= payload.now, + (c) => c.nextDueAt !== null && c.nextDueAt <= status.now, ).length, overdue: eligible.filter((c) => c.overdue).length, running: getRegistry() @@ -50,6 +62,6 @@ export async function buildSyncRow(): Promise<SyncRowView> { (j) => j.kind === "sync" && (j.status === "running" || j.status === "queued"), ).length, - heartbeatSeconds: payload.heartbeatSeconds, + heartbeatSeconds: status.heartbeatSeconds, }; } diff --git a/editor/app/settings/actions.ts b/editor/app/settings/actions.ts @@ -226,6 +226,8 @@ export async function saveSettingsAction( archiveStorage, reportDebouncePreset, autoRefreshIntervalSeconds: autoRefreshParsed, + // syncScheduler: edited on /operations/sync, below the schedule, by + // saveSchedulerSettingsAction. syncScheduler: getSettings().syncScheduler, // Preserve the existing auto-queue policy on an unrelated settings save // (this form doesn't edit it; the Auto-queue page does). writeSettings