Archilyzer · Source

archilyzer

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

commit a1dbe0b13af15e1083f18df55854fab3b7caa587
parent 9849af0a3bb6302a36a36a3dc4d486af3807253e
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Fri,  2 Oct 2026 01:30:55 -0400

common, editor: review L3, N9, N5 — a tier migration's marker is never cleared or previewed past; identity before tiering

tierMigrationRefusal is exported and gives one sentence to the job, the
preview (which now refuses too and tiers nothing), Resume/Reconcile and Clear
marker (which no longer removes the phase a migration resumes from). The
move-out checks the destination's presence and identity before the preflight
tiers the channel.

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

Diffstat:
Mcommon/controller/relocateChannelMedia.test.ts | 9+++++++++
Mcommon/controller/relocateChannelMedia.ts | 16+++++++++++++---
Meditor/app/channels/[slug]/storageActions.ts | 19++++++++++---------
3 files changed, 32 insertions(+), 12 deletions(-)

diff --git a/common/controller/relocateChannelMedia.test.ts b/common/controller/relocateChannelMedia.test.ts @@ -663,6 +663,15 @@ test("a tier migration's marker is never resumed or replaced by the mover", asyn /media-tier migration in flight or interrupted .* archilyzer storage migrate-tier alpha/, ); } + // The preview gives the same sentence, and tiers nothing (review N9). + await assert.rejects( + () => previewRelocation({ paths, slug: "alpha", root }), + /media-tier migration in flight or interrupted .* archilyzer storage migrate-tier alpha/, + ); + assert.equal( + await pathIsThere(path.join(paths.channelsDir, "alpha", "media")), + false, + ); assert.equal((await readRelocationMarker(paths, "alpha"))?.scope, "tier-migration"); }); }); diff --git a/common/controller/relocateChannelMedia.ts b/common/controller/relocateChannelMedia.ts @@ -227,9 +227,11 @@ async function legacyRefusal( return `Channel "${slug}" cannot be moved: ${legacyDetail(slug)}.`; } -// A TIER MIGRATION'S MARKER is not this mover's to resume or replace: the -// migration rebuilds `data/` itself, with the editor stopped. -function tierMigrationRefusal( +// A TIER MIGRATION'S MARKER is not this mover's to resume, replace or clear: +// the migration rebuilds `data/` itself, with the editor stopped, and resumes +// from its marker's phase. ONE sentence, for the job, the preview, and the +// Storage panel's Resume, Reconcile and Clear marker. +export function tierMigrationRefusal( slug: string, marker: RelocationMarker | null, ): string | null { @@ -589,6 +591,8 @@ export async function previewRelocation({ if (problem) throw new Error(problem); const channelDir = path.join(paths.channelsDir, slug); const config = await readChannelConfig(paths, slug); + const migrating = tierMigrationRefusal(slug, await readMarkerRaw(paths, slug)); + if (migrating) throw new Error(migrating); const legacy = await legacyRefusal(channelDir, slug, config); if (legacy) throw new Error(legacy); // The same refusal the job's first step gives, before the operator commits. @@ -875,6 +879,12 @@ async function moveOut(args: { // until the next sweep tiers it onto the far side). let tiered = 0; if (args.phase === "copy") { + // THE DESTINATION'S IDENTITY FIRST (review N5): a bare mountpoint, or a + // drive whose learned volume does not match, is refused before the + // channel is touched at all — the tiering below is harmless, but "refused + // before anything" should be true here too. Asked again immediately before + // the mkdir below, for a job that sat in the queue. + await assertRelocationRootPresent(root, args.io.read().storage, paths); const counts = await tierChannelMedia(paths, slug, { createMediaDir: true, onLog: (line) => log(line.replace(/\n$/, "")), diff --git a/editor/app/channels/[slug]/storageActions.ts b/editor/app/channels/[slug]/storageActions.ts @@ -37,6 +37,7 @@ import { relocatedMediaDir } from "yt-dlp-transcript-common/lib/mediaTier-server import { previewRelocation, relocationRootProblem, + tierMigrationRefusal, type RelocationPreview, } from "yt-dlp-transcript-common/controller/relocateChannelMedia"; import { enqueueRelocation } from "../lib/relocationJob"; @@ -169,15 +170,8 @@ async function resumeRelocation( if (refusal) return { ok: false, error: refusal }; // A TIER MIGRATION'S MARKER (release 17) is not this panel's to resume: // the migration runs with the editor stopped and resumes itself. - if (marker.scope === "tier-migration") { - return { - ok: false, - error: - `Channel "${slug}" has a media-tier migration in flight or ` + - `interrupted (phase "${marker.phase}") — finish it with ` + - `archilyzer storage migrate-tier ${slug}, with the editor stopped.`, - }; - } + const migrating = tierMigrationRefusal(slug, marker); + if (migrating) return { ok: false, error: migrating }; if (marker.direction === "back") { return enqueueRelocation({ slug, direction: "back", reconcile }); } @@ -228,6 +222,13 @@ export async function clearRelocationMarkerAction( { mediaOnly: true }, ); if (refusal) return { ok: false, error: refusal }; + // AN INTERRUPTED TIER MIGRATION'S MARKER is never cleared from here: it is + // the phase the migration resumes from (the same sentence Resume gives). + const migrating = tierMigrationRefusal( + slug, + await readRelocationMarker(getPaths(), slug), + ); + if (migrating) return { ok: false, error: migrating }; try { await clearRelocationMarker(getPaths(), slug); } catch (e) {